Skip to content

Commit

Permalink
Step4 - 로또(2등) (#543)
Browse files Browse the repository at this point in the history
* docs(step4): add requirements and todo lists for step4

* fix(step3): apply code review feedbacks

* feat(step4): implement bonus ball for second prize

* fix(pr): apply step4 code review feedbacks

- add first class collection for lotto prize
- add missing tests
  • Loading branch information
stevejkang committed May 19, 2022
1 parent 2e24c09 commit 618a2fa
Show file tree
Hide file tree
Showing 17 changed files with 271 additions and 48 deletions.
14 changes: 14 additions & 0 deletions README.md
Expand Up @@ -70,3 +70,17 @@
- [x] 생성된 로또 번호와 입력받은 지난 주 당첨번호를 비교한다.
- [x] 비교한 결과를 기반으로 통계를 출력한다.
- [x] 총 수익률을 계산하고 출력한다.

## 4단계 - 로또(2등)
### 기능 요구사항
- 2등을 위해 추가 번호를 하나 더 추첨한다.
- 당첨 통계에 2등도 추가해야 한다.
### 기능 목록 도출
#### 보너스 볼 입력
- [x] 1 ~ 45 사이의 숫자인 보너스 볼을 입력받는다.
- [x] 보너스 볼은 입력 받은 지난 주 당첨번호 6개와 겹칠 수 없다.
#### 당첨 여부 판별시 2등 추가
- [x] LottoPrize 에 2등이 추가되어야 한다.
- [x] 5개 일치 + 보너스 볼 일치의 경우에 2등으로 판별한다.
#### 통계 출력에 2등 추가
- [x] 통계 출력시 2등을 추가하여 출력한다.
2 changes: 2 additions & 0 deletions src/main/java/lotto/constants/LottoGameConstant.java
@@ -1,6 +1,8 @@
package lotto.constants;

public class LottoGameConstant {
private LottoGameConstant() {}

public static final int LOTTO_TICKET_PRICE = 1_000;
public static final double PROFIT_CRITERIA = 1.0;
}
4 changes: 4 additions & 0 deletions src/main/java/lotto/constants/LottoGameErrorMessage.java
@@ -1,8 +1,12 @@
package lotto.constants;

public class LottoGameErrorMessage {
private LottoGameErrorMessage() {}

public static final String INVALID_LOTTO_NUMBER_RANGE = "[ERROR] 로또 숫자는 1부터 45 사이의 값이어야 합니다.";
public static final String INVALID_LOTTO_NUMBER_NOT_UNIQUE = "[ERROR] 로또 숫자는 중복될 수 없습니다.";
public static final String INVALID_BONUS_LOTTO_NUMBER = "[ERROR] 보너스 볼 숫자는 지난 주 당첨번호와 같을 수 없습니다.";

public static final String INVALID_LOTTO_NUMBERS_SIZE = "[ERROR] 로또 숫자는 6개로 이루어져야 합니다.";
public static final String INSUFFICIENT_MONEY = "[ERROR] 받은 금액으로 로또를 구매하기에 부족합니다.";
public static final String INVALID_MONEY_LEFT = "[ERROR] 1000원 단위의 금액을 지불해주세요.";
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/lotto/constants/LottoGameMessage.java
@@ -1,12 +1,16 @@
package lotto.constants;

public class LottoGameMessage {
private LottoGameMessage() {}

public static final String WAIT_FOR_USER_MONEY_INPUT = "구입금액을 입력해 주세요.";
public static final String PURCHASED_LOTTO_COUNT_INFORMATION = "%s개를 구매했습니다.";
public static final String WAIT_FOR_LATEST_LOTTO_RESULT_INPUT = "지난 주 당첨 번호를 입력해 주세요.";
public static final String WAIT_FOR_BONUS_LOTTO_NUMBER_INPUT = "보너스 볼을 입력해 주세요.";
public static final String LOTTO_STATISTICS_INFORMATION_TITLE = "당첨 통계";
public static final String DIVIDER = "---------";
public static final String STATISTICS_PER_NUMBER_OF_MATCH = "%s개 일치 (%s원)- %s개";
public static final String STATISTICS_PER_NUMBER_OF_MATCH = "%s개 일치%s(%s원)- %s개";
public static final String STATISTICS_PER_NUMBER_OF_MATCH_BONUS_MATCHED = ", 보너스 볼 일치";
public static final String TOTAL_PROFIT_RESULT = "총 수익률은 %2.2f입니다.";
public static final String TOTAL_PROFIT_DESCRIPTION = "(기준이 1이기 때문에 결과적으로는 %s라는 의미임)";

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/lotto/constants/LottoNumberConstant.java
@@ -1,6 +1,8 @@
package lotto.constants;

public class LottoNumberConstant {
private LottoNumberConstant() {}

public static final Integer LOTTO_NUMBER_SIZE = 6;
public static final Integer LOTTO_NUMBER_MINIMUM_VALUE = 1;
public static final Integer LOTTO_NUMBER_MAXIMUM_VALUE = 45;
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/lotto/domain/LottoGame.java
@@ -1,5 +1,6 @@
package lotto.domain;

import lotto.view.InputView;
import lotto.view.ResultView;

import java.util.List;
Expand All @@ -14,14 +15,18 @@ public LottoGame(LottoTickets lottoTickets, Money money) {
}

public void play() {
System.out.println("print");
ResultView.printCount(money.calculateLottoTicketCount());
ResultView.printLottoTickets(lottoTickets);

List<Integer> lottoNumbers = LottoNumbers.getLottoNumbersFromInput();
String receivedNumbers = InputView.inputLatestLottoResult();
List<Integer> numberList = LottoNumbers.getLottoNumbersFromInput(receivedNumbers);
LottoNumbers lastWinningLottoNumbers = LottoNumbers.generateLottoNumbers(numberList);

List<LottoPrize> matchResults = lottoTickets.matchResults(
new LottoTicket(LottoNumbers.generateLottoNumbers(lottoNumbers).getReadOnlyLottoNumbers())
LottoNumber receivedBonusLottoNumber = InputView.inputBonusLottoNumber(lastWinningLottoNumbers);

LottoPrizes matchResults = lottoTickets.matchResults(
new LottoTicket(lastWinningLottoNumbers.getReadOnlyLottoNumbers()),
receivedBonusLottoNumber
);
ResultView.printStatistics(matchResults, money);
}
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/lotto/domain/LottoNumbers.java
@@ -1,7 +1,6 @@
package lotto.domain;

import lotto.view.InputView;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
Expand All @@ -11,7 +10,7 @@ public class LottoNumbers {
private List<LottoNumber> lottoNumbers;

private LottoNumbers(List<LottoNumber> lottoNumbers) {
this.lottoNumbers = lottoNumbers;
this.lottoNumbers = new ArrayList<>(lottoNumbers);
}

public static LottoNumbers generateLottoNumbers(NumbersGenerator numbersGenerator) {
Expand All @@ -33,10 +32,12 @@ public List<LottoNumber> getReadOnlyLottoNumbers() {
return Collections.unmodifiableList(this.lottoNumbers);
}

public static List<Integer> getLottoNumbersFromInput() {
String lottoNumbers = InputView.inputLatestLottoResult();
public boolean notContains(LottoNumber lottoNumber) {
return !this.lottoNumbers.contains(lottoNumber);
}

return LottoNumbersGenerator.generate(lottoNumbers);
public static List<Integer> getLottoNumbersFromInput(String receivedLottoNumbers) {
return LottoNumbersGenerator.generate(receivedLottoNumbers);
}

private static List<LottoNumber> parseIntegerToLottoNumber(List<Integer> generateNumbers) {
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/lotto/domain/LottoPrize.java
@@ -1,9 +1,14 @@
package lotto.domain;

import lotto.utils.StringUtil;

import java.util.Arrays;

import static lotto.constants.LottoGameMessage.STATISTICS_PER_NUMBER_OF_MATCH_BONUS_MATCHED;

public enum LottoPrize {
WIN_WITH_FULL_MATCHES(6, 2_000_000_000),
WIN_WITH_5_MATCHES_AND_BONUS(5, 30_000_000),
WIN_WITH_5_MATCHES(5, 1_500_000),
WIN_WITH_4_MATCHES(4, 50_000),
WIN_WITH_3_MATCHES(3, 5_000),
Expand All @@ -25,10 +30,39 @@ public int getPrize() {
return this.prize;
}

private boolean isNormalPrize(LottoPrize lottoPrize) {
return !lottoPrize.equals(LottoPrize.WIN_WITH_5_MATCHES_AND_BONUS);
}

public static LottoPrize valueOf(int numberOfMatch) {
return Arrays.stream(LottoPrize.values())
.filter(prize -> prize.isNormalPrize(prize))
.filter(prize -> prize.getNumberOfMatch() == numberOfMatch)
.findAny()
.orElse(NONE);
}

public static LottoPrize valueOf(int numberOfMatch, boolean hasBonusBall) {
if (isSecondWinningRank(numberOfMatch, hasBonusBall)) {
return WIN_WITH_5_MATCHES_AND_BONUS;
}

if (isThirdWinningRank(numberOfMatch, hasBonusBall)) {
return WIN_WITH_5_MATCHES;
}

return valueOf(numberOfMatch);
}

private static boolean isThirdWinningRank(int matchCount, boolean hasBonusBallNumber) {
return matchCount == WIN_WITH_5_MATCHES.getNumberOfMatch() && !hasBonusBallNumber;
}

private static boolean isSecondWinningRank(int matchCount, boolean hasBonusBallNumber) {
return matchCount == WIN_WITH_5_MATCHES_AND_BONUS.getNumberOfMatch() && hasBonusBallNumber;
}

public String additionalWinningStatistics() {
return this == WIN_WITH_5_MATCHES_AND_BONUS ? STATISTICS_PER_NUMBER_OF_MATCH_BONUS_MATCHED : StringUtil.EMPTY;
}
}
40 changes: 40 additions & 0 deletions src/main/java/lotto/domain/LottoPrizes.java
@@ -0,0 +1,40 @@
package lotto.domain;

import java.util.List;
import java.util.Objects;

public class LottoPrizes {
private final List<LottoPrize> lottoPrizes;

public LottoPrizes(List<LottoPrize> lottoPrizes) {
this.lottoPrizes = lottoPrizes;
}

public List<LottoPrize> getLottoPrizes() {
return this.lottoPrizes;
}

public int size() {
return this.lottoPrizes.size();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LottoPrizes that = (LottoPrizes) o;
return Objects.equals(lottoPrizes, that.lottoPrizes);
}

@Override
public int hashCode() {
return Objects.hash(lottoPrizes);
}

@Override
public String toString() {
return "LottoPrizes{" +
"lottoPrizes=" + lottoPrizes +
'}';
}
}
11 changes: 7 additions & 4 deletions src/main/java/lotto/domain/LottoTicket.java
@@ -1,6 +1,5 @@
package lotto.domain;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -39,12 +38,16 @@ public List<LottoNumber> getLottoNumbers() {
return lottoNumbers;
}

public LottoPrize match(LottoTicket lottoTicket) {
public boolean hasBonusBallNumber(LottoNumber bonusBallNumber) {
return this.lottoNumbers.contains(bonusBallNumber);
}

public LottoPrize match(LottoTicket lastWinningLottoTicket, boolean hasBonusBallNumber) {
int numberOfMatch = (int) lottoNumbers.stream()
.filter(number -> lottoTicket.getLottoNumbers().contains(number))
.filter(number -> lastWinningLottoTicket.getLottoNumbers().contains(number))
.count();

return LottoPrize.valueOf(numberOfMatch);
return LottoPrize.valueOf(numberOfMatch, hasBonusBallNumber);
}

@Override
Expand Down
23 changes: 12 additions & 11 deletions src/main/java/lotto/domain/LottoTickets.java
@@ -1,7 +1,6 @@
package lotto.domain;

import lotto.view.ResultView;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -11,7 +10,7 @@ public class LottoTickets {
private final List<LottoTicket> lottoTickets;

public LottoTickets(int count) {
this.lottoTickets = Collections.unmodifiableList(generateLottoTickets(count));
this.lottoTickets = new ArrayList<>(generateLottoTickets(count));
}

private List<LottoTicket> generateLottoTickets(int count) {
Expand All @@ -24,15 +23,17 @@ public int size() {
return lottoTickets.size();
}

public List<LottoPrize> matchResults(LottoTicket lottoTicket) {
return lottoTickets.stream()
.map(ticket -> ticket.match(lottoTicket))
.collect(Collectors.toList());
public LottoPrizes matchResults(LottoTicket lastWinningLottoTicket, LottoNumber bonusLottoNumber) {
return new LottoPrizes(lottoTickets.stream()
.map(ticket -> ticket.match(lastWinningLottoTicket, hasBonusBallNumber(ticket, bonusLottoNumber)))
.collect(Collectors.toList()));
}

public boolean hasBonusBallNumber(LottoTicket generatedLottoTicket, LottoNumber bonusBallNumber) {
return generatedLottoTicket.hasBonusBallNumber(bonusBallNumber);
}

public void printLottoTickets() {
for (LottoTicket lotto : lottoTickets) {
ResultView.printLottoTicket(lotto);
}
public List<LottoTicket> getLottoTickets() {
return Collections.unmodifiableList(lottoTickets);
}
}
2 changes: 2 additions & 0 deletions src/main/java/lotto/utils/StringUtil.java
Expand Up @@ -3,6 +3,8 @@
public class StringUtil {
private StringUtil() {}

public static final String EMPTY = "";

public static boolean isNullOrEmpty(String string) {
return string == null || string.length() == 0;
}
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/lotto/view/InputView.java
@@ -1,5 +1,10 @@
package lotto.view;

import lotto.domain.LottoNumber;
import lotto.domain.LottoNumbers;
import lotto.utils.StringParser;

import static lotto.constants.LottoGameErrorMessage.INVALID_BONUS_LOTTO_NUMBER;
import static lotto.constants.LottoGameMessage.*;

import java.util.*;
Expand All @@ -18,4 +23,19 @@ public static String inputLatestLottoResult() {
System.out.println(WAIT_FOR_LATEST_LOTTO_RESULT_INPUT);
return scanner.nextLine();
}

public static LottoNumber inputBonusLottoNumber(LottoNumbers lastWinningLottoNumbers) {
System.out.println(WAIT_FOR_BONUS_LOTTO_NUMBER_INPUT);
LottoNumber receivedBonusLottoNumber = new LottoNumber(StringParser.parseAsInteger(scanner.nextLine()));

if (!isValidBonusBall(lastWinningLottoNumbers, receivedBonusLottoNumber)) {
throw new IllegalArgumentException(INVALID_BONUS_LOTTO_NUMBER);
}

return receivedBonusLottoNumber;
}

private static boolean isValidBonusBall(LottoNumbers lastWinningLottoNumbers, LottoNumber bonusBall) {
return lastWinningLottoNumbers.notContains(bonusBall);
}
}

0 comments on commit 618a2fa

Please sign in to comment.