-
Notifications
You must be signed in to change notification settings - Fork 106
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
[로또] 이지은 미션 제출합니다. #82
base: main
Are you sure you want to change the base?
Changes from all commits
898e849
7559f42
dc6d616
53b5857
32e7d13
c8da03c
a5f9a7c
ab4731d
b7106be
763a590
22e1ba8
2d00d74
6ead5e9
b5ca74e
b721128
e1cd813
7ab7f9e
1cd58ef
1dfa8a0
e2b8f2a
5f53211
bbd7b6a
e400861
4aba21f
c871f76
9dcc294
173453b
f0dc807
7d4a129
f6c0c7a
4e47b39
41bdd6f
915c0c9
419212a
e781007
66defe7
5737f3c
4745f77
1866c4d
5fcf7e9
383a97c
7d2249d
f533e13
df2e65f
fe60b65
b8e83f1
37bd82e
5740c44
5df1b7e
2d342e9
45627a2
1baced4
a965acf
8e7b59b
93267bc
2ca4b77
cbe81ea
032f116
b68d854
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 |
---|---|---|
|
@@ -33,3 +33,6 @@ out/ | |
|
||
### Mac OS ### | ||
.DS_Store | ||
|
||
# Local configuration file (sdk path, etc) | ||
local.properties |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,64 @@ | ||
# kotlin-lotto-precourse | ||
# 프리코스 3주차 _ 로또 | ||
|
||
## 📍 구현할 기능 목록 | ||
1. 로또 금액 입력받기 | ||
- [x] '구입금액을 입력해 주세요.' 메시지 출력 | ||
- [x] 사용자 입력 받기 | ||
- 입력받은 로또 금액 유효성 검사 (* `[ERROR]`로 시작하는 에러 문구 출력) | ||
- [x] 빈 값을 입력한 경우 | ||
- [x] 양의 정수가 아닌 값을 입력한 경우 | ||
- [x] 숫자가 아닌 값을 입력한 경우 | ||
- [x] Int 범위를 벗어나는 값을 입력한 경우 | ||
- [x] 1000원으로 나누어 떨어지지 않는 경우 | ||
|
||
<br> | ||
|
||
2. 로또 번호 생성 | ||
- [x] 로또 번호를 생성 | ||
- [x] `Randoms.pickUniqueNumbersInRange(1, 45, 6)` 이용 | ||
- 제공된 `Lotto` 클래스를 사용하여 구현 | ||
|
||
<br> | ||
|
||
3. 발행한 로또 수량 및 번호 출력 | ||
- [x] 입력한 금액만큼 발행한 로또 수량 출력 | ||
- [x] 수량만큼 발행된 번호 출력 | ||
|
||
<br> | ||
|
||
4. 당첨 번호 입력받기 | ||
- [x] '당첨 번호를 입력해 주세요.' 메시지 출력 | ||
- [x] 사용자 입력 입력받기 | ||
- 입력받은 당첨 번호 유효성 검사 (* `[ERROR]`로 시작하는 에러 문구 출력) | ||
- [x] 빈 값을 입력한 경우 | ||
- [x] 구분자로 쉼표를 사용하지 않은 경우 (문자열로 처리해서 에러 발생) | ||
- [x] 1 ~ 45 범위를 넘어간 값을 입력한 경우 | ||
- [x] 6개보다 적거나 6개를 넘게 입력한 경우 | ||
|
||
<br> | ||
|
||
5. 보너스 번호 입력받기 | ||
- [x] '보너스 번호를 입력해 주세요.' 메시지 출력 | ||
- [x] 사용자 입력받기 | ||
- 입력받은 보너스 번호 유효성 검사 (* `[ERROR]`로 시작하는 에러 문구 출력) | ||
- [x] 1 ~ 45 범위를 넘어간 값을 입력한 경우 | ||
- [x] 빈 값을 입력한 경우 | ||
- [x] 당첨 번호와 중복되는 경우 | ||
|
||
<br> | ||
|
||
6. 당첨 내역 출력하기 | ||
- [x] 당첨금은 3자리 마다 쉼표(,)로 구분 | ||
- [x] 3개부터 6까지 일치하는 내역 계산 | ||
- [x] 당첨 내역 출력 | ||
|
||
<br> | ||
|
||
7. 수익률 출력하기 | ||
- [x] `당첨금액 / 구입금액 * 100` 으로 수익률 계산 | ||
- [x] 수익률 결과값은 소수점 둘째 자리에서 반올림 | ||
|
||
<br> | ||
|
||
8. 단위 테스트 진행하기 | ||
- [x] 구현한 기능에 대한 단위 테스트 진행 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
package lotto | ||
|
||
import lotto.controller.LottoController | ||
|
||
fun main() { | ||
// TODO: 프로그램 구현 | ||
LottoController().start() | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package lotto.controller | ||
|
||
import lotto.model.LottoGame | ||
import lotto.parser.Parser | ||
import lotto.util.Constants | ||
import lotto.validator.Validator | ||
import lotto.view.InputView | ||
import lotto.view.ResultView | ||
|
||
class LottoController { | ||
private val validator = Validator() | ||
private val parser = Parser() | ||
private val inputView = InputView(validator, parser) | ||
private val resultView = ResultView() | ||
private val lottoGame = LottoGame() | ||
Comment on lines
+11
to
+15
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. MVC 모르는 문외한이라 Controller가 내부 요소들을 직접 인스턴스화하는 역할이 있는걸까요 ? 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. 작은 프로그램에선 Controller가 내부 요소들을 직접 인스턴스화 하기도 하고, 사실 저는 main에서 어떠한 설정 없이 바로 Controller를 통해 프로그램을 시작해주고 싶어서 내부에서 인스턴스를 생성하도록 했습니다! |
||
|
||
fun start() { | ||
val purchaseAmount = inputView.inputPurchaseAmount() | ||
val lottoCount = purchaseAmount / Constants.LOTTO_UNIT_PRICE | ||
val lottos = lottoGame.generateLotto(lottoCount) | ||
|
||
resultView.displayPurchasedLotto(lottoCount, lottos) | ||
|
||
val winningNumbers = inputView.inputWinningNumbers() | ||
val bonusNumber = inputView.inputBonusNumber(winningNumbers) | ||
val lottoResult = lottoGame.calculateResult(lottos, winningNumbers, bonusNumber) | ||
|
||
resultView.displayResult(lottoResult) | ||
} | ||
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. 저도 이전에 받았던 리뷰인데, 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.
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package lotto.model | ||
|
||
import lotto.util.ExceptionConstants | ||
|
||
class Lotto(private val numbers: List<Int>) { | ||
init { | ||
require(numbers.size == 6) { ExceptionConstants.ERROR_INVALID_WINNING_NUMBERS_COUNT } | ||
require(numbers.all { it in 1..45 }) { ExceptionConstants.ERROR_INVALID_WINNING_NUMBERS_RANGE } | ||
require(numbers.distinct().size == 6) { ExceptionConstants.ERROR_DUPLICATE_WINNING_NUMBERS } | ||
Comment on lines
+7
to
+9
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. 넘버들을 상수화하는건 어떨까요 ? 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. 이제 보니 Constant class엔 선언되어 있는데 까먹으신거 같네요 ㅎ_ㅎ 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. 선언해두고 다른 클래스들에선 사용했는데 여기 부분을 놓쳤네요.. ㅎㅎ |
||
} | ||
|
||
fun getNumbers(): List<Int> { | ||
return numbers | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package lotto.model | ||
|
||
import camp.nextstep.edu.missionutils.Randoms | ||
import lotto.util.Constants | ||
|
||
class LottoGame { | ||
|
||
fun generateLotto(count: Int): List<Lotto> { | ||
val lotto = List(count) { Lotto(generateRandomNumbers()) } | ||
|
||
return lotto | ||
} | ||
|
||
private fun generateRandomNumbers(): List<Int> { | ||
val randomNumbers = Randoms.pickUniqueNumbersInRange( | ||
Constants.MIN_NUMBER, | ||
Constants.MAX_NUMBER, | ||
Constants.WINNING_NUMBERS_COUNT | ||
) | ||
return randomNumbers | ||
} | ||
|
||
fun calculateResult( | ||
lottos: List<Lotto>, | ||
winningNumbers: List<Int>, | ||
bonusNumber: Int | ||
): LottoResult { | ||
val prizeCounts = Prize.values().associateWith { 0 }.toMutableMap() | ||
var totalPrize = 0 | ||
|
||
lottos.forEach { lotto -> | ||
updatePrizeCounts(lotto, winningNumbers, bonusNumber, prizeCounts).also { | ||
totalPrize += it | ||
} | ||
} | ||
|
||
val purchaseAmount = lottos.size * Constants.LOTTO_UNIT_PRICE | ||
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. 입력으로 |
||
|
||
return LottoResult(prizeCounts, totalPrize, purchaseAmount) | ||
} | ||
|
||
private fun updatePrizeCounts( | ||
lotto: Lotto, | ||
winningNumbers: List<Int>, | ||
bonusNumber: Int, | ||
prizeCounts: MutableMap<Prize, Int> | ||
): Int { | ||
val (matchCount, isBonusMatch) = calculateMatch(lotto, winningNumbers, bonusNumber) | ||
val prize = Prize.getPrize(matchCount, isBonusMatch) | ||
prizeCounts[prize] = prizeCounts.getValue(prize) + 1 | ||
val prizeMoney = prize.prizeMoney | ||
|
||
return prizeMoney | ||
} | ||
|
||
private fun calculateMatch( | ||
lotto: Lotto, | ||
winningNumbers: List<Int>, | ||
bonusNumber: Int | ||
): Pair<Int, Boolean> { | ||
val matchCount = lotto.getNumbers().count { it in winningNumbers } | ||
val isBonusMatch = bonusNumber in lotto.getNumbers() && matchCount == 5 | ||
val matchResult = Pair(matchCount, isBonusMatch) | ||
|
||
return matchResult | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package lotto.model | ||
|
||
import lotto.util.Constants | ||
|
||
data class LottoResult( | ||
val prizeCounts: Map<Prize, Int>, | ||
val totalPrize: Int, | ||
val purchaseAmount: Int | ||
) { | ||
val profitRate: Double | ||
get() { | ||
if (purchaseAmount > 0) { | ||
return (totalPrize.toDouble() / purchaseAmount * Constants.PERCENTAGE_MULTIPLIER) | ||
.roundToTwoDecimalPlaces() | ||
} | ||
return 0.0 | ||
} | ||
|
||
private fun Double.roundToTwoDecimalPlaces(): Double { | ||
return String.format("%.2f", this).toDouble() | ||
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.
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package lotto.model | ||
|
||
import lotto.util.Constants | ||
|
||
enum class Prize(val matchCount: Int, val prizeMoney: Int, val bonusMatch: Boolean = false) { | ||
FIRST(6, Constants.FIRST_PRIZE_MONEY), | ||
SECOND(5, Constants.SECOND_PRIZE_MONEY, true), | ||
THIRD(5, Constants.THIRD_PRIZE_MONEY), | ||
FOURTH(4, Constants.FOURTH_PRIZE_MONEY), | ||
FIFTH(3, Constants.FIFTH_PRIZE_MONEY), | ||
NONE(0, 0); | ||
|
||
companion object { | ||
fun getPrize(matchCount: Int, bonusMatch: Boolean): Prize { | ||
return when { | ||
matchCount == 6 -> FIRST | ||
matchCount == 5 && bonusMatch -> SECOND | ||
matchCount == 5 -> THIRD | ||
matchCount == 4 -> FOURTH | ||
matchCount == 3 -> FIFTH | ||
else -> NONE | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package lotto.parser | ||
|
||
import lotto.util.ExceptionConstants | ||
|
||
class Parser { | ||
fun parsePurchaseAmount(input: String): Int { | ||
return input.toInt() | ||
} | ||
|
||
fun parseWinningNumbers(input: String): List<Int> { | ||
val delimiter = "," | ||
require(input.contains(delimiter)) { ExceptionConstants.ERROR_INVALID_DELIMITER } | ||
|
||
return input.split(delimiter).map { it.trim().toInt() } | ||
} | ||
|
||
fun parseBonusNumber(input: String): Int { | ||
return input.toInt() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package lotto.util | ||
|
||
class Constants { | ||
companion object { | ||
const val LOTTO_UNIT_PRICE = 1000 | ||
const val MIN_NUMBER = 1 | ||
const val MAX_NUMBER = 45 | ||
const val WINNING_NUMBERS_COUNT = 6 | ||
const val PERCENTAGE_MULTIPLIER = 100 | ||
|
||
const val FIRST_PRIZE_MONEY = 2_000_000_000 | ||
const val SECOND_PRIZE_MONEY = 30_000_000 | ||
const val THIRD_PRIZE_MONEY = 1_500_000 | ||
const val FOURTH_PRIZE_MONEY = 50_000 | ||
const val FIFTH_PRIZE_MONEY = 5_000 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package lotto.util | ||
|
||
class ExceptionConstants { | ||
companion object { | ||
const val ERROR_INVALID_WINNING_NUMBERS_COUNT = "[ERROR] 로또 번호는 6개여야 합니다." | ||
const val ERROR_INVALID_WINNING_NUMBERS_RANGE = "[ERROR] 로또 번호는 1부터 45 사이여야 합니다." | ||
const val ERROR_DUPLICATE_WINNING_NUMBERS = "[ERROR] 로또 번호는 중복될 수 없습니다." | ||
const val ERROR_EMPTY_WINNING_NUMBERS = "[ERROR] 당첨 번호가 비어 있습니다." | ||
const val ERROR_INVALID_DELIMITER = "[ERROR] 당첨 번호는 쉼표(,)로 구분해야 합니다." | ||
|
||
const val ERROR_INVALID_PURCHASE_AMOUNT = "[ERROR] 로또는 1,000원 단위로 입력해야 합니다." | ||
const val ERROR_EMPTY_PURCHASE_AMOUNT = "[ERROR] 구매 금액이 비어 있습니다." | ||
const val ERROR_NOT_A_NUMBER = "[ERROR] 구매 금액은 숫자여야 합니다." | ||
const val ERROR_OUT_OF_RANGE = "[ERROR] 구매 금액이 정수의 범위를 벗어납니다." | ||
|
||
const val ERROR_INVALID_BONUS_NUMBER = "[ERROR] 보너스 번호는 1부터 45 사이여야 합니다." | ||
const val ERROR_DUPLICATE_BONUS_NUMBER = "[ERROR] 보너스 번호가 당첨 번호와 중복됩니다." | ||
const val ERROR_EMPTY_BONUS_NUMBERS = "[ERROR] 보너스 번호가 비어 있습니다." | ||
Comment on lines
+5
to
+18
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. [ERROR] 요 부분은 공통되는 부분이라 따로 분리해도 될 것 같네요! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package lotto.validator | ||
|
||
import lotto.util.Constants | ||
import lotto.util.ExceptionConstants | ||
|
||
class Validator { | ||
|
||
fun validatePurchaseAmount(amountString: String) { | ||
require(amountString.isNotBlank()) { ExceptionConstants.ERROR_EMPTY_PURCHASE_AMOUNT } | ||
require(amountString.all { it.isDigit() }) { ExceptionConstants.ERROR_NOT_A_NUMBER } | ||
require(amountString.toBigInteger() <= Int.MAX_VALUE.toBigInteger()) { ExceptionConstants.ERROR_OUT_OF_RANGE } | ||
|
||
val amount = amountString.toInt() | ||
|
||
require(amount > 0) { ExceptionConstants.ERROR_INVALID_PURCHASE_AMOUNT } | ||
require(amount % Constants.LOTTO_UNIT_PRICE == 0) { ExceptionConstants.ERROR_INVALID_PURCHASE_AMOUNT } | ||
} | ||
|
||
fun validateWinningNumbers(numbers: List<Int>) { | ||
require(numbers.isNotEmpty()) { ExceptionConstants.ERROR_EMPTY_WINNING_NUMBERS } | ||
require(numbers.size == Constants.WINNING_NUMBERS_COUNT) { ExceptionConstants.ERROR_INVALID_WINNING_NUMBERS_COUNT } | ||
require(numbers.all { it in Constants.MIN_NUMBER..Constants.MAX_NUMBER }) { ExceptionConstants.ERROR_INVALID_WINNING_NUMBERS_RANGE } | ||
require(numbers.distinct().size == Constants.WINNING_NUMBERS_COUNT) { ExceptionConstants.ERROR_DUPLICATE_WINNING_NUMBERS } | ||
} | ||
|
||
fun validateBonusNumber(bonusNumber: Int, winningNumbers: List<Int>) { | ||
require(winningNumbers.isNotEmpty()) { ExceptionConstants.ERROR_EMPTY_BONUS_NUMBERS } | ||
require(bonusNumber in Constants.MIN_NUMBER..Constants.MAX_NUMBER) { ExceptionConstants.ERROR_INVALID_BONUS_NUMBER } | ||
require(bonusNumber !in winningNumbers) { ExceptionConstants.ERROR_DUPLICATE_BONUS_NUMBER } | ||
} | ||
} |
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.
제공된
Lotto class
에서는 난수생성을 하고 있지 않네요.LottoGame class
에서 해당 작업을 진행중입니다!2주차 공통 피드백에서 기능 구현 중 바뀐 목록도 지속적으로 업데이트 해달라는 피드백이 있어서 알려드립니다!