Skip to content
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,59 @@
# java-planetlotto-precourse


## 기본 요구 사항

### 로또 구매

- [x] 구입금액 입력 프롬프트를 출력한다.
- 메시지: "구입금액을 입력해 주세요."
- [x] 구입금액을 입력받는다.
- [x] `IllegalArgumentException`: 정수가 아닌 경우
- [x] `IllegalArgumentException`: 500의 배수인 자연수가 아닌 경우
- [x] 로또 구매 개수를 출력한다.
- 메시지: 개수 + "개를 구매했습니다."
- [x] 구매 개수만큼 로또를 발행한다.
- [x] 각 로또에 대해, 1~30 사이의 중복되지 않는 숫자 5개를 뽑는다.
- [x] 각 로또에 대해 오름차순으로 정렬해, 구매한 로또들의 번호를 출력한다.
- 메시지 예시: "[8, 11, 13, 21, 22]"

### 로또 추첨

- [x] 당첨 번호 입력 프롬프트를 출력한다.
- 메시지: "당첨 번호를 입력해 주세요."
- [x] 당첨 번호를 쉼표를 기준으로 5개 입력받는다.
- [x] `IllegalArgumentException`: 정수가 아닌게 포함된 경우
- [x] `IllegalArgumentException`: 1~30 사이가 아닌 값이 포함된 경우
- [x] `IllegalArgumentException`: 중복된 숫자가 포함된 경우
- [x] 보너스 번호를 입력받는다.
- [x] `IllegalArgumentException`: 정수가 아닌 경우
- [x] `IllegalArgumentException`: 1~30 사이가 아닌 경우
- [x] `IllegalArgumentException`: 당첨 번호에 같은 숫자가 포함 경우
- [x] 당첨을 진행한다.
- [x] 각 로또에 대해, 당첨번호 + 보너스 번호와 비교하여 등수를 계산한다.
- 1등: 5개 번호 일치 / 100,000,000원
- 2등: 4개 번호 + 보너스 번호 일치 / 10,000,000원
- 3등: 4개 번호 일치 / 1,500,000원
- 4등: 3개 번호 일치 + 보너스 번호 일치 / 500,000원
- 5등: 2개 번호 일치 + 보너스 번호 일치 / 5,000원
- 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다.

### 당첨 통계

- [x] 각 등수 별로, 해당되는 개수를 계산한다.
- [x] 1등부터 차례대로, 등수 별 해당되는 개수를 출력한다.
- 메시지: 등수 + "개 일치 (" + String.format("%,d", 상금) + "원) - " + 해당_개수 + "개"

## 기능 확장

### 목표
사용자 경험 개선

### 문제 상황
- 전체 프로세스에 대한 설명이 부족하다.
- 어떤 식으로 어떻게 진행되는지에 대해, 처음에 설명하도록 한다.

### 로또 규칙 설명
- [x] 프로그램 시작 시, 프로그램에 대한 소개를 한다.
- [x] 전체적인 절차를 설명한다.
- [x] 로또의 가격이나 당첨 기준과 같은 규칙을 설명한다.
4 changes: 4 additions & 0 deletions src/main/java/planetlotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package planetlotto;

import planetlotto.controller.PlanetLottoController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
PlanetLottoController planetLottoController = new PlanetLottoController();
planetLottoController.run();
}
}
70 changes: 70 additions & 0 deletions src/main/java/planetlotto/controller/PlanetLottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package planetlotto.controller;

import java.util.List;
import planetlotto.model.lotto.Lottos;
import planetlotto.model.lotto.BonusNumber;
import planetlotto.model.lotto.Lotto;
import planetlotto.model.winningDetail.WinningDetail;
import planetlotto.view.InputView;
import planetlotto.view.OutputView;

public class PlanetLottoController {

public PlanetLottoController() {
}

public void run() {
Lottos lottos = purchaseLottos();
OutputView.printPurchasedLottos(lottos.getElementsForOutput());

Lotto winningLotto = inputWinningLotto();
BonusNumber bonusNumber = inputBonusNumber(winningLotto);

WinningDetail winningDetail = lottos.computeWinningDetailWith(winningLotto, bonusNumber);

OutputView.printResult(winningDetail.getCountsByRankForOutput());
}

private Lottos purchaseLottos() {
Lottos lottos;

try {
OutputView.printIntroduction();
int purchaseAmount = InputView.askAmount();
lottos = new Lottos(purchaseAmount);
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
lottos = purchaseLottos();
}

return lottos;
}

private Lotto inputWinningLotto() {
Lotto winningLotto;

try {
List<Integer> winningNumbers = InputView.askWinningLotto();
winningLotto = new Lotto(winningNumbers);
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
winningLotto = inputWinningLotto();
}

return winningLotto;
}

private BonusNumber inputBonusNumber(Lotto winningLotto) {
BonusNumber bonusNumber;

try {
int bonusNumberInput = InputView.askBonusNumber();
bonusNumber = new BonusNumber(bonusNumberInput, winningLotto);
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
bonusNumber = inputBonusNumber(winningLotto);
}

return bonusNumber;
}
}
9 changes: 9 additions & 0 deletions src/main/java/planetlotto/model/LottoRules.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package planetlotto.model;

public class LottoRules {

public static final int LOTTO_PRICE = 500;
public static final int LOTTO_START_NUMBER = 1;
public static final int LOTTO_END_NUMBER = 30;
public static final int LOTTO_NUMBER_COUNT = 5;
}
30 changes: 30 additions & 0 deletions src/main/java/planetlotto/model/lotto/BonusNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package planetlotto.model.lotto;

import static planetlotto.model.LottoRules.LOTTO_END_NUMBER;
import static planetlotto.model.LottoRules.LOTTO_START_NUMBER;

