diff --git a/README.md b/README.md index 5fa2560b46..b40be1bccb 100644 --- a/README.md +++ b/README.md @@ -1 +1,26 @@ # java-lotto-precourse + +## 기능 요구사항 + +### 1. 입력 기능 +- 로또 구입 금액 입력받기 (1,000원 단위 검증) +- 당첨 번호 6개 입력받기 (쉼표 구분) +- 보너스 번호 입력받기 + +### 2.로또 발행 기능 +- 구입 금액에 따른 로또 개수 계산 +- 1~45 범위에서 중복 없는 6개 번호 생성 +- 로또 번호 오름차순 정렬 및 출력 + +### 3.당첨 처리 기능 +- 구매 로또와 당첨 번호 비교 +- 당첨 등수 판정 (1등~5등) +- 당첨 내역 집계 + +### 4.결과 출력 기능 +- 당첨 통계 출력 +- 수익률 계산 및 출력 + +### 5.예외 처리 기능 +- 입력값 검증 및 예외 처리 +- 에러 메시지 출력 \ No newline at end of file diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba4..4f0135afd9 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,169 @@ package lotto; +import camp.nextstep.edu.missionutils.Console; +import camp.nextstep.edu.missionutils.Randoms; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + System.out.println("구입금액을 입력해 주세요."); + int purchaseAmount = readPurchaseAmount(); + + List lottos = generateLottos(purchaseAmount); + printLottos(lottos); + + System.out.println("당첨 번호를 입력해 주세요."); + List winningNumbers = readWinningNumbers(); + + System.out.println("보너스 번호를 입력해 주세요."); + int bonusNumber = readBonusNumber(winningNumbers); + + Map results = checkWinnings(lottos, winningNumbers, bonusNumber); + printResults(results, purchaseAmount); + } + + private static int readPurchaseAmount() { + while (true) { + try { + return parsePurchaseAmount(Console.readLine()); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + + private static List readWinningNumbers() { + while (true) { + try { + return parseWinningNumbers(Console.readLine()); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + + private static int readBonusNumber(List winningNumbers) { + while (true) { + try { + return parseBonusNumber(Console.readLine(), winningNumbers); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + + private static Map checkWinnings(List lottos, List winningNumbers, int bonusNumber) { + Map results = new HashMap<>(); + + for (Rank rank : Rank.values()) { + results.put(rank, 0); + } + + for (Lotto lotto : lottos) { + Rank rank = lotto.checkRank(winningNumbers, bonusNumber); + results.put(rank, results.get(rank) + 1); + } + + return results; + } + + private static void printResults(Map results, int purchaseAmount) { + System.out.println("당첨 통계"); + System.out.println("---"); + System.out.println("3개 일치 (5,000원) - " + results.get(Rank.FIFTH) + "개"); + System.out.println("4개 일치 (50,000원) - " + results.get(Rank.FOURTH) + "개"); + System.out.println("5개 일치 (1,500,000원) - " + results.get(Rank.THIRD) + "개"); + System.out.println("5개 일치, 보너스 볼 일치 (30,000,000원) - " + results.get(Rank.SECOND) + "개"); + System.out.println("6개 일치 (2,000,000,000원) - " + results.get(Rank.FIRST) + "개"); + + double profitRate = calculateProfitRate(results, purchaseAmount); + System.out.println("총 수익률은 " + String.format("%.1f", profitRate) + "%입니다."); + } + + private static double calculateProfitRate(Map results, int purchaseAmount) { + int totalPrize = 0; + for (Rank rank : results.keySet()) { + totalPrize += rank.getPrize() * results.get(rank); + } + return (double) totalPrize / purchaseAmount * 100; + } + + private static List generateLottos(int purchaseAmount) { + int count = purchaseAmount / 1000; + System.out.println(count + "개를 구매했습니다."); + + List lottos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + List numbers = Randoms.pickUniqueNumbersInRange(1, 45, 6); + lottos.add(new Lotto(numbers)); + } + return lottos; + } + + private static void printLottos(List lottos) { + for (Lotto lotto : lottos) { + System.out.println(lotto.toString()); + } + System.out.println(); + } + + private static int parsePurchaseAmount(String input) { + try { + int amount = Integer.parseInt(input); + if (amount % 1000 != 0) { + throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다."); + } + return amount; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] 구입 금액은 숫자여야 합니다."); + } + } + + private static List parseWinningNumbers(String input) { + String[] parts = input.split(","); + if (parts.length != 6) { + throw new IllegalArgumentException("[ERROR] 당첨 번호는 6개여야 합니다."); + } + + try { + List numbers = Arrays.stream(parts) + .map(String::trim) + .map(Integer::parseInt) + .toList(); + + for (int number : numbers) { + if (number < 1 || number > 45) { + throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + } + + if (new HashSet<>(numbers).size() != 6) { + throw new IllegalArgumentException("[ERROR] 당첨 번호는 중복될 수 없습니다."); + } + + return numbers; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] 당첨 번호는 숫자여야 합니다."); + } + } + + private static int parseBonusNumber(String input, List winningNumbers) { + try { + int bonusNumber = Integer.parseInt(input); + if (bonusNumber < 1 || bonusNumber > 45) { + throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + if (winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException("[ERROR] 보너스 번호는 당첨 번호와 중복될 수 없습니다."); + } + return bonusNumber; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] 보너스 번호는 숫자여야 합니다."); + } } -} +} \ No newline at end of file diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 88fc5cf12b..7fe4d27e9f 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -1,6 +1,8 @@ package lotto; import java.util.List; +import java.util.Collections; +import java.util.HashSet; public class Lotto { private final List numbers; @@ -14,7 +16,41 @@ private void validate(List numbers) { if (numbers.size() != 6) { throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다."); } + + for (int number : numbers) { + if (number < 1 || number > 45) { + throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + } + + if (new HashSet<>(numbers).size() != 6) { + throw new IllegalArgumentException("[ERROR] 로또 번호는 중복될 수 없습니다."); + } + } + + @Override + public String toString() { + List sortedNumbers = numbers.stream().sorted().toList(); + return sortedNumbers.toString(); } - // TODO: 추가 기능 구현 -} + public List getNumbers() { + return numbers; + } + + public Rank checkRank(List winningNumbers, int bonusNumber) { + int matchCount = countMatches(winningNumbers); + boolean bonusMatch = numbers.contains(bonusNumber); + return Rank.valueOf(matchCount, bonusMatch); + } + + private int countMatches(List winningNumbers) { + int count = 0; + for (int number : numbers) { + if (winningNumbers.contains(number)) { + count++; + } + } + return count; + } +} \ No newline at end of file diff --git a/src/main/java/lotto/Rank.java b/src/main/java/lotto/Rank.java new file mode 100644 index 0000000000..287f6b2052 --- /dev/null +++ b/src/main/java/lotto/Rank.java @@ -0,0 +1,51 @@ +package lotto; + +public enum Rank { + FIRST(6, false, 2_000_000_000), + SECOND(5, true, 30_000_000), + THIRD(5, false, 1_500_000), + FOURTH(4, false, 50_000), + FIFTH(3, false, 5_000), + NONE(0, false, 0); + + private final int matchCount; + private final boolean bonusMatch; + private final int prize; + + Rank(int matchCount, boolean bonusMatch, int prize) { + this.matchCount = matchCount; + this.bonusMatch = bonusMatch; + this.prize = prize; + } + + public static Rank valueOf(int matchCount, boolean bonusMatch) { + if (matchCount == 6) { + return FIRST; + } + if (matchCount == 5 && bonusMatch) { + return SECOND; + } + if (matchCount == 5) { + return THIRD; + } + if (matchCount == 4) { + return FOURTH; + } + if (matchCount == 3) { + return FIFTH; + } + return NONE; + } + + public int getPrize() { + return prize; + } + + public int getMatchCount() { + return matchCount; + } + + public boolean isBonusMatch() { + return bonusMatch; + } +} \ No newline at end of file