From 08cf7e3a144d2e4ffe81ed7ec7c427d22b25a1d0 Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Wed, 14 Dec 2022 18:56:38 +0900 Subject: [PATCH 1/8] =?UTF-8?q?docs=20:=20README=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/kotlin/lotto/README.md diff --git a/src/main/kotlin/lotto/README.md b/src/main/kotlin/lotto/README.md new file mode 100644 index 0000000000..18c80d812c --- /dev/null +++ b/src/main/kotlin/lotto/README.md @@ -0,0 +1,10 @@ +# Step2. 로또 기능 요구사항 + * 구입금액을 입력받는다 + * 구입 금액에 해당하는 로또를 발급한다 (로또 1장의 가격은 1000원이다) + * 발급한 로또를 출력한다 + * 지난 주 당첨번호를 입력받는다 + * 발급한 로또의 당첨 통계를 구한다 + * 발급한 로또의 총 수익률을 구한다 + * 당첨 통계와 수익률을 출력한다 + + From 2efcfbc0225557aca0ba1a7f14472557a31f14fe Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Wed, 14 Dec 2022 22:30:32 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat=20:=20=EA=B5=AC=EC=9E=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=EC=97=90=20=ED=95=B4=EB=8B=B9=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=EB=A5=BC=20=EB=B0=9C=EA=B8=89=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Main.kt | 33 +++++++++++++ .../kotlin/lotto/domain/LotteryMachine.kt | 47 +++++++++++++++++++ src/main/kotlin/lotto/domain/LotteryResult.kt | 8 ++++ .../kotlin/lotto/domain/LotteryMachineTest.kt | 36 ++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 src/main/kotlin/lotto/Main.kt create mode 100644 src/main/kotlin/lotto/domain/LotteryMachine.kt create mode 100644 src/main/kotlin/lotto/domain/LotteryResult.kt create mode 100644 src/test/kotlin/lotto/domain/LotteryMachineTest.kt diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt new file mode 100644 index 0000000000..70feeafb2c --- /dev/null +++ b/src/main/kotlin/lotto/Main.kt @@ -0,0 +1,33 @@ +package lotto + +import lotto.domain.LotteryMachine + +fun main() { + val lotteryMachine = LotteryMachine() + + println("구입금액을 입력해 주세요.") + val payAmount = readLine()?.toIntOrNull() ?: throw IllegalArgumentException() + + val lotteries = lotteryMachine.buyLotteries(payAmount) + + println("${lotteries.count()}개를 구매했습니다.") + + for (it in lotteries.lotteries) { + println("$it") + } + println("지난 주 당첨 번호를 입력해 주세요.") + val lastWinningLottery = readLine()?.split(",")?.map { it.toInt() }?.toList() ?: throw IllegalArgumentException() + + val result = lotteryMachine.getResult(lotteries, lastWinningLottery) + println( + """ + |당첨 통계 + |--------- + |3개 일치 (5000원)- ${result.matchCount(3)}개 + |4개 일치 (50000원)- ${result.matchCount(4)}개 + |5개 일치 (1500000원)- ${result.matchCount(5)}개 + |6개 일치 (2000000000원)- ${result.matchCount(6)}개 + |총 수익률은 ${result.returnRate}입니다. + """ + ) +} diff --git a/src/main/kotlin/lotto/domain/LotteryMachine.kt b/src/main/kotlin/lotto/domain/LotteryMachine.kt new file mode 100644 index 0000000000..290e125495 --- /dev/null +++ b/src/main/kotlin/lotto/domain/LotteryMachine.kt @@ -0,0 +1,47 @@ +package lotto.domain + +class LotteryMachine { + + fun buyLotteries(payAmount: Int): Lotteries { + val howMany = (payAmount / PRICE) + val lotteryList = List(howMany) { Lottery(LottoNumber.allNumbers().shuffled().subList(0, 6)) } + return Lotteries(lotteryList) + } + + fun getResult(lotteries: Lotteries, lastWinningLottery: List): LotteryResult { + + return LotteryResult() + } + + companion object { + const val PRICE = 1_000 + } +} + +data class Lotteries(val lotteries: List) { + fun count(): Int { + return lotteries.size + } +} + +data class Lottery(val numbers: List) { + init { + require(numbers.size == 6) + } +} + +class LottoNumber private constructor(number: Int) { + val number: Int = number + + init { + require(number in (1..45)) + } + + companion object { + private val NUMBERS = List(45) { LottoNumber(it + 1) } + + fun allNumbers(): List { + return NUMBERS + } + } +} diff --git a/src/main/kotlin/lotto/domain/LotteryResult.kt b/src/main/kotlin/lotto/domain/LotteryResult.kt new file mode 100644 index 0000000000..7a234fa36a --- /dev/null +++ b/src/main/kotlin/lotto/domain/LotteryResult.kt @@ -0,0 +1,8 @@ +package lotto.domain + +class LotteryResult() { + val returnRate: Double = 0.1 + fun matchCount(howMany: Int): Int { + return 0 + } +} diff --git a/src/test/kotlin/lotto/domain/LotteryMachineTest.kt b/src/test/kotlin/lotto/domain/LotteryMachineTest.kt new file mode 100644 index 0000000000..2e51b788f4 --- /dev/null +++ b/src/test/kotlin/lotto/domain/LotteryMachineTest.kt @@ -0,0 +1,36 @@ +package lotto.domain + +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +internal class LotteryMachineTest { + lateinit var sut: LotteryMachine + + @BeforeEach + fun beforeEach() { + sut = LotteryMachine() + } + + @DisplayName("구입 금액에 해당하는 로또를 발급한다") + @Test + fun buyLottery() { + listOf( + 1000 to 1, + 1500 to 1, + 3900 to 3, + 10000 to 10 + ).map { (payAmount, howManyBought) -> + { + val result = sut.buyLotteries(payAmount) + + result.count() shouldBe howManyBought + result.lotteries.shouldForAll { + it.numbers.size shouldBe 6 + } + } + } + } +} From 38ed95725765131f57ec07bc88539b895a889969 Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Wed, 14 Dec 2022 23:18:35 +0900 Subject: [PATCH 3/8] =?UTF-8?q?refactor=20:=20=EA=B0=92=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=A1=9C=20=EB=B6=84=EB=A6=AC=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/lotto/domain/LotteryMachine.kt | 28 --------- src/main/kotlin/lotto/domain/Model.kt | 59 +++++++++++++++++++ .../kotlin/lotto/domain/LottoNumberTest.kt | 59 +++++++++++++++++++ 3 files changed, 118 insertions(+), 28 deletions(-) create mode 100644 src/main/kotlin/lotto/domain/Model.kt create mode 100644 src/test/kotlin/lotto/domain/LottoNumberTest.kt diff --git a/src/main/kotlin/lotto/domain/LotteryMachine.kt b/src/main/kotlin/lotto/domain/LotteryMachine.kt index 290e125495..ce5b7346c9 100644 --- a/src/main/kotlin/lotto/domain/LotteryMachine.kt +++ b/src/main/kotlin/lotto/domain/LotteryMachine.kt @@ -17,31 +17,3 @@ class LotteryMachine { const val PRICE = 1_000 } } - -data class Lotteries(val lotteries: List) { - fun count(): Int { - return lotteries.size - } -} - -data class Lottery(val numbers: List) { - init { - require(numbers.size == 6) - } -} - -class LottoNumber private constructor(number: Int) { - val number: Int = number - - init { - require(number in (1..45)) - } - - companion object { - private val NUMBERS = List(45) { LottoNumber(it + 1) } - - fun allNumbers(): List { - return NUMBERS - } - } -} diff --git a/src/main/kotlin/lotto/domain/Model.kt b/src/main/kotlin/lotto/domain/Model.kt new file mode 100644 index 0000000000..f6bb5d1274 --- /dev/null +++ b/src/main/kotlin/lotto/domain/Model.kt @@ -0,0 +1,59 @@ +package lotto.domain + +data class Lotteries(val lotteries: List) { + fun count(): Int { + return lotteries.size + } +} + +class Lottery(numbers: List) { + val numbers: List + + init { + require(numbers.size == 6) + this.numbers = numbers.sorted() + } +} + +class LottoNumber private constructor(val number: Int) : Comparable { + init { + require(number in (1..45)) + } + + companion object { + private val NUMBERS = List(45) { LottoNumber(it + 1) } + + fun allNumbers(): List { + return NUMBERS + } + + fun getInstance(number: Int): LottoNumber { + require(number - 1 in (NUMBERS.indices)) + + return NUMBERS[number - 1] + } + } + + override fun toString(): String { + return "LottoNumber(number=$number)" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as LottoNumber + + if (number != other.number) return false + + return true + } + + override fun hashCode(): Int { + return number + } + + override fun compareTo(other: LottoNumber): Int { + return this.number.compareTo(other.number) + } +} diff --git a/src/test/kotlin/lotto/domain/LottoNumberTest.kt b/src/test/kotlin/lotto/domain/LottoNumberTest.kt new file mode 100644 index 0000000000..7f30e34da6 --- /dev/null +++ b/src/test/kotlin/lotto/domain/LottoNumberTest.kt @@ -0,0 +1,59 @@ +package lotto.domain + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.comparables.shouldBeEqualComparingTo +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeSameInstanceAs +import org.junit.jupiter.api.Assertions.assertAll +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +internal class LottoNumberTest { + + @DisplayName("특정 로또 번호로 생성할 수 있다") + @ParameterizedTest + @ValueSource(ints = [1, 2, 3, 4, 43, 44, 45]) + fun number(number: Int) { + LottoNumber.getInstance(number).number shouldBe number + } + + @DisplayName("로또 번호는 1-45 사이의 값이여야 한다") + @ParameterizedTest + @ValueSource(ints = [0, -1, 46, 50, 100]) + fun getInstanceFailIfNotLottoNumber(number: Int) { + shouldThrow { LottoNumber.getInstance(number) } + } + + @DisplayName("값 객체이다") + @ParameterizedTest + @ValueSource(ints = [1, 2, 3, 4, 43, 44, 45]) + fun getInstance(number: Int) { + val sut = LottoNumber.getInstance(number) + val other = LottoNumber.getInstance(number) + + assertAll( + { sut shouldBe other }, + { sut shouldBeSameInstanceAs other }, + { sut shouldBeEqualComparingTo other } + ) + } + + @DisplayName("로또 번호 값으로 비교할 수 있다") + @Test + fun compare() { + listOf( + 1 to 1, + 2 to 0, + 3 to -1, + ).map { (compareTo, compareResult) -> + { + val sut = LottoNumber.getInstance(2) + val other = LottoNumber.getInstance(compareTo) + + sut.compareTo(other) shouldBe compareResult + } + } + } +} From 828ec8a7d8c68f63b5febb15c5df0428c9f96c81 Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Wed, 14 Dec 2022 23:21:43 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EB=B0=9C=EA=B8=89=ED=95=9C=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=EB=A5=BC=20=EC=B6=9C=EB=A0=A5=ED=95=9C?= =?UTF-8?q?=EB=8B=A4,=20=EC=A7=80=EB=82=9C=20=EC=A3=BC=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=EB=B2=88=ED=98=B8=EB=A5=BC=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EB=B0=9B=EB=8A=94=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Main.kt | 9 ++++++--- src/main/kotlin/lotto/domain/LotteryMachine.kt | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index 70feeafb2c..ddb2f0d0ef 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -1,6 +1,8 @@ package lotto +import lotto.domain.Lottery import lotto.domain.LotteryMachine +import lotto.domain.LottoNumber fun main() { val lotteryMachine = LotteryMachine() @@ -13,10 +15,11 @@ fun main() { println("${lotteries.count()}개를 구매했습니다.") for (it in lotteries.lotteries) { - println("$it") + println("[${it.numbers.joinToString(", ", transform = { t -> t.number.toString() })}]") } println("지난 주 당첨 번호를 입력해 주세요.") - val lastWinningLottery = readLine()?.split(",")?.map { it.toInt() }?.toList() ?: throw IllegalArgumentException() + val inputNumbers = readLine()?.split(",")?.map { it.toInt() }?.toList() ?: throw IllegalArgumentException() + val lastWinningLottery = Lottery(inputNumbers.map { LottoNumber.getInstance(it) }.toList()) val result = lotteryMachine.getResult(lotteries, lastWinningLottery) println( @@ -28,6 +31,6 @@ fun main() { |5개 일치 (1500000원)- ${result.matchCount(5)}개 |6개 일치 (2000000000원)- ${result.matchCount(6)}개 |총 수익률은 ${result.returnRate}입니다. - """ + """.trimIndent() ) } diff --git a/src/main/kotlin/lotto/domain/LotteryMachine.kt b/src/main/kotlin/lotto/domain/LotteryMachine.kt index ce5b7346c9..c03aa3d7c9 100644 --- a/src/main/kotlin/lotto/domain/LotteryMachine.kt +++ b/src/main/kotlin/lotto/domain/LotteryMachine.kt @@ -8,7 +8,7 @@ class LotteryMachine { return Lotteries(lotteryList) } - fun getResult(lotteries: Lotteries, lastWinningLottery: List): LotteryResult { + fun getResult(lotteries: Lotteries, lastWinningLottery: Lottery): LotteryResult { return LotteryResult() } From 83fce921f2379054d012b0318f40218f0c9862ed Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Thu, 15 Dec 2022 00:19:40 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=EB=B0=9C=EA=B8=89=ED=95=9C=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=EC=9D=98=20=EB=8B=B9=EC=B2=A8=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=EB=A5=BC=20=EA=B5=AC=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Main.kt | 6 ++-- .../kotlin/lotto/domain/LotteryMachine.kt | 7 +++- src/main/kotlin/lotto/domain/LotteryResult.kt | 6 ++-- src/main/kotlin/lotto/domain/Model.kt | 28 ++++++++++----- .../kotlin/lotto/domain/LotteryMachineTest.kt | 22 +++++++++--- src/test/kotlin/lotto/domain/LotteryTest.kt | 34 +++++++++++++++++++ .../kotlin/lotto/domain/LottoNumberTest.kt | 12 +++---- 7 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 src/test/kotlin/lotto/domain/LotteryTest.kt diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index ddb2f0d0ef..6a098150cf 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -15,11 +15,11 @@ fun main() { println("${lotteries.count()}개를 구매했습니다.") for (it in lotteries.lotteries) { - println("[${it.numbers.joinToString(", ", transform = { t -> t.number.toString() })}]") + println("[${it.getLottoNumbers().joinToString(", ")}]") } println("지난 주 당첨 번호를 입력해 주세요.") val inputNumbers = readLine()?.split(",")?.map { it.toInt() }?.toList() ?: throw IllegalArgumentException() - val lastWinningLottery = Lottery(inputNumbers.map { LottoNumber.getInstance(it) }.toList()) + val lastWinningLottery = Lottery(inputNumbers.map { LottoNumber.of(it) }.toList()) val result = lotteryMachine.getResult(lotteries, lastWinningLottery) println( @@ -30,7 +30,7 @@ fun main() { |4개 일치 (50000원)- ${result.matchCount(4)}개 |5개 일치 (1500000원)- ${result.matchCount(5)}개 |6개 일치 (2000000000원)- ${result.matchCount(6)}개 - |총 수익률은 ${result.returnRate}입니다. + |총 수익률은 ${lotteryMachine.calculateReturnRate(payAmount, result)}입니다. """.trimIndent() ) } diff --git a/src/main/kotlin/lotto/domain/LotteryMachine.kt b/src/main/kotlin/lotto/domain/LotteryMachine.kt index c03aa3d7c9..0b4ecb3c39 100644 --- a/src/main/kotlin/lotto/domain/LotteryMachine.kt +++ b/src/main/kotlin/lotto/domain/LotteryMachine.kt @@ -9,8 +9,13 @@ class LotteryMachine { } fun getResult(lotteries: Lotteries, lastWinningLottery: Lottery): LotteryResult { + return LotteryResult( + lotteries.lotteries.map { it.countSameLottoNumbers(lastWinningLottery) }.toList() + ) + } - return LotteryResult() + fun calculateReturnRate(payAmount: Int, lotteryResult: LotteryResult): Double { + return 0.1 } companion object { diff --git a/src/main/kotlin/lotto/domain/LotteryResult.kt b/src/main/kotlin/lotto/domain/LotteryResult.kt index 7a234fa36a..629f029e0d 100644 --- a/src/main/kotlin/lotto/domain/LotteryResult.kt +++ b/src/main/kotlin/lotto/domain/LotteryResult.kt @@ -1,8 +1,8 @@ package lotto.domain -class LotteryResult() { - val returnRate: Double = 0.1 +class LotteryResult(private val countSameLottoNumber: List) { + fun matchCount(howMany: Int): Int { - return 0 + return countSameLottoNumber.count { it == howMany } } } diff --git a/src/main/kotlin/lotto/domain/Model.kt b/src/main/kotlin/lotto/domain/Model.kt index f6bb5d1274..88af7ffad1 100644 --- a/src/main/kotlin/lotto/domain/Model.kt +++ b/src/main/kotlin/lotto/domain/Model.kt @@ -1,23 +1,35 @@ package lotto.domain data class Lotteries(val lotteries: List) { + constructor(vararg lotteries: Lottery) : this(lotteries.toList()) + fun count(): Int { return lotteries.size } } class Lottery(numbers: List) { - val numbers: List + private val numbers: List + + constructor(vararg inputNumbers: Int) : this(inputNumbers.map { LottoNumber.of(it) }.toList()) init { require(numbers.size == 6) this.numbers = numbers.sorted() } + + fun countSameLottoNumbers(other: Lottery): Int { + return this.numbers.count { other.numbers.contains(it) } + } + + fun getLottoNumbers(): List { + return numbers.map { it.value }.toList() + } } -class LottoNumber private constructor(val number: Int) : Comparable { +class LottoNumber private constructor(val value: Int) : Comparable { init { - require(number in (1..45)) + require(value in (1..45)) } companion object { @@ -27,7 +39,7 @@ class LottoNumber private constructor(val number: Int) : Comparable return NUMBERS } - fun getInstance(number: Int): LottoNumber { + fun of(number: Int): LottoNumber { require(number - 1 in (NUMBERS.indices)) return NUMBERS[number - 1] @@ -35,7 +47,7 @@ class LottoNumber private constructor(val number: Int) : Comparable } override fun toString(): String { - return "LottoNumber(number=$number)" + return "LottoNumber(number=$value)" } override fun equals(other: Any?): Boolean { @@ -44,16 +56,16 @@ class LottoNumber private constructor(val number: Int) : Comparable other as LottoNumber - if (number != other.number) return false + if (value != other.value) return false return true } override fun hashCode(): Int { - return number + return value } override fun compareTo(other: LottoNumber): Int { - return this.number.compareTo(other.number) + return this.value.compareTo(other.value) } } diff --git a/src/test/kotlin/lotto/domain/LotteryMachineTest.kt b/src/test/kotlin/lotto/domain/LotteryMachineTest.kt index 2e51b788f4..5ac9c051dd 100644 --- a/src/test/kotlin/lotto/domain/LotteryMachineTest.kt +++ b/src/test/kotlin/lotto/domain/LotteryMachineTest.kt @@ -1,10 +1,10 @@ package lotto.domain -import io.kotest.inspectors.shouldForAll import io.kotest.matchers.shouldBe import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll internal class LotteryMachineTest { lateinit var sut: LotteryMachine @@ -27,10 +27,24 @@ internal class LotteryMachineTest { val result = sut.buyLotteries(payAmount) result.count() shouldBe howManyBought - result.lotteries.shouldForAll { - it.numbers.size shouldBe 6 - } } } } + + @Test + fun getResult() { + + val result = sut.getResult( + Lotteries( + Lottery(1, 2, 13, 14, 15, 16), + Lottery(11, 12, 13, 4, 5, 6), + Lottery(1, 2, 3, 14, 15, 16) + ), + Lottery(1, 2, 3, 4, 5, 6) + ) + assertAll( + { result.matchCount(2) shouldBe 1 }, + { result.matchCount(3) shouldBe 2 } + ) + } } diff --git a/src/test/kotlin/lotto/domain/LotteryTest.kt b/src/test/kotlin/lotto/domain/LotteryTest.kt new file mode 100644 index 0000000000..f2309a5bd1 --- /dev/null +++ b/src/test/kotlin/lotto/domain/LotteryTest.kt @@ -0,0 +1,34 @@ +package lotto.domain + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +internal class LotteryTest { + + @DisplayName("6개의 로또 번호를 포함해야한다") + @Test + fun create() { + listOf( + intArrayOf(1, 2), + intArrayOf(1, 2, 3, 4, 5), + intArrayOf(1, 2, 3, 4, 5, 6, 7) + ).map { numbers -> + shouldThrow { Lottery(*numbers) } + } + } + + @DisplayName("일치하는 로또 번호 갯수를 구한다") + @Test + fun countSameLottoNumbers() { + val sut = Lottery(1, 2, 3, 4, 5, 6) + listOf( + Lottery(1, 12, 13, 14, 15, 16) to 1, + Lottery(1, 2, 13, 14, 15, 16) to 2, + Lottery(1, 2, 3, 4, 5, 6) to 6 + ).map { (other, expected) -> + sut.countSameLottoNumbers(other) shouldBe expected + } + } +} diff --git a/src/test/kotlin/lotto/domain/LottoNumberTest.kt b/src/test/kotlin/lotto/domain/LottoNumberTest.kt index 7f30e34da6..8604b13fb7 100644 --- a/src/test/kotlin/lotto/domain/LottoNumberTest.kt +++ b/src/test/kotlin/lotto/domain/LottoNumberTest.kt @@ -16,22 +16,22 @@ internal class LottoNumberTest { @ParameterizedTest @ValueSource(ints = [1, 2, 3, 4, 43, 44, 45]) fun number(number: Int) { - LottoNumber.getInstance(number).number shouldBe number + LottoNumber.of(number).value shouldBe number } @DisplayName("로또 번호는 1-45 사이의 값이여야 한다") @ParameterizedTest @ValueSource(ints = [0, -1, 46, 50, 100]) fun getInstanceFailIfNotLottoNumber(number: Int) { - shouldThrow { LottoNumber.getInstance(number) } + shouldThrow { LottoNumber.of(number) } } @DisplayName("값 객체이다") @ParameterizedTest @ValueSource(ints = [1, 2, 3, 4, 43, 44, 45]) fun getInstance(number: Int) { - val sut = LottoNumber.getInstance(number) - val other = LottoNumber.getInstance(number) + val sut = LottoNumber.of(number) + val other = LottoNumber.of(number) assertAll( { sut shouldBe other }, @@ -49,8 +49,8 @@ internal class LottoNumberTest { 3 to -1, ).map { (compareTo, compareResult) -> { - val sut = LottoNumber.getInstance(2) - val other = LottoNumber.getInstance(compareTo) + val sut = LottoNumber.of(2) + val other = LottoNumber.of(compareTo) sut.compareTo(other) shouldBe compareResult } From b9026028326537fa74960c8ab7caff652199abe3 Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Sun, 18 Dec 2022 21:04:30 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=EB=B0=9C=EA=B8=89=ED=95=9C=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=EC=9D=98=20=EC=B4=9D=20=EC=88=98=EC=9D=B5?= =?UTF-8?q?=EB=A5=A0=EC=9D=84=20=EA=B5=AC=ED=95=98=EA=B3=A0=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 발급한 로또의 총 수익률을 구한다 - refactor : view 클래스 및 패키지 분리 - refactor : LotteryResult 의 matchCount 함수 삭제 -> dto 로, 로직 같지 않도록 변경 --- src/main/kotlin/lotto/Main.kt | 34 ++++---------- .../kotlin/lotto/domain/LotteryMachine.kt | 37 ++++++++++----- .../kotlin/lotto/domain/LotteryMatchCount.kt | 3 ++ src/main/kotlin/lotto/domain/LotteryResult.kt | 8 ---- src/main/kotlin/lotto/domain/Model.kt | 10 ++-- .../lotto/view/LotteryMachineInputView.kt | 15 ++++++ .../lotto/view/LotteryMachineOutputView.kt | 29 ++++++++++++ .../kotlin/lotto/domain/LotteryMachineTest.kt | 46 ++++++++++++++----- 8 files changed, 120 insertions(+), 62 deletions(-) create mode 100644 src/main/kotlin/lotto/domain/LotteryMatchCount.kt delete mode 100644 src/main/kotlin/lotto/domain/LotteryResult.kt create mode 100644 src/main/kotlin/lotto/view/LotteryMachineInputView.kt create mode 100644 src/main/kotlin/lotto/view/LotteryMachineOutputView.kt diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index 6a098150cf..925233fe97 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -2,35 +2,17 @@ package lotto import lotto.domain.Lottery import lotto.domain.LotteryMachine -import lotto.domain.LottoNumber +import lotto.view.LotteryMachineInputView +import lotto.view.LotteryMachineOutputView fun main() { - val lotteryMachine = LotteryMachine() - println("구입금액을 입력해 주세요.") - val payAmount = readLine()?.toIntOrNull() ?: throw IllegalArgumentException() + val payAmount = LotteryMachineInputView.inputPayAmount() + val lotteries = LotteryMachine.buyLotteries(payAmount) + LotteryMachineOutputView.printLotteries(lotteries) - val lotteries = lotteryMachine.buyLotteries(payAmount) + val lastWinningLottery = Lottery(LotteryMachineInputView.inputLastWinningNumbers()) - println("${lotteries.count()}개를 구매했습니다.") - - for (it in lotteries.lotteries) { - println("[${it.getLottoNumbers().joinToString(", ")}]") - } - println("지난 주 당첨 번호를 입력해 주세요.") - val inputNumbers = readLine()?.split(",")?.map { it.toInt() }?.toList() ?: throw IllegalArgumentException() - val lastWinningLottery = Lottery(inputNumbers.map { LottoNumber.of(it) }.toList()) - - val result = lotteryMachine.getResult(lotteries, lastWinningLottery) - println( - """ - |당첨 통계 - |--------- - |3개 일치 (5000원)- ${result.matchCount(3)}개 - |4개 일치 (50000원)- ${result.matchCount(4)}개 - |5개 일치 (1500000원)- ${result.matchCount(5)}개 - |6개 일치 (2000000000원)- ${result.matchCount(6)}개 - |총 수익률은 ${lotteryMachine.calculateReturnRate(payAmount, result)}입니다. - """.trimIndent() - ) + val result = LotteryMachine.getMatchCount(lotteries, lastWinningLottery) + LotteryMachineOutputView.printResult(result, LotteryMachine.calculateReturnRate(payAmount, result)) } diff --git a/src/main/kotlin/lotto/domain/LotteryMachine.kt b/src/main/kotlin/lotto/domain/LotteryMachine.kt index 0b4ecb3c39..95ff7f38ab 100644 --- a/src/main/kotlin/lotto/domain/LotteryMachine.kt +++ b/src/main/kotlin/lotto/domain/LotteryMachine.kt @@ -1,24 +1,39 @@ package lotto.domain -class LotteryMachine { +import java.math.BigDecimal +import java.math.RoundingMode + +object LotteryMachine { + private const val PRICE = 1_000 + private val LOTTO_RETURN_MAP = mapOf( + 3 to 5_000, + 4 to 50_000, + 5 to 1_500_000, + 6 to 2_000_000_000 + ) fun buyLotteries(payAmount: Int): Lotteries { val howMany = (payAmount / PRICE) - val lotteryList = List(howMany) { Lottery(LottoNumber.allNumbers().shuffled().subList(0, 6)) } + val lotteryList = List(howMany) { Lottery(randomLottoNumbers()) } return Lotteries(lotteryList) } - fun getResult(lotteries: Lotteries, lastWinningLottery: Lottery): LotteryResult { - return LotteryResult( - lotteries.lotteries.map { it.countSameLottoNumbers(lastWinningLottery) }.toList() - ) - } + private fun randomLottoNumbers(howMany: Int = 6) = LottoNumber.allNumbers().shuffled().subList(0, howMany) - fun calculateReturnRate(payAmount: Int, lotteryResult: LotteryResult): Double { - return 0.1 + fun getMatchCount(lotteries: Lotteries, lastWinningLottery: Lottery): LotteryMatchCount { + val matchCount = lotteries.lotteries + .groupingBy { it.countSameLottoNumbers(lastWinningLottery) } + .eachCount() + + return LotteryMatchCount(matchCount) } - companion object { - const val PRICE = 1_000 + fun calculateReturnRate(payAmount: Int, lotteryResult: LotteryMatchCount): BigDecimal { + val totalPrize = LOTTO_RETURN_MAP.entries.map { + it.value * (lotteryResult.matchCount[it.key] ?: 0) + }.sum() + + return BigDecimal.valueOf(totalPrize / payAmount.toDouble()) + .setScale(2, RoundingMode.FLOOR) } } diff --git a/src/main/kotlin/lotto/domain/LotteryMatchCount.kt b/src/main/kotlin/lotto/domain/LotteryMatchCount.kt new file mode 100644 index 0000000000..1ff528ba0e --- /dev/null +++ b/src/main/kotlin/lotto/domain/LotteryMatchCount.kt @@ -0,0 +1,3 @@ +package lotto.domain + +data class LotteryMatchCount(val matchCount: Map) diff --git a/src/main/kotlin/lotto/domain/LotteryResult.kt b/src/main/kotlin/lotto/domain/LotteryResult.kt deleted file mode 100644 index 629f029e0d..0000000000 --- a/src/main/kotlin/lotto/domain/LotteryResult.kt +++ /dev/null @@ -1,8 +0,0 @@ -package lotto.domain - -class LotteryResult(private val countSameLottoNumber: List) { - - fun matchCount(howMany: Int): Int { - return countSameLottoNumber.count { it == howMany } - } -} diff --git a/src/main/kotlin/lotto/domain/Model.kt b/src/main/kotlin/lotto/domain/Model.kt index 88af7ffad1..c921142ed3 100644 --- a/src/main/kotlin/lotto/domain/Model.kt +++ b/src/main/kotlin/lotto/domain/Model.kt @@ -8,14 +8,14 @@ data class Lotteries(val lotteries: List) { } } -class Lottery(numbers: List) { +class Lottery(numbers: List) { private val numbers: List - constructor(vararg inputNumbers: Int) : this(inputNumbers.map { LottoNumber.of(it) }.toList()) + constructor(vararg inputNumbers: Int) : this(inputNumbers.toList()) init { require(numbers.size == 6) - this.numbers = numbers.sorted() + this.numbers = numbers.map { LottoNumber.of(it) }.sorted() } fun countSameLottoNumbers(other: Lottery): Int { @@ -35,8 +35,8 @@ class LottoNumber private constructor(val value: Int) : Comparable companion object { private val NUMBERS = List(45) { LottoNumber(it + 1) } - fun allNumbers(): List { - return NUMBERS + fun allNumbers(): List { + return NUMBERS.map { it.value } } fun of(number: Int): LottoNumber { diff --git a/src/main/kotlin/lotto/view/LotteryMachineInputView.kt b/src/main/kotlin/lotto/view/LotteryMachineInputView.kt new file mode 100644 index 0000000000..3a7c54fcd3 --- /dev/null +++ b/src/main/kotlin/lotto/view/LotteryMachineInputView.kt @@ -0,0 +1,15 @@ +package lotto.view + +object LotteryMachineInputView { + + fun inputPayAmount(): Int { + println("구입금액을 입력해 주세요.") + return readLine()?.toIntOrNull() ?: throw IllegalArgumentException() + } + + fun inputLastWinningNumbers(): List { + println("지난 주 당첨 번호를 입력해 주세요.") + + return readLine()?.split(",")?.map { it.toIntOrNull() ?: 0 } ?: throw IllegalArgumentException() + } +} diff --git a/src/main/kotlin/lotto/view/LotteryMachineOutputView.kt b/src/main/kotlin/lotto/view/LotteryMachineOutputView.kt new file mode 100644 index 0000000000..1a040b30d1 --- /dev/null +++ b/src/main/kotlin/lotto/view/LotteryMachineOutputView.kt @@ -0,0 +1,29 @@ +package lotto.view + +import lotto.domain.Lotteries +import lotto.domain.LotteryMatchCount +import java.math.BigDecimal + +object LotteryMachineOutputView { + fun printLotteries(lotteries: Lotteries) { + println("${lotteries.count()}개를 구매했습니다.") + + for (it in lotteries.lotteries) { + println("[${it.getLottoNumbers().joinToString(", ")}]") + } + } + + fun printResult(result: LotteryMatchCount, returnRate: BigDecimal) { + println( + """ + |당첨 통계 + |--------- + |3개 일치 (5000원)- ${result.matchCount[3] ?: 0}개 + |4개 일치 (50000원)- ${result.matchCount[4] ?: 0}개 + |5개 일치 (1500000원)- ${result.matchCount[5] ?: 0}개 + |6개 일치 (2000000000원)- ${result.matchCount[6] ?: 0}개 + |총 수익률은 ${returnRate}입니다. + """.trimIndent() + ) + } +} diff --git a/src/test/kotlin/lotto/domain/LotteryMachineTest.kt b/src/test/kotlin/lotto/domain/LotteryMachineTest.kt index 5ac9c051dd..e1fdb04c1a 100644 --- a/src/test/kotlin/lotto/domain/LotteryMachineTest.kt +++ b/src/test/kotlin/lotto/domain/LotteryMachineTest.kt @@ -1,18 +1,18 @@ package lotto.domain +import io.kotest.matchers.comparables.shouldBeEqualComparingTo +import io.kotest.matchers.maps.shouldNotHaveKey import io.kotest.matchers.shouldBe -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.math.BigDecimal +import java.util.stream.Stream internal class LotteryMachineTest { - lateinit var sut: LotteryMachine - - @BeforeEach - fun beforeEach() { - sut = LotteryMachine() - } @DisplayName("구입 금액에 해당하는 로또를 발급한다") @Test @@ -24,7 +24,7 @@ internal class LotteryMachineTest { 10000 to 10 ).map { (payAmount, howManyBought) -> { - val result = sut.buyLotteries(payAmount) + val result = LotteryMachine.buyLotteries(payAmount) result.count() shouldBe howManyBought } @@ -32,9 +32,9 @@ internal class LotteryMachineTest { } @Test - fun getResult() { + fun getMatchResult() { - val result = sut.getResult( + val result = LotteryMachine.getMatchCount( Lotteries( Lottery(1, 2, 13, 14, 15, 16), Lottery(11, 12, 13, 4, 5, 6), @@ -43,8 +43,30 @@ internal class LotteryMachineTest { Lottery(1, 2, 3, 4, 5, 6) ) assertAll( - { result.matchCount(2) shouldBe 1 }, - { result.matchCount(3) shouldBe 2 } + { result.matchCount[2] shouldBe 1 }, + { result.matchCount[3] shouldBe 2 }, + { listOf(0, 1, 4, 5).forEach { result.matchCount shouldNotHaveKey it } } + ) + } + + @DisplayName("수익률을 구한다 (소숫점 2자리수에서 반올림한다)") + @ParameterizedTest + @MethodSource("calculateReturnRateProvider") + fun calculateReturnRate(payAmount: Int, countSameLottoNumber: Map, expected: BigDecimal) { + + LotteryMachine.calculateReturnRate( + payAmount, + LotteryMatchCount(countSameLottoNumber) + ) shouldBeEqualComparingTo expected + } + + companion object { + @JvmStatic + fun calculateReturnRateProvider(): Stream = Stream.of( + Arguments.of(14000, mapOf(3 to 1, 2 to 1), BigDecimal.valueOf(0.35)), + Arguments.of(10000, mapOf(1 to 3), BigDecimal.ZERO), + Arguments.of(30000, mapOf(4 to 1, 3 to 1, 2 to 1), BigDecimal.valueOf(1.83)), + Arguments.of(20000, mapOf(5 to 1, 4 to 1), BigDecimal.valueOf(77.5)) ) } } From d1854d77587cb392bc8ae1aa8cc302f76efcb1b0 Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Sun, 18 Dec 2022 21:13:34 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor=20:=20=EB=8B=B9=EC=B2=A8=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9E=85=EB=A0=A5=EC=8B=9C=20null=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/view/LotteryMachineInputView.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/lotto/view/LotteryMachineInputView.kt b/src/main/kotlin/lotto/view/LotteryMachineInputView.kt index 3a7c54fcd3..facab6d227 100644 --- a/src/main/kotlin/lotto/view/LotteryMachineInputView.kt +++ b/src/main/kotlin/lotto/view/LotteryMachineInputView.kt @@ -10,6 +10,8 @@ object LotteryMachineInputView { fun inputLastWinningNumbers(): List { println("지난 주 당첨 번호를 입력해 주세요.") - return readLine()?.split(",")?.map { it.toIntOrNull() ?: 0 } ?: throw IllegalArgumentException() + return readLine().orEmpty() + .split(",") + .map { it.toIntOrNull() ?: throw IllegalArgumentException() } } } From 2a75875e402406563100a306eb7ba5440c6a8686 Mon Sep 17 00:00:00 2001 From: "miaww.yun" Date: Tue, 3 Jan 2023 08:31:19 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor=20:=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/lotto/domain/LotteryMachine.kt | 3 +- src/main/kotlin/lotto/domain/Model.kt | 43 ++++++++++--------- .../lotto/view/LotteryMachineOutputView.kt | 2 +- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/main/kotlin/lotto/domain/LotteryMachine.kt b/src/main/kotlin/lotto/domain/LotteryMachine.kt index 95ff7f38ab..ef88e3df2e 100644 --- a/src/main/kotlin/lotto/domain/LotteryMachine.kt +++ b/src/main/kotlin/lotto/domain/LotteryMachine.kt @@ -5,6 +5,7 @@ import java.math.RoundingMode object LotteryMachine { private const val PRICE = 1_000 + private val LOTTO_RETURN_MAP = mapOf( 3 to 5_000, 4 to 50_000, @@ -18,7 +19,7 @@ object LotteryMachine { return Lotteries(lotteryList) } - private fun randomLottoNumbers(howMany: Int = 6) = LottoNumber.allNumbers().shuffled().subList(0, howMany) + private fun randomLottoNumbers() = LottoNumber.allNumbers().shuffled().subList(0, Lottery.COUNT) fun getMatchCount(lotteries: Lotteries, lastWinningLottery: Lottery): LotteryMatchCount { val matchCount = lotteries.lotteries diff --git a/src/main/kotlin/lotto/domain/Model.kt b/src/main/kotlin/lotto/domain/Model.kt index c921142ed3..d011e78534 100644 --- a/src/main/kotlin/lotto/domain/Model.kt +++ b/src/main/kotlin/lotto/domain/Model.kt @@ -1,6 +1,6 @@ package lotto.domain -data class Lotteries(val lotteries: List) { +class Lotteries(val lotteries: List) { constructor(vararg lotteries: Lottery) : this(lotteries.toList()) fun count(): Int { @@ -9,41 +9,27 @@ data class Lotteries(val lotteries: List) { } class Lottery(numbers: List) { - private val numbers: List + + val numbers: List constructor(vararg inputNumbers: Int) : this(inputNumbers.toList()) init { - require(numbers.size == 6) + require(numbers.size == COUNT) this.numbers = numbers.map { LottoNumber.of(it) }.sorted() } fun countSameLottoNumbers(other: Lottery): Int { return this.numbers.count { other.numbers.contains(it) } } - - fun getLottoNumbers(): List { - return numbers.map { it.value }.toList() + companion object { + const val COUNT = 6 } } class LottoNumber private constructor(val value: Int) : Comparable { init { - require(value in (1..45)) - } - - companion object { - private val NUMBERS = List(45) { LottoNumber(it + 1) } - - fun allNumbers(): List { - return NUMBERS.map { it.value } - } - - fun of(number: Int): LottoNumber { - require(number - 1 in (NUMBERS.indices)) - - return NUMBERS[number - 1] - } + require(value in (MIN_LOTTO_NUMBER..MAX_LOTTO_NUMBER)) } override fun toString(): String { @@ -68,4 +54,19 @@ class LottoNumber private constructor(val value: Int) : Comparable override fun compareTo(other: LottoNumber): Int { return this.value.compareTo(other.value) } + + companion object { + private const val MIN_LOTTO_NUMBER = 1 + private const val MAX_LOTTO_NUMBER = 45 + private val NUMBERS = List(MAX_LOTTO_NUMBER) { LottoNumber(it + MIN_LOTTO_NUMBER) } + fun allNumbers(): List { + return NUMBERS.map { it.value } + } + + fun of(number: Int): LottoNumber { + require(number - MIN_LOTTO_NUMBER in (NUMBERS.indices)) + + return NUMBERS[number - MIN_LOTTO_NUMBER] + } + } } diff --git a/src/main/kotlin/lotto/view/LotteryMachineOutputView.kt b/src/main/kotlin/lotto/view/LotteryMachineOutputView.kt index 1a040b30d1..cb29170990 100644 --- a/src/main/kotlin/lotto/view/LotteryMachineOutputView.kt +++ b/src/main/kotlin/lotto/view/LotteryMachineOutputView.kt @@ -9,7 +9,7 @@ object LotteryMachineOutputView { println("${lotteries.count()}개를 구매했습니다.") for (it in lotteries.lotteries) { - println("[${it.getLottoNumbers().joinToString(", ")}]") + println("[${it.numbers.map { it.value }.toList().joinToString(", ")}]") } }