From f6024db81cda085643fd847bc5cbf88d822f8c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=94=BC=EC=9C=A0=EC=A7=84?= <86800087+PIYUJIN@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:05:40 +0900 Subject: [PATCH 01/15] =?UTF-8?q?docs($README.md):=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 216 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fc0ae874..751493426 100644 --- a/README.md +++ b/README.md @@ -1 +1,215 @@ -# kotlin-lotto-precourse +# [2주차] 자동차 경주(kotlin-racingcar-precourse) + +### 🔍 진행 방식 +- 미션은 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항 세 가지로 구성되어 있다. +- 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. +- 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. + +
+ +### 📮 미션 제출 방법 +- 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다. + - GitHub을 활용한 제출 방법은 [프리코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/main/precourse)를 참고해 제출한다. +- GitHub에 미션을 제출한 후 우아한테크코스 지원 플랫폼에 **PR 링크를 포함하여 최종 제출**한다. + - 자세한 안내는 [제출 가이드](https://github.com/woowacourse/woowacourse-docs/tree/main/precourse#%EC%A0%9C%EC%B6%9C-%EA%B0%80%EC%9D%B4%EB%93%9C)를 참고한다. + - 과제를 수행하면서 느낀 점, 배운 점, 많은 시간을 투자한 부분 등 자유롭게 작성한다. + +
+ +### ✔️ 과제 제출 전 체크 리스트 +- 기능을 올바르게 구현했더라도 요구 사항에 명시된 출력 형식을 따르지 않으면 0점을 받게 된다. +- 기능 구현을 완료한 후 아래 가이드에 따라 모든 테스트가 성공적으로 실행되는지 확인한다. +- 테스트가 실패하면 점수가 0점이 되므로 제출하기 전에 반드시 확인한다. + +#### 테스트 실행 가이드 +- IntelliJ IDEA 또는 Android Studio와 같은 IDE에서 ```Kotlin 1.9.24```로 실행되는지 확인한다. +- 터미널에서 Mac 또는 Linux 사용자의 경우 ```./gradlew clean test``` 명령을 실행하고, Windows 사용자의 경우 ```gradlew.bat clean test``` 또는 ```.\gradlew.bat clean test``` 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다. + +``` +BUILD SUCCESSFUL in 0s +``` + +

