diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java new file mode 100644 index 00000000..bd006947 --- /dev/null +++ b/src/main/java/lotto/Application.java @@ -0,0 +1,10 @@ +package lotto; + +import lotto.controller.Controller; + +public class Application { + public static void main(String[] args) { + Controller controller = new Controller(); + controller.run(); + } +} diff --git a/src/main/java/lotto/Constant.java b/src/main/java/lotto/Constant.java new file mode 100644 index 00000000..1447d083 --- /dev/null +++ b/src/main/java/lotto/Constant.java @@ -0,0 +1,27 @@ +package lotto; + +public final class Constant { + + private Constant() { + } + + public static final int LOTTO_NUM_COUNT = 6; + + public static final int LOTTO_PRICE = 1000; + + public static final int NO_REWARD = 0; + + public static final int FIFTH_REWARD = 5000; + + public static final int FOURTH_REWARD = 50000; + + public static final int THIRD_REWARD = 1500000; + + public static final int SECOND_REWARD = 30000000; + + public static final int FIRST_REWARD = 2000000000; + + public static final int MIN_NUM = 1; + + public static final int MAX_NUM = 45; +} diff --git a/src/main/java/lotto/Rank.java b/src/main/java/lotto/Rank.java new file mode 100644 index 00000000..dc9ede16 --- /dev/null +++ b/src/main/java/lotto/Rank.java @@ -0,0 +1,42 @@ +package lotto; + +public enum Rank { + _1ST_PLACE(6, false, Constant.FIRST_REWARD), + _2ND_PLACE(5, true, Constant.SECOND_REWARD), + _3RD_PLACE(5, false, Constant.THIRD_REWARD), + _4TH_PLACE(4, false, Constant.FOURTH_REWARD), + _5TH_PLACE(3, false, Constant.FIFTH_REWARD), + _NO_PLACE(0, false, Constant.NO_REWARD); + + private final int match; + private final boolean bonus; + private final int reward; + + Rank(int match, boolean bonus, int reward) { + this.match = match; + this.bonus = bonus; + this.reward = reward; + } + + public static Rank of(int match, boolean bonus) { + for (Rank rank : Rank.values()) { + if (rank.match == match && rank.bonus == bonus) { + return rank; + } + } + + return _NO_PLACE; + } + + public int getMatch() { + return match; + } + + public boolean getBonus() { + return bonus; + } + + public int getReward() { + return reward; + } +} diff --git a/src/main/java/lotto/controller/Controller.java b/src/main/java/lotto/controller/Controller.java new file mode 100644 index 00000000..1156f290 --- /dev/null +++ b/src/main/java/lotto/controller/Controller.java @@ -0,0 +1,33 @@ +package lotto.controller; + +import lotto.domain.CheckPlace; +import lotto.domain.CustomLotto; +import lotto.domain.Reward; +import lotto.domain.WinNums; +import lotto.generator.NumberGenerator; +import lotto.generator.RandomNumberGenerator; +import lotto.domain.LottoGame; +import lotto.view.InputView; +import lotto.view.OutputView; + +public class Controller { + + private final NumberGenerator numberGenerator = new RandomNumberGenerator(); + + public void run() { + int inputMoney = InputView.getMoney(); + + int customCount = InputView.getCustomLottoCount(); + CustomLotto customLotto = new CustomLotto(InputView.getCustomLotto(customCount)); + + LottoGame lottoGame = new LottoGame(inputMoney, numberGenerator, customLotto); + OutputView.printLottoes(customCount, lottoGame.getLottoList()); + + WinNums winNums = new WinNums(InputView.getWinLotto(), InputView.getBonusBall()); + CheckPlace checkPlace = new CheckPlace(lottoGame, winNums); + OutputView.printStatistics(checkPlace.getResultMap()); + + Reward reward = new Reward(checkPlace.getResult(), inputMoney); + OutputView.printReward(reward.getRate()); + } +} diff --git a/src/main/java/lotto/domain/CheckPlace.java b/src/main/java/lotto/domain/CheckPlace.java new file mode 100644 index 00000000..a1830311 --- /dev/null +++ b/src/main/java/lotto/domain/CheckPlace.java @@ -0,0 +1,51 @@ +package lotto.domain; + +import java.util.List; +import java.util.Map; + +import lotto.Rank; + +public class CheckPlace { + private final WinNums winNums; + private final Result result; + private final List lottoList; + private final List winList; + + public CheckPlace(LottoGame lottoGame, WinNums winNums) { + this.winNums = winNums; + this.lottoList = lottoGame.getLottoList(); + this.winList = winNums.getWinNums(); + result = new Result(); + } + + public Map getResultMap() { + checkNum(); + return result.getResult(); + } + + private void checkNum() { + int match; + boolean isBonus; + + for (Lotto lotto : lottoList) { + match = 0; + isBonus = false; + for (int num : winList) { + if (lotto.getLottoNums().contains(num)) { + match++; + } + } + if (match == 5 && lotto.getLottoNums().contains(winNums.getBonusNum())) { + isBonus = true; + } + + if (match >= 3) { + result.addResult(Rank.of(match, isBonus)); + } + } + } + + public Result getResult() { + return result; + } +} diff --git a/src/main/java/lotto/domain/CustomLotto.java b/src/main/java/lotto/domain/CustomLotto.java new file mode 100644 index 00000000..0f8c6f4e --- /dev/null +++ b/src/main/java/lotto/domain/CustomLotto.java @@ -0,0 +1,28 @@ +package lotto.domain; + +import java.util.ArrayList; +import java.util.List; + +public class CustomLotto { + private final List lottoList = new ArrayList<>(); + private final List> customLottoes; + + public CustomLotto(List> customLottoes) { + this.customLottoes = customLottoes; + makeCustomLottoes(); + } + + private void makeCustomLottoes() { + for (List lotto : customLottoes) { + lottoList.add(new Lotto(lotto)); + } + } + + public List getCustomLottoList() { + return lottoList; + } + + public int getCustomLottoCount() { + return lottoList.size(); + } +} diff --git a/src/main/java/lotto/domain/Lotto.java b/src/main/java/lotto/domain/Lotto.java new file mode 100644 index 00000000..0f527304 --- /dev/null +++ b/src/main/java/lotto/domain/Lotto.java @@ -0,0 +1,41 @@ +package lotto.domain; + +import java.util.List; + +import lotto.Constant; +import lotto.generator.NumberGenerator; +import lotto.message.ErrorMessage; + +public class Lotto { + + private final List lottoNums; + + public Lotto(NumberGenerator numberGenerator) { + lottoNums = numberGenerator.generateLottoNum(); // 생성자에서 어디까지의 역할을 해야하는가? + validateLottoSize(); + } + + public Lotto(List lottoNums) { + this.lottoNums = lottoNums; + validateLottoSize(); + validateLottoNum(); + } + + public List getLottoNums() { + return lottoNums; + } + + private void validateLottoSize() { + if (lottoNums.size() != Constant.LOTTO_NUM_COUNT) { + throw new IllegalArgumentException(ErrorMessage.INVALID_LOTTO_NUM.getMessage()); + } + } + + private void validateLottoNum(){ + for(int num : lottoNums){ + if(!(num >= Constant.MIN_NUM && num <= Constant.MAX_NUM)){ + throw new IllegalArgumentException(ErrorMessage.INVALID_NUM.getMessage()); + } + } + } +} diff --git a/src/main/java/lotto/domain/LottoGame.java b/src/main/java/lotto/domain/LottoGame.java new file mode 100644 index 00000000..630a7460 --- /dev/null +++ b/src/main/java/lotto/domain/LottoGame.java @@ -0,0 +1,54 @@ +package lotto.domain; + +import java.util.ArrayList; +import java.util.List; + +import lotto.Constant; +import lotto.generator.NumberGenerator; +import lotto.message.ErrorMessage; + +public class LottoGame { + private final int price; + private final List lottoList = new ArrayList<>(); + private final int trial; + private final NumberGenerator numberGenerator; + private final CustomLotto customLotto; + + public LottoGame(int price, NumberGenerator numberGenerator, CustomLotto customLotto) { + validatePrice(price); + this.price = price; + this.trial = getTrial(); + this.numberGenerator = numberGenerator; + this.customLotto = customLotto; + validateTrial(); + makeLottoList(); + } + + public List getLottoList() { + return lottoList; + } + + private void makeLottoList() { + lottoList.addAll(customLotto.getCustomLottoList()); + + for (int j = 0; j < trial - customLotto.getCustomLottoCount(); j++) { + lottoList.add(new Lotto(numberGenerator)); + } + } + + private void validatePrice(int price) { + if ((price % 1000) != 0) { + throw new IllegalArgumentException(ErrorMessage.INVALID_MONEY.getMessage()); + } + } + + private int getTrial() { + return price / Constant.LOTTO_PRICE; + } + + private void validateTrial(){ + if(trial < customLotto.getCustomLottoCount()){ + throw new IllegalArgumentException(ErrorMessage.TOO_MANY_CUSTOM.getMessage()); + } + } +} diff --git a/src/main/java/lotto/domain/Result.java b/src/main/java/lotto/domain/Result.java new file mode 100644 index 00000000..fad28aad --- /dev/null +++ b/src/main/java/lotto/domain/Result.java @@ -0,0 +1,33 @@ +package lotto.domain; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import lotto.Rank; + +public class Result { + Map result = new LinkedHashMap<>(); + + public Result() { + makeEmptyResult(); + } + + private void makeEmptyResult() { + List ranks = List.of(Rank.values()); + for (int i = ranks.size() - 1; i >= 0; i--) { + Rank rank = ranks.get(i); + if (rank != Rank._NO_PLACE) { + result.put(rank, 0); + } + } + } + + public void addResult(Rank rank) { + result.put(rank, result.get(rank) + 1); + } + + public Map getResult() { + return result; + } +} diff --git a/src/main/java/lotto/domain/Reward.java b/src/main/java/lotto/domain/Reward.java new file mode 100644 index 00000000..68e0bfed --- /dev/null +++ b/src/main/java/lotto/domain/Reward.java @@ -0,0 +1,35 @@ +package lotto.domain; + +import java.util.Map; + +import lotto.Rank; + +public class Reward { + private final int inputMoney; + private int totalReward; + private final Map resultMap; + + public Reward(Result result, int inputMoney) { + this.inputMoney = inputMoney; + this.resultMap = result.getResult(); + } + + private void caculateTotalReward() { + resultMap.forEach((rank, value) -> + totalReward += rank.getReward() * value); + } + + private String calculate() { + caculateTotalReward(); + + if (totalReward == 0) { + return "0"; + } + double rewardRate = (double)totalReward / inputMoney; + return String.format("%.2f", rewardRate); + } + + public String getRate() { + return calculate(); + } +} diff --git a/src/main/java/lotto/domain/WinNums.java b/src/main/java/lotto/domain/WinNums.java new file mode 100644 index 00000000..2e439e88 --- /dev/null +++ b/src/main/java/lotto/domain/WinNums.java @@ -0,0 +1,40 @@ +package lotto.domain; + +import java.util.List; + +import lotto.Constant; +import lotto.message.ErrorMessage; + +public class WinNums { + private final List winNums; + private final int bonusNum; + + public WinNums(List winNums, int bonusNum) { + this.winNums = winNums; + this.bonusNum = bonusNum; + validateNum(); + validateBonusNum(); + } + + public List getWinNums() { + return winNums; + } + + public int getBonusNum() { + return bonusNum; + } + + private void validateNum() { + for (int num : winNums) { + if (!(num >= Constant.MIN_NUM && num <= Constant.MAX_NUM)) { + throw new IllegalArgumentException(ErrorMessage.INVALID_NUM.getMessage()); + } + } + } + + private void validateBonusNum() { + if (!(bonusNum >= Constant.MIN_NUM && bonusNum <= Constant.MAX_NUM)) { + throw new IllegalArgumentException(ErrorMessage.INVALID_NUM.getMessage()); + } + } +} diff --git a/src/main/java/lotto/generator/NumberGenerator.java b/src/main/java/lotto/generator/NumberGenerator.java new file mode 100644 index 00000000..096d5267 --- /dev/null +++ b/src/main/java/lotto/generator/NumberGenerator.java @@ -0,0 +1,8 @@ +package lotto.generator; + +import java.util.List; + +public interface NumberGenerator { + + List generateLottoNum(); +} diff --git a/src/main/java/lotto/generator/RandomNumberGenerator.java b/src/main/java/lotto/generator/RandomNumberGenerator.java new file mode 100644 index 00000000..d3274d26 --- /dev/null +++ b/src/main/java/lotto/generator/RandomNumberGenerator.java @@ -0,0 +1,29 @@ +package lotto.generator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import lotto.Constant; + +public class RandomNumberGenerator implements NumberGenerator { + + private final Random random = new Random(); + + @Override + public List generateLottoNum() { + List lotto = new ArrayList<>(); + int num; + + while (lotto.size() != Constant.LOTTO_NUM_COUNT) { + num = random.nextInt(45) + 1; + if (!lotto.contains(num)) { + lotto.add(num); + } + } + Collections.sort(lotto); + + return lotto; + } +} diff --git a/src/main/java/lotto/message/ConsoleMessage.java b/src/main/java/lotto/message/ConsoleMessage.java new file mode 100644 index 00000000..974c8174 --- /dev/null +++ b/src/main/java/lotto/message/ConsoleMessage.java @@ -0,0 +1,25 @@ +package lotto.message; + +public enum ConsoleMessage { + INPUT_MONEY("구입금액을 입력해 주세요."), + COUNT_LOTTO("수동으로 %d장, 자동으로 %d개를 구매했습니다."), + LAST_WIN_NUM("지난 주 당첨 번호를 입력해 주세요."), + WINNING_STATISTICS("당첨 통계"), + CHECK_SAME("%d개 일치 (%d원) - %d개"), + CHECK_SAME_2ND("%d개 일치, 보너스 볼 일치 (%d원) -%d개"), + REWARD_RATE("총 수익률은 %s입니다."), + INPUT_BONUS("보너스 볼을 입력해 주세요."), + CUSTOM_LOTTO_COUNT("수동으로 구매할 로또 수를 입력해 주세요."), + CUSTOM_LOTTO_INPUT("수동으로 구매할 번호를 입력해 주세요.") + ; + + private final String message; + + ConsoleMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/lotto/message/ErrorMessage.java b/src/main/java/lotto/message/ErrorMessage.java new file mode 100644 index 00000000..52a6a1a6 --- /dev/null +++ b/src/main/java/lotto/message/ErrorMessage.java @@ -0,0 +1,19 @@ +package lotto.message; + +public enum ErrorMessage { + INVALID_MONEY("천원 단위로 입력해주세요!"), + INVALID_LOTTO_NUM("로또의 개수가 이상합니다!"), + INVALID_NUM("유효한 숫자가 아닙니다!"), + TOO_MANY_CUSTOM("수동 로또가 너무 많습니다!"), + ; + + private final String message; + + ErrorMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java new file mode 100644 index 00000000..ec279ece --- /dev/null +++ b/src/main/java/lotto/view/InputView.java @@ -0,0 +1,60 @@ +package lotto.view; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +import lotto.message.ConsoleMessage; + +public class InputView { + + private InputView() { + } + + private static final Scanner scanner = new Scanner(System.in); + + public static int getMoney() { + System.out.println(ConsoleMessage.INPUT_MONEY.getMessage()); + return Integer.parseInt(scanner.nextLine()); + } + + public static List getWinLotto() { + System.out.println("\n" + ConsoleMessage.LAST_WIN_NUM.getMessage()); + String winNum = scanner.nextLine(); + + return Arrays.stream(winNum.split(",")) + .map(String::trim) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } + + public static int getBonusBall() { + System.out.println("\n" + ConsoleMessage.INPUT_BONUS.getMessage()); + + return Integer.parseInt(scanner.nextLine()); + } + + public static int getCustomLottoCount() { + System.out.println("\n" + ConsoleMessage.CUSTOM_LOTTO_COUNT.getMessage()); + return Integer.parseInt(scanner.nextLine()); + } + + public static List> getCustomLotto(int customLottoCount) { + System.out.println("\n" + ConsoleMessage.CUSTOM_LOTTO_INPUT.getMessage()); + String winNum; + List> customLottos = new ArrayList<>(new ArrayList<>()); + + for (int i = 0; i < customLottoCount; i++) { + winNum = scanner.nextLine(); + + customLottos.add(Arrays.stream(winNum.split(",")) + .map(String::trim) + .map(Integer::parseInt) + .collect(Collectors.toList())); + } + + return customLottos; + } +} diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java new file mode 100644 index 00000000..47739108 --- /dev/null +++ b/src/main/java/lotto/view/OutputView.java @@ -0,0 +1,42 @@ +package lotto.view; + +import java.util.List; +import java.util.Map; + +import lotto.Rank; +import lotto.domain.Lotto; +import lotto.message.ConsoleMessage; + +public class OutputView { + + private OutputView() { + } + + public static void printLottoes(int customCount, List lottoList) { + System.out.printf("\n" + ConsoleMessage.COUNT_LOTTO.getMessage() + "\n", customCount, + lottoList.size() - customCount); + + for (Lotto lotto : lottoList) { + System.out.println(lotto.getLottoNums()); + } + } + + public static void printStatistics(Map resultMap) { + System.out.println("\n" + ConsoleMessage.WINNING_STATISTICS.getMessage()); + System.out.println("--------"); + + resultMap.forEach(((rank, integer) -> { + if (rank.equals(Rank._2ND_PLACE)) { + System.out.printf(ConsoleMessage.CHECK_SAME_2ND.getMessage() + "\n" + , rank.getMatch(), rank.getReward(), integer); + } else { + System.out.printf(ConsoleMessage.CHECK_SAME.getMessage() + "\n" + , rank.getMatch(), rank.getReward(), integer); + } + })); + } + + public static void printReward(String rewardRate) { + System.out.printf(ConsoleMessage.REWARD_RATE.getMessage(), rewardRate); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java new file mode 100644 index 00000000..ffd18676 --- /dev/null +++ b/src/test/java/lotto/LottoTest.java @@ -0,0 +1,34 @@ +package lotto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import lotto.domain.Lotto; +import lotto.generator.NumberGenerator; +import lotto.generator.RandomNumberGenerator; +import lotto.mock.Lotto7NumberGenerator; + +class LottoTest { + + private final NumberGenerator numberGenerator = new RandomNumberGenerator(); + private final NumberGenerator numberGenerator7 = new Lotto7NumberGenerator(); + + @Test + @DisplayName("로또번호 개수 확인") + void checkLottoCount6() { + Lotto lotto = new Lotto(numberGenerator); + assertThat(lotto.getLottoNums()).hasSize(6); + } + + @Test + @DisplayName("로또번호 개수가 6이 아니면 오류") + void checkLottoCount() { + assertThatThrownBy(() -> { + Lotto lotto = new Lotto(numberGenerator7); + } + ).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/lotto/mock/Lotto7NumberGenerator.java b/src/test/java/lotto/mock/Lotto7NumberGenerator.java new file mode 100644 index 00000000..5a9fc1b7 --- /dev/null +++ b/src/test/java/lotto/mock/Lotto7NumberGenerator.java @@ -0,0 +1,13 @@ +package lotto.mock; + +import java.util.List; + +import lotto.generator.NumberGenerator; + +public class Lotto7NumberGenerator implements NumberGenerator { + + @Override + public List generateLottoNum() { + return List.of(1, 2, 3, 4, 5, 6, 7); + } +} diff --git a/src/test/java/lotto/mock/TestNumberGenerator.java b/src/test/java/lotto/mock/TestNumberGenerator.java new file mode 100644 index 00000000..e45e2783 --- /dev/null +++ b/src/test/java/lotto/mock/TestNumberGenerator.java @@ -0,0 +1,13 @@ +package lotto.mock; + +import java.util.List; + +import lotto.generator.NumberGenerator; + +public class TestNumberGenerator implements NumberGenerator { + + @Override + public List generateLottoNum() { + return List.of(1, 2, 3, 4, 5, 6); + } +}