Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,44 @@
# java-lotto-precourse
### 간단한 로또 발매기 구현
- 로또 구입 금액을 입력 받는다. 로또 1장의 가격은 1,000원이다. 구입 금액에 해당하는 만큼 로또를 발행해야 한다.
- 구입 금액은 1,000원 단위로 입력 받으며 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다.
> 구입금액을 입력해 주세요.
8000
- 1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다.
- 로또 번호의 숫자 범위는 1~45까지이다.
- 발행한 로또 수량 및 번호를 출력한다. 로또 번호는 오름차순으로 정렬하여 보여준다.
> 8개를 구매했습니다.
[8, 21, 23, 41, 42, 43]
[3, 5, 11, 16, 32, 38]
[7, 11, 16, 35, 36, 44]
[1, 8, 11, 31, 41, 42]
[13, 14, 16, 38, 42, 45]
[7, 11, 30, 40, 42, 43]
[2, 13, 22, 32, 38, 45]
[1, 3, 5, 14, 22, 45]
- 당첨 번호를 입력 받는다. 번호는 쉼표(,)를 기준으로 구분한다.
- 보너스 번호를 입력 받는다.
- 당첨 번호는 중복되지 않는 숫자 6개이고, 보너스 번호 1개도 중복되지 않아야 한다.
> 당첨 번호를 입력해 주세요.
1,2,3,4,5,6
보너스 번호를 입력해 주세요.
7
- 당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다.
- 1등: 6개 번호 일치 / 2,000,000,000원
- 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원
- 3등: 5개 번호 일치 / 1,500,000원
- 4등: 4개 번호 일치 / 50,000원
- 5등: 3개 번호 일치 / 5,000원
- 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다.
- 수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%)
> 당첨 통계
`---`
3개 일치 (5,000원) - 1개
4개 일치 (50,000원) - 0개
5개 일치 (1,500,000원) - 0개
5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
6개 일치 (2,000,000,000원) - 0개
총 수익률은 62.5%입니다.
- 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.
- Exception이 아닌 IllegalArgumentException, IllegalStateException 등과 같은 명확한 유형을 처리한다.
> [ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.
155 changes: 154 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,160 @@
package lotto;

import camp.nextstep.edu.missionutils.Console;

import java.util.ArrayList;
import java.util.List;

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

public static void main(String[] args) {
// TODO: 프로그램 구현
int purchaseAmount = getPurchaseAmount();
int numberOfLottos = purchaseAmount / LOTTO_PRICE;
System.out.println("\n" + numberOfLottos + "개를 구매했습니다.");

List<Lotto> lottos = new ArrayList<>();
for (int i = 0; i < numberOfLottos; i++) {
lottos.add(Lotto.generateRandomLotto());
}

for (Lotto lotto : lottos) {
System.out.println(lotto);
}

List<Integer> winningNumbers = inputWinningNumbers();
int bonusNumber = inputBonusNumber(winningNumbers);
printWinningStatistics(lottos, winningNumbers, bonusNumber, purchaseAmount);
}

private static int getPurchaseAmount() {
while (true) {
try {
System.out.println("구입금액을 입력해 주세요.");
String input = Console.readLine();
int amount = Integer.parseInt(input);

if (amount % LOTTO_PRICE != 0) {
throw new IllegalArgumentException();
}
return amount;
} catch (NumberFormatException e) {
System.out.println("[ERROR] 구입 금액은 숫자로 입력해야 합니다.");
} catch (IllegalArgumentException e) {
System.out.println("[ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다.");
}
}
}

private static List<Integer> inputWinningNumbers() {
List<Integer> winningNumbers = new ArrayList<>();
while (winningNumbers.size() < 6) {
try {
System.out.println("\n당첨 번호를 입력해 주세요.");
String winningInput = Console.readLine();
winningNumbers = parseNumbers(winningInput);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
return winningNumbers;
}

private static int inputBonusNumber(List<Integer> winningNumbers) {
while (true) {
try {
System.out.println("\n보너스 번호를 입력해 주세요.");
int bonusNumber = Integer.parseInt(Console.readLine());
validateBonusNumber(winningNumbers, bonusNumber);
return bonusNumber; // 유효한 보너스 번호가 입력된 경우 반환
} catch (NumberFormatException e) {
System.out.println("[ERROR] 보너스 번호는 숫자로 입력해야 합니다.");
}
}
}

private static List<Integer> parseNumbers(String input) {
String[] numbers = input.split(",");
List<Integer> winningNumbers = new ArrayList<>();

for (String number : numbers) {
int num = Integer.parseInt(number.trim());
validateLottoNumber(num); // 각 번호에 대해 유효성 검사
winningNumbers.add(num);
}
return winningNumbers;
}

private static void validateLottoNumber(int number) {
if (number < 1 || number > 45) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}

private static void validateBonusNumber(List<Integer> winningNumbers, int bonusNumber) {
if (bonusNumber < 1 || bonusNumber > 45) {
throw new IllegalArgumentException("[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다.");
}
if (winningNumbers.contains(bonusNumber)) {
throw new IllegalArgumentException("[ERROR] 보너스 번호는 당첨 번호와 중복될 수 없습니다.");
}
}

private static void printWinningStatistics(List<Lotto> lottos, List<Integer> winningNumbers, int bonusNumber, int purchaseAmount) {
int[] counts = new int[5]; // [3개, 4개, 5개, 5개+보너스, 6개]
int totalPrize = 0;

for (Lotto lotto : lottos) {
int matchCount = countMatches(lotto.getNumbers(), winningNumbers);
boolean bonusMatch = lotto.getNumbers().contains(bonusNumber);

totalPrize += calculatePrize(counts, matchCount, bonusMatch);
}

// 출력
System.out.println("\n당첨 통계");
System.out.println("---");
printPrizeCounts(counts);

double profitRate = (double) totalPrize / purchaseAmount * 100;
System.out.printf("총 수익률은 %.1f%%입니다.\n", profitRate);
}

private static int calculatePrize(int[] counts, int matchCount, boolean bonusMatch) {
if (matchCount == 6) {
counts[4]++;
return 2000000000;
}
if (matchCount == 5 && bonusMatch) {
counts[3]++;
return 30000000;
}
if (matchCount == 5) {
counts[2]++;
return 1500000;
}
if (matchCount == 4) {
counts[1]++;
return 50000;
}
if (matchCount == 3) {
counts[0]++;
return 5000;
}
return 0; // 당첨되지 않은 경우
}

private static void printPrizeCounts(int[] counts) {
System.out.printf("3개 일치 (5,000원) - %d개\n", counts[0]);
System.out.printf("4개 일치 (50,000원) - %d개\n", counts[1]);
System.out.printf("5개 일치 (1,500,000원) - %d개\n", counts[2]);
System.out.printf("5개 일치, 보너스 볼 일치 (30,000,000원) - %d개\n", counts[3]);
System.out.printf("6개 일치 (2,000,000,000원) - %d개\n", counts[4]);
}

private static int countMatches(List<Integer> userNumbers, List<Integer> winningNumbers) {
return (int) userNumbers.stream()
.filter(winningNumbers::contains)
.count();
}
}
29 changes: 27 additions & 2 deletions src/main/java/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
package lotto;

import camp.nextstep.edu.missionutils.Randoms;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Lotto {
private static final int LOTTO_NUMBER_COUNT = 6;
private static final int MIN_NUMBER = 1;
private static final int MAX_NUMBER = 45;

private final List<Integer> numbers;

public Lotto(List<Integer> numbers) {
Expand All @@ -11,10 +20,26 @@ public Lotto(List<Integer> numbers) {
}

private void validate(List<Integer> numbers) {
if (numbers.size() != 6) {
if (numbers.size() != LOTTO_NUMBER_COUNT) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다.");
}
Set<Integer> uniqueNumbers = new HashSet<>(numbers);
if (uniqueNumbers.size() != LOTTO_NUMBER_COUNT) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 중복될 수 없습니다.");
}
}

public static Lotto generateRandomLotto() {
List<Integer> randomNumbers = Randoms.pickUniqueNumbersInRange(MIN_NUMBER, MAX_NUMBER, LOTTO_NUMBER_COUNT);
return new Lotto(randomNumbers.stream().sorted().collect(Collectors.toList()));
}

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

// TODO: 추가 기능 구현
@Override
public String toString() {
return numbers.toString();
}
}
2 changes: 2 additions & 0 deletions src/test/java/lotto/ApplicationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class ApplicationTest extends NsTest {
assertSimpleTest(() -> {
runException("1000j");
assertThat(output()).contains(ERROR_MESSAGE);
runException("1500");
assertThat(output()).contains(ERROR_MESSAGE);
});
}

Expand Down
6 changes: 5 additions & 1 deletion src/test/java/lotto/LottoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@ class LottoTest {
.isInstanceOf(IllegalArgumentException.class);
}

// TODO: 추가 기능 구현에 따른 테스트 코드 작성
@Test
void 로또_번호의_개수가_6개보다_작으면_예외가_발생한다() {
assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5)))
.isInstanceOf(IllegalArgumentException.class);
}
}