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

[로또] 버디 미션 제출합니다. #12

Open
wants to merge 7 commits into
base: stopmin-main
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

.idea/workspace.xml
3 changes: 2 additions & 1 deletion kotlin-lotto/src/main/kotlin/lotto/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package lotto

fun main() {
TODO("프로그램 구현")
val lottoGame = LottoGame()
lottoGame.run()
}
23 changes: 21 additions & 2 deletions kotlin-lotto/src/main/kotlin/lotto/Lotto.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
package lotto

class Lotto(private val numbers: List<Int>) {
class Lotto(val numbers: List<Number>) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필드를 외부에 노출시키지 않도록, 캡슐화를 지키도록 코드 작성해보셔도 좋을 것 같습니다~!

init {
require(numbers.size == 6)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

init()에서 중복 검사하면 중복 테스트 케이스 통과 할 것 같아요!
numbers를 set으로 바꿔서 사이즈 체크를 한다던가 등 여러 방법이 있을 것 같습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오~! 그렇군요 이거 그냥 예제코드에 있어서 그대로 썼는데 set도 꽤 좋은 것 같습니닷

}
}

enum class LottoPrize(
val matchCount: Int,
val prize: Int,
val description: String,
val isBonusMatched: Boolean = false
) {
THREE_MATCH(3, 5_000, "3개 일치 (5,000원)"),
FOUR_MATCH(4, 50_000, "4개 일치 (50,000원)"),
FIVE_MATCH(5, 1_500_000, "5개 일치 (1,500,000원)"),
FIVE_PLUS_BONUS_MATCH(5, 30_000_000, "5개 일치, 보너스 볼 일치 (30,000,000원)", true),
SIX_MATCH(6, 2_000_000_000, "6개 일치 (2,000,000,000원)"),
LOSE(0, 0, "낙첨");

// TODO: 추가 기능 구현
companion object {
fun findPrize(matchCount: Int, isBonusMatched: Boolean = false): LottoPrize {
return entries.find { it.matchCount == matchCount && it.isBonusMatched == isBonusMatched } ?: LOSE
}
}
Comment on lines +15 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

간결하게 잘 구현하신 것 같아요 👍

}

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

import lotto.LottoPrize.Companion.findPrize