+ +------------- +### 📝 과제 진행 요구 사항 +- 미션은 [로또 저장소](https://github.com/woowacourse-precourse/kotlin-lotto-7)를 포크하고 클론하는 것으로 시작한다. +- 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다. +- Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다. + - [AngularJS Git Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153)을 참고해 커밋 메시지를 작성한다. +- 자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다. + +
+ +### 🚀 기능 요구 사항 +간단한 로또 발매기를 구현한다. + +- 로또 번호의 숫자 범위는 1~45까지이다. +- 1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다. +- 당첨 번호 추첨 시 중복되지 않는 숫자 6개와 보너스 번호 1개를 뽑는다. +- 당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다. + - 1등: 6개 번호 일치 / 2,000,000,000원 + - 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 + - 3등: 5개 번호 일치 / 1,500,000원 + - 4등: 4개 번호 일치 / 50,000원 + - 5등: 3개 번호 일치 / 5,000원 +- 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. +- 로또 1장의 가격은 1,000원이다. +- 당첨 번호와 보너스 번호를 입력받는다. +- 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다. +- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시키고, `[ERROR]`로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. + - `Exception`이 아닌 `IllegalArgumentException`, `IllegalStateException` 등과 같은 명확한 유형을 처리한다. + + + + +#### 💬 입출력 요구 사항 +- 입력 : + - 로또 구입 금액을 입력 받는다. 구입 금액은 1,000원 단위로 입력 받으며 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다. + + ``` + 14000 + ``` + + - 당첨 번호를 입력 받는다. 번호는 쉼표(,)를 기준으로 구분한다. + + ``` + 1,2,3,4,5,6 + ``` + + - 당첨 번호를 입력 받는다. 번호는 쉼표(,)를 기준으로 구분한다. + + ``` + 7 + ``` + + +- 출력 : + - 발행한 로또 수량 및 번호를 출력한다. 로또 번호는 오름차순으로 정렬하여 보여준다. + ``` + 8개를 구매했습니다. + [8, 21, 23, 41, 42, 43] + [3, 5, 11, 16, 32, 38] + [7, 11, 16, 35, 36, 44] + [1, 8, 11, 31, 41, 42] + [13, 14, 16, 38, 42, 45] + [7, 11, 30, 40, 42, 43] + [2, 13, 22, 32, 38, 45] + [1, 3, 5, 14, 22, 45] + ``` + - 당첨 내역을 출력한다. + ``` + 3개 일치 (5,000원) - 1개 + 4개 일치 (50,000원) - 0개 + 5개 일치 (1,500,000원) - 0개 + 5개 일치, 보너스 볼 일치 (30,000,000원) - 0개 + 6개 일치 (2,000,000,000원) - 0개 + ``` + - 수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%) + ``` + 총 수익률은 62.5%입니다. + ``` + + - 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 "[ERROR]"로 시작해야 한다. + ``` + [ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다. + ``` + +> 실행 결과 예시 + + ``` +구입금액을 입력해 주세요. +8000 + +8개를 구매했습니다. +[8, 21, 23, 41, 42, 43] +[3, 5, 11, 16, 32, 38] +[7, 11, 16, 35, 36, 44] +[1, 8, 11, 31, 41, 42] +[13, 14, 16, 38, 42, 45] +[7, 11, 30, 40, 42, 43] +[2, 13, 22, 32, 38, 45] +[1, 3, 5, 14, 22, 45] + +당첨 번호를 입력해 주세요. +1,2,3,4,5,6 + +보너스 번호를 입력해 주세요. +7 + +당첨 통계 +--- +3개 일치 (5,000원) - 1개 +4개 일치 (50,000원) - 0개 +5개 일치 (1,500,000원) - 0개 +5개 일치, 보너스 볼 일치 (30,000,000원) - 0개 +6개 일치 (2,000,000,000원) - 0개 +총 수익률은 62.5%입니다. + ``` + +
+ +### 🖥️ 프로그래밍 요구 사항 +- Kotlin **1.9.24**에서 실행 가능해야 한다. +- Java 코드가 아닌 **Kotlin 코드**로만 구현해야 한다. +- 프로그램 실행의 시작점은 ```Application의 main()```이다. +- build.gradle.kts 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다. +- 프로그램 종료 시 ```System.exit()``` 또는 ```exitProcess()```를 호출하지 않는다. +- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. +- 코틀린 코드 컨벤션을 지키면서 프로그래밍한다. +- 기본적으로 [Kotlin Style Guide](https://github.com/woowacourse/woowacourse-docs/tree/main/styleguide/kotlin)를 원칙으로 한다. + +### 🖥️ 프로그래밍 요구 사항2 +- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. + - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. + - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. +- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. +- JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. + - 테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다. + - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/) + - [AssertJ User Guide](https://assertj.github.io/doc/) + - [AssertJ Exception Assertions](https://www.baeldung.com/assertj-exception-assertion) + - [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5) + +### 🖥️ 프로그래밍 요구 사항3 +- 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다. + - 함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다. +- else를 지양한다. + - 때로는 if/else, when문을 사용하는 것이 더 깔끔해 보일 수 있다. 어느 경우에 쓰는 것이 적절할지 스스로 고민해 본다. + - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다. +- Enum 클래스를 적용하여 프로그램을 구현한다. +- 구현한 기능에 대한 단위 테스트를 작성한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다. + - 단위 테스트 작성이 익숙하지 않다면 `LottoTest`를 참고하여 학습한 후 테스트를 작성한다. + + + +#### 라이브러리 +- ```camp.nextstep.edu.missionutils```에서 제공하는 ```Randoms 및 Console API```를 사용하여 구현해야 한다. + - Random 값 추출은 ```camp.nextstep.edu.missionutils.Randoms의 pickUniqueNumbersInRange()```를 활용한다. + - 사용자가 입력하는 값은 ```camp.nextstep.edu.missionutils.Console의 readLine()```을 활용한다. + +> 사용 예시 : 1에서 45 사이의 중복되지 않은 정수 6개 반환 + +``` +Randoms.pickUniqueNumbersInRange(1, 45, 6) +``` + +#### Lotto 클래스 +- 제공된 `Lotto 클래스`를 사용하여 구현해야 한다. +- `Lotto`에 `numbers` 이외의 필드(인스턴스 변수)를 추가할 수 없다. +- `numbers`의 접근 제어자인 `private`은 변경할 수 없다. +- `Lotto`의 패키지를 변경할 수 있다. + +``` +package lotto + +class Lotto(private val numbers: List) { + init { + require(numbers.size == 6) { "[ERROR] 로또 번호는 6개여야 합니다." } + } + + // TODO: 추가 기능 구현 +} +``` + From dbedb04f2b1a446a3ccd009345fa322a226d0e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=94=BC=EC=9C=A0=EC=A7=84?= <86800087+PIYUJIN@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:25:18 +0900 Subject: [PATCH 02/15] =?UTF-8?q?docs($README.md):=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 751493426..0c2836882 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [2주차] 자동차 경주(kotlin-racingcar-precourse) +# [3주차] 로또 (kotlin-lotto-precourse) ### 🔍 진행 방식 - 미션은 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항 세 가지로 구성되어 있다. From bf4bae9b12b4c71e278ea2a1b30f65285e15662e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=94=BC=EC=9C=A0=EC=A7=84?= <86800087+PIYUJIN@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:11:17 +0900 Subject: [PATCH 03/15] =?UTF-8?q?docs($README.md):=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=ED=95=A0=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0c2836882..f8aba7e92 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,14 @@ BUILD SUCCESSFUL in 0s - 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시키고, `[ERROR]`로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. - `Exception`이 아닌 `IllegalArgumentException`, `IllegalStateException` 등과 같은 명확한 유형을 처리한다. - +--- +- [ ] 로또 구입 금액 입력 받기 (1000원 단위) +- [ ] 로또 당첨 번호 + 보너스 번호 입력 받기 (쉼표를 기준으로 구분) +- [ ] 로또 구입 금액에 해당하는 만큼 로또를 발행 (로또 1장 가격 : 1000원) +- [ ] 1~45까지의 숫자 중 중복되지 않는 6개의 숫자와 보너스 번호 1개 숫자 추출 +- [ ] 사용자가 구매한 로또 번호와 당첨 번호를 비교 → 당첨 내역 및 수익률(소수점 둘째 자리에서 반올림) 출력 후 로또 게임 종료 +- [ ] 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생(`[ERROR]`로 시작하는 에러 메시지)시킨 후 애플리케이션은 종료 +--- #### 💬 입출력 요구 사항 @@ -78,7 +84,7 @@ BUILD SUCCESSFUL in 0s 1,2,3,4,5,6 ``` - - 당첨 번호를 입력 받는다. 번호는 쉼표(,)를 기준으로 구분한다. + - 보너스 번호를 입력 받는다. ``` 7 From a87bb89dee915e8d3802620508b3b77bcb71c075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=94=BC=EC=9C=A0=EC=A7=84?= <86800087+PIYUJIN@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:14:56 +0900 Subject: [PATCH 04/15] =?UTF-8?q?docs($README.md):=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=ED=95=A0=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f8aba7e92..031c22b20 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,16 @@ BUILD SUCCESSFUL in 0s - [ ] 로또 구입 금액에 해당하는 만큼 로또를 발행 (로또 1장 가격 : 1000원) - [ ] 1~45까지의 숫자 중 중복되지 않는 6개의 숫자와 보너스 번호 1개 숫자 추출 - [ ] 사용자가 구매한 로또 번호와 당첨 번호를 비교 → 당첨 내역 및 수익률(소수점 둘째 자리에서 반올림) 출력 후 로또 게임 종료 -- [ ] 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생(`[ERROR]`로 시작하는 에러 메시지)시킨 후 애플리케이션은 종료 +- [ ] 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생(`[ERROR]`로 시작하는 에러 메시지)시킨 후 애플리케이션은 종료 + - [ ] 로또 구입 금액이 1,000원으로 나누어 떨어지지 않는 경우 + - [ ] 로또 구입 금액이 공백인 경우 + - [ ] 당첨 번호가 공백인 경우 + - [ ] 당첨 번호에 숫자가 아닌 다른 문자열이 포함된 경우 + - [ ] 당첨 번호가 1~45까지의 숫자에 포함되지 않는 경우 + - [ ] 보너스 번호가 공백인 경우 + - [ ] 보너스 번호가 숫자가 아닌 다른 문자열인 경우 + - [ ] 보너스 번호가 1~45까지의 숫자에 포함되지 않는 경우 + - [ ] 당첨 번호 및 보너스 번호에 중복된 숫자가 있는 경우 --- From 10d03da10d99ef7c6c32bb58592e72a5cc4fb871 Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 16:40:16 +0900 Subject: [PATCH 05/15] =?UTF-8?q?docs($.gitignore):=20gitignore=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Git으로 관리할 자원 설정 --- .gitignore | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5dca701a7..9d23512ef 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,36 @@ build/ .springBeans .sts4-cache +# User-specific configurations +.idea/caches/ +.idea/libraries/ +.idea/shelf/ +.idea/workspace.xml +.idea/tasks.xml +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/modules.xml +.idea/scopes/scope_settings.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml +.idea/datasources.xml +.idea/dataSources.ids +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml +.idea/assetWizardSettings.xml +.idea/gradle.xml +.idea/jarRepositories.xml +.idea/navEditor.xml + +### Kotlin ### +# Compiled class file +*.class + ### IntelliJ IDEA ### .idea *.iws @@ -31,5 +61,11 @@ out/ ### VS Code ### .vscode/ -### Mac OS ### +### macOS ### +# General .DS_Store +.AppleDouble +.LSOverride + +# Local configuration file (sdk path, etc) +local.properties From 049e9132553c1a4710053cc2534704ee3785393f Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:05:58 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat($InputView):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/View/InputView.kt | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/kotlin/lotto/View/InputView.kt diff --git a/src/main/kotlin/lotto/View/InputView.kt b/src/main/kotlin/lotto/View/InputView.kt new file mode 100644 index 000000000..18da57e50 --- /dev/null +++ b/src/main/kotlin/lotto/View/InputView.kt @@ -0,0 +1,53 @@ +package lotto.View + +import camp.nextstep.edu.missionutils.Console +import lotto.InputValidation +import lotto.Lotto +import lotto.LottoResult + +class InputView { + fun inputPurchaseLotto(): Int { + println("구입금액을 입력해 주세요.") + + while (true) { + try { + val lottoPayment = Console.readLine().toIntOrNull() + ?: throw IllegalArgumentException("[ERROR] 구입 금액은 숫자로 입력해야 합니다.") + + return lottoAmount + } catch (e: IllegalArgumentException) { + println(e.message) + } + } + + } + + fun inputLottoNum(): List { + println("당첨 번호를 입력해 주세요.") + + while (true) { + try { + val lottoNum = Console.readLine().split(",") + + return lottoNum + } catch (e: IllegalArgumentException) { + println(e.message) + } + } + } + + fun inputBonusLottoNum(lottoNum: List): Int { + println("보너스 번호를 입력해 주세요.") + while (true) { + try { + val bonusLottoNum = Console.readLine().toIntOrNull() + ?: throw IllegalArgumentException("[ERROR] 구입 금액은 숫자로 입력해야 합니다.") + + return bonusLottoNum + } catch (e: IllegalArgumentException) { + // 예외 메시지를 출력하고 재입력을 받도록 한다. + println(e.message) + } + } + } +} \ No newline at end of file From 85f2d52addf236e79c9c16b252740cda0f7f6d61 Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:06:39 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat($InputValidation):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=EA=B0=80=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=EC=9E=85=EB=A0=A5=ED=95=A0=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/InputValidation.kt | 40 ++++++++++++++++++++++++ src/main/kotlin/lotto/View/InputView.kt | 7 +++++ 2 files changed, 47 insertions(+) create mode 100644 src/main/kotlin/lotto/InputValidation.kt diff --git a/src/main/kotlin/lotto/InputValidation.kt b/src/main/kotlin/lotto/InputValidation.kt new file mode 100644 index 000000000..94f0aa719 --- /dev/null +++ b/src/main/kotlin/lotto/InputValidation.kt @@ -0,0 +1,40 @@ +package lotto + +class InputValidation { + + fun checkPayment(payment: Int) { + if(payment <= 0) { + throw IllegalArgumentException("[ERROR] 구입 금액은 1000원 이상 입력해야 합니다.") + } + + if((payment%1000) != 0) { + throw IllegalArgumentException("[ERROR] 구입 금액은 1000원 단위로 입력해야 합니다.") + } + } + + fun checkLottoNum(lottoNum: List) { + lottoNum.forEach { num -> + val lottoNumber = num.toIntOrNull() ?: throw IllegalArgumentException("[ERROR] 당첨 번호는 정수로 입력해야 합니다.") + + if (lottoNumber !in 1..45) { + throw IllegalArgumentException("[ERROR] 당첨 번호는 1~45까지의 숫자로만 입력해야 합니다. : $num") + } + } + + if(lottoNum.size != 6) { + throw IllegalArgumentException("[ERROR] 당첨 번호는 6개 입력해야 합니다.") + } + } + + fun checkBonusLottoNum(bonusLottoNum: Int) { + if(bonusLottoNum !in 1..45) { + throw IllegalArgumentException("[ERROR] 보너스 번호는 1~45까지의 숫자로만 입력해야 합니다. : $bonusLottoNum") + } + } + + fun checkDuplicate(lottoNum: List, bonusLottoNum: Int) { + if(lottoNum.contains(bonusLottoNum)) { + throw IllegalArgumentException("[ERROR] 로또 당첨 번호는 중복이 아닌 6개의 숫자여야 합니다.") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/lotto/View/InputView.kt b/src/main/kotlin/lotto/View/InputView.kt index 18da57e50..51ae733a3 100644 --- a/src/main/kotlin/lotto/View/InputView.kt +++ b/src/main/kotlin/lotto/View/InputView.kt @@ -14,6 +14,8 @@ class InputView { val lottoPayment = Console.readLine().toIntOrNull() ?: throw IllegalArgumentException("[ERROR] 구입 금액은 숫자로 입력해야 합니다.") + val lottoAmount = LottoResult().checkAmount(lottoPayment) + return lottoAmount } catch (e: IllegalArgumentException) { println(e.message) @@ -29,6 +31,8 @@ class InputView { try { val lottoNum = Console.readLine().split(",") + InputValidation().checkLottoNum(lottoNum) + return lottoNum } catch (e: IllegalArgumentException) { println(e.message) @@ -43,6 +47,9 @@ class InputView { val bonusLottoNum = Console.readLine().toIntOrNull() ?: throw IllegalArgumentException("[ERROR] 구입 금액은 숫자로 입력해야 합니다.") + InputValidation().checkBonusLottoNum(bonusLottoNum) + InputValidation().checkDuplicate(lottoNum, bonusLottoNum) + return bonusLottoNum } catch (e: IllegalArgumentException) { // 예외 메시지를 출력하고 재입력을 받도록 한다. From baff899f31d338910d22b9afdeefa74b48f6b9cb Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:08:14 +0900 Subject: [PATCH 08/15] =?UTF-8?q?feat($Lotto):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EA=B0=80=20=EB=A1=9C=EB=98=90=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EB=A5=BC=20=EC=9E=98=EB=AA=BB=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=ED=95=A0=20=EA=B2=BD=EC=9A=B0=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로또 당첨 번호가 중복되는 경우 - 로또 당첨 번호가 1~45까지의 숫자에 포함되지 않는 경우 - 로또 당첨 번호가 공백인 경우 --- src/main/kotlin/lotto/Lotto.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/kotlin/lotto/Lotto.kt b/src/main/kotlin/lotto/Lotto.kt index b97abc385..a9ce36f6b 100644 --- a/src/main/kotlin/lotto/Lotto.kt +++ b/src/main/kotlin/lotto/Lotto.kt @@ -1,8 +1,13 @@ package lotto class Lotto(private val numbers: List) { + init { require(numbers.size == 6) { "[ERROR] 로또 번호는 6개여야 합니다." } + require(numbers.size == numbers.distinct().size) { "[ERROR] 로또 당첨 번호는 중복이 아닌 6개의 숫자여야 합니다." } + require(numbers.all { it in 1..45}) {"[ERROR] 로또 당첨 번호는 1~45까지의 숫자로만 입력해야 합니다."} + require(numbers.isNotEmpty()) {"[ERROR] 로또 당첨 번호는 중복이 아닌 6개의 숫자여야 합니다."} + } } // TODO: 추가 기능 구현 From 865bf59fac007c6f4a0b8569b9fdb5c641ddb5bf Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:10:38 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat($OutputView):=20=EB=A1=9C=EB=98=90?= =?UTF-8?q?=20=EA=B5=AC=EC=9E=85=20=EA=B8=88=EC=95=A1=EC=97=90=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=ED=95=98=EB=8A=94=20=EB=A1=9C=EB=98=90=20=EB=B0=9C?= =?UTF-8?q?=ED=96=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/View/OutputView.kt | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/kotlin/lotto/View/OutputView.kt diff --git a/src/main/kotlin/lotto/View/OutputView.kt b/src/main/kotlin/lotto/View/OutputView.kt new file mode 100644 index 000000000..96093b500 --- /dev/null +++ b/src/main/kotlin/lotto/View/OutputView.kt @@ -0,0 +1,25 @@ +package lotto.View + +import camp.nextstep.edu.missionutils.Randoms +import lotto.Lotto +import lotto.LottoResult + +class OutputView { + + fun outputLottoNum(amount: Int): MutableList> { + + println("${amount}개를 구매했습니다.") + + var outputLotto = mutableListOf>() + + for (num in 0 until amount) { + outputLotto.add(Randoms.pickUniqueNumbersInRange(1, 45, 6).sorted()) + } + + outputLotto.forEach { lottoNumbers -> + println(lottoNumbers) + } + + return outputLotto + } +} \ No newline at end of file From a77f55cb5846201691689f1291aa9cb907a2d8d3 Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:11:55 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat($Lotto):=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=8B=B9=EC=B2=A8=20=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=B3=B4?= =?UTF-8?q?=EB=84=88=EC=8A=A4=20=EB=B2=88=ED=98=B8=20=EB=B9=84=EA=B5=90=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8B=B9=EC=B2=A8=20=EA=B2=B0=EA=B3=BC=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Lotto.kt | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/lotto/Lotto.kt b/src/main/kotlin/lotto/Lotto.kt index a9ce36f6b..d7946451b 100644 --- a/src/main/kotlin/lotto/Lotto.kt +++ b/src/main/kotlin/lotto/Lotto.kt @@ -1,5 +1,6 @@ package lotto + class Lotto(private val numbers: List) { init { @@ -8,7 +9,29 @@ class Lotto(private val numbers: List) { require(numbers.all { it in 1..45}) {"[ERROR] 로또 당첨 번호는 1~45까지의 숫자로만 입력해야 합니다."} require(numbers.isNotEmpty()) {"[ERROR] 로또 당첨 번호는 중복이 아닌 6개의 숫자여야 합니다."} } + + fun checkMatch(outputLottoNumbers: MutableList>, bonusLottoNum: Int): MutableList { + val results = MutableList(5) { 0 } + + outputLottoNumbers.forEach { outputNum -> + val count = outputNum.count { it in numbers } + val isBonusMatch = bonusLottoNum in outputNum + val rank = checkWinning(count, isBonusMatch) + + rank?.let { results[it] += 1 } + } + + return results } - // TODO: 추가 기능 구현 + fun checkWinning(count: Int, bonusMatch: Boolean): Int? { + return when { + count == 6 -> 4 // 1등 + count == 5 && bonusMatch -> 3 // 2등 + count == 5 -> 2 // 3등 + count == 4 -> 1 // 4등 + count == 3 -> 0 // 5등 + else -> null + } + } } From 4fb1766f8d12d2716a91576a477b557e989c085e Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:14:33 +0900 Subject: [PATCH 11/15] =?UTF-8?q?feat($LottoResult):=20=EB=A1=9C=EB=98=90?= =?UTF-8?q?=20=EC=88=98=EC=9D=B5=EB=A5=A0=20=EA=B3=84=EC=82=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/LottoResult.kt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/kotlin/lotto/LottoResult.kt diff --git a/src/main/kotlin/lotto/LottoResult.kt b/src/main/kotlin/lotto/LottoResult.kt new file mode 100644 index 000000000..8420c1b1f --- /dev/null +++ b/src/main/kotlin/lotto/LottoResult.kt @@ -0,0 +1,24 @@ +package lotto + +class LottoResult { + + fun checkAmount(payment: Int): Int { + InputValidation().checkPayment(payment) + + return (payment / 1000) + } + + fun calculateRevenueRate(payment: Int, results: List): Double { + val revenue = calculateRevenue(results) + + return if (payment == 0) 0.00 else String.format("%.2f", (revenue / payment) * 100).toDouble() + } + + fun calculateRevenue(results: List): Double { + val earningPerRank = listOf(5000, 50000, 1500000, 30000000, 2000000000) + + val totalEarnings = results.zip(earningPerRank).sumOf { (count, earning) -> count * earning } + + return totalEarnings.toDouble() + } +} \ No newline at end of file From fa90a26102c4cfaae4882417619ed3b9b9942c52 Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:15:12 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat($OutputView):=20=EB=A1=9C=EB=98=90?= =?UTF-8?q?=20=EB=8B=B9=EC=B2=A8=20=EB=82=B4=EC=97=AD=20=EB=B0=8F=20?= =?UTF-8?q?=EC=88=98=EC=9D=B5=EB=A5=A0=20=EA=B2=B0=EA=B3=BC=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/View/OutputView.kt | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/kotlin/lotto/View/OutputView.kt b/src/main/kotlin/lotto/View/OutputView.kt index 96093b500..ac86d18b2 100644 --- a/src/main/kotlin/lotto/View/OutputView.kt +++ b/src/main/kotlin/lotto/View/OutputView.kt @@ -22,4 +22,31 @@ class OutputView { return outputLotto } + + fun outputResult( + payment: Int, + outputLottoNum: MutableList>, + lottoNum: List, + bonusLottoNum: Int + ) { + println("당첨 통계\n---") + + val matchResult = Lotto(lottoNum).checkMatch(outputLottoNum, bonusLottoNum) + + val matchCountLabels = listOf( + "3개 일치 (5,000원) -", + "4개 일치 (50,000원) -", + "5개 일치 (1,500,000원) -", + "5개 일치, 보너스 볼 일치 (30,000,000원) -", + "6개 일치 (2,000,000,000원) -" + ) + + matchResult.forEachIndexed { index, count -> + println("${matchCountLabels[index]} ${count}개") + } + + val revenue = LottoResult().calculateRevenueRate(payment, matchResult) + + println("총 수익률은 ${revenue}%입니다.") + } } \ No newline at end of file From f0657ec1c8e4e60c7a887f0906fcb8e50ae395de Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:15:46 +0900 Subject: [PATCH 13/15] =?UTF-8?q?feat($LottoController):=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EB=B0=9C=EB=A7=A4=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/LottoController.kt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/kotlin/lotto/LottoController.kt diff --git a/src/main/kotlin/lotto/LottoController.kt b/src/main/kotlin/lotto/LottoController.kt new file mode 100644 index 000000000..0bedf5928 --- /dev/null +++ b/src/main/kotlin/lotto/LottoController.kt @@ -0,0 +1,20 @@ +package lotto + +import lotto.View.InputView +import lotto.View.OutputView + +class LottoController { + + private var inputView = InputView() + private var outputView = OutputView() + + + fun run() { + val lottoAmount = inputView.inputPurchaseLotto() + val outputLottoNum = outputView.outputLottoNum(lottoAmount) + val lottoNum = inputView.inputLottoNum().map { it.toIntOrNull() ?: throw IllegalArgumentException("[ERROR] 당첨 번호는 정수로 입력해야 합니다.")} + val bonusLottoNum = inputView.inputBonusLottoNum(lottoNum) + + outputView.outputResult(lottoAmount*1000, outputLottoNum, lottoNum, bonusLottoNum) + } +} \ No newline at end of file From 1e7de4259a67e14c1ca234f4477f1b6ffe827bcd Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:16:10 +0900 Subject: [PATCH 14/15] =?UTF-8?q?feat($Application.kt):=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EB=B0=9C=EB=A7=A4=EA=B8=B0=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Application.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/lotto/Application.kt b/src/main/kotlin/lotto/Application.kt index 151821c9c..c0477a6af 100644 --- a/src/main/kotlin/lotto/Application.kt +++ b/src/main/kotlin/lotto/Application.kt @@ -1,5 +1,5 @@ package lotto fun main() { - // TODO: 프로그램 구현 + LottoController().run() } From c0dd6feefa0ef5a6103a02970a5e89cdd3cdc164 Mon Sep 17 00:00:00 2001 From: PIYUJIN Date: Mon, 4 Nov 2024 23:16:44 +0900 Subject: [PATCH 15/15] =?UTF-8?q?feat($LottoTest):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EA=B0=80=20=EC=9E=98=EB=AA=BB=EB=90=9C=20=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20=EC=9E=85=EB=A0=A5=ED=95=A0=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/lotto/LottoTest.kt | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/test/kotlin/lotto/LottoTest.kt b/src/test/kotlin/lotto/LottoTest.kt index 122fae572..080cfdead 100644 --- a/src/test/kotlin/lotto/LottoTest.kt +++ b/src/test/kotlin/lotto/LottoTest.kt @@ -20,4 +20,54 @@ class LottoTest { } // TODO: 추가 기능 구현에 따른 테스트 코드 작성 + @Test + fun `로또 번호의 개수가 6개 이하면 예외가 발생한다`() { + assertThrows { + Lotto(listOf(1, 2, 3, 4, 5)) + } + } + + @Test + fun `로또 번호가 1~45까지의 숫자가 아닌 경우 예외가 발생한다`() { + assertThrows { + Lotto(listOf(1, 2, 3, 4, 5, 55)) + } + + assertThrows { + Lotto(listOf(1, 2, 3, 4, 5, 0)) + } + } + + @Test + fun `보너스 번호가 1~45까지의 숫자가 아닌 경우 예외가 발생한다`() { + + assertThrows { + val bonusLottoNum = 46 + InputValidation().checkBonusLottoNum(bonusLottoNum) + } + + assertThrows { + val bonusLottoNum = 0 + InputValidation().checkBonusLottoNum(bonusLottoNum) + } + } + + @Test + fun `당첨 번호 및 보너스 번호에 중복된 숫자가 있으면 예외가 발생한다`() { + val lottoNum = listOf(1, 2, 3, 4, 5, 6) + val bonusLottoNum = 1 + + assertThrows { + InputValidation().checkDuplicate(lottoNum, bonusLottoNum) + } + } + + @Test + fun `로또 구입 금액이 1,000원으로 나누어 떨어지지 않으면 예외가 발생한다`() { + val lottoPayment = 1500 + + assertThrows { + InputValidation().checkPayment(lottoPayment) + } + } }