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

Step3 : 로또 (2등) #602

Open
wants to merge 77 commits into
base: choiyounho
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
287d457
docs : 리드미 작성
Choiyounho Nov 24, 2022
0301978
feat : 문자열 분리 전략 기초 설계
Choiyounho Nov 24, 2022
a6cdcb2
feat : Expression 수도 코드 작성
Choiyounho Nov 24, 2022
80ae902
refactor : 수식 바꾸기 전략 구현
Choiyounho Nov 27, 2022
78b6536
feat : 수식 기본 틀 구현
Choiyounho Nov 27, 2022
9386538
feat : 스플릿 조건 클래스 기본 틀 구현
Choiyounho Nov 27, 2022
c9c948d
feat : 스플릿 유틸 구현
Choiyounho Nov 27, 2022
9dad631
feat : 계산기 기능 구현
Choiyounho Nov 27, 2022
b0e1d45
refactor : 수식 생성 전략 리팩토링
Choiyounho Nov 27, 2022
5de9f48
feat : 상수 코틀린 파일 생성
Choiyounho Nov 27, 2022
c2ddf8e
feat : 커스텀 정규식 문자 제거 확장함수 구현
Choiyounho Nov 27, 2022
2cf2fb7
feat : SplitTerms 팩토리 함수 구현
Choiyounho Nov 27, 2022
bf30795
refactor : SplitUtil 빈문자일 경우 0으로 변환 기능 구현
Choiyounho Nov 27, 2022
9db90e1
test : 수식 테스트 코드 작성
Choiyounho Nov 27, 2022
605a085
test : 수식 생성 테스트 코드 작성
Choiyounho Nov 27, 2022
12e1d0b
feat : 수식을 생성할 때 숫자만 있는 검증 로직 추가
Choiyounho Nov 27, 2022
f45cb69
test : 수식 생성 전략 테스트 코드 작성
Choiyounho Nov 27, 2022
1264c10
refactor : 수식 테스트 코드 수정
Choiyounho Nov 27, 2022
a2b890d
test : 계산기 테스트 코드 작성
Choiyounho Nov 27, 2022
2377f2c
refactor : 수식 생성전략이 있어 수식 테스트 코드 삭제
Choiyounho Nov 27, 2022
44451cb
refactor : 수식 생성 전략 테스트 코드 수정
Choiyounho Nov 27, 2022
1121e91
refactor : 수식 생성 전략 제거
Choiyounho Dec 4, 2022
ebe87bf
refactor : 수식 객체 리팩토링 및 네이밍 변경
Choiyounho Dec 4, 2022
7f89b72
refactor : 상수 파일 및 문자열 확장파일 제거
Choiyounho Dec 4, 2022
02fa475
refactor : SplitUtil 제거
Choiyounho Dec 4, 2022
25a68ac
refactor : 수식 클래스 코드 리팩토링 및 테스트 코드 작성
Choiyounho Dec 4, 2022
10414cd
refactor : 연산자 이넘 클래스 생성 및 테스트 코드 작성
Choiyounho Dec 4, 2022
8c3b02d
refactor : 정규식 유틸 생성
Choiyounho Dec 4, 2022
6825b55
refactor : 계산기 코드 리팩토링 및 테스트코드 작성
Choiyounho Dec 4, 2022
bd83bbc
refactor : 수식 클래스 생성자 private
Choiyounho Dec 4, 2022
9058a0d
refactor : 빈 수식이 들어올 경우 0으로 반환하는 코드 및 테스트 코드 작성
Choiyounho Dec 4, 2022
dc8e1fe
refactor : 구분자 테스트 코드 오타 수정
Choiyounho Dec 6, 2022
ab93e29
feat : RunTimeException Contrat
Choiyounho Dec 12, 2022
66ef1e0
refactor : 연산자 현재 사용하지 않는 코드 제거
Choiyounho Dec 12, 2022
225559a
refactor : 연산자 변경
Choiyounho Dec 12, 2022
7647619
refactor : seald class -> seald interface
Choiyounho Dec 12, 2022
fdf7d08
refactor : 계산기 코드 리팩토링
Choiyounho Dec 12, 2022
df428ea
refactor : 구분자 코드 수정
Choiyounho Dec 12, 2022
2b5432f
refactor : 수식 코드 리팩토링
Choiyounho Dec 12, 2022
1ad9f82
feat : 계산기 인터페이스 구현
Choiyounho Dec 19, 2022
3b39bd5
refactor : 문자열 덧셈 계산기 클래스로 네이밍 변경
Choiyounho Dec 19, 2022
5855986
refactor : 구분자 클래스 리팩토링
Choiyounho Dec 19, 2022
fe95b3b
refactor : 수식 클래스 리팩토링
Choiyounho Dec 19, 2022
eac142c
feat : 문자열 확장함수 구현
Choiyounho Dec 19, 2022
c9744bd
refactor : 계산기 테스트 코드 리팩토링
Choiyounho Dec 19, 2022
06e2425
refactor : 구분자 테스트 코드 리팩토링
Choiyounho Dec 19, 2022
77ec270
refactor : 수식 테스트 코드 리팩토링
Choiyounho Dec 19, 2022
a54c059
feat : 2단계 로또 구현
Choiyounho Dec 25, 2022
a6470ea
Merge branch 'choiyounho' into step2
Choiyounho Dec 25, 2022
7e1957e
refactor : div -> / 코드 수정
Choiyounho Dec 31, 2022
5a13224
Merge branch 'step2' of https://github.com/Choiyounho/kotlin-lotto in…
Choiyounho Dec 31, 2022
1d44528
refactor : 구매 가격 클래스 인라인 클래스로 변경
Choiyounho Dec 31, 2022
9332f39
refactor : printBoughtLotto 함수 OutputView로 이동
Choiyounho Dec 31, 2022
c5a6e9e
refactor : 로또 자동 생성 전략 클래스 네이밍 변경
Choiyounho Dec 31, 2022
eb0baa9
refactor : LottoGenerator -> LottoFactory 네이밍 변경
Choiyounho Dec 31, 2022
0bb5bce
refactor : WinningLotteryNumber -> WinningLotto 네이밍 변경
Choiyounho Dec 31, 2022
ede278b
refactor : Lotto, LottoNumber 네이밍 변경
Choiyounho Dec 31, 2022
3d8346f
refactor : toFloat -> toDouble 함수 변경
Choiyounho Dec 31, 2022
f9e7b4f
refactor : 로또 자동 생성 함수 구조 변경
Choiyounho Dec 31, 2022
59fc795
refactor : Lottos 함수 구조 변경
Choiyounho Dec 31, 2022
de33bb1
refactor : nullable 제거 및 전달 값 변경
Choiyounho Jan 8, 2023
0924751
refactor : Lotto inline class 변경
Choiyounho Jan 8, 2023
4ffaff3
refactor : Lottos inline class 변경 및 객체에 메세지 전달 기능 적용
Choiyounho Jan 8, 2023
60a929b
refactor : toList() 제거
Choiyounho Jan 8, 2023
b3376c9
refactor : require 적용
Choiyounho Jan 8, 2023
62a07be
refactor : 불필요한 오버로딩 제거
Choiyounho Jan 8, 2023
a5d390f
refactor : 정적 팩토리 메서드 적용
Choiyounho Jan 8, 2023
012484d
test : Lottos 테스트 코드 작성
Choiyounho Jan 8, 2023
3e94c77
refactor : ProfitRate -> LottoResult 객체 네이밍 변경
Choiyounho Jan 8, 2023
97edf42
feat : 보너스 번호 입력 ui 구현
Choiyounho Jan 8, 2023
8a32ecd
refactor : 보너스 번호 출력 및 당첨 통계 멘트 변경
Choiyounho Jan 8, 2023
974ca7e
refactor : 당첨금액 및 보너스 번호 적용
Choiyounho Jan 8, 2023
2a58990
refactor : 테스트 코드에 보너스 번호 적용
Choiyounho Jan 8, 2023
c161ea9
feat : 보너스 번호 확인 기능 구현
Choiyounho Jan 8, 2023
5ffbe75
refactor : 보너스 번호 적용하여 당첨번호 확인
Choiyounho Jan 8, 2023
b289192
refactor : 당첨번호 입력 받게 App 변경
Choiyounho Jan 8, 2023
86e93c1
Merge branch 'choiyounho' into step3
Choiyounho Jan 8, 2023
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
21 changes: 14 additions & 7 deletions src/main/kotlin/lotto/LottoApp.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package lotto

