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
67 changes: 67 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 구현할 기능 목록
- [X] 구입 금액을 입력받고, 구입 금액에 해당하는 만큼 로또를 발행한다.
- [X] 로또 1장의 가격은 1,000원이다. 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다.


- [X] 발행한 로또 수량 및 번호를 출력한다.
- [X] 로또 번호는 오름차순으로 정렬하여 보여준다.
- [X] 로또 번호의 숫자 범위는 1~45까지이다.
- [X] 1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다.


- [X] 당첨 번호를 입력받는다.
- [X] 1 이상 45 이하, 중복되지 않는 6개의 숫자인지 검증


- [X] 보너스 번호를 입력받는다.
- [X] 1 이상 45 이하 이고, 당첨 번호와 중복되지 않는 숫자 여야 함


- [X] 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 게임을 종료한다.
- [X] 당첨 내역을 출력한다.
- [X] 당첨은 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원

- [X] 수익률을 출력한다.
- [X] 수익률은 소수점 둘째 자리에서 반올림한다.
- (ex. 100.0%, 51.5%, 1,000,000.0%)


- [X] 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.


---
## 전체 흐름
```
구입금액을 입력해 주세요.
8000

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]

당첨 번호를 입력해 주세요.
1,2,3,4,5,6

보너스 번호를 입력해 주세요.
7

당첨 통계
---
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%입니다.
```
7 changes: 6 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package lotto;

import camp.nextstep.edu.missionutils.Console;
import lotto.controller.MainController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
MainController mainController = MainController.create();
mainController.run();
Console.close();
}
}
20 changes: 0 additions & 20 deletions src/main/java/lotto/Lotto.java

This file was deleted.

8 changes: 8 additions & 0 deletions src/main/java/lotto/constants/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package lotto.constants;

public class Constants {
public static final int LOTTO_PRICE = 1000;
public static final int MIN_LOTTO_NUMBER = 1;
public static final int MAX_LOTTO_NUMBER = 45;
public static final int LOTTO_NUMBERS_SIZE = 6;
}
83 changes: 83 additions & 0 deletions src/main/java/lotto/controller/MainController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package lotto.controller;

import lotto.domain.*;
import lotto.dto.LottosDto;
import lotto.dto.ResultDto;
import lotto.service.LottoMaker;
import lotto.utils.Mapper;
import lotto.view.InputView;
import lotto.view.OutputView;

import java.util.List;
import java.util.function.Supplier;

