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

[로또] 이재원 미션 제출합니다. #190

Open
wants to merge 50 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
06413b0
docs(docs/README.md): write feature list
leejewon97 Nov 7, 2023
de4c0cb
feat(enumeration/PrintBuy.kt): enum 클래스 및 인스턴스 생성
leejewon97 Nov 8, 2023
d30d51d
feat(LottoUI): UI를 위한 클래스 및 메서드 생성
leejewon97 Nov 8, 2023
82234e6
feat(main): 구입 금액 입력 구문 출력
leejewon97 Nov 8, 2023
d22c163
feat(LottoUI): 구입 금액 입력
leejewon97 Nov 8, 2023
27e55f8
feat(LottoUI): 구입 금액 입력 예외 처리
leejewon97 Nov 8, 2023
d6b8f3c
feat(main): 정상 구입 금액을 변수 buyPrice로 할당
leejewon97 Nov 8, 2023
97f0d3d
docs(docs/README.md): 구입금액 예외에서 조건을 "숫자"에서 "정수"로 변경
leejewon97 Nov 8, 2023
891c0d8
feat(enumeration/PrintBuy.kt): 구입금액 예외 처리 문구들 추가
leejewon97 Nov 8, 2023
28c9424
feat(LottoUI): 구입금액 예외 처리 열거형들 적용
leejewon97 Nov 8, 2023
fc60c79
refactor(enumeration/Buy.kt): 클래스명 및 인스턴스명 변경
leejewon97 Nov 8, 2023
4244270
refactor(LottoUI): Buy 클래스명 및 인스턴스명 변경 적용
leejewon97 Nov 8, 2023
d450a8b
refactor(LottoUI): 메서드 checkInvalidBuyPrice의 접근제어자 변경
leejewon97 Nov 8, 2023
e711112
test(LottoTest.kt): checkInvalidBuyPrice 단위테스트 생성
leejewon97 Nov 8, 2023
f6dcd66
fix(LottoUI): checkInvalidBuyPrice 에서 0원 에러처리 추가
leejewon97 Nov 8, 2023
a09c523
feat(enumeration/Buy.kt): 열거형 HOW_MANY 추가
leejewon97 Nov 8, 2023
e2f0838
feat(LottoUI): 몇개를 구매했는지 출력해주는 메서드 추가
leejewon97 Nov 8, 2023
f02facc
feat(main): printBuyLottoCount 추가
leejewon97 Nov 8, 2023
4e7dbfc
feat(LottoService): 클래스 생성 및 메서드 생성
leejewon97 Nov 8, 2023
364910c
feat(main): buyLotto 추가
leejewon97 Nov 8, 2023
cd32e15
feat(LottoService): 구입 개수에 맞게 로또 발행
leejewon97 Nov 8, 2023
ee2223b
refactor(Lotto*Test.kt): `구입 금액 입력 에러 발생 테스트` 이사
leejewon97 Nov 8, 2023
c15a01f
feat(Lotto.kt): getNumbers 생성
leejewon97 Nov 8, 2023
53afa53
feat(LottoUI): printLottoNumbers 생성
leejewon97 Nov 8, 2023
d0519e7
feat(main): printLottoNumbers 적용
leejewon97 Nov 8, 2023
e8762cf
test(LottoServiceTest.kt): buyLotto() 테스트 추가
leejewon97 Nov 8, 2023
53744ec
feat(main): 이후 뼈대(구조) 먼저 작성
leejewon97 Nov 8, 2023
35fcc53
feat(main): 당첨 번호 입력 구문 출력
leejewon97 Nov 8, 2023
e7e2d00
feat(Winning): 열거형 NUMBER_INPUT 추가
leejewon97 Nov 8, 2023
12400a5
feat(LottoUI): 메서드 inputWinningNumbers 추가
leejewon97 Nov 8, 2023
ccc197c
feat(LottoUI): 메서드 winningNumbersToInt 추가
leejewon97 Nov 8, 2023
ecb0e98
feat(Winning): 예외 사항 열거형 추가
leejewon97 Nov 8, 2023
04a1472
feat(LottoUI): checkInvalidWinningNumbers 구현 완료
leejewon97 Nov 8, 2023
5d3353c
test(LottoUITest): checkInvalidWinningNumbers 테스트 완료
leejewon97 Nov 8, 2023
1975936
test(LottoUI): checkInvalidWinningNumbers 의 접근제한자 변경
leejewon97 Nov 8, 2023
20e5e54
test(Lotto): 중복이 들어왔을 경우 불허
leejewon97 Nov 8, 2023
3e36af8
refactor: LottoUI에 있는 것들 LottoService로 이사
leejewon97 Nov 8, 2023
278255a
feat(Bonus): 보너스번호에 관한 enum 클래스및 인스턴스 생성
leejewon97 Nov 8, 2023
c81e313
feat(LottoUI): printBonusNumber 생성
leejewon97 Nov 8, 2023
9a80d00
docs(docs/README.md): 보너스 번호 예외 수정
leejewon97 Nov 8, 2023
e9a63b0
feat(Bonus): 수정된 보너스 예외 사항 적용
leejewon97 Nov 8, 2023
041a734
feat(LottoUI): 보너스 번호 입력
leejewon97 Nov 8, 2023
7336ab6
feat(LottoService): 보너스 번호 예외 처리
leejewon97 Nov 8, 2023
ae5a77d
test(LottoServiceTest.kt): 보너스 번호 입력 에러 발생 테스트
leejewon97 Nov 8, 2023
75e9a19
feat(LottoUI): 당첨 통계 UI 출력 부분
leejewon97 Nov 8, 2023
14bea98
fix(main): inputBonusNumber 사용 변경 적용
leejewon97 Nov 8, 2023
05b3557
feat(WinningResult): 당첨 통계 출력용 enum class 생성
leejewon97 Nov 8, 2023
a6883b8
feat(LottoUI): printResult 구현 완료
leejewon97 Nov 8, 2023
ba8c74c
feat(LottoService): 마지막 통계 계산 및 출력
leejewon97 Nov 8, 2023
4f3914b
feat(LottoService): 최종
leejewon97 Nov 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
39 changes: 39 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 기능목록

