Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
16944f1
docs: 기본 feature 및 구조 적용
sjiwon Jul 15, 2023
9833710
feat: Lotto 생성과 관련되 ExceptionConstants 정의
sjiwon Jul 15, 2023
b8b17b4
feat: Lotto 생성 로직 구현
sjiwon Jul 15, 2023
132673f
test: Lotto 생성과 관련된 테스트 코드 작성
sjiwon Jul 15, 2023
b5f87e4
refactor: Lotto 관련 상수 컴포넌트 분리
sjiwon Jul 15, 2023
048e27c
feat: 1000원 단위 로또 1장 자동 발급을 위한 UserLotto 컴포넌트 구현
sjiwon Jul 15, 2023
3229f0e
feat: 구매한 N장 UserLotto 보관을 위한 UserLottos 컴포넌트 구현
sjiwon Jul 15, 2023
5dae29f
feat: 로또 당첨 번호 & 보너스 번호를 기록하는 LottoWinningMachine 구현
sjiwon Jul 15, 2023
a64a1f9
feat: 로또 번호에 따른 순위 결정을 위한 WinningRank 구현
sjiwon Jul 15, 2023
34471aa
refactor: Lotto 생성 시 numbers 정렬 로직 추가
sjiwon Jul 15, 2023
4b844b0
refactor: UserLotto 생성 시 random에 대한 의존성 분리 (LottoRandomGenerator)
sjiwon Jul 15, 2023
89dd6b3
feat: UserLottos 로또번호 주입을 위한 `@VisibleForTesting` 전용 생성자 추가
sjiwon Jul 15, 2023
f559c1a
feat: 로또 통계 추출을 위한 LottoStatistics 구현
sjiwon Jul 15, 2023
dfe2bc5
refactor: final 추가
sjiwon Jul 15, 2023
7caf7a5
refactor: 구매한 로또 개수를 얻기 위한 메소드 추가
sjiwon Jul 15, 2023
2081329
fix: 5등 당첨금 오류 수정
sjiwon Jul 15, 2023
8c5ec97
feat: 로또 당첨 수익률 계산 로직 구현
sjiwon Jul 15, 2023
ccc2319
feat: 사용자 Input에 대한 기본 Validator 구현
sjiwon Jul 15, 2023
debb63e
feat: 로또 구입금액 입력에 대한 검증 컴포넌트 구현
sjiwon Jul 15, 2023
03da5e1
refactor: validateInputIsNumeric Validator로 이동
sjiwon Jul 15, 2023
116f429
feat: 당첨번호 입력에 대한 검증 컴포넌트 구현
sjiwon Jul 15, 2023
62780ab
refactor: 당첨번호 검증 간 Exception 메시지 수정
sjiwon Jul 15, 2023
f6aeb7d
feat: 보너스 번호 입력에 대한 검증 컴포넌트 구현
sjiwon Jul 15, 2023
52e8417
feat: 사용자 입력에 대한 InputView 구현
sjiwon Jul 15, 2023
4400938
refactor: 미당첨자 matchCount 0으로 수정
sjiwon Jul 15, 2023
af69857
feat: UserLotto toString() 구현
sjiwon Jul 15, 2023
1be1d24
feat: 순위에 따른 당첨 개수 반환하는 메소드 추가
sjiwon Jul 15, 2023
c96e0e6
fix: UserLottos generate()시 unmodifiable 예외 처리를 위한 wrapping
sjiwon Jul 15, 2023
a9838c3
refactor: 구입 금액 입력 시 금액 -> 금액에 따른 로또 개수 반환으로 수정
sjiwon Jul 15, 2023
21bd2dc
feat: 게임 진행 시 콘솔 출력을 위한 OutputView 구현
sjiwon Jul 15, 2023
fdfaf44
feat: 콘솔 프로그램 진행을 위한 GameController 구현, Application 적용
sjiwon Jul 15, 2023
54f6ff5
docs: check clear feature
sjiwon Jul 15, 2023
488b96c
docs: validator 문서 추가
sjiwon Jul 15, 2023
fd0ce65
refactor: Lotto/UserLotto/UserLottos 계층에서 UserLotto Wrapping 제거
sjiwon Jul 15, 2023
ff34b25
docs: docs Model 수정
sjiwon Jul 15, 2023
944fc21
typo: getLottoPurchseCount -> getLottoPurchaseCount
sjiwon Jul 17, 2023
8bdd182
typo: WINNNG_FORMAT -> WINNING_FORMAT
sjiwon Jul 17, 2023
8fb3ec9
refactor: 메소드 순서 수정
sjiwon Jul 17, 2023
8668b15
refactor: `refineReward` 리턴 타입 Object -> String
sjiwon Jul 17, 2023
3c8a799
refactor: 로또 구입금액에 대한 `음수 검증 케이스` 추가
sjiwon Jul 17, 2023
2702a95
refactor: 당첨 보너스 번호에 대한 `BonusNumber 모델` 분리
sjiwon Jul 17, 2023
e9e86be
docs: `BonusNumber` docs 추가
sjiwon Jul 17, 2023
8574505
typo: baseballs -> numbers
sjiwon Jul 19, 2023
ff2a951
typo: LottoStatistics of -> checkLotteryResult
sjiwon Jul 19, 2023
a40cb93
fix: matchCount 6에 대한 bonusNumber 케이스 오류 수정
sjiwon Jul 19, 2023
05f3b14
refactor: 로또 당첨 통계 테스트 케이스 추가 및 이전 Calculation 오류 수정
sjiwon Jul 19, 2023
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
90 changes: 90 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
## Feature

