From 8246a990226e9462cd4de745383e1424aa5b0a85 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 13:12:57 +0900 Subject: [PATCH 01/10] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index 8a4f22ed0..320c7da53 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ # java-planetlotto-precourse + +- 입력/출력 역할은 제공된 InputView, OutputView에서 수행하며 기존 메서드를 수정, 삭제할 수 없다. + +## 구매 + +- [ ] 구입금액 입력 프롬프트를 출력한다. + - 메시지: "구입금액을 입력해 주세요." +- [ ] 구입금액을 입력받는다. + - [ ] `IllegalArgumentException`: 정수가 아닌 경우 + - [ ] `IllegalArgumentException`: 500의 배수인 자연수가 아닌 경우 +- [ ] 로또 구매 개수를 출력한다. + - 메시지: 개수 + "개를 구매했습니다." +- [ ] 구매 개수만큼 로또를 발행한다. + - [ ] 각 로또에 대해, 1~30 사이의 중복되지 않는 숫자 5개를 뽑는다. +- [ ] 각 로또에 대해 오름차순으로 정렬해, 구매한 로또들의 번호를 출력한다. + - 메시지 예시: "[8, 11, 13, 21, 22]" + +## 추첨 + +- [ ] 당첨 번호 입력 프롬프트를 출력한다. + - 메시지: "당첨 번호를 입력해 주세요." +- [ ] 당첨 번호를 쉼표를 기준으로 5개 입력받는다. + - [x] `IllegalArgumentException`: 정수가 아닌게 포함된 경우 + - [x] `IllegalArgumentException`: 1~30 사이가 아닌 값이 포함된 경우 + - [x] `IllegalArgumentException`: 중복된 숫자가 포함된 경우 +- [ ] 보너스 번호를 입력받는다. + - [ ] `IllegalArgumentException`: 정수가 아닌 경우 + - [ ] `IllegalArgumentException`: 1~30 사이가 아닌 경우 +- [ ] 당첨을 진행한다. + - [ ] 각 로또에 대해, 당첨번호 + 보너스 번호와 비교하여 등수를 계산한다. + - 1등: 5개 번호 일치 / 100,000,000원 + - 2등: 4개 번호 + 보너스 번호 일치 / 10,000,000원 + - 3등: 4개 번호 일치 / 1,500,000원 + - 4등: 3개 번호 일치 + 보너스 번호 일치 / 500,000원 + - 5등: 2개 번호 일치 + 보너스 번호 일치 / 5,000원 + - 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. + +## 당첨 통계 + +- [ ] 각 등수 별로, 해당되는 개수를 계산한다. +- [ ] 1등부터 차례대로, 등수 별 해당되는 개수를 출력한다. + - 메시지: 등수 + "개 일치 (" + String.format("%,d", 상금) + "원) - " + 해당_개수 + "개" +- [ ] 총 수익율을 출력한다. + - 메시지: "총 수익률은 " + 소숫점_1_자리_수익률 + "%입니다." + From 9981b331386d138f7a60fbaeba5fb533dedf0861 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 13:36:12 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EA=B5=AC?= =?UTF-8?q?=EB=A7=A4=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 --- README.md | 26 ++++----- src/main/java/planetlotto/Application.java | 2 + src/main/java/planetlotto/Controller.java | 33 ++++++++++++ src/main/java/planetlotto/model/Lotto.java | 36 +++++++++++++ src/main/java/planetlotto/model/Lottos.java | 53 +++++++++++++++++++ .../java/planetlotto/view/OutputView.java | 3 ++ 6 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 src/main/java/planetlotto/Controller.java create mode 100644 src/main/java/planetlotto/model/Lotto.java create mode 100644 src/main/java/planetlotto/model/Lottos.java diff --git a/README.md b/README.md index 320c7da53..3e38eab58 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,28 @@ - 입력/출력 역할은 제공된 InputView, OutputView에서 수행하며 기존 메서드를 수정, 삭제할 수 없다. -## 구매 +## 로또 구매 -- [ ] 구입금액 입력 프롬프트를 출력한다. +- [x] 구입금액 입력 프롬프트를 출력한다. - 메시지: "구입금액을 입력해 주세요." -- [ ] 구입금액을 입력받는다. - - [ ] `IllegalArgumentException`: 정수가 아닌 경우 - - [ ] `IllegalArgumentException`: 500의 배수인 자연수가 아닌 경우 -- [ ] 로또 구매 개수를 출력한다. +- [x] 구입금액을 입력받는다. + - [x] `IllegalArgumentException`: 정수가 아닌 경우 + - [x] `IllegalArgumentException`: 500의 배수인 자연수가 아닌 경우 +- [x] 로또 구매 개수를 출력한다. - 메시지: 개수 + "개를 구매했습니다." -- [ ] 구매 개수만큼 로또를 발행한다. - - [ ] 각 로또에 대해, 1~30 사이의 중복되지 않는 숫자 5개를 뽑는다. -- [ ] 각 로또에 대해 오름차순으로 정렬해, 구매한 로또들의 번호를 출력한다. +- [x] 구매 개수만큼 로또를 발행한다. + - [x] 각 로또에 대해, 1~30 사이의 중복되지 않는 숫자 5개를 뽑는다. +- [x] 각 로또에 대해 오름차순으로 정렬해, 구매한 로또들의 번호를 출력한다. - 메시지 예시: "[8, 11, 13, 21, 22]" -## 추첨 +## 로또 추첨 - [ ] 당첨 번호 입력 프롬프트를 출력한다. - 메시지: "당첨 번호를 입력해 주세요." - [ ] 당첨 번호를 쉼표를 기준으로 5개 입력받는다. - - [x] `IllegalArgumentException`: 정수가 아닌게 포함된 경우 - - [x] `IllegalArgumentException`: 1~30 사이가 아닌 값이 포함된 경우 - - [x] `IllegalArgumentException`: 중복된 숫자가 포함된 경우 + - [ ] `IllegalArgumentException`: 정수가 아닌게 포함된 경우 + - [ ] `IllegalArgumentException`: 1~30 사이가 아닌 값이 포함된 경우 + - [ ] `IllegalArgumentException`: 중복된 숫자가 포함된 경우 - [ ] 보너스 번호를 입력받는다. - [ ] `IllegalArgumentException`: 정수가 아닌 경우 - [ ] `IllegalArgumentException`: 1~30 사이가 아닌 경우 diff --git a/src/main/java/planetlotto/Application.java b/src/main/java/planetlotto/Application.java index 27d0a8f96..9b029353c 100644 --- a/src/main/java/planetlotto/Application.java +++ b/src/main/java/planetlotto/Application.java @@ -3,5 +3,7 @@ public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + Controller controller = new Controller(); + controller.run(); } } diff --git a/src/main/java/planetlotto/Controller.java b/src/main/java/planetlotto/Controller.java new file mode 100644 index 000000000..07aadff6e --- /dev/null +++ b/src/main/java/planetlotto/Controller.java @@ -0,0 +1,33 @@ +package planetlotto; + +import planetlotto.model.Lottos; +import planetlotto.view.InputView; +import planetlotto.view.OutputView; + +public class Controller { + + private final static String ERROR_PREFIX = "[ERROR] "; + + public Controller() { + } + + public void run() { + int purchaseAmount = inputPurchaseAmount(); + Lottos lottos = new Lottos(purchaseAmount); + + OutputView.printPurchasedLottos(lottos.getElements()); + } + + private int inputPurchaseAmount() { + int purchaseAmount; + + try { + purchaseAmount = InputView.askAmount(); + } catch (IllegalArgumentException e) { + System.out.println(ERROR_PREFIX + e.getMessage()); + purchaseAmount = inputPurchaseAmount(); + } + + return purchaseAmount; + } +} \ No newline at end of file diff --git a/src/main/java/planetlotto/model/Lotto.java b/src/main/java/planetlotto/model/Lotto.java new file mode 100644 index 000000000..5bfc894d0 --- /dev/null +++ b/src/main/java/planetlotto/model/Lotto.java @@ -0,0 +1,36 @@ +package planetlotto.model; + +import java.util.HashSet; +import java.util.List; + +public class Lotto { + private final List numbers; + + public Lotto(List numbers) { + validate(numbers); + this.numbers = numbers.stream().sorted().toList(); + } + + private void validate(List numbers) { + if (numbers.size() != 5) { + throw new IllegalArgumentException("로또 번호는 5개여야 합니다."); + } + + numbers.forEach(Lotto::validateNumberRange); + + if (new HashSet<>(numbers).size() < numbers.size()) { + throw new IllegalArgumentException("중복된 번호가 포함됐습니다."); + } + } + + public static void validateNumberRange(int number) { + if (number < 1 || number > 30) { + throw new IllegalArgumentException("보너스 번호는 1에서 30 사이여야 합니다."); + } + } + + public List getNumbers() { + return numbers; + } +} + diff --git a/src/main/java/planetlotto/model/Lottos.java b/src/main/java/planetlotto/model/Lottos.java new file mode 100644 index 000000000..1f3fb0d69 --- /dev/null +++ b/src/main/java/planetlotto/model/Lottos.java @@ -0,0 +1,53 @@ +package planetlotto.model; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.List; + +public class Lottos { + + private static final int PRICE = 500; + + private final int purchaseAmount; + private final List elements; + + public Lottos(int purchaseAmount) { + validate(purchaseAmount); + + this.purchaseAmount = purchaseAmount; + this.elements = createElements(); + } + + private void validate(int purchaseAmount) { + if (purchaseAmount < PRICE || purchaseAmount % PRICE != 0) { + throw new IllegalArgumentException(PRICE + "의 배수인 자연수가 아닙니다: " + purchaseAmount); + } + } + + private List createElements() { + List result = new ArrayList<>(); + int count = purchaseAmount / PRICE; + + for (int i = 0; i < count; i++) { + List numbers = + Randoms.pickUniqueNumbersInRange(1, 30, 5); + result.add(new Lotto(numbers)); + } + + return result; + } + + public int getLottoCount() { + return elements.size(); + } + + public int getPurchaseAmount() { + return purchaseAmount; + } + + public List> getElements() { + return elements.stream() + .map(Lotto::getNumbers) + .toList(); + } +} diff --git a/src/main/java/planetlotto/view/OutputView.java b/src/main/java/planetlotto/view/OutputView.java index 0452898da..a6667daaa 100644 --- a/src/main/java/planetlotto/view/OutputView.java +++ b/src/main/java/planetlotto/view/OutputView.java @@ -7,6 +7,9 @@ import static java.lang.System.lineSeparator; public class OutputView { + + private static final String NEW_LINE = "\n"; + public static void printPurchasedLottos(final List> lottos) { final String header = String.format("%d개를 구매했습니다.", lottos.size()); final String output = lottos.stream() From b3eac4139abb97c155185180fc0bb475bc5edcad Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 13:55:01 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EC=B6=94?= =?UTF-8?q?=EC=B2=A8=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 --- README.md | 20 +++---- src/main/java/planetlotto/Controller.java | 39 ++++++++++++- .../java/planetlotto/model/BonusNumber.java | 21 +++++++ src/main/java/planetlotto/model/Lotto.java | 16 +++++ src/main/java/planetlotto/model/Lottos.java | 10 +++- src/main/java/planetlotto/model/Rank.java | 48 +++++++++++++++ .../java/planetlotto/model/WinningDetail.java | 58 +++++++++++++++++++ 7 files changed, 200 insertions(+), 12 deletions(-) create mode 100644 src/main/java/planetlotto/model/BonusNumber.java create mode 100644 src/main/java/planetlotto/model/Rank.java create mode 100644 src/main/java/planetlotto/model/WinningDetail.java diff --git a/README.md b/README.md index 3e38eab58..1a78ca9e1 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ ## 로또 추첨 -- [ ] 당첨 번호 입력 프롬프트를 출력한다. +- [x] 당첨 번호 입력 프롬프트를 출력한다. - 메시지: "당첨 번호를 입력해 주세요." -- [ ] 당첨 번호를 쉼표를 기준으로 5개 입력받는다. - - [ ] `IllegalArgumentException`: 정수가 아닌게 포함된 경우 - - [ ] `IllegalArgumentException`: 1~30 사이가 아닌 값이 포함된 경우 - - [ ] `IllegalArgumentException`: 중복된 숫자가 포함된 경우 -- [ ] 보너스 번호를 입력받는다. - - [ ] `IllegalArgumentException`: 정수가 아닌 경우 - - [ ] `IllegalArgumentException`: 1~30 사이가 아닌 경우 -- [ ] 당첨을 진행한다. - - [ ] 각 로또에 대해, 당첨번호 + 보너스 번호와 비교하여 등수를 계산한다. +- [x] 당첨 번호를 쉼표를 기준으로 5개 입력받는다. + - [x] `IllegalArgumentException`: 정수가 아닌게 포함된 경우 + - [x] `IllegalArgumentException`: 1~30 사이가 아닌 값이 포함된 경우 + - [x] `IllegalArgumentException`: 중복된 숫자가 포함된 경우 +- [x] 보너스 번호를 입력받는다. + - [x] `IllegalArgumentException`: 정수가 아닌 경우 + - [x] `IllegalArgumentException`: 1~30 사이가 아닌 경우 +- [x] 당첨을 진행한다. + - [x] 각 로또에 대해, 당첨번호 + 보너스 번호와 비교하여 등수를 계산한다. - 1등: 5개 번호 일치 / 100,000,000원 - 2등: 4개 번호 + 보너스 번호 일치 / 10,000,000원 - 3등: 4개 번호 일치 / 1,500,000원 diff --git a/src/main/java/planetlotto/Controller.java b/src/main/java/planetlotto/Controller.java index 07aadff6e..f5136f67a 100644 --- a/src/main/java/planetlotto/Controller.java +++ b/src/main/java/planetlotto/Controller.java @@ -1,6 +1,10 @@ package planetlotto; +import java.util.List; +import planetlotto.model.BonusNumber; +import planetlotto.model.Lotto; import planetlotto.model.Lottos; +import planetlotto.model.WinningDetail; import planetlotto.view.InputView; import planetlotto.view.OutputView; @@ -15,7 +19,12 @@ public void run() { int purchaseAmount = inputPurchaseAmount(); Lottos lottos = new Lottos(purchaseAmount); - OutputView.printPurchasedLottos(lottos.getElements()); + OutputView.printPurchasedLottos(lottos.getElementsForOutput()); + + Lotto winningLotto = inputWinningLotto(); + BonusNumber bonusNumber = inputBonusNumber(); + + WinningDetail winningDetail = lottos.computeWinningDetail(winningLotto, bonusNumber); } private int inputPurchaseAmount() { @@ -30,4 +39,32 @@ private int inputPurchaseAmount() { return purchaseAmount; } + + private Lotto inputWinningLotto() { + Lotto winningLotto; + + try { + List winningNumbers = InputView.askWinningLotto(); + winningLotto = new Lotto(winningNumbers); + } catch (IllegalArgumentException e) { + System.out.println(ERROR_PREFIX + e.getMessage()); + winningLotto = inputWinningLotto(); + } + + return winningLotto; + } + + private BonusNumber inputBonusNumber() { + BonusNumber bonusNumber; + + try { + int bonusNumberInput = InputView.askBonusNumber(); + bonusNumber = new BonusNumber(bonusNumberInput); + } catch (IllegalArgumentException e) { + System.out.println(ERROR_PREFIX + e.getMessage()); + bonusNumber = inputBonusNumber(); + } + + return bonusNumber; + } } \ No newline at end of file diff --git a/src/main/java/planetlotto/model/BonusNumber.java b/src/main/java/planetlotto/model/BonusNumber.java new file mode 100644 index 000000000..61b353283 --- /dev/null +++ b/src/main/java/planetlotto/model/BonusNumber.java @@ -0,0 +1,21 @@ +package planetlotto.model; + +public class BonusNumber { + + private final int value; + + public BonusNumber(int value) { + validate(value); + this.value = value; + } + + private void validate(int value) { + if (value < 1 || value > 30) { + throw new IllegalArgumentException("보너스 번호는 1에서 30 사이여야 합니다: " + value); + } + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/planetlotto/model/Lotto.java b/src/main/java/planetlotto/model/Lotto.java index 5bfc894d0..57eefe998 100644 --- a/src/main/java/planetlotto/model/Lotto.java +++ b/src/main/java/planetlotto/model/Lotto.java @@ -2,6 +2,7 @@ import java.util.HashSet; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; public class Lotto { private final List numbers; @@ -32,5 +33,20 @@ public static void validateNumberRange(int number) { public List getNumbers() { return numbers; } + + public int computeMatchingCount(Lotto winningLotto) { + List winningNumbers = winningLotto.getNumbers(); + AtomicInteger count = new AtomicInteger(); + + numbers.stream() + .filter(winningNumbers::contains) + .forEach(other -> count.getAndIncrement()); + + return count.get(); + } + + public boolean isMatchedWithBonusNumber(BonusNumber bonusNumber) { + return numbers.contains(bonusNumber.getValue()); + } } diff --git a/src/main/java/planetlotto/model/Lottos.java b/src/main/java/planetlotto/model/Lottos.java index 1f3fb0d69..248e91ced 100644 --- a/src/main/java/planetlotto/model/Lottos.java +++ b/src/main/java/planetlotto/model/Lottos.java @@ -45,9 +45,17 @@ public int getPurchaseAmount() { return purchaseAmount; } - public List> getElements() { + public List getElements() { + return elements; + } + + public List> getElementsForOutput() { return elements.stream() .map(Lotto::getNumbers) .toList(); } + + public WinningDetail computeWinningDetail(Lotto winningLotto, BonusNumber bonusNumber) { + return new WinningDetail(winningLotto, bonusNumber, this); + } } diff --git a/src/main/java/planetlotto/model/Rank.java b/src/main/java/planetlotto/model/Rank.java new file mode 100644 index 000000000..97aea304d --- /dev/null +++ b/src/main/java/planetlotto/model/Rank.java @@ -0,0 +1,48 @@ +package planetlotto.model; + +public enum Rank { + FIRST(5, false, 100000000), + SECOND(4, true, 10000000), + THIRD(4, false, 1500000), + FIRTH(3, false, 500000), + FIFTH(2, false, 5000), + NONE(0, false, 0); + + private final int matchingCount; + private final boolean isMatchedWithBonusNumber; + private final int prize; + + Rank(int matchingCount, boolean isMatchedWithBonusNumber, int prize) { + this.matchingCount = matchingCount; + this.isMatchedWithBonusNumber = isMatchedWithBonusNumber; + this.prize = prize; + } + + public int getMatchingCount() { + return matchingCount; + } + + public boolean isMatchedWithBonusNumber() { + return isMatchedWithBonusNumber; + } + + public int getPrize() { + return prize; + } + + public static Rank of(int matchingCount, boolean isMatchedWithBonusNumber) { + if (matchingCount == 5) { + return FIRST; + } else if (matchingCount == 4 && isMatchedWithBonusNumber) { + return SECOND; + } else if (matchingCount == 4) { + return THIRD; + } else if (matchingCount == 3) { + return FIRTH; + } else if (matchingCount == 2) { + return FIFTH; + } + + return NONE; + } +} diff --git a/src/main/java/planetlotto/model/WinningDetail.java b/src/main/java/planetlotto/model/WinningDetail.java new file mode 100644 index 000000000..1414ef9c8 --- /dev/null +++ b/src/main/java/planetlotto/model/WinningDetail.java @@ -0,0 +1,58 @@ +package planetlotto.model; + +import static planetlotto.model.Rank.FIFTH; +import static planetlotto.model.Rank.FIRST; +import static planetlotto.model.Rank.FIRTH; +import static planetlotto.model.Rank.NONE; +import static planetlotto.model.Rank.SECOND; +import static planetlotto.model.Rank.THIRD; + +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +public class WinningDetail { + + private final Map rankCounts = new TreeMap<>(Collections.reverseOrder()); + private final int purchaseAmount; + + public WinningDetail(Lotto winning, BonusNumber bonusNumber, Lottos lottos) { + fillCountsWithZero(); + + lottos.getElements().forEach(lotto -> { + Rank rank = Rank.of( + lotto.computeMatchingCount(winning), + lotto.isMatchedWithBonusNumber(bonusNumber) + ); + + rankCounts.put(rank, rankCounts.get(rank) + 1); + }); + + this.purchaseAmount = lottos.getPurchaseAmount(); + } + + private void fillCountsWithZero() { + rankCounts.put(FIRST, 0); + rankCounts.put(SECOND, 0); + rankCounts.put(THIRD, 0); + rankCounts.put(FIRTH, 0); + rankCounts.put(FIFTH, 0); + rankCounts.put(NONE, 0); + } + + public Map getRankCounts() { + return rankCounts; + } + + public void calculateProfitRate() { + Set> entries = rankCounts.entrySet(); + int sum = 0; + + for (Entry entry : entries) { + sum += (entry.getKey().getPrize() * entry.getValue()); + } + } +} + From 9187a4812be657d9c4eacdbb4fe7e301851bf006 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 14:02:57 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=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 --- README.md | 6 ++--- src/main/java/planetlotto/Controller.java | 2 ++ src/main/java/planetlotto/model/Rank.java | 22 ++++++++++++------- .../java/planetlotto/model/WinningDetail.java | 20 +++++++---------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 1a78ca9e1..6fa25f903 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,7 @@ ## 당첨 통계 -- [ ] 각 등수 별로, 해당되는 개수를 계산한다. -- [ ] 1등부터 차례대로, 등수 별 해당되는 개수를 출력한다. +- [x] 각 등수 별로, 해당되는 개수를 계산한다. +- [x] 1등부터 차례대로, 등수 별 해당되는 개수를 출력한다. - 메시지: 등수 + "개 일치 (" + String.format("%,d", 상금) + "원) - " + 해당_개수 + "개" -- [ ] 총 수익율을 출력한다. - - 메시지: "총 수익률은 " + 소숫점_1_자리_수익률 + "%입니다." diff --git a/src/main/java/planetlotto/Controller.java b/src/main/java/planetlotto/Controller.java index f5136f67a..493110bc9 100644 --- a/src/main/java/planetlotto/Controller.java +++ b/src/main/java/planetlotto/Controller.java @@ -25,6 +25,8 @@ public void run() { BonusNumber bonusNumber = inputBonusNumber(); WinningDetail winningDetail = lottos.computeWinningDetail(winningLotto, bonusNumber); + + OutputView.printResult(winningDetail.getRankCountsForOutput()); } private int inputPurchaseAmount() { diff --git a/src/main/java/planetlotto/model/Rank.java b/src/main/java/planetlotto/model/Rank.java index 97aea304d..4582c0122 100644 --- a/src/main/java/planetlotto/model/Rank.java +++ b/src/main/java/planetlotto/model/Rank.java @@ -1,23 +1,29 @@ package planetlotto.model; public enum Rank { - FIRST(5, false, 100000000), - SECOND(4, true, 10000000), - THIRD(4, false, 1500000), - FIRTH(3, false, 500000), - FIFTH(2, false, 5000), - NONE(0, false, 0); - + FIRST(1,5, false, 100000000), + SECOND(2,4, true, 10000000), + THIRD(3,4, false, 1500000), + FIRTH(4, 3, false, 500000), + FIFTH(5, 2, false, 5000), + NONE(0, 0, false, 0); + + private final int order; private final int matchingCount; private final boolean isMatchedWithBonusNumber; private final int prize; - Rank(int matchingCount, boolean isMatchedWithBonusNumber, int prize) { + Rank(int order, int matchingCount, boolean isMatchedWithBonusNumber, int prize) { + this.order = order; this.matchingCount = matchingCount; this.isMatchedWithBonusNumber = isMatchedWithBonusNumber; this.prize = prize; } + public int getOrder() { + return order; + } + public int getMatchingCount() { return matchingCount; } diff --git a/src/main/java/planetlotto/model/WinningDetail.java b/src/main/java/planetlotto/model/WinningDetail.java index 1414ef9c8..6518cc02d 100644 --- a/src/main/java/planetlotto/model/WinningDetail.java +++ b/src/main/java/planetlotto/model/WinningDetail.java @@ -8,6 +8,7 @@ import static planetlotto.model.Rank.THIRD; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -19,29 +20,18 @@ public class WinningDetail { private final int purchaseAmount; public WinningDetail(Lotto winning, BonusNumber bonusNumber, Lottos lottos) { - fillCountsWithZero(); - lottos.getElements().forEach(lotto -> { Rank rank = Rank.of( lotto.computeMatchingCount(winning), lotto.isMatchedWithBonusNumber(bonusNumber) ); - rankCounts.put(rank, rankCounts.get(rank) + 1); + rankCounts.put(rank, rankCounts.getOrDefault(rank, 0) + 1); }); this.purchaseAmount = lottos.getPurchaseAmount(); } - private void fillCountsWithZero() { - rankCounts.put(FIRST, 0); - rankCounts.put(SECOND, 0); - rankCounts.put(THIRD, 0); - rankCounts.put(FIRTH, 0); - rankCounts.put(FIFTH, 0); - rankCounts.put(NONE, 0); - } - public Map getRankCounts() { return rankCounts; } @@ -54,5 +44,11 @@ public void calculateProfitRate() { sum += (entry.getKey().getPrize() * entry.getValue()); } } + + public Map getRankCountsForOutput() { + Map rankCountsForOutput = new TreeMap<>(Collections.reverseOrder()); + rankCounts.forEach((rank, count) -> rankCountsForOutput.put(rank.getOrder(),count)); + return rankCountsForOutput; + } } From 10bf5dc4f29d16029fce8ffecc045a7d07587455 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 14:29:38 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EA=B0=80=EB=8F=85=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/planetlotto/Application.java | 4 +- ...roller.java => PlanetLottoController.java} | 31 +++++----- src/main/java/planetlotto/model/Lottos.java | 61 ------------------- .../java/planetlotto/model/WinningDetail.java | 54 ---------------- .../planetlotto/model/{ => lotto}/Lotto.java | 15 ++--- .../java/planetlotto/model/lotto/Lottos.java | 59 ++++++++++++++++++ .../{ => winningDetail}/BonusNumber.java | 6 +- .../model/{ => winningDetail}/Rank.java | 10 +-- .../model/winningDetail/WinningDetail.java | 46 ++++++++++++++ 9 files changed, 134 insertions(+), 152 deletions(-) rename src/main/java/planetlotto/{Controller.java => PlanetLottoController.java} (69%) delete mode 100644 src/main/java/planetlotto/model/Lottos.java delete mode 100644 src/main/java/planetlotto/model/WinningDetail.java rename src/main/java/planetlotto/model/{ => lotto}/Lotto.java (81%) create mode 100644 src/main/java/planetlotto/model/lotto/Lottos.java rename src/main/java/planetlotto/model/{ => winningDetail}/BonusNumber.java (75%) rename src/main/java/planetlotto/model/{ => winningDetail}/Rank.java (86%) create mode 100644 src/main/java/planetlotto/model/winningDetail/WinningDetail.java diff --git a/src/main/java/planetlotto/Application.java b/src/main/java/planetlotto/Application.java index 9b029353c..9386782c6 100644 --- a/src/main/java/planetlotto/Application.java +++ b/src/main/java/planetlotto/Application.java @@ -3,7 +3,7 @@ public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 - Controller controller = new Controller(); - controller.run(); + PlanetLottoController planetLottoController = new PlanetLottoController(); + planetLottoController.run(); } } diff --git a/src/main/java/planetlotto/Controller.java b/src/main/java/planetlotto/PlanetLottoController.java similarity index 69% rename from src/main/java/planetlotto/Controller.java rename to src/main/java/planetlotto/PlanetLottoController.java index 493110bc9..4cd986da1 100644 --- a/src/main/java/planetlotto/Controller.java +++ b/src/main/java/planetlotto/PlanetLottoController.java @@ -1,45 +1,44 @@ package planetlotto; import java.util.List; -import planetlotto.model.BonusNumber; -import planetlotto.model.Lotto; -import planetlotto.model.Lottos; -import planetlotto.model.WinningDetail; +import planetlotto.model.lotto.Lottos; +import planetlotto.model.winningDetail.BonusNumber; +import planetlotto.model.lotto.Lotto; +import planetlotto.model.winningDetail.WinningDetail; import planetlotto.view.InputView; import planetlotto.view.OutputView; -public class Controller { +public class PlanetLottoController { private final static String ERROR_PREFIX = "[ERROR] "; - public Controller() { + public PlanetLottoController() { } public void run() { - int purchaseAmount = inputPurchaseAmount(); - Lottos lottos = new Lottos(purchaseAmount); - + Lottos lottos = purchaseLottos(); OutputView.printPurchasedLottos(lottos.getElementsForOutput()); Lotto winningLotto = inputWinningLotto(); BonusNumber bonusNumber = inputBonusNumber(); - WinningDetail winningDetail = lottos.computeWinningDetail(winningLotto, bonusNumber); + WinningDetail winningDetail = lottos.computeWinningDetailWith(winningLotto, bonusNumber); - OutputView.printResult(winningDetail.getRankCountsForOutput()); + OutputView.printResult(winningDetail.getCountsByRankForOutput()); } - private int inputPurchaseAmount() { - int purchaseAmount; + private Lottos purchaseLottos() { + Lottos lottos; try { - purchaseAmount = InputView.askAmount(); + int purchaseAmount = InputView.askAmount(); + lottos = new Lottos(purchaseAmount); } catch (IllegalArgumentException e) { System.out.println(ERROR_PREFIX + e.getMessage()); - purchaseAmount = inputPurchaseAmount(); + lottos = purchaseLottos(); } - return purchaseAmount; + return lottos; } private Lotto inputWinningLotto() { diff --git a/src/main/java/planetlotto/model/Lottos.java b/src/main/java/planetlotto/model/Lottos.java deleted file mode 100644 index 248e91ced..000000000 --- a/src/main/java/planetlotto/model/Lottos.java +++ /dev/null @@ -1,61 +0,0 @@ -package planetlotto.model; - -import camp.nextstep.edu.missionutils.Randoms; -import java.util.ArrayList; -import java.util.List; - -public class Lottos { - - private static final int PRICE = 500; - - private final int purchaseAmount; - private final List elements; - - public Lottos(int purchaseAmount) { - validate(purchaseAmount); - - this.purchaseAmount = purchaseAmount; - this.elements = createElements(); - } - - private void validate(int purchaseAmount) { - if (purchaseAmount < PRICE || purchaseAmount % PRICE != 0) { - throw new IllegalArgumentException(PRICE + "의 배수인 자연수가 아닙니다: " + purchaseAmount); - } - } - - private List createElements() { - List result = new ArrayList<>(); - int count = purchaseAmount / PRICE; - - for (int i = 0; i < count; i++) { - List numbers = - Randoms.pickUniqueNumbersInRange(1, 30, 5); - result.add(new Lotto(numbers)); - } - - return result; - } - - public int getLottoCount() { - return elements.size(); - } - - public int getPurchaseAmount() { - return purchaseAmount; - } - - public List getElements() { - return elements; - } - - public List> getElementsForOutput() { - return elements.stream() - .map(Lotto::getNumbers) - .toList(); - } - - public WinningDetail computeWinningDetail(Lotto winningLotto, BonusNumber bonusNumber) { - return new WinningDetail(winningLotto, bonusNumber, this); - } -} diff --git a/src/main/java/planetlotto/model/WinningDetail.java b/src/main/java/planetlotto/model/WinningDetail.java deleted file mode 100644 index 6518cc02d..000000000 --- a/src/main/java/planetlotto/model/WinningDetail.java +++ /dev/null @@ -1,54 +0,0 @@ -package planetlotto.model; - -import static planetlotto.model.Rank.FIFTH; -import static planetlotto.model.Rank.FIRST; -import static planetlotto.model.Rank.FIRTH; -import static planetlotto.model.Rank.NONE; -import static planetlotto.model.Rank.SECOND; -import static planetlotto.model.Rank.THIRD; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeMap; - -public class WinningDetail { - - private final Map rankCounts = new TreeMap<>(Collections.reverseOrder()); - private final int purchaseAmount; - - public WinningDetail(Lotto winning, BonusNumber bonusNumber, Lottos lottos) { - lottos.getElements().forEach(lotto -> { - Rank rank = Rank.of( - lotto.computeMatchingCount(winning), - lotto.isMatchedWithBonusNumber(bonusNumber) - ); - - rankCounts.put(rank, rankCounts.getOrDefault(rank, 0) + 1); - }); - - this.purchaseAmount = lottos.getPurchaseAmount(); - } - - public Map getRankCounts() { - return rankCounts; - } - - public void calculateProfitRate() { - Set> entries = rankCounts.entrySet(); - int sum = 0; - - for (Entry entry : entries) { - sum += (entry.getKey().getPrize() * entry.getValue()); - } - } - - public Map getRankCountsForOutput() { - Map rankCountsForOutput = new TreeMap<>(Collections.reverseOrder()); - rankCounts.forEach((rank, count) -> rankCountsForOutput.put(rank.getOrder(),count)); - return rankCountsForOutput; - } -} - diff --git a/src/main/java/planetlotto/model/Lotto.java b/src/main/java/planetlotto/model/lotto/Lotto.java similarity index 81% rename from src/main/java/planetlotto/model/Lotto.java rename to src/main/java/planetlotto/model/lotto/Lotto.java index 57eefe998..46894a621 100644 --- a/src/main/java/planetlotto/model/Lotto.java +++ b/src/main/java/planetlotto/model/lotto/Lotto.java @@ -1,27 +1,28 @@ -package planetlotto.model; +package planetlotto.model.lotto; import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import planetlotto.model.winningDetail.BonusNumber; public class Lotto { private final List numbers; public Lotto(List numbers) { - validate(numbers); + validateNumbers(numbers); this.numbers = numbers.stream().sorted().toList(); } - private void validate(List numbers) { + private void validateNumbers(List numbers) { if (numbers.size() != 5) { throw new IllegalArgumentException("로또 번호는 5개여야 합니다."); } - numbers.forEach(Lotto::validateNumberRange); - if (new HashSet<>(numbers).size() < numbers.size()) { throw new IllegalArgumentException("중복된 번호가 포함됐습니다."); } + + numbers.forEach(Lotto::validateNumberRange); } public static void validateNumberRange(int number) { @@ -34,7 +35,7 @@ public List getNumbers() { return numbers; } - public int computeMatchingCount(Lotto winningLotto) { + public int countMatchingNumbersWith(Lotto winningLotto) { List winningNumbers = winningLotto.getNumbers(); AtomicInteger count = new AtomicInteger(); @@ -45,7 +46,7 @@ public int computeMatchingCount(Lotto winningLotto) { return count.get(); } - public boolean isMatchedWithBonusNumber(BonusNumber bonusNumber) { + public boolean isMatchedWith(BonusNumber bonusNumber) { return numbers.contains(bonusNumber.getValue()); } } diff --git a/src/main/java/planetlotto/model/lotto/Lottos.java b/src/main/java/planetlotto/model/lotto/Lottos.java new file mode 100644 index 000000000..f4fb277dd --- /dev/null +++ b/src/main/java/planetlotto/model/lotto/Lottos.java @@ -0,0 +1,59 @@ +package planetlotto.model.lotto; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.List; +import planetlotto.model.winningDetail.BonusNumber; +import planetlotto.model.winningDetail.WinningDetail; + +public class Lottos { + + private static final int LOTTO_PRICE = 500; + + private final int purchaseAmount; + private final List elements; + + public Lottos(int purchaseAmount) { + validatePurchaseAmount(purchaseAmount); + + this.purchaseAmount = purchaseAmount; + this.elements = createElements(); + } + + private void validatePurchaseAmount(int purchaseAmount) { + if (purchaseAmount < LOTTO_PRICE || purchaseAmount % LOTTO_PRICE != 0) { + throw new IllegalArgumentException("구입금액은 " + LOTTO_PRICE + "원 단위여야 합니다: " + purchaseAmount); + } + } + + private List createElements() { + List elements = new ArrayList<>(); + int count = purchaseAmount / LOTTO_PRICE; + + for (int i = 0; i < count; i++) { + elements.add(new Lotto( + Randoms.pickUniqueNumbersInRange(1, 30, 5)) + ); + } + + return elements; + } + + public int getPurchaseAmount() { + return purchaseAmount; + } + + public List getElements() { + return elements; + } + + public List> getElementsForOutput() { + return elements.stream() + .map(Lotto::getNumbers) + .toList(); + } + + public WinningDetail computeWinningDetailWith(Lotto winningLotto, BonusNumber bonusNumber) { + return new WinningDetail(this, winningLotto, bonusNumber); + } +} diff --git a/src/main/java/planetlotto/model/BonusNumber.java b/src/main/java/planetlotto/model/winningDetail/BonusNumber.java similarity index 75% rename from src/main/java/planetlotto/model/BonusNumber.java rename to src/main/java/planetlotto/model/winningDetail/BonusNumber.java index 61b353283..a58250085 100644 --- a/src/main/java/planetlotto/model/BonusNumber.java +++ b/src/main/java/planetlotto/model/winningDetail/BonusNumber.java @@ -1,15 +1,15 @@ -package planetlotto.model; +package planetlotto.model.winningDetail; public class BonusNumber { private final int value; public BonusNumber(int value) { - validate(value); + validateValue(value); this.value = value; } - private void validate(int value) { + private void validateValue(int value) { if (value < 1 || value > 30) { throw new IllegalArgumentException("보너스 번호는 1에서 30 사이여야 합니다: " + value); } diff --git a/src/main/java/planetlotto/model/Rank.java b/src/main/java/planetlotto/model/winningDetail/Rank.java similarity index 86% rename from src/main/java/planetlotto/model/Rank.java rename to src/main/java/planetlotto/model/winningDetail/Rank.java index 4582c0122..87c237dcc 100644 --- a/src/main/java/planetlotto/model/Rank.java +++ b/src/main/java/planetlotto/model/winningDetail/Rank.java @@ -1,4 +1,4 @@ -package planetlotto.model; +package planetlotto.model.winningDetail; public enum Rank { FIRST(1,5, false, 100000000), @@ -24,14 +24,6 @@ public int getOrder() { return order; } - public int getMatchingCount() { - return matchingCount; - } - - public boolean isMatchedWithBonusNumber() { - return isMatchedWithBonusNumber; - } - public int getPrize() { return prize; } diff --git a/src/main/java/planetlotto/model/winningDetail/WinningDetail.java b/src/main/java/planetlotto/model/winningDetail/WinningDetail.java new file mode 100644 index 000000000..9b93c8d3b --- /dev/null +++ b/src/main/java/planetlotto/model/winningDetail/WinningDetail.java @@ -0,0 +1,46 @@ +package planetlotto.model.winningDetail; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; +import planetlotto.model.lotto.Lotto; +import planetlotto.model.lotto.Lottos; + +public class WinningDetail { + + private final Lottos purchasedLottos; + private final Lotto winningLotto; + private final BonusNumber bonusNumber; + private final Map countsByRank; + + public WinningDetail(Lottos purchasedLottos, + Lotto winningLotto, + BonusNumber bonusNumber) { + this.purchasedLottos = purchasedLottos; + this.winningLotto = winningLotto; + this.bonusNumber = bonusNumber; + this.countsByRank = createCountsByRank(); + } + + private Map createCountsByRank() { + Map countsByRank = new TreeMap<>(Collections.reverseOrder()); + + purchasedLottos.getElements().forEach(lotto -> { + Rank rank = Rank.of( + lotto.countMatchingNumbersWith(winningLotto), + lotto.isMatchedWith(bonusNumber) + ); + + countsByRank.put(rank, countsByRank.getOrDefault(rank, 0) + 1); + }); + + return countsByRank; + } + + public Map getCountsByRankForOutput() { + Map rankCountsForOutput = new TreeMap<>(Collections.reverseOrder()); + countsByRank.forEach((rank, count) -> rankCountsForOutput.put(rank.getOrder(),count)); + return rankCountsForOutput; + } +} + From 546f905bb5ad0e0788444184cc18c6d065101707 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 14:41:54 +0900 Subject: [PATCH 06/10] =?UTF-8?q?refactor:=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++++++-- src/main/java/planetlotto/Application.java | 2 ++ .../{ => controller}/PlanetLottoController.java | 2 +- src/main/java/planetlotto/model/LottoRules.java | 9 +++++++++ src/main/java/planetlotto/model/lotto/Lotto.java | 12 ++++++++---- .../java/planetlotto/model/lotto/Lottos.java | 16 +++++++--------- .../model/winningDetail/BonusNumber.java | 9 +++++++-- 7 files changed, 44 insertions(+), 18 deletions(-) rename src/main/java/planetlotto/{ => controller}/PlanetLottoController.java (98%) create mode 100644 src/main/java/planetlotto/model/LottoRules.java diff --git a/README.md b/README.md index 6fa25f903..09e23ec37 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # java-planetlotto-precourse -- 입력/출력 역할은 제공된 InputView, OutputView에서 수행하며 기존 메서드를 수정, 삭제할 수 없다. - ## 로또 구매 - [x] 구입금액 입력 프롬프트를 출력한다. @@ -42,3 +40,13 @@ - [x] 1등부터 차례대로, 등수 별 해당되는 개수를 출력한다. - 메시지: 등수 + "개 일치 (" + String.format("%,d", 상금) + "원) - " + 해당_개수 + "개" + +도전 방향 +리팩터링: 작동은 그대로 유지하면서 코드 품질을 높이는 방향 +기능 확장: 기본 기능 위에 새로운 기능을 추가하는 방향 +💡 어떤 도전을 선택할지 고민된다면 프리코스 1~3주 차와 오픈 미션을 돌아보세요. + +아쉬웠던 점은 무엇인가요? +다음에는 다르게 해보고 싶었던 것은 무엇인가요? +피드백을 받았지만 적용하지 못한 것은 무엇인가요? +정해진 정답은 없습니다. 본인의 프리코스 경험을 바탕으로 의미 있는 도전을 설계하세요. \ No newline at end of file diff --git a/src/main/java/planetlotto/Application.java b/src/main/java/planetlotto/Application.java index 9386782c6..92e3b96c4 100644 --- a/src/main/java/planetlotto/Application.java +++ b/src/main/java/planetlotto/Application.java @@ -1,5 +1,7 @@ package planetlotto; +import planetlotto.controller.PlanetLottoController; + public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 diff --git a/src/main/java/planetlotto/PlanetLottoController.java b/src/main/java/planetlotto/controller/PlanetLottoController.java similarity index 98% rename from src/main/java/planetlotto/PlanetLottoController.java rename to src/main/java/planetlotto/controller/PlanetLottoController.java index 4cd986da1..2b3f1c7a3 100644 --- a/src/main/java/planetlotto/PlanetLottoController.java +++ b/src/main/java/planetlotto/controller/PlanetLottoController.java @@ -1,4 +1,4 @@ -package planetlotto; +package planetlotto.controller; import java.util.List; import planetlotto.model.lotto.Lottos; diff --git a/src/main/java/planetlotto/model/LottoRules.java b/src/main/java/planetlotto/model/LottoRules.java new file mode 100644 index 000000000..7c2d8483a --- /dev/null +++ b/src/main/java/planetlotto/model/LottoRules.java @@ -0,0 +1,9 @@ +package planetlotto.model; + +public class LottoRules { + + public static final int LOTTO_PRICE = 500; + public static final int LOTTO_START_NUMBER = 1; + public static final int LOTTO_END_NUMBER = 30; + public static final int LOTTO_NUMBER_COUNT = 5; +} diff --git a/src/main/java/planetlotto/model/lotto/Lotto.java b/src/main/java/planetlotto/model/lotto/Lotto.java index 46894a621..151e50157 100644 --- a/src/main/java/planetlotto/model/lotto/Lotto.java +++ b/src/main/java/planetlotto/model/lotto/Lotto.java @@ -1,5 +1,9 @@ package planetlotto.model.lotto; +import static planetlotto.model.LottoRules.LOTTO_END_NUMBER; +import static planetlotto.model.LottoRules.LOTTO_NUMBER_COUNT; +import static planetlotto.model.LottoRules.LOTTO_START_NUMBER; + import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -14,8 +18,8 @@ public Lotto(List numbers) { } private void validateNumbers(List numbers) { - if (numbers.size() != 5) { - throw new IllegalArgumentException("로또 번호는 5개여야 합니다."); + if (numbers.size() != LOTTO_NUMBER_COUNT) { + throw new IllegalArgumentException("로또 번호는 " + LOTTO_NUMBER_COUNT + "개여야 합니다."); } if (new HashSet<>(numbers).size() < numbers.size()) { @@ -26,8 +30,8 @@ private void validateNumbers(List numbers) { } public static void validateNumberRange(int number) { - if (number < 1 || number > 30) { - throw new IllegalArgumentException("보너스 번호는 1에서 30 사이여야 합니다."); + if (number < LOTTO_START_NUMBER || number > LOTTO_END_NUMBER) { + throw new IllegalArgumentException("로또 번호는 " + LOTTO_START_NUMBER + "에서 " + LOTTO_END_NUMBER + " 사이여야 합니다."); } } diff --git a/src/main/java/planetlotto/model/lotto/Lottos.java b/src/main/java/planetlotto/model/lotto/Lottos.java index f4fb277dd..2a1c148f7 100644 --- a/src/main/java/planetlotto/model/lotto/Lottos.java +++ b/src/main/java/planetlotto/model/lotto/Lottos.java @@ -1,5 +1,10 @@ package planetlotto.model.lotto; +import static planetlotto.model.LottoRules.LOTTO_END_NUMBER; +import static planetlotto.model.LottoRules.LOTTO_NUMBER_COUNT; +import static planetlotto.model.LottoRules.LOTTO_PRICE; +import static planetlotto.model.LottoRules.LOTTO_START_NUMBER; + import camp.nextstep.edu.missionutils.Randoms; import java.util.ArrayList; import java.util.List; @@ -7,9 +12,6 @@ import planetlotto.model.winningDetail.WinningDetail; public class Lottos { - - private static final int LOTTO_PRICE = 500; - private final int purchaseAmount; private final List elements; @@ -21,7 +23,7 @@ public Lottos(int purchaseAmount) { } private void validatePurchaseAmount(int purchaseAmount) { - if (purchaseAmount < LOTTO_PRICE || purchaseAmount % LOTTO_PRICE != 0) { + if ((purchaseAmount < LOTTO_PRICE) || ((purchaseAmount % LOTTO_PRICE) != 0)) { throw new IllegalArgumentException("구입금액은 " + LOTTO_PRICE + "원 단위여야 합니다: " + purchaseAmount); } } @@ -32,17 +34,13 @@ private List createElements() { for (int i = 0; i < count; i++) { elements.add(new Lotto( - Randoms.pickUniqueNumbersInRange(1, 30, 5)) + Randoms.pickUniqueNumbersInRange(LOTTO_START_NUMBER, LOTTO_END_NUMBER, LOTTO_NUMBER_COUNT)) ); } return elements; } - public int getPurchaseAmount() { - return purchaseAmount; - } - public List getElements() { return elements; } diff --git a/src/main/java/planetlotto/model/winningDetail/BonusNumber.java b/src/main/java/planetlotto/model/winningDetail/BonusNumber.java index a58250085..59bdf78b7 100644 --- a/src/main/java/planetlotto/model/winningDetail/BonusNumber.java +++ b/src/main/java/planetlotto/model/winningDetail/BonusNumber.java @@ -1,5 +1,10 @@ package planetlotto.model.winningDetail; +import static planetlotto.model.LottoRules.LOTTO_END_NUMBER; +import static planetlotto.model.LottoRules.LOTTO_START_NUMBER; + +import planetlotto.model.LottoRules; + public class BonusNumber { private final int value; @@ -10,8 +15,8 @@ public BonusNumber(int value) { } private void validateValue(int value) { - if (value < 1 || value > 30) { - throw new IllegalArgumentException("보너스 번호는 1에서 30 사이여야 합니다: " + value); + if (value < LottoRules.LOTTO_START_NUMBER || value > LottoRules.LOTTO_END_NUMBER) { + throw new IllegalArgumentException("보너스 번호는 " + LOTTO_START_NUMBER + "에서 " + LOTTO_END_NUMBER + " 사이여야 합니다."); } } From 1b30991d66df4c4188d333c9f36adcbb1aa75686 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 14:47:00 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=EB=B3=B4=EB=84=88=EC=8A=A4=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EC=99=80=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=EC=9D=98=20=EC=9D=BC=EC=B9=98=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=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 --- README.md | 2 +- .../controller/PlanetLottoController.java | 10 +++++----- .../model/{winningDetail => lotto}/BonusNumber.java | 12 ++++++++---- src/main/java/planetlotto/model/lotto/Lotto.java | 1 - src/main/java/planetlotto/model/lotto/Lottos.java | 1 - .../model/winningDetail/WinningDetail.java | 1 + 6 files changed, 15 insertions(+), 12 deletions(-) rename src/main/java/planetlotto/model/{winningDetail => lotto}/BonusNumber.java (59%) diff --git a/README.md b/README.md index 09e23ec37..2f3e21d54 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ - [x] 보너스 번호를 입력받는다. - [x] `IllegalArgumentException`: 정수가 아닌 경우 - [x] `IllegalArgumentException`: 1~30 사이가 아닌 경우 + - [x] `IllegalArgumentException`: 당첨 번호에 같은 숫자가 포함된 경우 - [x] 당첨을 진행한다. - [x] 각 로또에 대해, 당첨번호 + 보너스 번호와 비교하여 등수를 계산한다. - 1등: 5개 번호 일치 / 100,000,000원 @@ -42,7 +43,6 @@ 도전 방향 -리팩터링: 작동은 그대로 유지하면서 코드 품질을 높이는 방향 기능 확장: 기본 기능 위에 새로운 기능을 추가하는 방향 💡 어떤 도전을 선택할지 고민된다면 프리코스 1~3주 차와 오픈 미션을 돌아보세요. diff --git a/src/main/java/planetlotto/controller/PlanetLottoController.java b/src/main/java/planetlotto/controller/PlanetLottoController.java index 2b3f1c7a3..e6760fe88 100644 --- a/src/main/java/planetlotto/controller/PlanetLottoController.java +++ b/src/main/java/planetlotto/controller/PlanetLottoController.java @@ -2,7 +2,7 @@ import java.util.List; import planetlotto.model.lotto.Lottos; -import planetlotto.model.winningDetail.BonusNumber; +import planetlotto.model.lotto.BonusNumber; import planetlotto.model.lotto.Lotto; import planetlotto.model.winningDetail.WinningDetail; import planetlotto.view.InputView; @@ -20,7 +20,7 @@ public void run() { OutputView.printPurchasedLottos(lottos.getElementsForOutput()); Lotto winningLotto = inputWinningLotto(); - BonusNumber bonusNumber = inputBonusNumber(); + BonusNumber bonusNumber = inputBonusNumber(winningLotto); WinningDetail winningDetail = lottos.computeWinningDetailWith(winningLotto, bonusNumber); @@ -55,15 +55,15 @@ private Lotto inputWinningLotto() { return winningLotto; } - private BonusNumber inputBonusNumber() { + private BonusNumber inputBonusNumber(Lotto winningLotto) { BonusNumber bonusNumber; try { int bonusNumberInput = InputView.askBonusNumber(); - bonusNumber = new BonusNumber(bonusNumberInput); + bonusNumber = new BonusNumber(bonusNumberInput, winningLotto); } catch (IllegalArgumentException e) { System.out.println(ERROR_PREFIX + e.getMessage()); - bonusNumber = inputBonusNumber(); + bonusNumber = inputBonusNumber(winningLotto); } return bonusNumber; diff --git a/src/main/java/planetlotto/model/winningDetail/BonusNumber.java b/src/main/java/planetlotto/model/lotto/BonusNumber.java similarity index 59% rename from src/main/java/planetlotto/model/winningDetail/BonusNumber.java rename to src/main/java/planetlotto/model/lotto/BonusNumber.java index 59bdf78b7..c8a5f8ff8 100644 --- a/src/main/java/planetlotto/model/winningDetail/BonusNumber.java +++ b/src/main/java/planetlotto/model/lotto/BonusNumber.java @@ -1,4 +1,4 @@ -package planetlotto.model.winningDetail; +package planetlotto.model.lotto; import static planetlotto.model.LottoRules.LOTTO_END_NUMBER; import static planetlotto.model.LottoRules.LOTTO_START_NUMBER; @@ -9,15 +9,19 @@ public class BonusNumber { private final int value; - public BonusNumber(int value) { - validateValue(value); + public BonusNumber(int value, Lotto winningLotto) { + validateValue(value, winningLotto); this.value = value; } - private void validateValue(int value) { + private void validateValue(int value, Lotto winningLotto) { if (value < LottoRules.LOTTO_START_NUMBER || value > LottoRules.LOTTO_END_NUMBER) { throw new IllegalArgumentException("보너스 번호는 " + LOTTO_START_NUMBER + "에서 " + LOTTO_END_NUMBER + " 사이여야 합니다."); } + + if (winningLotto.getNumbers().contains(value)) { + throw new IllegalArgumentException("보너스 번호는 당첨 번호의 모든 숫자와 달라야 합니다."); + } } public int getValue() { diff --git a/src/main/java/planetlotto/model/lotto/Lotto.java b/src/main/java/planetlotto/model/lotto/Lotto.java index 151e50157..cd19b8c47 100644 --- a/src/main/java/planetlotto/model/lotto/Lotto.java +++ b/src/main/java/planetlotto/model/lotto/Lotto.java @@ -7,7 +7,6 @@ import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import planetlotto.model.winningDetail.BonusNumber; public class Lotto { private final List numbers; diff --git a/src/main/java/planetlotto/model/lotto/Lottos.java b/src/main/java/planetlotto/model/lotto/Lottos.java index 2a1c148f7..0181c5bd2 100644 --- a/src/main/java/planetlotto/model/lotto/Lottos.java +++ b/src/main/java/planetlotto/model/lotto/Lottos.java @@ -8,7 +8,6 @@ import camp.nextstep.edu.missionutils.Randoms; import java.util.ArrayList; import java.util.List; -import planetlotto.model.winningDetail.BonusNumber; import planetlotto.model.winningDetail.WinningDetail; public class Lottos { diff --git a/src/main/java/planetlotto/model/winningDetail/WinningDetail.java b/src/main/java/planetlotto/model/winningDetail/WinningDetail.java index 9b93c8d3b..8d4a9bdde 100644 --- a/src/main/java/planetlotto/model/winningDetail/WinningDetail.java +++ b/src/main/java/planetlotto/model/winningDetail/WinningDetail.java @@ -3,6 +3,7 @@ import java.util.Collections; import java.util.Map; import java.util.TreeMap; +import planetlotto.model.lotto.BonusNumber; import planetlotto.model.lotto.Lotto; import planetlotto.model.lotto.Lottos; From 7eab430138410e0b5ad309d7707587cae5029539 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 15:24:19 +0900 Subject: [PATCH 08/10] =?UTF-8?q?docs:=20=EC=B6=94=EA=B0=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=EC=97=90=20=EB=8C=80=ED=95=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2f3e21d54..8464f5232 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # java-planetlotto-precourse -## 로또 구매 + +## 기본 요구 사항 + +### 로또 구매 - [x] 구입금액 입력 프롬프트를 출력한다. - 메시지: "구입금액을 입력해 주세요." @@ -14,7 +17,7 @@ - [x] 각 로또에 대해 오름차순으로 정렬해, 구매한 로또들의 번호를 출력한다. - 메시지 예시: "[8, 11, 13, 21, 22]" -## 로또 추첨 +### 로또 추첨 - [x] 당첨 번호 입력 프롬프트를 출력한다. - 메시지: "당첨 번호를 입력해 주세요." @@ -25,7 +28,7 @@ - [x] 보너스 번호를 입력받는다. - [x] `IllegalArgumentException`: 정수가 아닌 경우 - [x] `IllegalArgumentException`: 1~30 사이가 아닌 경우 - - [x] `IllegalArgumentException`: 당첨 번호에 같은 숫자가 포함된 경우 + - [x] `IllegalArgumentException`: 당첨 번호에 같은 숫자가 포함 경우 - [x] 당첨을 진행한다. - [x] 각 로또에 대해, 당첨번호 + 보너스 번호와 비교하여 등수를 계산한다. - 1등: 5개 번호 일치 / 100,000,000원 @@ -35,18 +38,36 @@ - 5등: 2개 번호 일치 + 보너스 번호 일치 / 5,000원 - 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. -## 당첨 통계 +### 당첨 통계 - [x] 각 등수 별로, 해당되는 개수를 계산한다. - [x] 1등부터 차례대로, 등수 별 해당되는 개수를 출력한다. - 메시지: 등수 + "개 일치 (" + String.format("%,d", 상금) + "원) - " + 해당_개수 + "개" +## 기능 확장 + +### 목표 +사용자 경험 개선 -도전 방향 -기능 확장: 기본 기능 위에 새로운 기능을 추가하는 방향 -💡 어떤 도전을 선택할지 고민된다면 프리코스 1~3주 차와 오픈 미션을 돌아보세요. +### 문제 상황 +- 전체 프로세스에 대한 설명이 부족하다. + - 어떤 식으로 어떻게 진행되는지에 대해, 처음에 설명하도록 한다. +- 전체 프로세스를 한 번 밖에 실행하지 못해서, 사용자 경험의 연속성이 떨어진다. + - 프로세스 실행 이후, 다시 프로세스를 진행할지를 사용자에게 뭍고, 응답을 바탕으로 진행한다. +- 당첨 통계에서 사용자가 이 프로그램에 원하는 정보가 충분하지 못하다. 예를 들어, 로또를 구매할 떄 사용자가 진정으로 원하는 것은 당첨에 대한 세부적인 정보보다는 타인이나 이전 대비 나의 수익률이 어떤가이다. 그러나 기존 기능은 이러한 욕구를 충족시키지 못한다. + - 구매한 로또에 대한 수익률을 출력한다. + - 이전 프로세스들에 대한 수익률과 이와 비교되는 나의 수익률을 출력한다. -아쉬웠던 점은 무엇인가요? -다음에는 다르게 해보고 싶었던 것은 무엇인가요? -피드백을 받았지만 적용하지 못한 것은 무엇인가요? -정해진 정답은 없습니다. 본인의 프리코스 경험을 바탕으로 의미 있는 도전을 설계하세요. \ No newline at end of file +### 로또 규칙 설명 +- [ ] 프로그램 시작 시, 프로그램에 대한 규칙을 설명한다. + - [ ] 전체적인 절차를 설명한다. + - [ ] 로또의 가격이나 당첨 기준과 같은 규칙을 설명한다. +- [ ] 전체 프로세스를 모두 수행한 후, 프로세스를 계속 진행할지 여부를 묻는다. + - [ ] 계속 진행 여부 입력 프롬프트를 출력한다. + - 메시지: "로또 프로그램이 종료되었습니다. 계속 해서 진행하시겠습니까? (Y/N)" + - [ ] 계속 진행 여부를 입력받는다. + - `IllegalArgumentException`: Y나 N이 입력되지 않은 경우 + - [ ] Y가 입력되면, 프로세스를 다시 진행한다. +- [ ] 이전 프로세스들에 대한 수익률 관련 정보를 제공한다. + - [ ] 구입 금액 입력 전에, 이전 수익률에 대한 정보를 제공한다. + - [ ] 당첨 통계에서, 이전 수익률 대비 나의 수익률이 몇 프로 높거나 낮은지 제공한다. \ No newline at end of file From 886c82b65ec91671dbf43163856f27ffedd33e90 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 15:38:27 +0900 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=86=8C=EA=B0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++--- .../controller/PlanetLottoController.java | 9 +++---- .../java/planetlotto/view/OutputView.java | 27 +++++++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8464f5232..cf948ff21 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ - 이전 프로세스들에 대한 수익률과 이와 비교되는 나의 수익률을 출력한다. ### 로또 규칙 설명 -- [ ] 프로그램 시작 시, 프로그램에 대한 규칙을 설명한다. - - [ ] 전체적인 절차를 설명한다. - - [ ] 로또의 가격이나 당첨 기준과 같은 규칙을 설명한다. +- [x] 프로그램 시작 시, 프로그램에 대한 소개를 한다. + - [x] 전체적인 절차를 설명한다. + - [x] 로또의 가격이나 당첨 기준과 같은 규칙을 설명한다. - [ ] 전체 프로세스를 모두 수행한 후, 프로세스를 계속 진행할지 여부를 묻는다. - [ ] 계속 진행 여부 입력 프롬프트를 출력한다. - 메시지: "로또 프로그램이 종료되었습니다. 계속 해서 진행하시겠습니까? (Y/N)" diff --git a/src/main/java/planetlotto/controller/PlanetLottoController.java b/src/main/java/planetlotto/controller/PlanetLottoController.java index e6760fe88..3eb4fdaa8 100644 --- a/src/main/java/planetlotto/controller/PlanetLottoController.java +++ b/src/main/java/planetlotto/controller/PlanetLottoController.java @@ -10,8 +10,6 @@ public class PlanetLottoController { - private final static String ERROR_PREFIX = "[ERROR] "; - public PlanetLottoController() { } @@ -31,10 +29,11 @@ private Lottos purchaseLottos() { Lottos lottos; try { + OutputView.printIntroduction(); int purchaseAmount = InputView.askAmount(); lottos = new Lottos(purchaseAmount); } catch (IllegalArgumentException e) { - System.out.println(ERROR_PREFIX + e.getMessage()); + OutputView.printErrorMessage(e.getMessage()); lottos = purchaseLottos(); } @@ -48,7 +47,7 @@ private Lotto inputWinningLotto() { List winningNumbers = InputView.askWinningLotto(); winningLotto = new Lotto(winningNumbers); } catch (IllegalArgumentException e) { - System.out.println(ERROR_PREFIX + e.getMessage()); + OutputView.printErrorMessage(e.getMessage()); winningLotto = inputWinningLotto(); } @@ -62,7 +61,7 @@ private BonusNumber inputBonusNumber(Lotto winningLotto) { int bonusNumberInput = InputView.askBonusNumber(); bonusNumber = new BonusNumber(bonusNumberInput, winningLotto); } catch (IllegalArgumentException e) { - System.out.println(ERROR_PREFIX + e.getMessage()); + OutputView.printErrorMessage(e.getMessage()); bonusNumber = inputBonusNumber(winningLotto); } diff --git a/src/main/java/planetlotto/view/OutputView.java b/src/main/java/planetlotto/view/OutputView.java index a6667daaa..cbba3d174 100644 --- a/src/main/java/planetlotto/view/OutputView.java +++ b/src/main/java/planetlotto/view/OutputView.java @@ -10,6 +10,33 @@ public class OutputView { private static final String NEW_LINE = "\n"; + public static void printIntroduction() { + System.out.println("행성 로또가 시작되었습니다!" + NEW_LINE); + printSteps(); + System.out.print(NEW_LINE); + printRules(); + System.out.println("=================================================================================="); + System.out.println("===================================================================================" + NEW_LINE); + } + + private static void printSteps() { + System.out.println("[절차]"); + System.out.println("1. 구입 금액 입력 (500원 단위)"); + System.out.println("2. 구입한 로또 수량 및 번호 출력"); + System.out.println("3. 당첨 번호 입력 (1에서 30 사이의 서로 다른 숫자 5개)"); + System.out.println("4. 보너스 번호 입력 (당첨 번호와 다른 1에서 30 사이의 숫자)"); + System.out.println("5. 당첨 통계 출력"); + System.out.println("6. 재진행 여부 입력 (Y 또는 N)"); + } + + public static void printRules() { + System.out.println("[규칙]"); + System.out.println("1. 로또의 가격은 500원 입니다."); + System.out.println("2. 로또는 1에서 30 사이의 서로 다른 숫자 5개를 순서 없이 가집니다."); + System.out.println("3. 당첨 번호는 1에서 30 사이의 서로 다른 숫자 5개로 구성되야 합니다. 이 때 숫자의 순서는 상관 없습니다."); + System.out.println("4. 보너스 번호는 당첨 번호에 포함되지 않은 1에서 30 사이의 숫자여야 합니다."); + } + public static void printPurchasedLottos(final List> lottos) { final String header = String.format("%d개를 구매했습니다.", lottos.size()); final String output = lottos.stream() From 6d0ca05b9f5ab6d5df1c66fe15700dd639fe2ea3 Mon Sep 17 00:00:00 2001 From: woohyeon Date: Sat, 10 Jan 2026 16:54:35 +0900 Subject: [PATCH 10/10] =?UTF-8?q?feat:=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 --- README.md | 16 +---- .../java/planetlotto/model/lotto/Lottos.java | 6 ++ .../model/winningDetail/WinningDetail.java | 4 ++ src/main/java/planetlotto/view/InputView.java | 1 - .../java/planetlotto/view/OutputView.java | 22 +++++++ .../model/lotto/BonusNumberTest.java | 26 ++++++++ .../planetlotto/model/lotto/LottoTest.java | 58 ++++++++++++++++ .../planetlotto/model/lotto/LottosTest.java | 45 +++++++++++++ .../model/winningDetail/RankTest.java | 46 +++++++++++++ .../winningDetail/WinningDetailTest.java | 66 +++++++++++++++++++ 10 files changed, 274 insertions(+), 16 deletions(-) create mode 100644 src/test/java/planetlotto/model/lotto/BonusNumberTest.java create mode 100644 src/test/java/planetlotto/model/lotto/LottoTest.java create mode 100644 src/test/java/planetlotto/model/lotto/LottosTest.java create mode 100644 src/test/java/planetlotto/model/winningDetail/RankTest.java create mode 100644 src/test/java/planetlotto/model/winningDetail/WinningDetailTest.java diff --git a/README.md b/README.md index cf948ff21..376174e91 100644 --- a/README.md +++ b/README.md @@ -52,22 +52,8 @@ ### 문제 상황 - 전체 프로세스에 대한 설명이 부족하다. - 어떤 식으로 어떻게 진행되는지에 대해, 처음에 설명하도록 한다. -- 전체 프로세스를 한 번 밖에 실행하지 못해서, 사용자 경험의 연속성이 떨어진다. - - 프로세스 실행 이후, 다시 프로세스를 진행할지를 사용자에게 뭍고, 응답을 바탕으로 진행한다. -- 당첨 통계에서 사용자가 이 프로그램에 원하는 정보가 충분하지 못하다. 예를 들어, 로또를 구매할 떄 사용자가 진정으로 원하는 것은 당첨에 대한 세부적인 정보보다는 타인이나 이전 대비 나의 수익률이 어떤가이다. 그러나 기존 기능은 이러한 욕구를 충족시키지 못한다. - - 구매한 로또에 대한 수익률을 출력한다. - - 이전 프로세스들에 대한 수익률과 이와 비교되는 나의 수익률을 출력한다. ### 로또 규칙 설명 - [x] 프로그램 시작 시, 프로그램에 대한 소개를 한다. - [x] 전체적인 절차를 설명한다. - - [x] 로또의 가격이나 당첨 기준과 같은 규칙을 설명한다. -- [ ] 전체 프로세스를 모두 수행한 후, 프로세스를 계속 진행할지 여부를 묻는다. - - [ ] 계속 진행 여부 입력 프롬프트를 출력한다. - - 메시지: "로또 프로그램이 종료되었습니다. 계속 해서 진행하시겠습니까? (Y/N)" - - [ ] 계속 진행 여부를 입력받는다. - - `IllegalArgumentException`: Y나 N이 입력되지 않은 경우 - - [ ] Y가 입력되면, 프로세스를 다시 진행한다. -- [ ] 이전 프로세스들에 대한 수익률 관련 정보를 제공한다. - - [ ] 구입 금액 입력 전에, 이전 수익률에 대한 정보를 제공한다. - - [ ] 당첨 통계에서, 이전 수익률 대비 나의 수익률이 몇 프로 높거나 낮은지 제공한다. \ No newline at end of file + - [x] 로또의 가격이나 당첨 기준과 같은 규칙을 설명한다. \ No newline at end of file diff --git a/src/main/java/planetlotto/model/lotto/Lottos.java b/src/main/java/planetlotto/model/lotto/Lottos.java index 0181c5bd2..b47ee37ca 100644 --- a/src/main/java/planetlotto/model/lotto/Lottos.java +++ b/src/main/java/planetlotto/model/lotto/Lottos.java @@ -21,6 +21,11 @@ public Lottos(int purchaseAmount) { this.elements = createElements(); } + public Lottos(List elements) { + this.elements = elements; + this.purchaseAmount = elements.size() * LOTTO_PRICE; + } + private void validatePurchaseAmount(int purchaseAmount) { if ((purchaseAmount < LOTTO_PRICE) || ((purchaseAmount % LOTTO_PRICE) != 0)) { throw new IllegalArgumentException("구입금액은 " + LOTTO_PRICE + "원 단위여야 합니다: " + purchaseAmount); @@ -53,4 +58,5 @@ public List> getElementsForOutput() { public WinningDetail computeWinningDetailWith(Lotto winningLotto, BonusNumber bonusNumber) { return new WinningDetail(this, winningLotto, bonusNumber); } + } diff --git a/src/main/java/planetlotto/model/winningDetail/WinningDetail.java b/src/main/java/planetlotto/model/winningDetail/WinningDetail.java index 8d4a9bdde..4f7e92444 100644 --- a/src/main/java/planetlotto/model/winningDetail/WinningDetail.java +++ b/src/main/java/planetlotto/model/winningDetail/WinningDetail.java @@ -38,6 +38,10 @@ private Map createCountsByRank() { return countsByRank; } + public Map getCountsByRank() { + return countsByRank; + } + public Map getCountsByRankForOutput() { Map rankCountsForOutput = new TreeMap<>(Collections.reverseOrder()); countsByRank.forEach((rank, count) -> rankCountsForOutput.put(rank.getOrder(),count)); diff --git a/src/main/java/planetlotto/view/InputView.java b/src/main/java/planetlotto/view/InputView.java index c1eb55343..f5029d8cf 100644 --- a/src/main/java/planetlotto/view/InputView.java +++ b/src/main/java/planetlotto/view/InputView.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.List; import java.util.function.Predicate; -import java.util.stream.Collectors; public class InputView { public static int askAmount() { diff --git a/src/main/java/planetlotto/view/OutputView.java b/src/main/java/planetlotto/view/OutputView.java index cbba3d174..8b2529173 100644 --- a/src/main/java/planetlotto/view/OutputView.java +++ b/src/main/java/planetlotto/view/OutputView.java @@ -37,6 +37,28 @@ public static void printRules() { System.out.println("4. 보너스 번호는 당첨 번호에 포함되지 않은 1에서 30 사이의 숫자여야 합니다."); } + public static void printPreviousProfitRate(List histories) { + if (!histories.isEmpty()) { + double totalProfitRate = calculateTotalProfitRate(histories); + System.out.println("이전 구매에서의 평균 수익률은 " + totalProfitRate + "%입니다."); + return; + } + + System.out.println("이전 구매에서의 평균 수익률에 대한 정보가 존재하지 않습니다."); + } + + private static double calculateTotalProfitRate(List histories) { + double sum = 0; + for (Double history : histories) { + sum += history; + } + + double totalProfitRate = sum / histories.size(); + totalProfitRate = Math.round(totalProfitRate * 10) / 10.0; + + return totalProfitRate; + } + public static void printPurchasedLottos(final List> lottos) { final String header = String.format("%d개를 구매했습니다.", lottos.size()); final String output = lottos.stream() diff --git a/src/test/java/planetlotto/model/lotto/BonusNumberTest.java b/src/test/java/planetlotto/model/lotto/BonusNumberTest.java new file mode 100644 index 000000000..76f4cdf6b --- /dev/null +++ b/src/test/java/planetlotto/model/lotto/BonusNumberTest.java @@ -0,0 +1,26 @@ +package planetlotto.model.lotto; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BonusNumberTest { + + @DisplayName("당첨 번호에 포함되지 않는 1에서 30 사이의 숫자가 아닌 경우 예외를 발생시킨다") + @Test + void BonusNumber() { + //given + Lotto winningLotto = new Lotto(List.of(1,2,3,4,5)); + List wrongs = List.of(1, 31); + + //when && then + wrongs.forEach(wrong -> { + Assertions.assertThatThrownBy(() -> new BonusNumber(wrong, winningLotto)) + .isInstanceOf(IllegalArgumentException.class); + }); + + } +} \ No newline at end of file diff --git a/src/test/java/planetlotto/model/lotto/LottoTest.java b/src/test/java/planetlotto/model/lotto/LottoTest.java new file mode 100644 index 000000000..3724937a2 --- /dev/null +++ b/src/test/java/planetlotto/model/lotto/LottoTest.java @@ -0,0 +1,58 @@ +package planetlotto.model.lotto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class LottoTest { + + @DisplayName("로또_번호의_개수가_5개가_넘어가면_예외가_발생한다") + @Test + void 로또_번호의_개수가_5개가_넘어가면_예외가_발생한다() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6))) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.") + @Test + void 로또_번호에_중복된_숫자가_있으면_예외가_발생한다() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 4))) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("로또 번호에 1에서 30 사이가 아닌 값이 포함되면 예외가 발생한다.") + @Test + void 로또_번호에_1에서_30_사이가_아닌_숫자가_있으면_예외가_발생한다() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 31))) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("당첨번호와 일치하는 번호의 개수를 반환한다.") + @Test + void countMatchingNumbersWith() { + //given + Lotto winningLotto = new Lotto(List.of(1,2,3,4,5)); + + List lottos = List.of( + new Lotto(List.of(1,2,3,4,5)), // 5 개 일치 + new Lotto(List.of(1,2,3,4,7)), // 4 개 일치 + new Lotto(List.of(1, 2, 3, 7, 8)) // 3 개 일치 + ); + + //when && then + assertThat(lottos.get(0).countMatchingNumbersWith(winningLotto)).isEqualTo(5); + assertThat(lottos.get(1).countMatchingNumbersWith(winningLotto)).isEqualTo(4); + assertThat(lottos.get(2).countMatchingNumbersWith(winningLotto)).isEqualTo(3); + } + + @DisplayName("보너스 번호와의 일치 여부를 반환한다.") + @Test + void isMatchedWith() { + assertThat(new Lotto(List.of(1,2,3,4,5)) + .isMatchedWith(new BonusNumber(5, new Lotto(List.of(6,7,8,9,10))))) + .isTrue(); + } +} \ No newline at end of file diff --git a/src/test/java/planetlotto/model/lotto/LottosTest.java b/src/test/java/planetlotto/model/lotto/LottosTest.java new file mode 100644 index 000000000..f4ae8c1e1 --- /dev/null +++ b/src/test/java/planetlotto/model/lotto/LottosTest.java @@ -0,0 +1,45 @@ +package planetlotto.model.lotto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class LottosTest { + + @DisplayName("구입 금액이 500원 단위가 아니면 예외가 발생한다.") + @Test + void validatePurchaseAmount() { + assertThatThrownBy(() -> new Lottos(100)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("구입한 로또를 발행한다.") + @Test + void Lottos() { + //given + Lottos lottos = new Lottos(1000); + + //when && then + Assertions.assertThat(lottos.getElements().size()).isEqualTo(2); + } + + @DisplayName("구입한 로또들의 번호들을 List> 타입으로 반환한다.") + @Test + void getElementsForOutput() { + //given + Lottos lottos = new Lottos(List.of( + new Lotto(List.of(1,2,3,4,5)), + new Lotto(List.of(6,7,8,9,10)) + )); + + //when && then + assertThat(lottos.getElementsForOutput()).contains( + List.of(1,2,3,4,5), + List.of(6,7,8,9,10) + ); + } +} \ No newline at end of file diff --git a/src/test/java/planetlotto/model/winningDetail/RankTest.java b/src/test/java/planetlotto/model/winningDetail/RankTest.java new file mode 100644 index 000000000..200832aea --- /dev/null +++ b/src/test/java/planetlotto/model/winningDetail/RankTest.java @@ -0,0 +1,46 @@ +package planetlotto.model.winningDetail; + +import static org.assertj.core.api.Assertions.assertThat; +import static planetlotto.model.winningDetail.Rank.FIFTH; +import static planetlotto.model.winningDetail.Rank.FIRST; +import static planetlotto.model.winningDetail.Rank.FIRTH; +import static planetlotto.model.winningDetail.Rank.NONE; +import static planetlotto.model.winningDetail.Rank.SECOND; +import static planetlotto.model.winningDetail.Rank.THIRD; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RankTest { + + @DisplayName("당첨번호와_보너스번호와_비교해_로또의_등수를_판단한다") + @Test + void of() { + //given + List matchingCounts = List.of( + 5, 5, 4, 4, 3, 3, 2, 2, 1, 0 + ); + + List isMatchedList = List.of( + true, false, true, false, true, false, true, false, false, false + ); + + //when + List ranks = new ArrayList<>(); + + for (int i = 0; i < matchingCounts.size(); i++) { + ranks.add(Rank.of(matchingCounts.get(i), isMatchedList.get(i))); + } + + //then + assertThat(ranks).containsExactly( + FIRST, FIRST, + SECOND, THIRD, + FIRTH, FIRTH, + FIFTH, FIFTH, + NONE, NONE + ); + } +} \ No newline at end of file diff --git a/src/test/java/planetlotto/model/winningDetail/WinningDetailTest.java b/src/test/java/planetlotto/model/winningDetail/WinningDetailTest.java new file mode 100644 index 000000000..25b9eafd0 --- /dev/null +++ b/src/test/java/planetlotto/model/winningDetail/WinningDetailTest.java @@ -0,0 +1,66 @@ +package planetlotto.model.winningDetail; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import planetlotto.model.lotto.BonusNumber; +import planetlotto.model.lotto.Lotto; +import planetlotto.model.lotto.Lottos; + +class WinningDetailTest { + + @DisplayName("각 등수 별 개수를 센다.") + @Test + void WinningDetail() { + //given + Lottos lottos = new Lottos(List.of( + new Lotto(List.of(1,2,3,4,5)), //1등 + new Lotto(List.of(1,2,3,4,6)), //2등 + new Lotto(List.of(1,2,3,4,7)), //3등 + new Lotto(List.of(1, 2, 3, 7, 8)), //4등 + new Lotto(List.of(1, 2, 6, 7, 8)), //5등 + new Lotto(List.of(6,7,8,9,10)) //등외 + )); + + Lotto winningLotto = new Lotto(List.of(1,2,3,4,5)); + BonusNumber bonusNumber = new BonusNumber(6, winningLotto); + + //when + WinningDetail winningDetail = new WinningDetail(lottos, winningLotto, bonusNumber); + + //then + winningDetail.getCountsByRank().values().forEach(count -> + assertThat(count).isEqualTo(1) + ); + } + + @DisplayName("각 등수와 해당 등수의 숫자를 정수 형태로 담아 반환한다.") + @Test + void getCountsByRankForOutput() { + //given + Lottos lottos = new Lottos(List.of( + new Lotto(List.of(1,2,3,4,5)), //1등 + new Lotto(List.of(1,2,3,4,7)), //3등 + new Lotto(List.of(1, 2, 6, 7, 8)), //5등 + new Lotto(List.of(6,7,8,9,10)) //등외 + )); + + Lotto winningLotto = new Lotto(List.of(1,2,3,4,5)); + BonusNumber bonusNumber = new BonusNumber(6, winningLotto); + + WinningDetail winningDetail = new WinningDetail(lottos, winningLotto, bonusNumber); + + //when + Map countsByRankForOutput = winningDetail.getCountsByRankForOutput(); + + //then + assertThat(countsByRankForOutput.get(1)).isEqualTo(1); + assertThat(countsByRankForOutput.get(3)).isEqualTo(1); + assertThat(countsByRankForOutput.get(5)).isEqualTo(1); + } +} \ No newline at end of file