-
Notifications
You must be signed in to change notification settings - Fork 357
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
[Step3] 로또 제출합니다. #1132
base: 2chang5
Are you sure you want to change the base?
[Step3] 로또 제출합니다. #1132
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package lotto.domain | ||
|
||
enum class BonusMatchResult { | ||
MATCH, | ||
NOT_MATCH, | ||
NO_EFFECT, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,48 @@ | ||
package lotto.domain | ||
|
||
class Lotto(private val lottoNumberGenerator: LottoNumberGenerator) { | ||
var lottoNumbers: Set<LottoNumber> = getByAuto() | ||
var lottoNumbers: Set<LottoNumber> | ||
private set | ||
var bonusNumber: LottoNumber | ||
private set | ||
|
||
init { | ||
getByAuto().run { | ||
lottoNumbers = first | ||
bonusNumber = second | ||
} | ||
} | ||
|
||
private fun getByAuto(): Set<LottoNumber> { | ||
lottoNumberGenerator ?: return setOf() | ||
return buildSet { while (size < 6) add(LottoNumber.get(lottoNumberGenerator.generateLottoNumber())) } | ||
private fun getByAuto(): Pair<Set<LottoNumber>, LottoNumber> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다른 개발자들도 |
||
val normal = buildSet { while (size < 6) add(LottoNumber.get(lottoNumberGenerator.generateLottoNumber())) } | ||
val bonus = getBonus(normal) | ||
return Pair(normal, bonus) | ||
} | ||
|
||
fun setLottoByManual(vararg lottoNumber: Int) { | ||
private fun getBonus(normal: Set<LottoNumber>): LottoNumber { | ||
while (true) { | ||
val stepNumber = LottoNumber.get(lottoNumberGenerator.generateLottoNumber()) | ||
if (stepNumber !in normal) return stepNumber | ||
} | ||
} | ||
|
||
fun setLottoByManual( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 개인적으로 private 함수보다는 public 함수를 먼저 위치시키는 것을 선호합니다. 다른 개발자들이 이 클래스의 역할을 파악할 때 public으로 외부에 드러난 함수를 먼저 보고, 그 다음에 숨겨진 함수를 보는 것이 자연스럽다고 느껴질 것 같아요. (이 내용을 정리한 레퍼런스가 있었는데 못찾겠네요 😂 |
||
bonus: Int, | ||
vararg lottoNumber: Int, | ||
) { | ||
require(lottoNumber.size == LOTTO_NUMBER_COUNT) { LOTTO_NUMBER_COUNT_EXCEPTION_MESSAGE } | ||
require(lottoNumber.distinct().size == LOTTO_NUMBER_COUNT) { LOTTO_NUMBER_DISTINCT_MESSAGE } | ||
require(bonus !in lottoNumber) { LOTTO_NUMBER_DISTINCT_MESSAGE } | ||
lottoNumbers = lottoNumber.map { LottoNumber.get(it) }.toSet() | ||
bonusNumber = LottoNumber.get(bonus) | ||
} | ||
|
||
fun match(winningNumber: List<LottoNumber>): MatchingResult? = | ||
MatchingResult.fromMatchNumber(lottoNumbers.intersect(winningNumber).size) | ||
fun match( | ||
winningNumber: List<LottoNumber>, | ||
bonusNumber: LottoNumber, | ||
): MatchingResult? { | ||
return MatchingResult.getResult(lottoNumbers.intersect(winningNumber).size, bonusNumber == this.bonusNumber) | ||
} | ||
Comment on lines
+40
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 데이터를 꺼내어(get) 조작하지 않고, 메세지를 던져 객체가 일하도록 구조를 잘 만들어주셨네요! |
||
|
||
companion object { | ||
private const val LOTTO_NUMBER_COUNT = 6 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,16 +6,20 @@ class LottoBunch(val value: List<Lotto>) { | |
lottoNumberGenerator: LottoNumberGenerator = RandomGenerator, | ||
) : this(List(purchaseCount) { Lotto(lottoNumberGenerator) }) | ||
|
||
fun getMatchLottoResult(winningNumbers: List<LottoNumber>): Map<MatchingResult, Int> { | ||
val matchResults = value.mapNotNull { lotto -> lotto.match(winningNumbers) } | ||
fun getMatchLottoResult( | ||
winningNumbers: List<LottoNumber>, | ||
bonusNumber: LottoNumber, | ||
): Map<MatchingResult, Int> { | ||
val matchResults = value.mapNotNull { lotto -> lotto.match(winningNumbers, bonusNumber) } | ||
return MatchingResult.getMatchLottoResult(matchResults) | ||
} | ||
|
||
fun getYield( | ||
winningNumbers: List<LottoNumber>, | ||
bonusNumber: LottoNumber, | ||
purchaseAmount: Int, | ||
): Double { | ||
val matchResults: Map<MatchingResult, Int> = getMatchLottoResult(winningNumbers) | ||
val matchResults: Map<MatchingResult, Int> = getMatchLottoResult(winningNumbers, bonusNumber) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다른 개발자들도 어떤 의미인지 한 눈에 이해할 수 있도록 Kotlin의 named arguments를 활용해보시면 어떨까요~? |
||
val totalPrize = | ||
matchResults.entries.fold(0) { acc, (matchingResult, winCount) -> | ||
acc + (matchingResult.prizeAmount * winCount) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,36 @@ | ||
package lotto.domain | ||
|
||
enum class MatchingResult(val prizeAmount: Int, val matchNumber: Int) { | ||
MATCHED_THREE(5_000, 3), | ||
MATCHED_FOUR(50_000, 4), | ||
MATCHED_FIVE(1_500_000, 5), | ||
MATCHED_SIX(2_000_000_000, 6), ; | ||
typealias MatchValues = Pair<Int, BonusMatchResult> | ||
|
||
enum class MatchingResult(val prizeAmount: Int, val matchNumber: Int, val bonusMatchResult: BonusMatchResult) { | ||
MATCHED_THREE(5_000, 3, BonusMatchResult.NO_EFFECT), | ||
MATCHED_FOUR(50_000, 4, BonusMatchResult.NO_EFFECT), | ||
MATCHED_FIVE(1_500_000, 5, BonusMatchResult.NOT_MATCH), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NOT_MATCH와 NO_EFFECT가 의미있게 구분되어야 하는 이유가 있을까요? |
||
MATCHED_FIVE_WITH_BONUS(30_000_000, 5, BonusMatchResult.MATCH), | ||
MATCHED_SIX(2_000_000_000, 6, BonusMatchResult.NO_EFFECT), ; | ||
|
||
companion object { | ||
private const val MATCH_NUMBER_TRANSFER_ERROR_MESSAGE = "로또 결과 이넘값 변환 오류가 발생하였습니다." | ||
private val matchNumberToMatchResultMap = entries.associateBy { it.matchNumber } | ||
private const val RELATED_BONUS_MATCH_NUMBER = 5 | ||
Comment on lines
+8
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 5라는 숫자가 이렇게 3번 선언될 필요가 있을까요? 정답은 없을 것 같은데 어떻게 생각하시나요? |
||
private val matchValuesToMatchResultMap = | ||
entries.associateBy { result -> | ||
MatchValues(result.matchNumber, result.bonusMatchResult) | ||
} | ||
|
||
fun getResult( | ||
matchNumber: Int, | ||
isBonusMatched: Boolean, | ||
): MatchingResult? { | ||
val bonusMatchResult = getBonusMatchResult(matchNumber, isBonusMatched) | ||
return matchValuesToMatchResultMap[MatchValues(matchNumber, bonusMatchResult)] | ||
} | ||
|
||
fun fromMatchNumber(matchNumber: Int): MatchingResult? = matchNumberToMatchResultMap[matchNumber] | ||
private fun getBonusMatchResult( | ||
matchNumber: Int, | ||
isBonusMatched: Boolean, | ||
): BonusMatchResult { | ||
if (matchNumber != RELATED_BONUS_MATCH_NUMBER) return BonusMatchResult.NO_EFFECT | ||
return if (isBonusMatched) BonusMatchResult.MATCH else BonusMatchResult.NOT_MATCH | ||
} | ||
|
||
fun getMatchLottoResult(matchResults: List<MatchingResult>): Map<MatchingResult, Int> = | ||
entries.associateWith { matchResult -> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,48 +8,67 @@ class LottoBunchTest : StringSpec({ | |
val classUnderTest: LottoBunch = | ||
getLottoBunch( | ||
listOf( | ||
listOf(1, 2, 3, 4, 5, 6), | ||
listOf(1, 2, 3, 4, 5, 6), | ||
listOf(1, 2, 3, 4, 5, 6), | ||
listOf(1, 2, 3, 4, 5, 6), | ||
listOf(1, 2, 3, 4, 5, 45), | ||
listOf(1, 2, 3, 4, 5, 45), | ||
listOf(1, 2, 3, 4, 5, 45), | ||
listOf(1, 2, 3, 4, 44, 45), | ||
listOf(1, 2, 3, 4, 44, 45), | ||
listOf(1, 2, 3, 43, 44, 45), | ||
Pair(listOf(1, 2, 3, 4, 5, 6), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 6), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 6), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 6), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 45), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 45), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 45), 30), | ||
Pair(listOf(1, 2, 3, 4, 44, 45), 30), | ||
Pair(listOf(1, 2, 3, 4, 44, 45), 30), | ||
Pair(listOf(1, 2, 3, 43, 44, 45), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 45), 15), | ||
Pair(listOf(1, 2, 3, 4, 5, 45), 15), | ||
), | ||
) | ||
val winningNumbers = listOf(1, 2, 3, 4, 5, 6).map { LottoNumber.get(it) } | ||
val bonusNumber = LottoNumber.get(15) | ||
val expected = | ||
mapOf( | ||
MatchingResult.MATCHED_THREE to 1, | ||
MatchingResult.MATCHED_FOUR to 2, | ||
MatchingResult.MATCHED_FIVE to 3, | ||
MatchingResult.MATCHED_SIX to 4, | ||
MatchingResult.MATCHED_FIVE_WITH_BONUS to 2, | ||
) | ||
classUnderTest.getMatchLottoResult(winningNumbers) shouldBe expected | ||
classUnderTest.getMatchLottoResult(winningNumbers, bonusNumber) shouldBe expected | ||
} | ||
|
||
"수익률을 계산한다." { | ||
val classUnderTest: LottoBunch = | ||
getLottoBunch( | ||
listOf( | ||
listOf(1, 2, 3, 4, 5, 6), | ||
listOf(1, 2, 3, 4, 5, 45), | ||
listOf(1, 2, 3, 4, 44, 45), | ||
listOf(1, 2, 3, 43, 44, 45), | ||
Pair(listOf(1, 2, 3, 4, 5, 6), 30), | ||
Pair(listOf(1, 2, 3, 4, 5, 45), 15), | ||
Pair(listOf(1, 2, 3, 4, 5, 45), 30), | ||
Pair(listOf(1, 2, 3, 4, 44, 45), 30), | ||
Pair(listOf(1, 2, 3, 43, 44, 45), 30), | ||
), | ||
) | ||
val winningNumbers = listOf(1, 2, 3, 4, 5, 6).map { LottoNumber.get(it) } | ||
val bonusNumber = LottoNumber.get(15) | ||
val purchaseAmount = 10000 | ||
val totalPrize = MatchingResult.entries.map { it.prizeAmount }.sum() | ||
|
||
classUnderTest.getYield(winningNumbers, purchaseAmount) shouldBe totalPrize.toDouble() / purchaseAmount | ||
classUnderTest.getYield( | ||
winningNumbers, | ||
bonusNumber, | ||
purchaseAmount, | ||
) shouldBe totalPrize.toDouble() / purchaseAmount | ||
} | ||
}) { | ||
private companion object { | ||
fun getLottoBunch(numbers: List<List<Int>>): LottoBunch = | ||
LottoBunch(numbers.map { Lotto(RandomGenerator).apply { setLottoByManual(*it.toIntArray()) } }) | ||
fun getLottoBunch(numbers: List<Pair<List<Int>, Int>>): LottoBunch = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다른 개발자들도 |
||
LottoBunch( | ||
numbers.map { | ||
Lotto(RandomGenerator).apply { | ||
setLottoByManual( | ||
it.second, | ||
*it.first.toIntArray(), | ||
) | ||
} | ||
}, | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우리가 일반적으로 로또를 구매할 때 보너스 번호를 같이 들고 있나요?
https://dhlottery.co.kr/common.do?method=main