Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# kotlin-lotto

## Step 4
### Programming Requirements
- Allow users to manually enter Lotto numbers in addition to automatically generated ones.
- Accept input for the purchase amount, the number of manually generated tickets, and their respective numbers.

## Step 3
### Programming Requirements
- Implement all features using TDD, with corresponding unit tests (excluding UI code such as System.out and System.in).
Expand Down Expand Up @@ -34,6 +39,8 @@
- [x] LottoMachine interface
- [x] AutoMachine
- [x] Generate Lotto
- [x] ManualMachine
- [x] Generate Lotto

### WinningLotto
- [x] Has one Lotto as a combination
Expand All @@ -51,7 +58,8 @@
- [x] Get purchase amount
- [x] Get winning numbers
- [x] Get bonus number
- [x] Get
- [x] Get the number of manual tickets
- [x] Get manual lotto numbers

### ResultView
- [x] Display all the lotto generated
Expand All @@ -64,6 +72,14 @@
- [x] Calculate profit
- [x] Return the count by rank

### LottoStore
- [x] Return lotto from lotto machine

### Order
- [x] Has money
- [x] Has manual lotto ticket numbers
- [x] Has manual lotto numbers

---

## Step 1 String Addition Calculator
Expand Down
23 changes: 12 additions & 11 deletions src/main/kotlin/lotto/LottoController.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
package lotto

import lotto.machine.AutoMachine
import lotto.machine.ManualMachine
import lotto.view.InputView
import lotto.view.ResultView

class LottoController(private val inputView: InputView, private val resultView: ResultView) {
fun run() {
val lottoStore = LottoStore()

val amount = inputView.getPurchaseAmount()
val ticketCount = (amount / TICKET_COST).toInt()
val winningLotto = inputView.getWinningNumbers()

val lottos = AutoMachine().generate(ticketCount)
resultView.printLottos(lottos)
val manualTicketCount = inputView.getManualTicketCount()
val manualLottoNumbers = List(manualTicketCount) { inputView.getManualLottosNumbers() }

val winningStatistics = WinningStatistics(lottos, winningLotto)
val order = Order(amount, manualTicketCount, manualLottoNumbers)

resultView.printWinningStatistics(winningStatistics)
resultView.printProfit(winningStatistics.calculateProfit(amount))
}
val lottos = lottoStore.sell(order, ManualMachine()) + lottoStore.sell(order, AutoMachine())
resultView.printLottos(order, lottos)

val winningLotto = inputView.getWinningNumbers()
val winningStatistics = WinningStatistics(lottos, winningLotto)

companion object {
private const val TICKET_COST = 1000
private const val DEFAULT = 0
resultView.printWinningStatistics(amount, winningStatistics)
}
}
12 changes: 12 additions & 0 deletions src/main/kotlin/lotto/LottoStore.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lotto

import lotto.machine.LottoMachine

class LottoStore {
fun sell(
order: Order,
machine: LottoMachine,
): List<Lotto> {
return machine.generate(order)
}
}
18 changes: 18 additions & 0 deletions src/main/kotlin/lotto/Order.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package lotto

