diff --git a/README.md b/README.md index 8a4f22ed0..70c460bcc 100644 --- a/README.md +++ b/README.md @@ -1 +1,93 @@ # java-planetlotto-precourse + +## 참고 +로또 번호 숫자 범위 +- 1 ~ 30 + +1개 로또 +- 중복되지 않는 5개의 숫자 + +당첨 번호 추첨 +- 중복되지 않는 숫자 5개 + 보너스 번호 1 개 + +당첨 기준 +1등 / 5개 일치 / 100,000,000 +2등 / ... + +예외 처리 +- 메세지 출력 후 해당 부분부터 다시 입력 +- [ERROR] 로또 번호는 1부터 30 사이의 숫자여야 합니다. + + +## 구현할 기능 목록 + +### 구입 금액 입력 +- [x] 예외가 발생하면 로또 구입 금액 입력부터 재실행 한다. + + + 입력 +- [x] 로또 구입 금액을 입력받는다. // int + + + 비즈니스 +- [x] 금액이 500원 단위가 맞는지 확인한다. +- [x] 500원 단위가 아니면 예외가 발생한다. +- [x] 로또 구입 수량을 계산한다. 금액 / 500 + +### 로또 발행 +비즈니스 +- [x] 로또 구입 수량만큼 반복한다. +- [x] 랜덤 번호를 생성한다. List<로또번호> +- [x] 로또를 생성한다. +- [x] 로또를 저장한다. + + +출력 +- [x] 구매한 로또를 출력한다 // + 입력 요구 -> List> lottos + +### 당첨 번호 입력 +- [x] 예외가 발생하면 당첨 번호 입력부터 재실행한다. + + + 입력 +- [x] 당첨 번호를 입력받는다. // List + + + 비즈니스 +- [x] 입력된 당첨 번호가 5개인지 검증한다. +- [x] 입력된 당첨 번호가 1~30 사이의 숫자인지 검증한다. +- [x] 입력된 당첨 번호가 중복이 없는지 검사한다. +- [x] 입력된 번호를 저장한다. // WinningLotto(List<당첨로또>, 보너스번호) + +### 보너스 번호 입력 +- [x] 예외가 발생하면 보너스 번호 입력부터 재실행한다. + + + 입력 +- [x] 보너스 번호를 입력받는다. // int + + + 비즈니스 +- [x] 보너스 번호가 당첨번호와 중복되지 않는지 검증 +- [x] 입력된 번호를 저장한다. + +### 당첨 통계 출력 +비즈니스 +- [x] 로또와 당첨번호의 결과를 비교하여 등수를 반환한다. +- [x] 구매한 로또의 등수를 계산한다. +- [x] 등수를 저장한다. +- [x] 결과를 출력한다. + 입력 요구 -> Map<등수, 당첨개수> countsByRank + +### 추가 +- [x] 출력 정렬 필요함 +- [x] 보너스 번호 검증 안되고 있음. // LottoPolicy에서 체크 어떤지? + +도메인 +- Lotto // 로또, 로또에 사용하는 숫자 검증 +- WinningLotto // 당첨로또, 보너스 번호 +- LottoGenerator // 로또 생성, List<로또 번호> +- PurchasedLotto // List +- LottoService // +- LottoPolicy // 구매금액 확인, 등수 계산 \ No newline at end of file diff --git a/src/main/java/planetlotto/Application.java b/src/main/java/planetlotto/Application.java index 27d0a8f96..1f31cc780 100644 --- a/src/main/java/planetlotto/Application.java +++ b/src/main/java/planetlotto/Application.java @@ -1,7 +1,19 @@ package planetlotto; +import planetlotto.controller.LottoController; +import planetlotto.domain.*; +import planetlotto.service.LottoService; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + LottoPolicy policy = new LottoPolicy(); + LottoGenerator generator = new RandomLottoGenerator(); + PurchasedLotto purchased = new PurchasedLotto(); + WinningLotto winning = new WinningLotto(); + + LottoService service = new LottoService(policy, generator, purchased, winning); + + LottoController controller = new LottoController(service); + controller.run(); } } diff --git a/src/main/java/planetlotto/controller/LottoController.java b/src/main/java/planetlotto/controller/LottoController.java new file mode 100644 index 000000000..4792613a1 --- /dev/null +++ b/src/main/java/planetlotto/controller/LottoController.java @@ -0,0 +1,77 @@ +package planetlotto.controller; + +import planetlotto.service.LottoService; +import planetlotto.view.InputView; +import planetlotto.view.OutputView; + +import java.util.List; + +public class LottoController { + private LottoService service; + + public LottoController(LottoService service) { + this.service = service; + } + + public void run() { + purchaseLottos(); + + setWinningNumbers(); + setBonus(); + + OutputView.printResult(service.aggregateRank()); + } + + private void setBonus() { + while (true) { + try { + int bonus = InputView.askBonusNumber(); + OutputView.printLienSeparator(); + service.setBonus(bonus); + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e.getMessage()); + continue; + } + break; + } + } + + private void setWinningNumbers() { + while (true) { + try { + List winningNumbers = InputView.askWinningLotto(); + OutputView.printLienSeparator(); + service.setWinningNumbers(winningNumbers); + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e.getMessage()); + continue; + } + break; + } + } + + private void purchaseLottos() { + int amount = getAmount(); + int purchaseQuantity = service.getPurchaseQuantity(amount); + + for (int i = 0; i < purchaseQuantity; i++) { + service.generateLotto(); + } + OutputView.printPurchasedLottos(service.getPurchasedLottos()); + } + + private int getAmount() { + int amount; + while (true) { + try { + amount = InputView.askAmount(); + OutputView.printLienSeparator(); + service.validateAmount(amount); + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e.getMessage()); + continue; + } + return amount; + } + } +} diff --git a/src/main/java/planetlotto/domain/Lotto.java b/src/main/java/planetlotto/domain/Lotto.java new file mode 100644 index 000000000..a01ee1b8c --- /dev/null +++ b/src/main/java/planetlotto/domain/Lotto.java @@ -0,0 +1,43 @@ +package planetlotto.domain; + +import java.util.HashSet; +import java.util.List; + +public class Lotto { + private List numbers; + + public Lotto(List numbers) { + this.numbers = numbers; + validateNumbers(); + } + + public List getNumbers() { + return numbers; + } + + private void validateNumbers() { + // 숫자 5개 검증 + if (numbers.size() != 5) { + throw new IllegalArgumentException("로또 숫자는 5개여야 합니다."); + } + + // 숫자 범위 검증 + for (Integer number : numbers) { + if (number < 1 || number > 30) { + throw new IllegalArgumentException("로또 번호는 1부터 30 사이의 숫자여야 합니다."); + } + } + + // 중복 검증 + if (numbers.size() != new HashSet<>(numbers).size()) { + throw new IllegalArgumentException("중복된 숫자 입력은 허용되지 않습니다."); + } + } + + @Override + public String toString() { + return "Lotto{" + + "numbers=" + numbers + + '}'; + } +} diff --git a/src/main/java/planetlotto/domain/LottoGenerator.java b/src/main/java/planetlotto/domain/LottoGenerator.java new file mode 100644 index 000000000..88e1ddda1 --- /dev/null +++ b/src/main/java/planetlotto/domain/LottoGenerator.java @@ -0,0 +1,5 @@ +package planetlotto.domain; + +public interface LottoGenerator { + Lotto generate(); +} diff --git a/src/main/java/planetlotto/domain/LottoPolicy.java b/src/main/java/planetlotto/domain/LottoPolicy.java new file mode 100644 index 000000000..89dde7f03 --- /dev/null +++ b/src/main/java/planetlotto/domain/LottoPolicy.java @@ -0,0 +1,27 @@ +package planetlotto.domain; + +public class LottoPolicy { + private static final int LOTTO_PRICE = 500; + + public boolean isCorrectUnit(int amount) { + return amount % LOTTO_PRICE == 0; + } + + public int getPurchaseQuantity(int amount) { + return amount / LOTTO_PRICE; + } + + public int determineRank(WinningLotto winning, Lotto lotto) { + int count = winning.compareWithWinning(lotto); + if (count == 5) return 1; + if (count == 4) { + if (winning.hasBonus(lotto)) { + return 2; + } + return 3; + } + if (count == 3 && winning.hasBonus(lotto)) return 4; + if (count == 2) return 5; + return 0; + } +} diff --git a/src/main/java/planetlotto/domain/PurchasedLotto.java b/src/main/java/planetlotto/domain/PurchasedLotto.java new file mode 100644 index 000000000..6f2df0483 --- /dev/null +++ b/src/main/java/planetlotto/domain/PurchasedLotto.java @@ -0,0 +1,28 @@ +package planetlotto.domain; + +import java.util.ArrayList; +import java.util.List; + +public class PurchasedLotto { + private List lottos = new ArrayList<>(); + + public void addLotto(Lotto lotto) { + lottos.add(lotto); + } + + public List> getLottostoList() { + List> resultLottos = new ArrayList<>(); + for (Lotto lotto : lottos) { + resultLottos.add( + lotto.getNumbers().stream() + .sorted(Integer::compareTo) + .toList() + ); + } + return resultLottos; + } + + public List getLottos() { + return lottos; + } +} diff --git a/src/main/java/planetlotto/domain/RandomLottoGenerator.java b/src/main/java/planetlotto/domain/RandomLottoGenerator.java new file mode 100644 index 000000000..343c13455 --- /dev/null +++ b/src/main/java/planetlotto/domain/RandomLottoGenerator.java @@ -0,0 +1,11 @@ +package planetlotto.domain; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomLottoGenerator implements LottoGenerator { + + @Override + public Lotto generate() { + return new Lotto(Randoms.pickUniqueNumbersInRange(1, 30, 5)); + } +} diff --git a/src/main/java/planetlotto/domain/WinningLotto.java b/src/main/java/planetlotto/domain/WinningLotto.java new file mode 100644 index 000000000..0ba363333 --- /dev/null +++ b/src/main/java/planetlotto/domain/WinningLotto.java @@ -0,0 +1,36 @@ +package planetlotto.domain; + +import java.util.List; + +public class WinningLotto { + private Lotto winning; + private int bonus; + + public void setWinning(List winningNumbers) { + this.winning = new Lotto(winningNumbers); + } + + public void setBonus(int bonus) { + if (bonus < 1 || bonus > 30) { + throw new IllegalArgumentException("보너스 번호는 1부터 30 사이의 숫자여야 합니다."); + } + + if (winning.getNumbers().contains(bonus)) { + throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); + } + } + + public int compareWithWinning(Lotto lotto) { + int count = 0; + for (Integer numbers : lotto.getNumbers()) { + if (winning.getNumbers().contains(numbers)) { + count++; + } + } + return count; + } + + public boolean hasBonus(Lotto lotto) { + return lotto.getNumbers().contains(bonus); + } +} diff --git a/src/main/java/planetlotto/service/LottoService.java b/src/main/java/planetlotto/service/LottoService.java new file mode 100644 index 000000000..69918fb50 --- /dev/null +++ b/src/main/java/planetlotto/service/LottoService.java @@ -0,0 +1,59 @@ +package planetlotto.service; + +import planetlotto.domain.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class LottoService { + private LottoPolicy policy; + private LottoGenerator generator; + private PurchasedLotto purchased; + private WinningLotto winning; + + public LottoService(LottoPolicy policy, LottoGenerator generator, PurchasedLotto purchased, WinningLotto winning) { + this.policy = policy; + this.generator = generator; + this.purchased = purchased; + this.winning = winning; + } + + public void validateAmount(int amount) { + if (!policy.isCorrectUnit(amount)) { + throw new IllegalArgumentException("구입 금액은 500원 단위로 입력해주세요."); + } + } + + public int getPurchaseQuantity(int amount) { + return policy.getPurchaseQuantity(amount); + } + + public void generateLotto() { + Lotto generate = generator.generate(); + purchased.addLotto(generate); + } + + public List> getPurchasedLottos() { + return purchased.getLottostoList(); + } + + public void setWinningNumbers(List winningNumbers) { + winning.setWinning(winningNumbers); + } + + public void setBonus(int bonus) { + winning.setBonus(bonus); + } + + public Map aggregateRank() { + List lottos = purchased.getLottos(); + Map countByRank = new HashMap<>(); + for (Lotto lotto : lottos) { + int rank = policy.determineRank(winning, lotto); + Integer value = countByRank.getOrDefault(rank, 0); + countByRank.put(rank, ++value); + } + return countByRank; + } +} diff --git a/src/main/java/planetlotto/view/InputView.java b/src/main/java/planetlotto/view/InputView.java index c1eb55343..e5ce7d2f4 100644 --- a/src/main/java/planetlotto/view/InputView.java +++ b/src/main/java/planetlotto/view/InputView.java @@ -18,7 +18,7 @@ public static int askAmount() { } public static List askWinningLotto() { - System.out.println("지난 주 당첨 번호를 입력해 주세요."); + System.out.println("당첨 번호를 입력해 주세요."); try { return Arrays.stream(Console.readLine() .split(",")) diff --git a/src/main/java/planetlotto/view/OutputView.java b/src/main/java/planetlotto/view/OutputView.java index 0452898da..507333637 100644 --- a/src/main/java/planetlotto/view/OutputView.java +++ b/src/main/java/planetlotto/view/OutputView.java @@ -8,7 +8,7 @@ public class OutputView { public static void printPurchasedLottos(final List> lottos) { - final String header = String.format("%d개를 구매했습니다.", lottos.size()); + final String header = String.format("%d개를 구매했습니다.\n", lottos.size()); final String output = lottos.stream() .map(Object::toString) .collect(Collectors.joining( @@ -18,6 +18,7 @@ public static void printPurchasedLottos(final List> lottos) { )); System.out.println(output); + System.out.println(); } /** @@ -44,4 +45,8 @@ public static void printResult(final Map countsByRank) { public static void printErrorMessage(final String message) { System.out.printf("[ERROR] %s%n", message); } + + public static void printLienSeparator() { + System.out.println(); + } }