class LottoGame {
companion object {
val lottoStore = LottoStore()
}

fun run() {
val (purchasePrice, lottoCount) = lottoStore.receivePaymentForLotto()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

component N 사용하니 좋네요! 👍🏻

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저번에 빅터가 쓴거 보고 따라해봤습니닷 ㅎㅎ

println("${lottoCount}개를 구매했습니다.")
val generateLottoTickets = lottoStore.generateLottoTickets(lottoCount)

printGenerateTickets(generateLottoTickets)

val winningNumbers = lottoStore.askWinningNumbers()
val bonusNumber = lottoStore.askBonusNumber()

val (prizeList, prizeMoney) = checkPrizes(generateLottoTickets, winningNumbers, bonusNumber)
val returnRate = getReturnRate(purchasePrice, prizeMoney)

printGameResult(prizeList, returnRate)
}

private fun getReturnRate(purchasePrice: Int, prizeMoney: Long): Double =
(prizeMoney.toDouble() / purchasePrice.toDouble() * 100.0)

private fun printGameResult(prizeList: List<LottoPrize>, returnRate: Double) {
println("당첨 통계")
println("---")


val result = prizeList.map { it.name }.groupingBy { it }.eachCount()

listOf(
LottoPrize.THREE_MATCH,
LottoPrize.FOUR_MATCH,
LottoPrize.FIVE_MATCH,
LottoPrize.FIVE_PLUS_BONUS_MATCH,
LottoPrize.SIX_MATCH,
).map { o ->
Comment on lines +37 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리스트를 생성하지 않아도 LottoPrize.entries를 사용하여 전체를 순회하는 방식으로 코드 작성할 수 있을 것 같습니다!

println("${o.description} - ${result.get(o.name) ?: 0}개")
}

println("총 수익률은 ${returnRate}%입니다.")
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

당첨 통계부분 간결하네요 ㄷㄷ
map으로 다루는 부분 배워갑니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엄 사실 map으로 사용한건 마음에 든다만 보름이 위에서 언급했듯이 굳이 리스트를 만든게 조금 문제였던 것 같아 좋은 코드인건 아닌 것 같네요 ㅠㅠㅠ

다음에 참고하실 때 보름님 말씀대로 LottoPrize.entries에서 map을 쓴다면 더 간결하고 좋은 코드일 것 같습니다!!!


private fun printGenerateTickets(generateLottoTickets: List<Lotto>) {
for (generateLottoTicket in generateLottoTickets) {
println(generateLottoTicket.numbers)
}
println()
}
}

fun checkPrizes(
generatedLottoTickets: List<Lotto>,
winningNumbers: List<Int>,
bonusNumber: Int
): Pair<List<LottoPrize>, Long> {
var prizeMoney: Long = 0
val prizeList = generatedLottoTickets.map { lotto ->
val matchCount = lotto.numbers.count { it in winningNumbers }
val isBonusMatched = bonusNumber in lotto.numbers
findPrize(matchCount, isBonusMatched)
}

prizeList.forEach { o ->
prizeMoney += o.prize
}

return Pair(prizeList, prizeMoney)
}


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

import camp.nextstep.edu.missionutils.Console
import camp.nextstep.edu.missionutils.Randoms
import java.lang.IllegalStateException
import kotlin.jvm.Throws

//import lotto.common.require

class LottoStore {
companion object {
const val LOTTO_NUMBER_MIN = 1
const val LOTTO_NUMBER_MAX = 45
const val LOTTO_NUMBERS_PER_TICKET = 6
const val LOTTO_PRICE = 1000
Comment on lines +12 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상수 분리 👍👍👍

}

fun receivePaymentForLotto(): Pair<Int, Int> {
var purchasePrice: Int?
println("구입급액을 입력해주세요.")
do {
val input = readLine()
purchasePrice = input?.toIntOrNull()
if (purchasePrice == null) {
println("[ERROR] 숫자로 입력해야합니다.")
throw NumberFormatException("[ERROR] 숫자로 입력해야합니다.")
} else if (purchasePrice % LOTTO_PRICE != 0) {
println("[ERROR] 숫자는 $LOTTO_PRICE(으)로 나누어 떨어져야 합니다.")
purchasePrice = null // 입력이 유효하지 않으므로 null로 설정하여 다시 입력 받음
}
// 유효한 입력을 받으면 자동으로 반복문에서 탈출
} while (purchasePrice == null)

println()
return Pair(purchasePrice!!, purchasePrice / LOTTO_PRICE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Pair(purchasePrice!!, purchasePrice / LOTTO_PRICE)
return Pair(purchasePrice, purchasePrice / LOTTO_PRICE)

!!는 없어도 되네요~!

}

fun askWinningNumbers(): List<Int> {
println("당첨 번호를 입력해주세요. 번호는 쉼표(,)로 구분하여 입력하세요:")
var numbers: List<Int>
do {
numbers = readLine()?.split(",")?.mapNotNull { it.trim().toIntOrNull() } ?: emptyList()

require(numbers.size == LOTTO_NUMBERS_PER_TICKET) {
IllegalArgumentException("[ERROR] 정확히 $LOTTO_NUMBERS_PER_TICKET}개의 숫자를 입력해야 합니다.")
}

numbers.forEach { number ->
require(number in LOTTO_NUMBER_MIN..LOTTO_NUMBER_MAX) {
IllegalArgumentException("[ERROR] 숫자는 ${LOTTO_NUMBER_MIN}과 ${LOTTO_NUMBER_MAX} 사이여야 합니다.")
}
}
} while (numbers.size != LOTTO_NUMBERS_PER_TICKET
|| !numbers.all { it in LOTTO_NUMBER_MIN..LOTTO_NUMBER_MAX }
)

println()

return numbers
}

fun askBonusNumber(): Int {
println("보너스 번호를 입력해 주세요.")
var bonusNumber: Int?
do {
try {
bonusNumber = Console.readLine().toInt()
check(bonusNumber in LOTTO_NUMBER_MIN..LOTTO_NUMBER_MAX) { NumberFormatException() }
} catch (e: IllegalArgumentException) {
bonusNumber = null
println("[ERROR] 정확한 숫자 형식으로 입력해주세요.")
} catch (e: IllegalStateException) {
bonusNumber = null
println("[ERROR] 숫자는 ${LOTTO_NUMBER_MIN}과 ${LOTTO_NUMBER_MAX} 사이여야 합니다.")
}
} while (bonusNumber !is Int)
println()

return bonusNumber!!
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

askWinningNumbers()에서는 require를 사용하여 유효성을 검사하고 있지만, askBonusNumber()에서는 try-catch 구문을 사용하는데, 유효성 검사 방식을 통일하는 것은 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하!!! 맞네요!! 유효성 검사하는 방식을 통일시키고 뭔가 캡슐화 하는 것도 나쁘지 않을 것 같다는 생각이 듭니닷...!! 다음 미션에서는 반영해보겠습니다!!


fun generateLottoTickets(count: Int): List<Lotto> {
return List(count) {
generateLotto()
}
}

fun generateLotto() = Lotto(generateRandomNumbers(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_NUMBERS_PER_TICKET))

}


fun generateRandomNumbers(startInclusive: Int, endInclusive: Int, count: Int): List<Number> =
Randoms.pickUniqueNumbersInRange(startInclusive, endInclusive, count)

4 changes: 4 additions & 0 deletions kotlin-lotto/src/main/kotlin/lotto/common/ErrorCode.kt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러코드를 분리하시려고 한 흔적이 보이네요!! 😆👍

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package lotto.common

class ErrorCode {
}