import planetlotto.model.LottoRules;

public class BonusNumber {

private final int value;

public BonusNumber(int value, Lotto winningLotto) {
validateValue(value, winningLotto);
this.value = value;
}

private void validateValue(int value, Lotto winningLotto) {
if (value < LottoRules.LOTTO_START_NUMBER || value > LottoRules.LOTTO_END_NUMBER) {
throw new IllegalArgumentException("보너스 번호는 " + LOTTO_START_NUMBER + "에서 " + LOTTO_END_NUMBER + " 사이여야 합니다.");
}

if (winningLotto.getNumbers().contains(value)) {
throw new IllegalArgumentException("보너스 번호는 당첨 번호의 모든 숫자와 달라야 합니다.");
}
}

public int getValue() {
return value;
}
}
56 changes: 56 additions & 0 deletions src/main/java/planetlotto/model/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package planetlotto.model.lotto;

import static planetlotto.model.LottoRules.LOTTO_END_NUMBER;
import static planetlotto.model.LottoRules.LOTTO_NUMBER_COUNT;
import static planetlotto.model.LottoRules.LOTTO_START_NUMBER;

import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class Lotto {
private final List<Integer> numbers;

public Lotto(List<Integer> numbers) {
validateNumbers(numbers);
this.numbers = numbers.stream().sorted().toList();
}

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

if (new HashSet<>(numbers).size() < numbers.size()) {
throw new IllegalArgumentException("중복된 번호가 포함됐습니다.");
}

numbers.forEach(Lotto::validateNumberRange);
}

public static void validateNumberRange(int number) {
if (number < LOTTO_START_NUMBER || number > LOTTO_END_NUMBER) {
throw new IllegalArgumentException("로또 번호는 " + LOTTO_START_NUMBER + "에서 " + LOTTO_END_NUMBER + " 사이여야 합니다.");
}
}

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

public int countMatchingNumbersWith(Lotto winningLotto) {
List<Integer> winningNumbers = winningLotto.getNumbers();
AtomicInteger count = new AtomicInteger();

numbers.stream()
.filter(winningNumbers::contains)
.forEach(other -> count.getAndIncrement());

return count.get();
}

public boolean isMatchedWith(BonusNumber bonusNumber) {
return numbers.contains(bonusNumber.getValue());
}
}

62 changes: 62 additions & 0 deletions src/main/java/planetlotto/model/lotto/Lottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package planetlotto.model.lotto;

import static planetlotto.model.LottoRules.LOTTO_END_NUMBER;
import static planetlotto.model.LottoRules.LOTTO_NUMBER_COUNT;
import static planetlotto.model.LottoRules.LOTTO_PRICE;
import static planetlotto.model.LottoRules.LOTTO_START_NUMBER;

import camp.nextstep.edu.missionutils.Randoms;
import java.util.ArrayList;
import java.util.List;
import planetlotto.model.winningDetail.WinningDetail;

public class Lottos {
private final int purchaseAmount;
private final List<Lotto> elements;

public Lottos(int purchaseAmount) {
validatePurchaseAmount(purchaseAmount);

this.purchaseAmount = purchaseAmount;
this.elements = createElements();
}

public Lottos(List<Lotto> elements) {
this.elements = elements;
this.purchaseAmount = elements.size() * LOTTO_PRICE;
}

private void validatePurchaseAmount(int purchaseAmount) {
if ((purchaseAmount < LOTTO_PRICE) || ((purchaseAmount % LOTTO_PRICE) != 0)) {
throw new IllegalArgumentException("구입금액은 " + LOTTO_PRICE + "원 단위여야 합니다: " + purchaseAmount);
}
}

private List<Lotto> createElements() {
List<Lotto> elements = new ArrayList<>();
int count = purchaseAmount / LOTTO_PRICE;

for (int i = 0; i < count; i++) {
elements.add(new Lotto(
Randoms.pickUniqueNumbersInRange(LOTTO_START_NUMBER, LOTTO_END_NUMBER, LOTTO_NUMBER_COUNT))
);
}

return elements;
}

public List<Lotto> getElements() {
return elements;
}

public List<List<Integer>> getElementsForOutput() {
return elements.stream()
.map(Lotto::getNumbers)
.toList();
}

public WinningDetail computeWinningDetailWith(Lotto winningLotto, BonusNumber bonusNumber) {
return new WinningDetail(this, winningLotto, bonusNumber);
}

}
46 changes: 46 additions & 0 deletions src/main/java/planetlotto/model/winningDetail/Rank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package planetlotto.model.winningDetail;

public enum Rank {
FIRST(1,5, false, 100000000),
SECOND(2,4, true, 10000000),
THIRD(3,4, false, 1500000),
FIRTH(4, 3, false, 500000),
FIFTH(5, 2, false, 5000),
NONE(0, 0, false, 0);

private final int order;
private final int matchingCount;
private final boolean isMatchedWithBonusNumber;
private final int prize;

Rank(int order, int matchingCount, boolean isMatchedWithBonusNumber, int prize) {
this.order = order;
this.matchingCount = matchingCount;
this.isMatchedWithBonusNumber = isMatchedWithBonusNumber;
this.prize = prize;
}

public int getOrder() {
return order;
}

public int getPrize() {
return prize;
}

public static Rank of(int matchingCount, boolean isMatchedWithBonusNumber) {
if (matchingCount == 5) {
return FIRST;
} else if (matchingCount == 4 && isMatchedWithBonusNumber) {
return SECOND;
} else if (matchingCount == 4) {
return THIRD;
} else if (matchingCount == 3) {
return FIRTH;
} else if (matchingCount == 2) {
return FIFTH;
}

return NONE;
}
}
Loading