diff --git a/docs/README.md b/docs/README.md index e69de29bb..443c76ca5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,46 @@ +# ๋ฏธ์…˜ - ๋กœ๋˜ + +## ๐Ÿš€ ๊ธฐ๋Šฅ ์š”๊ตฌ ์‚ฌํ•ญ + +๋กœ๋˜ ๊ฒŒ์ž„ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ๋กœ๋˜ ๊ฒŒ์ž„์€ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ทœ์น™์œผ๋กœ ์ง„ํ–‰๋œ๋‹ค. + +-[x] ๊ตฌ๋งค๊ธ‰์•ก ์ž…๋ ฅ๋ฐ›๊ธฐ +-[x] ๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ์ˆซ์ž ๋ฒ”์œ„๋Š” 1~45๊นŒ์ง€์ด๋‹ค. +-[x] 1๊ฐœ์˜ ๋กœ๋˜๋ฅผ ๋ฐœํ–‰ํ•  ๋•Œ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” 6๊ฐœ์˜ ์ˆซ์ž๋ฅผ ๋ฝ‘๋Š”๋‹ค. +-[x] ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ถ”์ฒจ ์‹œ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” ์ˆซ์ž 6๊ฐœ์™€ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ 1๊ฐœ๋ฅผ ๋ฝ‘๋Š”๋‹ค. +-[x] ์ผ์น˜ํ•˜๋Š” ๋กฏ๋„ ๋ฒˆํ˜ธ ๊ฐœ์ˆ˜๋ฅผ ๊ตฌํ•œ๋‹ค. +-[x] ๋‹น์ฒจ์€ 1๋“ฑ๋ถ€ํ„ฐ 5๋“ฑ๊นŒ์ง€ ์žˆ๋‹ค. ๋‹น์ฒจ ๊ธฐ์ค€๊ณผ ๊ธˆ์•ก์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค. +-[x] ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅํ•˜๋ฉด ๊ตฌ์ž… ๊ธˆ์•ก์— ํ•ด๋‹นํ•˜๋Š” ๋งŒํผ ๋กœ๋˜๋ฅผ ๋ฐœํ–‰ํ•ด์•ผ ํ•œ๋‹ค. +-[x] ๋กœ๋˜ 1์žฅ์˜ ๊ฐ€๊ฒฉ์€ 1,000์›์ด๋‹ค. +-[x] ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ๋ฐ›๋Š”๋‹ค. +-[x]๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š”๋‹ค. +-[x]์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๋งคํ•œ ๋กœ๋˜ ๋ฒˆํ˜ธ์™€ ๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋‹น์ฒจ ๋‚ด์—ญ ๋ฐ ์ˆ˜์ต๋ฅ ์„ ์ถœ๋ ฅํ•˜๊ณ  ๋กœ๋˜ ๊ฒŒ์ž„์„ ์ข…๋ฃŒํ•œ๋‹ค. +-[x] ์‚ฌ์šฉ์ž๊ฐ€ ์ž˜๋ชป๋œ ๊ฐ’์„ ์ž…๋ ฅํ•  ๊ฒฝ์šฐ `IllegalArgumentException`๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ณ , "[ERROR]"๋กœ ์‹œ์ž‘ํ•˜๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅ ํ›„ ๊ทธ ๋ถ€๋ถ„๋ถ€ํ„ฐ ์ž…๋ ฅ์„ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค. `Exception`์ด ์•„๋‹Œ `IllegalArgumentException`, `IllegalStateException` ๋“ฑ๊ณผ ๊ฐ™์€ ๋ช…ํ™•ํ•œ ์œ ํ˜•์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. + +### ์ถ”๊ฐ€๋œ ์š”๊ตฌ ์‚ฌํ•ญ + +-[ ] ํ•จ์ˆ˜(๋˜๋Š” ๋ฉ”์„œ๋“œ)์˜ ๊ธธ์ด๊ฐ€ 15๋ผ์ธ์„ ๋„˜์–ด๊ฐ€์ง€ ์•Š๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค. + - ํ•จ์ˆ˜(๋˜๋Š” ๋ฉ”์„œ๋“œ)๊ฐ€ ํ•œ ๊ฐ€์ง€ ์ผ๋งŒ ์ž˜ ํ•˜๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค. +-[ ] else๋ฅผ ์ง€์–‘ํ•œ๋‹ค. + - ํžŒํŠธ: if ์กฐ๊ฑด์ ˆ์—์„œ ๊ฐ’์„ returnํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด else๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. + - ๋•Œ๋กœ๋Š” if/else, when๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๊น”๋”ํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค. ์–ด๋Š ๊ฒฝ์šฐ์— ์“ฐ๋Š” ๊ฒƒ์ด ์ ์ ˆํ• ์ง€ ์Šค์Šค๋กœ ๊ณ ๋ฏผํ•ด ๋ณธ๋‹ค. +-[x] Enum ํด๋ž˜์Šค๋ฅผ ์ ์šฉํ•ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๊ตฌํ˜„ํ•œ๋‹ค. +-[x] ๋„๋ฉ”์ธ ๋กœ์ง์— ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ๋‹จ, UI(System.out, System.in, Scanner) ๋กœ์ง์€ ์ œ์™ธํ•œ๋‹ค. + - ํ•ต์‹ฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š” ์ฝ”๋“œ์™€ UI๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋กœ์ง์„ ๋ถ„๋ฆฌํ•ด ๊ตฌํ˜„ํ•œ๋‹ค. + - ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์ด ์ต์ˆ™ํ•˜์ง€ ์•Š๋‹ค๋ฉด `test/kotlin/lotto/LottoTest`๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ํ•™์Šตํ•œ ํ›„ ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. +-[x] `camp.nextstep.edu.missionutils`์—์„œ ์ œ๊ณตํ•˜๋Š” `Randoms` ๋ฐ `Console` API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. + - Random ๊ฐ’ ์ถ”์ถœ์€ `camp.nextstep.edu.missionutils.Randoms`์˜ `pickUniqueNumbersInRange()`๋ฅผ ํ™œ์šฉํ•œ๋‹ค. + - ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋Š” ๊ฐ’์€ `camp.nextstep.edu.missionutils.Console`์˜ `readLine()`์„ ํ™œ์šฉํ•œ๋‹ค. +-[x] ์ œ๊ณต๋œ `Lotto` ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•ด ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. + +## MVC๋ชจ๋ธ ํ™œ์šฉ + +### Model +- [x] Lotto ํด๋ž˜์Šค์— ๋ฒˆํ˜ธ ์ €์žฅ + +### View +- [x] ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ + +### Controller +- [x] LottoController : ์ˆซ์ž ์ž…๋ ฅ ๋ฐ›์€ ๊ฒƒ ์ฒ˜๋ฆฌ ๋ฐ ๊ฒŒ์ž„ ์‹คํ–‰ +- [x] ์‚ฌ์šฉ์ž์—๊ฒŒ ์ˆซ์ž ์ž…๋ ฅ ๋ฐ›๊ธฐ diff --git a/src/main/kotlin/lotto/Application.kt b/src/main/kotlin/lotto/Application.kt index d7168761c..9a26b5a07 100644 --- a/src/main/kotlin/lotto/Application.kt +++ b/src/main/kotlin/lotto/Application.kt @@ -1,5 +1,9 @@ package lotto +import lotto.Controller.LottoController + fun main() { - TODO("ํ”„๋กœ๊ทธ๋žจ ๊ตฌํ˜„") + val lottoController = LottoController() + lottoController.startGame() + } diff --git a/src/main/kotlin/lotto/Controller/LottoController.kt b/src/main/kotlin/lotto/Controller/LottoController.kt new file mode 100644 index 000000000..32bb0b9b8 --- /dev/null +++ b/src/main/kotlin/lotto/Controller/LottoController.kt @@ -0,0 +1,131 @@ +package lotto.Controller + +import lotto.View.LottoView +import camp.nextstep.edu.missionutils.Randoms +import camp.nextstep.edu.missionutils.Console + +class LottoController { + private val lottoView = LottoView() + + enum class LottoPrize(val sameCount: Int, val prizeMoney: Int, val prizeName: String) { + threeSame(3, 5000, "3๊ฐœ ์ผ์น˜"), + fourSame(4, 50000, "4๊ฐœ ์ผ์น˜"), + fiveSame(5, 1500000, "5๊ฐœ ์ผ์น˜"), + fiveSamePlusBonus(5, 30000000, "5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜"), + sixSame(6, 2000000000, "6๊ฐœ ์ผ์น˜") + } + + fun startGame() { + try { + val inputMoney = lottoMoneyInput() + val count = lottoCnt(inputMoney) + val lottoList = lottoNumberLimit(count) + lottoView.printLottoNumbers(count, lottoList) + + val lottoNumber = lottoNumberChoose() + val bonusNumber = lottoNumberBonus() + + val result = lottoNumberCheck(lottoList, lottoNumber, bonusNumber) + lottoView.printResult(result, count) + } catch (e: IllegalArgumentException) { + lottoView.printMessage("[ERROR] ${e.message}") + startGame() + } + } + + private fun lottoMoneyInput(): Int { + val message = "๊ตฌ์ž…๊ธˆ์•ก์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”." + return readIntFromConsole(message) + } + + private fun readIntFromConsole(message: String): Int { + while (true) { + try { + lottoView.printMessage(message) + val input = Console.readLine().toInt() + if (input % 1000 != 0) { + throw IllegalArgumentException("1000์› ๋‹จ์œ„๋กœ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.") + } + return input + } catch (e: NumberFormatException) { + lottoView.printMessage("[ERROR] ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.") + } catch (e: IllegalArgumentException) { + lottoView.printMessage("[ERROR] ${e.message}") + } + } + } + + private fun lottoCnt(inputMoney: Int): Int { + return inputMoney / 1000 + } + + private fun lottoNumberLimit(count: Int): List> { + val comLottoList = mutableListOf>() + repeat(count) { + val numbers = Randoms.pickUniqueNumbersInRange(1, 45, 6) + comLottoList.add(numbers) + } + return comLottoList + } + + private fun lottoNumberChoose(): List { + val message = "๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”." + return readNumbersFromConsole(message, 6) + } + + private fun lottoNumberBonus(): Int { + val message = "\n๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”." + return readNumbersFromConsole(message, 1)[0] + } + + private fun readNumbersFromConsole(message: String, expectedSize: Int): List { + while (true) { + try { + lottoView.printMessage(message) + val input = Console.readLine() + val numbers = input.split(",").map { it.trim().toInt() } + if (numbers.size != expectedSize) { + throw IllegalArgumentException("์ž…๋ ฅํ•œ ์ˆซ์ž์˜ ๊ฐœ์ˆ˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.") + } + if (numbers.toSet().size != expectedSize) { + throw IllegalArgumentException("์ค‘๋ณต๋˜๋Š” ๋ฒˆํ˜ธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.") + } + if (numbers.any { it < 1 || it > 45 }) { + throw IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์˜ ๊ฐ’์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.") + } + return numbers + } catch (e: NumberFormatException) { + lottoView.printMessage("[ERROR] ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.") + } catch (e: IllegalArgumentException) { + lottoView.printMessage("[ERROR] ${e.message}") + } + } + } + + private fun lottoNumberCheck(lottoList: List>, comNumber: List, bonusNumber: Int): Map { + val lottoMoneyList = mutableMapOf() + lottoMoneyList["3๊ฐœ ์ผ์น˜"] = 0 + lottoMoneyList["4๊ฐœ ์ผ์น˜"] = 0 + lottoMoneyList["5๊ฐœ ์ผ์น˜"] = 0 + lottoMoneyList["5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜"] = 0 + lottoMoneyList["6๊ฐœ ์ผ์น˜"] = 0 + + for (lotto in lottoList) { + val sameNumber = lotto.filter { it in comNumber }.size + when (sameNumber) { + 3 -> lottoMoneyList["3๊ฐœ ์ผ์น˜"] = lottoMoneyList.getOrDefault("3๊ฐœ ์ผ์น˜", 0) + 1 + 4 -> lottoMoneyList["4๊ฐœ ์ผ์น˜"] = lottoMoneyList.getOrDefault("4๊ฐœ ์ผ์น˜", 0) + 1 + 5 -> { + if (lotto.contains(bonusNumber)) { + lottoMoneyList["5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜"] = + lottoMoneyList.getOrDefault("5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜", 0) + 1 + } else { + lottoMoneyList["5๊ฐœ ์ผ์น˜"] = lottoMoneyList.getOrDefault("5๊ฐœ ์ผ์น˜", 0) + 1 + } + } + 6 -> lottoMoneyList["6๊ฐœ ์ผ์น˜"] = lottoMoneyList.getOrDefault("6๊ฐœ ์ผ์น˜", 0) + 1 + } + } + return lottoMoneyList + } +} diff --git a/src/main/kotlin/lotto/Lotto.kt b/src/main/kotlin/lotto/Lotto.kt deleted file mode 100644 index 5ca00b4e4..000000000 --- a/src/main/kotlin/lotto/Lotto.kt +++ /dev/null @@ -1,9 +0,0 @@ -package lotto - -class Lotto(private val numbers: List) { - init { - require(numbers.size == 6) - } - - // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ -} diff --git a/src/main/kotlin/lotto/Model/Lotto.kt b/src/main/kotlin/lotto/Model/Lotto.kt new file mode 100644 index 000000000..952ab8ce7 --- /dev/null +++ b/src/main/kotlin/lotto/Model/Lotto.kt @@ -0,0 +1,27 @@ +package lotto.Model + +class Lotto(private val numbers: List) { + init { + require(numbers.size == 6) { "๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค." } + require(numbers.toSet().size == 6) { "๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค." } + require(numbers.all { it in 1..45 }) { "๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์˜ ๊ฐ’์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค." } + } + + fun getNumbers(): List { + return numbers + } + + fun lottoNumberCheck(winningNumbers: List, bonusNumber: Int): String { + val matchedNumbers = numbers.filter { it in winningNumbers } + val matchingCount = matchedNumbers.size + return when (matchingCount) { + 3 -> "3๊ฐœ ์ผ์น˜" + 4 -> "4๊ฐœ ์ผ์น˜" + 5 -> { + if (numbers.contains(bonusNumber)) "5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜" else "5๊ฐœ ์ผ์น˜" + } + 6 -> "6๊ฐœ ์ผ์น˜" + else -> "๊ฝ" + } + } +} diff --git a/src/main/kotlin/lotto/View/LottoView.kt b/src/main/kotlin/lotto/View/LottoView.kt new file mode 100644 index 000000000..899281bfe --- /dev/null +++ b/src/main/kotlin/lotto/View/LottoView.kt @@ -0,0 +1,45 @@ +package lotto.View + +import lotto.Controller.LottoController + +class LottoView { + fun printMessage(message: String) { + println(message) + } + + fun printLottoNumbers(lottoCount: Int, lottoList: List>) { + printMessage("\n${lottoCount}๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.") + lottoList.forEach { lotto -> + printMessage(lotto.sorted().joinToString(", ", "[", "]")) + } + println() + } + + fun printResult(result: Map, lottoCount: Int) { + printMessage("\n๋‹น์ฒจ ํ†ต๊ณ„") + printMessage("---") + var totalPrize = 0 + val prizeMoney = mapOf( + "3๊ฐœ ์ผ์น˜" to 5000, + "4๊ฐœ ์ผ์น˜" to 50000, + "5๊ฐœ ์ผ์น˜" to 1500000, + "5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜" to 30000000, + "6๊ฐœ ์ผ์น˜" to 2000000000 + ) + for ((key, value) in result) { + val prize = when (key) { + "5๊ฐœ ์ผ์น˜" -> if (result[LottoController.LottoPrize.fiveSamePlusBonus.prizeName] == 0) "1,500,000์›" else "30,000,000์›" + else -> "${prizeMoney[key]?.let { "%,d".format(it) }}์›" + } + printMessage("$key ($prize) - $value ๊ฐœ") + totalPrize += if (key == "5๊ฐœ ์ผ์น˜") { + if (result[LottoController.LottoPrize.fiveSamePlusBonus.prizeName] == 0) prizeMoney["5๊ฐœ ์ผ์น˜"]!! * value else prizeMoney["5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜"]!! * value + } else { + prizeMoney[key]!! * value + } + } + val inputMoney = lottoCount * 1000 + val rateOfReturn = ((totalPrize - inputMoney) / inputMoney.toDouble() * 100).coerceAtLeast(0.0) + printMessage("์ด ์ˆ˜์ต๋ฅ ์€ ${"%,.1f".format(rateOfReturn)}%์ž…๋‹ˆ๋‹ค.") + } +} diff --git a/src/test/kotlin/lotto/LottoTest.kt b/src/test/kotlin/lotto/LottoTest.kt index 11d85ac2c..730ad49c3 100644 --- a/src/test/kotlin/lotto/LottoTest.kt +++ b/src/test/kotlin/lotto/LottoTest.kt @@ -1,5 +1,6 @@ package lotto +import lotto.Model.Lotto import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows