diff --git a/README.md b/README.md index 7c9722c..0f0927a 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,60 @@ 총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임) ``` +# 🚀 로또 2단계 - 수동 구매 + +## 기능 요구사항 +- 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다. +- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다. + +## 프로그래밍 요구사항 +- 예외가 발생하는 부분에 대해 자바 Exception을 적용해 예외처리한다. +- 사용자가 입력한 값에 대한 예외 처리를 철저히 한다. + +### 실행 결과 +``` +구입금액을 입력해 주세요. +14000 + +수동으로 구매할 로또 수를 입력해 주세요. +3 + +수동으로 구매할 번호를 입력해 주세요. +8, 21, 23, 41, 42, 43 +3, 5, 11, 16, 32, 38 +7, 11, 16, 35, 36, 44 + +수동으로 3장, 자동으로 11개를 구매했습니다. +[8, 21, 23, 41, 42, 43] +[3, 5, 11, 16, 32, 38] +[7, 11, 16, 35, 36, 44] +[1, 8, 11, 31, 41, 42] +[13, 14, 16, 38, 42, 45] +[7, 11, 30, 40, 42, 43] +[2, 13, 22, 32, 38, 45] +[23, 25, 33, 36, 39, 41] +[1, 3, 5, 14, 22, 45] +[5, 9, 38, 41, 43, 44] +[2, 8, 9, 18, 19, 21] +[13, 14, 18, 21, 23, 35] +[17, 21, 29, 37, 42, 45] +[3, 8, 27, 30, 35, 44] + +지난 주 당첨 번호를 입력해 주세요. +1, 2, 3, 4, 5, 6 +보너스 볼을 입력해 주세요. +7 + +당첨 통계 +--------- +3개 일치 (5000원)- 1개 +4개 일치 (50000원)- 0개 +5개 일치 (1500000원)- 0개 +5개 일치, 보너스 볼 일치(30000000원) - 0개 +6개 일치 (2000000000원)- 0개 +총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임) +``` + ## 프로그래밍 요구사항 - indent(인덴트, 들여쓰기) depth를 2단계에서 1단계로 줄여라. - depth의 경우 if문을 사용하는 경우 1단계의 depth가 증가한다. if문 안에 while문을 사용한다면 depth가 2단계가 된다. @@ -51,6 +105,75 @@ - 규칙 8: 일급 콜렉션을 쓴다. ## 힌트 + - 로또 자동 생성은 Collections.shuffle() 메소드 활용한다. - Collections.sort() 메소드를 활용해 정렬 가능하다. - ArrayList의 contains() 메소드를 활용하면 어떤 값이 존재하는지 유무를 판단할 수 있다. + +___ + +### 1단계 기능 목록 + +- [x] 구입금액 입력 + - [x] 예외 +- [x] 구입한 로또 개수 출력 +- [x] 개수만큼 로또 번호 출력 + - [x] 번호 자동 생성 +- [x] 지난주 당첨 번호와 보너스 볼 입력 + - [x] 예외 +- [x] 번호 비교 +- [x] 당첨 통계 출력 +- [x] 수익률 계산 및 출력 + +### 예외 + +- 구입 금액 입력 + - [x] 구입금액이 0원 이하인 경우 +- 당첨 번호와 보너스 볼 입력 + - [x] 지난 주 당첨 번호가 중복되었을 경우 + - [x] 당첨 번호가 1-45가 아닐 경우 + - [x] 당첨 번호 입력에 숫자와 쉼표가 아닌 글자가 들어간 경우 + - [x] 당첨 번호 개수가 6개가 아닐 경우 + +### 2단계 기능 목록 + +- [x] 숫자를 가진 클래스 분류 +- [x] 수동으로 구매할 로또 수 입력 +- [x] 수동으로 구매할 번호 입력 + - [x] 수동 로또 생성 +- [x] Printer %s, %f 사용 +- [x] LotteryComparator 기능을 Lotteries로 합치기 +- [ ] PrizeMoney로 당첨 횟수 세기 + +### 논의거리 + +- 일급컬렉션, 원시값 래핑 잘 했는지 +- 테스트 커버리지 + +- BonusNumber 생성자에 winningNumbers가 들어가는거 +- validate 메서드들 try-catch 해주기 +- WinningStatistics if분기 어떻게 처리할건지 +- enum에서 값을 변경해도 되는지 + +### LotteryNumber + +- 최소(1), 최대(45) 값 +- validate range + +### 숫자를 가지는 클래스 + +- Numbers: 6개 List + - Lottery가 만들어질 때 TargetNumbers로부터 받음 + - 당첨 번호 6개와 비교 +- [x] LotteryNumber + - 최댓값, 최솟값, 검증 로직 가지는 부모 클래스 +- [x] TargetNumbers: 45개 -> AutoNumbersGenerator + - 자동 생성 + - shuffle, 앞의 6개 숫자 가져오기 -> Numbers + - 범위 검증 필요 X + - 최댓값, 최솟값 필요 O +- [x] WinningNumbers: 6개 -> ManualNumbersGenerator + - 입력 받음, 수동 생성 + - 범위 검증 필요 +- [x] BonusNumber: 1개 + - 범위 검증 필요 diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 0000000..d6202a0 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,45 @@ +import java.util.List; + +import domain.lotteryStore.Lotteries; +import domain.lotteryStore.LotteryStore; +import domain.lotteryStore.PurchasePrice; +import domain.lotteryStore.numbers.BonusNumber; +import domain.lotteryStore.numbers.ManualNumbersGenerator; +import domain.winningStatistics.PrizeMoney; +import domain.winningStatistics.WinningStatistics; +import ui.Printer; +import ui.Receiver; + +public class Application { + private final Receiver receiver; + private final Printer printer; + + public Application() { + receiver = new Receiver(); + printer = new Printer(); + } + + public static void main(String[] args) { + Application application = new Application(); + application.run(); + } + + public void run() { + PurchasePrice purchasePrice = new PurchasePrice(receiver.receivePurchasePrice()); + int purchasedCount = purchasePrice.getPurchasedCount(); + int manualCount = receiver.receiveManualCount(); + List manualNumbers = receiver.receiveManualNumbers(manualCount); + printer.printPurchasedCount(manualCount, purchasedCount); + + Lotteries lotteries = LotteryStore.createLotteries(purchasedCount, manualCount, manualNumbers); + printer.printPurchasedLotteries(lotteries); + + ManualNumbersGenerator winningNumbers = new ManualNumbersGenerator(receiver.receiveWinningNumbers()); + BonusNumber bonusNumber = new BonusNumber(receiver.receiveBonusNumber(), winningNumbers); + + WinningStatistics winningStatistics = lotteries.compareWithWinningNumbersAndBonusNumber(winningNumbers, bonusNumber); + + printer.printWinningStatistics(winningStatistics); + printer.printTotalEarningsRate(PrizeMoney.calculateEarningsRate(winningStatistics, purchasedCount)); + } +} diff --git a/src/main/java/domain/lotteryStore/Lotteries.java b/src/main/java/domain/lotteryStore/Lotteries.java new file mode 100644 index 0000000..099561a --- /dev/null +++ b/src/main/java/domain/lotteryStore/Lotteries.java @@ -0,0 +1,42 @@ +package domain.lotteryStore; + +import java.util.List; + +import domain.lotteryStore.numbers.BonusNumber; +import domain.lotteryStore.numbers.ManualNumbersGenerator; +import domain.winningStatistics.ComparisonResult; +import domain.winningStatistics.WinningStatistics; + +public class Lotteries { + private final List lotteries; + + public Lotteries(List lotteries) { + this.lotteries = lotteries; + } + + public WinningStatistics compareWithWinningNumbersAndBonusNumber(ManualNumbersGenerator winningNumbers, + BonusNumber bonusNumber) { + WinningStatistics winningStatistics = new WinningStatistics(); + for (Lottery lottery : lotteries) { + ComparisonResult comparisonResult = compareOneTicketNumbers(winningNumbers, bonusNumber, lottery); + comparisonResult.checkRanking(winningStatistics); + } + + return winningStatistics; + } + + public ComparisonResult compareOneTicketNumbers(ManualNumbersGenerator winningNumbers, BonusNumber bonusNumber, + Lottery lottery) { + int countOfMatchingNumbers = lottery.getCountOfMatchingWinningNumbers(winningNumbers); + boolean hasBonusNumber = lottery.hasBonusNumber(bonusNumber); + return new ComparisonResult(countOfMatchingNumbers, hasBonusNumber); + } + + public Lottery get(int index) { + return lotteries.get(index); + } + + public List getLotteries() { + return lotteries; + } +} diff --git a/src/main/java/domain/lotteryStore/Lottery.java b/src/main/java/domain/lotteryStore/Lottery.java new file mode 100644 index 0000000..531286a --- /dev/null +++ b/src/main/java/domain/lotteryStore/Lottery.java @@ -0,0 +1,25 @@ +package domain.lotteryStore; + +import domain.lotteryStore.numbers.BonusNumber; +import domain.lotteryStore.numbers.ManualNumbersGenerator; +import domain.lotteryStore.numbers.Numbers; + +public class Lottery { + private final Numbers numbers; + + public Lottery(Numbers numbers) { + this.numbers = numbers; + } + + public Numbers getNumbers() { + return numbers; + } + + public int getCountOfMatchingWinningNumbers(ManualNumbersGenerator winningNumbers) { + return numbers.countMatchingNumbers(winningNumbers); + } + + public boolean hasBonusNumber(BonusNumber bonusNumber) { + return numbers.contains(bonusNumber.getBonusNumber()); + } +} diff --git a/src/main/java/domain/lotteryStore/LotteryStore.java b/src/main/java/domain/lotteryStore/LotteryStore.java new file mode 100644 index 0000000..c509fdd --- /dev/null +++ b/src/main/java/domain/lotteryStore/LotteryStore.java @@ -0,0 +1,46 @@ +package domain.lotteryStore; + +import java.util.ArrayList; +import java.util.List; + +import domain.lotteryStore.numbers.AutoNumbersGenerator; +import domain.lotteryStore.numbers.ManualNumbersGenerator; +import domain.lotteryStore.numbers.Numbers; + +public class LotteryStore { + public static Lotteries createLotteries(int purchasedCount, int manualCount, List manualNumbers) { + List lotteries = new ArrayList<>(); + List manualLotteries = makeManualLotteries(manualCount, manualNumbers); + List autoLotteries = makeAutoLotteries(purchasedCount, manualCount); + + lotteries.addAll(manualLotteries); + lotteries.addAll(autoLotteries); + + return new Lotteries(lotteries); + } + + private static List makeManualLotteries(int manualCount, List manualNumbers) { + List manualLotteries = new ArrayList<>(); + for (int i = 0; i < manualCount; i++) { + Numbers numbers = new Numbers(new ManualNumbersGenerator(manualNumbers.get(i)).getManualNumbers()); + manualLotteries.add(new Lottery(numbers)); + } + return manualLotteries; + } + + private static List makeAutoLotteries(int purchasedCount, int manualCount) { + AutoNumbersGenerator autoNumbersGenerator = new AutoNumbersGenerator(); + List autoLotteries = new ArrayList<>(); + int autoCount = purchasedCount - manualCount; + while (autoCount-- > 0) { + autoLotteries.add(makeAutoLottery(autoNumbersGenerator)); + } + return autoLotteries; + } + + private static Lottery makeAutoLottery(AutoNumbersGenerator autoNumbersGenerator) { + autoNumbersGenerator.shuffle(); + Numbers numbers = new Numbers(autoNumbersGenerator.getSixNumbersFromTheFront()); + return new Lottery(numbers); + } +} diff --git a/src/main/java/domain/lotteryStore/PurchasePrice.java b/src/main/java/domain/lotteryStore/PurchasePrice.java new file mode 100644 index 0000000..aeefe66 --- /dev/null +++ b/src/main/java/domain/lotteryStore/PurchasePrice.java @@ -0,0 +1,24 @@ +package domain.lotteryStore; + +import ui.message.ExceptionMessage; + +public class PurchasePrice { + public static final int PRICE_OF_ONE_LOTTERY_TICKET = 1000; + + private final int purchasePrice; + + public PurchasePrice(int purchasePrice) { + validateSmallerThanPurchasePrice(purchasePrice); + this.purchasePrice = purchasePrice; + } + + private void validateSmallerThanPurchasePrice(int purchasePrice) { + if (purchasePrice < PRICE_OF_ONE_LOTTERY_TICKET) { + throw new IllegalArgumentException(ExceptionMessage.MUST_BUY_MORE_THAN_ONE_TICKET.getMessage()); + } + } + + public int getPurchasedCount() { + return purchasePrice / PRICE_OF_ONE_LOTTERY_TICKET; + } +} diff --git a/src/main/java/domain/lotteryStore/numbers/AutoNumbersGenerator.java b/src/main/java/domain/lotteryStore/numbers/AutoNumbersGenerator.java new file mode 100644 index 0000000..79574da --- /dev/null +++ b/src/main/java/domain/lotteryStore/numbers/AutoNumbersGenerator.java @@ -0,0 +1,30 @@ +package domain.lotteryStore.numbers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class AutoNumbersGenerator { + private final List autoNumbers = new ArrayList<>(); + + public AutoNumbersGenerator() { + initiateNumbers(); + } + + private void initiateNumbers() { + for (int i = LotteryNumberRange.MINIMUM_LOTTERY_NUMBER; i <= LotteryNumberRange.MAXIMUM_LOTTERY_NUMBER; i++) { + autoNumbers.add(i); + } + } + + public void shuffle() { + Collections.shuffle(autoNumbers); + } + + public List getSixNumbersFromTheFront() { + return autoNumbers.stream() + .limit(6) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/domain/lotteryStore/numbers/BonusNumber.java b/src/main/java/domain/lotteryStore/numbers/BonusNumber.java new file mode 100644 index 0000000..be26e63 --- /dev/null +++ b/src/main/java/domain/lotteryStore/numbers/BonusNumber.java @@ -0,0 +1,24 @@ +package domain.lotteryStore.numbers; + +import ui.message.ExceptionMessage; + +public class BonusNumber extends LotteryNumberRange { + private final int bonusNumber; + + public BonusNumber(int bonusNumber, ManualNumbersGenerator winningNumbers) { + validateRange(bonusNumber); + validateDuplicate(bonusNumber, winningNumbers); + this.bonusNumber = bonusNumber; + } + + public void validateDuplicate(int bonusNumber, ManualNumbersGenerator winningNumbers) { + if (winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException( + ExceptionMessage.BONUS_NUMBER_CANNOT_EQUAL_WINNING_NUMBERS.getMessage()); + } + } + + public int getBonusNumber() { + return bonusNumber; + } +} diff --git a/src/main/java/domain/lotteryStore/numbers/LotteryNumberRange.java b/src/main/java/domain/lotteryStore/numbers/LotteryNumberRange.java new file mode 100644 index 0000000..681d9c5 --- /dev/null +++ b/src/main/java/domain/lotteryStore/numbers/LotteryNumberRange.java @@ -0,0 +1,18 @@ +package domain.lotteryStore.numbers; + +import ui.message.ExceptionMessage; + +public class LotteryNumberRange { + public static final int MINIMUM_LOTTERY_NUMBER = 1; + public static final int MAXIMUM_LOTTERY_NUMBER = 45; + + public void validateRange(int number) { + if (!isInValidRange(number)) { + throw new IllegalArgumentException(ExceptionMessage.MUST_INPUT_NUMBERS_IN_VALID_RANGE.getMessage()); + } + } + + private boolean isInValidRange(Integer number) { + return number >= MINIMUM_LOTTERY_NUMBER && number <= MAXIMUM_LOTTERY_NUMBER; + } +} diff --git a/src/main/java/domain/lotteryStore/numbers/ManualNumbersGenerator.java b/src/main/java/domain/lotteryStore/numbers/ManualNumbersGenerator.java new file mode 100644 index 0000000..73ccb63 --- /dev/null +++ b/src/main/java/domain/lotteryStore/numbers/ManualNumbersGenerator.java @@ -0,0 +1,70 @@ +package domain.lotteryStore.numbers; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import ui.message.ExceptionMessage; + +public class ManualNumbersGenerator extends LotteryNumberRange { + private static final String CHARACTERS_CONTAINING_NUMBER_COMMA = "^[0-9,\\s]+$"; + private static final int SIZE_OF_NUMBERS = 6; + private static final String DELIMITER = ","; + private static final String CHARACTER_BEFORE_REPLACING = " "; + private static final String CHARACTER_AFTER_REPLACING = ""; + + private final List manualNumbers; + + public ManualNumbersGenerator(String manualNumbers) { + validateOtherCharacterSymbols(manualNumbers); + this.manualNumbers = splitWinningNumbers(manualNumbers); + validateSplitInput(); + } + + private void validateOtherCharacterSymbols(String input) { + if (!Pattern.matches(CHARACTERS_CONTAINING_NUMBER_COMMA, input)) { + throw new IllegalArgumentException(ExceptionMessage.INPUT_ONLY_NUMBERS_AND_COMMA.getMessage()); + } + } + + private List splitWinningNumbers(String input) { + return Arrays.stream(input.replaceAll(CHARACTER_BEFORE_REPLACING, CHARACTER_AFTER_REPLACING) + .split(DELIMITER)) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } + + private void validateSplitInput() { + validateSizeOfWinningNumbers(); + validateRangeOfLotteryNumbers(); + validateDuplicateWinningNumbers(); + } + + private void validateSizeOfWinningNumbers() { + if (manualNumbers.size() != SIZE_OF_NUMBERS) { + throw new IllegalArgumentException(ExceptionMessage.MUST_INPUT_SIX_WINNING_NUMBERS.getMessage()); + } + } + + private void validateRangeOfLotteryNumbers() { + manualNumbers.forEach(super::validateRange); + } + + private void validateDuplicateWinningNumbers() { + Set numbers = new HashSet<>(manualNumbers); + if (numbers.size() != SIZE_OF_NUMBERS) { + throw new IllegalArgumentException(ExceptionMessage.DUPLICATE_NUMBERS.getMessage()); + } + } + + public boolean contains(int numberToCompare) { + return manualNumbers.contains(numberToCompare); + } + + public List getManualNumbers() { + return manualNumbers; + } +} diff --git a/src/main/java/domain/lotteryStore/numbers/Numbers.java b/src/main/java/domain/lotteryStore/numbers/Numbers.java new file mode 100644 index 0000000..2813a33 --- /dev/null +++ b/src/main/java/domain/lotteryStore/numbers/Numbers.java @@ -0,0 +1,28 @@ +package domain.lotteryStore.numbers; + +import java.util.Collections; +import java.util.List; + +public class Numbers { + private final List numbers; + + public Numbers(List numbers) { + Collections.sort(numbers); + this.numbers = numbers; + } + + public int countMatchingNumbers(ManualNumbersGenerator winningNumbers) { + return (int)winningNumbers.getManualNumbers() + .stream() + .filter(this::contains) + .count(); + } + + public boolean contains(int numberToCompare) { + return numbers.contains(numberToCompare); + } + + public List getNumbers() { + return numbers; + } +} diff --git a/src/main/java/domain/winningStatistics/ComparisonResult.java b/src/main/java/domain/winningStatistics/ComparisonResult.java new file mode 100644 index 0000000..85fbfee --- /dev/null +++ b/src/main/java/domain/winningStatistics/ComparisonResult.java @@ -0,0 +1,36 @@ +package domain.winningStatistics; + +public class ComparisonResult { + private final int matchingCount; + private final boolean havingBonusNumber; + + public ComparisonResult(int matchingCount, boolean havingBonusNumber) { + this.matchingCount = matchingCount; + this.havingBonusNumber = havingBonusNumber; + } + + public void checkRanking(WinningStatistics winningStatistics) { + for (PrizeMoney prizeMoney : PrizeMoney.values()) { + checkWinnings(winningStatistics, prizeMoney); + } + } + + private void checkWinnings(WinningStatistics winningStatistics, PrizeMoney prizeMoney) { + if (prizeMoney.isSameMatchingCount(matchingCount) + && isHavingBonusNumberWithFiveMatchingCount() == prizeMoney.isHavingBonusNumber()) { + winningStatistics.increase(prizeMoney); + } + } + + private boolean isHavingBonusNumberWithFiveMatchingCount() { + return matchingCount == 5 && havingBonusNumber; + } + + public int getMatchingCount() { + return matchingCount; + } + + public boolean isHavingBonusNumber() { + return havingBonusNumber; + } +} diff --git a/src/main/java/domain/winningStatistics/PrizeMoney.java b/src/main/java/domain/winningStatistics/PrizeMoney.java new file mode 100644 index 0000000..44cb9a6 --- /dev/null +++ b/src/main/java/domain/winningStatistics/PrizeMoney.java @@ -0,0 +1,50 @@ +package domain.winningStatistics; + +import domain.lotteryStore.PurchasePrice; + +import java.util.Arrays; +import java.util.List; + +import static domain.lotteryStore.PurchasePrice.PRICE_OF_ONE_LOTTERY_TICKET; + +public enum PrizeMoney { + THREE(3, false, 5000), + FOUR(4, false, 50000), + FIVE(5, false, 1500000), + FIVE_BONUS(5, true, 30000000), + SIX(6, false, 2000000000); + + private final int matchingCount; + private final boolean isHavingBonusNumber; + private final int prizeMoney; + + PrizeMoney(int matchingCount, boolean isHavingBonusNumber, int prizeMoney) { + this.matchingCount = matchingCount; + this.isHavingBonusNumber = isHavingBonusNumber; + this.prizeMoney = prizeMoney; + } + + public static double calculateEarningsRate(WinningStatistics winningStatistics, int purchasedCount) { + int totalPrizeMoney = Arrays.stream(values()) + .mapToInt(money -> winningStatistics.get(money) * money.prizeMoney) + .sum(); + + return (double)totalPrizeMoney / (purchasedCount * PRICE_OF_ONE_LOTTERY_TICKET); + } + + public int getMatchingCount() { + return matchingCount; + } + + public boolean isHavingBonusNumber() { + return isHavingBonusNumber; + } + + public int getPrizeMoney() { + return prizeMoney; + } + + public boolean isSameMatchingCount(int matchingCount) { + return this.matchingCount == matchingCount; + } +} diff --git a/src/main/java/domain/winningStatistics/WinningStatistics.java b/src/main/java/domain/winningStatistics/WinningStatistics.java new file mode 100644 index 0000000..512264c --- /dev/null +++ b/src/main/java/domain/winningStatistics/WinningStatistics.java @@ -0,0 +1,30 @@ +package domain.winningStatistics; + +import java.util.*; + +public class WinningStatistics { + private Map winningStatistics = new HashMap<>(); + + + public WinningStatistics() { + initiate(); + } + + private void initiate() { + for (PrizeMoney value : PrizeMoney.values()) { + winningStatistics.put(value, 0); + } + } + + public void increase(PrizeMoney value) { + winningStatistics.put(value, winningStatistics.get(value) + 1); + } + + public int get(PrizeMoney value) { + return winningStatistics.get(value); + } + + public Map getWinningStatistics() { + return winningStatistics; + } +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/ui/Printer.java b/src/main/java/ui/Printer.java new file mode 100644 index 0000000..87ae2b2 --- /dev/null +++ b/src/main/java/ui/Printer.java @@ -0,0 +1,52 @@ +package ui; + +import java.util.List; +import java.util.Map; + +import domain.lotteryStore.Lotteries; +import domain.winningStatistics.PrizeMoney; +import domain.winningStatistics.WinningStatistics; +import ui.message.OutputMessage; + +public class Printer { + private static final int SIZE_OF_RANK = 5; + private static final int WIN_WITH_BONUS_NUMBER = 3; + + public void printPurchasedCount(int manualCount, int purchasedCount) { + System.out.printf(OutputMessage.PURCHASED_COUNT.getMessage(), manualCount, purchasedCount - manualCount); + } + + public void printPurchasedLotteries(Lotteries lotteries) { + lotteries.getLotteries() + .stream() + .map(lottery -> lottery.getNumbers().getNumbers()) + .forEach(System.out::println); + } + + public void printWinningStatistics(WinningStatistics winningStatistics) { + StringBuilder builder = new StringBuilder(); + PrizeMoney[] prizeMonies = PrizeMoney.values(); + + builder.append(OutputMessage.WINNING_STATISTICS.getMessage()); + + for (int i = 0; i < SIZE_OF_RANK; i++) { + printWinningStatisticsDetails(builder, prizeMonies[i], winningStatistics.get(prizeMonies[i]), i); + } + System.out.print(builder); + } + + private void printWinningStatisticsDetails(StringBuilder builder, PrizeMoney prizeMoney, Integer result, int i) { + if (i == WIN_WITH_BONUS_NUMBER) { + builder.append( + String.format(OutputMessage.MATCH_COUNT_WITH_BONUS.getMessage(), prizeMoney.getMatchingCount(), + prizeMoney.getPrizeMoney(), result)); + return; + } + builder.append(String.format(OutputMessage.MATCH_COUNT.getMessage(), prizeMoney.getMatchingCount(), + prizeMoney.getPrizeMoney(), result)); + } + + public void printTotalEarningsRate(double totalEarningsRate) { + System.out.printf(OutputMessage.TOTAL_EARNINGS_RATE.getMessage(), totalEarningsRate); + } +} diff --git a/src/main/java/ui/Receiver.java b/src/main/java/ui/Receiver.java new file mode 100644 index 0000000..a4fa092 --- /dev/null +++ b/src/main/java/ui/Receiver.java @@ -0,0 +1,42 @@ +package ui; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import ui.message.InputMessage; + +public class Receiver { + private static final Scanner SCANNER = new Scanner(System.in); + + public int receivePurchasePrice() { + System.out.println(InputMessage.INPUT_PURCHASE_PRICE.getMessage()); + return SCANNER.nextInt(); + } + + public int receiveManualCount() { + System.out.println(InputMessage.INPUT_MANUAL_COUNT.getMessage()); + SCANNER.nextLine(); + return SCANNER.nextInt(); + } + + public List receiveManualNumbers(int manualCount) { + System.out.println(InputMessage.INPUT_MANUAL_NUMBERS.getMessage()); + SCANNER.nextLine(); + List inputManualNumbers = new ArrayList<>(); + for (int i = 0; i < manualCount; i++) { + inputManualNumbers.add(SCANNER.nextLine()); + } + return inputManualNumbers; + } + + public String receiveWinningNumbers() { + System.out.println(InputMessage.INPUT_LAST_WEEK_WINNING_NUMBERS.getMessage()); + return SCANNER.nextLine(); + } + + public int receiveBonusNumber() { + System.out.println(InputMessage.INPUT_BONUS_BALL.getMessage()); + return SCANNER.nextInt(); + } +} diff --git a/src/main/java/ui/message/ExceptionMessage.java b/src/main/java/ui/message/ExceptionMessage.java new file mode 100644 index 0000000..1efe9cf --- /dev/null +++ b/src/main/java/ui/message/ExceptionMessage.java @@ -0,0 +1,20 @@ +package ui.message; + +public enum ExceptionMessage { + MUST_BUY_MORE_THAN_ONE_TICKET("한 장 이상 구매하셔야 합니다."), + DUPLICATE_NUMBERS("중복 번호를 입력할 수 없습니다."), + BONUS_NUMBER_CANNOT_EQUAL_WINNING_NUMBERS("당첨 번호와 일치하는 보너스 번호는 입력할 수 없습니다."), + MUST_INPUT_NUMBERS_IN_VALID_RANGE("1과 45 사이 번호를 입력해야 합니다."), + MUST_INPUT_SIX_WINNING_NUMBERS("6개의 당첨 번호를 입력해야 합니다."), + INPUT_ONLY_NUMBERS_AND_COMMA("숫자와 , 이외의 문자는 입력할 수 없습니다."); + + private String message; + + ExceptionMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/ui/message/InputMessage.java b/src/main/java/ui/message/InputMessage.java new file mode 100644 index 0000000..30f282c --- /dev/null +++ b/src/main/java/ui/message/InputMessage.java @@ -0,0 +1,19 @@ +package ui.message; + +public enum InputMessage { + INPUT_PURCHASE_PRICE("구입금액을 입력해 주세요."), + INPUT_MANUAL_COUNT("\n수동으로 구매할 로또 수를 입력해 주세요."), + INPUT_MANUAL_NUMBERS("\n수동으로 구매할 번호를 입력해 주세요."), + INPUT_LAST_WEEK_WINNING_NUMBERS("\n지난 주 당첨 번호를 입력해 주세요."), + INPUT_BONUS_BALL("보너스 볼을 입력해 주세요."); + + private String message; + + InputMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/ui/message/OutputMessage.java b/src/main/java/ui/message/OutputMessage.java new file mode 100644 index 0000000..91b7b30 --- /dev/null +++ b/src/main/java/ui/message/OutputMessage.java @@ -0,0 +1,19 @@ +package ui.message; + +public enum OutputMessage { + PURCHASED_COUNT("\n수동으로 %d장, 자동으로 %d장을 구매했습니다.\n"), + WINNING_STATISTICS("\n당첨 통계\n---------\n"), + MATCH_COUNT("%d개 일치 (%d원)- %d개\n"), + MATCH_COUNT_WITH_BONUS("%d개 일치, 보너스 볼 일치 (%d원)- %d개\n"), + TOTAL_EARNINGS_RATE("총 수익률은 %.2f입니다.(1 기준)\n"); + + private String message; + + OutputMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/test/java/domain/lotteryStore/LotteriesTest.java b/src/test/java/domain/lotteryStore/LotteriesTest.java new file mode 100644 index 0000000..a18fddd --- /dev/null +++ b/src/test/java/domain/lotteryStore/LotteriesTest.java @@ -0,0 +1,64 @@ +package domain.lotteryStore; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import domain.winningStatistics.PrizeMoney; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import domain.lotteryStore.numbers.BonusNumber; +import domain.lotteryStore.numbers.ManualNumbersGenerator; +import domain.lotteryStore.numbers.Numbers; +import domain.winningStatistics.ComparisonResult; +import domain.winningStatistics.WinningStatistics; + +class LotteriesTest { + private static ManualNumbersGenerator winningNumbers; + private static BonusNumber bonusNumber; + private static Lotteries lotteries; + + @BeforeAll + public static void setup() { + winningNumbers = new ManualNumbersGenerator("1, 2, 3, 4, 5, 6"); + bonusNumber = new BonusNumber(7, winningNumbers); + lotteries = new Lotteries(Arrays.asList( + new Lottery(new Numbers(Arrays.asList(8, 2, 23, 41, 4, 5))), + new Lottery(new Numbers(Arrays.asList(3, 5, 29, 6, 2, 38))), + new Lottery(new Numbers(Arrays.asList(4, 31, 5, 40, 2, 1))), + new Lottery(new Numbers(Arrays.asList(4, 1, 3, 45, 5, 2))), + new Lottery(new Numbers(Arrays.asList(7, 1, 2, 3, 4, 5))), + new Lottery(new Numbers(Arrays.asList(1, 2, 3, 4, 5, 6))) + )); + } + + @Test + public void 로또_티켓_하나의_비교_결과를_확인한다() { + //when + ComparisonResult result1 = lotteries.compareOneTicketNumbers(winningNumbers, bonusNumber, lotteries.get(1)); + ComparisonResult result2 = lotteries.compareOneTicketNumbers(winningNumbers, bonusNumber, lotteries.get(4)); + + //then + assertThat(result1.getMatchingCount()).isEqualTo(4); + assertThat(result1.isHavingBonusNumber()).isEqualTo(false); + assertThat(result2.getMatchingCount()).isEqualTo(5); + assertThat(result2.isHavingBonusNumber()).isEqualTo(true); + } + + @Test + public void 당첨_통계를_확인한다() throws Exception { + //when + lotteries.compareWithWinningNumbersAndBonusNumber(winningNumbers, bonusNumber); + WinningStatistics winningStatistics = lotteries.compareWithWinningNumbersAndBonusNumber(winningNumbers, bonusNumber); + PrizeMoney[] prizeMonies = PrizeMoney.values(); + + //then + assertThat(winningStatistics.get(prizeMonies[0])).isEqualTo(1); + assertThat(winningStatistics.get(prizeMonies[1])).isEqualTo(2); + assertThat(winningStatistics.get(prizeMonies[2])).isEqualTo(1); + assertThat(winningStatistics.get(prizeMonies[3])).isEqualTo(1); + assertThat(winningStatistics.get(prizeMonies[4])).isEqualTo(1); + } +} diff --git a/src/test/java/domain/lotteryStore/LotteryStoreTest.java b/src/test/java/domain/lotteryStore/LotteryStoreTest.java new file mode 100644 index 0000000..057424d --- /dev/null +++ b/src/test/java/domain/lotteryStore/LotteryStoreTest.java @@ -0,0 +1,30 @@ +package domain.lotteryStore; + +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayList; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class LotteryStoreTest { + + private LotteryStore lotteryStore; + + @BeforeEach + void setUp() { + lotteryStore = new LotteryStore(); + } + + @Test + public void 공_6개를_뽑는다() throws Exception { + //given + int sizeOfLottery = 6; + + //when + Lotteries lotteries = lotteryStore.createLotteries(1, 0, new ArrayList<>()); + + //then + assertThat(lotteries.getLotteries().get(0).getNumbers().getNumbers().size()).isEqualTo(sizeOfLottery); + } +} \ No newline at end of file diff --git a/src/test/java/domain/value/BonusNumberTest.java b/src/test/java/domain/value/BonusNumberTest.java new file mode 100644 index 0000000..5d563fb --- /dev/null +++ b/src/test/java/domain/value/BonusNumberTest.java @@ -0,0 +1,34 @@ +package domain.value; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import domain.lotteryStore.numbers.BonusNumber; +import domain.lotteryStore.numbers.ManualNumbersGenerator; +import ui.message.ExceptionMessage; + +public class BonusNumberTest { + @Test + public void 범위_초과하는_수를_검증한다() { + //given + int bonusNumber = 46; + ManualNumbersGenerator winningNumbers = new ManualNumbersGenerator("1, 2, 3, 4, 5, 6"); + + //then + Assertions.assertThatThrownBy(() -> new BonusNumber(bonusNumber, winningNumbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.MUST_INPUT_NUMBERS_IN_VALID_RANGE.getMessage()); + } + + @Test + public void 중복을_검사한다() { + //given + int bonusNumber = 6; + ManualNumbersGenerator winningNumbers = new ManualNumbersGenerator("1, 2, 3, 4, 5, 6"); + + //then + Assertions.assertThatThrownBy(() -> new BonusNumber(bonusNumber, winningNumbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.BONUS_NUMBER_CANNOT_EQUAL_WINNING_NUMBERS.getMessage()); + } +} diff --git a/src/test/java/domain/value/LotteryNumberRangeTest.java b/src/test/java/domain/value/LotteryNumberRangeTest.java new file mode 100644 index 0000000..2378c51 --- /dev/null +++ b/src/test/java/domain/value/LotteryNumberRangeTest.java @@ -0,0 +1,26 @@ +package domain.value; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import domain.lotteryStore.numbers.LotteryNumberRange; +import ui.message.ExceptionMessage; + +class LotteryNumberRangeTest { + private LotteryNumberRange lotteryNumberRange; + + @BeforeEach + void setUp() { + lotteryNumberRange = new LotteryNumberRange(); + } + + @ParameterizedTest + @ValueSource(ints = {-1, 0, 46, 47, 52, 55}) + public void 입력_범위안에_들어오는지_검증한다(int invalidNumber) { + Assertions.assertThatThrownBy(() -> lotteryNumberRange.validateRange(invalidNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.MUST_INPUT_NUMBERS_IN_VALID_RANGE.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/domain/value/ManualNumbersGeneratorTest.java b/src/test/java/domain/value/ManualNumbersGeneratorTest.java new file mode 100644 index 0000000..6a2deac --- /dev/null +++ b/src/test/java/domain/value/ManualNumbersGeneratorTest.java @@ -0,0 +1,42 @@ +package domain.value; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import domain.lotteryStore.numbers.ManualNumbersGenerator; +import ui.message.ExceptionMessage; + +class ManualNumbersGeneratorTest { + @ParameterizedTest + @ValueSource(strings = {"Hello", "3 2 ; 3", "#0, 3"}) + public void 숫자와_콤마_이외의_값을_검증한다(String invalidInput) { + Assertions.assertThatThrownBy(() -> new ManualNumbersGenerator(invalidInput)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.INPUT_ONLY_NUMBERS_AND_COMMA.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"1, 2, 3, 4, 5", "1, 2, 3, 4", "1, 2, 3,", "1, 2", "1", "1, 2, 3, 4, 5, 6, 7"}) + public void 당첨번호_갯수를_검증한다(String invalidInput) { + Assertions.assertThatThrownBy(() -> new ManualNumbersGenerator(invalidInput)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.MUST_INPUT_SIX_WINNING_NUMBERS.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"1, 1, 3, 4, 5, 6", "42, 1,34, 26,34, 19"}) + public void 중복_입력된_수를_검증한다(String invalidInput) { + Assertions.assertThatThrownBy(() -> new ManualNumbersGenerator(invalidInput)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.DUPLICATE_NUMBERS.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"1, 2, 3, 4, 67, 6", "2, 1, 0, 26, 34, 19"}) + public void 입력_범위안에_들어오는지_검증한다(String invalidInput) { + Assertions.assertThatThrownBy(() -> new ManualNumbersGenerator(invalidInput)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.MUST_INPUT_NUMBERS_IN_VALID_RANGE.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/domain/value/PurchasePriceTest.java b/src/test/java/domain/value/PurchasePriceTest.java new file mode 100644 index 0000000..9c30956 --- /dev/null +++ b/src/test/java/domain/value/PurchasePriceTest.java @@ -0,0 +1,20 @@ +package domain.value; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import domain.lotteryStore.PurchasePrice; +import ui.message.ExceptionMessage; + +class PurchasePriceTest { + @Test + public void 최소_입력값을_만족하는지_검증한다() { + //given + int purchasePrice = 900; + + //then + Assertions.assertThatThrownBy(() -> new PurchasePrice(purchasePrice)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ExceptionMessage.MUST_BUY_MORE_THAN_ONE_TICKET.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/domain/winningStatistics/ComparisonResultTest.java b/src/test/java/domain/winningStatistics/ComparisonResultTest.java new file mode 100644 index 0000000..f1bacba --- /dev/null +++ b/src/test/java/domain/winningStatistics/ComparisonResultTest.java @@ -0,0 +1,26 @@ +package domain.winningStatistics; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +public class ComparisonResultTest { + + @Test + void 랭킹을_확인한다() { + WinningStatistics winningStatistics = new WinningStatistics(); + int matchingCount = 5; + boolean havingBonusNumber = false; + ComparisonResult comparisonResult = new ComparisonResult(matchingCount, havingBonusNumber); + + comparisonResult.checkRanking(winningStatistics); + Map rankings = winningStatistics.getWinningStatistics(); + + Assertions.assertThat(rankings.get(PrizeMoney.THREE)).isEqualTo(0); + Assertions.assertThat(rankings.get(PrizeMoney.FOUR)).isEqualTo(0); + Assertions.assertThat(rankings.get(PrizeMoney.FIVE)).isEqualTo(1); + Assertions.assertThat(rankings.get(PrizeMoney.FIVE_BONUS)).isEqualTo(0); + Assertions.assertThat(rankings.get(PrizeMoney.SIX)).isEqualTo(0); + } +} diff --git a/src/test/java/domain/winningStatistics/PrizeMoneyTest.java b/src/test/java/domain/winningStatistics/PrizeMoneyTest.java new file mode 100644 index 0000000..ef312b7 --- /dev/null +++ b/src/test/java/domain/winningStatistics/PrizeMoneyTest.java @@ -0,0 +1,36 @@ +package domain.winningStatistics; + +import static javax.swing.UIManager.put; +import static org.assertj.core.api.Assertions.*; + +import java.util.*; + +import org.junit.jupiter.api.Test; + +class PrizeMoneyTest { + + @Test + void 총수익률을_계산한다() { + // given + WinningStatistics winningStatistics = new WinningStatistics() {{ + put(PrizeMoney.THREE, 3); + put(PrizeMoney.FOUR, 1); + put(PrizeMoney.FIVE, 3); + put(PrizeMoney.FIVE_BONUS, 1); + put(PrizeMoney.SIX, 1); + }}; + int purchasedCount = 20; + + // when + double earningsRate = PrizeMoney.calculateEarningsRate(winningStatistics, purchasedCount); + PrizeMoney[] values = PrizeMoney.values(); + + // then + assertThat(earningsRate).isEqualTo( + (float)(winningStatistics.get(values[0]) * values[0].getPrizeMoney() + + winningStatistics.get(values[1]) * values[1].getPrizeMoney() + + winningStatistics.get(values[2]) * values[2].getPrizeMoney() + + winningStatistics.get(values[3]) * values[3].getPrizeMoney() + + winningStatistics.get(values[4]) * values[4].getPrizeMoney()) / (purchasedCount * 1000)); + } +} \ No newline at end of file diff --git a/src/test/java/domain/winningStatistics/WinningStatisticsTest.java b/src/test/java/domain/winningStatistics/WinningStatisticsTest.java new file mode 100644 index 0000000..debe374 --- /dev/null +++ b/src/test/java/domain/winningStatistics/WinningStatisticsTest.java @@ -0,0 +1,32 @@ +package domain.winningStatistics; + +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WinningStatisticsTest { + + private WinningStatistics winningStatistics; + + @BeforeEach + void setUp() { + winningStatistics = new WinningStatistics(); + } + + @Test + void 당첨자_수를_증가시킨다() { + winningStatistics.increase(PrizeMoney.THREE); + PrizeMoney[] prizeMonies = PrizeMoney.values(); + + assertThat(winningStatistics.get(prizeMonies[0])).isEqualTo(1); + assertThat(winningStatistics.get(prizeMonies[1])).isEqualTo(0); + assertThat(winningStatistics.get(prizeMonies[2])).isEqualTo(0); + assertThat(winningStatistics.get(prizeMonies[3])).isEqualTo(0); + assertThat(winningStatistics.get(prizeMonies[4])).isEqualTo(0); + } +} \ No newline at end of file diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29..0000000