diff --git a/README.md b/README.md index f7c43182ff..6b715089ef 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,15 @@ 로또 (Data) - [O] 로또 번호의 조합을 가지고 있는다. +당첨 로또 +- [O] 당첨 번호 일치 개수 반환 +- [O] 보너스 번호 일치 검증 +- [] 당첨번호 & 보너스 번호 중복 + +로또 계산기 +- [O] 수익률 반환 +- [O] 게임 횟수 반환 + 로또 머신 (object) - [O] 입력받은 번호로 구성된 로또로 생성한다. - [O] 당첨 번호 생성(with 보너스 번호) diff --git a/src/main/kotlin/lotto/LottoMain.kt b/src/main/kotlin/lotto/LottoMain.kt index cfdcff828e..116cc60abb 100644 --- a/src/main/kotlin/lotto/LottoMain.kt +++ b/src/main/kotlin/lotto/LottoMain.kt @@ -1,6 +1,5 @@ package lotto -import lotto.data.LottoNumber import lotto.domain.LottoMachine import lotto.domain.RandomLogic import lotto.service.LottoGame diff --git a/src/main/kotlin/lotto/data/LottoRanking.kt b/src/main/kotlin/lotto/data/LottoRanking.kt index 92f6bb9bc3..33f7351c12 100644 --- a/src/main/kotlin/lotto/data/LottoRanking.kt +++ b/src/main/kotlin/lotto/data/LottoRanking.kt @@ -1,36 +1,34 @@ package lotto.data enum class LottoRanking(val matchingNumberCnt: Int, val price: Int) : Prize { - FirstPlace(6, 2_000_000_000) { - override fun findPrize(winningStatus: Pair): Int { - return winningStatus.first.price * winningStatus.second - } - }, - SecondPlace(5, 30_000_000) { - override fun findPrize(winningStatus: Pair): Int { - return winningStatus.first.price * winningStatus.second - } - }, - ThirdPlace(5, 1_500_000) { - override fun findPrize(winningStatus: Pair): Int { - return winningStatus.first.price * winningStatus.second - } - }, - FourthPlace(4, 50_000) { - override fun findPrize(winningStatus: Pair): Int { - return winningStatus.first.price * winningStatus.second - } - }, - FifthPlace(3, 5_000) { - override fun findPrize(winningStatus: Pair): Int { - return winningStatus.first.price * winningStatus.second + FirstPlace(6, 2_000_000_000), + SecondPlace(5, 30_000_000), + ThirdPlace(5, 1_500_000), + FourthPlace(4, 50_000), + FifthPlace(3, 5_000), + None(0, 0); + + override fun findPrize(winningStatus: Pair): Int { + return winningStatus.first.price * winningStatus.second + } + + companion object { + fun findLottoRanking(matchingNumberCnt: Int, hasBonusNumber: Boolean): LottoRanking { + return if (matchingNumberCnt == SecondPlace.matchingNumberCnt) { + determineRankingWithBonusNumber(hasBonusNumber) + } else { + LottoRanking.values().find { it.matchingNumberCnt == matchingNumberCnt } ?: None + } } - }, - None(0, 0) { - override fun findPrize(winningStatus: Pair): Int { - return winningStatus.first.price * winningStatus.second + + private fun determineRankingWithBonusNumber(isContainBonusNumber: Boolean): LottoRanking { + return if (isContainBonusNumber) { + SecondPlace + } else { + ThirdPlace + } } - }; + } } fun interface Prize { diff --git a/src/main/kotlin/lotto/data/WinningLotto.kt b/src/main/kotlin/lotto/data/WinningLotto.kt index 9eb3c2d2f6..4faded4b37 100644 --- a/src/main/kotlin/lotto/data/WinningLotto.kt +++ b/src/main/kotlin/lotto/data/WinningLotto.kt @@ -1,3 +1,24 @@ package lotto.data -data class WinningLotto(val lotto: Lotto, val bonusNumber: LottoNumber) +data class WinningLotto(val lotto: Lotto, val bonusNumber: LottoNumber) { + + init { + validateWinningLotto() + } + + fun countMatchingNumbers(lotto: Lotto): Int { + return this.lotto.selectNumbers.intersect(lotto.selectNumbers).size + } + + fun hasBonusNumber(lotto: Lotto): Boolean { + return lotto.selectNumbers.contains(bonusNumber) + } + + private fun validateWinningLotto() { + validateDuplicationBonusNumber() + } + + private fun validateDuplicationBonusNumber() { + require(!lotto.selectNumbers.contains(bonusNumber)) { "당첨 번호 구성과 보너스 번호가 중복됩니다." } + } +} diff --git a/src/main/kotlin/lotto/domain/LottoCalculator.kt b/src/main/kotlin/lotto/domain/LottoCalculator.kt new file mode 100644 index 0000000000..2215a099fd --- /dev/null +++ b/src/main/kotlin/lotto/domain/LottoCalculator.kt @@ -0,0 +1,18 @@ +package lotto.domain + +import lotto.data.LottoRanking + +object LottoCalculator { + + private const val GAME_COST = 1000 + + fun calculateWinningRate(cash: Int, winningStatus: Map): Float { + val totalPrice = winningStatus.toList().sumOf { it.first.findPrize(it) } + + return totalPrice / cash.toFloat() + } + + fun getTimes(cash: Int): Int { + return cash / GAME_COST + } +} diff --git a/src/main/kotlin/lotto/domain/LottoMachine.kt b/src/main/kotlin/lotto/domain/LottoMachine.kt index d323cd0b18..7fd4f83370 100644 --- a/src/main/kotlin/lotto/domain/LottoMachine.kt +++ b/src/main/kotlin/lotto/domain/LottoMachine.kt @@ -3,12 +3,6 @@ package lotto.domain import lotto.data.Lotto import lotto.data.LottoNumber import lotto.data.LottoRanking -import lotto.data.LottoRanking.FifthPlace -import lotto.data.LottoRanking.FirstPlace -import lotto.data.LottoRanking.FourthPlace -import lotto.data.LottoRanking.None -import lotto.data.LottoRanking.SecondPlace -import lotto.data.LottoRanking.ThirdPlace import lotto.data.WinningLotto object LottoMachine { @@ -24,34 +18,13 @@ object LottoMachine { } fun checkLotto(purchaseLotto: Lotto, winningLotto: WinningLotto): LottoRanking { - val intersectNumber = winningLotto.lotto.selectNumbers.intersect(purchaseLotto.selectNumbers) + val matchingNumberCnt = winningLotto.countMatchingNumbers(purchaseLotto) + val hasBonusNumber = winningLotto.hasBonusNumber(purchaseLotto) - return when (intersectNumber.size) { - FirstPlace.matchingNumberCnt -> FirstPlace - SecondPlace.matchingNumberCnt, ThirdPlace.matchingNumberCnt -> { - checkSecondPlace(purchaseLotto, winningLotto.bonusNumber) - } - FourthPlace.matchingNumberCnt -> FourthPlace - FifthPlace.matchingNumberCnt -> FifthPlace - else -> None - } + return LottoRanking.findLottoRanking(matchingNumberCnt, hasBonusNumber) } fun createWinningRate(cash: Int, winningStatus: Map): Float { - val totalPrice = createTotalWinningPrice(winningStatus) - - return totalPrice / cash.toFloat() - } - - private fun checkSecondPlace(purchaseLotto: Lotto, bonusLottoNumber: LottoNumber): LottoRanking { - return if (purchaseLotto.selectNumbers.contains(bonusLottoNumber)) { - SecondPlace - } else { - ThirdPlace - } - } - - private fun createTotalWinningPrice(winningStatus: Map): Int { - return winningStatus.toList().sumOf { it.first.findPrize(it) } + return LottoCalculator.calculateWinningRate(cash, winningStatus) } } diff --git a/src/main/kotlin/lotto/service/LottoGame.kt b/src/main/kotlin/lotto/service/LottoGame.kt index 6702b4291c..547d5d8d3d 100644 --- a/src/main/kotlin/lotto/service/LottoGame.kt +++ b/src/main/kotlin/lotto/service/LottoGame.kt @@ -4,6 +4,7 @@ import lotto.data.Lotto import lotto.data.LottoNumber import lotto.data.LottoRanking import lotto.data.WinningLotto +import lotto.domain.LottoCalculator import lotto.domain.LottoMachine import lotto.domain.RandomLogicInterface import lotto.domain.WinningDomain @@ -11,14 +12,17 @@ import lotto.domain.WinningDomain class LottoGame(private val randomLogic: RandomLogicInterface) { fun buyLotto(cash: Int): List { - val lottoList = mutableListOf() - val times = cash / GAME_COST + val times = LottoCalculator.getTimes(cash) + + return createLotto(times) + } + private fun createLotto(times: Int): List { + val lottoList = mutableListOf() repeat(times) { val lottoNumberCombination = LottoNumber.createRandomLottoNumber(randomLogic) lottoList.add(LottoMachine.createSelectLotto(lottoNumberCombination)) } - return lottoList } @@ -26,8 +30,4 @@ class LottoGame(private val randomLogic: RandomLogicInterface) { return WinningDomain.checkWinningResult(winningLotto, purchaseLottoList) } - - companion object { - private const val GAME_COST = 1000 - } } diff --git a/src/test/kotlin/lotto/LottoNumberTest.kt b/src/test/kotlin/lotto/data/LottoNumberTest.kt similarity index 96% rename from src/test/kotlin/lotto/LottoNumberTest.kt rename to src/test/kotlin/lotto/data/LottoNumberTest.kt index dde22d134c..0bbdc7595e 100644 --- a/src/test/kotlin/lotto/LottoNumberTest.kt +++ b/src/test/kotlin/lotto/data/LottoNumberTest.kt @@ -1,6 +1,5 @@ -package lotto +package lotto.data -import lotto.data.LottoNumber import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/lotto/LottoTest.kt b/src/test/kotlin/lotto/data/LottoTest.kt similarity index 95% rename from src/test/kotlin/lotto/LottoTest.kt rename to src/test/kotlin/lotto/data/LottoTest.kt index c0b77e571a..a1ab83dfd3 100644 --- a/src/test/kotlin/lotto/LottoTest.kt +++ b/src/test/kotlin/lotto/data/LottoTest.kt @@ -1,7 +1,5 @@ -package lotto +package lotto.data -import lotto.data.Lotto -import lotto.data.LottoNumber import org.assertj.core.api.Assertions import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/lotto/data/WinningLottoTest.kt b/src/test/kotlin/lotto/data/WinningLottoTest.kt new file mode 100644 index 0000000000..8f9d632ec6 --- /dev/null +++ b/src/test/kotlin/lotto/data/WinningLottoTest.kt @@ -0,0 +1,58 @@ +package lotto.data + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class WinningLottoTest { + + @Test + fun `당첨 번호와 일치하는 번호 수 반환`() { + // given : 당첨로또와 로또를 받는다. + val lotto1 = Lotto(LottoNumber.createLottoNumbers(listOf(1, 2, 3, 4, 5, 6))) + val lotto2 = Lotto(LottoNumber.createLottoNumbers(listOf(1, 2, 3, 4, 5, 7))) + + // 당첨번호 [1,2,3,4,5,7] + 보너스 번호 [6] + val wLotto1 = Lotto(LottoNumber.createLottoNumbers(listOf(1, 2, 3, 4, 5, 7))) + val winningLotto = WinningLotto(wLotto1, LottoNumber.from(6)) + + // when : 검증 로직을 실행한다. + val actual1 = winningLotto.countMatchingNumbers(lotto1) + val actual2 = winningLotto.countMatchingNumbers(lotto2) + + // then : 일치하는 번호의 개수를 반환한다. + assertThat(actual1).isEqualTo(5) + assertThat(actual2).isEqualTo(6) + } + + @Test + fun `보너스 번호 일치 검증`() { + // given : 당첨로또와 로또를 받는다. + val lotto1 = Lotto(LottoNumber.createLottoNumbers(listOf(1, 2, 3, 4, 5, 6))) + val lotto2 = Lotto(LottoNumber.createLottoNumbers(listOf(1, 2, 3, 4, 5, 7))) + + // 당첨번호 [1,2,3,4,5,7] + 보너스 번호 [6] + val wLotto1 = Lotto(LottoNumber.createLottoNumbers(listOf(1, 2, 3, 4, 5, 7))) + val winningLotto = WinningLotto(wLotto1, LottoNumber.from(6)) + + // when : 보너스 번호 일치 검증 로직을 호출한다. + val actual1 = winningLotto.hasBonusNumber(lotto1) + val actual2 = winningLotto.hasBonusNumber(lotto2) + + // then : + assertThat(actual1).isTrue() + assertThat(actual2).isFalse() + } + + @Test + fun `보너스 번호 중복 검증`() { + // given : 당첨 번호로 구성된 로또와 이와 중복되는 보너스 번호를 받는다. + val lotto = Lotto(LottoNumber.createLottoNumbers(listOf(1, 2, 3, 4, 5, 6))) + val bonusLottoNumber = LottoNumber.from(6) + + // when : + val actual = runCatching { WinningLotto(lotto, bonusLottoNumber) }.exceptionOrNull() + + // then : + assertThat(actual).isInstanceOf(IllegalArgumentException::class.java) + } +} diff --git a/src/test/kotlin/lotto/domain/LottoCalculatorTest.kt b/src/test/kotlin/lotto/domain/LottoCalculatorTest.kt new file mode 100644 index 0000000000..e743b4ffbe --- /dev/null +++ b/src/test/kotlin/lotto/domain/LottoCalculatorTest.kt @@ -0,0 +1,22 @@ +package lotto.domain + +import lotto.data.LottoRanking +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class LottoCalculatorTest { + @Test + fun `로또 수익율 반환 로직`() { + // given : 구매한 로또의 통계와 구매 금액을 받는다. + // 총 당첨금 5천원 + val cash = 100000 + val winningStatus = mutableMapOf() + winningStatus[LottoRanking.FifthPlace] = 1 + + // when : 구매 금액 대비 당첨 금액에 대한 수익률은 요청한다. + val actual: Float = LottoCalculator.calculateWinningRate(cash, winningStatus) + + // then : 수익률이 반환된다. + assertThat(actual).isEqualTo(0.05f) + } +} diff --git a/src/test/kotlin/lotto/LottoMachineTest.kt b/src/test/kotlin/lotto/domain/LottoMachineTest.kt similarity index 98% rename from src/test/kotlin/lotto/LottoMachineTest.kt rename to src/test/kotlin/lotto/domain/LottoMachineTest.kt index 4899d0fdb8..9fb0d43dbd 100644 --- a/src/test/kotlin/lotto/LottoMachineTest.kt +++ b/src/test/kotlin/lotto/domain/LottoMachineTest.kt @@ -1,10 +1,9 @@ -package lotto +package lotto.domain import lotto.data.Lotto import lotto.data.LottoNumber import lotto.data.LottoRanking import lotto.data.WinningLotto -import lotto.domain.LottoMachine import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test