Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
# java-lotto-precourse

# 기능 구현목록
- [X] 로또 구입 예산 입력기능
- [X] 로또 구입 개수 출력기능
- [X] 구입금액메시지 출력 기능
- [X] 구입예산에 따른 로또 추출기능
- [X] 추출된 로또 조합 출력기능
- [X] 발행한 로또 수량,번호 출력 기능
- [X] 당첨번호 입력 받는 기능
- [X] 로또 당첨번호 검증 기능
- [X] 입력받은 당첨번호 출력 기능
- [X] 보너스번호 입력 받는 기능
- [X] 보너스번호 입력메시지 출력기능
- [X] 보너스 번호 검증기능
- [X] 당첨내역 계산 기능
- [X] 당첨내역 출력 기능
- [X] 수익률 계산 기능
- [X] 수익률 출력 기능
270 changes: 270 additions & 0 deletions src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,277 @@
package lotto;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


enum Messages {
PURCHASEABLE_INPUT_MESSAGE("구입금액을 입력해 주세요."),
ANSWER_INPUT_MESSAGE("당첨 번호를 입력해 주세요."),
BONUS_INPUT_MESSAGE("보너스 번호를 입력해 주세요."),
STATISTICS_HEADER("당첨 통계\n---"),
ERROR_MESSAGE("[ERROR] ");

private final String description;

Messages(String description) {
this.description = description;
}

public String getDescription() {
return description;
}
}

enum Prize {
FIRST(6, 2_000_000_000, "6개 일치"),
SECOND(5, 30_000_000, "5개 일치, 보너스 볼 일치"),
THIRD(5, 1_500_000, "5개 일치"),
FOURTH(4, 50_000, "4개 일치"),
FIFTH(3, 5_000, "3개 일치"),
NONE(0, 0, "일치하는 번호 없음");

private final int matchCount;
private final int prizeMoney;
private final String description;

Prize(int matchCount, int prizeMoney, String description) {
this.matchCount = matchCount;
this.prizeMoney = prizeMoney;
this.description = description;
}

public static Prize of(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 NONE;
}

public int getPrizeMoney() {
return prizeMoney;
}

public String getDescription() {
return description;
}
}

record WinningStatistics(
Map<Prize, Integer> prizeCount,
double profitRate
) {}

public class Application {
private static final int TICKET_PRICE = 1000;

public static void main(String[] args) {
// TODO: 프로그램 구현
try {
// 예산 입력받기 기능
int totalLottoCnt = getPurchaseableLottoNum(getBudgetInput());

// 입력받은 금액 출력 기능
printTotalLottoNum(totalLottoCnt);

// 로또 추출기능
List<List<Integer>> extractedLottoNums = extractLottoNumbers(totalLottoCnt);

// 당첨번호 입력받기
Lotto lotto = new Lotto(getJackpotInput());

// 보너스 번호 입력받기
int bonusNum = lotto.addBonusNumbers(getBonusNumber());

// 당첨 여부 판단
WinningStatistics statistics = calculateResults(extractedLottoNums, lotto.getNumbers(), bonusNum);

// 당첨 여부 출력
printStatistics(statistics);
} catch (IllegalArgumentException e) {
System.out.println(Messages.ERROR_MESSAGE.getDescription() + e.getMessage());
}
}

// 예산 입력받기 기능구현
public static Integer getPurchaseableLottoNum(int budget) {
if (budget < TICKET_PRICE) {
throw new IllegalArgumentException("구입 금액은 1000원 이상이어야 합니다.");
}
return budget / TICKET_PRICE;
}

// 입력받은 금액 출력 기능
public static void printTotalLottoNum(int totalLottoCNt){
System.out.println(totalLottoCNt + "개를 구매했습니다.");
}

// 구입금액 메시지 출력기능
public static void printPurchaseableInputMessage(){
System.out.println(Messages.PURCHASEABLE_INPUT_MESSAGE.getDescription());
}

public static Integer getBudgetInput() {
printPurchaseableInputMessage();
String input = camp.nextstep.edu.missionutils.Console.readLine();
validateNumericInput(input);
return Integer.parseInt(input);
}

// 입력값 검증기능
private static void validateNumericInput(String input) {
if (!input.matches("\\d+")) {
throw new IllegalArgumentException("숫자만 입력 가능합니다.");
}
}

// 로또 추출기능
public static List<List<Integer>> extractLottoNumbers(int totalLottoCnt) {
List<List<Integer>> lottos = new ArrayList<>();
for (int i = 0; i < totalLottoCnt; i++) {
List<Integer> numbers = camp.nextstep.edu.missionutils.Randoms.pickUniqueNumbersInRange(1, 45, 6);
printExtractedLottoNumbers(numbers);
lottos.add(numbers);
}
return lottos;
}

// 추출된 로또번호 출력기능
public static void printExtractedLottoNumbers(List<Integer> numbers){
System.out.println(numbers);
}

// 로또 당첨번호 입력 기능
public static List<Integer> getJackpotInput() {
pringJackpotInputMessage();
String input = camp.nextstep.edu.missionutils.Console.readLine();
return parseAndValidateNumbers(input);
}

// 로또 당첨번호 입력메시지 출력 기능
public static void pringJackpotInputMessage(){
System.out.println(Messages.ANSWER_INPUT_MESSAGE.getDescription());
}

private static List<Integer> parseAndValidateNumbers(String input) {
try {
List<Integer> numbers = Arrays.stream(input.split(","))
.map(String::trim)
.map(Integer::parseInt)
.collect(Collectors.toList());
validateLottoNumbers(numbers);
return numbers;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("올바른 숫자 형식이 아닙니다.");
}
}

// 로또 번호 형태 검증기능
private static void validateLottoNumbers(List<Integer> numbers) {
if (numbers.size() != 6) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
if (numbers.stream().distinct().count() != 6) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다.");
}
if (numbers.stream().anyMatch(n -> n < 1 || n > 45)) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이여야 합니다.");
}
}

