Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step4 - 로또(수동) #3238

Open
wants to merge 10 commits into
base: gisungpark
Choose a base branch
from
64 changes: 52 additions & 12 deletions src/main/java/step2/controller/LottoGameController.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package step2.controller;

import step2.domain.LottoTicket;
import step2.domain.LottoCommonValue;
import step2.domain.LottoGames;
import step2.domain.LottoResultReport;
import step2.domain.LottoTicket;
import step2.view.InputView;
import step2.view.ResultView;

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

public class LottoGameController {

Expand All @@ -15,27 +18,64 @@ public class LottoGameController {
public void playLottoGame() {
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved

int money = InputView.readAmountOfPurchase();
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved
int gameCount = lottoGames.calculateBuyingTicketCount(money);
ResultView.printMessage(gameCount + "개를 구매했습니다.");
if (gameCount == 0) {
int manualLottoTicketCount = InputView.readCountOfManualTicket();
List<LottoTicket> manualLottoTickets = buyManualTickets(money, manualLottoTicketCount);

int automaticTicketCount = lottoGames.calculateBuyingTicketCount(money, manualLottoTicketCount);
ResultView.printNumberOfTickets(manualLottoTicketCount, automaticTicketCount);
if (automaticTicketCount == 0 && manualLottoTicketCount == 0) {
return;
}

List<LottoTicket> lottoTickets = lottoGames.buyLottoGame(gameCount);
ResultView.printLottoTicket(lottoTickets);
LottoTicket winningTicket = lottoGames.readWinningNumber(InputView.readWinningNumbers());
List<LottoTicket> automaticLottoTickets = getBuyAutomaticTickets(automaticTicketCount);
ResultView.printLottoTicket(automaticLottoTickets);
LottoTicket winningTicket = readWinningTicket();
int bonusNumber = InputView.readBonusNumber();

ResultView.printBlankLine();
ResultView.printMessage("당첨 통계");

LottoResultReport lottoResultReport = new LottoResultReport();
for (LottoTicket lottoTicket : lottoTickets) {
lottoResultReport.recordRank(lottoTicket.checkLottoTicket(winningTicket, bonusNumber));
}
LottoResultReport lottoResultReport = getLottoResultReport(manualLottoTickets, automaticLottoTickets, winningTicket, bonusNumber);

ResultView.printResultReport(lottoResultReport);
double profit = lottoResultReport.calculateProfit(gameCount);
double profit = lottoResultReport.calculateProfit(manualLottoTicketCount + automaticTicketCount);
ResultView.printMessage("총 수익률은 " + profit + "입니다.");
}

private List<LottoTicket> buyManualTickets(int totalMoney, int manualTicketCount) {
if(manualTicketCount * LottoCommonValue.DEFAULT_LOTTO_PRICE.value() > totalMoney) {
throw new IllegalArgumentException("수동으로 구매할 수 있는 티켓 개수를 초과하셨습니다.");
}
ResultView.printMessage("수동으로 구매할 번호를 입력해 주세요");
List<LottoTicket> manualLottoTickets = new ArrayList<>();
for(int i=0; i<manualTicketCount; i++) {
Optional<LottoTicket> lottoTicket = lottoGames.toLottoTicket(InputView.readManualTicketNumbers());
manualLottoTickets.add(lottoTicket.orElseThrow(IllegalArgumentException::new));
}
return manualLottoTickets;
}

private List<LottoTicket> getBuyAutomaticTickets(int automaticTicketCount) {
return lottoGames.buyAutomaticLottoTickets(automaticTicketCount);
}

private LottoTicket readWinningTicket() {
ResultView.printMessage("지난 주 당첨 번호를 입력해 주세요");
return lottoGames.toLottoTicket(InputView.readWinningNumbers())
.orElseThrow(() -> new IllegalArgumentException("잘못 입력하셨습니다."));
}

private LottoResultReport getLottoResultReport(List<LottoTicket> manualLottoTickets,
List<LottoTicket> automaticLottoTickets,
LottoTicket winningTicket, int bonusNumber) {

LottoResultReport lottoResultReport = new LottoResultReport();
for (LottoTicket lottoTicket : manualLottoTickets) {
lottoResultReport.recordRank(lottoTicket.checkLottoTicket(winningTicket, bonusNumber));
}
for (LottoTicket lottoTicket : automaticLottoTickets) {
lottoResultReport.recordRank(lottoTicket.checkLottoTicket(winningTicket, bonusNumber));
}
return lottoResultReport;
}
}
29 changes: 17 additions & 12 deletions src/main/java/step2/domain/LottoGames.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package step2.domain;

import java.util.*;
import java.util.stream.Collectors;

public class LottoGames {

public LottoGames() {
}

public int calculateBuyingTicketCount(int money) {
public int calculateBuyingTicketCount(int money, int manualLottoTicketCount) {
money -= manualLottoTicketCount * LottoCommonValue.DEFAULT_LOTTO_PRICE.value();
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved
return new Integer(money / LottoCommonValue.DEFAULT_LOTTO_PRICE.value());
}

public List<LottoTicket> buyLottoGame(int gameCount) {
public List<LottoTicket> buyAutomaticLottoTickets(int gameCount) {
List<LottoTicket> lottoTickets = new ArrayList<>(gameCount);
for (int i = 0; i < gameCount; i++) {
lottoTickets.add(createLottoGame());
Expand All @@ -20,27 +22,30 @@ public List<LottoTicket> buyLottoGame(int gameCount) {
}

private LottoTicket createLottoGame() {
return new LottoTicket(RandomIntegersGenerator.createNumberList());
List<LottoNo> lottoNumbers = RandomIntegersGenerator.createNumberList().stream()
.map(i -> LottoNo.of(i))
.collect(Collectors.toList());
return new LottoTicket(lottoNumbers);
}

public LottoTicket readWinningNumber(String stringNumber) {
String[] numbers = splitByDelimiter(stringNumber);
Set<Integer> integers = toSet(numbers);
if (integers.size() != LottoCommonValue.DEFAULT_LOTTO_NUMBER_COUNT.value()) {
throw new IllegalArgumentException(stringNumber + " : 입력한 숫자를 확인해 주세요");
public Optional<LottoTicket> toLottoTicket(String stringNumber) {
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved
String[] splits = splitByDelimiter(stringNumber);
Set<LottoNo> numbers = toSet(splits);
if (numbers.size() != LottoCommonValue.DEFAULT_LOTTO_NUMBER_COUNT.value()) {
return Optional.empty();
}
return new LottoTicket(integers);
return Optional.ofNullable(new LottoTicket(numbers));
}

private String[] splitByDelimiter(String stringNumber) {
stringNumber = stringNumber.replaceAll(" ", "");
return stringNumber.split(",");
}

private Set<Integer> toSet(String[] numbers) {
HashSet<Integer> hashSet = new HashSet<>();
private Set<LottoNo> toSet(String[] numbers) {
Set<LottoNo> hashSet = new HashSet<>();
for (String number : numbers) {
hashSet.add(toInt(number));
hashSet.add(LottoNo.of(toInt(number)));
}
return hashSet;
}
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/step2/domain/LottoNo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package step2.domain;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.IntStream;

public class LottoNo {
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved
private static final int MIN_LOTTO_NUMBER = 1;
private static final int MAX_LOTTO_NUMBER = 45;

private static Map<Integer, LottoNo> lottoNumberCache = new HashMap<>();
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved

static {
IntStream.range(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER + 1)
.forEach(i -> lottoNumberCache.put(i, new LottoNo(i)));
}

private int number;

private LottoNo(int number) {
this.number = number;
}

public static LottoNo of(int number) {
Optional<LottoNo> lottoNo = Optional.ofNullable(lottoNumberCache.get(number));
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved
return lottoNo.orElseThrow(() -> new IllegalArgumentException("잘못 입력하셨습니다."));
}

public int number() {
return number;
}
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LottoNo lottoNo = (LottoNo) o;
return number == lottoNo.number;
}

@Override
public int hashCode() {
return Objects.hash(number);
}
}
4 changes: 1 addition & 3 deletions src/main/java/step2/domain/LottoResultReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ public LottoResultReport() {
}

public int recordRank(Rank rank) {
Integer value = lottoResultReport.getOrDefault(rank, 0);
lottoResultReport.put(rank, value + 1);
return lottoResultReport.get(rank);
return lottoResultReport.compute(rank, (key, value) -> value == null ? 1 : value + 1);
lxxjn0 marked this conversation as resolved.
Show resolved Hide resolved
}

public int findReportByMatchCount(Rank rank) {
Expand Down
34 changes: 17 additions & 17 deletions src/main/java/step2/domain/LottoTicket.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@

public class LottoTicket {

private final List<Integer> lottoTicket;
private final List<LottoNo> lottoTicket;

public LottoTicket(List<Integer> lottoTicket) {
validInputNumber(lottoTicket);
this.lottoTicket = lottoTicket;
public LottoTicket(List<LottoNo> lottoNumbers) {
validInputNumber(lottoNumbers);
this.lottoTicket = new ArrayList<>(lottoNumbers);
}

public LottoTicket(Set<Integer> numbers) {
validInputNumber(numbers);
lottoTicket = new ArrayList<>(numbers);
public LottoTicket(Set<LottoNo> lottoNumbers) {
validInputNumber(lottoNumbers);
lottoTicket = new ArrayList<>(lottoNumbers);
}

private static void validInputNumber(Collection<Integer> numbers) {
if (numbers.size() != LottoCommonValue.DEFAULT_LOTTO_NUMBER_COUNT.value()) {
throw new IllegalArgumentException(numbers + " : 입력한 숫자를 확인해 주세요");
private static void validInputNumber(Collection<LottoNo> lottoNos) {
if (lottoNos.size() != LottoCommonValue.DEFAULT_LOTTO_NUMBER_COUNT.value()) {
throw new IllegalArgumentException(lottoNos + " : 입력한 숫자를 확인해 주세요");
}
}

public boolean isContain(Integer number) {
public boolean isContain(LottoNo number) {
return this.lottoTicket.contains(number);
}

Expand All @@ -34,15 +34,15 @@ public Rank checkLottoTicket(LottoTicket winningTicket, int bonusNumber) {
.filter(i -> winningTicket.isContain(i))
.count();

if(count == 5 && isContain(bonusNumber)) {
return Rank.SECOND;
}

return Rank.toPrizeMoney(count);
return Rank.toPrizeMoney(count, isContain(LottoNo.of(bonusNumber)));
}

public String printTicket() {
return lottoTicket.toString();
StringBuilder stringBuilder = new StringBuilder();
lottoTicket.stream()
.forEach(lottoNo -> stringBuilder.append(lottoNo.number() + ", "));
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
return stringBuilder.toString();
}

}
29 changes: 16 additions & 13 deletions src/main/java/step2/domain/Rank.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
package step2.domain;

import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.function.BiPredicate;

public enum Rank {
MISS(0, 0, "0개 일치 (0)"),
FIFTH(3, 5_000, "3개 일치 (5000)"),
FOURTH(4, 50_000, "4개 일치 (50000)"),
THIRD(5, 1_500_000, "5개 일치 (1500000)"),
SECOND(5, 30_000_000, "5개 일치, 보너스 볼 일치(30000000원)"),
FIRST(6, 2_000_000_000, "6개 일치 (2000000000)");
MISS(0, 0, "0개 일치 (0)", (matchCount, hasBonus) -> matchCount < 3),
FIFTH(3, 5_000, "3개 일치 (5000)", (matchCount, hasBonus) -> matchCount == 3),
FOURTH(4, 50_000, "4개 일치 (50000)", (matchCount, hasBonus) -> matchCount == 4),
THIRD(5, 1_500_000, "5개 일치 (1500000)", (matchCount, hasBonus) -> matchCount == 5 && !hasBonus),
SECOND(5, 30_000_000, "5개 일치, 보너스 볼 일치(30000000원)", (matchCount, hasBonus) -> matchCount == 5 && hasBonus),
FIRST(6, 2_000_000_000, "6개 일치 (2000000000)", (matchCount, hasBonus) -> matchCount == 6);

private static final int THIRD_COUNT = 5;

private int matchCount;
private long prizeMoney;
private BiPredicate<Integer, Boolean> predicate;

private String message;

Rank(int matchCount, long rank, String message) {
Rank(int matchCount, long rank, String message, BiPredicate<Integer, Boolean> predicate) {
this.matchCount = matchCount;
this.prizeMoney = rank;
this.message = message;
this.predicate = predicate;
}

public static Rank toPrizeMoney(int matchCount) {
if (matchCount == THIRD_COUNT) {
return Rank.THIRD;
}
public static Rank toPrizeMoney(int matchCount, boolean hasBonus) {

return Arrays.stream(values())
.filter(prizeMoney -> prizeMoney.matchCount == matchCount)
.filter(prizeMoney -> prizeMoney.predicate.test(matchCount, hasBonus))
.findAny()
.orElse(Rank.MISS);
}
Expand All @@ -46,4 +45,8 @@ public long prizeMoney() {
public String message() {
return this.message;
}

private boolean isSameMathCount(int matchCount) {
return this.matchCount == matchCount;
}
}
13 changes: 10 additions & 3 deletions src/main/java/step2/view/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,19 @@ private static int toInt(String nextLine) {
}
}

public static String readManualTicketNumbers() {
return readString();
}

public static String readWinningNumbers() {
return readString("지난 주 당첨 번호를 입력해 주세요");
return readString();
}

private static String readString(String message) {
System.out.println(message);
private static String readString() {
return scanner.nextLine();
}

public static int readCountOfManualTicket() {
return readInt("수동으로 구매할 로또 수를 입력해 주세요.");
}
}
4 changes: 4 additions & 0 deletions src/main/java/step2/view/ResultView.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

public class ResultView {

public static void printNumberOfTickets(int manualTicketCount, int automaticTicketCount) {
System.out.println("수동으로 " + manualTicketCount + "장, 자동으로 " + automaticTicketCount + "개를 구매했습니다.");
}

public static void printMessage(String message) {
System.out.println(message);
}
Expand Down
10 changes: 5 additions & 5 deletions src/test/java/step2/domain/LottoGamesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,28 @@ class LottoGamesTest {
@ParameterizedTest
@ValueSource(ints = {3000, 4000, 15000, 34000})
public void 구매가능_게임_개수(int money) throws Exception {
assertThat(lottoGames.calculateBuyingTicketCount(money)).isEqualTo(money / LottoCommonValue.DEFAULT_LOTTO_PRICE.value());
assertThat(lottoGames.calculateBuyingTicketCount(money, 0)).isEqualTo(money / LottoCommonValue.DEFAULT_LOTTO_PRICE.value());
}

@DisplayName("개수만큼 로또 게임을 반환한다.")
@ParameterizedTest
@ValueSource(ints = {12, 100, 30, 17})
public void n개의_게임_생성(int gameCount) throws Exception {
assertThat(lottoGames.buyLottoGame(gameCount)).size()
assertThat(lottoGames.buyAutomaticLottoTickets(gameCount)).size()
.isEqualTo(gameCount);
}

@DisplayName("지난주 당첨 번호를 읽어온다.")
@ParameterizedTest
@ValueSource(strings = {"1, 2, 3, 4, 5, 6", "1, 2, 12, 23, 35, 36", "7, 8, 22,23, 35,43"})
public void 지난주_당첨_번호_파싱(String winningNumbers) throws Exception {
assertThat(lottoGames.readWinningNumber(winningNumbers));
assertThat(lottoGames.toLottoTicket(winningNumbers));
}

@DisplayName("지난주 당첨 번호를 읽는 과정에서 예외가 발생한다.")
@ParameterizedTest
@ValueSource(strings = {"1, 2, &, 4, %, 6", "*, 2, 12, 23, 35, 36", "1, 12, 8, 22,23, 35,43"})
@ValueSource(strings = {"1, 2, &, 4, %, 6", "*, 2, 12, 23, 35, 36"})
public void 지난주_당첨_번호_파싱_예외(String winningNumbers) throws Exception {
assertThatIllegalArgumentException().isThrownBy(() -> lottoGames.readWinningNumber(winningNumbers));
assertThatIllegalArgumentException().isThrownBy(() -> lottoGames.toLottoTicket(winningNumbers));
}
}
Loading