From 16d099facbaaebf51a6e94f01e8b174153f81141 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 14:57:50 +0800 Subject: [PATCH 01/30] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/README.md b/README.md index 5fa2560b46..c24d60859f 100644 --- a/README.md +++ b/README.md @@ -1 +1,66 @@ # java-lotto-precourse + +간단한 로또 발매기를 구현한다. + +- [ ] 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]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. + - 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다. + - 로또 번호의 숫자 범위는 1~45까지이다. + - 당첨 번호의 개수는 6개이다. + - 로또 번호는 중복되지 않는다. +- Exception이 아닌 IllegalArgumentException, IllegalStateException 등과 같은 명확한 유형을 처리한다. +- 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 "[ERROR]"로 시작해야 한다. + - ex) [ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다. + +# View (입출력 요구 사항) + +## 입력 +로또 구입 금액을 입력 받는다. 구입 금액은 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개 +총 수익률은 62.5%입니다. +``` +수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%) \ No newline at end of file From 6d5ffe07062f15b3704ac639f993a3a07224701e Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 14:58:08 +0800 Subject: [PATCH 02/30] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=EA=B0=80=20=EC=A4=91=EB=B3=B5=EC=9D=BC=EB=95=8C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Lotto.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 88fc5cf12b..18a73fbabd 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -11,10 +11,22 @@ public Lotto(List numbers) { } private void validate(List numbers) { + validateNumbersSize(numbers); + validateDuplicationNumbers(numbers); + } + + private void validateNumbersSize(List numbers) { if (numbers.size() != 6) { throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다."); } } + private void validateDuplicationNumbers(List numbers) { + List uniqueNumbers = numbers.stream().distinct().toList(); + if (uniqueNumbers.size() != numbers.size()) { + throw new IllegalArgumentException("[ERROR] 로또 번호는 중복될 수 없습니다."); + } + } + // TODO: 추가 기능 구현 } From f784fada0f5247115ea86f0e6d02b4090e31786a Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 15:07:39 +0800 Subject: [PATCH 03/30] =?UTF-8?q?feat:=20=EB=9E=9C=EB=8D=A4=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=EC=9D=B8=20=EB=A1=9C=EB=98=90=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=EB=A7=A4=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/User.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/lotto/User.java diff --git a/src/main/java/lotto/User.java b/src/main/java/lotto/User.java new file mode 100644 index 0000000000..310170c9bc --- /dev/null +++ b/src/main/java/lotto/User.java @@ -0,0 +1,17 @@ +package lotto; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.ArrayList; +import java.util.List; + +public class User { + List boughtLotto = new ArrayList<>(); + + private void buyLottoReceipt(int amount) { + for (int i = 0; i < amount; i++) { + List randomNumbers = Randoms.pickUniqueNumbersInRange(1, 45, 6); + boughtLotto.add(new Lotto(randomNumbers)); + } + } +} From 758e4efe90de0d7ba0e8704c47eda9a5dd873ffb Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 15:11:06 +0800 Subject: [PATCH 04/30] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EB=8A=94=20=EB=8F=88=EC=9D=84=20=EA=B0=80=EC=A7=84=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Money.java | 9 +++++++++ src/main/java/lotto/User.java | 7 ++++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/main/java/lotto/Money.java diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java new file mode 100644 index 0000000000..de9f8173fb --- /dev/null +++ b/src/main/java/lotto/Money.java @@ -0,0 +1,9 @@ +package lotto; + +public class Money { + private final Integer initMoney; + + public Money(int initMoney) { + this.initMoney = initMoney; + } +} diff --git a/src/main/java/lotto/User.java b/src/main/java/lotto/User.java index 310170c9bc..ac28e659a3 100644 --- a/src/main/java/lotto/User.java +++ b/src/main/java/lotto/User.java @@ -6,7 +6,12 @@ import java.util.List; public class User { - List boughtLotto = new ArrayList<>(); + private final List boughtLotto = new ArrayList<>(); + private final Money money; + + public User(int initMoney) { + this.money = new Money(initMoney); + } private void buyLottoReceipt(int amount) { for (int i = 0; i < amount; i++) { From db6601c825d7b0797df84d4fd827d780dd91cb9b Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 17:03:37 +0800 Subject: [PATCH 05/30] =?UTF-8?q?test:=20=EB=8F=88=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 돈은 0이하가 될 수 없다. - 돈은_천원으로_나누어_떨어진다 --- src/test/java/lotto/MoneyTest.java | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/java/lotto/MoneyTest.java diff --git a/src/test/java/lotto/MoneyTest.java b/src/test/java/lotto/MoneyTest.java new file mode 100644 index 0000000000..67a2e49242 --- /dev/null +++ b/src/test/java/lotto/MoneyTest.java @@ -0,0 +1,26 @@ +package lotto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class MoneyTest { + @ParameterizedTest + @DisplayName("돈은 0이하가 될 수 없다.") + @ValueSource(ints = {0,-1,-2}) + void money_must_positive_integer(int money) { + assertThatThrownBy(() -> new Money(money)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("돈은 천원으로 나누어 떨어진다.") + void 돈은_천원으로_나누어_떨어진다() { + assertThatThrownBy(() -> new Money(1540)) + .isInstanceOf(IllegalArgumentException.class); + } + +} \ No newline at end of file From ca5af59bb8053dda599e2387a3fe5cd45093701e Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 17:05:24 +0800 Subject: [PATCH 06/30] =?UTF-8?q?test:=20=EB=A1=9C=EB=98=90=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로또 번호는 1~45사이의 수다 --- src/test/java/lotto/LottoTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 309f4e50ae..05768d75b6 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -21,5 +21,10 @@ class LottoTest { .isInstanceOf(IllegalArgumentException.class); } - // TODO: 추가 기능 구현에 따른 테스트 코드 작성 + @DisplayName("로또 번호는 1~45 사이의 수다.") + @Test + void validateNumberRange() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 99))) + .isNotInstanceOf(IllegalArgumentException.class); + } } From 26f3ba0f50ad45508ee3fafdab8f8d24861b87d0 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 17:15:26 +0800 Subject: [PATCH 07/30] =?UTF-8?q?feat:=20=EA=B5=AC=EC=9E=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=EC=9C=BC=EB=A1=9C=20=EB=B6=80=ED=84=B0=20=EA=B5=AC?= =?UTF-8?q?=EB=A7=A4=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=AA=A8?= =?UTF-8?q?=EB=93=A0=20=EB=A1=9C=EB=98=90=EB=A5=BC=20=EA=B5=AC=EB=A7=A4?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Money.java | 5 +++++ src/main/java/lotto/User.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java index de9f8173fb..dfab782cd5 100644 --- a/src/main/java/lotto/Money.java +++ b/src/main/java/lotto/Money.java @@ -2,8 +2,13 @@ public class Money { private final Integer initMoney; + static int DIVIDE_STANDARD = 1_000; public Money(int initMoney) { this.initMoney = initMoney; } + + public Integer getLottoBuyAvailableAmount() { + return initMoney / DIVIDE_STANDARD; + } } diff --git a/src/main/java/lotto/User.java b/src/main/java/lotto/User.java index ac28e659a3..7e3dd05a09 100644 --- a/src/main/java/lotto/User.java +++ b/src/main/java/lotto/User.java @@ -11,6 +11,7 @@ public class User { public User(int initMoney) { this.money = new Money(initMoney); + this.buyLottoReceipt(this.money.getLottoBuyAvailableAmount()); } private void buyLottoReceipt(int amount) { @@ -19,4 +20,8 @@ private void buyLottoReceipt(int amount) { boughtLotto.add(new Lotto(randomNumbers)); } } + + public List getBoughtLotto() { + return this.boughtLotto; + } } From 247032c77ebfc0b2b0a2134a4e181e1eb1b0aa35 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 17:15:45 +0800 Subject: [PATCH 08/30] =?UTF-8?q?test:=20=EA=B5=AC=EB=A7=A4=ED=95=A0=20?= =?UTF-8?q?=EB=8F=88=20=EB=A7=8C=ED=81=BC=20=EB=A1=9C=EB=98=90=EB=A5=BC=20?= =?UTF-8?q?=EA=B5=AC=EB=A7=A4=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/lotto/UserTest.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/java/lotto/UserTest.java diff --git a/src/test/java/lotto/UserTest.java b/src/test/java/lotto/UserTest.java new file mode 100644 index 0000000000..5c6cf06b5c --- /dev/null +++ b/src/test/java/lotto/UserTest.java @@ -0,0 +1,20 @@ +package lotto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.*; + +class UserTest { + + @DisplayName("구매할 돈 만큼 로또를 구매한다.") + @ParameterizedTest + @ValueSource(ints = {1_000, 18_000, 3_000}) + void buy_lotto_available(int initMoney) { + User user = new User(initMoney); + + assertEquals(initMoney / Money.DIVIDE_STANDARD, user.getBoughtLotto().size()); + } + +} \ No newline at end of file From c7377246ca3382bd276c55e608289089a0544384 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 19:22:20 +0800 Subject: [PATCH 09/30] =?UTF-8?q?feat:=20Integer=20=EC=9E=90=EB=A3=8C?= =?UTF-8?q?=ED=98=95=EC=97=90=EC=84=9C=20LottoNumber=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Lotto.java | 12 +++---- src/main/java/lotto/LottoNumber.java | 45 ++++++++++++++++++++++++ src/main/java/lotto/LottoNumberType.java | 6 ++++ src/main/java/lotto/User.java | 5 ++- 4 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 src/main/java/lotto/LottoNumber.java create mode 100644 src/main/java/lotto/LottoNumberType.java diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 18a73fbabd..80fbb65cd4 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -3,26 +3,26 @@ import java.util.List; public class Lotto { - private final List numbers; + private final List numbers; - public Lotto(List numbers) { + public Lotto(List numbers) { validate(numbers); this.numbers = numbers; } - private void validate(List numbers) { + private void validate(List numbers) { validateNumbersSize(numbers); validateDuplicationNumbers(numbers); } - private void validateNumbersSize(List numbers) { + private void validateNumbersSize(List numbers) { if (numbers.size() != 6) { throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다."); } } - private void validateDuplicationNumbers(List numbers) { - List uniqueNumbers = numbers.stream().distinct().toList(); + private void validateDuplicationNumbers(List numbers) { + List uniqueNumbers = numbers.stream().distinct().toList(); if (uniqueNumbers.size() != numbers.size()) { throw new IllegalArgumentException("[ERROR] 로또 번호는 중복될 수 없습니다."); } diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java new file mode 100644 index 0000000000..693b604978 --- /dev/null +++ b/src/main/java/lotto/LottoNumber.java @@ -0,0 +1,45 @@ +package lotto; + +import java.util.Objects; + +public class LottoNumber extends Number{ + private final LottoNumberType type; + private final Integer number; + + public LottoNumber(Integer number) { + this.type = LottoNumberType.ORDINARY_NUMBER; + this.number = number; + } + + public LottoNumber(LottoNumberType type, Integer number) { + this.type = type; + this.number = number; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof LottoNumber lottoNumber)) return false; + return Objects.equals(this.number, lottoNumber.number); + } + + @Override + public int intValue() { + return this.number; + } + + @Override + public long longValue() { + return this.number; + } + + @Override + public float floatValue() { + return this.number; + } + + @Override + public double doubleValue() { + return this.number; + } +} diff --git a/src/main/java/lotto/LottoNumberType.java b/src/main/java/lotto/LottoNumberType.java new file mode 100644 index 0000000000..59ada1b707 --- /dev/null +++ b/src/main/java/lotto/LottoNumberType.java @@ -0,0 +1,6 @@ +package lotto; + +public enum LottoNumberType { + ORDINARY_NUMBER, + BONUS_NUMBER; +} diff --git a/src/main/java/lotto/User.java b/src/main/java/lotto/User.java index 7e3dd05a09..993bfd3e29 100644 --- a/src/main/java/lotto/User.java +++ b/src/main/java/lotto/User.java @@ -16,7 +16,10 @@ public User(int initMoney) { private void buyLottoReceipt(int amount) { for (int i = 0; i < amount; i++) { - List randomNumbers = Randoms.pickUniqueNumbersInRange(1, 45, 6); + List randomNumbers = Randoms.pickUniqueNumbersInRange(1, 45, 6) + .stream() + .map(LottoNumber::new) + .toList(); boughtLotto.add(new Lotto(randomNumbers)); } } From 7f636a0817e8cede8c0dc16fdb9cddb50fae75f5 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 19:23:19 +0800 Subject: [PATCH 10/30] =?UTF-8?q?test:=20LottoTest=20=EC=9E=90=EB=A3=8C?= =?UTF-8?q?=ED=98=95=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/lotto/LottoTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 05768d75b6..b717cc3701 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -4,27 +4,28 @@ import org.junit.jupiter.api.Test; import java.util.List; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThatThrownBy; class LottoTest { @Test void 로또_번호의_개수가_6개가_넘어가면_예외가_발생한다() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) + assertThatThrownBy(() -> new Lotto(Stream.of(1, 2, 3, 4, 5, 6, 7).map(LottoNumber::new).toList())) .isInstanceOf(IllegalArgumentException.class); } @DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.") @Test void 로또_번호에_중복된_숫자가_있으면_예외가_발생한다() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) + assertThatThrownBy(() -> new Lotto(Stream.of(1, 2, 3, 4, 5, 5).map(LottoNumber::new).toList())) .isInstanceOf(IllegalArgumentException.class); } @DisplayName("로또 번호는 1~45 사이의 수다.") @Test void validateNumberRange() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 99))) + assertThatThrownBy(() -> new Lotto(Stream.of(1, 2, 3, 4, 5, 99).map(LottoNumber::new).toList())) .isNotInstanceOf(IllegalArgumentException.class); } } From 61797cc2f035b8f8ed6cf89822177074ef418bb0 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 19:23:33 +0800 Subject: [PATCH 11/30] =?UTF-8?q?test:=20equal=20override=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/lotto/LottoNumberTest.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/java/lotto/LottoNumberTest.java diff --git a/src/test/java/lotto/LottoNumberTest.java b/src/test/java/lotto/LottoNumberTest.java new file mode 100644 index 0000000000..331315704d --- /dev/null +++ b/src/test/java/lotto/LottoNumberTest.java @@ -0,0 +1,20 @@ +package lotto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class LottoNumberTest { + + @DisplayName("숫자가 같으면 동일하다") + @Test + void equal_test() { + LottoNumber number1 = new LottoNumber(LottoNumberType.ORDINARY_NUMBER, 1); + LottoNumber number2 = new LottoNumber(LottoNumberType.ORDINARY_NUMBER, 1); + LottoNumber number3 = new LottoNumber(LottoNumberType.BONUS_NUMBER, 1); + assertEquals(number1, number2); + assertEquals(number1, number3); + } + +} \ No newline at end of file From 1de61715905f7256dc77460e25e3a6340521b975 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 20:59:07 +0800 Subject: [PATCH 12/30] =?UTF-8?q?feat:=20=EA=B0=99=EC=9D=80=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EB=B2=88=ED=98=B8=20=EA=B0=9C=EC=88=98=20=EC=83=88?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Lotto.java | 31 ++++++++++++++++++++++++---- src/main/java/lotto/LottoNumber.java | 6 +++++- src/main/java/lotto/User.java | 5 +---- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 80fbb65cd4..5b082427a8 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -1,13 +1,17 @@ package lotto; +import java.util.Arrays; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; public class Lotto { private final List numbers; - public Lotto(List numbers) { - validate(numbers); - this.numbers = numbers; + public Lotto(List numbers) { + List mapNumbers = numbers.stream().map(LottoNumber::new).toList(); + validate(mapNumbers); + this.numbers = mapNumbers; } private void validate(List numbers) { @@ -28,5 +32,24 @@ private void validateDuplicationNumbers(List numbers) { } } - // TODO: 추가 기능 구현 + public List getNumbers() { + return this.numbers; + } + + public int getSameOrdinaryLottoNumberCount(List lottoNumbers) { + List numbers = this.numbers.stream() + .map(LottoNumber::getNumber) + .toList(); + + long sameCount = lottoNumbers.stream() + .map(LottoNumber::getNumber) + .filter(numbers::contains) + .count(); + + return (int) sameCount; + } + + public void getSameBonusLottoNumber(List lottoNumbers) { + + } } diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java index 693b604978..9972881a10 100644 --- a/src/main/java/lotto/LottoNumber.java +++ b/src/main/java/lotto/LottoNumber.java @@ -2,7 +2,7 @@ import java.util.Objects; -public class LottoNumber extends Number{ +public class LottoNumber extends Number { private final LottoNumberType type; private final Integer number; @@ -16,6 +16,10 @@ public LottoNumber(LottoNumberType type, Integer number) { this.number = number; } + public Integer getNumber() { + return number; + } + @Override public boolean equals(Object obj) { if (this == obj) return true; diff --git a/src/main/java/lotto/User.java b/src/main/java/lotto/User.java index 993bfd3e29..7e3dd05a09 100644 --- a/src/main/java/lotto/User.java +++ b/src/main/java/lotto/User.java @@ -16,10 +16,7 @@ public User(int initMoney) { private void buyLottoReceipt(int amount) { for (int i = 0; i < amount; i++) { - List randomNumbers = Randoms.pickUniqueNumbersInRange(1, 45, 6) - .stream() - .map(LottoNumber::new) - .toList(); + List randomNumbers = Randoms.pickUniqueNumbersInRange(1, 45, 6); boughtLotto.add(new Lotto(randomNumbers)); } } From b77733dcaed18cd85b519ffce8b0a118dcee5f1c Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 21:00:22 +0800 Subject: [PATCH 13/30] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=EB=90=9C=20?= =?UTF-8?q?=EC=88=AB=EC=9E=90=20=EC=A1=B4=EC=9E=AC=EC=8B=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EB=B0=9C=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Lotto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 5b082427a8..49db5ac87e 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -26,7 +26,7 @@ private void validateNumbersSize(List numbers) { } private void validateDuplicationNumbers(List numbers) { - List uniqueNumbers = numbers.stream().distinct().toList(); + Set uniqueNumbers = numbers.stream().map(LottoNumber::getNumber).collect(Collectors.toSet()); if (uniqueNumbers.size() != numbers.size()) { throw new IllegalArgumentException("[ERROR] 로또 번호는 중복될 수 없습니다."); } From d4917a764b6cc989109cb186544938ebc1a1b167 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 21:26:06 +0800 Subject: [PATCH 14/30] =?UTF-8?q?feat:=20=EA=B2=B9=EC=B9=98=EB=8A=94=20?= =?UTF-8?q?=EC=88=AB=EC=9E=90=EB=A5=BC=20=EA=B5=AC=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Lotto.java | 39 ++++++++++++++++++++++------ src/main/java/lotto/LottoNumber.java | 4 +++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 49db5ac87e..57c491c982 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -1,15 +1,13 @@ package lotto; -import java.util.Arrays; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; public class Lotto { private final List numbers; public Lotto(List numbers) { - List mapNumbers = numbers.stream().map(LottoNumber::new).toList(); + List mapNumbers = new ArrayList<>(numbers.stream().map(LottoNumber::new).toList()); validate(mapNumbers); this.numbers = mapNumbers; } @@ -26,16 +24,30 @@ private void validateNumbersSize(List numbers) { } private void validateDuplicationNumbers(List numbers) { - Set uniqueNumbers = numbers.stream().map(LottoNumber::getNumber).collect(Collectors.toSet()); - if (uniqueNumbers.size() != numbers.size()) { + List combined = new ArrayList<>(numbers); + + if (this.numbers != null) { + combined.addAll(this.numbers); + } + + long distinctCount = combined.stream() + .map(LottoNumber::getNumber) + .distinct() + .count(); + + if (distinctCount != combined.size()) { throw new IllegalArgumentException("[ERROR] 로또 번호는 중복될 수 없습니다."); } } - public List getNumbers() { return this.numbers; } + public void addLottoNumber(LottoNumber lottoNumber) { + validateDuplicationNumbers(new ArrayList<>(List.of(lottoNumber))); + numbers.add(lottoNumber); + } + public int getSameOrdinaryLottoNumberCount(List lottoNumbers) { List numbers = this.numbers.stream() .map(LottoNumber::getNumber) @@ -49,7 +61,18 @@ public int getSameOrdinaryLottoNumberCount(List lottoNumbers) { return (int) sameCount; } - public void getSameBonusLottoNumber(List lottoNumbers) { + public int getSameBonusLottoNumberCount(List lottoNumbers) { + List numbers = this.numbers.stream() + .filter(lottoNumber -> lottoNumber.getType() == LottoNumberType.BONUS_NUMBER) + .map(LottoNumber::getNumber) + .toList(); + long sameCount = lottoNumbers.stream() + .filter(lottoNumber -> lottoNumber.getType() == LottoNumberType.BONUS_NUMBER) + .map(LottoNumber::getNumber) + .filter(numbers::contains) + .count(); + + return (int) sameCount; } } diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java index 9972881a10..3f808909d0 100644 --- a/src/main/java/lotto/LottoNumber.java +++ b/src/main/java/lotto/LottoNumber.java @@ -20,6 +20,10 @@ public Integer getNumber() { return number; } + public LottoNumberType getType() { + return type; + } + @Override public boolean equals(Object obj) { if (this == obj) return true; From 173f7cc52a425f1cadc4447002ca3c02edc72844 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 21:26:25 +0800 Subject: [PATCH 15/30] =?UTF-8?q?test:=20=EA=B2=B9=EC=B9=98=EB=8A=94=20?= =?UTF-8?q?=EC=88=AB=EC=9E=90=EB=A5=BC=20=EA=B5=AC=ED=95=9C=EB=8B=A4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/lotto/LottoTest.java | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index b717cc3701..d5d7b0d9f2 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -4,28 +4,48 @@ import org.junit.jupiter.api.Test; import java.util.List; -import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; class LottoTest { @Test void 로또_번호의_개수가_6개가_넘어가면_예외가_발생한다() { - assertThatThrownBy(() -> new Lotto(Stream.of(1, 2, 3, 4, 5, 6, 7).map(LottoNumber::new).toList())) + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) .isInstanceOf(IllegalArgumentException.class); } @DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.") @Test void 로또_번호에_중복된_숫자가_있으면_예외가_발생한다() { - assertThatThrownBy(() -> new Lotto(Stream.of(1, 2, 3, 4, 5, 5).map(LottoNumber::new).toList())) + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) .isInstanceOf(IllegalArgumentException.class); } @DisplayName("로또 번호는 1~45 사이의 수다.") @Test void validateNumberRange() { - assertThatThrownBy(() -> new Lotto(Stream.of(1, 2, 3, 4, 5, 99).map(LottoNumber::new).toList())) + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 99))) .isNotInstanceOf(IllegalArgumentException.class); } + + @DisplayName("겹치는 숫자를 구할 수 있다.") + @Test + void getSameOrdinaryNumberCount() { + Lotto lotto1 = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto lotto2 = new Lotto(List.of(1, 2, 3, 9, 4, 5)); + + assertEquals(5, lotto1.getSameOrdinaryLottoNumberCount(lotto2.getNumbers())); + } + + @DisplayName("겹치는 보너스 숫자를 구할 수 있다.") + @Test + void getSameBonusNumberCount() { + Lotto lotto1 = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto lotto2 = new Lotto(List.of(1, 2, 3, 9, 4, 5)); + lotto1.addLottoNumber(new LottoNumber(LottoNumberType.BONUS_NUMBER, 10)); + lotto2.addLottoNumber(new LottoNumber(LottoNumberType.BONUS_NUMBER, 10)); + + assertEquals(1, lotto1.getSameBonusLottoNumberCount(lotto2.getNumbers())); + } } From 891d94bbd0b3977ac1e8334cc312556d687bb31a Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 21:36:12 +0800 Subject: [PATCH 16/30] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=20=EA=B8=88=EC=95=A1=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Money.java | 10 ++++++++ .../java/lotto/reward/RewardCondition.java | 7 ++++++ .../lotto/reward/StandardRewardCondition.java | 23 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/main/java/lotto/reward/RewardCondition.java create mode 100644 src/main/java/lotto/reward/StandardRewardCondition.java diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java index dfab782cd5..78de666c5f 100644 --- a/src/main/java/lotto/Money.java +++ b/src/main/java/lotto/Money.java @@ -2,13 +2,23 @@ public class Money { private final Integer initMoney; + private Integer currentMoney; static int DIVIDE_STANDARD = 1_000; public Money(int initMoney) { this.initMoney = initMoney; + this.currentMoney = initMoney; } public Integer getLottoBuyAvailableAmount() { return initMoney / DIVIDE_STANDARD; } + + public void addMoney(Money money) { + currentMoney += money.getMoney(); + } + + public Integer getMoney() { + return currentMoney; + } } diff --git a/src/main/java/lotto/reward/RewardCondition.java b/src/main/java/lotto/reward/RewardCondition.java new file mode 100644 index 0000000000..82a67bf758 --- /dev/null +++ b/src/main/java/lotto/reward/RewardCondition.java @@ -0,0 +1,7 @@ +package lotto.reward; + +import lotto.Money; + +public interface RewardCondition { + public Money getReward(int ordinaryNumberCount, int bonusNumberCount); +} diff --git a/src/main/java/lotto/reward/StandardRewardCondition.java b/src/main/java/lotto/reward/StandardRewardCondition.java new file mode 100644 index 0000000000..1162c717ea --- /dev/null +++ b/src/main/java/lotto/reward/StandardRewardCondition.java @@ -0,0 +1,23 @@ +package lotto.reward; + +import lotto.Money; + +public class StandardRewardCondition implements RewardCondition { + /** + * 1등: 6개 번호 일치 / 2,000,000,000원 + * 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 + * 3등: 5개 번호 일치 / 1,500,000원 + * 4등: 4개 번호 일치 / 50,000원 + * 5등: 3개 번호 일치 / 5,000원 + */ + @Override + public Money getReward(int ordinaryNumberCount, int bonusNumberCount) { + int totalCount = ordinaryNumberCount + bonusNumberCount; + if (totalCount >= 6) return new Money(2_000_000_000); + if (ordinaryNumberCount == 5 && bonusNumberCount == 1) return new Money(30_000_000); + if (totalCount == 5) return new Money(1_500_000); + if (totalCount == 4) return new Money(50_000); + if (totalCount == 3) return new Money(5_000); + return new Money(0); + } +} From e1403a0e4cb39034d11bb9d22359ddf21628cc61 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 21:42:59 +0800 Subject: [PATCH 17/30] =?UTF-8?q?feat:=20=EC=88=98=EC=9D=B5=EB=A5=A0?= =?UTF-8?q?=EC=9D=84=20=EA=B5=AC=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Money.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java index 78de666c5f..ecc1106151 100644 --- a/src/main/java/lotto/Money.java +++ b/src/main/java/lotto/Money.java @@ -21,4 +21,9 @@ public void addMoney(Money money) { public Integer getMoney() { return currentMoney; } + + public Double getRateOfReturn() { + double roi = (double) (currentMoney - initMoney) / initMoney * 100; + return Math.round(roi * 100) / 100.0; + } } From a946eeb8a59c79a093820a02ddaeef5d730fb09a Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 21:43:13 +0800 Subject: [PATCH 18/30] =?UTF-8?q?test:=20=EC=88=98=EC=9D=B5=EB=A5=A0?= =?UTF-8?q?=EC=9D=B4=20=EC=98=AC=EB=B0=94=EB=A5=B4=EA=B2=8C=20=EB=82=98?= =?UTF-8?q?=EC=98=A4=EB=8A=94=EC=A7=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/lotto/MoneyTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/lotto/MoneyTest.java b/src/test/java/lotto/MoneyTest.java index 67a2e49242..f1bf5d5a99 100644 --- a/src/test/java/lotto/MoneyTest.java +++ b/src/test/java/lotto/MoneyTest.java @@ -1,11 +1,13 @@ package lotto; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; class MoneyTest { @ParameterizedTest @@ -23,4 +25,12 @@ void money_must_positive_integer(int money) { .isInstanceOf(IllegalArgumentException.class); } + @Test + @DisplayName("수익률을 계산한다.") + void calculate_rate_of_return() { + Money money = new Money(8_000); + money.addMoney(new Money(5_000)); + assertEquals(62.5, money.getRateOfReturn()); + } + } \ No newline at end of file From b8f0015cf1a8a1de486a7eaa6face7572459f009 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:04:26 +0800 Subject: [PATCH 19/30] =?UTF-8?q?feat:=20=EC=88=AB=EC=9E=90=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/LottoNumber.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java index 3f808909d0..417fb4db05 100644 --- a/src/main/java/lotto/LottoNumber.java +++ b/src/main/java/lotto/LottoNumber.java @@ -7,15 +7,23 @@ public class LottoNumber extends Number { private final Integer number; public LottoNumber(Integer number) { + validateNumberRange(number); this.type = LottoNumberType.ORDINARY_NUMBER; this.number = number; } public LottoNumber(LottoNumberType type, Integer number) { + validateNumberRange(number); this.type = type; this.number = number; } + private void validateNumberRange(int number) { + if (number < 1 || number > 45) { + throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + } + public Integer getNumber() { return number; } @@ -50,4 +58,9 @@ public float floatValue() { public double doubleValue() { return this.number; } + + @Override + public String toString() { + return this.number.toString(); + } } From 02c241f452df31c02a7407d04252279ab4a719d8 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:05:45 +0800 Subject: [PATCH 20/30] =?UTF-8?q?feat:=20=EA=B5=AC=EB=A7=A4=ED=95=A0=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=EC=9D=B4=20=EC=B2=9C=EC=9B=90=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=EB=A1=9C=20=EB=81=9D=EB=82=98=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Money.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java index ecc1106151..c639b68e01 100644 --- a/src/main/java/lotto/Money.java +++ b/src/main/java/lotto/Money.java @@ -6,6 +6,9 @@ public class Money { static int DIVIDE_STANDARD = 1_000; public Money(int initMoney) { + if (initMoney % DIVIDE_STANDARD != 0) { + throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위여야 합니다."); + } this.initMoney = initMoney; this.currentMoney = initMoney; } From a50a04e59a3e396f634a813d082786f5f99cf00e Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:06:12 +0800 Subject: [PATCH 21/30] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=20=EB=B7=B0=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/java/lotto/view/InputView.java | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/lotto/view/InputView.java diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java new file mode 100644 index 0000000000..b94b62e45c --- /dev/null +++ b/src/main/java/lotto/view/InputView.java @@ -0,0 +1,28 @@ +package lotto.view; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static camp.nextstep.edu.missionutils.Console.readLine; + +public class InputView { + public static int readPurchaseAmount() { + System.out.println("구입금액을 입력해 주세요."); + return Integer.parseInt(readLine()); + } + + public static List readWinningNumbers() { + System.out.println("당첨 번호를 입력해 주세요."); + String[] numbers = readLine().split(","); + return Arrays.stream(numbers) + .map(String::trim) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } + + public static int readBonusNumber() { + System.out.println("보너스 번호를 입력해 주세요."); + return Integer.parseInt(readLine()); + } +} From 098c7b9af0acb795c11b353faf9dba3b01776f71 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:15:51 +0800 Subject: [PATCH 22/30] =?UTF-8?q?refactor:=20LottoNumber=20Number=20?= =?UTF-8?q?=EC=83=81=EC=86=8D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/LottoNumber.java | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java index 417fb4db05..de5155795c 100644 --- a/src/main/java/lotto/LottoNumber.java +++ b/src/main/java/lotto/LottoNumber.java @@ -2,7 +2,7 @@ import java.util.Objects; -public class LottoNumber extends Number { +public class LottoNumber { private final LottoNumberType type; private final Integer number; @@ -39,26 +39,6 @@ public boolean equals(Object obj) { return Objects.equals(this.number, lottoNumber.number); } - @Override - public int intValue() { - return this.number; - } - - @Override - public long longValue() { - return this.number; - } - - @Override - public float floatValue() { - return this.number; - } - - @Override - public double doubleValue() { - return this.number; - } - @Override public String toString() { return this.number.toString(); From d9a35df1996b2bc559a449f67406fdce8c4b1af0 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:36:21 +0800 Subject: [PATCH 23/30] =?UTF-8?q?refactor:=20=EB=A6=AC=EC=9B=8C=EB=93=9C?= =?UTF-8?q?=20=EC=A0=84=EB=9E=B5=EC=9D=84=20ENUM=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/lotto/reward/RewardCondition.java | 42 +++++++++++++++++-- .../lotto/reward/StandardRewardCondition.java | 23 ---------- 2 files changed, 38 insertions(+), 27 deletions(-) delete mode 100644 src/main/java/lotto/reward/StandardRewardCondition.java diff --git a/src/main/java/lotto/reward/RewardCondition.java b/src/main/java/lotto/reward/RewardCondition.java index 82a67bf758..96e889b4e8 100644 --- a/src/main/java/lotto/reward/RewardCondition.java +++ b/src/main/java/lotto/reward/RewardCondition.java @@ -1,7 +1,41 @@ package lotto.reward; -import lotto.Money; +public enum RewardCondition { + FIRST(6, 2_000_000_000L), + SECOND(5, 30_000_000L), + THIRD(5, 1_500_000L), + FOURTH(4, 50_000L), + FIFTH(3, 5_000L), + MISS(0, 0L); -public interface RewardCondition { - public Money getReward(int ordinaryNumberCount, int bonusNumberCount); -} + private final int matchCount; + private final long prize; + + RewardCondition(int matchCount, long prize) { + this.matchCount = matchCount; + this.prize = prize; + } + + public static RewardCondition valueOf(int matchCount, boolean hasBonus) { + if (matchCount == 6) { + return FIRST; + } + if (matchCount == 5) { + if (hasBonus) { + return SECOND; + } + return THIRD; + } + if (matchCount == 4) { + return FOURTH; + } + if (matchCount == 3) { + return FIFTH; + } + return MISS; + } + + public long getPrize() { + return prize; + } +} \ No newline at end of file diff --git a/src/main/java/lotto/reward/StandardRewardCondition.java b/src/main/java/lotto/reward/StandardRewardCondition.java deleted file mode 100644 index 1162c717ea..0000000000 --- a/src/main/java/lotto/reward/StandardRewardCondition.java +++ /dev/null @@ -1,23 +0,0 @@ -package lotto.reward; - -import lotto.Money; - -public class StandardRewardCondition implements RewardCondition { - /** - * 1등: 6개 번호 일치 / 2,000,000,000원 - * 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 - * 3등: 5개 번호 일치 / 1,500,000원 - * 4등: 4개 번호 일치 / 50,000원 - * 5등: 3개 번호 일치 / 5,000원 - */ - @Override - public Money getReward(int ordinaryNumberCount, int bonusNumberCount) { - int totalCount = ordinaryNumberCount + bonusNumberCount; - if (totalCount >= 6) return new Money(2_000_000_000); - if (ordinaryNumberCount == 5 && bonusNumberCount == 1) return new Money(30_000_000); - if (totalCount == 5) return new Money(1_500_000); - if (totalCount == 4) return new Money(50_000); - if (totalCount == 3) return new Money(5_000); - return new Money(0); - } -} From ed8548e6d5ca1e8124fbc139a5505bbbcf043f07 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:36:39 +0800 Subject: [PATCH 24/30] =?UTF-8?q?refactor:=20Integer=20=EC=9E=90=EB=A3=8C?= =?UTF-8?q?=ED=98=95=20long=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Money.java | 17 +++++++++-------- src/main/java/lotto/User.java | 6 +++++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java index c639b68e01..c183b9f445 100644 --- a/src/main/java/lotto/Money.java +++ b/src/main/java/lotto/Money.java @@ -1,11 +1,11 @@ package lotto; public class Money { - private final Integer initMoney; - private Integer currentMoney; - static int DIVIDE_STANDARD = 1_000; + private final Long initMoney; + private Long currentMoney; + static long DIVIDE_STANDARD = 1_000L; - public Money(int initMoney) { + public Money(long initMoney) { if (initMoney % DIVIDE_STANDARD != 0) { throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위여야 합니다."); } @@ -13,20 +13,21 @@ public Money(int initMoney) { this.currentMoney = initMoney; } - public Integer getLottoBuyAvailableAmount() { - return initMoney / DIVIDE_STANDARD; + public int getLottoBuyAvailableAmount() { + return (int) (initMoney / DIVIDE_STANDARD); } public void addMoney(Money money) { currentMoney += money.getMoney(); } - public Integer getMoney() { + public Long getMoney() { return currentMoney; } public Double getRateOfReturn() { + if (currentMoney == 0) return 0.0; double roi = (double) (currentMoney - initMoney) / initMoney * 100; - return Math.round(roi * 100) / 100.0; + return Math.round(roi * 10.0) / 10.0; } } diff --git a/src/main/java/lotto/User.java b/src/main/java/lotto/User.java index 7e3dd05a09..f59ddfa84f 100644 --- a/src/main/java/lotto/User.java +++ b/src/main/java/lotto/User.java @@ -9,7 +9,7 @@ public class User { private final List boughtLotto = new ArrayList<>(); private final Money money; - public User(int initMoney) { + public User(long initMoney) { this.money = new Money(initMoney); this.buyLottoReceipt(this.money.getLottoBuyAvailableAmount()); } @@ -24,4 +24,8 @@ private void buyLottoReceipt(int amount) { public List getBoughtLotto() { return this.boughtLotto; } + + public Money getMoney() { + return money; + } } From b25d2fa96f4210cb8c4772b918f179cfd723fbc0 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:38:03 +0800 Subject: [PATCH 25/30] =?UTF-8?q?feat:=20=EC=B6=9C=EB=A0=A5=20view=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/java/lotto/view/OutputView.java | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/lotto/view/OutputView.java diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java new file mode 100644 index 0000000000..7a257f3688 --- /dev/null +++ b/src/main/java/lotto/view/OutputView.java @@ -0,0 +1,32 @@ +package lotto.view; + +import lotto.Lotto; +import lotto.LottoNumber; +import lotto.reward.RewardCondition; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public class OutputView { + + public static void printPurchasedLottos(List lottos) { + System.out.println(lottos.size() + "개를 구매했습니다."); + for (Lotto lotto : lottos) { + lotto.getNumbers().sort(Comparator.comparingInt(LottoNumber::getNumber)); + System.out.println(lotto.getNumbers()); + } + System.out.println(); + } + + public static void printStatistics(Map result, double profitRate) { + System.out.println("당첨 통계"); + System.out.println("---"); + System.out.printf("3개 일치 (5,000원) - %d개%n", result.getOrDefault(RewardCondition.FIFTH, 0)); + System.out.printf("4개 일치 (50,000원) - %d개%n", result.getOrDefault(RewardCondition.FOURTH, 0)); + System.out.printf("5개 일치 (1,500,000원) - %d개%n", result.getOrDefault(RewardCondition.THIRD, 0)); + System.out.printf("5개 일치, 보너스 볼 일치 (30,000,000원) - %d개%n", result.getOrDefault(RewardCondition.SECOND, 0)); + System.out.printf("6개 일치 (2,000,000,000원) - %d개%n", result.getOrDefault(RewardCondition.FIRST, 0)); + System.out.printf("총 수익률은 %.1f%%입니다.%n", profitRate); + } +} From 14efcab20f857b423e5e0e5618c7547f8338f7ab Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:44:05 +0800 Subject: [PATCH 26/30] =?UTF-8?q?feat:=20controller=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Application.java | 79 +++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba4..aef1b1a2f7 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,82 @@ package lotto; +import lotto.reward.RewardCondition; +import lotto.view.InputView; +import lotto.view.OutputView; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + Money purchaseAmount = readPurchaseAmountWithRetry(); + User user = new User(purchaseAmount.getMoney()); + OutputView.printPurchasedLottos(user.getBoughtLotto()); + + Lotto winningLotto = readWinningLottoWithRetry(); + readBonusNumberWithRetry(winningLotto); + + Map result = calculateResults(user.getBoughtLotto(), winningLotto); + + long totalWinnings = 0; + for (Map.Entry entry : result.entrySet()) { + if (entry.getKey() != RewardCondition.MISS) { + totalWinnings += entry.getKey().getPrize() * entry.getValue(); + } + } + + Money userMoney = user.getMoney(); + userMoney.addMoney(new Money(totalWinnings - userMoney.getMoney())); + + double roi = userMoney.getRateOfReturn(); + + OutputView.printStatistics(result, roi); + } + + private static Money readPurchaseAmountWithRetry() { + while (true) { + try { + return new Money(InputView.readPurchaseAmount()); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + + private static Lotto readWinningLottoWithRetry() { + while (true) { + try { + return new Lotto(InputView.readWinningNumbers()); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + + private static void readBonusNumberWithRetry(Lotto winningLotto) { + while (true) { + try { + winningLotto.addLottoNumber(new LottoNumber(LottoNumberType.BONUS_NUMBER, InputView.readBonusNumber())); + return; + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + + private static Map calculateResults(List purchasedLottos, Lotto winningLotto) { + Map result = new EnumMap<>(RewardCondition.class); + for (RewardCondition condition : RewardCondition.values()) { + result.put(condition, 0); + } + + for (Lotto lotto : purchasedLottos) { + int matchCount = lotto.getSameOrdinaryLottoNumberCount(winningLotto.getNumbers()); + boolean hasBonus = lotto.getSameBonusLottoNumberCount(winningLotto.getNumbers()) >= 1; + RewardCondition reward = RewardCondition.valueOf(matchCount, hasBonus); + result.put(reward, result.get(reward) + 1); + } + return result; } -} +} \ No newline at end of file From f5cfe87212deae202b0d26bf3b3137fd27d9652b Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:46:25 +0800 Subject: [PATCH 27/30] =?UTF-8?q?refactor:=20=EB=A7=A4=EC=A7=81=EB=84=98?= =?UTF-8?q?=EB=B2=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/LottoNumber.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java index de5155795c..fd27756f59 100644 --- a/src/main/java/lotto/LottoNumber.java +++ b/src/main/java/lotto/LottoNumber.java @@ -5,6 +5,8 @@ public class LottoNumber { private final LottoNumberType type; private final Integer number; + private final static int MIN_RANGE_NUMBER = 1; + private final static int MAX_RANGE_NUMBER = 45; public LottoNumber(Integer number) { validateNumberRange(number); @@ -19,7 +21,7 @@ public LottoNumber(LottoNumberType type, Integer number) { } private void validateNumberRange(int number) { - if (number < 1 || number > 45) { + if (number < MIN_RANGE_NUMBER || number > MAX_RANGE_NUMBER) { throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); } } From 534e379bc18c011343eb8efd0563c31830a19f88 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:49:22 +0800 Subject: [PATCH 28/30] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/lotto/LottoNumberTest.java | 10 +++++++++- src/test/java/lotto/LottoTest.java | 6 ------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/test/java/lotto/LottoNumberTest.java b/src/test/java/lotto/LottoNumberTest.java index 331315704d..db7b18cef2 100644 --- a/src/test/java/lotto/LottoNumberTest.java +++ b/src/test/java/lotto/LottoNumberTest.java @@ -3,7 +3,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; class LottoNumberTest { @@ -17,4 +18,11 @@ void equal_test() { assertEquals(number1, number3); } + @DisplayName("숫자는 1~45까지 숫자를 가질 수 있다.") + @Test + void validateNumberRange() { + assertThatThrownBy(() -> new LottoNumber(99)) + .isInstanceOf(IllegalArgumentException.class); + } + } \ No newline at end of file diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index d5d7b0d9f2..88e3c96148 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -22,12 +22,6 @@ class LottoTest { .isInstanceOf(IllegalArgumentException.class); } - @DisplayName("로또 번호는 1~45 사이의 수다.") - @Test - void validateNumberRange() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 99))) - .isNotInstanceOf(IllegalArgumentException.class); - } @DisplayName("겹치는 숫자를 구할 수 있다.") @Test From 0528c77554803ddc64585ad3a4c6fe2a0e589748 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:54:17 +0800 Subject: [PATCH 29/30] =?UTF-8?q?fix:=20=EC=88=98=EC=9D=B5=EB=A5=A0=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=EA=B3=B5=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Application.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index aef1b1a2f7..08061f5a6f 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -27,7 +27,7 @@ public static void main(String[] args) { } Money userMoney = user.getMoney(); - userMoney.addMoney(new Money(totalWinnings - userMoney.getMoney())); + userMoney.addMoney(new Money(totalWinnings)); double roi = userMoney.getRateOfReturn(); @@ -39,7 +39,7 @@ private static Money readPurchaseAmountWithRetry() { try { return new Money(InputView.readPurchaseAmount()); } catch (IllegalArgumentException e) { - System.out.println(e.getMessage()); + System.out.println("[ERROR] " + e.getMessage()); } } } @@ -49,7 +49,7 @@ private static Lotto readWinningLottoWithRetry() { try { return new Lotto(InputView.readWinningNumbers()); } catch (IllegalArgumentException e) { - System.out.println(e.getMessage()); + System.out.println("[ERROR] " + e.getMessage()); } } } From b115684c44027db92e1a2504827e4a579b22c238 Mon Sep 17 00:00:00 2001 From: lepitaaar Date: Mon, 3 Nov 2025 22:54:34 +0800 Subject: [PATCH 30/30] =?UTF-8?q?fix:=20=EC=96=91=EC=88=98=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Money.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java index c183b9f445..08f342a9c4 100644 --- a/src/main/java/lotto/Money.java +++ b/src/main/java/lotto/Money.java @@ -6,6 +6,7 @@ public class Money { static long DIVIDE_STANDARD = 1_000L; public Money(long initMoney) { + validatePositiveInteger(initMoney); if (initMoney % DIVIDE_STANDARD != 0) { throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위여야 합니다."); } @@ -13,6 +14,10 @@ public Money(long initMoney) { this.currentMoney = initMoney; } + private void validatePositiveInteger(long initMoney) { + if (initMoney <= 0) throw new IllegalArgumentException("[ERROR] 돈은 0이하가 될 수 없습니다."); + } + public int getLottoBuyAvailableAmount() { return (int) (initMoney / DIVIDE_STANDARD); }