1. 구입 금액 입력 구문 출력
2. 구입 금액 입력
- 예외 `IllegalArgumentException`
- 구입 금액이 정수가 아닐 경우(문구 : [ERROR] 구입 금액은 정수여야 합니다.)
- 구입 금액이 1000원 단위가 아닐 경우(문구 : [ERROR] 구입 금액은 1000원 단위여야 합니다.)
3. 구입 개수 계산
4. 구입 개수 출력
5. 구입 개수에 맞게 로또 발행
6. 발행된 로또 출력
7. 당첨 번호 입력 구문 출력
8. 당첨 번호 입력
- 당첨 번호를 쉼표를 구분자로 나누어 문자열 배열로 저장
- 나눠진 각 문자열 예외 `IllegalArgumentException`
- 숫자가 아닐 경우(문구 : [ERROR] 당첨 번호는 숫자여야 합니다.)
- 1 ~ 45 가 아닐 경우(문구 : [ERROR] 당첨 번호는 1 ~ 45 사이여야 합니다.)
- 당첨 번호가 6개가 아닐 경우(문구 : [ERROR] 당첨 번호는 6개여야 합니다.)
- 당첨 번호가 중복될 경우(문구 : [ERROR] 당첨 번호는 중복되지 않아야 합니다.)
- 예외사항이 아니면 정수 배열로 변경 후 Lotto 클래스의 생성자에 멤버변수로 전달
9. 보너스 번호 입력 구문 출력
10. 보너스 번호 입력
- 예외 `IllegalArgumentException`
- 정수가 아닐 경우(문구 : [ERROR] 보너스 번호는 정수여야 합니다.)
- 1 ~ 45 가 아닐 경우(문구 : [ERROR] 보너스 번호는 1 ~ 45 사이여야 합니다.)
- 당첨 번호들과 중복될 경우(문구 : [ERROR] 보너스 번호는 당첨 번호와 중복되지 않아야 합니다.)
11. 발행된 로또들을 순회하며 당첨 번호와 몇 개가 일치하는지 확인
- 3,4,6개가 일치한다면, 일치 개수에 따라 5칸짜리 정수 배열에 할당
- 0번째 인덱스 : 3개 일치 개수
- 1번째 인덱스 : 4개 일치 개수
- 2번째 인덱스 : 0
- 3번째 인덱스 : 0
- 4번째 인덱스 : 6개 일치 개수
- 5개가 일치한다면, 보너스 번호와 일치하는지 확인
- 일치한다면 3번째 인덱스에 1 증가
- 일치하지 않는다면 2번째 인덱스에 1 증가
12. 수익률 계산
- 수익률 = 당첨 금액 / 구입 금액 * 100
13. 당첨 통계 및 수익률 출력
12 changes: 11 additions & 1 deletion src/main/kotlin/lotto/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package lotto

