diff --git a/README.md b/README.md index e591a7678..0331793eb 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,5 @@ ๐Ÿš€ 3๋‹จ๊ณ„ - ๋กœ๋˜(2๋“ฑ) ## ๊ตฌํ˜„ ๊ธฐ๋Šฅ ๋ชฉ๋ก -- [x] 2๋“ฑ์„ ์œ„ํ•ด ์ถ”๊ฐ€ ๋ฒˆํ˜ธ๋ฅผ ํ•˜๋‚˜ ๋” ์ถ”์ฒจํ•œ๋‹ค. -- [x] ๋‹น์ฒจ ํ†ต๊ณ„์— 2๋“ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค. +- [x] ํ˜„์žฌ ๋กœ๋˜ ์ƒ์„ฑ๊ธฐ๋Š” ์ž๋™ ์ƒ์„ฑ ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณตํ•œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ˆ˜๋™์œผ๋กœ ์ถ”์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค +- [x] ์ž…๋ ฅํ•œ ๊ธˆ์•ก, ์ž๋™ ์ƒ์„ฑ ์ˆซ์ž, ์ˆ˜๋™ ์ƒ์„ฑ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค. diff --git a/src/main/kotlin/lotto/controller/LottoController.kt b/src/main/kotlin/lotto/controller/LottoController.kt index b36203a67..7cc965e22 100644 --- a/src/main/kotlin/lotto/controller/LottoController.kt +++ b/src/main/kotlin/lotto/controller/LottoController.kt @@ -1,18 +1,23 @@ package lotto.controller import lotto.domain.LottoPrice +import lotto.domain.LottoPurchaseManager import lotto.service.LottoService import lotto.view.InputView import lotto.view.ResultView fun main() { - val purchaseAmount = InputView().readPurchaseAmount() - val lottos = LottoService().purchase(LottoPrice(purchaseAmount)) - ResultView().printPurchaseResult(lottos) + val purchaseAmount = LottoPrice(InputView().readPurchaseAmount()) + + val manualCount = InputView().readManualLottoCount() + val manualLottos = InputView().readManualLottoNumbers(manualCount) + + val lottos = LottoService(LottoPurchaseManager()).purchase(purchaseAmount, manualLottos) + ResultView().printPurchaseResult(manualCount, lottos) val winningNumbers = InputView().readWinningNumbers() val bonusBall = InputView().readBonusBall(winningNumbers) - val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusBall) + val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusBall) ResultView().printWinningStatistics(winningResult) ResultView().printProfitRate(winningResult.calculateProfitRate(purchaseAmount)) diff --git a/src/main/kotlin/lotto/domain/LottoFactory.kt b/src/main/kotlin/lotto/domain/LottoFactory.kt index 9c04073fd..21f076978 100644 --- a/src/main/kotlin/lotto/domain/LottoFactory.kt +++ b/src/main/kotlin/lotto/domain/LottoFactory.kt @@ -3,9 +3,8 @@ package lotto.domain import Lottos object LottoFactory { - fun create(price: LottoPrice): Lottos { - val purchaseCount = price.calculatePurchaseCount() - val tickets = (1..purchaseCount).map { LottoRandomGenerator.randomGenerate() } + fun create(count: Int): Lottos { + val tickets = (1..count).map { LottoRandomGenerator.randomGenerate() } return Lottos(tickets) } } diff --git a/src/main/kotlin/lotto/domain/LottoPrice.kt b/src/main/kotlin/lotto/domain/LottoPrice.kt index b3c5af4fa..fd2bc61ad 100644 --- a/src/main/kotlin/lotto/domain/LottoPrice.kt +++ b/src/main/kotlin/lotto/domain/LottoPrice.kt @@ -1,11 +1,14 @@ package lotto.domain @JvmInline -value class LottoPrice(val money: Int) { +value class LottoPrice(private val money: Int) { init { require(money >= LottoConstants.LOTTO_PRICE) { "๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์€ 1000์› ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค." } } + val value: Int + get() = money + fun calculatePurchaseCount(): Int { return money / LottoConstants.LOTTO_PRICE } diff --git a/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt b/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt new file mode 100644 index 000000000..ae36c4ebd --- /dev/null +++ b/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt @@ -0,0 +1,17 @@ +package lotto.domain + +import Lottos + +class LottoPurchaseManager { + fun purchase( + price: LottoPrice, + manualLottos: Lottos, + ): Lottos { + val totalCount = price.calculatePurchaseCount() + val autoCount = totalCount - manualLottos.size + require(autoCount >= 0) { "์ˆ˜๋™ ๊ตฌ๋งค ๊ฐœ์ˆ˜๊ฐ€ ์ด ๊ตฌ๋งค ๊ฐ€๋Šฅ ๊ฐœ์ˆ˜๋ฅผ ์ดˆ๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค." } + + val autoLottos = LottoFactory.create(autoCount) + return Lottos(manualLottos + autoLottos) + } +} diff --git a/src/main/kotlin/lotto/domain/Lottos.kt b/src/main/kotlin/lotto/domain/Lottos.kt index dd59384b6..baf4cfc6e 100644 --- a/src/main/kotlin/lotto/domain/Lottos.kt +++ b/src/main/kotlin/lotto/domain/Lottos.kt @@ -19,7 +19,16 @@ class Lottos(private val tickets: List) { ): Map { return tickets .map { it.match(winningNumber, bonusBall) } - .groupBy { it } - .mapValues { it.value.size } + .groupingBy { + it + }.eachCount() + } + + operator fun plus(lotto: Lottos): List { + return tickets + lotto.tickets + } + + companion object { + fun from(lottos: List): Lottos = Lottos(lottos) } } diff --git a/src/main/kotlin/lotto/domain/WinningResult.kt b/src/main/kotlin/lotto/domain/WinningResult.kt index 07d167150..089668393 100644 --- a/src/main/kotlin/lotto/domain/WinningResult.kt +++ b/src/main/kotlin/lotto/domain/WinningResult.kt @@ -11,8 +11,8 @@ class WinningResult(private val winningStatistics: Map) { } } - fun calculateProfitRate(purchaseAmount: Int): Double { - return calculateTotalPrize().toDouble() / purchaseAmount + fun calculateProfitRate(purchaseAmount: LottoPrice): Double { + return calculateTotalPrize().toDouble() / purchaseAmount.value } fun getWinningCount(rank: Rank): Int { diff --git a/src/main/kotlin/lotto/service/LottoService.kt b/src/main/kotlin/lotto/service/LottoService.kt index 741cb4a65..e4f8d11de 100644 --- a/src/main/kotlin/lotto/service/LottoService.kt +++ b/src/main/kotlin/lotto/service/LottoService.kt @@ -2,15 +2,18 @@ package lotto.service import Lottos import lotto.domain.Lotto -import lotto.domain.LottoFactory import lotto.domain.LottoNumber import lotto.domain.LottoPrice +import lotto.domain.LottoPurchaseManager import lotto.domain.WinningLotto import lotto.domain.WinningResult -class LottoService { - fun purchase(price: LottoPrice): Lottos { - return LottoFactory.create(price) +class LottoService(private val purchaseManager: LottoPurchaseManager) { + fun purchase( + price: LottoPrice, + manualLottos: Lottos, + ): Lottos { + return purchaseManager.purchase(price, manualLottos) } fun checkWinning( diff --git a/src/main/kotlin/lotto/view/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt index 005b166e7..ff8e96d7e 100644 --- a/src/main/kotlin/lotto/view/InputView.kt +++ b/src/main/kotlin/lotto/view/InputView.kt @@ -1,5 +1,6 @@ package lotto.view +import Lottos import lotto.domain.Lotto import lotto.domain.LottoNumber @@ -29,4 +30,23 @@ class InputView { require(!winningNumbers.numbers.contains(bonusBallNumber)) { "๋ณด๋„ˆ์Šค ๋ณผ์€ ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค." } return bonusBallNumber } + + fun readManualLottoCount(): Int { + println("\n์ˆ˜๋™์œผ๋กœ ๊ตฌ๋งคํ•  ๋กœ๋˜ ์ˆ˜๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.") + return readln().toInt() + } + + fun readManualLottoNumbers(count: Int): Lottos { + println("\n์ˆ˜๋™์œผ๋กœ ๊ตฌ๋งคํ•  ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.") + + val manualLottos = + (1..count).map { + val numbers = + readln().split(",") + .map { it.trim().toInt() } + .map { LottoNumber(it) } + Lotto(numbers) + } + return Lottos.from(manualLottos) + } } diff --git a/src/main/kotlin/lotto/view/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt index cedfd2279..aa57d2ad8 100644 --- a/src/main/kotlin/lotto/view/ResultView.kt +++ b/src/main/kotlin/lotto/view/ResultView.kt @@ -5,9 +5,13 @@ import lotto.domain.Rank import lotto.domain.WinningResult class ResultView { - fun printPurchaseResult(lottos: Lottos) { - println("${lottos.size}๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.") - lottos.lottos.forEach { println(it) } + fun printPurchaseResult( + manualCount: Int, + lottos: Lottos, + ) { + val autoCount = lottos.size - manualCount + println("\n์ˆ˜๋™์œผ๋กœ ${manualCount}์žฅ, ์ž๋™์œผ๋กœ ${autoCount}์žฅ์„ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.") + lottos.lottos.forEach { println(it.numbers.map { num -> num.number }) } } fun printWinningStatistics(winningResult: WinningResult) { diff --git a/src/test/kotlin/lotto/LottoGameTest.kt b/src/test/kotlin/lotto/LottoGameTest.kt index 6ed1335ba..3b9978f8e 100644 --- a/src/test/kotlin/lotto/LottoGameTest.kt +++ b/src/test/kotlin/lotto/LottoGameTest.kt @@ -4,6 +4,7 @@ import Lottos import lotto.domain.Lotto import lotto.domain.LottoNumber import lotto.domain.LottoPrice +import lotto.domain.LottoPurchaseManager import lotto.domain.Rank import lotto.service.LottoService import org.assertj.core.api.Assertions.assertThat @@ -17,12 +18,25 @@ class LottoGameTest { } @ParameterizedTest - @CsvSource("1000", "2000", "3000") - fun `๊ตฌ๋งคํ•œ ๋กœ๋˜ ๋งŒํผ ๋žœ๋คํ•œ ๋ฒˆํ˜ธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค`(money: Int) { + @CsvSource( + "2000, 1", + "3000, 2", + ) + fun `์ˆ˜๋™๊ณผ ์ž๋™ ๋กœ๋˜๋ฅผ ํ•ฉ์ณ์„œ ๊ตฌ๋งค ๊ธˆ์•ก๋งŒํผ ๋กœ๋˜๋ฅผ ์ƒ์„ฑํ•œ๋‹ค`( + money: Int, + manualCount: Int, + ) { val lottoPrice = LottoPrice(money) - val purchasedLottos = LottoService().purchase(lottoPrice) + val manualLottos = + List(manualCount) { + Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6)) + } + + val purchasedLottos = LottoService(LottoPurchaseManager()).purchase(lottoPrice, Lottos(manualLottos)) assertThat(purchasedLottos.size).isEqualTo(lottoPrice.calculatePurchaseCount()) + assertThat(purchasedLottos.lottos.take(manualCount)) + .containsExactlyElementsOf(manualLottos) purchasedLottos.lottos.forEach { assertThat(it.numbers.distinct().size).isEqualTo(6) @@ -58,7 +72,7 @@ class LottoGameTest { val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6)) val bonusNumber = LottoNumber(7) - val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber) + val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber) assertThat(winningResult.getWinningCount(Rank.NONE)).isEqualTo(0) } @@ -77,7 +91,7 @@ class LottoGameTest { val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6)) val bonusNumber = LottoNumber(7) - val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber) + val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber) assertThat(winningResult.getWinningCount(Rank.FIRST)).isEqualTo(1) assertThat(winningResult.getWinningCount(Rank.SECOND)).isEqualTo(1) @@ -96,7 +110,7 @@ class LottoGameTest { val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6)) val bonusNumber = LottoNumber(7) - val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber) + val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber) assertThat(winningResult.getWinningCount(Rank.FIFTH)).isEqualTo(0) assertThat(winningResult.getWinningCount(Rank.FOURTH)).isEqualTo(0) @@ -118,7 +132,7 @@ class LottoGameTest { val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6)) val bonusNumber = LottoNumber(7) - val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber) + val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber) assertThat(winningResult.getWinningCount(Rank.FIRST)).isEqualTo(1) assertThat(winningResult.getWinningCount(Rank.SECOND)).isEqualTo(1) diff --git a/src/test/kotlin/lotto/WinningResultTest.kt b/src/test/kotlin/lotto/WinningResultTest.kt index 90eee886b..aef7fb334 100644 --- a/src/test/kotlin/lotto/WinningResultTest.kt +++ b/src/test/kotlin/lotto/WinningResultTest.kt @@ -1,5 +1,6 @@ package lotto +import lotto.domain.LottoPrice import lotto.domain.Rank import lotto.domain.WinningResult import org.assertj.core.api.Assertions.assertThat @@ -47,7 +48,7 @@ class WinningResultTest { @Test fun `์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•œ๋‹ค`() { val winningStatistics = mapOf(Rank.FOURTH to 1) - val purchaseAmount = 10000 + val purchaseAmount = LottoPrice(10000) val winningResult = WinningResult(winningStatistics) val profitRate = winningResult.calculateProfitRate(purchaseAmount) assertThat(profitRate).isEqualTo(0.5) diff --git a/src/test/kotlin/lotto/domain/RankTest.kt b/src/test/kotlin/lotto/domain/RankTest.kt new file mode 100644 index 000000000..fbbc07bed --- /dev/null +++ b/src/test/kotlin/lotto/domain/RankTest.kt @@ -0,0 +1,47 @@ +package lotto.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class RankTest { + @Test + fun `6๊ฐœ ๋งž์ถ”๋ฉด 1๋“ฑ์ด๋‹ค`() { + val rank = Rank.from(matchCount = 6, matchBonus = false) + assertThat(rank).isEqualTo(Rank.FIRST) + } + + @Test + fun `5๊ฐœ์™€ ๋ณด๋„ˆ์Šค ๋ณผ์„ ๋งž์ถ”๋ฉด 2๋“ฑ์ด๋‹ค`() { + val rank = Rank.from(matchCount = 5, matchBonus = true) + assertThat(rank).isEqualTo(Rank.SECOND) + } + + @Test + fun `5๊ฐœ ๋งž์ถ”๊ณ  ๋ณด๋„ˆ์Šค ๋ณผ์„ ๋ชป ๋งž์ถ”๋ฉด 3๋“ฑ์ด๋‹ค`() { + val rank = Rank.from(matchCount = 5, matchBonus = false) + assertThat(rank).isEqualTo(Rank.THIRD) + } + + @Test + fun `4๊ฐœ ๋งž์ถ”๋ฉด 4๋“ฑ์ด๋‹ค`() { + val rank = Rank.from(matchCount = 4, matchBonus = false) + assertThat(rank).isEqualTo(Rank.FOURTH) + } + + @Test + fun `3๊ฐœ ๋งž์ถ”๋ฉด 5๋“ฑ์ด๋‹ค`() { + val rank = Rank.from(matchCount = 3, matchBonus = false) + assertThat(rank).isEqualTo(Rank.FIFTH) + } + + @Test + fun `2๊ฐœ ์ดํ•˜ ๋งž์ถ”๋ฉด ๋ฏธ๋‹น์ฒจ์ด๋‹ค`() { + val rank2 = Rank.from(matchCount = 2, matchBonus = false) + val rank1 = Rank.from(matchCount = 1, matchBonus = false) + val rank0 = Rank.from(matchCount = 0, matchBonus = false) + + assertThat(rank2).isEqualTo(Rank.NONE) + assertThat(rank1).isEqualTo(Rank.NONE) + assertThat(rank0).isEqualTo(Rank.NONE) + } +}