diff --git a/README.md b/README.md index ec9fffb7ab..6550047ede 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,20 @@ # kotlin-lotto +# 로또 +## 기능 요구 사항 +- 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다. +- 로또 1장의 가격은 1000원이다. + +## 기능 목록 +- [x] 로또 생성기는 1~45까지 중복되지 않은 숫자 6개를 생성한다. +- [x] 로또 샵에서는 몇장을 살 건지 받아서 해당하는 만큼 로또를 산다. +- [x] 6개 숫자가 모두 맞으면 1등이고, 20억을 받는다. +- [x] 5개 숫자가 맞으면 2등이고, 150만원을 받는다. +- [x] 4개 숫자가 맞으면 3등이고, 5만원을 받는다. +- [x] 3개 숫자가 맞으면 4등이고 5천원을 받는다. +- [] 로또 수익 계산기는 결과를 받아서 수익을 계산한다. + +# 문자열 계산기 ## 기능 요구 사항 - 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환 - (예: “” => 0, "1,2" => 3, "1,2,3" => 6, “1,2:3” => 6) diff --git a/src/main/kotlin/lotto/BenefitCalculator.kt b/src/main/kotlin/lotto/BenefitCalculator.kt new file mode 100644 index 0000000000..1c8eeb0f71 --- /dev/null +++ b/src/main/kotlin/lotto/BenefitCalculator.kt @@ -0,0 +1,32 @@ +package lotto + +import java.math.RoundingMode +import java.text.DecimalFormat + +class BenefitCalculator { + fun calculate(result: Map): String { + val spend = result.map { it.value }.sum() * LOTTO_PRICE + val benefit = result.map { + when (it.key) { + 1 -> WINNER_REWARD * it.value + 2 -> SECOND_REWARD * it.value + 3 -> THIRD_REWARD * it.value + 4 -> FORTH_REWARD * it.value + else -> 0 + } + }.sum() + + val rate = benefit.toDouble() / spend + df.roundingMode = RoundingMode.FLOOR + return df.format(rate) + } + + companion object { + private const val WINNER_REWARD = 2000000000 + private const val SECOND_REWARD = 1500000 + private const val THIRD_REWARD = 50000 + private const val FORTH_REWARD = 5000 + private const val LOTTO_PRICE = 1000 + private val df = DecimalFormat("0.00") + } +} diff --git a/src/main/kotlin/lotto/LottoGame.kt b/src/main/kotlin/lotto/LottoGame.kt new file mode 100644 index 0000000000..ab452f9a28 --- /dev/null +++ b/src/main/kotlin/lotto/LottoGame.kt @@ -0,0 +1,52 @@ +package lotto + +import lotto.view.InputView +import lotto.view.ResultView + +class LottoGame { + fun match( + userLotto: Set, + winningLotto: Set, + ): Int { + val matchCount = userLotto.intersect(winningLotto.toSet()).size + return rank(matchCount) + } + + private fun rank( + matchCount: Int, + ): Int { + return when (matchCount) { + 6 -> 1 + 5 -> 2 + 4 -> 3 + 3 -> 4 + else -> 0 + } + } +} + +fun main() { + val inputView = InputView() + val resultView = ResultView() + val shop = LottoShop() + val game = LottoGame() + val money = inputView.get("구입금액을 입력해 주세요.") + // 얼마를 살 수 있다. + val count = money / 1000 + val lottos = shop.buy(count) + println("${count}개를 구매했습니다.") + lottos.forEach { + println(it) + } + val winningLotto = inputView.getWinningLotto("지난 주 당첨 번호를 입력해 주세요.") + // 맞춰 본다. + val result = lottos.groupingBy { + game.match(it, winningLotto) + }.eachCount() + + val benefitCalculator = BenefitCalculator() + val benefit = benefitCalculator.calculate(result) + resultView.show(result, benefit) +} + + diff --git a/src/main/kotlin/lotto/LottoGenerator.kt b/src/main/kotlin/lotto/LottoGenerator.kt new file mode 100644 index 0000000000..4d66d5ac89 --- /dev/null +++ b/src/main/kotlin/lotto/LottoGenerator.kt @@ -0,0 +1,18 @@ +package lotto + +import kotlin.random.Random + +class LottoGenerator { + fun generate(): Set { + val numbers = mutableSetOf() + while (numbers.size < LIMIT_SIZE) { + numbers.add(Random.nextInt(MAXIMUM)) + } + return numbers + } + + companion object { + private const val MAXIMUM = 45 + private const val LIMIT_SIZE = 6 + } +} diff --git a/src/main/kotlin/lotto/LottoShop.kt b/src/main/kotlin/lotto/LottoShop.kt new file mode 100644 index 0000000000..390a5a83bc --- /dev/null +++ b/src/main/kotlin/lotto/LottoShop.kt @@ -0,0 +1,13 @@ +package lotto + +class LottoShop { + fun buy(count: Int): List> { + val lottoGenerator = LottoGenerator() + val lottos = mutableListOf>() + repeat(count) { + lottos.add(lottoGenerator.generate()) + } + + return lottos + } +} diff --git a/src/main/kotlin/lotto/view/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt new file mode 100644 index 0000000000..4bd5ce3169 --- /dev/null +++ b/src/main/kotlin/lotto/view/InputView.kt @@ -0,0 +1,14 @@ +package lotto.view + +class InputView { + fun get(prompt: String): Int { + print(prompt) + return readln().toInt() + } + + fun getWinningLotto(prompt: String): Set { + print(prompt) + return readln().split(",").map { it.toInt() }.toSet() + } + +} diff --git a/src/main/kotlin/lotto/view/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt new file mode 100644 index 0000000000..88e254fca5 --- /dev/null +++ b/src/main/kotlin/lotto/view/ResultView.kt @@ -0,0 +1,23 @@ +package lotto.view + +class ResultView { + fun show(result: Map, benefit: String) { + println("당첨 통계") + println("---------") + (1..4).reversed().forEach { + showOne(it, result.getOrDefault(it, 0)) + } + + val bep = if (benefit.toDouble() > 1.0) "이익" else "손해" + println("총 수익률은 ${benefit}입니다. (기준이 1이기 때문에 결과적으로 ${bep}라는 의미임)") + } + + private fun showOne(rank: Int, count: Int) { + when (rank) { + 1 -> println("6개 일치 (2000000000원) - ${count}개") + 2 -> println("5개 일치 (1500000원) - ${count}개") + 3 -> println("4개 일치 (50000원) - ${count}개") + 4 -> println("3개 일치 (5000원) - ${count}개") + } + } +} diff --git a/src/test/kotlin/lotto/BenefitCalculatorTest.kt b/src/test/kotlin/lotto/BenefitCalculatorTest.kt new file mode 100644 index 0000000000..2d3ead2fd6 --- /dev/null +++ b/src/test/kotlin/lotto/BenefitCalculatorTest.kt @@ -0,0 +1,22 @@ +package lotto + +import org.assertj.core.api.AssertionsForClassTypes.assertThat +import org.junit.jupiter.api.Test + +class BenefitCalculatorTest { + + @Test + fun `14장 중에 4등 1장 미당첨 13장일 경우 수익률은 0,35이다`() { + val calculator = BenefitCalculator() + val result = mapOf( + 1 to 0, + 2 to 0, + 3 to 0, + 4 to 1, + 0 to 13 + ) + + val actual = calculator.calculate(result) + assertThat(actual).isEqualTo("0.35") + } +} diff --git a/src/test/kotlin/lotto/LottoGameTest.kt b/src/test/kotlin/lotto/LottoGameTest.kt new file mode 100644 index 0000000000..c961598de0 --- /dev/null +++ b/src/test/kotlin/lotto/LottoGameTest.kt @@ -0,0 +1,77 @@ +package lotto + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class LottoGameTest { + + @Test + fun `1등은 6개 일치` () { + val lottoGame = LottoGame() + val userLotto = setOf(1, 2, 3, 4, 5, 6) + val winningLotto = setOf(1, 2, 3, 4, 5, 6) + val actual = lottoGame.match(userLotto, winningLotto) + + actual shouldBe 1 + } + + @Test + fun `2등은 5개 일치`() { + val lottoGame = LottoGame() + val userLotto = setOf(1, 2, 3, 4, 5, 6) + val winningLotto = setOf(1, 2, 3, 4, 5, 7) + val actual = lottoGame.match(userLotto, winningLotto) + + actual shouldBe 2 + } + + @Test + fun `3등은 4개 일치`() { + val lottoGame = LottoGame() + val userLotto = setOf(1, 2, 3, 4, 5, 6) + val winningLotto = setOf(1, 2, 3, 4, 7, 8) + val actual = lottoGame.match(userLotto, winningLotto) + + actual shouldBe 3 + } + + @Test + fun `4등은 3개 일치`() { + val lottoGame = LottoGame() + val userLotto = setOf(1, 2, 3, 4, 5, 6) + val winningLotto = setOf(1, 2, 3, 7, 8, 9) + val actual = lottoGame.match(userLotto, winningLotto) + + actual shouldBe 4 + } + + @Test + fun `2개 경우 일치 하면 0`(userLotto: Set) { + val lottoGame = LottoGame() + val userLotto = setOf(1, 2, 12, 13, 14, 15) + val winningLotto = setOf(1, 2, 3, 7, 8, 9) + val actual = lottoGame.match(userLotto, winningLotto) + + actual shouldBe 4 + } + + @Test + fun `1개 경우 일치 하면 0`(userLotto: Set) { + val lottoGame = LottoGame() + val userLotto = setOf(1, 11, 12, 13, 14, 16) + val winningLotto = setOf(1, 2, 3, 7, 8, 9) + val actual = lottoGame.match(userLotto, winningLotto) + + actual shouldBe 4 + } + + @Test + fun `0개 경우 일치 하면 0`(userLotto: Set) { + val lottoGame = LottoGame() + val userLotto = setOf(10, 11, 12, 13, 14, 16) + val winningLotto = setOf(1, 2, 3, 7, 8, 9) + val actual = lottoGame.match(userLotto, winningLotto) + + actual shouldBe 4 + } +} diff --git a/src/test/kotlin/lotto/LottoGeneratorTest.kt b/src/test/kotlin/lotto/LottoGeneratorTest.kt new file mode 100644 index 0000000000..7fc10fe137 --- /dev/null +++ b/src/test/kotlin/lotto/LottoGeneratorTest.kt @@ -0,0 +1,22 @@ +package lotto + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class LottoGeneratorTest { + + @Test + fun `로또 생성기는 숫자 6개를 생성한다`() { + val lottoGenerator = LottoGenerator() + val lotto = lottoGenerator.generate() + lotto.size shouldBe 6 + } + + @Test + fun `로또 생성기는 숫자가 겹치지 않는다`() { + val lottoGenerator = LottoGenerator() + val lotto = lottoGenerator.generate() + lotto.distinct().size shouldBe 6 + } + +} diff --git a/src/test/kotlin/lotto/LottoShopTest.kt b/src/test/kotlin/lotto/LottoShopTest.kt new file mode 100644 index 0000000000..2fe38b701e --- /dev/null +++ b/src/test/kotlin/lotto/LottoShopTest.kt @@ -0,0 +1,15 @@ +package lotto + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class LottoShopTest { + + @Test + fun `로또 n장 사기`() { + val lottoShop = LottoShop() + val count = 5 + val lottos = lottoShop.buy(5) + lottos.size shouldBe count + } +}