public class MainController {
private final InputView inputView;
private final OutputView outputView;

private MainController(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public static MainController create() {
return new MainController(InputView.getInstance(), OutputView.getInstance());
}

public void run() {
PurchaseAmount purchaseAmount = createPurchaseAmount();
Lottos lottos = createLottos(purchaseAmount);
printLottos(lottos);
WinningLotto winningLotto = createWinningLotto();
printResult(lottos, winningLotto, purchaseAmount);
}

private PurchaseAmount createPurchaseAmount() {
return readUserInput(() -> {
long input = inputView.readPurchaseAmount();
return PurchaseAmount.from(input);
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 인라인으로 처리해도 될 것 같아요!!

}

private Lottos createLottos(PurchaseAmount purchaseAmount) {
long quantityOfLotto = purchaseAmount.getQuantityOfLotto();
List<Lotto> lottosMade = LottoMaker.makeLottos(quantityOfLotto);
return Lottos.from(lottosMade);
}

private void printLottos(Lottos lottos) {
LottosDto lottosDto = Mapper.toLottosDto(lottos);
outputView.printLottos(lottosDto);
}

private WinningLotto createWinningLotto() {
Lotto winningNumbers = createWinningNumbers();
return readUserInput(() -> {
int bonusNumber = inputView.readBonusNumber();
return WinningLotto.of(winningNumbers, bonusNumber);
});
}

private Lotto createWinningNumbers() {
return readUserInput(() -> {
List<Integer> numbers = inputView.readWinningNumbers();
return new Lotto(numbers);
});
}

private void printResult(Lottos lottos, WinningLotto winningLotto, PurchaseAmount purchaseAmount) {
RankResult rankResult = lottos.findRanks(winningLotto);
ResultDto resultDto = Mapper.toTotalRankDto(purchaseAmount, rankResult);
outputView.printResult(resultDto);
}

private <T> T readUserInput(Supplier<T> supplier) {
while (true) {
try {
return supplier.get();
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
}
}
}
}
57 changes: 57 additions & 0 deletions src/main/java/lotto/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package lotto.domain;

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

import static lotto.constants.Constants.*;
import static lotto.exception.ErrorMessage.*;

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

public Lotto(List<Integer> numbers) {
validate(numbers);
this.numbers = numbers;
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

number를 별도의 클래스로 묶어주고 이를 vo로 설정해주면 비교가 편해질것 같아요

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

private static void validateSize(List<Integer> numbers) {
if (numbers.size() != LOTTO_NUMBERS_SIZE) {
throw new IllegalArgumentException(INVALID_LOTTO_NUMBERS_SIZE.getMessage());
}
}

private void validateDuplicates(List<Integer> numbers) {
HashSet<Integer> uniqueNumbers = new HashSet<>(numbers);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private boolean isDuplicated(List<Integer> numbers){
      return new HashSet<>(numbers)
                 .size() < numbers.size();
}
if(isDuplicated(numbers)){
...
}

이렇게 구성할 수 있을 거 같아요

if (numbers.size() != uniqueNumbers.size()) {
throw new IllegalArgumentException(DUPLICATED_LOTTO_NUMBERS.getMessage());
}
}

private void validateRange(List<Integer> numbers) {
boolean isOutOfRange = numbers.stream()
.anyMatch(number -> number < MIN_LOTTO_NUMBER || number > MAX_LOTTO_NUMBER);
if (isOutOfRange) {
throw new IllegalArgumentException(INVALID_LOTTO_NUMBER_RANGE.getMessage());
}
}

public int getMatchCount(Lotto lotto) {
return (int) numbers.stream()
.filter(lotto.numbers::contains)
.count();
}

public boolean contains(int number) {
return numbers.contains(number);
}

public List<Integer> getNumbers() {
return List.copyOf(numbers);
}
}
40 changes: 40 additions & 0 deletions src/main/java/lotto/domain/LottoRank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package lotto.domain;

import java.util.Arrays;
import java.util.Optional;

public enum LottoRank {
FIFTH(3, false,5_000),
FOURTH(4, false,50_000),
THIRD(5, false,1_500_000),
SECOND(5, true, 30_000_000),
FIRST(6, false, 2_000_000_000);

private final int matchCount;
private final boolean requiresBonusMatch;
private final int prize;

LottoRank(int matchCount, boolean requiresBonusMatch, int prize) {
this.matchCount = matchCount;
this.requiresBonusMatch = requiresBonusMatch;
this.prize = prize;
}

public static Optional<LottoRank> findByMatchResult(int matchCount, boolean bonusMatch) {
return Arrays.stream(LottoRank.values())
.filter(rank -> rank.matchCount == matchCount && (matchCount != 5 || rank.requiresBonusMatch == bonusMatch))
.findAny();
}

public int getMatchCount() {
return matchCount;
}

public boolean isRequiresBonusMatch() {
return requiresBonusMatch;
}

public int getPrize() {
return prize;
}
}
34 changes: 34 additions & 0 deletions src/main/java/lotto/domain/Lottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package lotto.domain;

import java.util.List;
import java.util.Optional;

public class Lottos {
private final List<Lotto> lottos;

private Lottos(List<Lotto> lottos) {
this.lottos = lottos;
}

public static Lottos from(List<Lotto> lottos) {
return new Lottos(lottos);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생성자와 똑같은 역할을 하는 정적 팩토리 메서드가 필요하진 않을 것 같습니다!

}

public List<Lotto> getLottos() {
return List.copyOf(lottos);
}

public RankResult findRanks(WinningLotto winningLotto) {
List<LottoRank> lottoRanks = lottos.stream()
.map(lotto -> LottoRank.findByMatchResult(
lotto.getMatchCount(winningLotto.getWinningNumbers()),
lotto.contains(winningLotto.getBonusNumber())))
.flatMap(Optional::stream)
.toList();
return RankResult.from(lottoRanks);
}

public long getQuantity() {
return lottos.size();
}
}
27 changes: 27 additions & 0 deletions src/main/java/lotto/domain/PurchaseAmount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lotto.domain;

import lotto.utils.PurchaseAmountValidator;

import static lotto.constants.Constants.LOTTO_PRICE;

public class PurchaseAmount {
private final long amount;

private PurchaseAmount(long amount) {
this.amount = amount;
}

public static PurchaseAmount from(long amount) {
PurchaseAmountValidator.validatePositive(amount);
PurchaseAmountValidator.validateDividedByUnit(amount);
return new PurchaseAmount(amount);
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate로직을 lotto 도메인과 다르게 개별 클래스로 분리를 한 이유가 있을까요?

public long getQuantityOfLotto() {
return amount / LOTTO_PRICE;
}

public long getAmount() {
return amount;
}
}
27 changes: 27 additions & 0 deletions src/main/java/lotto/domain/RankResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lotto.domain;

import java.util.List;

public class RankResult {
private final List<LottoRank> lottoRanks;

private RankResult(List<LottoRank> lottoRanks) {
this.lottoRanks = lottoRanks;
}

public static RankResult from(List<LottoRank> lottoRanks) {
return new RankResult(lottoRanks);
}

public long getRankCount(LottoRank rank) {
return lottoRanks.stream()
.filter(lottoRank -> lottoRank == rank)
.count();
}

public long getTotalPrize() {
return lottoRanks.stream()
.mapToLong(LottoRank::getPrize)
.sum();
}
}
Loading