- [X] User는 `1000원` 단위로 로또 N장을 구매한다
- 구매 후 로또 생성기로부터 N장의 로또를 생성한다
- `1000원` 단위로 입력하지 않을 경우 `IllegalArgumentException`를 발생시키고 `[Error]`로 시작하는 에러 메시지를 출력 후 `종료`한다

- [X] LottoWinningMachine은 로또 당첨 번호를 생성한다
- 총 7개의 중복되지 않는 숫자 (기본 당첨 번호 6개 + 보너스 번호 1개)

- [X] LottoStatistics는 N장의 로또에 대한 당첨 통계를 산출한다

<br>
<hr>

## Model

### `Lotto`

- 로또 번호 List를 추상화시킨 모델
- [X] 로또 번호는 1..45 범위 안에 존재해야 한다
- [X] 각 번호들은 중복되지 않아야 한다

### `UserLotto`

- `1000원` 단위의 로또 N장을 구매하는 컴포넌트
- `List<Lotto>`

### `LottoWinningMachine`

- 로또 당첨 번호 6개 + 보너스 번호를 생성하는 컴포넌트

### `BonusNumber`

- `보너스 번호`에 대한 컴포넌트

### `WinningRank`

- 당첨과 관련된 순위를 표현하는 컴포넌트

### `LottoStatistics`

- 로또 당첨 내역 및 수익률을 산출하기 위한 통계용 컴포넌트

<br>
<hr>

## Utils

### `ExceptionConstants`

- 전역 예외 메시지 통합 컴포넌트

### `LottoConstants`

- Lotto 숫자 범위 & 전체 사이즈 관련 상수 전용 컴포넌트

### `LottoRandomGenerator`

- 로또 번호 자동 생성기

### `Validator`

- 사용자 Input에 대한 기본 검증 컴포넌트
- `LottoPurchaseAmountValidator` -> 로또 구입금액 검증 컴포넌트
- `WinningNumberValidator` -> 로또 당첨번호 검증 컴포넌트
- `BonusNumberValidator` -> 보너스 번호 검증 컴포넌트

<br>
<hr>

## View

### `InputView`

- 사용자 Input을 받기 위한 컴포넌트

### `OutputView`

- 로또 게임 진행과 관련된 출력 컴포넌트

<br>
<hr>

## Controller

### `GameController`

- 로또 게임 진행과 관련된 컨트롤러

<br>
4 changes: 3 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package lotto;

import lotto.controller.GameController;

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

This file was deleted.

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

import lotto.model.LottoStatistics;
import lotto.model.LottoWinningMachine;
import lotto.model.UserLotto;
import lotto.view.InputView;
import lotto.view.OutputView;

import java.util.List;

public class GameController {
private static UserLotto userLotto;
private static LottoWinningMachine lottoWinningMachine;

public void run() {
try {
buyLotto();
initLottoWinningMachine();
displayLottoResult();
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
}
}

private void buyLotto() {
final int lottoPurchaseCount = InputView.readLottoPurchaseCount();
userLotto = UserLotto.issueLottoByPurchaseCount(lottoPurchaseCount);
OutputView.printPurchaseInformation(userLotto.getUserLottos());
}

private void initLottoWinningMachine() {
final List<Integer> winingNumbers = InputView.readWiningNumbers();
final int bonusNumber = InputView.readBonusNumber();
lottoWinningMachine = LottoWinningMachine.drawWinningLottery(winingNumbers, bonusNumber);
}

private void displayLottoResult() {
final LottoStatistics lottoStatistics = LottoStatistics.checkLotteryResult(lottoWinningMachine, userLotto);
OutputView.printWinningStatistics(lottoStatistics);
}
}
28 changes: 28 additions & 0 deletions src/main/java/lotto/model/BonusNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package lotto.model;

import static lotto.utils.ExceptionConstants.LottoMachineException.BONUS_NUMBER_IS_NOT_IN_RANGE;
import static lotto.utils.LottoConstants.MAX_VALUE;
import static lotto.utils.LottoConstants.MIN_VALUE;

