From 03abc49625c3f6f02bd9dab913d0c30248178761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=95=B8=EB=AA=A8?= Date: Wed, 14 Feb 2024 20:23:27 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=95=BC=EA=B5=AC=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/baseball/Application.kt | 6 +- .../baseball/controller/BaseballController.kt | 29 ++++++++++ .../src/main/kotlin/baseball/model/Numbers.kt | 57 +++++++++++++++++++ .../main/kotlin/baseball/model/StrikeBall.kt | 17 ++++++ .../kotlin/baseball/model/UserContinueGame.kt | 14 +++++ .../src/main/kotlin/baseball/view/View.kt | 29 ++++++++++ 6 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt create mode 100644 kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt create mode 100644 kotlin-baseball/src/main/kotlin/baseball/model/StrikeBall.kt create mode 100644 kotlin-baseball/src/main/kotlin/baseball/model/UserContinueGame.kt create mode 100644 kotlin-baseball/src/main/kotlin/baseball/view/View.kt diff --git a/kotlin-baseball/src/main/kotlin/baseball/Application.kt b/kotlin-baseball/src/main/kotlin/baseball/Application.kt index 148d75c..42931f3 100644 --- a/kotlin-baseball/src/main/kotlin/baseball/Application.kt +++ b/kotlin-baseball/src/main/kotlin/baseball/Application.kt @@ -1,5 +1,9 @@ package baseball +import baseball.controller.BaseballController +import baseball.view.View + fun main() { - TODO("프로그램 구현") + val baseballController = BaseballController(View()) + baseballController.start() } diff --git a/kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt b/kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt new file mode 100644 index 0000000..a938d4e --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt @@ -0,0 +1,29 @@ +package baseball.controller + +import baseball.model.NUMBER_LENGTH +import baseball.model.Numbers +import baseball.model.StrikeBall +import baseball.model.UserContinueGame +import baseball.view.View + +class BaseballController( + private val view: View, +) { + fun start() { + view.printStartMessage() + do { + val computerNumbers = Numbers.generateRandom() + do { + val userNumbers = view.getUserNumbers() + val strikeBall = userNumbers.compare(computerNumbers) + view.printStrikeBall(strikeBall) + } while (strikeBall.isGameContinue()) + view.printEndMessage() + } while (view.getUserContinueGame() == UserContinueGame.RESTART) + } + + private fun StrikeBall.isGameContinue(): Boolean { + println("${this.strike} ${this.ball}") + return this.strike != NUMBER_LENGTH + } +} diff --git a/kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt b/kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt new file mode 100644 index 0000000..a814b76 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt @@ -0,0 +1,57 @@ +package baseball.model + +import camp.nextstep.edu.missionutils.Randoms + +const val MIN_NUMBER_INCLUDE = 1 +const val MAX_NUMBER_INCLUDE = 9 +const val NUMBER_LENGTH = 3 + +class Numbers(input: String) { + private val numbers: List = input.map { Number(it.digitToInt()) } + + companion object { + fun generateRandom() = + Numbers( + mutableListOf().apply { + while (size != NUMBER_LENGTH) { + val pickNumber = Randoms.pickNumberInRange(MIN_NUMBER_INCLUDE, MAX_NUMBER_INCLUDE) + if (pickNumber !in this) { + this.add(pickNumber) + } + } + }.joinToString("") + ) + } + + init { + require(numbers.size == NUMBER_LENGTH) + require(numbers.distinct().size == numbers.size) + } + + fun compare(other: Numbers): StrikeBall { + val strike = getStrike(other) + val ball = getBall(other, strike) + return StrikeBall(strike, ball) + } + + fun getStrike(other: Numbers) = + (0 until NUMBER_LENGTH) + .count { this.numbers[it] == other.numbers[it] } + + fun getBall(other: Numbers, strike: Int) = + (0 until NUMBER_LENGTH) + .count { i -> isBall(i, other) } + + private fun isBall(i: Int, other: Numbers) = + (0 until NUMBER_LENGTH) + .any { j -> + if (i == j) return@any false + this.numbers[i] == other.numbers[j] + } +} + +data class Number(val number: Int) { + init { + require(number in MIN_NUMBER_INCLUDE..MAX_NUMBER_INCLUDE) + } +} diff --git a/kotlin-baseball/src/main/kotlin/baseball/model/StrikeBall.kt b/kotlin-baseball/src/main/kotlin/baseball/model/StrikeBall.kt new file mode 100644 index 0000000..4308870 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/baseball/model/StrikeBall.kt @@ -0,0 +1,17 @@ +package baseball.model + +data class StrikeBall(val strike: Int, val ball: Int) { + override fun toString() = + buildString { + if (ball != 0) { + append("${ball}볼 ") + } + if (strike != 0) { + append("${strike}스트라이크 ") + } + if (strike == 0 && ball == 0) { + append("낫싱") + } + return toString() + } +} diff --git a/kotlin-baseball/src/main/kotlin/baseball/model/UserContinueGame.kt b/kotlin-baseball/src/main/kotlin/baseball/model/UserContinueGame.kt new file mode 100644 index 0000000..6e8a823 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/baseball/model/UserContinueGame.kt @@ -0,0 +1,14 @@ +package baseball.model + +enum class UserContinueGame(val value: Int) { + RESTART(1), + END(2), + ; + + companion object { + fun of(input: String): UserContinueGame { + return entries.find { it.value == input.toInt() } + ?: throw IllegalArgumentException("Input must in $entries. input: $input") + } + } +} diff --git a/kotlin-baseball/src/main/kotlin/baseball/view/View.kt b/kotlin-baseball/src/main/kotlin/baseball/view/View.kt new file mode 100644 index 0000000..8a14290 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/baseball/view/View.kt @@ -0,0 +1,29 @@ +package baseball.view + +import baseball.model.NUMBER_LENGTH +import baseball.model.Numbers +import baseball.model.StrikeBall +import baseball.model.UserContinueGame +import camp.nextstep.edu.missionutils.Console + +class View { + fun printStartMessage() { + println("숫자 야구 게임을 시작합니다.") + } + + fun getUserNumbers(): Numbers { + print("숫자를 입력해주세요 : ") + return Numbers(Console.readLine()) + } + + fun getUserContinueGame() = UserContinueGame.of(Console.readLine()) + + fun printStrikeBall(strikeBall: StrikeBall) { + println(strikeBall) + } + + fun printEndMessage() { + println("${NUMBER_LENGTH}개의 숫자를 모두 맞히셨습니다! 게임 종료") + println("게임을 새로 시작하려면 ${UserContinueGame.RESTART.value}, 종료하려면 ${UserContinueGame.END.value}를 입력하세요.") + } +} From c8cea8e0d1820748a76fb433dec827a3efa18c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=95=B8=EB=AA=A8?= Date: Thu, 15 Feb 2024 19:57:26 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baseball/controller/BaseballController.kt | 5 +---- .../src/main/kotlin/baseball/model/Numbers.kt | 14 ++++++-------- .../src/main/kotlin/baseball/view/View.kt | 3 ++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt b/kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt index a938d4e..c7c68e0 100644 --- a/kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt +++ b/kotlin-baseball/src/main/kotlin/baseball/controller/BaseballController.kt @@ -22,8 +22,5 @@ class BaseballController( } while (view.getUserContinueGame() == UserContinueGame.RESTART) } - private fun StrikeBall.isGameContinue(): Boolean { - println("${this.strike} ${this.ball}") - return this.strike != NUMBER_LENGTH - } + private fun StrikeBall.isGameContinue() = this.strike != NUMBER_LENGTH } diff --git a/kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt b/kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt index a814b76..0723b6c 100644 --- a/kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt +++ b/kotlin-baseball/src/main/kotlin/baseball/model/Numbers.kt @@ -6,20 +6,18 @@ const val MIN_NUMBER_INCLUDE = 1 const val MAX_NUMBER_INCLUDE = 9 const val NUMBER_LENGTH = 3 -class Numbers(input: String) { - private val numbers: List = input.map { Number(it.digitToInt()) } - +data class Numbers(val numbers: List) { companion object { fun generateRandom() = Numbers( - mutableListOf().apply { + mutableListOf().apply { while (size != NUMBER_LENGTH) { - val pickNumber = Randoms.pickNumberInRange(MIN_NUMBER_INCLUDE, MAX_NUMBER_INCLUDE) + val pickNumber = Number(Randoms.pickNumberInRange(MIN_NUMBER_INCLUDE, MAX_NUMBER_INCLUDE)) if (pickNumber !in this) { this.add(pickNumber) } } - }.joinToString("") + }.toList() ) } @@ -30,7 +28,7 @@ class Numbers(input: String) { fun compare(other: Numbers): StrikeBall { val strike = getStrike(other) - val ball = getBall(other, strike) + val ball = getBall(other) return StrikeBall(strike, ball) } @@ -38,7 +36,7 @@ class Numbers(input: String) { (0 until NUMBER_LENGTH) .count { this.numbers[it] == other.numbers[it] } - fun getBall(other: Numbers, strike: Int) = + fun getBall(other: Numbers) = (0 until NUMBER_LENGTH) .count { i -> isBall(i, other) } diff --git a/kotlin-baseball/src/main/kotlin/baseball/view/View.kt b/kotlin-baseball/src/main/kotlin/baseball/view/View.kt index 8a14290..1915437 100644 --- a/kotlin-baseball/src/main/kotlin/baseball/view/View.kt +++ b/kotlin-baseball/src/main/kotlin/baseball/view/View.kt @@ -1,6 +1,7 @@ package baseball.view import baseball.model.NUMBER_LENGTH +import baseball.model.Number import baseball.model.Numbers import baseball.model.StrikeBall import baseball.model.UserContinueGame @@ -13,7 +14,7 @@ class View { fun getUserNumbers(): Numbers { print("숫자를 입력해주세요 : ") - return Numbers(Console.readLine()) + return Numbers(Console.readLine().map { Number(it.digitToInt()) }) } fun getUserContinueGame() = UserContinueGame.of(Console.readLine())