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

[로또 2단계] 시아 미션 제출합니다. #135

Open
wants to merge 27 commits into
base: leeyerin0210
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
79bc0e4
refactor : Lotto의 가격 부분 삭제
Leeyerin0210 Feb 25, 2025
133dce9
refactor : 출력 로직 수정 , 로또 결과 산출 로직 수정
Leeyerin0210 Feb 25, 2025
02cb6d7
feat : 유저가 입력한 숫자의 로또를 수동으로 발행하는 기능 추가
Leeyerin0210 Feb 25, 2025
29794c2
fix : 수동과 자동 모든 로또가 출력되도록 변경
Leeyerin0210 Feb 25, 2025
d9ec1a0
refactor: 입력값 검증 방식 수정하여 반복 입력 가능하도록 변경
Leeyerin0210 Feb 25, 2025
66b2a02
refactor: 잘못된 입력값을 받았을 때 다시 입력 받도록 수정
Leeyerin0210 Feb 25, 2025
1907483
docs : 리드미 수정
Leeyerin0210 Feb 25, 2025
6a9e9d9
refactor : 뷰와 도메인이 너무 강하게 결합되지 않도록 수정
Leeyerin0210 Feb 25, 2025
4ed8d62
refactor : 로또 번호를 잘못 입력하면 바로 다시 입력 받도록 수정
Leeyerin0210 Feb 25, 2025
1db3c57
refactor : 출력 로직 일부 수정
Leeyerin0210 Feb 26, 2025
4485eac
refactor : sealed 클래스를 활용해서 오류 처리 일부 수정
Leeyerin0210 Feb 26, 2025
6dd8b3d
refactor : 오류 원인을 사용자가 파악할 수 있도록 출력 수정
Leeyerin0210 Feb 26, 2025
63bb963
feat : 로또 금액 단위의 금액을 입력하지 않으면 남는 돈을 출력하도록 추가
Leeyerin0210 Feb 26, 2025
e401cbd
fix : 테스트 함수 수정
Leeyerin0210 Feb 26, 2025
d07db8c
refactor: 기존 코드 구조를 폐기하고 새로운 설계를 시작
Leeyerin0210 Feb 27, 2025
422eaac
docs: 코드 작성 중 유의할 사항 기재
Leeyerin0210 Feb 27, 2025
0050e23
feat : 로또 넘버 객체 생성
Leeyerin0210 Feb 27, 2025
e7f5664
feat : 로또 객체 생성
Leeyerin0210 Feb 27, 2025
8eb1895
feat : amount 객체 생성
Leeyerin0210 Feb 28, 2025
8444009
feat : amount 객체 지불 함수 작성
Leeyerin0210 Feb 28, 2025
0fd886a
feat : rank 객채 및 winningLotto 객체 생성
Leeyerin0210 Feb 28, 2025
fece2a8
feat : 당첨금 계산 서비스 작성 및 파일 이동
Leeyerin0210 Mar 1, 2025
0220a90
feat : 로또를 자동으로 생성하는 로또 메이커 서비스 추가
Leeyerin0210 Mar 1, 2025
09eb61b
feat : 입출력 기능 추가
Leeyerin0210 Mar 2, 2025
19253ff
refactor : 생성 인터페이스 일관성 추가
Leeyerin0210 Mar 5, 2025
33d22ea
refactor : 비지니스 로직 분리 및 순회 횟수 감소
Leeyerin0210 Mar 6, 2025
00d4c70
refactor : 명확한 오류 원인 전달
Leeyerin0210 Mar 9, 2025
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@
- [x] 입력된 금액과 비교하여 수익률을 도출한다


# 2단계

### 로또 발행
- [x] 입력된 금액에 초과되는 수동 로또 구매 숫자를 입력하면 예외처리한다.
- [x] 입력한 수동 로또 구매 숫자에 맞춰서 로또를 입력받아서 발행한다.
- [x] 입력값이 올바르지 않으면, 다시 입력받도록 수정한다.
67 changes: 48 additions & 19 deletions src/main/kotlin/lotto/controller/LottoController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package lotto.controller
import lotto.model.Amount
import lotto.model.Lotto
import lotto.model.LottoMachine
import lotto.model.LottoMatcher
import lotto.model.LottoNumber
import lotto.model.LottoNumbers
import lotto.model.PrizeCalculator
import lotto.model.WinningLotto
import lotto.view.InputView
import lotto.view.OutputView

