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
32 changes: 32 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## domain
#### 로또 구입

- 입력 받은 로또 구입 금액을 통해 로또 발행한 수량을 구한다.
- 각 로또 번호를 오름차순으로 구한다.

#### 로또 당첨 번호

- 입력 받은 당첨 번호와 보너스 번호를 저장한다.

#### 당첨 통계

- 구매한 로또 번호와 당첨 번호를 비교하여 당첨 결과를 확인한다.
- 당첨 내역을 이용하여 총 수익률을 계산한다.

## view
#### 입력

- 로또 구입 금액을 입력 받는다.
- 당첨 번호를 입력 받는다.
- 보너스 번호를 입력 받는다.

#### 출력

- 발행한 로또 수량 및 번호(오름차순)를 출력한다.
- 당첨 내역을 출력한다.
- 수익률은 소수점 둘째 자리에서 반올림하여 출력한다.
- 예외 상황 시 에러 문구를 출력한다.

## controller

- domain과 view를 결정하여 전달
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStorePath=wrapper/dists
4 changes: 4 additions & 0 deletions src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package lotto;

import lotto.controller.LottoController;

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

import lotto.domain.*;
import lotto.view.InputView;
import lotto.view.OutputView;

import java.util.Map;

public class LottoController {
public void lottoStart() {
try {
int purchaseAmount = InputView.getPurchaseAmount(); // 구매 로또 개수
LottoGenerator lottoGenerator = new LottoGenerator(purchaseAmount); // 구매 개수만큼 로또 발행
Lottos lottos = new Lottos(lottoGenerator.generateLottos()); // 발행한 로또들 저장
printLottosInformation(lottoGenerator, lottos); // 구매 개수, 발행한 로또 번호 출력
WinningLotto winningLotto = new WinningLotto(InputView.getLottoNumber(), InputView.getLottoBonusNumber()); //당첨 번호, 보너스 번호 저장
Map<WinningRank, Integer> winningDetails = WinningStatistics.getWinningDetails(lottos, winningLotto); //로또 당첨 통계
printWinningInformation(winningDetails, purchaseAmount); //로또 당첨 내역, 수익률 출력

} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}

private void printLottosInformation(LottoGenerator lottoGenerator, Lottos lottos) {
OutputView.printHowManyLottoUserPurchased(lottoGenerator.getLottoQuantity());
OutputView.printLottos(lottos);
}

private void printWinningInformation(Map<WinningRank, Integer> winningDetails, int purchaseAmount) {
OutputView.printWinningStatistics();
OutputView.printWinningDetails(winningDetails);
long winningAmount = WinningStatistics.getWinningAmount(winningDetails);
OutputView.printLottoYield(WinningStatistics.getLottoYield(winningAmount, purchaseAmount));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package lotto;
package lotto.domain;

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

public class Lotto {
private final List<Integer> numbers;
Expand All @@ -10,11 +12,16 @@ public Lotto(List<Integer> numbers) {
this.numbers = numbers;
}

// 입력받은 로또 번호가 6개가 아니라면 예외 발생
private void validate(List<Integer> numbers) {
if (numbers.size() != 6) {
throw new IllegalArgumentException();
}
}

// TODO: 추가 기능 구현

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


import org.kokodak.Randoms;

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

public class LottoGenerator {

private static final int LOTTO_NUMBER_MIN = 1;
private static final int LOTTO_NUMBER_MAX = 45;
private static final int LOTTO_NUMBER_QUANTITY = 6;

private List<Lotto> lottos = new ArrayList<>();
private int lottoQuantity;



//구매 가능한 로또 수량
public LottoGenerator(int money) {
lottoQuantity = money / 1000;
}

//돈을 0원 이하로 받거나 1000원 단위로 받지 않으면 예외 발생
private void validateMoney(int money) {
if (isZeroOrNegativeNumber(money) || !isDividedByOneThousand(money)) {
throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위로만 받을 수 있습니다.");
}
}

//돈을 0원 이하로 받았는지 확인
private boolean isZeroOrNegativeNumber(int money) {
if (money <= 0){
return true;
}
return false;
}

//돈을 1000원 단위로 받았는지 확인
private boolean isDividedByOneThousand(int money) {
if (money % 1000 == 0) {
return true;
}
return false;
}

//1 ~ 45 사이의 6개 숫자를 랜덤으로 뽑아서 로또 발행
public Lotto generateLotto() {
List<Integer> randomNumbers = new ArrayList<>(
Randoms.pickUniqueNumbersInRange(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_NUMBER_QUANTITY)
);
randomNumbers.sort(Comparator.naturalOrder());
return new Lotto(randomNumbers);

}

//
public List<Lotto> generateLottos() {
for (int i = 0; i < lottoQuantity; i++) {
Lotto lotto = generateLotto();
lottos.add(lotto);
}
return lottos;
}

//
public int getLottoQuantity() {
return lottoQuantity;
}

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

import java.util.List;

public class Lottos {
private List<Lotto> lottos;

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

public List<Lotto> getLottos() {
return lottos;
}
}
75 changes: 75 additions & 0 deletions src/main/java/lotto/domain/WinningLotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package lotto.domain;

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

public class WinningLotto {
private static final String WINNING_NUMBERS_ARE_BETWEEN_ONE_AND_FORTY_FIVE = "[ERROR] 당첨 번호는 1부터 45 사이의 숫자여야 합니다.";
private static final String WINNING_NUMBERS_MUST_BE_SIX_DIFFERENT_NUMBERS = "[ERROR] 당첨 번호는 서로 다른 6개의 수여야 합니다.";
private static final String BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE = "[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다.";
private static final String WINNING_NUMBERS_CONTAIN_BONUS_NUMBER = "[ERROR] 당첨 번호와 보너스 번호가 중복됩니다.";
private static final int LOTTO_NUMBER_LOWER_LIMIT = 1;
private static final int LOTTO_NUMBER_UPPER_LIMIT = 45;
private static final int LOTTO_NUMBERS_SIZE = 6;

private final List<Integer> winningNumbers;
private final int bonusNumber;

public WinningLotto(List<Integer> winningNumbers, int bonusNumber) {
validateWinningNumbers(winningNumbers);
validateBonusNumber(bonusNumber);
validateDuplicate(winningNumbers, bonusNumber);
this.winningNumbers = winningNumbers;
this.bonusNumber = bonusNumber;
}

private void validateWinningNumbers(List<Integer> winningNumbers) {
if (!isSixDifferentNumbers(winningNumbers)) {
throw new IllegalArgumentException(WINNING_NUMBERS_MUST_BE_SIX_DIFFERENT_NUMBERS);
}
if (!isBetweenOneAndFortyFive(winningNumbers)) {
throw new IllegalArgumentException(WINNING_NUMBERS_ARE_BETWEEN_ONE_AND_FORTY_FIVE);
}
}

// 중복을 허용하지 않는 Set의 특성을 이용해서
// 6개가 전부 다른 숫자인지 확인
private boolean isSixDifferentNumbers(List<Integer> winningNumbers) {
Set<Integer> duplicateChecker = new HashSet<>(winningNumbers);
return duplicateChecker.size() == LOTTO_NUMBERS_SIZE;
}

private boolean isBetweenOneAndFortyFive(List<Integer> winningNumbers) {
for (int winningNumber : winningNumbers) {
if (winningNumber < LOTTO_NUMBER_LOWER_LIMIT || winningNumber > LOTTO_NUMBER_UPPER_LIMIT) {
return false;
}
}
return true;
}

private void validateBonusNumber(int bonusNumber) {
if (!isBetweenOneAndFortyFive((bonusNumber))) {
throw new IllegalArgumentException(BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE);
}
}

private boolean isBetweenOneAndFortyFive(int bonusNumber) {
return bonusNumber >= LOTTO_NUMBER_LOWER_LIMIT && bonusNumber <= LOTTO_NUMBER_UPPER_LIMIT;
}

private void validateDuplicate(List<Integer> winningNumbers, int bonusNumber) {
if (winningNumbers.contains(bonusNumber)) {
throw new IllegalArgumentException(WINNING_NUMBERS_CONTAIN_BONUS_NUMBER);
}
}

public List<Integer> getWinningNumbers() {
return winningNumbers;
}

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


import java.util.Arrays;

public enum WinningRank {

LAST_PLACE(0, false, 0),
FIFTH_PLACE(3, false, 5_000),
FOURTH_PLACE(4, false, 50_000),
THIRD_PLACE(5, false, 1_500_000),
SECOND_PLACE(5, true, 30_000_000),
FIRST_PLACE(6, false, 2_000_000_000);

private final int matchingCount;
private final boolean containsBonusNumber;
private final int winningPrice;

WinningRank(int matchingCount, boolean containsBonusNumber, int winningPrice) {
this.matchingCount = matchingCount;
this.containsBonusNumber = containsBonusNumber;
this.winningPrice = winningPrice;
}

public static WinningRank findWinningRank(int matchingCount, boolean containsBonusNumber) {
return Arrays.stream(values())
.filter(winningRank -> winningRank.matchingCount == matchingCount)
.filter(winningRank -> winningRank.containsBonusNumber == containsBonusNumber)
.findFirst()
.orElse(WinningRank.LAST_PLACE);
}

public int getMatchingCount() {
return matchingCount;
}

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

import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class WinningStatistics {

private static final int AT_LEAST_THIRD_PLACE = 5;
private static final int PERCENTAGE = 100;
private static final int INITIAL_VALUE = 0;

public static Map<WinningRank, Integer> getWinningDetails(Lottos lottos, WinningLotto winningLotto) {
Map<WinningRank, Integer> winningDetails = generateWinningDetails();
for (Lotto lotto : lottos.getLottos()) {
int matchingCount = compareNumbersWithWinningNumbers(lotto, winningLotto);
boolean containsBonusNumber = compareNumbersWithBonusNumber(lotto, winningLotto, matchingCount);
WinningRank winningRank = WinningRank.findWinningRank(matchingCount, containsBonusNumber);
winningDetails.replace(winningRank, winningDetails.get(winningRank) + 1);
}
return winningDetails;
}

public static Map<WinningRank, Integer> generateWinningDetails() {
Map<WinningRank, Integer> winningDetails = new EnumMap<>(WinningRank.class);
Arrays.stream(WinningRank.values()).forEach(winningRank -> winningDetails.put(winningRank, INITIAL_VALUE));
return winningDetails;
}

private static int compareNumbersWithWinningNumbers(Lotto lotto, WinningLotto winningLotto) {
List<Integer> numbers = lotto.getNumbers();
List<Integer> winningNumbers = winningLotto.getWinningNumbers();
return (int) numbers.stream()
.filter(winningNumbers::contains)
.count();
}

private static boolean compareNumbersWithBonusNumber(Lotto lotto, WinningLotto winningLotto, int matchingCount) {
if (matchingCount != AT_LEAST_THIRD_PLACE) {
return false;
}
List<Integer> numbers = lotto.getNumbers();
int bonusNumber = winningLotto.getBonusNumber();
return numbers.contains(bonusNumber);
}

public static long getWinningAmount(Map<WinningRank, Integer> winningDetails) {
return winningDetails.entrySet().stream()
.mapToLong(entry -> (long) entry.getKey().getWinningPrice() * entry.getValue())
.sum();
}

public static double getLottoYield(long winningAmount, int money) {
double lottoYield = PERCENTAGE + (double) (winningAmount - money) / money * PERCENTAGE;
return Math.round(lottoYield * 10) / 10.0;
}
}
Loading