data class Order(
private val money: Int,
val manualTicketNumber: Int,
val lottoNumbers: List<List<Int>>,
) {
init {
require((money / LOTTO_PRICE) >= manualTicketNumber) { ERROR_TICKET_NUMBER }
}

val autoTicketNumber = money / LOTTO_PRICE - manualTicketNumber

companion object {
private const val LOTTO_PRICE = 1000
private const val ERROR_TICKET_NUMBER = "Manual ticket number cannot exceed total ticket number"
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/lotto/WinningStatistics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class WinningStatistics(lottos: List<Lotto>, winningLotto: WinningLotto) {
}
}

fun calculateProfit(cost: Double): Double {
return rankCount.entries.sumOf { (rank, count) -> rank.prize * count } / cost
fun calculateProfit(cost: Int): Double {
return rankCount.entries.sumOf { (rank, count) -> rank.prize * count } / cost.toDouble()
}

fun countBy(rank: Rank): Int {
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/lotto/machine/AutoMachine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package lotto.machine

import lotto.Lotto
import lotto.LottoNumber
import lotto.Order

class AutoMachine : LottoMachine {
override fun generate(count: Int): List<Lotto> {
return List(count) { Lotto(LottoNumber.all().shuffled().take(LOTTO_NUMBER_COUNT)) }
override fun generate(order: Order): List<Lotto> {
return List(order.autoTicketNumber) { Lotto(LottoNumber.all().shuffled().take(LOTTO_NUMBER_COUNT)) }
}

companion object {
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/lotto/machine/LottoMachine.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package lotto.machine

import lotto.Lotto
import lotto.Order

interface LottoMachine {
fun generate(count: Int): List<Lotto>
fun generate(order: Order): List<Lotto>
}
11 changes: 11 additions & 0 deletions src/main/kotlin/lotto/machine/ManualMachine.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package lotto.machine

import lotto.Lotto
import lotto.LottoNumber
import lotto.Order

class ManualMachine : LottoMachine {
override fun generate(order: Order): List<Lotto> {
return order.lottoNumbers.map { lottoNumbers -> Lotto(lottoNumbers.map { LottoNumber.of(it) }) }
}
}
22 changes: 19 additions & 3 deletions src/main/kotlin/lotto/view/InputView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,32 @@ import lotto.LottoNumber
import lotto.WinningLotto

class InputView {
fun getPurchaseAmount(): Double {
fun getPurchaseAmount(): Int {
println(GUIDE_PURCHASE_AMOUNT)
return readln().toDouble()
return readln().toInt()
}

fun getWinningNumbers(): WinningLotto {
println(GUIDE_WINNING_NUMBER)
val numbers = readln().split(DELIMITER).map { LottoNumber.of(it.trim().toInt()) }
val numbers = getLottoNumbers().map { LottoNumber.of(it) }
val bonusNumber = getBonusNumber()
return WinningLotto(Lotto(numbers), bonusNumber)
}

fun getManualTicketCount(): Int {
println(GUIDE_MANUAL_TICKET_COUNT)
return readln().toInt()
}

fun getManualLottosNumbers(): List<Int> {
println(GUIDE_MANUAL_LOTTO_NUMBER)
return getLottoNumbers()
}

private fun getLottoNumbers(): List<Int> {
return readln().split(DELIMITER).map { it.trim().toInt() }
}

private fun getBonusNumber(): LottoNumber {
println(GUIDE_BONUS_NUMBER)
return LottoNumber.of(readln().toInt())
Expand All @@ -26,6 +40,8 @@ class InputView {
private const val GUIDE_PURCHASE_AMOUNT = "Please enter the purchase amount."
private const val GUIDE_WINNING_NUMBER = "Please enter last week's winning numbers"
private const val GUIDE_BONUS_NUMBER = "Please enter the bonus number"
private const val GUIDE_MANUAL_TICKET_COUNT = "Enter the number of manual tickets to purchase."
private const val GUIDE_MANUAL_LOTTO_NUMBER = "Enter the numbers for manual tickets."
private const val DELIMITER = ","
}
}
16 changes: 13 additions & 3 deletions src/main/kotlin/lotto/view/ResultView.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package lotto.view

import lotto.Lotto
import lotto.Order
import lotto.Rank
import lotto.WinningStatistics

class ResultView {
fun printWinningStatistics(winningStatistics: WinningStatistics) {
fun printWinningStatistics(
amount: Int,
winningStatistics: WinningStatistics,
) {
println(GUIDE_STATISTICS)
Rank.entries.reversed().forEach { rank ->
printLottoResult(rank, winningStatistics.countBy(rank))
}
printProfit(winningStatistics.calculateProfit(amount))
}

private fun printLottoResult(
Expand All @@ -22,11 +27,15 @@ class ResultView {
}
}

fun printProfit(profit: Double) {
private fun printProfit(profit: Double) {
println(GUIDE_PROFIT.format(profit))
}

fun printLottos(lottos: List<Lotto>) {
fun printLottos(
order: Order,
lottos: List<Lotto>,
) {
println(GUIDE_LOTTO_NUMBERS.format(order.manualTicketNumber, order.autoTicketNumber))
lottos.forEach { printLotto(it) }
}

Expand All @@ -39,5 +48,6 @@ class ResultView {
private const val GUIDE_RANK_INFO = "%d Matches%s (%,d KRW) - %d Tickets"
private const val GUIDE_STATISTICS = "Winning Statistics \n------------------"
private const val GUIDE_MATCH_BONUS = " + Bonus Ball"
private const val GUIDE_LOTTO_NUMBERS = "Purchased %d manual and %d automatic tickets"
}
}
32 changes: 32 additions & 0 deletions src/test/kotlin/lotto/LottoStoreTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package lotto

import lotto.machine.LottoMachine
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class LottoStoreTest {
@Test
fun `Return lottos according to count`() {
// given
val lottoStore = LottoStore()
val order = Order(3000, 0, emptyList())
val expected = 3

// when
val actual = lottoStore.sell(order, FakeMachine())

// then
assertThat(actual.size).isEqualTo(expected)
}

private class FakeMachine : LottoMachine {
private val lotto =
List(10) {
Lotto((1..6).map { LottoNumber.of(it) })
}

override fun generate(order: Order): List<Lotto> {
return lotto.subList(0, order.autoTicketNumber)
}
}
}
45 changes: 45 additions & 0 deletions src/test/kotlin/lotto/OrderTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package lotto

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows

class OrderTest {
@Test
fun `Throw exception when manual ticket number exceed total ticket number`() {
// given && when && then
assertThrows<IllegalArgumentException> {
Order(
1000,
2,
listOf(),
)
}
}

@Test
fun `Does not throw exception when manual ticket number exceed total ticket number`() {
// given && when && then
assertDoesNotThrow {
Order(
2000,
2,
listOf(),
)
}
}

@Test
fun `Auto ticket number can be counted by properties`() {
// given
val order = Order(2000, 1, listOf())
val expected = 1

// when
val actual = order.autoTicketNumber

// then
assertThat(actual).isEqualTo(expected)
}
}
2 changes: 1 addition & 1 deletion src/test/kotlin/lotto/WinningStatisticsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class WinningStatisticsTest {
val winningLotto = WinningLotto(Lotto((1..6).map { LottoNumber.of(it) }), bonusNumber)

val winningStatistics = WinningStatistics(lotto, winningLotto)
val cost = 1000.0
val cost = 1000
val expected = 50.0

// when
Expand Down