diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba44..6f0191f2865 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,12 @@ package lotto; +import lotto.controller.InstanceManager; +import lotto.view.LottoGame; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + InstanceManager instanceManager = new InstanceManager(); + LottoGame lottoGame = instanceManager.lottoGame(); + lottoGame.start(); } } diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java deleted file mode 100644 index 519793d1f73..00000000000 --- a/src/main/java/lotto/Lotto.java +++ /dev/null @@ -1,20 +0,0 @@ -package lotto; - -import java.util.List; - -public class Lotto { - private final List numbers; - - public Lotto(List numbers) { - validate(numbers); - this.numbers = numbers; - } - - private void validate(List numbers) { - if (numbers.size() != 6) { - throw new IllegalArgumentException(); - } - } - - // TODO: 추가 기능 구현 -} diff --git a/src/main/java/lotto/controller/InstanceManager.java b/src/main/java/lotto/controller/InstanceManager.java new file mode 100644 index 00000000000..7988ad6d8ab --- /dev/null +++ b/src/main/java/lotto/controller/InstanceManager.java @@ -0,0 +1,22 @@ +package lotto.controller; + +import lotto.model.LottoMachineModel; +import lotto.model.WinningResultModel; +import lotto.view.ConsoleInput; +import lotto.view.LottoGame; + +public class InstanceManager { + private final ConsoleInput consoleInput; + private final LottoMachineModel lottoMachine; + private final WinningResultModel winningResult; + + public InstanceManager() { + this.consoleInput = new ConsoleInput(); + this.lottoMachine = new LottoMachineModel(); + this.winningResult = new WinningResultModel(); + } + + public LottoGame lottoGame() { + return new LottoGame(consoleInput, lottoMachine, winningResult); + } +} diff --git a/src/main/java/lotto/model/LottoMachineModel.java b/src/main/java/lotto/model/LottoMachineModel.java new file mode 100644 index 00000000000..d470236e31c --- /dev/null +++ b/src/main/java/lotto/model/LottoMachineModel.java @@ -0,0 +1,21 @@ +package lotto.model; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.ArrayList; +import java.util.List; + +public class LottoMachineModel { + private static final int LOTTO_NUMBER_MIN = 1; + private static final int LOTTO_NUMBER_MAX = 45; + private static final int LOTTO_NUMBERS_COUNT = 6; + + public List generateLottos(int count) { + List lottos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + List numbers = Randoms.pickUniqueNumbersInRange(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_NUMBERS_COUNT); + lottos.add(new LottoModel(numbers)); + } + return lottos; + } +} diff --git a/src/main/java/lotto/model/LottoModel.java b/src/main/java/lotto/model/LottoModel.java new file mode 100644 index 00000000000..ece39ebdded --- /dev/null +++ b/src/main/java/lotto/model/LottoModel.java @@ -0,0 +1,36 @@ +package lotto.model; + +// cannot reslove symbol -> gradle rebuild로 해결 + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LottoModel { + private final List numbers; + + public LottoModel(List numbers) { + validate(numbers); + this.numbers = numbers; + } + + private void validate(List numbers) { + if (numbers.size() != 6) { + throw new IllegalArgumentException("로또 번호는 6개여야 합니다."); + } + Set numberSet = new HashSet<>(); + for (int number : numbers) { + if (number < 1 || number > 45) { + throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + if (!numberSet.add(number)) { + throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); + } + } + } + + // TODO: 추가 기능 구현 + public List getNumbers() { + return numbers; + } +} \ No newline at end of file diff --git a/src/main/java/lotto/model/RankEnum.java b/src/main/java/lotto/model/RankEnum.java new file mode 100644 index 00000000000..be3cde1cd21 --- /dev/null +++ b/src/main/java/lotto/model/RankEnum.java @@ -0,0 +1,45 @@ +package lotto.model; + +public enum RankEnum { + FIRST(6, 2_000_000_000), + SECOND(5, 30_000_000), + THIRD(5, 1_500_000), + FOURTH(4, 50_000), + FIFTH(3, 5_000), + MISS(0, 0); + + private final int matchCount; + private final int prize; + + RankEnum(int matchCount, int prize) { + this.matchCount = matchCount; + this.prize = prize; + } + + public int getMatchCount() { + return matchCount; + } + + public int getPrize() { + return prize; + } + + public static RankEnum valueOf(int matchCount, boolean matchBonus) { + if (matchCount == 6) { + return FIRST; + } + if (matchCount == 5 && matchBonus) { + return SECOND; + } + if (matchCount == 5) { + return THIRD; + } + if (matchCount == 4) { + return FOURTH; + } + if (matchCount == 3) { + return FIFTH; + } + return MISS; + } +} diff --git a/src/main/java/lotto/model/WinningResultModel.java b/src/main/java/lotto/model/WinningResultModel.java new file mode 100644 index 00000000000..b74a8380373 --- /dev/null +++ b/src/main/java/lotto/model/WinningResultModel.java @@ -0,0 +1,46 @@ +package lotto.model; + + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.HashSet; + +public class WinningResultModel { + private final Map result = new EnumMap<>(RankEnum.class); + + public WinningResultModel() { + for (RankEnum rank : RankEnum.values()) { + result.put(rank, 0); + } + } + + public void checkWinning(List purchasedLottos, Set winningNumbers, int bonusNumber) { + for (LottoModel lotto : purchasedLottos) { + Set intersection = new HashSet<>(lotto.getNumbers()); + intersection.retainAll(winningNumbers); + int matchCount = intersection.size(); + boolean matchBonus = lotto.getNumbers().contains(bonusNumber); + + RankEnum rank = RankEnum.valueOf(matchCount, matchBonus); + result.put(rank, result.get(rank) + 1); + } + } + + public void printWinningResult() { + System.out.println("당첨 통계\n---"); + System.out.printf("3개 일치 (5,000원) - %d개%n", result.get(RankEnum.FIFTH)); + System.out.printf("4개 일치 (50,000원) - %d개%n", result.get(RankEnum.FOURTH)); + System.out.printf("5개 일치 (1,500,000원) - %d개%n", result.get(RankEnum.THIRD)); + System.out.printf("5개 일치, 보너스 볼 일치 (30,000,000원) - %d개%n", result.get(RankEnum.SECOND)); + System.out.printf("6개 일치 (2,000,000,000원) - %d개%n", result.get(RankEnum.FIRST)); + } + + public double calculateProfitRate(int purchaseAmount) { + int totalWinnings = result.entrySet().stream() + .mapToInt(entry -> entry.getKey().getPrize() * entry.getValue()) + .sum(); + return ((double) totalWinnings / purchaseAmount) * 100; + } +} diff --git a/src/main/java/lotto/view/ConsoleInput.java b/src/main/java/lotto/view/ConsoleInput.java new file mode 100644 index 00000000000..9ed19aaa980 --- /dev/null +++ b/src/main/java/lotto/view/ConsoleInput.java @@ -0,0 +1,68 @@ +package lotto.view; + +import camp.nextstep.edu.missionutils.Console; +import java.util.HashSet; +import java.util.Set; + +public class ConsoleInput { + public int getPurchaseAmount() { + System.out.println("구입금액을 입력해 주세요."); + String input = Console.readLine(); + int purchaseAmount; + try { + purchaseAmount = Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("구입 금액은 숫자여야 합니다."); + } + if (purchaseAmount % 1000 != 0) { + throw new IllegalArgumentException("구입 금액은 1,000원 단위여야 합니다."); + } + return purchaseAmount; + } + + public Set getWinningNumbers() { + System.out.println("당첨 번호를 입력해 주세요."); + String input = Console.readLine(); + String[] split = input.split(","); + if (split.length != 6) { + throw new IllegalArgumentException("당첨 번호는 6개여야 합니다."); + } + + Set winningNumbers = new HashSet<>(); + for (String number : split) { + int num; + try { + num = Integer.parseInt(number); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("당첨 번호는 숫자여야 합니다."); + } + if (num < 1 || num > 45) { + throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + winningNumbers.add(num); + } + if (winningNumbers.size() != 6) { + throw new IllegalArgumentException("당첨 번호는 중복되지 않는 6개의 숫자여야 합니다."); + } + return winningNumbers; + } + + public int getBonusNumber(Set winningNumbers) { + System.out.println("보너스 번호를 입력해 주세요."); + String input = Console.readLine(); + int bonusNumber; + try { + bonusNumber = Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("보너스 번호는 숫자여야 합니다."); + } + if (bonusNumber < 1 || bonusNumber > 45) { + throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + if (winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복되지 않아야 합니다."); + } + return bonusNumber; + } +} + diff --git a/src/main/java/lotto/view/LottoGame.java b/src/main/java/lotto/view/LottoGame.java new file mode 100644 index 00000000000..5917de56e8d --- /dev/null +++ b/src/main/java/lotto/view/LottoGame.java @@ -0,0 +1,69 @@ +package lotto.view; + +import lotto.model.LottoModel; +import lotto.model.LottoMachineModel; +import lotto.model.WinningResultModel; + +import java.util.List; +import java.util.Set; + +public class LottoGame { + + private static final int LOTTO_PRICE = 1000; + private final ConsoleInput consoleInput; + private final LottoMachineModel lottoMachine; + private final WinningResultModel winningResult; + + public LottoGame(ConsoleInput consoleInput, LottoMachineModel lottoMachine, WinningResultModel winningResult) { + this.consoleInput = consoleInput; + this.lottoMachine = lottoMachine; + this.winningResult = winningResult; + } + + public void start() { + try { + int purchaseAmount = getPurchaseAmount(); + List purchasedLottos = generateLottos(purchaseAmount); + printPurchasedLottos(purchasedLottos); + + Set winningNumbers = getWinningNumbers(); + int bonusNumber = getBonusNumber(winningNumbers); + + checkAndPrintWinningResult(purchasedLottos, winningNumbers, bonusNumber, purchaseAmount); + } catch (IllegalArgumentException e) { + System.out.println("[ERROR] " + e.getMessage()); + } + } + + private int getPurchaseAmount() { + return consoleInput.getPurchaseAmount(); + } + + private List generateLottos(int purchaseAmount) { + int numberOfLottos = purchaseAmount / LOTTO_PRICE; + return lottoMachine.generateLottos(numberOfLottos); + } + + private void printPurchasedLottos(List lottos) { + System.out.printf("%d개를 구매했습니다.%n", lottos.size()); + for (LottoModel lotto : lottos) { + System.out.println(lotto.getNumbers()); + } + } + + private Set getWinningNumbers() { + return consoleInput.getWinningNumbers(); + } + + private int getBonusNumber(Set winningNumbers) { + return consoleInput.getBonusNumber(winningNumbers); + } + + private void checkAndPrintWinningResult(List purchasedLottos, Set winningNumbers, int bonusNumber, int purchaseAmount) { + winningResult.checkWinning(purchasedLottos, winningNumbers, bonusNumber); + winningResult.printWinningResult(); + + double profitRate = winningResult.calculateProfitRate(purchaseAmount); + System.out.printf("총 수익률은 %.1f%%입니다.%n", profitRate); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 9f5dfe7eb83..d1ca7d29f78 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -1,5 +1,6 @@ package lotto; +import lotto.model.LottoModel; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,7 +12,7 @@ class LottoTest { @DisplayName("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.") @Test void createLottoByOverSize() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) + assertThatThrownBy(() -> new LottoModel(List.of(1, 2, 3, 4, 5, 6, 7))) .isInstanceOf(IllegalArgumentException.class); } @@ -19,7 +20,7 @@ void createLottoByOverSize() { @Test void createLottoByDuplicatedNumber() { // TODO: 이 테스트가 통과할 수 있게 구현 코드 작성 - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) + assertThatThrownBy(() -> new LottoModel(List.of(1, 2, 3, 4, 5, 5))) .isInstanceOf(IllegalArgumentException.class); }