fun main() {
TODO("프로그램 구현")
LottoUI().printBuyPrice()
val buyPrice = LottoUI().inputBuyPrice()
LottoUI().printBuyLottoCount(buyPrice)
val buyCount = buyPrice.toInt() / 1000
val lottos = LottoService().buyLotto(buyCount)
LottoUI().printLottoNumbers(lottos)
LottoUI().printWinningNumbers()
val winningLotto = LottoUI().inputWinningNumbers()
LottoUI().printBonusNumber()
val bonusNumber = LottoUI().inputBonusNumber(winningLotto)
LottoUI().printResult(lottos, winningLotto, bonusNumber)
}
13 changes: 12 additions & 1 deletion src/main/kotlin/lotto/Lotto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ package lotto
class Lotto(private val numbers: List<Int>) {
init {
require(numbers.size == 6)
require(numbers.distinct().size == 6)
}

// TODO: 추가 기능 구현
fun getNumbers(): List<Int> {
return numbers
}

fun matchCount(winningLotto: Lotto): Any {
return numbers.intersect(winningLotto.getNumbers().toSet()).size
}

fun matchBonusNumber(bonusNumber: Int): Boolean {
return numbers.contains(bonusNumber)
}
}
78 changes: 78 additions & 0 deletions src/main/kotlin/lotto/LottoService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package lotto

import camp.nextstep.edu.missionutils.Randoms
import lotto.enumeration.Bonus
import lotto.enumeration.Buy
import lotto.enumeration.Winning

class LottoService {
fun buyLotto(buyCount: Int): List<Lotto> {
val buyLottos = mutableListOf<Lotto>()
repeat(buyCount) {
buyLottos.add(Lotto(lottoMaker()))
}
return buyLottos
}


private fun lottoMaker(): List<Int> {
return Randoms.pickUniqueNumbersInRange(1, 45, 6)
}

fun checkInvalidBuyPrice(buyPrice: String) {
when {
buyPrice.toIntOrNull() == null -> throw IllegalArgumentException(Buy.ERROR_NOT_INTEGER.value)
buyPrice.toInt() % 1000 != 0 -> throw IllegalArgumentException(Buy.ERROR_NOT_THOUSAND.value)
buyPrice.toInt() == 0 -> throw IllegalArgumentException(Buy.ERROR_NOT_THOUSAND.value)
}
}

fun checkInvalidWinningNumbers(winningNumbers: List<String>) {
when {
winningNumbers.map { it.toIntOrNull() }
.contains(null) -> throw IllegalArgumentException(Winning.ERROR_NOT_INTEGER.value)

winningNumbers.map { it.toInt() }
.any { it !in 1..45 } -> throw IllegalArgumentException(Winning.ERROR_NOT_RANGE.value)

winningNumbers.size != 6 -> throw IllegalArgumentException(Winning.ERROR_NOT_SIX.value)
winningNumbers.map { it.toInt() }
.distinct().size != 6 -> throw IllegalArgumentException(Winning.ERROR_NOT_UNIQUE.value)
}
}

fun checkInvalidBonusNumber(bonusNumber: String, winningLotto: Lotto) {
when {
bonusNumber.toIntOrNull() == null -> throw IllegalArgumentException(Bonus.ERROR_NOT_INTEGER.value)
bonusNumber.toInt() !in 1..45 -> throw IllegalArgumentException(Bonus.ERROR_NOT_RANGE.value)
bonusNumber.toInt() in winningLotto.getNumbers() -> throw IllegalArgumentException(Bonus.ERROR_NOT_UNIQUE.value)
}
}

fun LottoResult(lottos: List<Lotto>, winningLotto: Lotto, bonusNumber: Int): List<Int> {
val result = mutableListOf<Int>(0, 0, 0, 0, 0, 0)
lottos.forEach {
when (it.matchCount(winningLotto)) {
3 -> result[0]++
4 -> result[1]++
5 -> {
if (it.matchBonusNumber(bonusNumber)) {
result[3]++
} else {
result[2]++
}
}
6 -> result[4]++
else -> result[5]++
}
}
return result
}

fun calculateEarnRate(result: List<Int>): Any {
val total = result.sum()
val rate =
(result[0] * 5000 + result[1] * 50000 + result[2] * 1500000 + result[3] * 30000000 + result[4] * 2000000000).toDouble() / (total * 1000) * 100
return "%.2f".format(rate).toDouble()
}
}
89 changes: 89 additions & 0 deletions src/main/kotlin/lotto/LottoUI.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package lotto