import lotto.domain.BuyPrice
import lotto.domain.ProfitRate
import lotto.domain.LottoResult
import lotto.domain.WinningLotto
import lotto.domain.strategy.LottoAutoGeneratorStrategy
import lotto.domain.strategy.LottoFactory
import lotto.ui.InputViews.inputBonusNumber
import lotto.ui.InputViews.inputPrice
import lotto.ui.InputViews.inputWinningNumber
import lotto.ui.OutputViews.printBonusNumber
import lotto.ui.OutputViews.printBoughtLotto
import lotto.ui.OutputViews.printBoughtLottos
import lotto.ui.OutputViews.printLottoMatchResult
import lotto.ui.OutputViews.printProfitRate

fun main() {
val inputPrice = inputPrice() ?: 0
val inputPrice = inputPrice()

val buyPrice = BuyPrice(inputPrice)
val lottoCount = buyPrice.getLottoCount()
Expand All @@ -22,13 +24,18 @@ fun main() {
val lottoFactory = LottoFactory()
val generateStrategy = LottoAutoGeneratorStrategy()

val lotto = lottoFactory.generate(lottoCount, generateStrategy)
val lottos = lottoFactory.generate(lottoCount, generateStrategy)

printBoughtLottos(lotto.value)
printBoughtLottos(lottos.value)

val winningNumbers = WinningLotto(inputWinningNumber()).winningNumbers
val matching = lotto.matchWinningNumbers(winningNumbers)
val winningNumber = inputWinningNumber()
val bonusNumber = inputBonusNumber()
printBonusNumber(bonusNumber)

val winningNumbers = WinningLotto.of(winningNumber, bonusNumber)

val matching = lottos.matchWinningNumbers(winningNumbers, bonusNumber)

printLottoMatchResult(matching)
printProfitRate(ProfitRate(matching, lottoCount).calculate())
printProfitRate(LottoResult(matching, inputPrice).calculate())
}
8 changes: 7 additions & 1 deletion src/main/kotlin/lotto/domain/Lotto.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
package lotto.domain

class Lotto(val value: List<Int>)
@JvmInline
value class Lotto(val value: List<Int>) {

fun findWinningCount(winningLotto: WinningLotto): Int {
return value.count { winningLotto.winningNumbers.contains(it) }
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/lotto/domain/LottoResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lotto.domain

import java.math.RoundingMode
import java.text.DecimalFormat

class LottoResult(
private val matchResult: Map<Ranking, Int>,
private val inputPrice: Int
) {

fun calculate(): Double {
val totalWinningMoney = getTotalWinningMoney()
val decimalFormat = DecimalFormat(PROFIT_RATE_PATTERN)
decimalFormat.roundingMode = RoundingMode.DOWN
val profitRate = totalWinningMoney / inputPrice.toDouble()
return decimalFormat.format(profitRate).toDouble()
}

private fun getTotalWinningMoney(): Int {
return matchResult.entries
.sumOf { it.key.getTotalWinningMoney(it.value) }
}

companion object {
private const val PROFIT_RATE_PATTERN = "#.##"
}
}
16 changes: 8 additions & 8 deletions src/main/kotlin/lotto/domain/Lottos.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package lotto.domain

class Lottos(val value: List<Lotto>) {
@JvmInline
value class Lottos(val value: List<Lotto>) {

fun matchWinningNumbers(winningNumbers: List<Int>): Map<Ranking, Int> {
return value.groupingBy { lottoNumber ->
Ranking.valueOf(
lottoNumber.value.count {
winningNumbers.contains(it)
}
)
fun matchWinningNumbers(winningNumbers: WinningLotto, bonusNumber: Int): Map<Ranking, Int> {
return value.groupingBy { lotto ->
val findWinningCount = lotto.findWinningCount(winningNumbers)
val isBonus = winningNumbers.isMatchBonus(bonusNumber)

Ranking.valueOf(findWinningCount, isBonus)
}.eachCount()
}
}
16 changes: 11 additions & 5 deletions src/main/kotlin/lotto/domain/Ranking.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ enum class Ranking(
) {

FIRST(6, 2_000_000_000),
SECOND(5, 1_500_000),
THIRD(4, 50_000),
FOURTH(3, 5_000),
SECOND(5, 30_000_000),
THIRD(5, 1_500_000),
FOURTH(4, 50_000),
FIFTH(3, 5_000),
MISS(0, 0);

fun getTotalWinningMoney(matchResultValue: Int): Int {
return winningMoney * matchResultValue
}

companion object {
fun valueOf(matchCount: Int): Ranking {
return values().find { it.matchCount == matchCount } ?: MISS
fun valueOf(matchCount: Int, isBonus: Boolean = false): Ranking {
return values().find {
if (matchCount == 5 && !isBonus) {
return THIRD
}
it.matchCount == matchCount
} ?: MISS
}
}
}
18 changes: 13 additions & 5 deletions src/main/kotlin/lotto/domain/WinningLotto.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
package lotto.domain

class WinningLotto(
inputNumbers: List<String>
val winningNumbers: List<Int>,
private val bonusNumber: Int
) {

val winningNumbers = inputNumbers.map { it.toInt() }

init {
if (winningNumbers.isEmpty() || winningNumbers.size != LIMIT_WINNING_NUMBER) {
throw IllegalArgumentException("당첨 번호는 6개 입니다.")
}
validateWinningNumber()
}

fun isMatchBonus(bonusNumber: Int): Boolean {
return this.bonusNumber == bonusNumber
}

private fun validateWinningNumber() {
for (winningNumber in winningNumbers) {
if (winningNumber !in LOTTO_FIRST_NUMBER..LOTTO_LAST_NUMBER) {
throw IllegalArgumentException("당첨 번호는 1 ~ 45 범위의 숫자로만 구성될 수 있습니다.")
require(winningNumber in LOTTO_FIRST_NUMBER..LOTTO_LAST_NUMBER) {
MESSAGE_LOTTO_RANGE
}
}
}
Expand All @@ -26,5 +29,10 @@ class WinningLotto(

private const val LOTTO_FIRST_NUMBER = 1
private const val LOTTO_LAST_NUMBER = 45
private const val MESSAGE_LOTTO_RANGE = "당첨 번호는 1 ~ 45 범위의 숫자로만 구성될 수 있습니다."

fun of(winningNumbers: List<String>, bonusNumber: Int): WinningLotto {
return WinningLotto(winningNumbers.map { it.toInt() }, bonusNumber)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package lotto.domain.strategy

import lotto.domain.Lottos
import lotto.domain.Lotto
import lotto.domain.Lottos

class LottoAutoGeneratorStrategy : LottoGeneratorStrategy {

Expand All @@ -16,7 +16,6 @@ class LottoAutoGeneratorStrategy : LottoGeneratorStrategy {
.shuffled()
.subList(LOTTO_FIRST_INDEX, LOTTO_LAST_INDEX)
.sorted()
.toList()
return Lotto(lottoNumbers)
}

Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/lotto/ui/InputViews.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ object InputViews {

private const val MESSAGE_INPUT_PRICE = "구입금액을 입력해 주세요."
private const val MESSAGE_INPUT_WINNING_NUMBER = "지난 주 당첨 번호를 입력해 주세요."
private const val MESSAGE_INPUT_BONUS_NUMBER = "보너스 볼을 입력해주세요."
private const val DELIMITERS = ", "
private const val DEFAULT_PRICE = 0
private const val DEFAULT_BONUS_NUMBER = 0

fun inputPrice(): Int {
println(MESSAGE_INPUT_PRICE)
Expand All @@ -16,4 +18,9 @@ object InputViews {
println(MESSAGE_INPUT_WINNING_NUMBER)
return readLine()?.split(DELIMITERS) ?: emptyList()
}

fun inputBonusNumber(): Int {
println(MESSAGE_INPUT_BONUS_NUMBER)
return readLine()?.toIntOrNull() ?: DEFAULT_BONUS_NUMBER
}
}
8 changes: 6 additions & 2 deletions src/main/kotlin/lotto/ui/OutputViews.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ object OutputViews {
}

fun printBoughtLotto(lottoCount: Int) {
println("${lottoCount}${MESSAGE_BOUGHT_LOTTO}")
println("${lottoCount}$MESSAGE_BOUGHT_LOTTO")
}

fun printBonusNumber(bonusNumber: Int) {
println(bonusNumber)
}

fun printLottoMatchResult(matchResult: Map<Ranking, Int>) {
println(PRINT_LOTTO_RESULT)
println("---------")
println("${FOURTH.matchCount}개 일치 (${FOURTH.winningMoney}원) - ${matchResult[FOURTH] ?: 0}개")
println("${THIRD.matchCount}개 일치 (${THIRD.winningMoney}원) - ${matchResult[THIRD] ?: 0}개")
println("${SECOND.matchCount}개 일치 (${SECOND.winningMoney}원) - ${matchResult[SECOND] ?: 0}개")
println("${SECOND.matchCount}개 일치, 보너스 볼 일치 (${SECOND.winningMoney}원) - ${matchResult[SECOND] ?: 0}개")
println("${FIRST.matchCount}개 일치 (${FIRST.winningMoney}원) - ${matchResult[FIRST] ?: 0}개")
}

Expand Down
27 changes: 27 additions & 0 deletions src/test/kotlin/lotto/domain/LottoResultTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lotto.domain

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

internal class LottoResultTest {

@Test
fun `구매금액 대비 당첨금액의 수익률을 계산한다`() {
// given
val matchResult = mapOf(
Ranking.FIRST to 0,
Ranking.SECOND to 0,
Ranking.THIRD to 0,
Ranking.FOURTH to 1
)

val price = 14000

// when
val actual = LottoResult(matchResult, price).calculate()

// then
val expected = 0.35
assertThat(actual).isEqualTo(expected)
}
}
41 changes: 41 additions & 0 deletions src/test/kotlin/lotto/domain/LottosTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package lotto.domain

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

internal class LottosTest {

@Test
fun `구매한 로또번호의 랭킹을 확인한다`() {
// given
val lottos = Lottos(
listOf(Lotto(listOf(1, 2, 3, 4, 5, 6)))
)
val bonusNumber = 7
val winningLotto = WinningLotto(listOf(1, 2, 3, 4, 5, 6), bonusNumber)

// when
val actual = lottos.matchWinningNumbers(winningLotto, bonusNumber)

// then
val expected = mapOf(Ranking.FIRST to 1)
assertThat(actual).isEqualTo(expected)
}

@Test
fun `구매한 로또번호가 5개 맞고 보너스번호가 맞을 경우 2등이 된다`() {
// given
val lottos = Lottos(
listOf(Lotto(listOf(1, 2, 3, 4, 5, 7)))
)
val bonusNumber = 7
val winningLotto = WinningLotto(listOf(1, 2, 3, 4, 5, 6), bonusNumber)

// when
val actual = lottos.matchWinningNumbers(winningLotto, bonusNumber)

// then
val expected = mapOf(Ranking.SECOND to 1)
assertThat(actual).isEqualTo(expected)
}
}
37 changes: 32 additions & 5 deletions src/test/kotlin/lotto/domain/WinningLottosTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import org.junit.jupiter.api.assertThrows
internal class WinningLottosTest {

@Test
fun `당첨번호를 6개 입력한다`() {
fun `당첨번호를 6개와 보너스 번호를 입력한다`() {
// given
val inputWinningNumber = listOf("1", "2", "3", "4", "5", "6")
val bonusNumber = 7

// when
val actual = WinningLotto(inputWinningNumber).winningNumbers.size
val actual = WinningLotto.of(inputWinningNumber, bonusNumber).winningNumbers.size

// then
val expected = 6
Expand All @@ -22,21 +23,47 @@ internal class WinningLottosTest {
@Test
fun `당첨번호가 입력되지 않으면 예외가 발생한다`() {
assertThrows<IllegalArgumentException> {
WinningLotto(emptyList())
WinningLotto(emptyList(), 7)
}
}

@Test
fun `당첨번호가 6개가 아니면 예외가 발생한다`() {
assertThrows<IllegalArgumentException> {
WinningLotto(listOf("1", "2"))
WinningLotto.of(listOf("1", "2"), 7)
}
}

@Test
fun `당첨번호가 1에서 45숫자가 아니면 예외가 발생한다`() {
assertThrows<IllegalArgumentException> {
WinningLotto(listOf("1", "2", "3", "4", "5", "46"))
WinningLotto.of(listOf("1", "2", "3", "4", "5", "46"), 7)
}
}

@Test
fun `입력한 로또 번호에 보너스 번호가 포함될 경우 true`() {
// given
val inputWinningNumber = listOf("1", "2", "3", "4", "5", "7")
val bonusNumber = 7

// when
val actual = WinningLotto.of(inputWinningNumber, bonusNumber).isMatchBonus(bonusNumber)

// then
assertThat(actual).isTrue
}

@Test
fun `입력한 로또 번호에 보너스 번호가 포함되지 않은 경우 false`() {
// given
val inputWinningNumber = listOf("1", "2", "3", "4", "5", "6")
val bonusNumber = 7

// when
val actual = WinningLotto.of(inputWinningNumber, bonusNumber).isMatchBonus(8)

// then
assertThat(actual).isFalse
}
}