diff --git a/README.md b/README.md index 80d326b9c8..8fd44fa149 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,18 @@ - [x] indent는 depth가 2를 넘지 않도록 구현 - ex) while 문 안에 if 문이 있으면 들여쓰기는 2이다 - [x] 함수의 길이가 10개 Line을 넘어가지 않도록 구현 - - 함수가 한 가지 일만 잘 하도록 구현 \ No newline at end of file + - 함수가 한 가지 일만 잘 하도록 구현 + + + +## 로또(자동) +### 기능 요구 사항 +- [x] 구입한 금액에 해당하는 구매 갯수 반환 +- [x] 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다 + +### 프로그래밍 요구 사항 +- [x] InputView 생성 +- [x] indent는 depth가 2를 넘지 않도록 구현 +- [x] 함수의 길이가 15 라인을 넘어가지 않도록 구현 +- [x] ResultView 생성 + diff --git a/src/main/kotlin/lotto/InputView.kt b/src/main/kotlin/lotto/InputView.kt new file mode 100644 index 0000000000..96b433a731 --- /dev/null +++ b/src/main/kotlin/lotto/InputView.kt @@ -0,0 +1,20 @@ +package lotto + +object InputView { + + fun requestPurchaseAmount(): Int { + printMessage("구입금액을 입력해 주세요.") + return readln().toInt() + } + + fun requestLastWinnerNumbers(): List { + printMessage("\n지난 주 당첨 번호를 입력해 주세요.") + return readln().split(",").map { + it.trim().toInt() + } + } + + private fun printMessage(message: String) { + println(message) + } +} diff --git a/src/main/kotlin/lotto/Lottery.kt b/src/main/kotlin/lotto/Lottery.kt new file mode 100644 index 0000000000..2fc5b67fe4 --- /dev/null +++ b/src/main/kotlin/lotto/Lottery.kt @@ -0,0 +1,5 @@ +package lotto + +data class Lottery( + val numbers: List +) diff --git a/src/main/kotlin/lotto/LotteryResult.kt b/src/main/kotlin/lotto/LotteryResult.kt new file mode 100644 index 0000000000..e76ac95cc5 --- /dev/null +++ b/src/main/kotlin/lotto/LotteryResult.kt @@ -0,0 +1,7 @@ +package lotto + +data class LotteryResult( + val prize: Int, + val matchCount: Int, + val message: String +) diff --git a/src/main/kotlin/lotto/LotteryShop.kt b/src/main/kotlin/lotto/LotteryShop.kt new file mode 100644 index 0000000000..62ce87afbe --- /dev/null +++ b/src/main/kotlin/lotto/LotteryShop.kt @@ -0,0 +1,21 @@ +package lotto + +class LotteryShop { + + fun buy(money: Int): UserLottery { + val count = money / LOTTERY_PRICE + val lotteryTickets = mutableListOf() + repeat(count) { + lotteryTickets.add( + Lottery((MIN_LOTTERY_NUMBER..MAX_LOTTERY_NUMBER).shuffled().take(6).sorted()) + ) + } + return UserLottery(count, lotteryTickets) + } + + companion object { + private const val LOTTERY_PRICE = 1000 + private const val MIN_LOTTERY_NUMBER = 1 + private const val MAX_LOTTERY_NUMBER = 45 + } +} diff --git a/src/main/kotlin/lotto/LotteryStatistic.kt b/src/main/kotlin/lotto/LotteryStatistic.kt new file mode 100644 index 0000000000..6d94541cde --- /dev/null +++ b/src/main/kotlin/lotto/LotteryStatistic.kt @@ -0,0 +1,36 @@ +package lotto + +object LotteryStatistic { + + fun getWinStatistic( + lotteryTickets: List, + lastWinnerNumbers: List + ): Pair, Double> { + val winResult = checkLotteryTickets(lotteryTickets, lastWinnerNumbers) + + val lotteryResults = mutableListOf() + var sum = 0 + winResult.forEach { (count, matchCount) -> + val prize = WinnerPrize.getPrize(count).money + val message = "${count}개 일치 (${prize}원) - ${matchCount}개" + lotteryResults.add(LotteryResult(prize, matchCount, message)) + sum += (prize * matchCount) + } + + return lotteryResults.toList() to sum.toDouble() + } + + private fun checkLotteryTickets( + lotteryTickets: List, + lastWinnerNumbers: List + ): List> = + lotteryTickets.map { lottery -> + lottery.numbers.intersect( + lastWinnerNumbers.sorted().toSet() + ).count() + }.filter { it >= 3 } + .groupingBy { it } + .eachCount() + .toList() + .sortedBy { it.first } +} diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt new file mode 100644 index 0000000000..87c064bbd0 --- /dev/null +++ b/src/main/kotlin/lotto/Main.kt @@ -0,0 +1,15 @@ +package lotto + +fun main() { + val lotteryShop = LotteryShop() + val money = InputView.requestPurchaseAmount() + val userLotteryInfo = lotteryShop.buy(money) + + ResultView.printLotteryCount(userLotteryInfo.lotteryCount) + ResultView.printAllLotteries(userLotteryInfo.lotteryTickets) + + val lastWinnerNumbers = InputView.requestLastWinnerNumbers() + val lotteryResults = LotteryStatistic.getWinStatistic(userLotteryInfo.lotteryTickets, lastWinnerNumbers) + + ResultView.printWinStatistic(lotteryResults, money) +} \ No newline at end of file diff --git a/src/main/kotlin/lotto/ResultView.kt b/src/main/kotlin/lotto/ResultView.kt new file mode 100644 index 0000000000..9cd946aad6 --- /dev/null +++ b/src/main/kotlin/lotto/ResultView.kt @@ -0,0 +1,25 @@ +package lotto + +object ResultView { + + fun printLotteryCount(size: Int) { + println("${size}개를 구매했습니다.") + } + + fun printAllLotteries(lotteryTickets: List) { + lotteryTickets.forEach { lottery -> + println(lottery.numbers) + } + } + + fun printWinStatistic( + lotteryResults: Pair, Double>, + money: Int + ) { + println("\n당첨 통계\n-------------------") + lotteryResults.first.forEach { lotteryResult -> + println(lotteryResult.message) + } + println(String.format("총 수익률은 %.2f 입니다", lotteryResults.second / money)) + } +} diff --git a/src/main/kotlin/lotto/UserLottery.kt b/src/main/kotlin/lotto/UserLottery.kt new file mode 100644 index 0000000000..72a097b546 --- /dev/null +++ b/src/main/kotlin/lotto/UserLottery.kt @@ -0,0 +1,6 @@ +package lotto + +data class UserLottery( + val lotteryCount: Int, + val lotteryTickets: List +) diff --git a/src/main/kotlin/lotto/WinnerPrize.kt b/src/main/kotlin/lotto/WinnerPrize.kt new file mode 100644 index 0000000000..1a729acacb --- /dev/null +++ b/src/main/kotlin/lotto/WinnerPrize.kt @@ -0,0 +1,18 @@ +package lotto + +enum class WinnerPrize(val money: Int) { + + FIRST(2_000_000_000), + SECOND(1_500_000), + THIRD(50_000), + LAST(5_000); + + companion object { + fun getPrize(sameCount: Int): WinnerPrize = when (sameCount) { + 3 -> LAST + 4 -> THIRD + 5 -> SECOND + else -> FIRST + } + } +} diff --git a/src/test/kotlin/lotto/LotteryShopKoTest.kt b/src/test/kotlin/lotto/LotteryShopKoTest.kt new file mode 100644 index 0000000000..4efc83e17f --- /dev/null +++ b/src/test/kotlin/lotto/LotteryShopKoTest.kt @@ -0,0 +1,53 @@ +package lotto + +import io.kotest.core.spec.style.StringSpec +import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe + +class LotteryShopKoTest : StringSpec({ + + "구매한 금액에 해당하는 로또 갯수 반환" { + val lotteryShop = LotteryShop() + val actualInput = 14_000 + lotteryShop.buy(actualInput).lotteryCount shouldBe 14 + } + + "일치하는 로또 갯수에 의한 당첨 금액" { + val first = WinnerPrize.getPrize(6) + val second = WinnerPrize.getPrize(5) + val third = WinnerPrize.getPrize(4) + val last = WinnerPrize.getPrize(3) + + first.money shouldBe 2_000_000_000 + second.money shouldBe 1_500_000 + third.money shouldBe 50_000 + last.money shouldBe 5_000 + } + + "구매한 로또들과 지난 주 당첨 번호가 일치하는지 확인" { + val lastWinnerNumbers = listOf(1, 2, 3, 4, 5, 6) + val lotteryTickets = listOf( + Lottery(listOf(1, 2, 3, 4, 39, 43)), + Lottery(listOf(9, 11, 21, 27, 28, 32)), + Lottery(listOf(4, 5, 14, 17, 31, 35)), + Lottery(listOf(11, 13, 17, 22, 25, 34)), + Lottery(listOf(1, 3, 5, 21, 41, 42)), + Lottery(listOf(6, 7, 17, 19, 32, 35)), + Lottery(listOf(2, 12, 19, 34, 35, 37)), + Lottery(listOf(1, 3, 5, 6, 39, 44)), + Lottery(listOf(2, 5, 8, 10, 13, 23)), + Lottery(listOf(7, 29, 30, 31, 34, 36)) + ) + + val lotteryResults = LotteryStatistic.getWinStatistic(lotteryTickets, lastWinnerNumbers).first + + lotteryResults.forEach { + when (it.prize) { + 3 -> it.matchCount shouldBe 1 + 4 -> it.matchCount shouldBe 2 + 5 -> it.matchCount shouldBe 0 + 6 -> it.matchCount shouldBe 0 + } + } + } +}) diff --git a/src/test/kotlin/lotto/LotteryShopTest.kt b/src/test/kotlin/lotto/LotteryShopTest.kt new file mode 100644 index 0000000000..f03174ffed --- /dev/null +++ b/src/test/kotlin/lotto/LotteryShopTest.kt @@ -0,0 +1,56 @@ +package lotto + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class LotteryShopTest { + + @Test + fun `구매한 금액에 해당하는 로또 갯수 반환`() { + val lotteryShop = LotteryShop() + val actualInput = 14_000 + val userLottery = lotteryShop.buy(actualInput) + assertThat(userLottery.lotteryCount).isEqualTo(14) + } + + @Test + fun `일치하는 로또 갯수에 의한 당첨 금액`() { + val first = WinnerPrize.getPrize(6) + val second = WinnerPrize.getPrize(5) + val third = WinnerPrize.getPrize(4) + val last = WinnerPrize.getPrize(3) + + assertThat(first.money).isEqualTo(2_000_000_000) + assertThat(second.money).isEqualTo(1_500_000) + assertThat(third.money).isEqualTo(50_000) + assertThat(last.money).isEqualTo(5_000) + } + + @Test + fun `구매한 로또들과 지난 주 당첨 번호가 일치하는지 확인`() { + val lastWinnerNumbers = listOf(1, 2, 3, 4, 5, 6) + val lotteryTickets = listOf( + Lottery(listOf(1, 2, 3, 4, 39, 43)), + Lottery(listOf(9, 11, 21, 27, 28, 32)), + Lottery(listOf(4, 5, 14, 17, 31, 35)), + Lottery(listOf(11, 13, 17, 22, 25, 34)), + Lottery(listOf(1, 3, 5, 21, 41, 42)), + Lottery(listOf(6, 7, 17, 19, 32, 35)), + Lottery(listOf(2, 12, 19, 34, 35, 37)), + Lottery(listOf(1, 3, 5, 6, 39, 44)), + Lottery(listOf(2, 5, 8, 10, 13, 23)), + Lottery(listOf(7, 29, 30, 31, 34, 36)) + ) + + val lotteryResults = LotteryStatistic.getWinStatistic(lotteryTickets, lastWinnerNumbers).first + + lotteryResults.forEach { + when (it.prize) { + 3 -> assertThat(it.matchCount).isEqualTo(1) + 4 -> assertThat(it.matchCount).isEqualTo(2) + 5 -> assertThat(it.matchCount).isEqualTo(0) + 6 -> assertThat(it.matchCount).isEqualTo(0) + } + } + } +}