import camp.nextstep.edu.missionutils.Console
import lotto.enumeration.Bonus
import lotto.enumeration.Buy
import lotto.enumeration.Winning
import lotto.enumeration.WinningResult

class LottoUI {
fun printBuyPrice() {
println(Buy.PRICE_INPUT.value)
}

fun inputBuyPrice(): String {
val buyPrice = Console.readLine()
try {
LottoService().checkInvalidBuyPrice(buyPrice)
} catch (e: IllegalArgumentException) {
println(e.message)
printBuyPrice()
return inputBuyPrice()
}
return buyPrice
}

fun printBuyLottoCount(buyPrice: String) {
println()
print(buyPrice.toInt() / 1000)
println(Buy.HOW_MANY.value)
}

fun printLottoNumbers(lottos: List<Lotto>) {
lottos.forEach {
println("${it.getNumbers()}")
}
}

fun printWinningNumbers() {
println()
println(Winning.NUMBER_INPUT.value)
}

fun inputWinningNumbers(): Lotto {
val winningNumbers = Console.readLine().split(",")
try {
LottoService().checkInvalidWinningNumbers(winningNumbers)
} catch (e: IllegalArgumentException) {
println(e.message)
printWinningNumbers()
return inputWinningNumbers()
}
return Lotto(winningNumbersToInt(winningNumbers))
}

private fun winningNumbersToInt(winningNumbers: List<String>): List<Int> {
return winningNumbers.map { it.toInt() }
}

fun printBonusNumber() {
println()
println(Bonus.NUMBER_INPUT.value)
}

fun inputBonusNumber(winningLotto: Lotto): Int {
val bonusNumber = Console.readLine()
try {
LottoService().checkInvalidBonusNumber(bonusNumber, winningLotto)
} catch (e: IllegalArgumentException) {
println(e.message)
printBonusNumber()
return inputBonusNumber(winningLotto)
}
return bonusNumber.toInt()
}

fun printResult(lottos: List<Lotto>, winningLotto: Lotto, bonusNumber: Int) {
println()
println(WinningResult.STATISTIC.value)
val result: List<Int> = LottoService().LottoResult(lottos, winningLotto, bonusNumber)
println("${WinningResult.THREE_COUNT.value}${result[0]}${WinningResult.UNIT.value}")
println("${WinningResult.FOUR_COUNT.value}${result[1]}${WinningResult.UNIT.value}")
println("${WinningResult.FIVE_COUNT.value}${result[2]}${WinningResult.UNIT.value}")
println("${WinningResult.FIVE_COUNT_WITH_BONUS.value}${result[3]}${WinningResult.UNIT.value}")
println("${WinningResult.SIX_COUNT.value}${result[4]}${WinningResult.UNIT.value}")
val earnRate = LottoService().calculateEarnRate(result)
println("${WinningResult.ALL_EARN_RATE.value}${earnRate}${WinningResult.END_PERCENT.value}")
}

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

enum class Bonus(val value: String) {
NUMBER_INPUT("보너스 번호를 입력해 주세요."),
ERROR_NOT_INTEGER("[ERROR] 보너스 번호는 정수여야 합니다."),
ERROR_NOT_RANGE("[ERROR] 보너스 번호는 1 ~ 45 사이여야 합니다."),
ERROR_NOT_UNIQUE("[ERROR] 보너스 번호는 당첨 번호와 중복되지 않아야 합니다.")
}
8 changes: 8 additions & 0 deletions src/main/kotlin/lotto/enumeration/Buy.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package lotto.enumeration

enum class Buy(val value: String) {
PRICE_INPUT("구입금액을 입력해 주세요."),
ERROR_NOT_INTEGER("[ERROR] 구입 금액은 정수여야 합니다."),
ERROR_NOT_THOUSAND("[ERROR] 구입 금액은 1000원 단위여야 합니다."),
HOW_MANY("개를 구매했습니다.")
}
9 changes: 9 additions & 0 deletions src/main/kotlin/lotto/enumeration/Winning.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package lotto.enumeration

enum class Winning(val value: String) {
NUMBER_INPUT("당첨 번호를 입력해 주세요."),
ERROR_NOT_INTEGER("[ERROR] 당첨 번호는 정수여야 합니다."),
ERROR_NOT_RANGE("[ERROR] 당첨 번호는 1 ~ 45 사이여야 합니다."),
ERROR_NOT_SIX("[ERROR] 당첨 번호는 6개여야 합니다."),
ERROR_NOT_UNIQUE("[ERROR] 당첨 번호는 중복되지 않아야 합니다.")
}
13 changes: 13 additions & 0 deletions src/main/kotlin/lotto/enumeration/WinningResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package lotto.enumeration

enum class WinningResult(val value: String) {
STATISTIC("당첨 통계\n---"),
THREE_COUNT("3개 일치 (5,000원) - "),
FOUR_COUNT("4개 일치 (50,000원) - "),
FIVE_COUNT("5개 일치 (1,500,000원) - "),
FIVE_COUNT_WITH_BONUS("5개 일치, 보너스 볼 일치 (30,000,000원) - "),
SIX_COUNT("6개 일치 (2,000,000,000원) - "),
UNIT("개"),
ALL_EARN_RATE("총 수익률은 "),
END_PERCENT("%입니다.");
}
68 changes: 68 additions & 0 deletions src/test/kotlin/lotto/LottoServiceTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package lotto

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

class LottoServiceTest {

@Test
fun `구입 개수에 맞게 로또 발행`() {
var lotto = LottoService().buyLotto(3)
assertEquals(3, lotto.size)
lotto = LottoService().buyLotto(5)
assertEquals(5, lotto.size)
}

@Test
fun `구입 금액 입력 에러 발생 테스트`() {
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidBuyPrice("abc")
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidBuyPrice("1001")
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidBuyPrice("0")
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidBuyPrice("-1")
}
LottoService().checkInvalidBuyPrice("1000")
}

@Test
fun `당첨 번호 입력 에러 발생 테스트`() {
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidWinningNumbers("a,b,c,d,5,6".split(","))
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidWinningNumbers("100,2,3,4,50,60".split(","))
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidWinningNumbers("1,2,3,4,5".split(","))
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidWinningNumbers("1,2,3,4,5,6,7".split(","))
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidWinningNumbers("1,2,6,4,5,6".split(","))
}
LottoService().checkInvalidWinningNumbers("1,2,3,4,5,6".split(","))
}

@Test
fun `보너스 번호 입력 에러 발생 테스트`() {
val lottoNumbers = Lotto("1,2,3,4,5,6".split(",").map { it.toInt() })
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidBonusNumber("a", lottoNumbers)
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidBonusNumber("100", lottoNumbers)
}
assertThrows<IllegalArgumentException> {
LottoService().checkInvalidBonusNumber("1", lottoNumbers)
}
LottoService().checkInvalidBonusNumber("7", lottoNumbers)
}
}