diff --git a/README.md b/README.md index 5d11c0a777..53974437d1 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,23 @@ - [x] 예외: 음수인 경우 - [x] 로또 개수 계산 - [x] 로또 구매 - - [x] 1~45의 숫자여야 함 - - [x] 숫자가 중복되지 않아야 함 - - [x] 숫자가 여섯개여야 함 + - [x] 자동 구매 + - [x] 검증: 1~45의 숫자여야 함 + - [x] 검증: 숫자가 중복되지 않아야 함 + - [x] 검증: 숫자가 여섯개여야 함 + - [x] 수동 구매 + - [x] 수동으로 구매할 로또 수 입력받기 + - [x] 검증: 숫자로 입력되어야 한다. + - [x] 검증: 음수가 아니어야 한다. + - [x] 검증: 입력받은 수 * 1000의 값이 구입 금액보다 클 수 없다. + - [x] 수동으로 구매할 번호 입력 + - [x] 검증: ", "로 구분되지 않는 경우 + - [x] 검증: 숫자여야 한다. + - [x] 검증: 숫자가 여섯개여야 함 + - [x] 검증: 1~45의 숫자여야 함 + - [x] 검증: 숫자가 중복되지 않아야 함 + - [x] 수동 구매 수에 따른 로또 자동 구매 + - [x] 전체 구입 가능 수에서 수동 구매 수 만큼을 제외하고 나머지 자동 구매 - [x] 당첨 번호 입력 - [x] 예외: ", "로 구분되지 않는 경우 - [x] 예외: 숫자가 아닌 경우 diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d9a90c67dc..f33c9eb0ea 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,10 +1,15 @@ package lotto; import lotto.game.LottoController; +import lotto.game.LottoService; +import lotto.ticket.strategy.NumbersGenerator; +import lotto.ticket.strategy.RandomNumbersGenerator; public class Application { public static void main(String[] args) { - LottoController lottoController = new LottoController(); + NumbersGenerator autoTicketGenerator = new RandomNumbersGenerator(); + LottoService lottoService = new LottoService(autoTicketGenerator); + LottoController lottoController = new LottoController(lottoService); lottoController.run(); } } diff --git a/src/main/java/lotto/game/LottoController.java b/src/main/java/lotto/game/LottoController.java index 27660905d1..2ec245a6a8 100644 --- a/src/main/java/lotto/game/LottoController.java +++ b/src/main/java/lotto/game/LottoController.java @@ -1,24 +1,25 @@ package lotto.game; +import lotto.money.Money; +import lotto.ranking.Statistics; import lotto.ticket.BonusBall; import lotto.ticket.Tickets; import lotto.ticket.WinnerTicket; -import lotto.ticket.strategy.RandomNumbersGenerator; -import lotto.money.Money; -import lotto.money.PrizeMoney; -import lotto.ranking.Statistics; import lotto.view.InputView; import lotto.view.OutputView; public class LottoController { + private final LottoService lottoService; + + public LottoController(LottoService lottoService) { + this.lottoService = lottoService; + } + public void run() { Money money = generateMoney(); - LottoCount lottoCount = generateCount(money); - Tickets tickets = generateTickets(lottoCount); - WinnerTicket winnerTicket = generateWinnerTicket(); - BonusBall bonusBall = generateBonusBall(winnerTicket); - Statistics statistics = generateStatistics(tickets, winnerTicket, bonusBall); - makeResult(money, statistics); + LottoCount lottoCount = possibleLottoCount(money); + Tickets tickets = purchaseTickets(lottoCount); + verifyResult(money, tickets); } private Money generateMoney() { @@ -31,40 +32,75 @@ private Money generateMoney() { } } - private LottoCount generateCount(Money money) { + private LottoCount possibleLottoCount(Money money) { try { - LottoCount lottoCount = new LottoCount(money); - OutputView.noticeLottoCount(lottoCount); - return lottoCount; + return new LottoCount(money); } catch (IllegalArgumentException e) { OutputView.printError(e); - return generateCount(money); + return possibleLottoCount(money); + } + } + + private Tickets purchaseTickets(LottoCount lottoCount) { + try { + LottoCount manualTicketAmount = manualTicketAmount(); + LottoCount autoTicketAmount = lottoCount.consumeTicket(manualTicketAmount); + return ticketPurchase(manualTicketAmount, autoTicketAmount); + } catch (RuntimeException e) { + OutputView.printError(e); + return purchaseTickets(lottoCount); + } + } + + private LottoCount manualTicketAmount() { + OutputView.enterManualTicketAmount(); + return InputView.inputManualTicketAmount(); + } + + private Tickets ticketPurchase(LottoCount manualTicketAmount, LottoCount autoTicketAmount) { + Tickets manualTickets = manualTicketGenerate(manualTicketAmount); + Tickets autoTickets = lottoService.buyAutoTickets(autoTicketAmount); + OutputView.noticeLottoCount(manualTicketAmount, autoTicketAmount); + + Tickets totalTicket = Tickets.joinTicket(manualTickets, autoTickets); + OutputView.showTickets(totalTicket); + return totalTicket; + } + + private Tickets manualTicketGenerate(LottoCount count) { + try { + OutputView.enterManualTicketNumber(); + return lottoService.buyManualTickets(InputView.inputNumbers(count)); + } catch (RuntimeException e) { + OutputView.printError(e); + return manualTicketGenerate(count); } } - private Tickets generateTickets(LottoCount lottoCount) { - Tickets tickets = new Tickets(lottoCount, new RandomNumbersGenerator()); - OutputView.showTickets(tickets); - return tickets; + private void verifyResult(Money money, Tickets totalTicket) { + WinnerTicket winnerTicket = verifyWinnerTicket(); + BonusBall bonusBall = verifyBonusBall(winnerTicket); + Statistics statistics = generateStatistics(totalTicket, winnerTicket, bonusBall); + OutputView.showProfit(statistics.calculateProfit(money)); } - private WinnerTicket generateWinnerTicket() { + private WinnerTicket verifyWinnerTicket() { try { OutputView.enterWinnerTicket(); return InputView.inputWinnerTicket(); - } catch (IllegalArgumentException e) { + } catch (RuntimeException e) { OutputView.printError(e); - return generateWinnerTicket(); + return verifyWinnerTicket(); } } - private BonusBall generateBonusBall(WinnerTicket winnerTicket) { + private BonusBall verifyBonusBall(WinnerTicket winnerTicket) { try { OutputView.enterBonusBall(); return InputView.inputBonusBall(winnerTicket); - } catch (IllegalArgumentException e) { + } catch (RuntimeException e) { OutputView.printError(e); - return generateBonusBall(winnerTicket); + return verifyBonusBall(winnerTicket); } } @@ -73,9 +109,4 @@ private Statistics generateStatistics(Tickets tickets, WinnerTicket winnerTicket OutputView.noticeStatistics(statistics); return statistics; } - - private void makeResult(Money money, Statistics statistics) { - PrizeMoney prizeMoney = new PrizeMoney(statistics); - OutputView.showProfit(prizeMoney.calculateProfit(money)); - } -} +} \ No newline at end of file diff --git a/src/main/java/lotto/game/LottoCount.java b/src/main/java/lotto/game/LottoCount.java index b1ebaa0b86..a997bb4059 100644 --- a/src/main/java/lotto/game/LottoCount.java +++ b/src/main/java/lotto/game/LottoCount.java @@ -1,30 +1,67 @@ package lotto.game; import lotto.money.Money; +import lotto.ticket.Ticket; import java.util.Objects; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_INPUT; +import static lotto.ticket.Number.validateNumber; + public class LottoCount { - public static final int ZERO = 0; - public static final int ONE_COUNT = 1; - private static final int LOTTO_PRICE = 1000; + public static final String ERROR_MESSAGE_INVALID_AMOUNT = "구매 금액보다 많이 구입할 수 없습니다."; private final int lottoCount; public LottoCount(Money money) { - this.lottoCount = money.divideMoney(LOTTO_PRICE); + this(money.divideMoney(Ticket.PRICE)); + } + + public LottoCount(String value) { + this(validate(value)); + } + + private LottoCount(int value) { + this.lottoCount = value; + } + + private static int validate(String value) { + int number = validateNumber(value); + validateNotNegative(number); + return number; } - private LottoCount(int lottoCount) { - this.lottoCount = lottoCount; + private static void validateNotNegative(int number) { + if (number < 0) { + throw new IllegalArgumentException(ERROR_MESSAGE_INVALID_INPUT); + } } public boolean isGreaterThanZero() { - return this.lottoCount > ZERO; + return this.lottoCount > 0; } public LottoCount decreaseOne() { - return new LottoCount(lottoCount - ONE_COUNT); + return new LottoCount(lottoCount - 1); + } + + public LottoCount consumeTicket(LottoCount count) { + validateAmount(lottoCount, count); + return count.remainCount(lottoCount); + } + + private void validateAmount(int currentCount, LottoCount count) { + if (!count.canPurchase(currentCount)) { + throw new IllegalStateException(ERROR_MESSAGE_INVALID_AMOUNT); + } + } + + private LottoCount remainCount(int lottoCount) { + return new LottoCount(lottoCount - this.lottoCount); + } + + public boolean canPurchase(int currentCount) { + return currentCount >= this.lottoCount; } public int getLottoCount() { diff --git a/src/main/java/lotto/game/LottoService.java b/src/main/java/lotto/game/LottoService.java new file mode 100644 index 0000000000..7a02a17f3d --- /dev/null +++ b/src/main/java/lotto/game/LottoService.java @@ -0,0 +1,36 @@ +package lotto.game; + +import lotto.ticket.Ticket; +import lotto.ticket.Tickets; +import lotto.ticket.strategy.ManualNumbersGenerator; +import lotto.ticket.strategy.NumbersGenerator; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class LottoService { + private final NumbersGenerator autoLottoGenerator; + + public LottoService(NumbersGenerator generator) { + this.autoLottoGenerator = generator; + } + + public Tickets buyManualTickets(List manualLottoNumber) { + List manualTickets = manualLottoNumber.stream() + .map(ManualNumbersGenerator::new) + .map(ManualNumbersGenerator::generate) + .map(Ticket::new) + .collect(Collectors.toList()); + return new Tickets(manualTickets); + } + + public Tickets buyAutoTickets(LottoCount count) { + List tickets = new ArrayList<>(); + while (count.isGreaterThanZero()) { + count = count.decreaseOne(); + tickets.add(new Ticket(autoLottoGenerator.generate())); + } + return new Tickets(tickets); + } +} diff --git a/src/main/java/lotto/money/Money.java b/src/main/java/lotto/money/Money.java index 1430aaf1f1..1aa3a38e7c 100644 --- a/src/main/java/lotto/money/Money.java +++ b/src/main/java/lotto/money/Money.java @@ -1,14 +1,14 @@ package lotto.money; -import lotto.ticket.TicketValidation; +import lotto.ticket.Ticket; import java.util.Objects; -public class Money { - public static final String ERROR_MESSAGE_MINIMUM_MONEY = "1000원 이상의 금액이 필요합니다."; +import static lotto.ticket.Number.validateNumber; - private static final int MINIMUM_PRICE = 1000; +public class Money { private static final int SECOND_DECIMAL_POINT_MAKER = 100; + public static final String ERROR_MESSAGE_MINIMUM_MONEY = Ticket.PRICE + "원 이상의 금액이 필요합니다."; private final int money; @@ -16,14 +16,14 @@ public Money(String money) { this.money = validate(money); } - private int validate(String money) { - int value = TicketValidation.validateNumber(money); - checkMinimum(value); - return value; + private int validate(String value) { + int money = validateNumber(value); + validateMinimumPrice(money); + return money; } - private void checkMinimum(int value) { - if (value < MINIMUM_PRICE) { + private void validateMinimumPrice(int value) { + if (value < Ticket.PRICE) { throw new IllegalArgumentException(ERROR_MESSAGE_MINIMUM_MONEY); } } @@ -32,9 +32,8 @@ public int divideMoney(int unit) { return this.money / unit; } - public String calculateProfit(int totalMoney) { - double profit = Math.floor((double) totalMoney / money * SECOND_DECIMAL_POINT_MAKER) / SECOND_DECIMAL_POINT_MAKER; - return Double.toString(profit); + public double calculateProfit(int totalMoney) { + return Math.floor((double) totalMoney / money * SECOND_DECIMAL_POINT_MAKER) / SECOND_DECIMAL_POINT_MAKER; } @Override diff --git a/src/main/java/lotto/money/PrizeMoney.java b/src/main/java/lotto/money/PrizeMoney.java deleted file mode 100644 index 0f35551799..0000000000 --- a/src/main/java/lotto/money/PrizeMoney.java +++ /dev/null @@ -1,39 +0,0 @@ -package lotto.money; - -import lotto.ranking.Ranking; -import lotto.ranking.Statistics; - -import java.util.Objects; - -public class PrizeMoney { - private final int prizeMoney; - - public PrizeMoney(Statistics statistics) { - this.prizeMoney = totalPrize(statistics); - } - - private int totalPrize(Statistics statistics) { - int prize = 0; - for (Ranking ranking : Ranking.values()) { - prize += ranking.calculatePrize(statistics.findRankingCount(ranking)); - } - return prize; - } - - public String calculateProfit(Money money) { - return money.calculateProfit(prizeMoney); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - PrizeMoney that = (PrizeMoney) o; - return prizeMoney == that.prizeMoney; - } - - @Override - public int hashCode() { - return Objects.hash(prizeMoney); - } -} diff --git a/src/main/java/lotto/ranking/Ranking.java b/src/main/java/lotto/ranking/Ranking.java index 92612bb224..9919531641 100644 --- a/src/main/java/lotto/ranking/Ranking.java +++ b/src/main/java/lotto/ranking/Ranking.java @@ -3,27 +3,31 @@ import java.util.Arrays; public enum Ranking { - FIRST(6, 2_000_000_000), - SECOND(5, 30_000_000), - THIRD(5, 1_500_000), - FORTH(4, 50_000), FIFTH(3, 5_000), + FORTH(4, 50_000), + THIRD(5, 1_500_000), + SECOND(5, 30_000_000, true), + FIRST(6, 2_000_000_000), NOTHING(0, 0); private final int matchCount; private final int price; + private final boolean matchBonus; Ranking(int matchCount, int price) { + this(matchCount, price, false); + } + + Ranking(int matchCount, int price, boolean matchBonus) { this.matchCount = matchCount; this.price = price; + this.matchBonus = matchBonus; } public static Ranking makePrice(int match, boolean bonusMatch) { - if (match == SECOND.matchCount && bonusMatch) { - return SECOND; - } return Arrays.stream(values()) - .filter(n -> n.matchCount == match && n != SECOND) + .filter(ranking -> ranking.matchCount == match) + .filter(ranking -> ranking.matchBonus == bonusMatch) .findFirst() .orElse(NOTHING); } diff --git a/src/main/java/lotto/ranking/Statistics.java b/src/main/java/lotto/ranking/Statistics.java index 44d183b5af..43890c796c 100644 --- a/src/main/java/lotto/ranking/Statistics.java +++ b/src/main/java/lotto/ranking/Statistics.java @@ -1,25 +1,22 @@ package lotto.ranking; +import lotto.money.Money; + import java.util.Collections; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; -import static lotto.game.LottoCount.ZERO; - public class Statistics { - private static final int STATISTIC_CAPACITY = 6; + public static final int INITIAL_VALUE = 0; private final Map statistics; public Statistics(List rankings) { - statistics = new HashMap<>(STATISTIC_CAPACITY); - statistics.put(Ranking.FIRST, ZERO); - statistics.put(Ranking.SECOND, ZERO); - statistics.put(Ranking.THIRD, ZERO); - statistics.put(Ranking.FORTH, ZERO); - statistics.put(Ranking.FIFTH, ZERO); - statistics.put(Ranking.NOTHING, ZERO); + statistics = new EnumMap<>(Ranking.class); + for (Ranking ranking : Ranking.values()) { + statistics.put(ranking, INITIAL_VALUE); + } calculateStatistics(rankings); } @@ -29,10 +26,22 @@ private void calculateStatistics(List rankings) { } } + public double calculateProfit(Money purchaseMoney) { + return purchaseMoney.calculateProfit(getTotalPrize()); + } + public int findRankingCount(Ranking ranking) { return statistics.get(ranking); } + public int getTotalPrize() { + int prize = 0; + for (Ranking ranking : Ranking.values()) { + prize += ranking.calculatePrize(findRankingCount(ranking)); + } + return prize; + } + public Map getStatistics() { return Collections.unmodifiableMap(statistics); } diff --git a/src/main/java/lotto/ticket/BonusBall.java b/src/main/java/lotto/ticket/BonusBall.java index b315eff71c..2772d1a809 100644 --- a/src/main/java/lotto/ticket/BonusBall.java +++ b/src/main/java/lotto/ticket/BonusBall.java @@ -2,15 +2,23 @@ import java.util.Objects; +import static lotto.ticket.Ticket.ERROR_MESSAGE_DUPLICATED; + public class BonusBall { private final Number bonusBall; public BonusBall(String value, WinnerTicket winnerTicket) { - Number number = new Number(value); - TicketValidation.validateSameNumber(number, winnerTicket); + Number number = Number.valueOf(value); + validateSameNumber(number, winnerTicket); this.bonusBall = number; } + private void validateSameNumber(Number number, WinnerTicket winnerTicket) { + if (winnerTicket.isSameNumber(number)) { + throw new IllegalArgumentException(ERROR_MESSAGE_DUPLICATED); + } + } + public boolean isSameThan(Number number) { return bonusBall.equals(number); } diff --git a/src/main/java/lotto/ticket/Number.java b/src/main/java/lotto/ticket/Number.java index 5b7f812695..ca69ce3a57 100644 --- a/src/main/java/lotto/ticket/Number.java +++ b/src/main/java/lotto/ticket/Number.java @@ -1,20 +1,68 @@ package lotto.ticket; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Number implements Comparable { + public static final String ERROR_MESSAGE_INVALID_INPUT = "잘못된 입력입니다."; + public static final String ERROR_MESSAGE_INVALID_RANGE = "숫자는 " + Ticket.MIN_NUMBER + "부터 " + Ticket.MAX_NUMBER + "사이여야 합니다."; + + private static final List CACHE; + + static { + CACHE = IntStream.rangeClosed(Ticket.MIN_NUMBER, Ticket.MAX_NUMBER) + .mapToObj(Integer::toString) + .map(Number::valueOf) + .collect(Collectors.toList()); + } -public class Number { private final int number; - public Number(String value) { - this.number = validate(value); + private Number(int value) { + this.number = value; + } + + public static Number valueOf(String value) { + int number = validate(value); + try { + return CACHE.get(number - 1); + } catch (NullPointerException e) { + return new Number(number); + } + } + + public static List values() { + return Collections.unmodifiableList(CACHE); } - private int validate(String value) { - int number = TicketValidation.validateNumber(value); - TicketValidation.validateNumberInRange(number); + private static int validate(String value) { + int number = validateNumber(value); + validateNumberInRange(number); return number; } + public static int validateNumber(String value) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(ERROR_MESSAGE_INVALID_INPUT); + } + } + + private static void validateNumberInRange(int number) { + if (number < Ticket.MIN_NUMBER || number > Ticket.MAX_NUMBER) { + throw new IllegalStateException(ERROR_MESSAGE_INVALID_RANGE); + } + } + + @Override + public int compareTo(Number o) { + return Integer.compare(this.number, o.number); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/lotto/ticket/Ticket.java b/src/main/java/lotto/ticket/Ticket.java index ff8cad364b..9f633f90a2 100644 --- a/src/main/java/lotto/ticket/Ticket.java +++ b/src/main/java/lotto/ticket/Ticket.java @@ -1,61 +1,69 @@ package lotto.ticket; -import lotto.ticket.strategy.NumbersGenerator; - import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; -import static lotto.game.LottoCount.ONE_COUNT; -import static lotto.game.LottoCount.ZERO; +import static lotto.ranking.Statistics.INITIAL_VALUE; public class Ticket { + public static final int PRICE = 1000; + public static final int MIN_NUMBER = 1; + public static final int MAX_NUMBER = 45; + public static final int NUMBER_COUNT = 6; + public static final String ERROR_MESSAGE_INVALID_SIZE = "숫자는 " + NUMBER_COUNT + "개여야 합니다."; + public static final String ERROR_MESSAGE_DUPLICATED = "중복되는 숫자가 존재합니다."; + private final List numbers; - public Ticket(NumbersGenerator numbersGenerator) { - this.numbers = new ArrayList<>(validate(numbersGenerator.generate())); + public Ticket(List numbers) { + validate(numbers); + this.numbers = new ArrayList<>(numbers); } - public Ticket(List numbers) { - this.numbers = new ArrayList<>(validate(numbers)); + private void validate(List values) { + validateSize(values); + validateDuplicated(values); } - private List validate(List values) { - TicketValidation.validateSize(values); - TicketValidation.validateDuplicated(values); - return values; + private void validateSize(List value) { + if (value.size() != Ticket.NUMBER_COUNT) { + throw new IllegalArgumentException(ERROR_MESSAGE_INVALID_SIZE); + } } - public int hasSameNumber(Number number) { + private void validateDuplicated(List value) { + boolean duplicated = value.stream() + .distinct() + .count() != value.size(); + + if (duplicated) { + throw new IllegalArgumentException(ERROR_MESSAGE_DUPLICATED); + } + } + + public int sameNumberCountOne(Number number) { if (numbers.contains(number)) { - return ONE_COUNT; + return 1; } - return ZERO; + return 0; } public boolean hasContainBonus(BonusBall bonusBall) { - for (Number number : numbers) { - if (bonusBall.isSameThan(number)) { - return true; - } - } - return false; + return numbers.stream() + .anyMatch(bonusBall::isSameThan); } public boolean contains(Number value) { - for (Number number : numbers) { - if (number.equals(value)) { - return true; - } - } - return false; + return numbers.stream() + .anyMatch(value::equals); } - public int hasSameNumberCount(Ticket winnerTicket) { - int matchCount = ZERO; + public int sameNumberCount(Ticket winnerTicket) { + int matchCount = INITIAL_VALUE; for (Number number : numbers) { - matchCount += winnerTicket.hasSameNumber(number); + matchCount += winnerTicket.sameNumberCountOne(number); } return matchCount; } diff --git a/src/main/java/lotto/ticket/TicketValidation.java b/src/main/java/lotto/ticket/TicketValidation.java deleted file mode 100644 index e12472ee3b..0000000000 --- a/src/main/java/lotto/ticket/TicketValidation.java +++ /dev/null @@ -1,48 +0,0 @@ -package lotto.ticket; - -import java.util.List; - -import static lotto.ticket.strategy.RandomNumbersGenerator.*; - -public class TicketValidation { - public static final String ERROR_MESSAGE_DUPLICATED = "중복되는 숫자가 존재합니다."; - public static final String ERROR_MESSAGE_INVALID_INPUT = "잘못된 입력입니다."; - public static final String ERROR_MESSAGE_INVALID_SIZE = "숫자는 6개여야 합니다."; - public static final String ERROR_MESSAGE_INVALID_RANGE = "숫자는 1부터 45사이여야 합니다."; - - public static Integer validateNumber(String number) { - try { - return Integer.parseInt(number); - } catch (NumberFormatException e) { - throw new IllegalArgumentException(ERROR_MESSAGE_INVALID_INPUT); - } - } - - public static void validateNumberInRange(int number) { - if (number < MINIMUM_NUMBER || number > MAXIMUM_NUMBER) { - throw new IllegalArgumentException(ERROR_MESSAGE_INVALID_RANGE); - } - } - - public static void validateSize(List value) { - if (value.size() != NUMBER_COUNT_IN_LOTTO) { - throw new IllegalArgumentException(ERROR_MESSAGE_INVALID_SIZE); - } - } - - public static void validateDuplicated(List value) { - boolean duplicated = value.stream() - .distinct() - .count() != value.size(); - - if (duplicated) { - throw new IllegalArgumentException(ERROR_MESSAGE_DUPLICATED); - } - } - - public static void validateSameNumber(Number number, WinnerTicket winnerTicket) { - if (winnerTicket.isSameNumber(number)) { - throw new IllegalArgumentException(ERROR_MESSAGE_DUPLICATED); - } - } -} \ No newline at end of file diff --git a/src/main/java/lotto/ticket/Tickets.java b/src/main/java/lotto/ticket/Tickets.java index 760387df7f..42b306877c 100644 --- a/src/main/java/lotto/ticket/Tickets.java +++ b/src/main/java/lotto/ticket/Tickets.java @@ -1,7 +1,5 @@ package lotto.ticket; -import lotto.game.LottoCount; -import lotto.ticket.strategy.NumbersGenerator; import lotto.ranking.Ranking; import java.util.ArrayList; @@ -13,12 +11,15 @@ public class Tickets { private final List tickets; - public Tickets(LottoCount lottoCount, NumbersGenerator numbersGenerator) { - this.tickets = new ArrayList<>(); - while (lottoCount.isGreaterThanZero()) { - lottoCount = lottoCount.decreaseOne(); - tickets.add(new Ticket(numbersGenerator)); - } + public Tickets(List tickets) { + this.tickets = tickets; + } + + public static Tickets joinTicket(Tickets manualTickets, Tickets autoTickets) { + List tickets = new ArrayList<>(); + tickets.addAll(manualTickets.tickets); + tickets.addAll(autoTickets.tickets); + return new Tickets(tickets); } public List makeResult(WinnerTicket winnerTicket, BonusBall bonusBall) { diff --git a/src/main/java/lotto/ticket/WinnerTicket.java b/src/main/java/lotto/ticket/WinnerTicket.java index 44154f3cb2..487149fbe7 100644 --- a/src/main/java/lotto/ticket/WinnerTicket.java +++ b/src/main/java/lotto/ticket/WinnerTicket.java @@ -1,15 +1,13 @@ package lotto.ticket; -import java.util.List; +import lotto.ticket.util.SplitNumbers; + import java.util.Objects; import java.util.stream.Collectors; -import java.util.stream.Stream; -public class WinnerTicket { - private static final String DELIMITER = ","; - private static final String SPACE = " "; - private static final String EMPTY = ""; +import static java.util.stream.Collectors.collectingAndThen; +public class WinnerTicket { private final Ticket winnerTicket; public WinnerTicket(String numbers) { @@ -17,11 +15,8 @@ public WinnerTicket(String numbers) { } private Ticket splitNumbers(String values) { - List numbers = Stream.of(values.split(DELIMITER)) - .map(s -> s.replaceAll(SPACE, EMPTY)) - .map(Number::new) - .collect(Collectors.toList()); - return new Ticket((numbers)); + return SplitNumbers.splitNumberStream(values) + .collect(collectingAndThen(Collectors.toList(), Ticket::new)); } public boolean isSameNumber(Number number) { @@ -29,7 +24,7 @@ public boolean isSameNumber(Number number) { } public int findMatchCount(Ticket ticket) { - return ticket.hasSameNumberCount(winnerTicket); + return ticket.sameNumberCount(winnerTicket); } @Override diff --git a/src/main/java/lotto/ticket/strategy/ManualNumbersGenerator.java b/src/main/java/lotto/ticket/strategy/ManualNumbersGenerator.java new file mode 100644 index 0000000000..f8f5ccd70a --- /dev/null +++ b/src/main/java/lotto/ticket/strategy/ManualNumbersGenerator.java @@ -0,0 +1,28 @@ +package lotto.ticket.strategy; + +import lotto.ticket.Number; +import lotto.ticket.util.SplitNumbers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class ManualNumbersGenerator implements NumbersGenerator { + private final List ticket; + + public ManualNumbersGenerator(String numbers) { + this.ticket = new ArrayList<>(splitNumbers(numbers)); + } + + private List splitNumbers(String numbers) { + return SplitNumbers.splitNumberStream(numbers) + .collect(Collectors.toList()); + } + + @Override + public List generate() { + Collections.sort(ticket); + return Collections.unmodifiableList(ticket); + } +} diff --git a/src/main/java/lotto/ticket/strategy/RandomNumbersGenerator.java b/src/main/java/lotto/ticket/strategy/RandomNumbersGenerator.java index 5ab198697d..9f784ef8fb 100644 --- a/src/main/java/lotto/ticket/strategy/RandomNumbersGenerator.java +++ b/src/main/java/lotto/ticket/strategy/RandomNumbersGenerator.java @@ -1,34 +1,23 @@ package lotto.ticket.strategy; import lotto.ticket.Number; +import lotto.ticket.Ticket; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.IntStream; public class RandomNumbersGenerator implements NumbersGenerator { - public static final int MINIMUM_NUMBER = 1; - public static final int MAXIMUM_NUMBER = 45; - public static final int NUMBER_COUNT_IN_LOTTO = 6; - - private static final List NUMBERS_IN_RANGE = - IntStream.rangeClosed(MINIMUM_NUMBER, MAXIMUM_NUMBER) - .boxed() - .collect(Collectors.toList()); @Override public List generate() { - List randomNumbers = new ArrayList<>(); - Collections.shuffle(NUMBERS_IN_RANGE); - for (int i = 0; i < NUMBER_COUNT_IN_LOTTO; i++) { - randomNumbers.add(NUMBERS_IN_RANGE.get(i)); - } - Collections.sort(randomNumbers); - return randomNumbers.stream() - .map(n -> Integer.toString(n)) - .map(Number::new) + List numbers = new ArrayList<>(Number.values()); + Collections.shuffle(numbers); + + return numbers.stream() + .limit(Ticket.NUMBER_COUNT) + .sorted() .collect(Collectors.toList()); } } diff --git a/src/main/java/lotto/ticket/util/SplitNumbers.java b/src/main/java/lotto/ticket/util/SplitNumbers.java new file mode 100644 index 0000000000..67231fb941 --- /dev/null +++ b/src/main/java/lotto/ticket/util/SplitNumbers.java @@ -0,0 +1,21 @@ +package lotto.ticket.util; + +import lotto.ticket.Number; + +import java.util.stream.Stream; + +public class SplitNumbers { + private static final String DELIMITER = ","; + private static final String SPACE = " "; + private static final String EMPTY = ""; + + private SplitNumbers() { + } + + public static Stream splitNumberStream(String numbers) { + return Stream.of(numbers.split(DELIMITER)) + .map(s -> s.replaceAll(SPACE, EMPTY)) + .map(Number::valueOf) + .sorted(); + } +} diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java index 89b7659a6b..49d089ff55 100644 --- a/src/main/java/lotto/view/InputView.java +++ b/src/main/java/lotto/view/InputView.java @@ -1,9 +1,12 @@ package lotto.view; +import lotto.game.LottoCount; +import lotto.money.Money; import lotto.ticket.BonusBall; import lotto.ticket.WinnerTicket; -import lotto.money.Money; +import java.util.ArrayList; +import java.util.List; import java.util.Scanner; public class InputView { @@ -26,4 +29,17 @@ public static BonusBall inputBonusBall(WinnerTicket winnerTicket) { String value = scanner.nextLine(); return new BonusBall(value, winnerTicket); } + + public static LottoCount inputManualTicketAmount() { + String value = scanner.nextLine(); + return new LottoCount(value); + } + + public static List inputNumbers(LottoCount count) { + List manualLottoNumbers = new ArrayList<>(); + for (int i = 0; i < count.getLottoCount(); i++) { + manualLottoNumbers.add(scanner.nextLine()); + } + return manualLottoNumbers; + } } diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java index 81a40117ca..f5e1d154e6 100644 --- a/src/main/java/lotto/view/OutputView.java +++ b/src/main/java/lotto/view/OutputView.java @@ -1,26 +1,25 @@ package lotto.view; import lotto.game.LottoCount; -import lotto.ticket.Ticket; -import lotto.ticket.Tickets; import lotto.ranking.Ranking; import lotto.ranking.Statistics; +import lotto.ticket.Ticket; +import lotto.ticket.Tickets; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.Map; public class OutputView { private static final String ENTER_PURCHASE_MONEY_MESSAGE = "구입금액을 입력해 주세요."; - private static final String COMPLETE_PURCHASE_MESSAGE = "개를 구매했습니다."; + private static final String ENTER_MANUAL_TICKET_AMOUNT = "수동으로 구매할 로또 수를 입력해 주세요."; + private static final String ENTER_MANUAL_TICKET_NUMBER = "수동으로 구매할 번호를 입력해 주세요."; + private static final String COMPLETE_PURCHASE_MESSAGE = "수동으로 %d장, 자동으로 %d개를 구매했습니다."; private static final String ENTER_WINNER_TICKET_MESSAGE = "지난 주 당첨 번호를 입력해 주세요."; private static final String ENTER_BONUS_BALL_MESSAGE = "보너스 볼을 입력해 주세요."; private static final String STATISTICS_TITLE = "당첨 통계"; private static final String STATISTICS_DIVIDER = "---------"; - private static final String RANKING_RESULT_FORMAT = "%d개 일치, 보너스 볼 일치 (%d원)- %d개%n"; + private static final String RANKING_RESULT_FORMAT = "%d개 일치 (%d원)- %d개%n"; private static final String RANKING_SECOND_RESULT_FORMAT = "%d개 일치, 보너스 볼 일치 (%d원)- %d개%n"; - private static final String TOTAL_PROFIT_FORMAT = "총 수익률은 %s 입니다."; + private static final String TOTAL_PROFIT_FORMAT = "총 수익률은 %.2f 입니다."; private OutputView() { } @@ -29,8 +28,9 @@ public static void enterPurchaseMoney() { System.out.println(ENTER_PURCHASE_MONEY_MESSAGE); } - public static void noticeLottoCount(LottoCount lottoCount) { - System.out.println(lottoCount.getLottoCount() + COMPLETE_PURCHASE_MESSAGE); + public static void noticeLottoCount(LottoCount manual, LottoCount auto) { + System.out.printf(COMPLETE_PURCHASE_MESSAGE, manual.getLottoCount(), auto.getLottoCount()); + System.out.println(); } public static void showTickets(Tickets tickets) { @@ -57,9 +57,7 @@ public static void noticeStatistics(Statistics statistics) { private static void printRankings(Statistics statistics) { Map result = statistics.getStatistics(); - List rankings = Arrays.asList(Ranking.values()); - Collections.reverse(rankings); - for (Ranking ranking : rankings) { + for (Ranking ranking : Ranking.values()) { printEachRanking(result, ranking); } } @@ -75,12 +73,20 @@ private static void printEachRanking(Map result, Ranking ranki System.out.printf(RANKING_RESULT_FORMAT, ranking.getMatchCount(), ranking.getPrice(), result.get(ranking)); } - public static void showProfit(String calculateProfit) { + public static void showProfit(double calculateProfit) { System.out.printf(TOTAL_PROFIT_FORMAT, calculateProfit); System.out.println(); } - public static void printError(IllegalArgumentException e) { + public static void printError(RuntimeException e) { System.err.println(e.getMessage()); } + + public static void enterManualTicketAmount() { + System.out.println(ENTER_MANUAL_TICKET_AMOUNT); + } + + public static void enterManualTicketNumber() { + System.out.println(ENTER_MANUAL_TICKET_NUMBER); + } } \ No newline at end of file diff --git a/src/test/java/lotto/game/LottoCountTest.java b/src/test/java/lotto/game/LottoCountTest.java index c0043b805d..650c058081 100644 --- a/src/test/java/lotto/game/LottoCountTest.java +++ b/src/test/java/lotto/game/LottoCountTest.java @@ -4,7 +4,10 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static lotto.game.LottoCount.ERROR_MESSAGE_INVALID_AMOUNT; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_INPUT; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; public class LottoCountTest { @@ -14,4 +17,59 @@ void lottoCountCreate() { LottoCount lottoCount = new LottoCount(new Money("10000")); assertThat(lottoCount).isEqualTo(new LottoCount(new Money("10000"))); } + + @Test + @DisplayName("구매 금액에 따른 로또 티켓 장수확인") + void lottoCount() { + LottoCount lottoCount = new LottoCount(new Money("14000")); + assertThat(lottoCount.getLottoCount()).isEqualTo(14); + } + + @Test + @DisplayName("수동으로 구매할 로또 수 입력에 따른 LottoCount 생성") + void manualLottoCountCreate() { + Money purchaseMoney = new Money("14000"); + LottoCount lottoCount = new LottoCount("3"); + assertThat(lottoCount).isEqualTo(new LottoCount("3")); + } + + @Test + @DisplayName("검증: 숫자로 입력되어야 한다.") + void checkNumber() { + assertThatThrownBy(() -> + new LottoCount("^") + ).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE_INVALID_INPUT); + } + + @Test + @DisplayName("검증: 음수가 아니어야 한다.") + void notNegative() { + assertThatThrownBy(() -> + new LottoCount("-1") + ).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE_INVALID_INPUT); + } + + @Test + @DisplayName("검증: 구매 금액 보다 더 많은 로또를 살 수 없다.") + void checkPossibleAmount() { + Money purchaseMoney = new Money("14000"); + LottoCount lottoCount = new LottoCount(purchaseMoney); + LottoCount manualLottoCount = new LottoCount("15"); + assertThatThrownBy(() -> + lottoCount.consumeTicket(manualLottoCount) + ).isInstanceOf(IllegalStateException.class) + .hasMessageContaining(ERROR_MESSAGE_INVALID_AMOUNT); + } + + @Test + @DisplayName("수동 구매 후 남은 구입 가능 횟수 확인") + void checkRemainCount() { + Money purchaseMoney = new Money("14000"); + LottoCount lottoCount = new LottoCount(purchaseMoney); + LottoCount manualLottoCount = new LottoCount("3"); + LottoCount remainCount = lottoCount.consumeTicket(manualLottoCount); + assertThat(remainCount).isEqualTo(new LottoCount("11")); + } } diff --git a/src/test/java/lotto/game/LottoServiceTest.java b/src/test/java/lotto/game/LottoServiceTest.java new file mode 100644 index 0000000000..74886a31c3 --- /dev/null +++ b/src/test/java/lotto/game/LottoServiceTest.java @@ -0,0 +1,41 @@ +package lotto.game; + +import lotto.ticket.Tickets; +import lotto.ticket.strategy.RandomNumbersGenerator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class LottoServiceTest { + LottoService lottoService; + + @BeforeEach + void setUp() { + lottoService = new LottoService(new RandomNumbersGenerator()); + } + + @Test + @DisplayName("수동 로또 구매") + void buyManualTicket() { + List ticketNumbers = Arrays.asList( + "1,2,3,4,5,6", + "1,3,5,7,8,10", + "4,8,10,15,17,45" + ); + Tickets tickets = lottoService.buyManualTickets(ticketNumbers); + assertThat(tickets.getTickets().size()).isEqualTo(3); + } + + @Test + @DisplayName("자동 로또 구매") + void buyAutoTicket() { + LottoCount lottoCount = new LottoCount("3"); + Tickets tickets = lottoService.buyAutoTickets(lottoCount); + assertThat(tickets.getTickets().size()).isEqualTo(3); + } +} diff --git a/src/test/java/lotto/money/MoneyTest.java b/src/test/java/lotto/money/MoneyTest.java index 2081fe22e9..fe92dd1b2f 100644 --- a/src/test/java/lotto/money/MoneyTest.java +++ b/src/test/java/lotto/money/MoneyTest.java @@ -3,8 +3,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static lotto.ticket.TicketValidation.ERROR_MESSAGE_INVALID_INPUT; import static lotto.money.Money.ERROR_MESSAGE_MINIMUM_MONEY; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_INPUT; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; @@ -45,6 +45,6 @@ void negativeAmount() { @DisplayName("수익률 계산") void calculateProfit() { Money money = new Money("14000"); - assertThat(money.calculateProfit(5000)).isEqualTo("0.35"); + assertThat(money.calculateProfit(5000)).isEqualTo(0.35); } } diff --git a/src/test/java/lotto/money/PrizeMoneyTest.java b/src/test/java/lotto/money/PrizeMoneyTest.java deleted file mode 100644 index 0500ef5c12..0000000000 --- a/src/test/java/lotto/money/PrizeMoneyTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package lotto.money; - -import lotto.ranking.Ranking; -import lotto.ranking.Statistics; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -public class PrizeMoneyTest { - - @Test - @DisplayName("당첨 금액 생성 확인") - void prizeMoneyCreate() { - Statistics statistics = new Statistics(Arrays.asList(Ranking.FIRST, Ranking.FORTH, Ranking.FORTH, Ranking.FORTH)); - PrizeMoney prizeMoney = new PrizeMoney(statistics); - assertThat(prizeMoney).isEqualTo(new PrizeMoney(statistics)); - } - - @Test - @DisplayName("수익률 계산 확인") - void calculatePrizeMoney() { - Statistics statistics = new Statistics(Arrays.asList(Ranking.FIFTH, Ranking.NOTHING, Ranking.NOTHING, Ranking.NOTHING)); - PrizeMoney prizeMoney = new PrizeMoney(statistics); - assertThat(prizeMoney.calculateProfit(new Money("14000"))).isEqualTo("0.35"); - } -} \ No newline at end of file diff --git a/src/test/java/lotto/ranking/RankingTest.java b/src/test/java/lotto/ranking/RankingTest.java index 78001a4031..017e7f3d1c 100644 --- a/src/test/java/lotto/ranking/RankingTest.java +++ b/src/test/java/lotto/ranking/RankingTest.java @@ -4,14 +4,20 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; public class RankingTest { @Test @DisplayName("상금 생성 확인") void priceCreate() { - assertThat(Ranking.makePrice(4, false)).isEqualTo(Ranking.FORTH); - assertThat(Ranking.makePrice(5, true)).isEqualTo(Ranking.SECOND); - assertThat(Ranking.makePrice(5, false)).isEqualTo(Ranking.THIRD); + assertAll( + () -> assertThat(Ranking.makePrice(2, false)).isEqualTo(Ranking.NOTHING), + () -> assertThat(Ranking.makePrice(3, false)).isEqualTo(Ranking.FIFTH), + () -> assertThat(Ranking.makePrice(4, false)).isEqualTo(Ranking.FORTH), + () -> assertThat(Ranking.makePrice(5, false)).isEqualTo(Ranking.THIRD), + () -> assertThat(Ranking.makePrice(5, true)).isEqualTo(Ranking.SECOND), + () -> assertThat(Ranking.makePrice(6, false)).isEqualTo(Ranking.FIRST) + ); } } diff --git a/src/test/java/lotto/ranking/StatisticsTest.java b/src/test/java/lotto/ranking/StatisticsTest.java index 70f80999c2..c4f5dd824a 100644 --- a/src/test/java/lotto/ranking/StatisticsTest.java +++ b/src/test/java/lotto/ranking/StatisticsTest.java @@ -1,5 +1,6 @@ package lotto.ranking; +import lotto.money.Money; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -17,4 +18,18 @@ void statisticsCreate() { assertThat(statistics.findRankingCount(Ranking.THIRD)).isEqualTo(0); assertThat(statistics.findRankingCount(Ranking.FORTH)).isEqualTo(3); } + + @Test + @DisplayName("당첨 결과에 따른 당첨금 총액 확인") + void totalPrize() { + Statistics statistics = new Statistics(Arrays.asList(Ranking.FIRST, Ranking.FORTH, Ranking.FORTH, Ranking.FORTH)); + assertThat(statistics.getTotalPrize()).isEqualTo(2_000_150_000); + } + + @Test + @DisplayName("수익률 계산 확인") + void calculateProfit() { + Statistics statistics = new Statistics(Arrays.asList(Ranking.FIFTH, Ranking.NOTHING, Ranking.NOTHING, Ranking.NOTHING)); + assertThat(statistics.calculateProfit(new Money("14000"))).isEqualTo(0.35); + } } diff --git a/src/test/java/lotto/ticket/BonusBallTest.java b/src/test/java/lotto/ticket/BonusBallTest.java index d58937eb40..fbfebd4fdc 100644 --- a/src/test/java/lotto/ticket/BonusBallTest.java +++ b/src/test/java/lotto/ticket/BonusBallTest.java @@ -3,7 +3,9 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static lotto.ticket.TicketValidation.*; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_INPUT; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_RANGE; +import static lotto.ticket.Ticket.ERROR_MESSAGE_DUPLICATED; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -30,7 +32,7 @@ void bonusBallNotNumber() { @DisplayName("숫자의 범위가 1부터 45사이의 수가 아닌 경우") void checkNumberInRange() { assertThatThrownBy(() -> new BonusBall("46", new WinnerTicket("1, 2, 3, 4, 5, 6"))) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(IllegalStateException.class) .hasMessage(ERROR_MESSAGE_INVALID_RANGE); } @@ -46,7 +48,7 @@ void checkDuplicated() { @DisplayName("보너스볼 포함 확인") void checkContainBonusBall() { BonusBall bonusBall = new BonusBall("15", new WinnerTicket("1, 2, 3, 4, 5, 6")); - assertTrue(bonusBall.isSameThan(new Number("15"))); - assertFalse(bonusBall.isSameThan(new Number("5"))); + assertTrue(bonusBall.isSameThan(Number.valueOf("15"))); + assertFalse(bonusBall.isSameThan(Number.valueOf("5"))); } } diff --git a/src/test/java/lotto/ticket/ManualNumbersGeneratorTest.java b/src/test/java/lotto/ticket/ManualNumbersGeneratorTest.java new file mode 100644 index 0000000000..22e9f33f28 --- /dev/null +++ b/src/test/java/lotto/ticket/ManualNumbersGeneratorTest.java @@ -0,0 +1,70 @@ +package lotto.ticket; + +import lotto.ticket.strategy.ManualNumbersGenerator; +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.List; + +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_INPUT; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_RANGE; +import static lotto.ticket.Ticket.ERROR_MESSAGE_DUPLICATED; +import static lotto.ticket.Ticket.ERROR_MESSAGE_INVALID_SIZE; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class ManualNumbersGeneratorTest { + @ParameterizedTest + @DisplayName("잘못된 구분자를 사용한 경우") + @ValueSource(strings = {"1, 2, 3, 4, 5. 6", "1#2,3,4,5,6", "1,,2,3,4,5,6"}) + void checkWrongDelimiter(String value) { + assertThatThrownBy(() -> { + List numbers = new ManualNumbersGenerator(value).generate(); + new Ticket(numbers); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage(ERROR_MESSAGE_INVALID_INPUT); + } + + @Test + @DisplayName("숫자가 아닌 경우") + void checkNotNumber() { + assertThatThrownBy(() -> { + List numbers = new ManualNumbersGenerator("1,^,2,3,4,5").generate(); + new Ticket(numbers); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage(ERROR_MESSAGE_INVALID_INPUT); + } + + @Test + @DisplayName("숫자가 6개가 아닌 경우") + void checkNumbersSize() { + assertThatThrownBy(() -> { + List numbers = new ManualNumbersGenerator("1,2,3,4,5").generate(); + new Ticket(numbers); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage(ERROR_MESSAGE_INVALID_SIZE); + } + + @Test + @DisplayName("숫자의 범위가 1부터 45사이의 수가 아닌 경우") + void checkNumberInRange() { + assertThatThrownBy(() -> { + List numbers = new ManualNumbersGenerator("1,2,3,4,5,46").generate(); + new Ticket(numbers); + }).isInstanceOf(IllegalStateException.class) + .hasMessage(ERROR_MESSAGE_INVALID_RANGE); + } + + @Test + @DisplayName("숫자가 중복되는 경우") + void checkDuplicated() { + assertThatThrownBy(() -> { + List numbers = new ManualNumbersGenerator("1,1,2,3,4,5").generate(); + new Ticket(numbers); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage(ERROR_MESSAGE_DUPLICATED); + } + + +} diff --git a/src/test/java/lotto/ticket/NumberTest.java b/src/test/java/lotto/ticket/NumberTest.java new file mode 100644 index 0000000000..e38dc52554 --- /dev/null +++ b/src/test/java/lotto/ticket/NumberTest.java @@ -0,0 +1,43 @@ +package lotto.ticket; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_INPUT; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_RANGE; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class NumberTest { + @Test + @DisplayName("번호 생성 테스트") + void create() { + Number number = Number.valueOf("43"); + assertThat(number).isEqualTo(Number.valueOf("43")); + } + + @Test + @DisplayName("생성된 숫자 갯수 확인") + void numberCacheCount() { + assertThat(Number.values().size()).isEqualTo(45); + } + + @Test + @DisplayName("검증: 숫자가 아닌 값이 들어온 경우") + void checkNotNumber() { + assertThatThrownBy(() -> + Number.valueOf("*") + ).isInstanceOf(IllegalArgumentException.class) + .hasMessage(ERROR_MESSAGE_INVALID_INPUT); + } + + @Test + @DisplayName("검증: 1~45 를 벗어나는 수를 가져오려는 경우") + void checkNotInRangeNumber() { + assertThatThrownBy(() -> { + Number.valueOf("0"); + Number.valueOf("46"); + }).isInstanceOf(IllegalStateException.class) + .hasMessage(ERROR_MESSAGE_INVALID_RANGE); + } +} diff --git a/src/test/java/lotto/ticket/RandomNumbersGeneratorTest.java b/src/test/java/lotto/ticket/RandomNumbersGeneratorTest.java index 6f979ae47e..9542bcf55e 100644 --- a/src/test/java/lotto/ticket/RandomNumbersGeneratorTest.java +++ b/src/test/java/lotto/ticket/RandomNumbersGeneratorTest.java @@ -23,7 +23,7 @@ void setUp() { @Test @DisplayName("자동 로또 생성 숫자 6개 확인") void randomNumbersCreate() { - assertThat(randomNumbers.size()).isEqualTo(6); + assertThat(randomNumbers.size()).isEqualTo(Ticket.NUMBER_COUNT); } @Test @@ -34,12 +34,4 @@ void checkDuplicated() { .count() == randomNumbers.size(); assertTrue(duplicated); } - -// @Test -// @DisplayName("자동 로또 생성 숫자 1부터 45 사이 확인") -// void checkNumbersInRange() { -// for (Number randomNumber : randomNumbers) { -// assertThat(randomNumber).isBetween(MINIMUM_NUMBER, MAXIMUM_NUMBER); -// } -// } } diff --git a/src/test/java/lotto/ticket/TicketTest.java b/src/test/java/lotto/ticket/TicketTest.java index ab9c36176a..ab32a4270d 100644 --- a/src/test/java/lotto/ticket/TicketTest.java +++ b/src/test/java/lotto/ticket/TicketTest.java @@ -1,12 +1,16 @@ package lotto.ticket; +import lotto.ticket.strategy.ManualNumbersGenerator; import lotto.ticket.strategy.NumbersGenerator; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; +import java.util.List; -import static lotto.ticket.TicketValidation.*; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_RANGE; +import static lotto.ticket.Ticket.ERROR_MESSAGE_DUPLICATED; +import static lotto.ticket.Ticket.ERROR_MESSAGE_INVALID_SIZE; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -17,15 +21,15 @@ public class TicketTest { void ticketCreate() { NumbersGenerator numbersGenerator = () -> Arrays.asList( - new Number("1"), - new Number("2"), - new Number("3"), - new Number("4"), - new Number("5"), - new Number("6") + Number.valueOf("1"), + Number.valueOf("2"), + Number.valueOf("3"), + Number.valueOf("4"), + Number.valueOf("5"), + Number.valueOf("6") ); - Ticket ticket = new Ticket(numbersGenerator); - assertThat(ticket).isEqualTo(new Ticket(numbersGenerator)); + Ticket ticket = new Ticket(numbersGenerator.generate()); + assertThat(ticket).isEqualTo(new Ticket(numbersGenerator.generate())); } @Test @@ -33,16 +37,16 @@ void ticketCreate() { void checkNumberInRange() { NumbersGenerator numbersGenerator = () -> Arrays.asList( - new Number("1"), - new Number("46"), - new Number("2"), - new Number("3"), - new Number("4"), - new Number("5") + Number.valueOf("1"), + Number.valueOf("46"), + Number.valueOf("2"), + Number.valueOf("3"), + Number.valueOf("4"), + Number.valueOf("5") ); assertThatThrownBy(() -> - new Ticket(numbersGenerator) - ).isInstanceOf(IllegalArgumentException.class) + new Ticket(numbersGenerator.generate()) + ).isInstanceOf(IllegalStateException.class) .hasMessageContaining(ERROR_MESSAGE_INVALID_RANGE); } @@ -51,15 +55,15 @@ void checkNumberInRange() { void checkDuplicatedNumber() { NumbersGenerator numbersGenerator = () -> Arrays.asList( - new Number("1"), - new Number("1"), - new Number("2"), - new Number("3"), - new Number("4"), - new Number("5") + Number.valueOf("1"), + Number.valueOf("1"), + Number.valueOf("2"), + Number.valueOf("3"), + Number.valueOf("4"), + Number.valueOf("5") ); assertThatThrownBy(() -> - new Ticket(numbersGenerator) + new Ticket(numbersGenerator.generate()) ).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ERROR_MESSAGE_DUPLICATED); } @@ -69,14 +73,14 @@ void checkDuplicatedNumber() { void checkSizeOfNumbers() { NumbersGenerator numbersGenerator = () -> Arrays.asList( - new Number("1"), - new Number("2"), - new Number("3"), - new Number("4"), - new Number("5") + Number.valueOf("1"), + Number.valueOf("2"), + Number.valueOf("3"), + Number.valueOf("4"), + Number.valueOf("5") ); assertThatThrownBy(() -> - new Ticket(numbersGenerator) + new Ticket(numbersGenerator.generate()) ).isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(ERROR_MESSAGE_INVALID_SIZE); } @@ -86,15 +90,23 @@ void checkSizeOfNumbers() { void checkContainBonusBall() { NumbersGenerator numbersGenerator = () -> Arrays.asList( - new Number("1"), - new Number("2"), - new Number("3"), - new Number("4"), - new Number("5"), - new Number("6") + Number.valueOf("1"), + Number.valueOf("2"), + Number.valueOf("3"), + Number.valueOf("4"), + Number.valueOf("5"), + Number.valueOf("6") ); BonusBall bonusBall = new BonusBall("6", new WinnerTicket(("1, 2, 3, 4, 5, 8"))); - Ticket ticket = new Ticket(numbersGenerator); + Ticket ticket = new Ticket(numbersGenerator.generate()); assertTrue(ticket.hasContainBonus(bonusBall)); } + + @Test + @DisplayName("수동 생성 시 티켓 내부 숫자 개수 확인") + void checkManualTicketSize() { + List numbers = new ManualNumbersGenerator("1,2,3,4,5,6").generate(); + List ticket = new Ticket(numbers).getTicket(); + assertThat(ticket.size()).isEqualTo(Ticket.NUMBER_COUNT); + } } diff --git a/src/test/java/lotto/ticket/TicketsTest.java b/src/test/java/lotto/ticket/TicketsTest.java index 5399a8c578..ec63e10c76 100644 --- a/src/test/java/lotto/ticket/TicketsTest.java +++ b/src/test/java/lotto/ticket/TicketsTest.java @@ -1,12 +1,13 @@ package lotto.ticket; import lotto.game.LottoCount; -import lotto.ticket.strategy.RandomNumbersGenerator; import lotto.money.Money; import lotto.ranking.Ranking; +import lotto.ticket.strategy.RandomNumbersGenerator; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -15,7 +16,11 @@ public class TicketsTest { @Test @DisplayName("티켓들 생성 확인") void ticketsCreate() { - Tickets tickets = new Tickets(new LottoCount(new Money("20000")), new RandomNumbersGenerator()); + List ticketList = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + ticketList.add(new Ticket(new RandomNumbersGenerator().generate())); + } + Tickets tickets = new Tickets(ticketList); assertThat(tickets.getTickets().size()).isEqualTo(20); } @@ -24,8 +29,28 @@ void ticketsCreate() { void checkTicketsRanking() { WinnerTicket winnerTicket = new WinnerTicket("1, 2, 3, 4, 5, 6"); BonusBall bonusBall = new BonusBall("7", winnerTicket); - Tickets tickets = new Tickets(new LottoCount(new Money("2000000")), new RandomNumbersGenerator()); + List ticketList = new ArrayList<>(); + for (int i = 0; i < 2000; i++) { + ticketList.add(new Ticket(new RandomNumbersGenerator().generate())); + } + Tickets tickets = new Tickets(ticketList); List result = tickets.makeResult(winnerTicket, bonusBall); assertThat(result.size()).isEqualTo(2000); } + + @Test + @DisplayName("수동 구매 후 남은 카운트로 자동 구매") + void manualTicketAndAutoTicketPurchase() { + Money purchaseMoney = new Money("14000"); + LottoCount lottoCount = new LottoCount(purchaseMoney); + LottoCount manualLottoCount = new LottoCount("3"); + LottoCount remainCount = lottoCount.consumeTicket(manualLottoCount); + List ticketList = new ArrayList<>(); + while (remainCount.isGreaterThanZero()) { + remainCount = remainCount.decreaseOne(); + ticketList.add(new Ticket(new RandomNumbersGenerator().generate())); + } + Tickets tickets = new Tickets(ticketList); + assertThat(tickets.getTickets().size()).isEqualTo(11); + } } diff --git a/src/test/java/lotto/ticket/WinnerTicketTest.java b/src/test/java/lotto/ticket/WinnerTicketTest.java index 2a1ce73121..d85004c5aa 100644 --- a/src/test/java/lotto/ticket/WinnerTicketTest.java +++ b/src/test/java/lotto/ticket/WinnerTicketTest.java @@ -8,7 +8,10 @@ import java.util.Arrays; -import static lotto.ticket.TicketValidation.*; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_INPUT; +import static lotto.ticket.Number.ERROR_MESSAGE_INVALID_RANGE; +import static lotto.ticket.Ticket.ERROR_MESSAGE_DUPLICATED; +import static lotto.ticket.Ticket.ERROR_MESSAGE_INVALID_SIZE; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; @@ -41,7 +44,7 @@ void checkNotNumber() { @DisplayName("숫자의 범위가 1부터 45사이의 수가 아닌 경우") void checkNumberInRange() { assertThatThrownBy(() -> new WinnerTicket("1,2,3,4,5,46")) - .isInstanceOf(IllegalArgumentException.class) + .isInstanceOf(IllegalStateException.class) .hasMessage(ERROR_MESSAGE_INVALID_RANGE); } @@ -66,15 +69,15 @@ void checkDuplicated() { void checkResult() { NumbersGenerator numbersGenerator = () -> Arrays.asList( - new Number("1"), - new Number("2"), - new Number("3"), - new Number("4"), - new Number("5"), - new Number("6") + Number.valueOf("1"), + Number.valueOf("2"), + Number.valueOf("3"), + Number.valueOf("4"), + Number.valueOf("5"), + Number.valueOf("6") ); WinnerTicket winnerTicket = new WinnerTicket("1,2,3,4,5,6"); - Ticket ticket = new Ticket(numbersGenerator); + Ticket ticket = new Ticket(numbersGenerator.generate()); assertThat(winnerTicket.findMatchCount(ticket)).isEqualTo(6); } }