diff --git a/docs/README.md b/docs/README.md index e69de29bb2..c98deb1eda 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,48 @@ +## 3주차 미션 로또 기능 정의 + +### 로또 게임 진행 (LottoController) +- [X] 게임 시작 +- [x] 게임 진행 + +### 로또 개수 계산 (LottoCounter) +- [x] 금액에 맞는 로또 개수 계산 + +### 랜덤 로또 생성 (RandomLottoGenerator) +- [x] 랜덤 번호 6개로 로또 생성 + +### 사용자 생성 로또 (PlayerLotto) +- [x] 원하는 개수만큼 로또 생성 + +### 당첨 로또 (WinningLotto) +- [x] 입력받은 당첨 로또 관리 +- [x] 보너스 숫자 관리 +- [x] 예외) 당첨 로또 숫자와 보너스 숫자가 겹칠 경우 + +### 로또 당첨 계산 (LottoResult) +- [x] 로또 당첨 여부 판별 +- [x] 수익률 계산 + +### 로또 번호 관리(Lotto) +- [x] 예외) 중복된 수를 입력한 경우 +- [x] 예외) 번호가 6개가 아닌 경우 +- [x] 예외) 범위 이외의 수인 경우 + +### 입력 (InputFactory) +- [x] 로또 구입 금액 입력 기능 +- [x] 당첨 번호 입력 기능 +- [x] 보너스 번호 입력 기능 + +### 로또 구매 안내 출력 (OutputView) +- [x] 구매 개수 안내 +- [x] 생성된 로또 번호 안내 +- [x] 당첨 통계 안내 +- [x] 수익률 안내 + +### 예외 처리(InputValidator) +- [x] 보너스 번호를 입력할 때 숫자가 아닌 입력한 경우 +- [x] 범위 이외의 숫자를 보너스 숫자로 입력한 경우 +- [x] 구입 금액이 1000원 단위가 아닌 경우 +- [x] 구입 금액 입력이 숫자가 아닌 경우 +- [x] 잘못된 형식으로 당첨 번호를 입력한 경우 (쉼표가 없는 경우 등) + + diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba4..7c6901cc7f 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,12 @@ package lotto; +import lotto.controller.LottoController; +import lotto.view.InputFactory; +import lotto.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + LottoController lottoController = new LottoController(new OutputView(), new InputFactory()); + lottoController.start(); } } diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java deleted file mode 100644 index 519793d1f7..0000000000 --- a/src/main/java/lotto/Lotto.java +++ /dev/null @@ -1,20 +0,0 @@ -package lotto; - -import java.util.List; - -public class Lotto { - private final List numbers; - - public Lotto(List numbers) { - validate(numbers); - this.numbers = numbers; - } - - private void validate(List numbers) { - if (numbers.size() != 6) { - throw new IllegalArgumentException(); - } - } - - // TODO: 추가 기능 구현 -} diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java new file mode 100644 index 0000000000..8c2b13ef5d --- /dev/null +++ b/src/main/java/lotto/controller/LottoController.java @@ -0,0 +1,45 @@ +package lotto.controller; + +import lotto.model.*; +import lotto.view.InputFactory; +import lotto.view.OutputView; + +public class LottoController { + + private final OutputView outputView; + private final InputFactory inputFactory; + + public LottoController(OutputView outputView, InputFactory inputFactory) { + this.outputView = outputView; + this.inputFactory = inputFactory; + } + + public void start() { + try { + PlayerLottoes playerLottoes = buyLotto(); + WinningLotto winningLotto = createWinningLotto(); + calculateResult(playerLottoes, winningLotto); + } catch (IllegalArgumentException error) { + outputView.printErrorMessage(error.getMessage()); + } + } + + private PlayerLottoes buyLotto() { + LottoCounter lottoCounter = new LottoCounter(inputFactory.readPurchaseAmount()); + PlayerLottoes playerLottoes = new PlayerLottoes(lottoCounter.getLotteryTicket()); + outputView.printPurchaseCount(lottoCounter.getLotteryTicket()); + outputView.printPlayerLottoes(playerLottoes); + return playerLottoes; + } + + private WinningLotto createWinningLotto() { + return new WinningLotto(inputFactory.readWinningNumber(), inputFactory.readBonusNumber()); + } + + private void calculateResult(PlayerLottoes playerLottoes, WinningLotto winningLotto) { + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(winningLotto, playerLottoes); + ProfitCalculator profitCalculator = new ProfitCalculator(lottoResultCalculator.getRankingsResult()); + outputView.printLottoResult(lottoResultCalculator.getRankingsResult()); + outputView.printProfit(profitCalculator.getProfit()); + } +} diff --git a/src/main/java/lotto/model/BonusNumber.java b/src/main/java/lotto/model/BonusNumber.java new file mode 100644 index 0000000000..1edb5c6c30 --- /dev/null +++ b/src/main/java/lotto/model/BonusNumber.java @@ -0,0 +1,26 @@ +package lotto.model; + +import lotto.view.ExceptionMessage; + +public class BonusNumber { + + private final int bonusNumber; + private static final int MAX_NUMBER = 45; + private static final int MIN_NUMBER = 1; + + public BonusNumber(int bonusNumber) { + validateCorrectRange(bonusNumber); + this.bonusNumber = bonusNumber; + } + + public int getBonus() { + return bonusNumber; + } + + private void validateCorrectRange(int bonusNumber) { + if (!(MIN_NUMBER <= bonusNumber && bonusNumber <= MAX_NUMBER)) { + ExceptionMessage exceptionMessage = ExceptionMessage.INCORRECT_RANGE; + throw new IllegalArgumentException(exceptionMessage.toString()); + } + } +} diff --git a/src/main/java/lotto/model/Constant.java b/src/main/java/lotto/model/Constant.java new file mode 100644 index 0000000000..722e8f9f52 --- /dev/null +++ b/src/main/java/lotto/model/Constant.java @@ -0,0 +1,19 @@ +package lotto.model; + +public enum Constant { + + MAX_NUMBER(45), + MIN_NUMBER(1), + LOTTO_SIZE(6), + LOTTO_PRICE(1000); + + private final Integer constant; + + Constant(Integer constant) { + this.constant = constant; + } + + public Integer getConstant() { + return this.constant; + } +} diff --git a/src/main/java/lotto/model/Lotto.java b/src/main/java/lotto/model/Lotto.java new file mode 100644 index 0000000000..22577afca5 --- /dev/null +++ b/src/main/java/lotto/model/Lotto.java @@ -0,0 +1,52 @@ +package lotto.model; + +import lotto.view.ExceptionMessage; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Lotto { + private final List numbers; + + public Lotto(List numbers) { + validateLottoSize(numbers); + validateDuplicateNumber(numbers); + validateRange(numbers); + this.numbers = numbers; + } + + private void validateLottoSize(List numbers) { + if (numbers.size() != Constant.LOTTO_SIZE.getConstant()) { + ExceptionMessage message = ExceptionMessage.INCORRECT_LOTTO_SIZE; + throw new IllegalArgumentException(message.toString()); + } + } + + private void validateDuplicateNumber(List numbers) { + Set numberToSet = new HashSet<>(numbers); + if (numberToSet.size() != Constant.LOTTO_SIZE.getConstant()) { + ExceptionMessage message = ExceptionMessage.DUPLICATE_NUMBER; + throw new IllegalArgumentException(message.toString()); + } + } + + private void validateRange(List numbers) { + numbers.forEach(this::isCorrectRange); + } + private void isCorrectRange(int number) { + int maxNumber = Constant.MAX_NUMBER.getConstant(); + int minNumber = Constant.MIN_NUMBER.getConstant(); + if (!(minNumber <= number && number <= maxNumber)) { + ExceptionMessage exceptionMessage = ExceptionMessage.INCORRECT_RANGE; + throw new IllegalArgumentException(exceptionMessage.toString()); + } + } + + public List getNumbers() { + return numbers; + } + public boolean hasNumber(int number) { + return numbers.contains(number); + } +} diff --git a/src/main/java/lotto/model/LottoCounter.java b/src/main/java/lotto/model/LottoCounter.java new file mode 100644 index 0000000000..eb7ca4c9b0 --- /dev/null +++ b/src/main/java/lotto/model/LottoCounter.java @@ -0,0 +1,28 @@ +package lotto.model; + +import lotto.view.ExceptionMessage; + +public class LottoCounter { + + private final int lotteryTicket; + + public LottoCounter(int payment) { + validateThousandUnits(payment); + this.lotteryTicket = calculateLotteryTicket(payment); + } + + public int calculateLotteryTicket(int payment) { + return payment / Constant.LOTTO_PRICE.getConstant(); + } + + public int getLotteryTicket() { + return lotteryTicket; + } + + private void validateThousandUnits(Integer amount) { + if((amount % Constant.LOTTO_PRICE.getConstant()) != 0) { + ExceptionMessage exceptionMessage = ExceptionMessage.INCORRECT_AMOUNT; + throw new IllegalArgumentException(exceptionMessage.toString()); + } + } +} diff --git a/src/main/java/lotto/model/LottoResult.java b/src/main/java/lotto/model/LottoResult.java new file mode 100644 index 0000000000..c9abf43ede --- /dev/null +++ b/src/main/java/lotto/model/LottoResult.java @@ -0,0 +1,68 @@ +package lotto.model; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.Map; +import java.util.Set; + +public class LottoResult { + + private final Map result; + + public LottoResult() { + this.result = new EnumMap<>(Ranking.class); + Arrays.stream(Ranking.values()) + .forEach(ranking -> result.put(ranking, 0)); + } + + public void putResult(Ranking ranking) { + result.put(ranking, result.get(ranking) + 1); + } + + public Set keySet() { + return result.keySet(); + } + + public Integer get(Ranking ranking) { + return result.get(ranking); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + Arrays.stream(Ranking.values()) + .filter(ranking -> ranking != Ranking.NONE) + .forEach(ranking -> { + appendMatchNumberMessage(ranking, stringBuilder); + appendBonusMessage(ranking, stringBuilder); + appendPrizeAmountMessage(ranking, stringBuilder); + appendWinningNumberMessage(ranking, stringBuilder); + stringBuilder.append(ResultMessage.NEW_LINE); + }); + return stringBuilder.toString(); + } + + private void appendMatchNumberMessage(Ranking ranking, StringBuilder stringBuilder) { + ResultMessage resultMessage = ResultMessage.MATCH_NUMBER_MESSAGE; + int matchNumber = ranking.getMatchNumber(); + stringBuilder.append(String.format(resultMessage.toString(), matchNumber)); + } + + private void appendPrizeAmountMessage(Ranking ranking, StringBuilder stringBuilder) { + ResultMessage resultMessage = ResultMessage.AMOUNT_MESSAGE; + int prize = ranking.getPrize(); + stringBuilder.append(String.format(resultMessage.toString(), prize)); + } + + private void appendBonusMessage(Ranking ranking, StringBuilder stringBuilder) { + if (ranking == Ranking.SECOND) { + stringBuilder.append(ResultMessage.BONUS_MESSAGE); + } + } + + private void appendWinningNumberMessage(Ranking ranking, StringBuilder stringBuilder) { + ResultMessage resultMessage = ResultMessage.WINNING_NUMBER_MESSAGE; + int winningNumber = result.get(ranking); + stringBuilder.append(String.format(resultMessage.toString(), winningNumber)); + } +} diff --git a/src/main/java/lotto/model/LottoResultCalculator.java b/src/main/java/lotto/model/LottoResultCalculator.java new file mode 100644 index 0000000000..7312394ff6 --- /dev/null +++ b/src/main/java/lotto/model/LottoResultCalculator.java @@ -0,0 +1,56 @@ +package lotto.model; + +import java.util.Arrays; + +public class LottoResultCalculator { + + private final WinningLotto winningLotto; + private final PlayerLottoes playerLottoes; + private final LottoResult lottoResult; + private static final int THIRD_RANKING_NUMBER = 5; + + public LottoResultCalculator(WinningLotto winningLotto, PlayerLottoes playerLottoes) { + this.winningLotto = winningLotto; + this.playerLottoes = playerLottoes; + this.lottoResult = calculateWinningRank(); + } + + private LottoResult calculateWinningRank() { + LottoResult result = new LottoResult(); + for (Lotto lotto : playerLottoes.getLottoes()) { + int matchNumber = calculateMatchNumber(lotto); + boolean matchBonusNumber = hasBonusNumber(matchNumber, lotto); + Ranking ranking = calculateRanking(matchNumber, matchBonusNumber); + result.putResult(ranking); + } + return result; + } + + private static Ranking calculateRanking(int matchNumber, boolean isMatchBonus) { + return Arrays.stream(Ranking.values()) + .filter(ranking -> ranking.getMatchNumber() == matchNumber) + .filter(ranking -> ranking.getBonusMatch() == isMatchBonus) + .findAny() + .orElse(Ranking.NONE); + } + + private Integer calculateMatchNumber(Lotto lotto) { + Lotto winningLottoNumbers = winningLotto.getLotto(); + return (int) winningLottoNumbers.getNumbers() + .stream() + .filter(lotto::hasNumber) + .count(); + } + + private boolean hasBonusNumber(int matchNumber, Lotto lotto) { + if (matchNumber != THIRD_RANKING_NUMBER) { + return false; + } + BonusNumber bonusNumber = winningLotto.getBonusNumber(); + return lotto.hasNumber(bonusNumber.getBonus()); + } + + public LottoResult getRankingsResult() { + return lottoResult; + } +} diff --git a/src/main/java/lotto/model/PlayerLottoes.java b/src/main/java/lotto/model/PlayerLottoes.java new file mode 100644 index 0000000000..e137325d08 --- /dev/null +++ b/src/main/java/lotto/model/PlayerLottoes.java @@ -0,0 +1,29 @@ +package lotto.model; + +import java.util.ArrayList; +import java.util.List; + +public class PlayerLottoes { + + private final List lottoes; + + public PlayerLottoes(int lotteryTicketsNumber) { + this.lottoes = generateLottoes(lotteryTicketsNumber); + } + + private List generateLottoes(int lotteryTicketsNumber) { + List lottoes = new ArrayList<>(); + for (int i = 0; i < lotteryTicketsNumber; i++) { + RandomLottoGenerator lottoGenerator = new RandomLottoGenerator(); + lottoes.add(lottoGenerator.getRandomNumber()); + } + return lottoes; + } + + public List getLottoes() { + return lottoes; + } + public int size() { + return lottoes.size(); + } +} diff --git a/src/main/java/lotto/model/ProfitCalculator.java b/src/main/java/lotto/model/ProfitCalculator.java new file mode 100644 index 0000000000..8549ec4d99 --- /dev/null +++ b/src/main/java/lotto/model/ProfitCalculator.java @@ -0,0 +1,31 @@ +package lotto.model; + +public class ProfitCalculator { + + private final LottoResult lottoResult; + private static final int PERCENTAGE = 100; + + public ProfitCalculator(LottoResult lottoResult) { + this.lottoResult = lottoResult; + } + + public double getProfit() { + double totalPrize = calculateTotalPrize(); + int ticketCount =calculateTicketCount(); + return (totalPrize / (Constant.LOTTO_PRICE.getConstant() * ticketCount)) * PERCENTAGE; + } + + private double calculateTotalPrize() { + return lottoResult.keySet() + .stream() + .mapToInt((ranking -> ranking.getPrize() * lottoResult.get(ranking))) + .sum(); + } + + private int calculateTicketCount() { + return lottoResult.keySet() + .stream() + .mapToInt(lottoResult::get) + .sum(); + } +} diff --git a/src/main/java/lotto/model/RandomLottoGenerator.java b/src/main/java/lotto/model/RandomLottoGenerator.java new file mode 100644 index 0000000000..25177a7c8f --- /dev/null +++ b/src/main/java/lotto/model/RandomLottoGenerator.java @@ -0,0 +1,23 @@ +package lotto.model; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomLottoGenerator { + + private final Lotto numbers; + + public RandomLottoGenerator() { + this.numbers = generateRandomNumber(); + } + + private Lotto generateRandomNumber() { + int maxNumber = Constant.MAX_NUMBER.getConstant(); + int minNumber = Constant.MIN_NUMBER.getConstant(); + int lottoSize = Constant.LOTTO_SIZE.getConstant(); + return new Lotto(Randoms.pickUniqueNumbersInRange(minNumber, maxNumber, lottoSize)); + } + + public Lotto getRandomNumber() { + return numbers; + } +} diff --git a/src/main/java/lotto/model/Ranking.java b/src/main/java/lotto/model/Ranking.java new file mode 100644 index 0000000000..bf6b14d516 --- /dev/null +++ b/src/main/java/lotto/model/Ranking.java @@ -0,0 +1,32 @@ +package lotto.model; + +public enum Ranking { + NONE(0, 0, false), + FIFTH(5_000, 3, false), + FOURTH(50_000, 4, false), + THIRD(1_500_000, 5, false), + SECOND(30_000_000, 5, true), + FIRST(2_000_000_000, 6, false); + + private final int prize; + private final int matchNumber; + private final boolean bonusMatch; + + Ranking(int prize, int matchNumber, boolean bonusMatch) { + this.prize = prize; + this.matchNumber = matchNumber; + this.bonusMatch = bonusMatch; + } + + public int getPrize() { + return prize; + } + + public int getMatchNumber() { + return matchNumber; + } + + public boolean getBonusMatch() { + return bonusMatch; + } +} diff --git a/src/main/java/lotto/model/ResultMessage.java b/src/main/java/lotto/model/ResultMessage.java new file mode 100644 index 0000000000..b59a9b6e66 --- /dev/null +++ b/src/main/java/lotto/model/ResultMessage.java @@ -0,0 +1,20 @@ +package lotto.model; + +public enum ResultMessage { + + MATCH_NUMBER_MESSAGE("%d개 일치"), + BONUS_MESSAGE(", 보너스 볼 일치"), + AMOUNT_MESSAGE(" (%,d원) - "), + WINNING_NUMBER_MESSAGE("%d개"), + NEW_LINE("\n"); + private final String message; + + ResultMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return message; + } +} diff --git a/src/main/java/lotto/model/WinningLotto.java b/src/main/java/lotto/model/WinningLotto.java new file mode 100644 index 0000000000..39643eddcf --- /dev/null +++ b/src/main/java/lotto/model/WinningLotto.java @@ -0,0 +1,30 @@ +package lotto.model; + +import lotto.view.ExceptionMessage; + +public class WinningLotto { + + private final Lotto lotto; + private final BonusNumber bonusNumber; + + public WinningLotto(Lotto lotto, BonusNumber bonusNumber) { + validateLottoAndBonusNumber(lotto, bonusNumber); + this.lotto = lotto; + this.bonusNumber = bonusNumber; + } + + private void validateLottoAndBonusNumber(Lotto lotto, BonusNumber bonusNumber) { + if (lotto.hasNumber(bonusNumber.getBonus())) { + ExceptionMessage message = ExceptionMessage.DUPLICATE_WINNING_NUMBER_AND_BONUS; + throw new IllegalArgumentException(message.toString()); + } + } + + public Lotto getLotto() { + return lotto; + } + + public BonusNumber getBonusNumber() { + return bonusNumber; + } +} diff --git a/src/main/java/lotto/view/ExceptionMessage.java b/src/main/java/lotto/view/ExceptionMessage.java new file mode 100644 index 0000000000..d72d7967e7 --- /dev/null +++ b/src/main/java/lotto/view/ExceptionMessage.java @@ -0,0 +1,23 @@ +package lotto.view; + +public enum ExceptionMessage { + + NOT_INTEGER("[ERROR] 번호는 숫자만 입력할 수 있습니다."), + INCORRECT_RANGE("[ERROR] 올바르지 않은 범위의 숫자입니다."), + INCORRECT_AMOUNT("[ERROR] 금액은 천 원단위로 입력할 수 있습니다."), + INCORRECT_LOTTO_SIZE("[ERROR] 6개의 숫자만 입력할 수 있습니다."), + DUPLICATE_NUMBER("[ERROR] 중복된 숫자는 입력할 수 없습니다."), + DUPLICATE_WINNING_NUMBER_AND_BONUS("[ERROR] 당첨 숫자와 보너스 숫자가 중복됩니다."), + NOT_CONTAIN_COMMA("[ERROR] 공백없이 ','로 숫자를 구분하여 주십시오."); + + private final String message; + + ExceptionMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return this.message; + } +} diff --git a/src/main/java/lotto/view/InputFactory.java b/src/main/java/lotto/view/InputFactory.java new file mode 100644 index 0000000000..508151fe61 --- /dev/null +++ b/src/main/java/lotto/view/InputFactory.java @@ -0,0 +1,44 @@ +package lotto.view; + +import camp.nextstep.edu.missionutils.Console; +import lotto.model.Lotto; +import lotto.model.BonusNumber; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class InputFactory { + + private final InputValidator inputValidator; + private final OutputView outputView; + + public InputFactory() { + this.outputView = new OutputView(); + this.inputValidator = new InputValidator(); + } + + public int readPurchaseAmount() { + outputView.printPurchaseMessage(); + String userInput = Console.readLine(); + inputValidator.validatePurchaseAmount(userInput); + return Integer.parseInt(userInput); + } + + public Lotto readWinningNumber() { + outputView.printWinningNumberMessage(); + String userInput = Console.readLine(); + inputValidator.validateWinningNumbers(userInput); + List winningNumber = Stream.of(userInput.split(",")) + .map(Integer::parseInt) + .collect(Collectors.toList()); + return new Lotto(winningNumber); + } + + public BonusNumber readBonusNumber() { + outputView.printBonusNumberMessage(); + String userInput = Console.readLine(); + inputValidator.validateBonusNumber(userInput); + return new BonusNumber(Integer.parseInt(userInput)); + } +} diff --git a/src/main/java/lotto/view/InputValidator.java b/src/main/java/lotto/view/InputValidator.java new file mode 100644 index 0000000000..5ca17d4829 --- /dev/null +++ b/src/main/java/lotto/view/InputValidator.java @@ -0,0 +1,35 @@ +package lotto.view; + +import java.util.regex.Pattern; + +public class InputValidator { + + String DELIMITER_REGEXP = "(\\d|,)+(\\d)"; + String NUMBER_REGEXP = "^\\d*$"; + + public void validateBonusNumber(String bonusNumber) { + validateInteger(bonusNumber); + } + + public void validatePurchaseAmount(String amount) { + validateInteger(amount); + } + + public void validateWinningNumbers(String winningNumbers) { + validateContainComma(winningNumbers); + } + + private void validateInteger(String userInput) { + if (!Pattern.matches(NUMBER_REGEXP, userInput)) { + ExceptionMessage exceptionMessage = ExceptionMessage.NOT_INTEGER; + throw new IllegalArgumentException(exceptionMessage.toString()); + } + } + + private void validateContainComma(String userInput) { + if (!Pattern.matches(DELIMITER_REGEXP, userInput)) { + ExceptionMessage exceptionMessage = ExceptionMessage.NOT_CONTAIN_COMMA; + throw new IllegalArgumentException(exceptionMessage.toString()); + } + } +} diff --git a/src/main/java/lotto/view/OutputMessage.java b/src/main/java/lotto/view/OutputMessage.java new file mode 100644 index 0000000000..578bec9d68 --- /dev/null +++ b/src/main/java/lotto/view/OutputMessage.java @@ -0,0 +1,23 @@ +package lotto.view; + +public enum OutputMessage { + + GUIDE_PURCHASE("구입 금액을 입력해주세요."), + WINNING_NUMBER("당첨 번호를 입력해 주세요."), + BONUS_NUMBER("보너스 번호를 입력해 주세요."), + RESULT_MESSAGE("당첨 통계"), + LINE("---"), + BUY_NUMBER("%d개를 구매했습니다.\n"), + PROFIT_MESSAGE("총 수익률은 %.1f%%입니다.\n"); + + private final String message; + + OutputMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return this.message; + } +} diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java new file mode 100644 index 0000000000..5ea4fee3d7 --- /dev/null +++ b/src/main/java/lotto/view/OutputView.java @@ -0,0 +1,47 @@ +package lotto.view; + +import lotto.model.Lotto; +import lotto.model.LottoResult; +import lotto.model.PlayerLottoes; + +public class OutputView { + + public void printPurchaseMessage() { + System.out.println(OutputMessage.GUIDE_PURCHASE); + } + + public void printWinningNumberMessage() { + System.out.println(OutputMessage.WINNING_NUMBER); + } + + public void printBonusNumberMessage() { + System.out.println(OutputMessage.BONUS_NUMBER); + } + + public void printLottoResult(LottoResult rankingResult) { + System.out.println(OutputMessage.RESULT_MESSAGE); + System.out.println(OutputMessage.LINE); + System.out.println(rankingResult.toString()); + } + + public void printPurchaseCount(int lottoTicketsNumber) { + System.out.printf(OutputMessage.BUY_NUMBER.toString(), lottoTicketsNumber); + } + + public void printPlayerLottoes(PlayerLottoes playerLottoes) { + playerLottoes.getLottoes() + .forEach(this::printLottoNumber); + } + + private void printLottoNumber(Lotto lotto) { + System.out.println(lotto.getNumbers()); + } + + public void printProfit(Double profit) { + System.out.printf((OutputMessage.PROFIT_MESSAGE.toString()), profit); + } + + public void printErrorMessage(String errorMessage) { + System.out.println(errorMessage); + } +} diff --git a/src/test/java/lotto/InputTest.java b/src/test/java/lotto/InputTest.java new file mode 100644 index 0000000000..77ae8b3f0c --- /dev/null +++ b/src/test/java/lotto/InputTest.java @@ -0,0 +1,37 @@ +package lotto; + +import lotto.view.InputValidator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class InputTest { + + private static final String ERROR_MESSAGE = "[ERROR]"; + private static final InputValidator inputValidator = new InputValidator(); + + @DisplayName("구매 금액 입력에 숫자가 아닌 입력을 하면 오류가 발생한다.") + @Test + void purchaseAmountError() { + assertThatThrownBy(() -> inputValidator.validatePurchaseAmount("1000j")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } + + @DisplayName("보너스 숫자 입력에 숫자가 아닌 입력을 하면 오류가 발생한다.") + @Test + void bonusNumberError() { + assertThatThrownBy(() -> inputValidator.validateBonusNumber("A")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } + + @DisplayName("콤마 이외의 구분자로 숫자를 구분할 경우 예외가 발생한다.") + @Test + void winningNumberSplitError() { + assertThatThrownBy(() -> inputValidator.validateWinningNumbers("1 2 3 4 5 6")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 0f3af0f6c4..5d48e1acd2 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -1,5 +1,6 @@ package lotto; +import lotto.model.Lotto; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -18,10 +19,14 @@ void createLottoByOverSize() { @DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.") @Test void createLottoByDuplicatedNumber() { - // TODO: 이 테스트가 통과할 수 있게 구현 코드 작성 assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) .isInstanceOf(IllegalArgumentException.class); } - // 아래에 추가 테스트 작성 가능 + @DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.") + @Test + void createLottoByIncorrectRange() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 47))) + .isInstanceOf(IllegalArgumentException.class); + } } diff --git a/src/test/java/lotto/model/BonusNumberTest.java b/src/test/java/lotto/model/BonusNumberTest.java new file mode 100644 index 0000000000..6fe6d4564b --- /dev/null +++ b/src/test/java/lotto/model/BonusNumberTest.java @@ -0,0 +1,19 @@ +package lotto.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class BonusNumberTest { + + private static final String ERROR_MESSAGE = "[ERROR]"; + + @DisplayName("범위를 벗어나는 보너스 숫자가 입력될 경우 오류가 발생한다.") + @Test + void bonusNumberRangeError() { + assertThatThrownBy(() -> new BonusNumber(47)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } +} diff --git a/src/test/java/lotto/model/LottoCounterTest.java b/src/test/java/lotto/model/LottoCounterTest.java new file mode 100644 index 0000000000..d47bcc64cd --- /dev/null +++ b/src/test/java/lotto/model/LottoCounterTest.java @@ -0,0 +1,28 @@ +package lotto.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class LottoCounterTest { + + private static final String ERROR_MESSAGE = "[ERROR]"; + + @DisplayName("금액을 입력하면 금액에 맞는 로또 개수를 반환한다.") + @Test + void createLottoTickets() { + LottoCounter lottoCounter = new LottoCounter(8000); + assertThat(lottoCounter.getLotteryTicket()).isEqualTo(8); + } + + @DisplayName("구매 금액이 천원 단위가 아닌 경우 오류가 발생한다.") + @Test + void purchaseAmountError() { + assertThatThrownBy(() -> new LottoCounter(1550)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } +} diff --git a/src/test/java/lotto/model/LottoResultTest.java b/src/test/java/lotto/model/LottoResultTest.java new file mode 100644 index 0000000000..e52ffa8c4a --- /dev/null +++ b/src/test/java/lotto/model/LottoResultTest.java @@ -0,0 +1,86 @@ +package lotto.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.mock; + +public class LottoResultTest { + + PlayerLottoes playerLottoes = mock(PlayerLottoes.class); + + @DisplayName("겹치는 숫자가 0개 ~ 2개일 경우 당첨이 아니다.") + @Test + void checkRankingNone() { + Lotto lotto = new Lotto(List.of(1,2,3,4,5,6)); + given(playerLottoes.getLottoes()).willReturn(List.of(lotto)); + WinningLotto winningLotto = new WinningLotto(new Lotto(List.of(7,8,9,10,11,12)), new BonusNumber(45)); + + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(winningLotto, playerLottoes); + assertThat(lottoResultCalculator.getRankingsResult().get(Ranking.NONE)).isEqualTo(1); + } + + @DisplayName("겹치는 숫자가 3개일 경우 5등 당첨") + @Test + void checkRankingFifth() { + Lotto lotto = new Lotto(List.of(1,2,3,4,5,6)); + given(playerLottoes.getLottoes()).willReturn(List.of(lotto)); + WinningLotto winningLotto = new WinningLotto(new Lotto(List.of(1,2,3,7,8,9)), new BonusNumber(4)); + + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(winningLotto, playerLottoes); + + assertThat(lottoResultCalculator.getRankingsResult().get(Ranking.FIFTH)).isEqualTo(1); + } + + @DisplayName("겹치는 숫자가 4개일 경우 4등 당첨") + @Test + void checkRankingFourth() { + Lotto lotto = new Lotto(List.of(1,2,3,4,5,6)); + given(playerLottoes.getLottoes()).willReturn(List.of(lotto)); + WinningLotto winningLotto = new WinningLotto(new Lotto(List.of(1,2,3,4,8,9)), new BonusNumber(5)); + + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(winningLotto, playerLottoes); + + assertThat(lottoResultCalculator.getRankingsResult().get(Ranking.FOURTH)).isEqualTo(1); + } + + @DisplayName("겹치는 숫자가 5개이고 보너스 번호가 겹치지 않을 경우 3등 당첨") + @Test + void checkRankingThird() { + Lotto lotto = new Lotto(List.of(1,2,3,4,5,6)); + given(playerLottoes.getLottoes()).willReturn(List.of(lotto)); + WinningLotto winningLotto = new WinningLotto(new Lotto(List.of(1,2,3,4,5,9)), new BonusNumber(45)); + + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(winningLotto, playerLottoes); + + assertThat(lottoResultCalculator.getRankingsResult().get(Ranking.THIRD)).isEqualTo(1); + } + + @DisplayName("겹치는 숫자가 5개이고 보너스 번호가 겹칠 경우 3등 당첨") + @Test + void checkRankingSecond() { + Lotto lotto = new Lotto(List.of(1,2,3,4,5,6)); + given(playerLottoes.getLottoes()).willReturn(List.of(lotto)); + WinningLotto winningLotto = new WinningLotto(new Lotto(List.of(1,2,3,4,5,8)), new BonusNumber(6)); + + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(winningLotto, playerLottoes); + + assertThat(lottoResultCalculator.getRankingsResult().get(Ranking.SECOND)).isEqualTo(1); + } + + @DisplayName("겹치는 숫자가 6개일 경우 1등 당첨") + @Test + void checkRankingFirst() { + Lotto lotto = new Lotto(List.of(1,2,3,4,5,6)); + given(playerLottoes.getLottoes()).willReturn(List.of(lotto)); + WinningLotto winningLotto = new WinningLotto(new Lotto(List.of(1,2,3,4,5,6)), new BonusNumber(10)); + + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(winningLotto, playerLottoes); + + assertThat(lottoResultCalculator.getRankingsResult().get(Ranking.FIRST)).isEqualTo(1); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/model/PlayerLottoesTest.java b/src/test/java/lotto/model/PlayerLottoesTest.java new file mode 100644 index 0000000000..108cca312f --- /dev/null +++ b/src/test/java/lotto/model/PlayerLottoesTest.java @@ -0,0 +1,17 @@ +package lotto.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlayerLottoesTest { + + @DisplayName("티켓 개수를 입력하면 갯수에 맞는 로또 번호 리스트가 생성된다.") + @Test + void createPlayerLottoes() { + int ticketNumber = 8; + PlayerLottoes playerLottoes = new PlayerLottoes(ticketNumber); + assertThat(playerLottoes.size()).isEqualTo(ticketNumber); + } +} diff --git a/src/test/java/lotto/model/WinnigLottoTest.java b/src/test/java/lotto/model/WinnigLottoTest.java new file mode 100644 index 0000000000..33e87203c4 --- /dev/null +++ b/src/test/java/lotto/model/WinnigLottoTest.java @@ -0,0 +1,30 @@ +package lotto.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class WinnigLottoTest { + + @DisplayName("당첨 번호와 보너스 번호를 입력할 경우 당첨 로또가 생성된다.") + @Test + void createWinningLotto() { + Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + BonusNumber bonusNumber = new BonusNumber(10); + assertThat(new WinningLotto(lotto, bonusNumber)) + .isInstanceOf(WinningLotto.class); + } + + @DisplayName("당첨 번호와 보너스 번호가 중복될 경 예외가 발생한다.") + @Test + void createWinningLottoByDuplicateNumbers() { + Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + BonusNumber bonusNumber = new BonusNumber(1); + assertThatThrownBy(() -> new WinningLotto(lotto, bonusNumber)) + .isInstanceOf(IllegalArgumentException.class); + } +}