Expand All @@ -17,36 +17,65 @@ class LottoController(
fun run() {
val amount = getAmount()
val lottoMachine = LottoMachine(amount)
val publishedLotto = publishLotto(lottoMachine)
val winningLotto = getWinningLotto()
val bonusNumber = getBonusNumber()
val lottoMatcher = LottoMatcher(winningLotto, bonusNumber)
showEarningRate(amount, lottoMatcher, publishedLotto)
val publishedLotto = validatePublishLottoList(lottoMachine) + autoPublishLotto(lottoMachine)
val winningLotto = validateWinningLotto(getWinningLotto(), getBonusNumber())
val prizeCalculator = PrizeCalculator(winningLotto, publishedLotto, amount)
showEarningRate(prizeCalculator)
}

private fun getAmount(): Amount = Amount(inputView.getMoney())

private fun publishLotto(lottoMachine: LottoMachine): List<Lotto> {
val publishedLotto = lottoMachine.publishLottoTickets(Amount(LOTTO_PRIZE))
private fun validateWinningLotto(
number: LottoNumbers?,
bonus: LottoNumber?,
): WinningLotto {
if (number == null || bonus == null) {
outputView.inputWinningError()
return validateWinningLotto(getWinningLotto(), getBonusNumber())
}
return WinningLotto(number, bonus)
}

private fun validatePublishLotto(lottoMachine: LottoMachine): Lotto {
var lotto = lottoMachine.publishManualLotto(inputView.getManualLotto())
while (lotto == null) {
outputView.inputLottoError()
lotto = lottoMachine.publishManualLotto(inputView.getManualLotto())
}
return lotto
}

private fun validatePublishLottoList(lottoMachine: LottoMachine): List<Lotto> {
var count = inputView.getManualCount()
while (lottoMachine.useMoney(Amount(count * LOTTO_PRIZE)) == false) {
outputView.inputCountError()
count = inputView.getManualCount()
}
val lottoList = mutableListOf<Lotto>()
inputView.lottoInputMessage()
for (i in 1..count) {
lottoList.add(validatePublishLotto(lottoMachine))
}
outputView.printManualLotto(count)
return lottoList
}

private fun autoPublishLotto(lottoMachine: LottoMachine): List<Lotto> {
val publishedLotto = lottoMachine.publishAutoTickets(Amount(LOTTO_PRIZE))
outputView.printPublishedLotto(publishedLotto)
return publishedLotto
}

private fun getWinningLotto(): Lotto {
private fun getWinningLotto(): LottoNumbers? {
val winningInput = inputView.getWinningLotto()
return Lotto(LottoNumbers(winningInput.map { number -> LottoNumber(number) }), Amount(LOTTO_PRIZE))
return LottoNumbers.create(winningInput.mapNotNull { number -> LottoNumber.create(number) })
}

private fun getBonusNumber(): LottoNumber = LottoNumber(inputView.getBonusNumber())
private fun getBonusNumber(): LottoNumber? = LottoNumber.create(inputView.getBonusNumber())

private fun showEarningRate(
amount: Amount,
lottoMatcher: LottoMatcher,
publishedLotto: List<Lotto>,
) {
val result = lottoMatcher.matchLotto(publishedLotto)
val prizeCalculator = PrizeCalculator()
val earningRate = prizeCalculator.calculateEarningRate(amount.money, result)
private fun showEarningRate(prizeCalculator: PrizeCalculator) {
val result = prizeCalculator.result
val earningRate = prizeCalculator.calculateEarningRate()
outputView.printPrize(result, earningRate)
}

Expand Down
12 changes: 8 additions & 4 deletions src/main/kotlin/lotto/model/Amount.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package lotto.model

data class Amount(
val money: Int,
var money: Int,
) {
init {
require(money > 0) { MONEY_UNDER_ZERO }
require(money >= 0) { MONEY_UNDER_ZERO }
}

fun payMoney(inputMoney: Amount) {
require(inputMoney.money <= money) { MONEY_OVER_INPUT }
money -= inputMoney.money
}

fun moneySplit(splitMoney: Amount): List<Amount> {
val moneyList: MutableList<Amount> = mutableListOf()
require(money % splitMoney.money == 0) { MONEY_SPILT_ERROR }
repeat(money / splitMoney.money) { moneyList.add(splitMoney) }
return moneyList
}

companion object {
const val MONEY_UNDER_ZERO = "[ERROR] 금액은 양수이어야 합니다."
const val MONEY_SPILT_ERROR = "[ERROR] 로또 금액에 맞게 나눌 수 없습니다"
const val MONEY_OVER_INPUT = "[ERROR] 입력된 금액보다 더 많은 수량의 로또는 살 수 없습니다."
}
}
3 changes: 0 additions & 3 deletions src/main/kotlin/lotto/model/Lotto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ package lotto.model

class Lotto(
val numbers: LottoNumbers,
val prize: Amount,
) {
fun getNumbers(): List<LottoNumber> = numbers.numberList.map { it }

fun findNumber(number: LottoNumber): Boolean = numbers.include(number)

fun countMatchedNumber(other: Lotto): Int = numbers.numberList.count { other.findNumber(it) }
}
22 changes: 17 additions & 5 deletions src/main/kotlin/lotto/model/LottoMachine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,41 @@ class NormalShuffle : ShuffleStrategy {
}

class LottoMachine(
private val amount: Amount,
var amount: Amount,
private val shuffle: ShuffleStrategy = NormalShuffle(),
) {
private val numbers: List<Int> = (LottoNumber.MIN_VALUE..LottoNumber.MAX_VALUE).toList()

fun publishLottoTickets(lottoPrize: Amount): List<Lotto> {
fun useMoney(inputMoney: Amount): Boolean {
if (inputMoney.money > amount.money) return false
amount.payMoney(inputMoney)
return true
}

fun publishManualLotto(numberList: List<Int>): Lotto? {
var lottoNumbers: LottoNumbers? = LottoNumbers.create(numberList.mapNotNull { it -> LottoNumber.create(it) })
if (lottoNumbers == null) return null
return Lotto(lottoNumbers)
}

fun publishAutoTickets(lottoPrize: Amount): List<Lotto> {
val lottoTickets = mutableListOf<Lotto>()
val moneyList = amount.moneySplit(lottoPrize)
repeat(moneyList.size) {
val lotto = publishLotto(lottoPrize)
val lotto = publishLotto()
lottoTickets.add(lotto)
}
return lottoTickets
}

private fun publishLotto(prize: Amount): Lotto {
private fun publishLotto(): Lotto {
val lottoNumbers =
shuffle
.shuffle(numbers)
.take(LOTTO_NUMBER_COUNT)
.sorted()
.map { LottoNumber(it) }
return Lotto(LottoNumbers(lottoNumbers), prize)
return Lotto(LottoNumbers(lottoNumbers))
}

companion object {
Expand Down
32 changes: 0 additions & 32 deletions src/main/kotlin/lotto/model/LottoMatcher.kt

This file was deleted.

13 changes: 7 additions & 6 deletions src/main/kotlin/lotto/model/LottoNumber.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ package lotto.model
value class LottoNumber(
val value: Int,
) {
init {
require(value in MIN_VALUE..MAX_VALUE) {
RANGE_ERROR_MESSAGE
}
}

companion object {
const val MIN_VALUE = 1
const val MAX_VALUE = 45
private const val RANGE_ERROR_MESSAGE = "[ERROR] 로또 번호는 1~45 사이여야 합니다."

fun create(value: Int): LottoNumber? {
if (value !in MIN_VALUE..MAX_VALUE) {
return null
}
return LottoNumber(value)
}
}
}
19 changes: 8 additions & 11 deletions src/main/kotlin/lotto/model/LottoNumbers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@ package lotto.model
class LottoNumbers(
val numberList: List<LottoNumber>,
) {
init {
require(numberList.size == LOTTO_NUMBER_SIZE) {
SIZE_ERROR_MESSAGE
}
require(numberList.distinct().size == numberList.size) {
DUPLICATE_ERROR_MESSAGE
}
}

fun include(number: LottoNumber): Boolean = numberList.contains(number)

companion object {
const val LOTTO_NUMBER_SIZE = 6
private const val SIZE_ERROR_MESSAGE = "[ERROR] 로또 번호는 6개여야 합니다."
private const val DUPLICATE_ERROR_MESSAGE = "[ERROR] 로또 번호는 중복될 수 없습니다."

fun create(numberList: List<LottoNumber>): LottoNumbers? {
when {
numberList.size != LOTTO_NUMBER_SIZE -> return null
numberList.distinct().size != numberList.size -> return null
}
return LottoNumbers(numberList)
}
}
}
31 changes: 21 additions & 10 deletions src/main/kotlin/lotto/model/PrizeCalculator.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
package lotto.model

class PrizeCalculator {
fun calculateInputMoney(lottoList: List<Lotto>): Int {
val allAmount = 0
lottoList.forEach {
allAmount + it.prize.money
class PrizeCalculator(
val winningLotto: WinningLotto,
val publishedLotto: List<Lotto>,
val amount: Amount,
) {
val result: Map<Rank, Int>

init {
result = makeResult()
}

private fun makeResult(): Map<Rank, Int> {
val result: MutableMap<Rank, Int> = mutableMapOf()
publishedLotto.forEach {
val rank = winningLotto.findRank(it)
result[rank] = result.getOrDefault(rank, 0) + 1
}
Rank.entries.forEach {
result[it] = result.getOrDefault(it, 0)
}
return allAmount
return result
}

fun calculateEarningRate(
money: Int,
result: Map<Rank, Int>,
): Double = calculateTotalPrize(result).toDouble() / money.toDouble()
fun calculateEarningRate(): Double = calculateTotalPrize(result).toDouble() / amount.money.toDouble()

private fun calculateTotalPrize(result: Map<Rank, Int>): Int = result.entries.sumOf { (rank, count) -> rank.winningMoney * count }
}
27 changes: 13 additions & 14 deletions src/main/kotlin/lotto/model/Rank.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,27 @@ package lotto.model

enum class Rank(
val countOfMatch: Int,
val bonusRequired: Boolean,
val winningMoney: Int,
val printSequence: Int?,
) {
FIRST(6, 2_000_000_000),
SECOND(5, 30_000_000),
THIRD(5, 1_500_000),
FOURTH(4, 50_000),
FIFTH(3, 5_000),
MISS(0, 0),
FIRST(6, false, 2_000_000_000, 5),
SECOND(5, true, 30_000_000, 4),
THIRD(5, false, 1_500_000, 3),
FOURTH(4, false, 50_000, 2),
FIFTH(3, false, 5_000, 1),
MISS(0, false, 0, null),
;

companion object {
fun valueOf(
countOfMatch: Int,
matchBonus: Boolean,
): Rank =
when {
countOfMatch == FIRST.countOfMatch -> FIRST
countOfMatch == SECOND.countOfMatch && matchBonus -> SECOND
countOfMatch == THIRD.countOfMatch -> THIRD
countOfMatch == FOURTH.countOfMatch -> FOURTH
countOfMatch == FIFTH.countOfMatch -> FIFTH
else -> MISS
): Rank {
entries.forEach {
if (countOfMatch >= it.countOfMatch && (it.bonusRequired && matchBonus) == it.bonusRequired) return it
}
return MISS
}
}
}
12 changes: 12 additions & 0 deletions src/main/kotlin/lotto/model/WinningLotto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lotto.model

class WinningLotto(
val numbers: LottoNumbers,
val bonusNumber: LottoNumber,
) {
fun findRank(other: Lotto): Rank = Rank.valueOf(countMatchedNumber(other), findBonusNumber(other))

private fun countMatchedNumber(other: Lotto): Int = numbers.numberList.count { other.findNumber(it) }

private fun findBonusNumber(other: Lotto): Boolean = other.findNumber(bonusNumber)
}
Loading