diff --git a/README.md b/README.md index c3b57585..10802ddf 100644 --- a/README.md +++ b/README.md @@ -2,42 +2,63 @@ - [x] 입력 - [x] 구입 금액을 입력한다. + - [x] 수동으로 구매할 로또 수를 입력한다. + - [x] 수동으로 구매할 번호를 입력한다. - [x] 지난 주 당첨 번호를 입력한다. - [x] 보너스 볼을 입력한다. -- [] 출력 - - [x] 구매 금액에 따른 로또 티켓 장수를 출력한다. +- [x] 출력 + - [x] 구입 금액에 따라 수동 구매 장수와 자동 구매 장수를 출력한다. - [x] 구매 금액에 따른 각 로또 티켓의 번호를 출력한다. - [x] 당첨 번호와 일치하는 로또 티켓 개수를 출력한다. - [x] 총 수익률을 출력한다. +*** + - [x] 구입 금액 + - [x] 구입 금액은 1000원 단위의 정수값이다. - [x] ERROR : 음수일 경우 예외가 발생한다. - [x] ERROR : 1000원 단위가 아닐 경우 예외가 발생한다. - - [x] 로또 티켓 금액으로 나눈 티켓 장수로 반환한다. + +- [x] 티켓 장수 + - [x] 구입 금액과 수동 로또 티켓 개수를 인자로 받아 로또 티켓 장수를 반환한다. + - [x] ERROR : 전체 티켓 장수 보다 수동 구매 장수가 많은 경우 예외가 발생한다. + - [x] 로또 티켓 머신 - - [x] 구입 금액을 1000원 단위로 나누어 로또 티켓 장수를 반환한다. - - [x] 로또 티켓 장수만큼 로또 티켓을 생성한다. + - [x] 수동으로 로또 티켓을 생성한다. + - [x] 자동으로 로또 티켓을 생성한다. + - [x] 전체 로또 티켓을 생성한다. -- [x] 로또 티켓 - - [x] 6개의 로또 번호를 가진다. +*** - [x] 로또 번호 생성기 - [x] 1 ~ 45 사이의 난수로 로또 번호를 생성한다. - [x] 6개의 로또 번호를 반환한다. - [x] 로또 번호 + - [x] 로또 번호는 1 ~ 45 사이의 값으로 로또 번호를 생성한다. - [x] ERROR : 1 ~ 45가 아닐 경우 예외가 발생한다. +- [x] 로또 티켓 + - [x] 6개의 로또 번호를 가진다. + - [x] ERROR : 로또 번호가 6개 보다 작거나 많은 경우 예외가 발생한다. + - [x] ERROR : 로또 번호가 중복될 경우 예외가 발생한다. + +- [x] 로또 순위 + - [x] 로또 티켓의 당첨 수에 따른 순위를 반환한다. + +*** + - [x] 당첨 번호 - [x] ERROR: 중복되는 로또 번호가 있을 경우 예외가 발생한다. - [x] 당첨 번호와 중복되지 않는 1 ~ 45 중 하나는 보너스 볼이다. - [x] 당첨 번호와 일치하는 로또 번호 개수를 반환한다. -- [x] 로또 상금 - - [x] 로또 티켓의 당첨 순위에 따른 당첨 금액을 반환한다. - -- [x] 로또 통계 +- [x] 당첨 결과 + - [x] 당첨 번호와 일치하는 로또 번호 개수를 반환한다. + - [x] 보너스 번호 일치하는 로또 번호 개수를 반환한다. - [x] 당첨 번호 개수가 같은 로또 티켓 개수를 반환한다. + +- [x] 당첨 통계 - [x] 당첨 순위의 수익률을 계산한다. diff --git a/settings.gradle b/settings.gradle index 4b94a0e2..1daa39f3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'java-racingcar' +rootProject.name = 'java-lotto' diff --git a/src/main/java/lotto/LottoApplication.java b/src/main/java/lotto/LottoApplication.java index 0abe19f7..0642d3d2 100644 --- a/src/main/java/lotto/LottoApplication.java +++ b/src/main/java/lotto/LottoApplication.java @@ -1,10 +1,34 @@ package lotto; import lotto.controller.LottoController; +import lotto.domain.lotto.LottoTickets; +import lotto.domain.vending.BuyingPrice; +import lotto.domain.vending.TicketAmount; +import lotto.domain.winning.WinningNumbers; +import lotto.view.InputView; +import lotto.view.OutputView; public class LottoApplication { public static void main(String[] args) { - LottoController lottoController = new LottoController(); - lottoController.run(); + try { + LottoController lottoController = new LottoController(); + + BuyingPrice buyingPrice = lottoController.getBuyingPrice(InputView.getBuyingPrice()); + TicketAmount ticketAmount = lottoController.getTicketAmount(buyingPrice.totalTicketAmount(), InputView.getManualCount()); + + LottoTickets lottoTickets = lottoController.issueTickets(ticketAmount); + OutputView.printTicketAmount(ticketAmount); + OutputView.printLottoTickets(lottoTickets); + + String winningNumber = InputView.getWinningNumber(); + String bonusNumber = InputView.getBonusNumber(); + WinningNumbers winningNumbers = lottoController.validateWinningLottoNumbers(winningNumber, bonusNumber); + float profitRate = lottoController.calculateProfitRate(ticketAmount.total(), lottoTickets, winningNumbers); + OutputView.printProfitRate(profitRate); + + } catch (RuntimeException e) { + System.out.println("=====> Error Message : " + e.getMessage()); + } } } + diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java index 67d8961f..fb2c94f7 100644 --- a/src/main/java/lotto/controller/LottoController.java +++ b/src/main/java/lotto/controller/LottoController.java @@ -1,40 +1,55 @@ package lotto.controller; -import lotto.domain.*; +import lotto.domain.lotto.LottoTickets; +import lotto.domain.rank.WinningLottoRank; +import lotto.domain.vending.BuyingPrice; +import lotto.domain.vending.TicketAmount; +import lotto.domain.winning.WinningNumbers; +import lotto.service.LottoService; +import lotto.util.StringUtil; import lotto.view.InputView; -import lotto.view.OutputView; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; -import static lotto.domain.LottoTicketVendingMachine.TICKET_PRICE; - public class LottoController { - public void run() { - String inputPrice = InputView.getBuyingPrice(); - BuyingPrice buyingPrice = new BuyingPrice(inputPrice); - int ticketAmount = buyingPrice.divide(TICKET_PRICE); - OutputView.printTicketAmount(ticketAmount); - - LottoTicketVendingMachine lottoTicketVendingMachine = new LottoTicketVendingMachine(); - List lottoTickets = lottoTicketVendingMachine.issueTickets(buyingPrice); - OutputView.printLottoTickets(lottoTickets); - - String inputWinningNumbers = InputView.getWinningNumber(); - List splitWinningNumbers = InputView.split(inputWinningNumbers) - .stream() - .map(Integer::parseInt) + private final LottoService lottoService = new LottoService(); + + public BuyingPrice getBuyingPrice(String price) throws NumberFormatException { + try { + return new BuyingPrice(Integer.parseInt(price)); + + } catch (NumberFormatException e) { + throw new IllegalArgumentException("구매 금액은 숫자로 입력해 주세요."); + } + } + + public TicketAmount getTicketAmount(int totalTicketAmount, String manualTicketAmount) { + try { + return new TicketAmount(totalTicketAmount, Integer.parseInt(manualTicketAmount)); + + } catch (NumberFormatException e) { + throw new IllegalArgumentException("수동으로 구매할 티켓 개수를 확인해 주세요."); + } + } + + public LottoTickets issueTickets(TicketAmount ticketAmount) { + List inputManualNumbers = InputView.getManualNumbers(ticketAmount.manual()); + List> manualNumbers = inputManualNumbers.stream() + .map(StringUtil::splitParseInt) .collect(Collectors.toList()); + return lottoService.issueTickets(ticketAmount.auto(), manualNumbers); + } - int bonusNumber = Integer.parseInt(InputView.getBonusNumber()); - WinningNumbers winningNumbers = new WinningNumbers(splitWinningNumbers, bonusNumber); - WinningStatistics winningStatistics = new WinningStatistics(winningNumbers); + public WinningNumbers validateWinningLottoNumbers(String inputWinningNumbers, String inputBonusNumber) { + List splitWinningNumbers = StringUtil.splitParseInt(inputWinningNumbers); + int bonusNumber = StringUtil.parseInt(inputBonusNumber); + return new WinningNumbers(splitWinningNumbers, bonusNumber); + } - Map ranks = winningStatistics.groupByWinningNumber(lottoTickets); - OutputView.printWinningStatistics(ranks); - float profitRate = winningStatistics.profitRate(ticketAmount, ranks); - OutputView.printProfitRate(profitRate); + public float calculateProfitRate(int totalTicketAmount, LottoTickets lottoTickets, WinningNumbers winningNumbers) { + WinningLottoRank winningLottoRank = lottoService.getWinningLottoRank(lottoTickets, winningNumbers); + return lottoService.getProfitRate(totalTicketAmount, winningLottoRank); } } diff --git a/src/main/java/lotto/domain/LottoGenerator.java b/src/main/java/lotto/domain/LottoGenerator.java deleted file mode 100644 index b19398f2..00000000 --- a/src/main/java/lotto/domain/LottoGenerator.java +++ /dev/null @@ -1,28 +0,0 @@ -package lotto.domain; - -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static lotto.domain.LottoNumber.MAX_LOTTO_BOUND; -import static lotto.domain.LottoNumber.MIN_LOTTO_BOUND; - -public class LottoGenerator { - - private static final List lottoNumberContainer = create(); - - private static List create() { - return IntStream.rangeClosed(MIN_LOTTO_BOUND, MAX_LOTTO_BOUND) - .mapToObj(LottoNumber::new) - .collect(Collectors.toList()); - } - - public List issueAutoLottoNumbers() { - Collections.shuffle(lottoNumberContainer); - return lottoNumberContainer.subList(0, 6).stream() - .sorted(Comparator.comparing(LottoNumber::value)) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/lotto/domain/LottoNumber.java b/src/main/java/lotto/domain/LottoNumber.java deleted file mode 100644 index 6ec92237..00000000 --- a/src/main/java/lotto/domain/LottoNumber.java +++ /dev/null @@ -1,38 +0,0 @@ -package lotto.domain; - -import java.util.Objects; - -public class LottoNumber { - public static final int MIN_LOTTO_BOUND = 1; - public static final int MAX_LOTTO_BOUND = 45; - - private final int lottoNumber; - - public LottoNumber(final int lottoNumber) { - validateBound(lottoNumber); - this.lottoNumber = lottoNumber; - } - - private void validateBound(int lottoNumber) { - if (lottoNumber < MIN_LOTTO_BOUND || lottoNumber > MAX_LOTTO_BOUND) { - throw new IllegalArgumentException("로또 번호는 1 ~ 45 까지 입니다."); - } - } - - public int value() { - return lottoNumber; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - LottoNumber that = (LottoNumber) o; - return lottoNumber == that.lottoNumber; - } - - @Override - public int hashCode() { - return Objects.hash(lottoNumber); - } -} diff --git a/src/main/java/lotto/domain/LottoPrize.java b/src/main/java/lotto/domain/LottoPrize.java deleted file mode 100644 index d8dcf0b8..00000000 --- a/src/main/java/lotto/domain/LottoPrize.java +++ /dev/null @@ -1,55 +0,0 @@ -package lotto.domain; - -import java.util.Arrays; - -public enum LottoPrize { - FIRST(6, false, 2_000_000_000), - SECOND(5, true, 30_000_000), // 보너스볼 일치 - THIRD(5, false, 1_500_000), - FOURTH(4, false, 50_000), - FIFTH(3, false, 5_000), - NONE(0, false, 0); - - private final int matchedWinningNumberCount; - private final boolean matchedBonusNumber; - private final int prizeMoney; - - LottoPrize(int matchedWinningNumberCount, boolean matchedBonusNumber, int prizeMoney) { - this.matchedWinningNumberCount = matchedWinningNumberCount; - this.matchedBonusNumber = matchedBonusNumber; - this.prizeMoney = prizeMoney; - } - - public static int prize(int matchedWinningNumberCount, boolean isMatchedBonusNumber) { - return Arrays.stream(values()) - .filter(lottoPrize -> lottoPrize.isMatchedWith(matchedWinningNumberCount, isMatchedBonusNumber)) - .mapToInt(LottoPrize::prizeMoney) - .findFirst() - .orElse(NONE.prizeMoney); - } - - public static LottoPrize findBy(int matchedCount) { - return Arrays.stream(values()) - .filter(lottoPrize -> lottoPrize.matchedWinningNumberCount == matchedCount) - .findFirst() - .orElse(NONE); - } - - private boolean isMatchedWith(int matchedWinningNumberCount, boolean matchedBonusNumber) { - return (this.matchedWinningNumberCount == matchedWinningNumberCount) - && (this.matchedBonusNumber == matchedBonusNumber); - } - - public int prizeMoney() { - return prizeMoney; - } - - public int matchedWinningNumberCount() { - return matchedWinningNumberCount; - } - - public boolean isMatchedBonusNumber() { - return matchedBonusNumber; - } -} - diff --git a/src/main/java/lotto/domain/LottoTicket.java b/src/main/java/lotto/domain/LottoTicket.java deleted file mode 100644 index 8d8ef393..00000000 --- a/src/main/java/lotto/domain/LottoTicket.java +++ /dev/null @@ -1,29 +0,0 @@ -package lotto.domain; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -public class LottoTicket { - private final List lottoNumbers; - - public LottoTicket(final List lottoNumbers) { - this.lottoNumbers = new ArrayList<>(lottoNumbers); - } - - public LottoTicket(int... lottoNumbers) { - this(Arrays.stream(lottoNumbers) - .mapToObj(LottoNumber::new) - .collect(Collectors.toList())); - } - - public List lottoNumbers() { - return Collections.unmodifiableList(lottoNumbers); - } - - public boolean contains(LottoNumber lottoNumber) { - return lottoNumbers.contains(lottoNumber); - } -} diff --git a/src/main/java/lotto/domain/LottoTicketVendingMachine.java b/src/main/java/lotto/domain/LottoTicketVendingMachine.java deleted file mode 100644 index c513367b..00000000 --- a/src/main/java/lotto/domain/LottoTicketVendingMachine.java +++ /dev/null @@ -1,17 +0,0 @@ -package lotto.domain; - -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public class LottoTicketVendingMachine { - public static final int TICKET_PRICE = 1000; - private static final LottoGenerator lottoGenerator = new LottoGenerator(); - - public List issueTickets(BuyingPrice buyingPrice) { - return IntStream.range(0, buyingPrice.divide(TICKET_PRICE)) - .mapToObj(i -> lottoGenerator.issueAutoLottoNumbers()) - .map(LottoTicket::new) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/lotto/domain/WinningNumbers.java b/src/main/java/lotto/domain/WinningNumbers.java deleted file mode 100644 index 469db5ad..00000000 --- a/src/main/java/lotto/domain/WinningNumbers.java +++ /dev/null @@ -1,56 +0,0 @@ -package lotto.domain; - -import java.util.*; -import java.util.stream.Collectors; - -public class WinningNumbers { - private static final int WINNING_NUMBER_SIZE = 6; - - private final List winningNumbers; - private final LottoNumber bonusNumber; - - public WinningNumbers(List winningNumbers, int bonusNumber) { - this(winningNumbers.stream() - .map(LottoNumber::new) - .collect(Collectors.toList()), new LottoNumber(bonusNumber)); - } - - public WinningNumbers(List winningNumbers, LottoNumber bonusNumber) { - validateDuplication(winningNumbers, bonusNumber); - this.winningNumbers = winningNumbers; - this.bonusNumber = bonusNumber; - } - - private void validateDuplication(List winningNumbers, LottoNumber bonusNumber) { - Set lottoNumbers = new HashSet<>(winningNumbers); - - if (lottoNumbers.size() < WINNING_NUMBER_SIZE || winningNumbers.contains(bonusNumber)) { - throw new IllegalArgumentException("당첨 번호가 중복됩니다."); - } - } - - public int matchedWinningNumberCount(LottoTicket lottoTicket) { - return (int) winningNumbers.stream() - .filter(winningNumber -> lottoTicket.contains(winningNumber)) - .count(); - } - - public boolean isMatchedBonusNumber(LottoTicket lottoTicket) { - return lottoTicket.contains(bonusNumber); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - WinningNumbers that = (WinningNumbers) o; - return Objects.equals(winningNumbers, that.winningNumbers) && - Objects.equals(bonusNumber, that.bonusNumber); - } - - - @Override - public int hashCode() { - return Objects.hash(winningNumbers, bonusNumber); - } -} diff --git a/src/main/java/lotto/domain/WinningStatistics.java b/src/main/java/lotto/domain/WinningStatistics.java deleted file mode 100644 index 9a5c3a65..00000000 --- a/src/main/java/lotto/domain/WinningStatistics.java +++ /dev/null @@ -1,62 +0,0 @@ -package lotto.domain; - -import java.util.*; -import java.util.stream.Collectors; - -import static lotto.domain.LottoPrize.*; -import static lotto.domain.LottoTicketVendingMachine.TICKET_PRICE; - -public class WinningStatistics { - private final WinningNumbers winningNumbers; - private final Map ranks = new LinkedHashMap<>(); - - public WinningStatistics(WinningNumbers winningNumbers) { - this.winningNumbers = winningNumbers; - initRanks(); - } - - private void initRanks() { - Arrays.stream(values()).sorted(Comparator.reverseOrder()) - .filter(v -> v.matchedWinningNumberCount() > NONE.matchedWinningNumberCount()) - .forEach(v -> ranks.put(v, 0)); - } - - public Map groupByWinningNumber(List lottoTickets) { - List matchedCounts = matchedCountList(lottoTickets); - - for (int i = 0; i < matchedCounts.size(); i++) { - int matchedCount = matchedCounts.get(i); - - if (matchedCount < FIFTH.matchedWinningNumberCount()) { - continue; - } - - if (matchedCount == SECOND.matchedWinningNumberCount() && winningNumbers.isMatchedBonusNumber(lottoTickets.get(i))) { - ranks.put(SECOND, ranks.get(SECOND) + 1); - continue; - } - - LottoPrize matchedLottoPrize = findBy(matchedCount); - ranks.put(matchedLottoPrize, ranks.get(matchedLottoPrize) + 1); - } - - return ranks; - } - - private List matchedCountList(List lottoTickets) { - return lottoTickets.stream() - .map(lottoTicket -> winningNumbers.matchedWinningNumberCount(lottoTicket)) - .collect(Collectors.toList()); - } - - public float profitRate(int ticketAmount, Map lottoPrize) { - int totalPrize = totalPrize(lottoPrize); - return (float) totalPrize / (ticketAmount * TICKET_PRICE); - } - - private int totalPrize(Map lottoPrizes) { - return lottoPrizes.keySet().stream() - .mapToInt(lottoPrize -> lottoPrize.prizeMoney() * lottoPrizes.get(lottoPrize)) - .sum(); - } -} diff --git a/src/main/java/lotto/domain/lotto/LottoNumber.java b/src/main/java/lotto/domain/lotto/LottoNumber.java new file mode 100644 index 00000000..d65bf67c --- /dev/null +++ b/src/main/java/lotto/domain/lotto/LottoNumber.java @@ -0,0 +1,36 @@ +package lotto.domain.lotto; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class LottoNumber { + public static final int MIN_LOTTO_BOUND = 1; + public static final int MAX_LOTTO_BOUND = 45; + private static final Map LOTTO_NUMBER_BOX = generate(); + + private final int lottoNumber; + + private LottoNumber(int lottoNumber) { + this.lottoNumber = lottoNumber; + } + + public static LottoNumber from(int number) { + Optional lottoNumber = Optional.ofNullable(LOTTO_NUMBER_BOX.get(number)); + return lottoNumber.orElseThrow(() -> new IllegalArgumentException("로또 번호는 1 ~ 45 까지 입니다.")); + } + + private static Map generate() { + return IntStream.rangeClosed(MIN_LOTTO_BOUND, MAX_LOTTO_BOUND) + .mapToObj(LottoNumber::new) + .collect(Collectors.toMap( + LottoNumber::value, Function.identity() + )); + } + + public int value() { + return lottoNumber; + } +} diff --git a/src/main/java/lotto/domain/lotto/LottoTicket.java b/src/main/java/lotto/domain/lotto/LottoTicket.java new file mode 100644 index 00000000..7bf0c74c --- /dev/null +++ b/src/main/java/lotto/domain/lotto/LottoTicket.java @@ -0,0 +1,53 @@ +package lotto.domain.lotto; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +public class LottoTicket { + public static final int LOTTO_NUMBER_SIZE = 6; + + private final Set lottoNumbers; + + private LottoTicket(Set lottoNumbers) { + validation(lottoNumbers); + this.lottoNumbers = lottoNumbers; + } + + public LottoTicket(int... lottoNumbers) { + this(Arrays.stream(lottoNumbers) + .mapToObj(LottoNumber::from) + .collect(Collectors.toSet())); + } + + public static LottoTicket from(Set lottoNumbers) { + return new LottoTicket(lottoNumbers); + } + + private static void validation(Set lottoNumbers) { + validateSize(lottoNumbers); + validateDuplication(new HashSet<>(lottoNumbers)); + } + + private static void validateSize(Set lottoNumberSet) { + if (lottoNumberSet.size() > LOTTO_NUMBER_SIZE) { + throw new IllegalArgumentException("로또 번호는 6개 입니다."); + } + } + + private static void validateDuplication(Set lottoNumberSet) { + if (lottoNumberSet.size() < LOTTO_NUMBER_SIZE) { + throw new IllegalArgumentException("로또 번호가 중복됩니다."); + } + } + + public Set lottoNumbers() { + return Collections.unmodifiableSet(lottoNumbers); + } + + public boolean contains(LottoNumber lottoNumber) { + return lottoNumbers.contains(lottoNumber); + } +} diff --git a/src/main/java/lotto/domain/lotto/LottoTickets.java b/src/main/java/lotto/domain/lotto/LottoTickets.java new file mode 100644 index 00000000..496873d0 --- /dev/null +++ b/src/main/java/lotto/domain/lotto/LottoTickets.java @@ -0,0 +1,36 @@ +package lotto.domain.lotto; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class LottoTickets { + private final List lottoTickets; + + public LottoTickets() { + this.lottoTickets = new ArrayList<>(); + } + + public LottoTickets(List lottoTickets) { + this.lottoTickets = new ArrayList<>(lottoTickets); + } + + public LottoTickets(LottoTicket lottoTicket) { + this.lottoTickets = Collections.singletonList(lottoTicket); + } + + public void add(List lottoTickets) { + if (lottoTickets == null) { + throw new NullPointerException("로또 티켓이 비어 있습니다."); + } + this.lottoTickets.addAll(lottoTickets); + } + + public int size() { + return lottoTickets.size(); + } + + public List values() { + return lottoTickets; + } +} diff --git a/src/main/java/lotto/domain/rank/LottoRank.java b/src/main/java/lotto/domain/rank/LottoRank.java new file mode 100644 index 00000000..b6b130f7 --- /dev/null +++ b/src/main/java/lotto/domain/rank/LottoRank.java @@ -0,0 +1,40 @@ +package lotto.domain.rank; + +import java.util.Arrays; + +public enum LottoRank { + LOSE(0, 0), + FIFTH(3, 5_000), + FOURTH(4, 50_000), + THIRD(5, 1_500_000), + SECOND(5, 30_000_000), + FIRST(6, 2_000_000_000); + + private final int hitCount; + private final int prizeMoney; + + LottoRank(int hitCount, int prizeMoney) { + this.hitCount = hitCount; + this.prizeMoney = prizeMoney; + } + + public static LottoRank findBy(int hitCount, boolean isBonus) { + if (hitCount == 5 && isBonus) { + return LottoRank.SECOND; + } + + return Arrays.stream(values()) + .filter(rank -> rank.hitCount == hitCount) + .findFirst() + .orElse(LottoRank.LOSE); + } + + public int hitCount() { + return hitCount; + } + + public int prizeMoney() { + return prizeMoney; + } +} + diff --git a/src/main/java/lotto/domain/rank/WinningLottoRank.java b/src/main/java/lotto/domain/rank/WinningLottoRank.java new file mode 100644 index 00000000..f6abf382 --- /dev/null +++ b/src/main/java/lotto/domain/rank/WinningLottoRank.java @@ -0,0 +1,45 @@ +package lotto.domain.rank; + +import lotto.domain.lotto.LottoTicket; +import lotto.domain.lotto.LottoTickets; +import lotto.domain.winning.WinningNumbers; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class WinningLottoRank { + private final WinningNumbers winningNumbers; + private final Map winningLottoRank; + + public WinningLottoRank(LottoTickets lottoTickets, WinningNumbers winningNumbers) { + this.winningNumbers = winningNumbers; + this.winningLottoRank = new LinkedHashMap<>(); + initRank(lottoTickets); + } + + private void initRank(LottoTickets lottoTickets) { + Arrays.stream(LottoRank.values()) + .forEach(rank -> winningLottoRank.put(rank, 0)); + groupByWinningCount(lottoTickets); + } + + private void groupByWinningCount(LottoTickets lottoTickets) { + lottoTickets.values().forEach(this::addWinningCount); + winningLottoRank.remove(LottoRank.LOSE); + } + + private void addWinningCount(LottoTicket lottoTicket) { + LottoRank key = LottoRank.findBy(winningNumbers.hitCount(lottoTicket), winningNumbers.hitBonus(lottoTicket)); + winningLottoRank.put(key, winningLottoRank.get(key) + 1); + } + + public int count(LottoRank lottoRank) { + return winningLottoRank.get(lottoRank); + } + + public Set keys() { + return winningLottoRank.keySet(); + } +} diff --git a/src/main/java/lotto/domain/BuyingPrice.java b/src/main/java/lotto/domain/vending/BuyingPrice.java similarity index 57% rename from src/main/java/lotto/domain/BuyingPrice.java rename to src/main/java/lotto/domain/vending/BuyingPrice.java index 3213e64d..93ad7d87 100644 --- a/src/main/java/lotto/domain/BuyingPrice.java +++ b/src/main/java/lotto/domain/vending/BuyingPrice.java @@ -1,42 +1,36 @@ -package lotto.domain; +package lotto.domain.vending; import java.util.Objects; public class BuyingPrice { + public static final int TICKET_PRICE = 1000; + private final int buyingPrice; - public BuyingPrice(final int buyingPrice) { + public BuyingPrice(int buyingPrice) { validate(buyingPrice); this.buyingPrice = buyingPrice; } - public BuyingPrice(String buyingPrice) { - this(Integer.parseInt(buyingPrice)); - } - private void validate(int buyingPrice) { validateMinimum(buyingPrice); - validateFactor(buyingPrice); + validateDivisible(buyingPrice); } private void validateMinimum(int buyingPrice) { - if (buyingPrice < LottoTicketVendingMachine.TICKET_PRICE) { - throw new IllegalArgumentException("구입 금액은 1,000원 이상이여야 합니다."); + if (buyingPrice < TICKET_PRICE) { + throw new IllegalArgumentException("구입 금액은 " + TICKET_PRICE + "원 이상이여야 합니다."); } } - private void validateFactor(int buyingPrice) { - if ((buyingPrice % LottoTicketVendingMachine.TICKET_PRICE) != 0) { - throw new IllegalArgumentException("구입 금액은 1,000원 단위여야 합니다."); + private void validateDivisible(int buyingPrice) { + if ((buyingPrice % TICKET_PRICE) != 0) { + throw new IllegalArgumentException("구입 금액은 " + TICKET_PRICE + "원 단위여야 합니다."); } } - public int value() { - return buyingPrice; - } - - public int divide(final int ticketPrice) { - return buyingPrice / ticketPrice; + public int totalTicketAmount() { + return buyingPrice / TICKET_PRICE; } @Override diff --git a/src/main/java/lotto/domain/vending/LottoTicketVendingMachine.java b/src/main/java/lotto/domain/vending/LottoTicketVendingMachine.java new file mode 100644 index 00000000..26f57949 --- /dev/null +++ b/src/main/java/lotto/domain/vending/LottoTicketVendingMachine.java @@ -0,0 +1,47 @@ +package lotto.domain.vending; + +import lotto.domain.lotto.LottoNumber; +import lotto.domain.lotto.LottoTicket; +import lotto.domain.lotto.LottoTickets; + +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static lotto.domain.lotto.LottoTicket.LOTTO_NUMBER_SIZE; + +public class LottoTicketVendingMachine { + + public LottoTickets issueTickets(int autoTicketAmount, List> manualLottoNumbers) { + LottoTickets lottoTickets = new LottoTickets(); + lottoTickets.add(manualLottoTickets(manualLottoNumbers)); + lottoTickets.add(autoIssueTickets(autoTicketAmount)); + return lottoTickets; + } + + public List autoIssueTickets(int autoTicketAmount) { + return IntStream.range(0, autoTicketAmount) + .mapToObj(i -> issueAutoLottoNumbers()) + .map(LottoTicket::from) + .collect(Collectors.toList()); + } + + public Set issueAutoLottoNumbers() { + return new Random().ints(1, 46) + .distinct() + .limit(LOTTO_NUMBER_SIZE) + .mapToObj(LottoNumber::from) + .collect(Collectors.toSet()); + } + + public List manualLottoTickets(List> manualLottoNumbers) { + return manualLottoNumbers.stream() + .map(manualNumbers -> manualNumbers.stream() + .map(LottoNumber::from) + .collect(Collectors.toSet())) + .map(LottoTicket::from) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/lotto/domain/vending/TicketAmount.java b/src/main/java/lotto/domain/vending/TicketAmount.java new file mode 100644 index 00000000..3ac1ecaf --- /dev/null +++ b/src/main/java/lotto/domain/vending/TicketAmount.java @@ -0,0 +1,30 @@ +package lotto.domain.vending; + +public class TicketAmount { + private final int manualTicketAmount; + private final int autoTicketAmount; + + public TicketAmount(int totalTicketAmount, int manualTicketAmount) { + validation(totalTicketAmount, manualTicketAmount); + this.manualTicketAmount = manualTicketAmount; + this.autoTicketAmount = totalTicketAmount - manualTicketAmount; + } + + private void validation(int totalTicketAmount, int manualCount) { + if (totalTicketAmount < manualCount) { + throw new IllegalArgumentException("수동 구매 티켓이 전체 티켓 보다 많습니다."); + } + } + + public int manual() { + return manualTicketAmount; + } + + public int auto() { + return autoTicketAmount; + } + + public int total() { + return manualTicketAmount + autoTicketAmount; + } +} diff --git a/src/main/java/lotto/domain/winning/WinningNumbers.java b/src/main/java/lotto/domain/winning/WinningNumbers.java new file mode 100644 index 00000000..222b2daa --- /dev/null +++ b/src/main/java/lotto/domain/winning/WinningNumbers.java @@ -0,0 +1,51 @@ +package lotto.domain.winning; + +import lotto.domain.lotto.LottoNumber; +import lotto.domain.lotto.LottoTicket; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static lotto.domain.lotto.LottoTicket.LOTTO_NUMBER_SIZE; + +public class WinningNumbers { + private final List winningNumbers; + private final LottoNumber bonusNumber; + + public WinningNumbers(List winningNumbers, LottoNumber bonusNumber) { + validateSize(winningNumbers.size()); + validateDuplication(new HashSet<>(winningNumbers), bonusNumber); + this.winningNumbers = winningNumbers; + this.bonusNumber = bonusNumber; + } + + public WinningNumbers(List winningNumbers, int bonusNumber) { + this(winningNumbers.stream() + .map(LottoNumber::from) + .collect(Collectors.toList()), LottoNumber.from(bonusNumber)); + } + + private void validateSize(int winningNumberSize) { + if (winningNumberSize != LOTTO_NUMBER_SIZE) { + throw new IllegalArgumentException("수동 로또 번호 입력 개수는 6개 입니다."); + } + } + + private void validateDuplication(Set winningNumbers, LottoNumber bonusNumber) { + if (winningNumbers.size() < LOTTO_NUMBER_SIZE || winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException("당첨 번호가 중복됩니다."); + } + } + + public int hitCount(LottoTicket lottoTicket) { + return (int) winningNumbers.stream() + .filter(lottoTicket::contains) + .count(); + } + + public boolean hitBonus(LottoTicket lottoTicket) { + return lottoTicket.contains(bonusNumber); + } +} diff --git a/src/main/java/lotto/domain/winning/WinningStatistics.java b/src/main/java/lotto/domain/winning/WinningStatistics.java new file mode 100644 index 00000000..87db4ecf --- /dev/null +++ b/src/main/java/lotto/domain/winning/WinningStatistics.java @@ -0,0 +1,24 @@ +package lotto.domain.winning; + +import lotto.domain.rank.WinningLottoRank; + +import static lotto.domain.vending.BuyingPrice.TICKET_PRICE; + +public class WinningStatistics { + private final WinningLottoRank winningLottoRank; + + public WinningStatistics(WinningLottoRank winningLottoRank) { + this.winningLottoRank = winningLottoRank; + } + + public float profitRate(int totalTicketAmount) { + int totalPrize = totalPrize(); + return (float) totalPrize / (totalTicketAmount * TICKET_PRICE); + } + + private int totalPrize() { + return winningLottoRank.keys().stream() + .mapToInt(rank -> rank.prizeMoney() * winningLottoRank.count(rank)) + .sum(); + } +} diff --git a/src/main/java/lotto/service/LottoService.java b/src/main/java/lotto/service/LottoService.java new file mode 100644 index 00000000..573a3357 --- /dev/null +++ b/src/main/java/lotto/service/LottoService.java @@ -0,0 +1,33 @@ +package lotto.service; + +import lotto.domain.lotto.LottoTickets; +import lotto.domain.rank.WinningLottoRank; +import lotto.domain.vending.LottoTicketVendingMachine; +import lotto.domain.winning.WinningNumbers; +import lotto.domain.winning.WinningStatistics; +import lotto.view.OutputView; + +import java.util.List; + +public class LottoService { + private final LottoTicketVendingMachine vendingMachine; + + public LottoService() { + this.vendingMachine = new LottoTicketVendingMachine(); + } + + public LottoTickets issueTickets(int autoTicketAmount, List> manualNumbers) { + return vendingMachine.issueTickets(autoTicketAmount, manualNumbers); + } + + public float getProfitRate(int totalTicketAmount, WinningLottoRank winningLottoRank) { + WinningStatistics winningStatistics = new WinningStatistics(winningLottoRank); + return winningStatistics.profitRate(totalTicketAmount); + } + + public WinningLottoRank getWinningLottoRank(LottoTickets lottoTickets, WinningNumbers winningNumbers) { + WinningLottoRank winningLottoRank = new WinningLottoRank(lottoTickets, winningNumbers); + OutputView.printWinningStatistics(winningLottoRank); + return winningLottoRank; + } +} diff --git a/src/main/java/lotto/util/StringUtil.java b/src/main/java/lotto/util/StringUtil.java new file mode 100644 index 00000000..0dd5939c --- /dev/null +++ b/src/main/java/lotto/util/StringUtil.java @@ -0,0 +1,43 @@ +package lotto.util; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class StringUtil { + public static final String DELIMITER = ","; + public static final int LOTTO_NUMBER_SIZE = 6; + + private StringUtil() { + } + + public static List split(String input) { + List splitStrings = Arrays.stream(input.split(DELIMITER)) + .map(String::trim) + .collect(Collectors.toList()); + if (splitStrings.size() > LOTTO_NUMBER_SIZE) { + throw new IllegalArgumentException("로또 번호는 6개 입니다."); + } + return splitStrings; + } + + public static int parseInt(String input) { + try { + return Integer.parseInt(input); + + } catch (NumberFormatException e) { + throw new IllegalArgumentException("로또 번호는 1 ~ 45 사이의 숫자를 입력해 주세요."); + } + } + + public static List splitParseInt(String input) { + try { + return split(input).stream() + .map(Integer::parseInt) + .collect(Collectors.toList()); + + } catch (NumberFormatException e) { + throw new IllegalArgumentException("로또 번호는 1 ~ 45 사이의 숫자를 입력해 주세요."); + } + } +} diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java index 26e8175a..bda7d6fb 100644 --- a/src/main/java/lotto/view/InputView.java +++ b/src/main/java/lotto/view/InputView.java @@ -1,16 +1,19 @@ package lotto.view; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Scanner; import java.util.stream.Collectors; +import java.util.stream.IntStream; public class InputView { - private static final Scanner scanner = new Scanner(System.in); - private static final String BUYING_PRICE_MESSAGE = "구입금액을 입력해 주세요."; - private static final String WINNING_NUMBER_MESSAGE = "지난 주 당첨 번호를 입력해 주세요."; + private static final String BUYING_PRICE_MESSAGE = "\n구입금액을 입력해 주세요."; + private static final String MANUAL_COUNT_MESSAGE = "\n수동으로 구매할 로또 수를 입력해 주세요."; + private static final String MANUAL_NUMBER_MESSAGE = "\n수동으로 구매할 번호를 입력해 주세요."; + private static final String WINNING_NUMBER_MESSAGE = "\n지난 주 당첨 번호를 입력해 주세요."; private static final String BONUS_NUMBER_MESSAGE = "보너스 볼을 입력해 주세요."; - private static final String DELIMITER = ","; + + private static final Scanner scanner = new Scanner(System.in); private InputView() { } @@ -20,6 +23,21 @@ public static String getBuyingPrice() { return scanner.nextLine().trim(); } + public static String getManualCount() { + System.out.println(MANUAL_COUNT_MESSAGE); + return scanner.nextLine().trim(); + } + + public static List getManualNumbers(int manualTicketAmount) { + if (manualTicketAmount == 0) { + return Collections.emptyList(); + } + System.out.println(MANUAL_NUMBER_MESSAGE); + return IntStream.range(0, manualTicketAmount) + .mapToObj(i -> scanner.nextLine().trim()) + .collect(Collectors.toList()); + } + public static String getWinningNumber() { System.out.println(WINNING_NUMBER_MESSAGE); return scanner.nextLine().trim(); @@ -29,10 +47,4 @@ public static String getBonusNumber() { System.out.println(BONUS_NUMBER_MESSAGE); return scanner.nextLine().trim(); } - - public static List split(String input) { - return Arrays.stream(input.split(DELIMITER)) - .map(String::trim) - .collect(Collectors.toList()); - } } diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java index c47306d4..ac908f7c 100644 --- a/src/main/java/lotto/view/OutputView.java +++ b/src/main/java/lotto/view/OutputView.java @@ -1,15 +1,20 @@ package lotto.view; -import lotto.domain.LottoPrize; -import lotto.domain.LottoTicket; +import lotto.domain.lotto.LottoTicket; +import lotto.domain.lotto.LottoTickets; +import lotto.domain.rank.LottoRank; +import lotto.domain.rank.WinningLottoRank; +import lotto.domain.vending.TicketAmount; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; public class OutputView { - public static void printLottoTickets(List lottoTickets) { - for (LottoTicket lottoTicket : lottoTickets) { + private OutputView() { + } + + public static void printLottoTickets(LottoTickets lottoTickets) { + for (LottoTicket lottoTicket : lottoTickets.values()) { List lottoNumbers = lottoNumberToString(lottoTicket); System.out.println("[" + String.join(",", lottoNumbers) + "]"); } @@ -21,24 +26,20 @@ private static List lottoNumberToString(LottoTicket lottoTicket) { .collect(Collectors.toList()); } - public static void printTicketAmount(int ticketAmount) { - System.out.printf("%s개를 구매했습니다.%n", ticketAmount); + public static void printTicketAmount(TicketAmount ticketAmount) { + System.out.printf("%n수동으로 %d장, 자동으로 %d장 구매했습니다.%n", ticketAmount.manual(), ticketAmount.auto()); } public static void printProfitRate(float profitRate) { System.out.printf("총 수익률은 %.2f 입니다.%n", profitRate); } - public static void printWinningStatistics(Map ranks) { - System.out.println("당첨 통계\n---------"); - for (LottoPrize lottoPrize : ranks.keySet()) { - if (lottoPrize.isMatchedBonusNumber()) { - System.out.printf("%d개 일치, 보너스 볼 일치 (%d원) - %d개%n", lottoPrize.matchedWinningNumberCount(), - lottoPrize.prizeMoney(), ranks.get(lottoPrize)); - continue; - } - System.out.printf("%d개 일치 (%d원) - %d개%n", lottoPrize.matchedWinningNumberCount(), - lottoPrize.prizeMoney(), ranks.get(lottoPrize)); + public static void printWinningStatistics(WinningLottoRank winningLottoRank) { + System.out.println("\n당첨 통계\n---------"); + for (LottoRank rank : winningLottoRank.keys()) { + String message = (rank == LottoRank.SECOND) ? "%d개 일치, 보너스 볼 일치 (%d원) - %d개%n" + : "%d개 일치 (%d원) - %d개%n"; + System.out.printf(message, rank.hitCount(), rank.prizeMoney(), winningLottoRank.count(rank)); } } } diff --git a/src/test/java/lotto/BuyingPriceTest.java b/src/test/java/lotto/BuyingPriceTest.java deleted file mode 100644 index 273820b9..00000000 --- a/src/test/java/lotto/BuyingPriceTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package lotto; - -import lotto.domain.BuyingPrice; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -public class BuyingPriceTest { - - @Test - @DisplayName("구입 금액을 인자로 받아 구입 금액 객체를 생성한다") - void create() { - //given //when - BuyingPrice buyingPrice = new BuyingPrice(1000); - - //then - assertThat(buyingPrice).isEqualTo(new BuyingPrice(1000)); - } - - @Test - @DisplayName("1000원 미만일 경우 예외가 발생한다") - void validate_minimum() { - //given - int invalidPrice = 999; - - //when //then - assertThatIllegalArgumentException() - .isThrownBy(() -> new BuyingPrice(invalidPrice)) - .withMessage("구입 금액은 1,000원 이상이여야 합니다."); - } - - @Test - @DisplayName("1000 단위가 아닐 경우 예외가 발생한다") - void validate_factor() { - //given - int invalidPrice = 1001; - - //when //then - assertThatIllegalArgumentException() - .isThrownBy(() -> new BuyingPrice(invalidPrice)) - .withMessage("구입 금액은 1,000원 단위여야 합니다."); - } - - @Test - @DisplayName("입력받은 티켓 금액으로 나눈 값을 반환한다") - void divide_ticket_price() { - //given - int ticketPrice = 1000; - BuyingPrice buyingPrice = new BuyingPrice(10000); - - //when - int ticketAmount = buyingPrice.divide(ticketPrice); - - //then - assertThat(ticketAmount).isEqualTo(10); - } -} diff --git a/src/test/java/lotto/LottoGeneratorTest.java b/src/test/java/lotto/LottoGeneratorTest.java deleted file mode 100644 index b7805796..00000000 --- a/src/test/java/lotto/LottoGeneratorTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package lotto; - -import lotto.domain.LottoGenerator; -import lotto.domain.LottoNumber; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class LottoGeneratorTest { - - @Test - @DisplayName("6개의 로또 번호를 반환한다") - void lotto_generator() { - //given - LottoGenerator lottoGenerator = new LottoGenerator(); - - //when - List lottoNumbers = lottoGenerator.issueAutoLottoNumbers(); - - //then - assertThat(lottoNumbers).hasSize(6); - } -} diff --git a/src/test/java/lotto/LottoPrizeTest.java b/src/test/java/lotto/LottoPrizeTest.java deleted file mode 100644 index 9fceb103..00000000 --- a/src/test/java/lotto/LottoPrizeTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package lotto; - -import lotto.domain.LottoPrize; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import static org.assertj.core.api.Assertions.assertThat; - -public class LottoPrizeTest { - - @ParameterizedTest - @CsvSource(value = {"6, false, 2000000000", "5, true, 30000000", "5, false, 1500000", "4, false, 50000", "3, false, 5000"}) - @DisplayName("로또 당첨 순위를 확인한다") - void lotto_prizes(int matchedWinningNumberCount, boolean isMatchedBonusNumber, int prizeMoney) { - //given - - //when - int prize = LottoPrize.prize(matchedWinningNumberCount, isMatchedBonusNumber); - - //then - assertThat(prize).isEqualTo(prizeMoney); - } -} diff --git a/src/test/java/lotto/LottoTicketTest.java b/src/test/java/lotto/LottoTicketTest.java deleted file mode 100644 index e11140bc..00000000 --- a/src/test/java/lotto/LottoTicketTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package lotto; - -import lotto.domain.LottoGenerator; -import lotto.domain.LottoNumber; -import lotto.domain.LottoTicket; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class LottoTicketTest { - - @Test - @DisplayName("로또 넘버 6개를 인자로 받아 로또 티켓 객체를 생성한다") - void create() { - //given - LottoGenerator lottoGenerator = new LottoGenerator(); - List lottoNumbers = lottoGenerator.issueAutoLottoNumbers(); - - //when - LottoTicket lottoTicket = new LottoTicket(lottoNumbers); - - //then - assertThat(lottoTicket.lottoNumbers()).hasSize(6); - } -} diff --git a/src/test/java/lotto/LottoTicketVendingMachineTest.java b/src/test/java/lotto/LottoTicketVendingMachineTest.java deleted file mode 100644 index 35521cbe..00000000 --- a/src/test/java/lotto/LottoTicketVendingMachineTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package lotto; - -import lotto.domain.BuyingPrice; -import lotto.domain.LottoTicket; -import lotto.domain.LottoTicketVendingMachine; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class LottoTicketVendingMachineTest { - - @Test - @DisplayName("로또 티켓 장수만큼 로또 티켓을 생성한다.") - void issue_tickets_from_buying_price() { - //given - BuyingPrice buyingPrice = new BuyingPrice(5000); - LottoTicketVendingMachine lottoTicketVendingMachine = new LottoTicketVendingMachine(); - - //when - List tickets = lottoTicketVendingMachine.issueTickets(buyingPrice); - - //then - assertThat(tickets).hasSize(5); - } - -} diff --git a/src/test/java/lotto/WinningNumbersTest.java b/src/test/java/lotto/WinningNumbersTest.java deleted file mode 100644 index ba41c512..00000000 --- a/src/test/java/lotto/WinningNumbersTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package lotto; - -import lotto.domain.LottoTicket; -import lotto.domain.WinningNumbers; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -public class WinningNumbersTest { - @Test - @DisplayName("6개의 로또 당첨 번호를 입력받아 당첨 번호를 객체를 생성한다") - void create() { - //given - List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 6); - int bonusNumber = 7; - - //when - WinningNumbers winningNumbers = new WinningNumbers(inputNumbers, bonusNumber); - - //then - assertThat(winningNumbers).isEqualTo(new WinningNumbers(inputNumbers, bonusNumber)); - } - - @Test - @DisplayName("당첨 번호가 중복되는 경우 예외가 발생한다") - void validate_duplication_winning_number() { - //given - List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 5); - int bonusNumber = 6; - - //when //then - assertThatIllegalArgumentException() - .isThrownBy(() -> new WinningNumbers(inputNumbers, bonusNumber)) - .withMessage("당첨 번호가 중복됩니다."); - } - - @Test - @DisplayName("당첨 번호와 보너스 번호가 중복되는 경우 예외가 발생한다") - void validate_duplication_bonus_number() { - //given - List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 6); - int bonusNumber = 6; - - //when //then - assertThatIllegalArgumentException() - .isThrownBy(() -> new WinningNumbers(inputNumbers, bonusNumber)) - .withMessage("당첨 번호가 중복됩니다."); - } - - @ParameterizedTest - @MethodSource("generateLottoTicket") - @DisplayName("당첨 번호와 일치하는 로또 번호 개수를 반환한다") - void matched_winning_numbers_count(int[] numbers, int expectedCount) { - //given - LottoTicket lottoTicket = new LottoTicket(numbers); - WinningNumbers winningNumbers = new WinningNumbers(Arrays.asList(1, 2, 3, 4, 5, 6), 45); - - //when - int matchedWinningNumber = winningNumbers.matchedWinningNumberCount(lottoTicket); - - //then - assertThat(matchedWinningNumber).isEqualTo(expectedCount); - } - - @Test - @DisplayName("보너스 번호와 일치하는 로또 번호가 있는지 확인한다") - void matched_bonus_number() { - //given - LottoTicket lottoTicket = new LottoTicket(1, 2, 3, 4, 5, 6); - WinningNumbers winningNumbers = new WinningNumbers(Arrays.asList(4, 5, 6, 7, 8, 9), 1); - - //when - boolean isMatchedBonusNumber = winningNumbers.isMatchedBonusNumber(lottoTicket); - - //then - assertThat(isMatchedBonusNumber).isTrue(); - } - - private static Stream generateLottoTicket() { - return Stream.of( - Arguments.of(new int[]{1, 2, 3, 4, 5, 6}, 6), - Arguments.of(new int[]{1, 2, 3, 4, 5, 7}, 5), - Arguments.of(new int[]{1, 2, 3, 4, 7, 8}, 4), - Arguments.of(new int[]{1, 2, 3, 7, 8, 9}, 3), - Arguments.of(new int[]{1, 2, 7, 8, 9, 10}, 2), - Arguments.of(new int[]{1, 7, 8, 9, 10, 11}, 1), - Arguments.of(new int[]{8, 9, 10, 11, 12, 13}, 0) - ); - } -} diff --git a/src/test/java/lotto/WinningStatisticsTest.java b/src/test/java/lotto/WinningStatisticsTest.java deleted file mode 100644 index 9f801d0e..00000000 --- a/src/test/java/lotto/WinningStatisticsTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package lotto; - -import lotto.domain.LottoPrize; -import lotto.domain.LottoTicket; -import lotto.domain.WinningNumbers; -import lotto.domain.WinningStatistics; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; - -public class WinningStatisticsTest { - private WinningNumbers winningNumbers = generateWinningNumber(); - - private WinningNumbers generateWinningNumber() { - List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 6); - int bonusNumber = 45; - return new WinningNumbers(inputNumbers, bonusNumber); - } - - @Test - @DisplayName("당첨 번호 개수가 같은 로또 티켓 개수를 반환한다") - void group_by_matched_count() { - //given - WinningStatistics winningStatistics = new WinningStatistics(winningNumbers); - List lottoTickets = generateLottoTicketList(); - - //when - Map ranks = winningStatistics.groupByWinningNumber(lottoTickets); - - //then - assertThat(ranks.get(LottoPrize.FIFTH)).isEqualTo(1); - assertThat(ranks.get(LottoPrize.FOURTH)).isEqualTo(1); - assertThat(ranks.get(LottoPrize.THIRD)).isZero(); - assertThat(ranks.get(LottoPrize.FIRST)).isZero(); - } - - @Test - @DisplayName("당첨 순위의 수익률을 계산한다") - void profit_rate() { - //given - WinningStatistics winningStatistics = new WinningStatistics(winningNumbers); - Map ranks = winningStatistics.groupByWinningNumber(generateLottoTicketList()); - - //when - float profit = winningStatistics.profitRate(5, ranks); - - //then - assertThat(profit).isEqualTo(11.0f); - } - - private ArrayList generateLottoTicketList() { - ArrayList lottoTicketList = new ArrayList<>(); - lottoTicketList.add(new LottoTicket(3, 4, 5, 6, 7, 8)); // 4개 일치 - 1 - lottoTicketList.add(new LottoTicket(4, 5, 6, 7, 8, 9)); // 3개 일치 - 1 - lottoTicketList.add(new LottoTicket(5, 6, 7, 8, 9, 10)); // 2개 일치 - lottoTicketList.add(new LottoTicket(6, 7, 8, 9, 10, 11)); // 1개 일치 - lottoTicketList.add(new LottoTicket(7, 8, 9, 10, 11, 12)); // 0개 일치 - return lottoTicketList; - } -} diff --git a/src/test/java/lotto/LottoNumberTest.java b/src/test/java/lotto/domain/lotto/LottoNumberTest.java similarity index 56% rename from src/test/java/lotto/LottoNumberTest.java rename to src/test/java/lotto/domain/lotto/LottoNumberTest.java index cea197ee..fefdb14c 100644 --- a/src/test/java/lotto/LottoNumberTest.java +++ b/src/test/java/lotto/domain/lotto/LottoNumberTest.java @@ -1,24 +1,23 @@ -package lotto; +package lotto.domain.lotto; -import lotto.domain.LottoNumber; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class LottoNumberTest { @Test - @DisplayName("로또 번호를 생성한다.") + @DisplayName("로또 번호 값 객체를 생성한다") void create() { //given - LottoNumber lottoNumber = new LottoNumber(1); + LottoNumber lottoNumber = LottoNumber.from(1); //when //then - assertThat(lottoNumber).isEqualTo(new LottoNumber(1)); + assertThat(lottoNumber).isEqualTo(LottoNumber.from(1)); } @ParameterizedTest() @@ -26,8 +25,7 @@ void create() { @DisplayName("로또 번호가 1 ~ 45이 아닐 경우 예외가 발생한다") void validate_lotto_number(int invalidLottoNumber) { //given //when //then - Assertions.assertThatIllegalArgumentException() - .isThrownBy(() -> new LottoNumber(invalidLottoNumber)) - .withMessage("로또 번호는 1 ~ 45 까지 입니다."); + assertThatThrownBy(() -> LottoNumber.from(invalidLottoNumber)) + .isInstanceOf(IllegalArgumentException.class); } } \ No newline at end of file diff --git a/src/test/java/lotto/domain/lotto/LottoTicketTest.java b/src/test/java/lotto/domain/lotto/LottoTicketTest.java new file mode 100644 index 00000000..720af5ca --- /dev/null +++ b/src/test/java/lotto/domain/lotto/LottoTicketTest.java @@ -0,0 +1,44 @@ +package lotto.domain.lotto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LottoTicketTest { + + @Test + @DisplayName("로또 넘버 6개를 인자로 받아 로또 티켓 객체를 생성한다") + void create() { + //given //when + LottoTicket lottoTicket = new LottoTicket(1, 2, 3, 4, 5, 6); + + //then + assertThat(lottoTicket.lottoNumbers()).hasSize(6); + } + + @Test + @DisplayName("로또 넘버가 6개 보다 적은 경우 예외가 발생한다.") + void less_then_lotto_size() { + //given //when //then + assertThatThrownBy(() -> new LottoTicket(1, 2, 3, 4, 5)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("로또 넘버가 6개 보다 많은 경우 예외가 발생한다.") + void more_then_lotto_size() { + //given //when //then + assertThatThrownBy(() -> new LottoTicket(1, 2, 3, 4, 5, 6, 7)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("로또 번호가 중복될 경우 예외가 발생한다") + void duplicate_lotto_numbers() { + //given //when //then + assertThatThrownBy(() -> new LottoTicket(1, 1, 2, 3, 4, 5)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/lotto/domain/rank/LottoRankTest.java b/src/test/java/lotto/domain/rank/LottoRankTest.java new file mode 100644 index 00000000..59092517 --- /dev/null +++ b/src/test/java/lotto/domain/rank/LottoRankTest.java @@ -0,0 +1,23 @@ +package lotto.domain.rank; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class LottoRankTest { + + @ParameterizedTest + @CsvSource(value = {"6, false, FIRST", "5, true, SECOND", "5, false, THIRD", "4, false, FOURTH", "3, false, FIFTH"}) + @DisplayName("로또 티의 당첨 갯수개에 따른 순위를 반환한다") + void lotto_hit_count_rank(int hitCount, boolean hitBonus, String ranking) { + //given + + //when + LottoRank rank = LottoRank.findBy(hitCount, hitBonus); + + //then + assertThat(rank).isEqualTo(LottoRank.valueOf(ranking)); + } +} diff --git a/src/test/java/lotto/domain/rank/WinningLottoRankTest.java b/src/test/java/lotto/domain/rank/WinningLottoRankTest.java new file mode 100644 index 00000000..02887887 --- /dev/null +++ b/src/test/java/lotto/domain/rank/WinningLottoRankTest.java @@ -0,0 +1,100 @@ +package lotto.domain.rank; + +import lotto.domain.lotto.LottoTicket; +import lotto.domain.lotto.LottoTickets; +import lotto.domain.winning.WinningNumbers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class WinningLottoRankTest { + @Test + @DisplayName("당첨 번호 개수가 같은 로또 티켓 개수를 반환한다") + void group_by_rank_count() { + //given + WinningNumbers winningNumbers = generateWinningNumber(45); + LottoTickets lottoTickets = generateLottoTickets(); + + //when + WinningLottoRank winningLottoRank = new WinningLottoRank(lottoTickets, winningNumbers); + + //thenR + assertThat(winningLottoRank.count(LottoRank.FIFTH)).isEqualTo(1); + assertThat(winningLottoRank.count(LottoRank.FOURTH)).isEqualTo(1); + assertThat(winningLottoRank.count(LottoRank.THIRD)).isZero(); + assertThat(winningLottoRank.count(LottoRank.FIRST)).isZero(); + } + + @ParameterizedTest + @MethodSource("matchedBonusTickets") + @DisplayName("보너스 번호가 일치하는 로또 순위를 반환한다") + void matched_bonus_tickets(LottoTicket lottoTicket, LottoRank lottoRank) { + //given + LottoTickets lottoTickets = new LottoTickets(lottoTicket); + + //when + WinningNumbers winningNumbers = generateWinningNumber(45); + WinningLottoRank winningLottoRank = new WinningLottoRank(lottoTickets, winningNumbers); + + //then + assertThat(winningLottoRank.count(lottoRank)).isEqualTo(1); + } + + @MethodSource("notMatchedBonusTickets") + @DisplayName("보너스 번호가 일치하지 않는 로또 순위를 반환한다") + void not_matched_bonus_tickets(LottoTicket lottoTicket, LottoRank lottoRank) { + //given + LottoTickets lottoTickets = new LottoTickets(lottoTicket); + + //when + WinningNumbers winningNumbers = generateWinningNumber(45); + WinningLottoRank winningLottoRank = new WinningLottoRank(lottoTickets, winningNumbers); + + //then + assertThat(winningLottoRank.count(lottoRank)).isEqualTo(1); + } + + private WinningNumbers generateWinningNumber(int bonusNumber) { + List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 6); + return new WinningNumbers(inputNumbers, bonusNumber); + } + + private LottoTickets generateLottoTickets() { + List lottoTickets = new ArrayList<>(); + lottoTickets.add(new LottoTicket(3, 4, 5, 6, 7, 8)); // 4개 일치 + lottoTickets.add(new LottoTicket(4, 5, 6, 7, 8, 9)); // 3개 일치 + lottoTickets.add(new LottoTicket(5, 6, 7, 8, 9, 10)); // 2개 일치 + lottoTickets.add(new LottoTicket(6, 7, 8, 9, 10, 11)); // 1개 일치 + lottoTickets.add(new LottoTicket(7, 8, 9, 10, 11, 12)); // 0개 일치 + return new LottoTickets(lottoTickets); + } + + private static Stream matchedBonusTickets() { + return Stream.of( + Arguments.of(new LottoTicket(1, 2, 3, 4, 5, 6), LottoRank.FIRST), + Arguments.of(new LottoTicket(1, 2, 3, 4, 5, 45), LottoRank.SECOND), + Arguments.of(new LottoTicket(2, 3, 4, 5, 6, 7), LottoRank.THIRD), + Arguments.of(new LottoTicket(3, 4, 5, 6, 7, 45), LottoRank.FOURTH), + Arguments.of(new LottoTicket(4, 5, 6, 7, 8, 45), LottoRank.FIFTH) + ); + } + + private static Stream notMatchedBonusTickets() { + return Stream.of( + Arguments.of(new LottoTicket(1, 2, 3, 4, 5, 6), LottoRank.FIRST), + Arguments.of(new LottoTicket(1, 2, 3, 4, 5, 45), LottoRank.SECOND), + Arguments.of(new LottoTicket(2, 3, 4, 5, 6, 7), LottoRank.THIRD), + Arguments.of(new LottoTicket(3, 4, 5, 6, 7, 8), LottoRank.FOURTH), + Arguments.of(new LottoTicket(4, 5, 6, 7, 8, 9), LottoRank.FIFTH) + ); + } +} diff --git a/src/test/java/lotto/domain/vending/BuyingPriceTest.java b/src/test/java/lotto/domain/vending/BuyingPriceTest.java new file mode 100644 index 00000000..d7603889 --- /dev/null +++ b/src/test/java/lotto/domain/vending/BuyingPriceTest.java @@ -0,0 +1,43 @@ +package lotto.domain.vending; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BuyingPriceTest { + + @Test + @DisplayName("구입 금액 값 객체를 생성한다") + void create() { + //given //when + BuyingPrice buyingPrice = new BuyingPrice(1000); + + //then + assertThat(buyingPrice).isEqualTo(new BuyingPrice(1000)); + } + + @Test + @DisplayName("1000원 미만일 경우 예외가 발생한다") + void validate_minimum() { + //given + int invalidPrice = 999; + + //when //then + assertThatThrownBy(() -> new BuyingPrice(invalidPrice)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("1000원 단위가 아닐 경우 예외가 발생한다") + void validate_factor() { + //given + int invalidPrice = 1001; + + //when //then + assertThatThrownBy(() -> new BuyingPrice(invalidPrice)) + .isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/lotto/domain/vending/LottoTicketVendingMachineTest.java b/src/test/java/lotto/domain/vending/LottoTicketVendingMachineTest.java new file mode 100644 index 00000000..d8916f41 --- /dev/null +++ b/src/test/java/lotto/domain/vending/LottoTicketVendingMachineTest.java @@ -0,0 +1,92 @@ +package lotto.domain.vending; + +import lotto.domain.lotto.LottoNumber; +import lotto.domain.lotto.LottoTicket; +import lotto.domain.lotto.LottoTickets; +import lotto.service.LottoService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; + +class LottoTicketVendingMachineTest { + + private final LottoService lottoService = new LottoService(); + private final LottoTicketVendingMachine vendingMachine = new LottoTicketVendingMachine(); + + @Test + @DisplayName("6개의 로또 번호를 반환한다") + void lotto_generator() { + //given + + //when + Set lottoNumbers = vendingMachine.issueAutoLottoNumbers(); + + //then + assertThat(lottoNumbers).hasSize(6); + } + + @Test + @DisplayName("자동으로 로또 티켓을 생성한다") + void auto_issue_tickets() { + //given + BuyingPrice buyingPrice = new BuyingPrice(5000); + TicketAmount ticketAmount = new TicketAmount(buyingPrice.totalTicketAmount(), 0); + + //when + List tickets = vendingMachine.autoIssueTickets(ticketAmount.auto()); + + //then + assertThat(tickets).hasSize(5); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2}) + @DisplayName("수동으로 로또 티켓을 생성한다") + void manual_issue_tickets(int index) { + //given + int manualCount = 3; + List> manualNumbers = generateManualNumbers(manualCount); + + //when + List lottoTickets = vendingMachine.manualLottoTickets(manualNumbers); + + //then + assertThat(lottoTickets.get(index).lottoNumbers()).isEqualTo(generateLottoTicket(index).lottoNumbers()); + } + + + @Test + @DisplayName("전체 로또 티켓을 생성한다") + void total_lotto_tickets() { + //given + BuyingPrice buyingPrice = new BuyingPrice(8000); + int manualCount = 3; + TicketAmount ticketAmount = new TicketAmount(buyingPrice.totalTicketAmount(), manualCount); + List> manualNumbers = generateManualNumbers(manualCount); + + //when + LottoTickets lottoTickets = vendingMachine.issueTickets(ticketAmount.auto(), manualNumbers); + + //then + assertThat(lottoTickets.size()).isEqualTo(ticketAmount.total()); + } + + private List> generateManualNumbers(int manualCount) { + return IntStream.range(0, manualCount) + .mapToObj(i -> Arrays.asList(i + 1, i + 2, i + 3, i + 4, i + 5, i + 6)) + .collect(Collectors.toList()); + } + + private LottoTicket generateLottoTicket(int i) { + return new LottoTicket(i + 1, i + 2, i + 3, i + 4, i + 5, i + 6); + } +} diff --git a/src/test/java/lotto/domain/vending/TicketAmountTest.java b/src/test/java/lotto/domain/vending/TicketAmountTest.java new file mode 100644 index 00000000..937cf61e --- /dev/null +++ b/src/test/java/lotto/domain/vending/TicketAmountTest.java @@ -0,0 +1,41 @@ +package lotto.domain.vending; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class TicketAmountTest { + + @ParameterizedTest + @CsvSource(value = {"5000, 1, 4", "10000, 3, 7", "12000, 2, 10"}) + @DisplayName("구입 금액과 수동 로또 티켓 개수를 인자로 받아 로또 티켓 장수를 반환한다") + void ticket_amount(int price, int manualCount, int exceptedAutoCount) { + //given + BuyingPrice buyingPrice = new BuyingPrice(price); + + //when + TicketAmount ticketAmount = new TicketAmount(buyingPrice.totalTicketAmount(), manualCount); + + //then + assertThat(ticketAmount.manual()).isEqualTo(manualCount); + assertThat(ticketAmount.auto()).isEqualTo(exceptedAutoCount); + } + + @Test + @DisplayName("전체 티켓 장수 보다 수동 구매 장수가 많은 경우 예외가 발생한다") + void manual_ticket_amount_more_than_total_amount() { + //given + BuyingPrice buyingPrice = new BuyingPrice(5000); + int totalTicketAmount = buyingPrice.totalTicketAmount(); + int manualTicketCount = 10; + + //when //then + assertThatThrownBy(() -> new TicketAmount(totalTicketAmount, manualTicketCount)) + .isInstanceOf(IllegalArgumentException.class); + + } +} diff --git a/src/test/java/lotto/domain/winning/WinningNumbersTest.java b/src/test/java/lotto/domain/winning/WinningNumbersTest.java new file mode 100644 index 00000000..fca8f6e1 --- /dev/null +++ b/src/test/java/lotto/domain/winning/WinningNumbersTest.java @@ -0,0 +1,86 @@ +package lotto.domain.winning; + +import lotto.domain.lotto.LottoTicket; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class WinningNumbersTest { + @Test + @DisplayName("당첨 번호가 중복되는 경우 예외가 발생한다") + void validate_duplication_winning_number() { + //given + List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 5); + int bonusNumber = 6; + + //when //then + assertThatThrownBy(() -> new WinningNumbers(inputNumbers, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("당첨 번호와 보너스 번호가 중복되는 경우 예외가 발생한다") + void validate_duplication_bonus_number() { + //given + List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 6); + int bonusNumber = 6; + + //when //then + assertThatThrownBy(() -> new WinningNumbers(inputNumbers, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @MethodSource("hitLottoNumbers") + @DisplayName("당첨 번호와 일치하는 로또 번호 개수를 반환한다") + void hit_winning_numbers_count(LottoTicket lottoTicket, int expectedCount) { + //given + WinningNumbers winningNumbers = generateWinningNumber(45); + + //when + int hitCount = winningNumbers.hitCount(lottoTicket); + + //then + assertThat(hitCount).isEqualTo(expectedCount); + } + + @Test + @DisplayName("보너스 번호 일치하는 로또 번호 개수를 반환한다") + void hit_bonus_number_count() { + //given + LottoTicket lottoTicket = new LottoTicket(5, 10, 15, 20, 25, 30); + WinningNumbers winningNumbers = generateWinningNumber(30); + + //when + boolean isBonus = winningNumbers.hitBonus(lottoTicket); + + //then + assertThat(isBonus).isTrue(); + } + + private WinningNumbers generateWinningNumber(int bonusNumber) { + List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 6); + return new WinningNumbers(inputNumbers, bonusNumber); + } + + private static Stream hitLottoNumbers() { + return Stream.of( + Arguments.of(new LottoTicket(1, 2, 3, 4, 5, 6), 6), + Arguments.of(new LottoTicket(2, 3, 4, 5, 6, 7), 5), + Arguments.of(new LottoTicket(3, 4, 5, 6, 7, 8), 4), + Arguments.of(new LottoTicket(4, 5, 6, 7, 8, 9), 3), + Arguments.of(new LottoTicket(5, 6, 7, 8, 9, 10), 2), + Arguments.of(new LottoTicket(6, 7, 8, 9, 10, 11), 1), + Arguments.of(new LottoTicket(7, 8, 9, 10, 11, 12), 0) + ); + } +} diff --git a/src/test/java/lotto/domain/winning/WinningStatisticsTest.java b/src/test/java/lotto/domain/winning/WinningStatisticsTest.java new file mode 100644 index 00000000..76b3c42f --- /dev/null +++ b/src/test/java/lotto/domain/winning/WinningStatisticsTest.java @@ -0,0 +1,52 @@ +package lotto.domain.winning; + +import lotto.domain.lotto.LottoTicket; +import lotto.domain.lotto.LottoTickets; +import lotto.domain.rank.WinningLottoRank; +import lotto.domain.vending.BuyingPrice; +import lotto.domain.vending.TicketAmount; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static lotto.domain.vending.BuyingPrice.TICKET_PRICE; +import static org.assertj.core.api.Assertions.assertThat; + +class WinningStatisticsTest { + private WinningNumbers generateWinningNumber(int bonusNumber) { + List inputNumbers = Arrays.asList(1, 2, 3, 4, 5, 6); + return new WinningNumbers(inputNumbers, bonusNumber); + } + + @Test + @DisplayName("당첨 순위의 수익률을 계산한다") + void profit_rate() { + //given + LottoTickets lottoTickets = generateLottoTickets(); + BuyingPrice buyingPrice = new BuyingPrice(lottoTickets.size() * TICKET_PRICE); + TicketAmount ticketAmount = new TicketAmount(buyingPrice.totalTicketAmount(), lottoTickets.size()); + + WinningNumbers winningNumbers = generateWinningNumber(45); + WinningLottoRank winningLottoRank = new WinningLottoRank(lottoTickets, winningNumbers); + + //when + WinningStatistics winningStatistics = new WinningStatistics(winningLottoRank); + float profit = winningStatistics.profitRate(ticketAmount.total()); + + //then + assertThat(profit).isEqualTo(11.0f); + } + + private LottoTickets generateLottoTickets() { + List lottoTickets = new ArrayList<>(); + lottoTickets.add(new LottoTicket(3, 4, 5, 6, 7, 8)); // 4개 일치 + lottoTickets.add(new LottoTicket(4, 5, 6, 7, 8, 9)); // 3개 일치 + lottoTickets.add(new LottoTicket(5, 6, 7, 8, 9, 10)); // 2개 일치 + lottoTickets.add(new LottoTicket(6, 7, 8, 9, 10, 11)); // 1개 일치 + lottoTickets.add(new LottoTicket(7, 8, 9, 10, 11, 12)); // 0개 일치 + return new LottoTickets(lottoTickets); + } +}