diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index c6c7322f7..1846c30bc 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -20,7 +20,10 @@ fun main() { val winningNumbers = inputView.getWinningNumbers() ?: return val winningLottoNumbers = winningNumbers.map { LottoNumber.get(it) } + val bonusNumber = inputView.getBonusNumber() ?: return + val bonusLottoNumber = LottoNumber.get(bonusNumber) + resultView.showResultInterface() - resultView.showMatchLottoResult(lottoBunch.getMatchLottoResult(winningLottoNumbers)) - resultView.showYield(lottoBunch.getYield(winningLottoNumbers, purchaseAmount)) + resultView.showMatchLottoResult(lottoBunch.getMatchLottoResult(winningLottoNumbers, bonusLottoNumber)) + resultView.showYield(lottoBunch.getYield(winningLottoNumbers, bonusLottoNumber, purchaseAmount)) } diff --git a/src/main/kotlin/lotto/domain/BonusMatchResult.kt b/src/main/kotlin/lotto/domain/BonusMatchResult.kt new file mode 100644 index 000000000..5c95db31c --- /dev/null +++ b/src/main/kotlin/lotto/domain/BonusMatchResult.kt @@ -0,0 +1,7 @@ +package lotto.domain + +enum class BonusMatchResult { + MATCH, + NOT_MATCH, + NO_EFFECT, +} diff --git a/src/main/kotlin/lotto/domain/Lotto.kt b/src/main/kotlin/lotto/domain/Lotto.kt index 084520b88..0bc6fa8e5 100644 --- a/src/main/kotlin/lotto/domain/Lotto.kt +++ b/src/main/kotlin/lotto/domain/Lotto.kt @@ -1,22 +1,48 @@ package lotto.domain class Lotto(private val lottoNumberGenerator: LottoNumberGenerator) { - var lottoNumbers: Set = getByAuto() + var lottoNumbers: Set private set + var bonusNumber: LottoNumber + private set + + init { + getByAuto().run { + lottoNumbers = first + bonusNumber = second + } + } - private fun getByAuto(): Set { - lottoNumberGenerator ?: return setOf() - return buildSet { while (size < 6) add(LottoNumber.get(lottoNumberGenerator.generateLottoNumber())) } + private fun getByAuto(): Pair, LottoNumber> { + val normal = buildSet { while (size < 6) add(LottoNumber.get(lottoNumberGenerator.generateLottoNumber())) } + val bonus = getBonus(normal) + return Pair(normal, bonus) } - fun setLottoByManual(vararg lottoNumber: Int) { + private fun getBonus(normal: Set): LottoNumber { + while (true) { + val stepNumber = LottoNumber.get(lottoNumberGenerator.generateLottoNumber()) + if (stepNumber !in normal) return stepNumber + } + } + + fun setLottoByManual( + bonus: Int, + vararg lottoNumber: Int, + ) { require(lottoNumber.size == LOTTO_NUMBER_COUNT) { LOTTO_NUMBER_COUNT_EXCEPTION_MESSAGE } require(lottoNumber.distinct().size == LOTTO_NUMBER_COUNT) { LOTTO_NUMBER_DISTINCT_MESSAGE } + require(bonus !in lottoNumber) { LOTTO_NUMBER_DISTINCT_MESSAGE } lottoNumbers = lottoNumber.map { LottoNumber.get(it) }.toSet() + bonusNumber = LottoNumber.get(bonus) } - fun match(winningNumber: List): MatchingResult? = - MatchingResult.fromMatchNumber(lottoNumbers.intersect(winningNumber).size) + fun match( + winningNumber: List, + bonusNumber: LottoNumber, + ): MatchingResult? { + return MatchingResult.getResult(lottoNumbers.intersect(winningNumber).size, bonusNumber == this.bonusNumber) + } companion object { private const val LOTTO_NUMBER_COUNT = 6 diff --git a/src/main/kotlin/lotto/domain/LottoBunch.kt b/src/main/kotlin/lotto/domain/LottoBunch.kt index 3093998bb..2fddaf1b5 100644 --- a/src/main/kotlin/lotto/domain/LottoBunch.kt +++ b/src/main/kotlin/lotto/domain/LottoBunch.kt @@ -6,16 +6,20 @@ class LottoBunch(val value: List) { lottoNumberGenerator: LottoNumberGenerator = RandomGenerator, ) : this(List(purchaseCount) { Lotto(lottoNumberGenerator) }) - fun getMatchLottoResult(winningNumbers: List): Map { - val matchResults = value.mapNotNull { lotto -> lotto.match(winningNumbers) } + fun getMatchLottoResult( + winningNumbers: List, + bonusNumber: LottoNumber, + ): Map { + val matchResults = value.mapNotNull { lotto -> lotto.match(winningNumbers, bonusNumber) } return MatchingResult.getMatchLottoResult(matchResults) } fun getYield( winningNumbers: List, + bonusNumber: LottoNumber, purchaseAmount: Int, ): Double { - val matchResults: Map = getMatchLottoResult(winningNumbers) + val matchResults: Map = getMatchLottoResult(winningNumbers, bonusNumber) val totalPrize = matchResults.entries.fold(0) { acc, (matchingResult, winCount) -> acc + (matchingResult.prizeAmount * winCount) diff --git a/src/main/kotlin/lotto/domain/MatchingResult.kt b/src/main/kotlin/lotto/domain/MatchingResult.kt index 3d4f86ad6..6ca6c84f6 100644 --- a/src/main/kotlin/lotto/domain/MatchingResult.kt +++ b/src/main/kotlin/lotto/domain/MatchingResult.kt @@ -1,16 +1,36 @@ package lotto.domain -enum class MatchingResult(val prizeAmount: Int, val matchNumber: Int) { - MATCHED_THREE(5_000, 3), - MATCHED_FOUR(50_000, 4), - MATCHED_FIVE(1_500_000, 5), - MATCHED_SIX(2_000_000_000, 6), ; +typealias MatchValues = Pair + +enum class MatchingResult(val prizeAmount: Int, val matchNumber: Int, val bonusMatchResult: BonusMatchResult) { + MATCHED_THREE(5_000, 3, BonusMatchResult.NO_EFFECT), + MATCHED_FOUR(50_000, 4, BonusMatchResult.NO_EFFECT), + MATCHED_FIVE(1_500_000, 5, BonusMatchResult.NOT_MATCH), + MATCHED_FIVE_WITH_BONUS(30_000_000, 5, BonusMatchResult.MATCH), + MATCHED_SIX(2_000_000_000, 6, BonusMatchResult.NO_EFFECT), ; companion object { - private const val MATCH_NUMBER_TRANSFER_ERROR_MESSAGE = "로또 결과 이넘값 변환 오류가 발생하였습니다." - private val matchNumberToMatchResultMap = entries.associateBy { it.matchNumber } + private const val RELATED_BONUS_MATCH_NUMBER = 5 + private val matchValuesToMatchResultMap = + entries.associateBy { result -> + MatchValues(result.matchNumber, result.bonusMatchResult) + } + + fun getResult( + matchNumber: Int, + isBonusMatched: Boolean, + ): MatchingResult? { + val bonusMatchResult = getBonusMatchResult(matchNumber, isBonusMatched) + return matchValuesToMatchResultMap[MatchValues(matchNumber, bonusMatchResult)] + } - fun fromMatchNumber(matchNumber: Int): MatchingResult? = matchNumberToMatchResultMap[matchNumber] + private fun getBonusMatchResult( + matchNumber: Int, + isBonusMatched: Boolean, + ): BonusMatchResult { + if (matchNumber != RELATED_BONUS_MATCH_NUMBER) return BonusMatchResult.NO_EFFECT + return if (isBonusMatched) BonusMatchResult.MATCH else BonusMatchResult.NOT_MATCH + } fun getMatchLottoResult(matchResults: List): Map = entries.associateWith { matchResult -> diff --git a/src/main/kotlin/lotto/view/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt index 657e58571..939a3feaf 100644 --- a/src/main/kotlin/lotto/view/InputView.kt +++ b/src/main/kotlin/lotto/view/InputView.kt @@ -31,4 +31,18 @@ class InputView { } return slicedInput.map { it.toInt() } } + + fun getBonusNumber(): Int? { + println("보너스 볼을 입력해 주세요.") + val input = readlnOrNull() + if (input.isNullOrEmpty()) { + println("아무값도 입력되지 않았습니다.") + return null + } + if (input.toIntOrNull() == null) { + println("숫자만 입력 가능합니다.") + return null + } + return input.toInt() + } } diff --git a/src/main/kotlin/lotto/view/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt index 149f10d40..d9dbe7de4 100644 --- a/src/main/kotlin/lotto/view/ResultView.kt +++ b/src/main/kotlin/lotto/view/ResultView.kt @@ -1,5 +1,6 @@ package lotto.view +import lotto.domain.BonusMatchResult import lotto.domain.LottoBunch import lotto.domain.MatchingResult @@ -21,7 +22,12 @@ class ResultView { fun showMatchLottoResult(result: Map) { result.forEach { key, value -> - println("${key.matchNumber}개 일치 (${key.prizeAmount}원)- ${value}개") + val bonusResultMessage = + when (key.bonusMatchResult) { + BonusMatchResult.MATCH -> ", 보너스 볼 일치" + BonusMatchResult.NOT_MATCH, BonusMatchResult.NO_EFFECT -> "" + } + println("${key.matchNumber}개 일치$bonusResultMessage (${key.prizeAmount}원)- ${value}개") } } diff --git "a/src/main/kotlin/lotto/\352\270\260\353\212\245\353\252\251\353\241\235.md" "b/src/main/kotlin/lotto/\352\270\260\353\212\245\353\252\251\353\241\235.md" index c37e8491e..ccc5c2286 100644 --- "a/src/main/kotlin/lotto/\352\270\260\353\212\245\353\252\251\353\241\235.md" +++ "b/src/main/kotlin/lotto/\352\270\260\353\212\245\353\252\251\353\241\235.md" @@ -9,12 +9,13 @@ - [x] 몇등 당첨인지 확인한다. - [x] 수익률을 계산한다. - [x] 로또 번호는 1~45사이로 한정한다. +- [ ] 보너스 점수가 있다. ## 뷰 ### 입력 -- [ ] 구입금액을 입력받는다. -- [ ] 로또 당첨 번호를 입력받는다. +- [x] 구입금액을 입력받는다. +- [x] 로또 당첨 번호를 입력받는다. ### 출력 -- [ ] 구매 내역을 출력해준다.(구매 갯수,로또 내역) -- [ ] 결과를 출력해준다. \ No newline at end of file +- [x] 구매 내역을 출력해준다.(구매 갯수,로또 내역) +- [x] 결과를 출력해준다. \ No newline at end of file diff --git a/src/test/kotlin/lotto/domain/LottoBunchTest.kt b/src/test/kotlin/lotto/domain/LottoBunchTest.kt index 7a8d5b616..b5b9ab3bb 100644 --- a/src/test/kotlin/lotto/domain/LottoBunchTest.kt +++ b/src/test/kotlin/lotto/domain/LottoBunchTest.kt @@ -8,48 +8,67 @@ class LottoBunchTest : StringSpec({ val classUnderTest: LottoBunch = getLottoBunch( listOf( - listOf(1, 2, 3, 4, 5, 6), - listOf(1, 2, 3, 4, 5, 6), - listOf(1, 2, 3, 4, 5, 6), - listOf(1, 2, 3, 4, 5, 6), - listOf(1, 2, 3, 4, 5, 45), - listOf(1, 2, 3, 4, 5, 45), - listOf(1, 2, 3, 4, 5, 45), - listOf(1, 2, 3, 4, 44, 45), - listOf(1, 2, 3, 4, 44, 45), - listOf(1, 2, 3, 43, 44, 45), + Pair(listOf(1, 2, 3, 4, 5, 6), 30), + Pair(listOf(1, 2, 3, 4, 5, 6), 30), + Pair(listOf(1, 2, 3, 4, 5, 6), 30), + Pair(listOf(1, 2, 3, 4, 5, 6), 30), + Pair(listOf(1, 2, 3, 4, 5, 45), 30), + Pair(listOf(1, 2, 3, 4, 5, 45), 30), + Pair(listOf(1, 2, 3, 4, 5, 45), 30), + Pair(listOf(1, 2, 3, 4, 44, 45), 30), + Pair(listOf(1, 2, 3, 4, 44, 45), 30), + Pair(listOf(1, 2, 3, 43, 44, 45), 30), + Pair(listOf(1, 2, 3, 4, 5, 45), 15), + Pair(listOf(1, 2, 3, 4, 5, 45), 15), ), ) val winningNumbers = listOf(1, 2, 3, 4, 5, 6).map { LottoNumber.get(it) } + val bonusNumber = LottoNumber.get(15) val expected = mapOf( MatchingResult.MATCHED_THREE to 1, MatchingResult.MATCHED_FOUR to 2, MatchingResult.MATCHED_FIVE to 3, MatchingResult.MATCHED_SIX to 4, + MatchingResult.MATCHED_FIVE_WITH_BONUS to 2, ) - classUnderTest.getMatchLottoResult(winningNumbers) shouldBe expected + classUnderTest.getMatchLottoResult(winningNumbers, bonusNumber) shouldBe expected } "수익률을 계산한다." { val classUnderTest: LottoBunch = getLottoBunch( listOf( - listOf(1, 2, 3, 4, 5, 6), - listOf(1, 2, 3, 4, 5, 45), - listOf(1, 2, 3, 4, 44, 45), - listOf(1, 2, 3, 43, 44, 45), + Pair(listOf(1, 2, 3, 4, 5, 6), 30), + Pair(listOf(1, 2, 3, 4, 5, 45), 15), + Pair(listOf(1, 2, 3, 4, 5, 45), 30), + Pair(listOf(1, 2, 3, 4, 44, 45), 30), + Pair(listOf(1, 2, 3, 43, 44, 45), 30), ), ) val winningNumbers = listOf(1, 2, 3, 4, 5, 6).map { LottoNumber.get(it) } + val bonusNumber = LottoNumber.get(15) val purchaseAmount = 10000 val totalPrize = MatchingResult.entries.map { it.prizeAmount }.sum() - classUnderTest.getYield(winningNumbers, purchaseAmount) shouldBe totalPrize.toDouble() / purchaseAmount + classUnderTest.getYield( + winningNumbers, + bonusNumber, + purchaseAmount, + ) shouldBe totalPrize.toDouble() / purchaseAmount } }) { private companion object { - fun getLottoBunch(numbers: List>): LottoBunch = - LottoBunch(numbers.map { Lotto(RandomGenerator).apply { setLottoByManual(*it.toIntArray()) } }) + fun getLottoBunch(numbers: List, Int>>): LottoBunch = + LottoBunch( + numbers.map { + Lotto(RandomGenerator).apply { + setLottoByManual( + it.second, + *it.first.toIntArray(), + ) + } + }, + ) } } diff --git a/src/test/kotlin/lotto/domain/LottoTest.kt b/src/test/kotlin/lotto/domain/LottoTest.kt index dde10edb4..908781a4e 100644 --- a/src/test/kotlin/lotto/domain/LottoTest.kt +++ b/src/test/kotlin/lotto/domain/LottoTest.kt @@ -12,9 +12,14 @@ class LottoTest : StringSpec({ beforeTest { sequentialNumberGenerator = object : LottoNumberGenerator { - val lottoNumberRanger = (1..45).toMutableList() - - override fun generateLottoNumber(): Int = lottoNumberRanger.removeFirst() + var lottoNumberRanger = (1..45).toMutableList() + + override fun generateLottoNumber(): Int { + if (lottoNumberRanger.isEmpty()) { + lottoNumberRanger = (1..45).toMutableList() + } + return lottoNumberRanger.removeFirst() + } } } @@ -24,14 +29,14 @@ class LottoTest : StringSpec({ } } - "로또를 직접 초기화할때 중복되지 않은 6개의 숫자를 입력해야한다." { + "로또를 직접 초기화할때 중복되지 않은 6개의 숫자와 1개의 보너스 숫자를 입력해야한다." { listOf( - listOf(1, 2, 3, 4), - listOf(1, 1, 2, 3, 4, 5), - listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), - ).forAll { numberList -> + Pair(listOf(1, 2, 3, 4), 45), + Pair(listOf(1, 1, 2, 3, 4, 5), 45), + Pair(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 45), + ).forAll { (numberList, bonus) -> shouldThrowAny { - Lotto(sequentialNumberGenerator).setLottoByManual(*numberList.toIntArray()) + Lotto(sequentialNumberGenerator).setLottoByManual(bonus, *numberList.toIntArray()) } } } @@ -40,18 +45,19 @@ class LottoTest : StringSpec({ Lotto(sequentialNumberGenerator).lottoNumbers.size shouldBe 6 } - "각 로또의 번호를 매칭하여 결과를 도출한다." { + "각 로또의 번호 및 보너스 번호를 매칭하여 결과를 도출한다." { listOf( - Pair(listOf(45, 44, 43, 42, 41, 40), null), - Pair(listOf(1, 45, 44, 43, 42, 41), null), - Pair(listOf(1, 2, 45, 44, 43, 42), null), - Pair(listOf(1, 2, 3, 45, 44, 43), MatchingResult.MATCHED_THREE), - Pair(listOf(1, 2, 3, 4, 45, 44), MatchingResult.MATCHED_FOUR), - Pair(listOf(1, 2, 3, 4, 5, 45), MatchingResult.MATCHED_FIVE), - Pair(listOf(1, 2, 3, 4, 5, 6), MatchingResult.MATCHED_SIX), - ).forAll { (winningNumbers, matchingResult) -> - Lotto(sequentialNumberGenerator).apply { setLottoByManual(1, 2, 3, 4, 5, 6) } - .match(winningNumbers.map { LottoNumber.get(it) }) shouldBe matchingResult + Triple(30, listOf(45, 44, 43, 42, 41, 40), null), + Triple(30, listOf(1, 45, 44, 43, 42, 41), null), + Triple(30, listOf(1, 2, 45, 44, 43, 42), null), + Triple(30, listOf(1, 2, 3, 45, 44, 43), MatchingResult.MATCHED_THREE), + Triple(30, listOf(1, 2, 3, 4, 45, 44), MatchingResult.MATCHED_FOUR), + Triple(30, listOf(1, 2, 3, 4, 5, 45), MatchingResult.MATCHED_FIVE), + Triple(30, listOf(1, 2, 3, 4, 5, 45), MatchingResult.MATCHED_FIVE), + Triple(30, listOf(1, 2, 3, 4, 5, 6), MatchingResult.MATCHED_SIX), + ).forAll { (bonusNumber, winningNumbers, matchingResult) -> + Lotto(sequentialNumberGenerator).apply { setLottoByManual(15, 1, 2, 3, 4, 5, 6) } + .match(winningNumbers.map { LottoNumber.get(it) }, LottoNumber.get(bonusNumber)) shouldBe matchingResult } } })