public class BonusNumber {
private final int value;

public BonusNumber(final int value) {
validateBonusNumberIsInRange(value);
this.value = value;
}

private void validateBonusNumberIsInRange(final int value) {
if (isOutOfRange(value)) {
throw new IllegalArgumentException(BONUS_NUMBER_IS_NOT_IN_RANGE.message);
}
}

private boolean isOutOfRange(final int value) {
return value < MIN_VALUE || value > MAX_VALUE;
}

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

import java.util.Collections;
import java.util.List;

import static java.util.Collections.sort;
import static lotto.utils.ExceptionConstants.LottoException.*;
import static lotto.utils.LottoConstants.*;

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

public Lotto(final List<Integer> numbers) {
validateEachLottoElementIsInRange(numbers);
validateTotalLottoSize(numbers);
validateLottoHasDuplicateElement(numbers);
sort(numbers);
this.numbers = numbers;
}

private void validateEachLottoElementIsInRange(final List<Integer> numbers) {
if (hasOutOfRange(numbers)) {
throw new IllegalArgumentException(LOTTO_NUMBER_IS_NOT_IN_RANGE.message);
}
}

private boolean hasOutOfRange(final List<Integer> numbers) {
return numbers.stream()
.anyMatch(number -> number < MIN_VALUE || number > MAX_VALUE);
}

private void validateTotalLottoSize(final List<Integer> numbers) {
if (numbers.size() != LOTTO_SIZE) {
throw new IllegalArgumentException(LOTTO_SIZE_IS_NOT_FULFILL.message);
}
}

private void validateLottoHasDuplicateElement(final List<Integer> numbers) {
if (hasDuplicateNumber(numbers)) {
throw new IllegalArgumentException(LOTTO_NUMBER_MUST_BE_UNIQUE.message);
}
}

private boolean hasDuplicateNumber(final List<Integer> number) {
return number.stream()
.distinct()
.count() != LOTTO_SIZE;
}

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

@Override
public String toString() {
return numbers.toString();
}
}
104 changes: 104 additions & 0 deletions src/main/java/lotto/model/LottoStatistics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package lotto.model;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class LottoStatistics {
private final LottoWinningMachine lottoWinningMachine;
private final UserLotto userLotto;
private final Map<WinningRank, Integer> winningResult = new EnumMap<>(WinningRank.class);

private LottoStatistics(
final LottoWinningMachine lottoWinningMachine,
final UserLotto userLotto
) {
this.lottoWinningMachine = lottoWinningMachine;
this.userLotto = userLotto;
initWinningResult();
calculateLotteryWinningResult();
}

public static LottoStatistics checkLotteryResult(
final LottoWinningMachine lottoWinningMachine,
final UserLotto userLotto
) {
return new LottoStatistics(lottoWinningMachine, userLotto);
}

private void initWinningResult() {
for (WinningRank winningRank : WinningRank.values()) {
winningResult.put(winningRank, 0);
}
}

private void calculateLotteryWinningResult() {
List<Integer> winningLotteryNumbers = lottoWinningMachine.getWinningLotteryNumbers();
int bonusNumber = lottoWinningMachine.getBonusNumber();

for (Lotto lotto : userLotto.getUserLottos()) {
List<Integer> lottoNumbers = lotto.getNumbers();
int matchCount = getLottoMatchCount(lottoNumbers, winningLotteryNumbers);
boolean hasBonus = isBonusNumberExists(lottoNumbers, bonusNumber);

WinningRank winningRank = WinningRank.of(matchCount, hasBonus);
updateWinningResult(winningRank);
}
}

private int getLottoMatchCount(
final List<Integer> lottoNumbers,
final List<Integer> winningLotteryNumbers
) {
return (int) lottoNumbers.stream()
.filter(winningLotteryNumbers::contains)
.count();
}

private boolean isBonusNumberExists(
final List<Integer> lottoNumbers,
final int bonusNumber
) {
return lottoNumbers.contains(bonusNumber);
}

private void updateWinningResult(final WinningRank winningRank) {
winningResult.put(winningRank, winningResult.get(winningRank) + 1);
}

public Map<WinningRank, Integer> getWinningResult() {
return Collections.unmodifiableMap(winningResult);
}

public int getWinningCountByRank(final WinningRank winningRank) {
return winningResult.getOrDefault(winningRank, 0);
}

public BigDecimal getEarningRate() {
final BigDecimal lottoPurchaseAmount = userLotto.getLottoPurchaseAmount();
final BigDecimal totalWinningAmount = calculateTotalWinningAmount();

return totalWinningAmount
.multiply(BigDecimal.valueOf(100)) // 백분율
.divide(lottoPurchaseAmount, 1, RoundingMode.HALF_UP);
}

private BigDecimal calculateTotalWinningAmount() {
BigDecimal amount = BigDecimal.ZERO;
for (WinningRank winningRank : winningResult.keySet()) {
final BigDecimal addPrize = getAddPrize(winningRank);
amount = amount.add(addPrize);
}
return amount;
}

private BigDecimal getAddPrize(final WinningRank winningRank) {
final int reward = winningRank.getReward();
final int count = winningResult.get(winningRank);

return BigDecimal.valueOf((long) reward * count);
}
}
Loading