// 보너스 번호 입력받기
public static Integer getBonusNumber() {
printBonumInputMessage();
String input = camp.nextstep.edu.missionutils.Console.readLine().trim();
validateNumericInput(input);
int number = Integer.parseInt(input);
if (number < 1 || number > 45) {
throw new IllegalArgumentException("보너스 번호는 1부터 45 사이여야 합니다.");
}
return number;
}

// 보너스 번호입력 메시지 출력기능
public static void printBonumInputMessage(){
System.out.println(Messages.BONUS_INPUT_MESSAGE.getDescription());
}

private static WinningStatistics calculateResults(
List<List<Integer>> purchasedLottos,
List<Integer> winningNumbers,
int bonusNumber
) {
Map<Prize, Integer> prizeCount = initializePrizeCount();

for (List<Integer> lotto : purchasedLottos) {
int matchCount = countMatches(lotto, winningNumbers);
boolean matchBonus = (matchCount == 5) && lotto.contains(bonusNumber);
Prize prize = Prize.of(matchCount, matchBonus);
prizeCount.merge(prize, 1, Integer::sum);
}

int totalPrize = calculateTotalPrize(prizeCount);
double profitRate = calculateProfitRate(purchasedLottos.size() * TICKET_PRICE, totalPrize);

return new WinningStatistics(prizeCount, profitRate);
}

private static Map<Prize, Integer> initializePrizeCount() {
return Arrays.stream(Prize.values())
.collect(Collectors.toMap(
prize -> prize,
prize -> 0,
(prev, current) -> current,
HashMap::new
));
}

private static int countMatches(List<Integer> lotto, List<Integer> winningNumbers) {
return (int) lotto.stream()
.filter(winningNumbers::contains)
.count();
}

private static int calculateTotalPrize(Map<Prize, Integer> prizeCount) {
return prizeCount.entrySet().stream()
.mapToInt(entry -> entry.getKey().getPrizeMoney() * entry.getValue())
.sum();
}

private static double calculateProfitRate(int totalCost, int totalPrize) {
return ((double) totalPrize / totalCost) * 100;
}

private static void printStatistics(WinningStatistics statistics) {
System.out.println(Messages.STATISTICS_HEADER.getDescription());

Arrays.stream(Prize.values())
.filter(prize -> prize != Prize.NONE)
.forEach(prize -> printPrizeResult(prize, statistics.prizeCount().get(prize)));

System.out.printf("총 수익률은 %.1f%%입니다.%n", statistics.profitRate());
}

private static void printPrizeResult(Prize prize, int count) {
System.out.printf("%s (%,d원) - %d개%n",
prize.getDescription(),
prize.getPrizeMoney(),
count);
}

}
43 changes: 41 additions & 2 deletions src/main/java/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package lotto;

import java.util.HashSet;
import java.util.List;

public class Lotto {
Expand All @@ -11,10 +12,48 @@ public Lotto(List<Integer> numbers) {
}

private void validate(List<Integer> numbers) {
validateSize(numbers);
validateDuplicate(numbers);
validateRange(numbers);
}

public List<Integer> getNumbers() {
return numbers;
}

private void validateSize(List<Integer> numbers) {
if (numbers.size() != 6) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다.");
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
}

// TODO: 추가 기능 구현
private void validateDuplicate(List<Integer> numbers) {
if (new HashSet<>(numbers).size() != 6) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다.");
}
}

private void validateRange(List<Integer> numbers) {
if (numbers.stream().anyMatch(num -> num < 1 || num > 45)) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이여야 합니다.");
}
}

// 보너스 번호 생성
public int addBonusNumbers(int bonusNumber) {
validateBonusNumber(bonusNumber);
return bonusNumber;
}

// 보너스 번호 검증기능
private void validateBonusNumber(int bonusNumber) {
if (numbers.contains(bonusNumber)) {
throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다.");
}
if (bonusNumber < 1 || bonusNumber > 45) {
throw new IllegalArgumentException("보너스 번호는 1부터 45 사이여야 합니다.");
}
}


}
Loading