diff --git a/README.md b/README.md index a162aabd30..626d11bf78 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,7 @@ ### 3단계 - 로또(2등) - [X] 보너스볼 입력 - [X] rank 2등 추가 + +### 4단계 - 로또(수동) +- [X] 수동으로 구매할 로또 수 입력 +- [X] 수동으로 구매할 로또 번호 입력 diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 5f5d5aa850..97b58009e9 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -3,33 +3,39 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; public class Lotto { public static final int LOTTO_PRICE = 1000; public static final long LOTTO_NUM_COUNT = 6; - private final List lottoNumbers; + private final Set lottoNumbers; - public Lotto(List lottoNumbers) { - validateLottoNumbers(lottoNumbers); - this.lottoNumbers = lottoNumbers; + public Lotto(List numbers) { + this(new HashSet<>(numbers)); } - public List getLottoNumbers() { + public Lotto(Set numbers) { + this.lottoNumbers = numbers.stream().map(LottoNumber::of).collect(Collectors.toSet()); + validateLottoNumbers(); + } + + public Set getLottoNumbers() { return lottoNumbers; } - public LottoRank getLottoRank(List winningNumbers, Integer bonusNumber) { - return LottoRank.fromMatchCount((int) this.lottoNumbers.stream().filter(winningNumbers::contains).count(), lottoNumbers.contains(bonusNumber)); + public LottoRank getLottoRank(Lotto winningNumbers, int bonusNumber) { + int matchCount = (int) this.lottoNumbers.stream().filter(winningNumbers.getLottoNumbers()::contains).count(); + boolean isBonusNumber = lottoNumbers.contains(LottoNumber.of(bonusNumber)); + return LottoRank.fromMatchCount(matchCount, isBonusNumber); } - private void validateLottoNumbers(List numbers) { - if (numbers.size() != LOTTO_NUM_COUNT) { + private void validateLottoNumbers() { + if (lottoNumbers.size() > LOTTO_NUM_COUNT) { throw new IllegalArgumentException("로또 번호는 6개여야 합니다."); } - Set uniqueNumbers = new HashSet<>(numbers); - if (uniqueNumbers.size() != LOTTO_NUM_COUNT) { + if (lottoNumbers.size() < LOTTO_NUM_COUNT) { throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); } } diff --git a/src/main/java/lotto/LottoApplication.java b/src/main/java/lotto/LottoApplication.java index 093e4cfe29..a3ea07f54e 100644 --- a/src/main/java/lotto/LottoApplication.java +++ b/src/main/java/lotto/LottoApplication.java @@ -1,10 +1,10 @@ package lotto; import calculator.Operator; -import view.LottoNumbersParser; import view.InputView; import view.OutputView; +import java.util.ArrayList; import java.util.List; import static lotto.Lotto.LOTTO_PRICE; @@ -13,14 +13,18 @@ public class LottoApplication { public static void main(String[] args) { - Integer lottoPurchaseAmount = InputView.showLottoPurchaseAmountInput(); - Integer lottoQuantity = Operator.DIVIDE.formula.apply(lottoPurchaseAmount, LOTTO_PRICE); - OutputView.showLottoQuantity(lottoQuantity); + int lottoPurchaseAmount = InputView.showLottoPurchaseAmountInput(); + int totalLottoCount = Operator.DIVIDE.formula.apply(lottoPurchaseAmount, LOTTO_PRICE); - List lottos = LottoNumberGenerator.generateLottoNumbers(lottoQuantity); + int manualLottoCount = InputView.showManualLottoCountInput(); + List lottos = new ArrayList<>(InputView.showManualLottoNumbersInput(manualLottoCount)); + + int autoLottoCount = totalLottoCount - manualLottoCount; + lottos.addAll(LottoNumberGenerator.generateLottoNumbers(autoLottoCount)); + OutputView.showTotalLottoCount(manualLottoCount, autoLottoCount); OutputView.showGeneratedLottoNumber(lottos); - List winningLotto = LottoNumbersParser.parse(InputView.showWinningLottoNumbersInput()); + Lotto winningLotto = InputView.showWinningLottoNumbersInput(); Integer bonusNumber = InputView.showLottoBonusNumberInput(); LottosResult lottosResult = new LottosResult(lottos, winningLotto, bonusNumber); diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java new file mode 100644 index 0000000000..f86b6f41c2 --- /dev/null +++ b/src/main/java/lotto/LottoNumber.java @@ -0,0 +1,41 @@ +package lotto; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.IntStream; + +public class LottoNumber { + + static final int LOTTO_MIN_NUM = 1; + static final int LOTTO_MAX_NUM = 45; + + public static final Map lottoNumberMap = IntStream.rangeClosed(LOTTO_MIN_NUM, LOTTO_MAX_NUM) + .boxed() + .collect(HashMap::new, (map, number) -> map.put(number, new LottoNumber(number)), HashMap::putAll); + + private final int number; + + private LottoNumber(int number) { + this.number = number; + } + + private static void validateLottoNumber(Integer number) { + if (number == null) { + throw new IllegalArgumentException("로또 번호는 null일 수 없습니다."); + } + if (number < LOTTO_MIN_NUM || number > LOTTO_MAX_NUM) { + throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + } + + public static LottoNumber of(Integer number) { + validateLottoNumber(number); + return lottoNumberMap.get(number); + } + + @Override + public String toString() { + return String.valueOf(number); + } + +} diff --git a/src/main/java/lotto/LottoNumberGenerator.java b/src/main/java/lotto/LottoNumberGenerator.java index 98f149eaf7..4d78b0cddb 100644 --- a/src/main/java/lotto/LottoNumberGenerator.java +++ b/src/main/java/lotto/LottoNumberGenerator.java @@ -1,20 +1,16 @@ package lotto; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import static lotto.Lotto.LOTTO_NUM_COUNT; +import static lotto.LottoNumber.*; public class LottoNumberGenerator { - - private static final int LOTTO_MIN_NUM = 1; - private static final int LOTTO_MAX_NUM = 45; - - private static List numbersPool = IntStream.rangeClosed(LOTTO_MIN_NUM, LOTTO_MAX_NUM) - .boxed() - .collect(Collectors.toList()); + private static final List numbersPool = new ArrayList<>(lottoNumberMap.keySet()); public static List generateLottoNumbers(int lottoQuantity) { return IntStream.range(0, lottoQuantity) diff --git a/src/main/java/lotto/LottoRank.java b/src/main/java/lotto/LottoRank.java index 401b54a342..e142122d8e 100644 --- a/src/main/java/lotto/LottoRank.java +++ b/src/main/java/lotto/LottoRank.java @@ -33,6 +33,7 @@ public static LottoRank fromMatchCount(int matchCount, boolean isBonusMatch) { @Override public String toString() { - return String.format("%d개 일치 (%,d원)", matchCount, prize); + String bonusText = isBonusMatch ? ", 보너스 볼 일치" : ""; + return String.format("%d개 일치%s (%,d원)", matchCount, bonusText, prize); } } diff --git a/src/main/java/lotto/LottosResult.java b/src/main/java/lotto/LottosResult.java index 2185f7124c..0847f02f2b 100644 --- a/src/main/java/lotto/LottosResult.java +++ b/src/main/java/lotto/LottosResult.java @@ -8,15 +8,15 @@ public class LottosResult { private List lottos; - private List winningNumbers; + private Lotto winningLotto; private final EnumMap lottoRankMap = new EnumMap<>(LottoRank.class); - public LottosResult(List lottos, List winningNumbers, int bonusNumber) { + public LottosResult(List lottos, Lotto winningLotto, int bonusNumber) { this.lottos = lottos; - this.winningNumbers = winningNumbers; + this.winningLotto = winningLotto; for (Lotto lotto : lottos) { - LottoRank lottoRank = lotto.getLottoRank(winningNumbers, bonusNumber); + LottoRank lottoRank = lotto.getLottoRank(winningLotto, bonusNumber); lottoRankMap.put( lottoRank, lottoRankMap.getOrDefault(lottoRank, 0) + 1 diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index f0532180c4..572e89fcff 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,30 +1,57 @@ package view; +import lotto.Lotto; + +import java.util.ArrayList; +import java.util.List; import java.util.Scanner; public class InputView { + private static final Scanner scanner = new Scanner(System.in); public static String showExpressionInput() { System.out.println("수식 입력 > "); - Scanner scanner = new Scanner(System.in); return scanner.nextLine(); } - public static Integer showLottoPurchaseAmountInput() { + public static int showLottoPurchaseAmountInput() { System.out.println("구입금액을 입력해 주세요."); - Scanner scanner = new Scanner(System.in); - return scanner.nextInt(); + return readInt(); } - public static String showWinningLottoNumbersInput() { + public static Lotto showWinningLottoNumbersInput() { System.out.println("\n지난 주 당첨 번호를 입력해 주세요."); - Scanner scanner = new Scanner(System.in); - return scanner.nextLine(); + String input = scanner.nextLine(); + return new Lotto(LottoNumbersParser.parse(input)); } - public static Integer showLottoBonusNumberInput() { + public static int showLottoBonusNumberInput() { System.out.println("보너스 볼을 입력해 주세요."); - Scanner scanner = new Scanner(System.in); - return scanner.nextInt(); + return readInt(); + } + + public static int showManualLottoCountInput() { + System.out.println("\n수동으로 구매할 로또 수를 입력해 주세요."); + return readInt(); + } + + public static List showManualLottoNumbersInput(int manualLottoCount) { + System.out.println("\n수동으로 구매할 번호를 입력해 주세요."); + List manualLottoNumbers = new ArrayList<>(); + for (int i = 0; i < manualLottoCount; i++) { + String input = scanner.nextLine(); + manualLottoNumbers.add(new Lotto(LottoNumbersParser.parse(input))); + } + return manualLottoNumbers; + } + + private static int readInt() { + String input = scanner.nextLine(); + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println("잘못된 입력입니다. 다시 시도하세요."); + return readInt(); + } } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index c547860194..a8015e1df4 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,11 +1,13 @@ package view; import lotto.Lotto; +import lotto.LottoNumber; import lotto.LottoRank; import lotto.LottosResult; import java.util.Arrays; import java.util.List; +import java.util.Set; public class OutputView { @@ -13,11 +15,11 @@ public static void showResult(int result) { System.out.println("result = " + result); } - public static void showLottoQuantity(int lottoQuantity) { - System.out.println(lottoQuantity + "개를 구매했습니다."); + public static void showTotalLottoCount(int manualLottoCount, int autoLottoCount) { + System.out.println("\n수동으로 " + manualLottoCount + "장, 자동으로 " + autoLottoCount + "개를 구매했습니다."); } - public static void showLottoNumbers(List numbers) { + public static void showLottoNumbers(Set numbers) { System.out.println(numbers); } @@ -40,3 +42,4 @@ public static void showGeneratedLottoNumber(List lottos) { lottos.forEach(lotto -> showLottoNumbers(lotto.getLottoNumbers())); } } + diff --git a/src/test/java/lotto/LottoNumberGeneratorTest.java b/src/test/java/lotto/LottoNumberGeneratorTest.java index 07a4f625aa..245192467d 100644 --- a/src/test/java/lotto/LottoNumberGeneratorTest.java +++ b/src/test/java/lotto/LottoNumberGeneratorTest.java @@ -2,10 +2,8 @@ import org.junit.jupiter.api.Test; -import java.util.HashSet; import java.util.List; -import static lotto.Lotto.LOTTO_NUM_COUNT; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; public class LottoNumberGeneratorTest { @@ -17,13 +15,4 @@ public class LottoNumberGeneratorTest { assertThat(lottos.size()).isEqualTo(quantity); } - @Test - void 각_Lotto는_6개의_서로_다른_숫자를_가진다() { - List lottos = LottoNumberGenerator.generateLottoNumbers(1); - List numbers = lottos.get(0).getLottoNumbers(); - - assertThat(numbers.size()).isEqualTo(LOTTO_NUM_COUNT); - assertThat(new HashSet<>(numbers).size()).isEqualTo(numbers.size()); - } - } diff --git a/src/test/java/lotto/LottoNumberTest.java b/src/test/java/lotto/LottoNumberTest.java new file mode 100644 index 0000000000..c83026933e --- /dev/null +++ b/src/test/java/lotto/LottoNumberTest.java @@ -0,0 +1,26 @@ +package lotto; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class LottoNumberTest { + + @Test + void of_1에서_45사이_숫자면_같은_객체_반환한다() { + assertThat(LottoNumber.of(2)).isEqualTo(LottoNumber.of(2)); + } + + @Test + void of_1미만일_경우_예외를_던진다() { + assertThatThrownBy(() -> LottoNumber.of(0)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void of_45초과일_경우_예외를_던진다() { + assertThatThrownBy(() -> LottoNumber.of(46)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 573e710d24..c23fb864e7 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; -import java.util.List; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; @@ -12,44 +11,43 @@ public class LottoTest { @Test void getLottoRank_None() { - List lottoNumbers = Arrays.asList(5, 6, 7, 10, 11, 12); - List winningNumbers = Arrays.asList(1, 2, 3, 40, 41, 42); + Lotto lotto = new Lotto(Arrays.asList(5, 6, 7, 10, 11, 12)); + Lotto winningNumbers = new Lotto(Arrays.asList(1, 2, 3, 40, 41, 42)); + int bonusNumber = 19; - Lotto lotto = new Lotto(lottoNumbers); - LottoRank result = lotto.getLottoRank(winningNumbers, null); + LottoRank result = lotto.getLottoRank(winningNumbers, bonusNumber); assertThat(result).isEqualTo(LottoRank.NONE); } @Test void getLottoRank_Fifth() { - List lottoNumbers = Arrays.asList(1, 2, 3, 10, 11, 12); - List winningNumbers = Arrays.asList(1, 2, 3, 40, 41, 42); + Lotto lotto = new Lotto(Arrays.asList(1, 2, 3, 10, 11, 12)); + Lotto winningNumbers = new Lotto(Arrays.asList(1, 2, 3, 40, 41, 42)); + int bonusNumber = 19; - Lotto lotto = new Lotto(lottoNumbers); - LottoRank result = lotto.getLottoRank(winningNumbers, null); + LottoRank result = lotto.getLottoRank(winningNumbers, bonusNumber); assertThat(result).isEqualTo(LottoRank.FIFTH); } @Test void getLottoRank_First() { - List lottoNumbers = Arrays.asList(1, 2, 3, 10, 11, 12); - List winningNumbers = Arrays.asList(1, 2, 3, 10, 11, 12); + Lotto lotto = new Lotto(Arrays.asList(1, 2, 3, 10, 11, 12)); + Lotto winningNumbers = new Lotto(Arrays.asList(1, 2, 3, 10, 11, 12)); + int bonusNumber = 19; - Lotto lotto = new Lotto(lottoNumbers); - LottoRank result = lotto.getLottoRank(winningNumbers, null); + LottoRank result = lotto.getLottoRank(winningNumbers, bonusNumber); assertThat(result).isEqualTo(LottoRank.FIRST); } @Test void getLottoRank_Second() { - List lottoNumbers = Arrays.asList(1, 2, 3, 10, 11, 19); - List winningNumbers = Arrays.asList(1, 2, 3, 10, 11, 12); - Integer bonusNumber = 19; + Lotto lotto = new Lotto(Arrays.asList(1, 2, 3, 10, 11, 19)); + Lotto winningNumbers = new Lotto(Arrays.asList(1, 2, 3, 10, 11, 12)); + int bonusNumber = 19; - Lotto lotto = new Lotto(lottoNumbers); LottoRank result = lotto.getLottoRank(winningNumbers, bonusNumber); assertThat(result).isEqualTo(LottoRank.SECOND); @@ -68,5 +66,4 @@ void getLottoRank_Second() { new Lotto(Arrays.asList(1, 2, 3, 3, 4, 5, 5)); }).isInstanceOf(IllegalArgumentException.class); } - }