Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[로또] 김지원 제출합니다. #99

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
10d29c7
docs(README): Create README for feature documentation
kimgwon Nov 4, 2024
fb2f2cc
feat(Input): Create getPurchaseAmount() function for user input
kimgwon Nov 4, 2024
bd0dc2e
feat(Input): Create getLotteryNumbers() function for user input
kimgwon Nov 4, 2024
9972422
feat(Input): Create getLotteryBonusNumber() function for user input
kimgwon Nov 4, 2024
c2b2a93
feat(InputValidation): Create lottoNumbers() function for validation …
kimgwon Nov 4, 2024
ca8c518
feat(LottoController): Create getLottoTicketCount() function for calc…
kimgwon Nov 4, 2024
4641e64
feat(LottoController): Add createRandomLotto() function for creating …
kimgwon Nov 4, 2024
b146422
feat(LottoController): Add createResult() function for creating lotto…
kimgwon Nov 4, 2024
747c80d
feat(Output): Add printLottoNumbers() function for printing lotto num…
kimgwon Nov 4, 2024
a31aa4f
feat(Lotto): Add getLottoNumbers() function for get lotto numbers
kimgwon Nov 4, 2024
3184f14
feat(LottoController): Add calculateTotalMatchResult() function for c…
kimgwon Nov 4, 2024
ecc058a
refactor(InputValidation): Modify lottoNumberNotDuplicate() function …
kimgwon Nov 4, 2024
b92ca75
fix(LottoController): Modify logic of prize count
kimgwon Nov 4, 2024
43e8a7e
feat(LottoController): Add calculateRateOfReturn() function to lotto'…
kimgwon Nov 4, 2024
4e2a2f8
fix(Input): Add validation for unique lotto numbers in 1..45 range
kimgwon Nov 4, 2024
c9c8711
chore(Input): Add println() to change line
kimgwon Nov 4, 2024
1e3e316
chore(LottoController): Add value amount to calculate rate of return
kimgwon Nov 4, 2024
d5348f0
feat(LottoController): Add runMachine() function to run lotto machine
kimgwon Nov 4, 2024
ab20982
fix(Lotto): Change condition in calculateMatchResult() function
kimgwon Nov 4, 2024
5f81f93
feat(Output): Add printLottoNumbers() to show lotto numbers
kimgwon Nov 4, 2024
61442b0
feat(Output): Add printTotalLottoPrize() to show total lotto prize
kimgwon Nov 4, 2024
4afc986
refactor: Extract common constant
kimgwon Nov 4, 2024
d0171b6
test(LottoTest): Create test
kimgwon Nov 4, 2024
ae72b41
refactor(Error): Extract common constant
kimgwon Nov 4, 2024
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
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,37 @@
# kotlin-lotto-precourse
## 입력
`camp.nextstep.edu.missionutils.Console`의 `readLine()을 활용
- [ ] `구입금액을 입력해 주세요.` 로또 구입 금액을 입력받는다.
- [ ] `당첨 번호를 입력해 주세요.` 당첨 번호를 입력 받는다. 쉼표를 기준으로 구분한다.
- [ ] `보너스 번호를 입력해 주세요.` 보너스 번호를 입력받는다.

## Lotto
- 변수1 `번호 리스트`
- 변수2 `당첨 금액`
- 함수1 번호 정렬해서 번호 리스트에 저장
- 함수2 번호가 몇 개 맞는지 체크해서 저장

## LottoController
- [ ] 로또 발매 (1,000원당 1장). `camp.nextstep.edu.missionutils.Randoms`의 `pickUniqueNumbersInRange()`를 활용.
- [ ] 수익률 계산

## LottoConstant
- 1등: 6개 번호 일치 / 2,000,000,000원
- 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원
- 3등: 5개 번호 일치 / 1,500,000원
- 4등: 4개 번호 일치 / 50,000원
- 5등: 3개 번호 일치 / 5,000원

## 출력
- [ ] `$num개를 구매했습니다.` 발행한 로또 수량 및 번호를 출력한다. 로또 번호는 오름차순으로 정렬하여 보여준다.
- [ ] `당첨 통계` 당첨 내역을 출력한다.
- [ ] `총 수익률 $profitRate` 수익률을 출력한다.

## 예외
- [ ] 구입 금액은 숫자만 가능하다.
- [ ] 로또 구입 금액이 1,000원 단위로 나누어 떨어지지 않는 경우 예외 처리한다.
- [ ] 당첨 번호는 숫자여야 한다.
- [ ] 당첨 번호는 6개여야 한다.
- [ ] 보너스 번호는 숫자여야 한다.
- [ ] 보너스 번호는 1개여야 한다.
- [ ] 로또 번호는 1~45 사이의 숫자여야 한다.
10 changes: 9 additions & 1 deletion src/main/kotlin/lotto/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package lotto

import lotto.controller.LottoController
import lotto.view.Input
import lotto.view.Output

fun main() {
// TODO: 프로그램 구현
val input: Input = Input()
val output: Output = Output()
val lottoController: LottoController = LottoController(input, output)

lottoController.runMachine()
}
9 changes: 0 additions & 9 deletions src/main/kotlin/lotto/Lotto.kt

This file was deleted.

9 changes: 9 additions & 0 deletions src/main/kotlin/lotto/constant/Error.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package lotto.constant

object Error {
const val NOT_NUMBER = "[ERROR] 숫자를 입력해주세요. %s은 숫자가 아닙니다."
const val UNIT_OF_PRICE = "[ERROR] %d원 단위로 입력해주세요."
const val LOTTO_RANGE = "[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."
const val LOTTO_SIZE = "[ERROR] 당첨 번호는 6개의 숫자로 입력해주세요."
const val LOTTO_DUPLICATE = "[ERROR] 번호는 중복되면 안됩니다."
}
15 changes: 15 additions & 0 deletions src/main/kotlin/lotto/constant/Message.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package lotto.constant

object Message {
const val INFO_GET_PURCHASE_AMOUNT = "구입금액을 입력해 주세요."
const val INFO_GET_LOTTO_NUMBERS = "당첨 번호를 입력해 주세요."
const val INFO_GET_LOTTO_BONUS_NUMBERS = "보너스 번호를 입력해주세요."

const val COMMA = ","

const val TICKET_PRICE = 1000


const val LOTTO_RESULT_MESSAGE = "당첨 통계\n---"
const val LOTTO_RATE_OF_RETURN = "총 수익률은 %f%입니다."
}
69 changes: 69 additions & 0 deletions src/main/kotlin/lotto/controller/LottoController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package lotto.controller

import camp.nextstep.edu.missionutils.Randoms.pickUniqueNumbersInRange
import lotto.constant.Message.TICKET_PRICE
import lotto.model.Lotto
import lotto.model.LottoTotalMatchResult
import lotto.validation.InputValidation
import lotto.view.Input
import lotto.view.Output

class LottoController(val input: Input, val output: Output) {
private val lottos: MutableList<Lotto> = mutableListOf()
private val validator = InputValidation()

private fun getLottoTicketCount(amount: Int): Int {
return amount / TICKET_PRICE
}

private fun createRandomLotto(): Lotto {
return Lotto(pickUniqueNumbersInRange(1, 45, 6).sorted())
}

private fun createResult(): List<Int> {
val numbers = input.getLottoNumbers()
val bonusNumbers = input.getLottoBonusNumber()
val resultNumbers: MutableList<Int> = mutableListOf()

repeat(numbers.size) {
resultNumbers.add(numbers[it])
}
resultNumbers.add(bonusNumbers)

return validator.lottoNumbersNotDuplicate(resultNumbers.toList())
}

private fun calculateTotalMatchResult(result: List<Int>): LottoTotalMatchResult {
val totalMatchResult = LottoTotalMatchResult()
repeat(lottos.size) { idx ->
val matchResult = lottos[idx].calculateMatchResult(result)
when (matchResult.matchNumbersCount) {
6 -> totalMatchResult.prizeCount1 += 1
5 -> if (matchResult.isMatchBonus) totalMatchResult.prizeCount2 += 1 else totalMatchResult.prizeCount3 += 1
4 -> totalMatchResult.prizeCount4 += 1
3 -> totalMatchResult.prizeCount5 += 1
}
}
return totalMatchResult
}

private fun calculateRateOfReturn(totalMatchResult: LottoTotalMatchResult, amount: Int): Float {
return String.format("%.1f", totalMatchResult.getTotalPrize() / amount).toFloat()
}

fun runMachine() {
val amount = input.getPurchaseAmount()
val count = getLottoTicketCount(amount)
repeat(count) {
val lotto: Lotto = createRandomLotto()
lottos.add(lotto)
}

output.printLottoNumbers(lottos)

val result = createResult()
val totalMatchResult = calculateTotalMatchResult(result)
val rateOfReturn = calculateRateOfReturn(totalMatchResult, amount)
output.printTotalLottoPrize(totalMatchResult, rateOfReturn)
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/lotto/model/Lotto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lotto.model

import lotto.validation.InputValidation

class Lotto(private val numbers: List<Int>) {
private val validator = InputValidation()

init {
validator.lottoNumbers(numbers)
}

fun getLottoNumbers(): List<Int> {
return numbers
}

fun calculateMatchResult(resultNumbers: List<Int>): LottoMatchResult {
val numbersForCheck = numbers.toSet()
val lottoResult = LottoMatchResult()

repeat(resultNumbers.size - 1) { idx ->
if (resultNumbers[idx] in numbersForCheck) lottoResult.matchNumbersCount += 1
}
if (resultNumbers.last() in numbersForCheck) lottoResult.isMatchBonus = true

return lottoResult
}
}
6 changes: 6 additions & 0 deletions src/main/kotlin/lotto/model/LottoMatchResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package lotto.model

data class LottoMatchResult(
var matchNumbersCount: Int = 0,
var isMatchBonus: Boolean = false
)
16 changes: 16 additions & 0 deletions src/main/kotlin/lotto/model/LottoTotalMatchResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package lotto.model

data class LottoTotalMatchResult(
var prizeCount1: Int = 0,
var prizeCount2: Int = 0,
var prizeCount3: Int = 0,
var prizeCount4: Int = 0,
var prizeCount5: Int = 0
) {
private fun getPrize1(): Int = prizeCount1 * 2000000000
private fun getPrize2(): Int = prizeCount2 * 30000000
private fun getPrize3(): Int = prizeCount3 * 1500000
private fun getPrize4(): Int = prizeCount4 * 50000
private fun getPrize5(): Int = prizeCount5 * 5000
fun getTotalPrize(): Float = (getPrize1() + getPrize2() + getPrize3() + getPrize4() + getPrize5()).toFloat()
}
50 changes: 50 additions & 0 deletions src/main/kotlin/lotto/validation/InputValidation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package lotto.validation

import lotto.constant.Error.LOTTO_DUPLICATE
import lotto.constant.Error.LOTTO_RANGE
import lotto.constant.Error.LOTTO_SIZE
import lotto.constant.Error.NOT_NUMBER
import lotto.constant.Error.UNIT_OF_PRICE
import lotto.constant.Message.COMMA

class InputValidation {
fun typeInt(input: String): Int {
return input.toIntOrNull() ?: throw IllegalArgumentException(NOT_NUMBER.format(input))
}

fun unitOfPrice(price: Int, unit: Int): Int {
if (price % unit == 0)
return price
throw IllegalArgumentException(UNIT_OF_PRICE.format(unit))
}

fun lottoNumbersDelimiter(input: String): List<Int> {
return input.split(COMMA).map {
typeInt(it)
}
}

fun lottoNumberRange(number: Int): Int {
require(number in 1..45) { LOTTO_RANGE }
return number
}

private fun lottoNumbersSize(numbers: List<Int>): List<Int> {
require(numbers.size == 6) { LOTTO_SIZE }
return numbers
}

fun lottoNumbersNotDuplicate(numbers: List<Int>): List<Int> {
require(numbers.toSet().size == numbers.size) { LOTTO_DUPLICATE }
return numbers
}

fun lottoNumbers(numbers: List<Int>): List<Int> {
return numbers.map {
lottoNumberRange(it)
}.also {
lottoNumbersSize(it)
lottoNumbersNotDuplicate(it)
}
}
}
45 changes: 45 additions & 0 deletions src/main/kotlin/lotto/view/Input.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package lotto.view

import camp.nextstep.edu.missionutils.Console
import lotto.constant.Message.INFO_GET_LOTTO_BONUS_NUMBERS
import lotto.constant.Message.INFO_GET_LOTTO_NUMBERS
import lotto.constant.Message.INFO_GET_PURCHASE_AMOUNT
import lotto.constant.Message.TICKET_PRICE
import lotto.validation.InputValidation

class Input {
private val inputValidation = InputValidation()

fun getPurchaseAmount(): Int {
println(INFO_GET_PURCHASE_AMOUNT)
val value = readLine().let {
inputValidation.typeInt(it).also { amount ->
inputValidation.unitOfPrice(amount, TICKET_PRICE)
}
}
println()
return value
}

fun getLottoNumbers(): List<Int> {
println(INFO_GET_LOTTO_NUMBERS)
val value = readLine().let {
inputValidation.lottoNumbers(inputValidation.lottoNumbersDelimiter(it))
}
println()
return value
}

fun getLottoBonusNumber(): Int {
println(INFO_GET_LOTTO_BONUS_NUMBERS)
val value = readLine().let {
inputValidation.typeInt(it).also { number ->
inputValidation.lottoNumberRange(number)
}
}
println()
return value
}

private fun readLine(): String = Console.readLine()
}
26 changes: 26 additions & 0 deletions src/main/kotlin/lotto/view/Output.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package lotto.view

import lotto.constant.Message.LOTTO_RATE_OF_RETURN
import lotto.constant.Message.LOTTO_RESULT_MESSAGE
import lotto.model.Lotto
import lotto.model.LottoTotalMatchResult

class Output {
fun printLottoNumbers(lottos: List<Lotto>) {
println("${lottos.size}개를 구매했습니다.")
repeat(lottos.size) { idx ->
println(lottos[idx].getLottoNumbers())
}
println()
}

fun printTotalLottoPrize(totalMatchResult: LottoTotalMatchResult, rateOfReturn: Float) {
println(LOTTO_RESULT_MESSAGE)
println("3개 일치 (5,000원) - ${totalMatchResult.prizeCount5}개")
println("4개 일치 (50,000원) - ${totalMatchResult.prizeCount4}개")
println("5개 일치 (1,500,000원) - ${totalMatchResult.prizeCount3}개")
println("5개 일치, 보너스 볼 일치 (30,000,000원) - ${totalMatchResult.prizeCount2}개")
println("6개 일치 (2,000,000,000원) - ${totalMatchResult.prizeCount1}개")
println(LOTTO_RATE_OF_RETURN.format(rateOfReturn))
}
}
14 changes: 14 additions & 0 deletions src/test/kotlin/lotto/LottoTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package lotto

import lotto.model.Lotto
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

Expand All @@ -20,4 +21,17 @@ class LottoTest {
}

// TODO: 추가 기능 구현에 따른 테스트 코드 작성
@Test
fun `로또 번호가 1보다 작은 숫자면 예외가 발생한다`() {
assertThrows<IllegalArgumentException> {
Lotto(listOf(0, 2, 3, 4, 5, 6))
}
}

@Test
fun `로또 번호가 45보다 큰 숫자면 예외가 발생한다`() {
assertThrows<IllegalArgumentException> {
Lotto(listOf(1, 2, 3, 4, 5, 46))
}
}
}