diff --git a/README.md b/README.md index 36f6adee56..5a4fa3398a 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,10 @@ ## lotto ### 기능 구현 사항 -1. 금액에 맞게 로또를 뽑아야한다 (ex. 14000원 14개를 뽑아야함) -2. 1 ~ 45 사이의 숫자를 랜덤으로 6개를 만들어서 로또를 만들어야한다 -3. 정답지와 얼마나 일치하는지에 대한 결과를 만들어야 한다 -4. 수익률 계산을 해야한다 -5. 수익이 얼마가 발생했는지 알아야 한다 \ No newline at end of file +- 금액에 맞게 로또를 뽑아야한다 (ex. 14000원 14개를 뽑아야함) +- 1 ~ 45 사이의 숫자를 랜덤으로 6개를 만들어서 로또를 만들어야한다 +- 정답지와 얼마나 일치하는지에 대한 결과를 만들어야 한다 + - 당첨번호를 맞출때 보너스 번호가 맞는지도 확인해야함 +- 수익률 계산을 해야한다 +- 수익이 얼마가 발생했는지 알아야 한다 + - 당첨번호 5개 일치 + 보너스 번호 일치인 경우에 대해서 수익을 계산해야한다 \ No newline at end of file diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index 2fb1e29055..b083f1233d 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -2,6 +2,7 @@ package lotto import lotto.domain.LottoAnswer import lotto.domain.LottoService +import lotto.domain.MatchCount import lotto.view.InputView import lotto.view.OutputView @@ -11,8 +12,15 @@ fun main() { OutputView.outputBuyResult(service.lottoCount, service.lotteries) val answer = InputView.getLottoAnswer() - val lottoAnswer = LottoAnswer(answer) - val earningStrategy = mapOf(3 to 5000, 4 to 50000, 5 to 1500000, 6 to 2000000000) + val bonusNumber = InputView.getBonusNumber() + val lottoAnswer = LottoAnswer(answer, bonusNumber) + val earningStrategy = mapOf( + MatchCount.THREE to 5000, + MatchCount.FOUR to 50000, + MatchCount.FIVE to 1500000, + MatchCount.FIVE_WITH_BONUS to 30000000, + MatchCount.SIX to 2000000000 + ) val result = service.play(lottoAnswer, earningStrategy) OutputView.outputLottoResult(result, earningStrategy) diff --git a/src/main/kotlin/lotto/domain/Earning.kt b/src/main/kotlin/lotto/domain/Earning.kt index 723845ae42..43353787db 100644 --- a/src/main/kotlin/lotto/domain/Earning.kt +++ b/src/main/kotlin/lotto/domain/Earning.kt @@ -2,10 +2,10 @@ package lotto.domain @JvmInline value class Earning( - private val strategy: Map + private val strategy: Map ) { - fun calculate(result: Map): Int = result + fun calculate(result: Map): Int = result .filter { strategy.containsKey(it.key) } .map { (strategy[it.key]?.times(it.value) ?: 0) } .reduceOrNull { acc, i -> acc + i } ?: 0 diff --git a/src/main/kotlin/lotto/domain/LottoAnswer.kt b/src/main/kotlin/lotto/domain/LottoAnswer.kt index d7e870eef8..78c24707af 100644 --- a/src/main/kotlin/lotto/domain/LottoAnswer.kt +++ b/src/main/kotlin/lotto/domain/LottoAnswer.kt @@ -1,19 +1,25 @@ package lotto.domain -@JvmInline -value class LottoAnswer( - private val answer: List +data class LottoAnswer( + private val answer: List, + private val bonusNumber: Int ) { - fun match(inputLottos: List): Map { + fun match(inputLottos: List): Map { return inputLottos .map { innerMatch(it) } .groupingBy { it } .eachCount() } - private fun innerMatch(inputLotto: Lotto): Int { - return inputLotto.numbers.count { outer -> isAnswerNumberMatch(outer) } + private fun innerMatch(inputLotto: Lotto): MatchCount { + val count = inputLotto.numbers.count { outer -> isAnswerNumberMatch(outer) } + val isBonusMatch = isBonusMatch(inputLotto) + return MatchCount.of(count, isBonusMatch) + } + + private fun isBonusMatch(inputLotto: Lotto): Boolean { + return inputLotto.numbers.find { bonusNumber == it } != null } private fun isAnswerNumberMatch(outer: Int): Boolean { @@ -21,6 +27,6 @@ value class LottoAnswer( } companion object { - fun create(answer: List) = LottoAnswer(answer) + fun create(answer: List, bonusNumber: Int) = LottoAnswer(answer, bonusNumber) } } diff --git a/src/main/kotlin/lotto/domain/LottoResult.kt b/src/main/kotlin/lotto/domain/LottoResult.kt index ec9dc1856c..71c9882b55 100644 --- a/src/main/kotlin/lotto/domain/LottoResult.kt +++ b/src/main/kotlin/lotto/domain/LottoResult.kt @@ -4,5 +4,5 @@ import java.math.BigDecimal data class LottoResult( val earningRate: BigDecimal, - val earnResult: Map + val earnResult: Map ) diff --git a/src/main/kotlin/lotto/domain/LottoService.kt b/src/main/kotlin/lotto/domain/LottoService.kt index 478d952d74..19550a0b33 100644 --- a/src/main/kotlin/lotto/domain/LottoService.kt +++ b/src/main/kotlin/lotto/domain/LottoService.kt @@ -9,7 +9,7 @@ class LottoService( ) { val lottoCount = lotteries.size - fun play(answer: LottoAnswer, earningStrategy: Map): LottoResult { + fun play(answer: LottoAnswer, earningStrategy: Map): LottoResult { val result = answer.match(lotteries) val earning = Earning(earningStrategy).calculate(result) val earningRate = EarningRate { earningRate -> diff --git a/src/main/kotlin/lotto/domain/MatchCount.kt b/src/main/kotlin/lotto/domain/MatchCount.kt new file mode 100644 index 0000000000..d3c0ec4f25 --- /dev/null +++ b/src/main/kotlin/lotto/domain/MatchCount.kt @@ -0,0 +1,25 @@ +package lotto.domain + +enum class MatchCount( + val matchCount: Int, + val isMatchBonus: Boolean +) { + ZERO(0, false), + ONE(1, false), + TWO(2, false), + THREE(3, false), + FOUR(4, false), + FIVE(5, false), + FIVE_WITH_BONUS(5, true), + SIX(6, false); + + companion object { + fun of(count: Int, isMatchBonus: Boolean): MatchCount { + val matchCount = values().find { it.matchCount == count } ?: throw IllegalArgumentException("해당하는 매치 카운트가 없습니다.") + if (matchCount == FIVE && isMatchBonus) { + return FIVE_WITH_BONUS + } + return matchCount + } + } +} diff --git a/src/main/kotlin/lotto/view/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt index a39d954d6b..fee26e860e 100644 --- a/src/main/kotlin/lotto/view/InputView.kt +++ b/src/main/kotlin/lotto/view/InputView.kt @@ -11,4 +11,9 @@ object InputView { println("지난 주 당첨 번호를 입력해 주세요.") return readln().split(", ").map { it.toInt() } } + + fun getBonusNumber(): Int { + println("보너스 볼을 입력해 주세요.") + return readln().toInt() + } } diff --git a/src/main/kotlin/lotto/view/OutputView.kt b/src/main/kotlin/lotto/view/OutputView.kt index 7197fa716b..3ae1ce4639 100644 --- a/src/main/kotlin/lotto/view/OutputView.kt +++ b/src/main/kotlin/lotto/view/OutputView.kt @@ -2,25 +2,33 @@ package lotto.view import lotto.domain.Lotto import lotto.domain.LottoResult +import lotto.domain.MatchCount object OutputView { fun outputBuyResult(lottoCount: Int, lotteries: List) { println("${lottoCount}개를 구매했습니다.") lotteries.forEach { - println(it.numbers) + println(it.numbers.sorted()) } println() } - fun outputLottoResult(result: LottoResult, strategy: Map) { + fun outputLottoResult(result: LottoResult, strategy: Map) { println() println("당첨 통계") println("---------") strategy.entries - .sortedBy { it.key } - .map { "${it.key}개 일치 (${it.value}원)- ${result.earnResult[it.key] ?: 0}개" } + .sortedBy { it.key.matchCount } + .map { outputLottoResultSeperate(it.key, it.value, result.earnResult) } .forEach { println(it) } println("총 수익률은 ${result.earningRate}입니다.") } + + private fun outputLottoResultSeperate(matchCount: MatchCount, amount: Int, earnResult: Map): String { + if (matchCount.isMatchBonus) { + return "${matchCount.matchCount}개 일치, 보너스 볼 일치(${amount}원)- ${earnResult[matchCount] ?: 0}개" + } + return "${matchCount.matchCount}개 일치 (${amount}원)- ${earnResult[matchCount] ?: 0}개" + } } diff --git a/src/test/kotlin/lotto/EarningTest.kt b/src/test/kotlin/lotto/EarningTest.kt index 873bf94087..7861cf21a6 100644 --- a/src/test/kotlin/lotto/EarningTest.kt +++ b/src/test/kotlin/lotto/EarningTest.kt @@ -5,14 +5,23 @@ import io.kotest.data.Row2 import io.kotest.data.forAll import io.kotest.matchers.shouldBe import lotto.domain.Earning +import lotto.domain.MatchCount class EarningTest : StringSpec({ "정답 결과에 따라서 알맞은 수익이 발생해야한다" { - val earning = Earning(mapOf(3 to 5000, 4 to 50000, 5 to 1500000, 6 to 2000000000)) + val earning = Earning( + mapOf( + MatchCount.THREE to 5000, + MatchCount.FOUR to 50000, + MatchCount.FIVE to 1500000, + MatchCount.FIVE_WITH_BONUS to 30000000, + MatchCount.SIX to 2000000000 + ) + ) forAll( - Row2(mapOf(3 to 1), 5000), - Row2(mapOf(3 to 1, 4 to 2), 105000) + Row2(mapOf(MatchCount.THREE to 1), 5000), + Row2(mapOf(MatchCount.THREE to 1, MatchCount.FOUR to 2), 105000) ) { lottoResult, actual -> val result = earning.calculate(lottoResult) result shouldBe actual diff --git a/src/test/kotlin/lotto/LottoAnswerTest.kt b/src/test/kotlin/lotto/LottoAnswerTest.kt index 17e1424635..0854df5057 100644 --- a/src/test/kotlin/lotto/LottoAnswerTest.kt +++ b/src/test/kotlin/lotto/LottoAnswerTest.kt @@ -4,12 +4,21 @@ import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import lotto.domain.Lotto import lotto.domain.LottoAnswer +import lotto.domain.MatchCount class LottoAnswerTest : StringSpec({ "정답지와 3개가 일치하는 로또가 1개만 존재할 경우 { 3 : 1 } 결과가 나와야 한다" { - val answer = LottoAnswer.create(listOf(1, 2, 3, 4, 5, 6)) + val bonusNumber = 0 + val answer = LottoAnswer.create(listOf(1, 2, 3, 4, 5, 6), bonusNumber) val inputLotto = Lotto.create(TestNumberGenerator) - answer.match(listOf(inputLotto)) shouldBe mapOf(3 to 1) + answer.match(listOf(inputLotto)) shouldBe mapOf(MatchCount.THREE to 1) + } + + "정답지와 5개가 일치하고 보너스 번호가 일치하는 로또가 존재할 경우 { FIVE_WITH_BONUS : 1} 결과가 나와야 한다" { + val bonusNumber = 7 + val answer = LottoAnswer.create(listOf(1, 2, 3, 4, 5, 6), bonusNumber) + val inputLotto = Lotto.create(TestNumberGeneratorFive) + answer.match(listOf(inputLotto)) shouldBe mapOf(MatchCount.FIVE_WITH_BONUS to 1) } }) diff --git a/src/test/kotlin/lotto/TestNumberGeneratorFive.kt b/src/test/kotlin/lotto/TestNumberGeneratorFive.kt new file mode 100644 index 0000000000..ea36a73661 --- /dev/null +++ b/src/test/kotlin/lotto/TestNumberGeneratorFive.kt @@ -0,0 +1,7 @@ +package lotto + +import lotto.domain.strategy.NumberGenerator + +object TestNumberGeneratorFive : NumberGenerator { + override fun generate(size: Int): List = listOf(1, 2, 3, 4, 5, 7) +}