diff --git "a/docs/step4\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/docs/step4\354\232\224\352\265\254\354\202\254\355\225\255.md" new file mode 100644 index 0000000000..d57240c324 --- /dev/null +++ "b/docs/step4\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -0,0 +1,26 @@ +# 로또 구현 + +## 기능 요구사항 + +## 입력 + +- [x] 수동으로 구매할 로또 수를 입력받는다 + - [x] 수동 구매 장수는 음수일수 없다. + - [x] 0개 입력시 전부 자동으로 구매한다. + - [x] 공백 입력 시 재입력받도록 한다. +- [x] 수동번호를 수동 갯수만큼 로또를 입력받는다. + +## 수동 로또 + +- [x] 입력받은 로또 리스트를 받아서 생성한다. + - [x] 빈List이거나 null인경우 빈 값으로 판단한다. +- [x] 수동로또가 비어있으면 빈 리스트를 반환한다. + +## 로또 티켓 + +- [x] 구입금액과 수동 로또를 받아서 생성한다. +- [x] 수동로또를 추가하고 남은 금만큼 자동 로또를 생성한다. + +## 출력 + +- [x] 수동, 자동 장수를 출력한다. diff --git a/src/main/java/lotto/LottoApplication.java b/src/main/java/lotto/LottoApplication.java index e628f4d665..90a24c8dce 100644 --- a/src/main/java/lotto/LottoApplication.java +++ b/src/main/java/lotto/LottoApplication.java @@ -3,18 +3,26 @@ import lotto.domain.Lotto; import lotto.domain.LottoNumber; import lotto.domain.LottoTickets; +import lotto.domain.LottoTicketsFactory; +import lotto.domain.ManualLottos; import lotto.domain.PurchaseAmount; import lotto.domain.WinningNumbers; import lotto.domain.WinningResult; import lotto.view.InputView; import lotto.view.ResultView; +import java.util.List; + public class LottoApplication { public static void main(String[] args) { PurchaseAmount purchaseAmount = new PurchaseAmount(InputView.inputPurchaseAmount()); - LottoTickets tickets = LottoTickets.create(purchaseAmount); - ResultView.printTicketCount(tickets.size()); + int manualCount = InputView.inputManualLottoCount(); + List manualLottosInputs = InputView.inputManualLottos(manualCount); + ManualLottos manualLottos = new ManualLottos(manualLottosInputs); + + LottoTickets tickets = LottoTicketsFactory.create(purchaseAmount, manualLottos); + ResultView.printTicketCount(tickets); ResultView.printLottoTickets(tickets); Lotto winningLotto = InputView.inputWinningNumbers(); diff --git a/src/main/java/lotto/domain/Lotto.java b/src/main/java/lotto/domain/Lotto.java index bec5d7d065..fe511ced63 100644 --- a/src/main/java/lotto/domain/Lotto.java +++ b/src/main/java/lotto/domain/Lotto.java @@ -23,28 +23,19 @@ public Lotto(Set numbers) { } private static Set numbersToSet(int[] intNumbers) { - validateInputSize(intNumbers.length); Set numbers = new HashSet<>(); for (int number : intNumbers) { numbers.add(LottoNumber.of(number)); } - validateNoDuplicate(numbers.size(), intNumbers.length); return numbers; } public static Lotto from(List intNumbers) { return new Lotto(createLottoNumbers(intNumbers)); } - - private static void validateNoDuplicate(int setSize, int inputSize) { - if (setSize != inputSize) { - throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); - } - } - private static void validateInputSize(int length) { if (length != LOTTO_NUMBER_COUNT) { - throw new IllegalArgumentException("로또 번호는 총 6개여야 합니다."); + throw new IllegalArgumentException("로또 번호는 중복없이 6개여야 합니다."); } } @@ -54,8 +45,6 @@ private static Set createLottoNumbers(List intNumbers) { lottoNumbers.add(LottoNumber.of(number)); } - validateNoDuplicate(lottoNumbers.size(), intNumbers.size()); - validateInputSize(lottoNumbers.size()); return lottoNumbers; } @@ -65,7 +54,7 @@ public List getNumbers() { result.add(number.getValue()); } Collections.sort(result); - return result; + return List.copyOf(result); } public boolean contains(LottoNumber number) { diff --git a/src/main/java/lotto/domain/LottoNumber.java b/src/main/java/lotto/domain/LottoNumber.java index b560abe023..4e74fde4ea 100644 --- a/src/main/java/lotto/domain/LottoNumber.java +++ b/src/main/java/lotto/domain/LottoNumber.java @@ -17,24 +17,16 @@ public class LottoNumber { private final int value; - private LottoNumber(String value) { - this(Integer.parseInt(value)); - } - private LottoNumber(int value) { - validate(value); this.value = value; } public static LottoNumber of(int number) { - validate(number); - return CACHE.get(number); - } - - private static void validate(int value) { - if (value < MIN_NUMBER || value > MAX_NUMBER) { - throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 한다"); + LottoNumber lottoNumber = CACHE.get(number); + if (lottoNumber == null) { + throw new IllegalArgumentException(String.format("로또 번호는 %d부터 %d 사이의 숫자여야 한다", MIN_NUMBER, MAX_NUMBER)); } + return lottoNumber; } public int getValue() { diff --git a/src/main/java/lotto/domain/LottoTickets.java b/src/main/java/lotto/domain/LottoTickets.java index ba6c2dbbaa..b557f2b1f7 100644 --- a/src/main/java/lotto/domain/LottoTickets.java +++ b/src/main/java/lotto/domain/LottoTickets.java @@ -1,24 +1,24 @@ package lotto.domain; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class LottoTickets { private final List lottos; + private final ManualLottos manualLottos; - public LottoTickets(List lottos) { + public LottoTickets(List lottos, ManualLottos manualLottos) { this.lottos = lottos; + this.manualLottos = manualLottos; } - public static LottoTickets create(PurchaseAmount purchaseAmount) { - int count = purchaseAmount.getLottoCount(); - List lottos = new ArrayList<>(); - for (int i = 0; i < count; i++) { - lottos.add(Lotto.from(LottoNumberGenerator.generate())); - } - return new LottoTickets(lottos); + public int getManualCount() { + return manualLottos.getCount(); + } + + public int getAutoCount() { + return lottos.size() - manualLottos.getCount(); } public int size() { @@ -26,10 +26,10 @@ public int size() { } public List getLottos() { - return new ArrayList<>(lottos); + return List.copyOf(lottos); } - public WinningResult matchWith(WinningNumbers winningNumbers){ + public WinningResult matchWith(WinningNumbers winningNumbers) { Map result = initializeResult(); for (Lotto lotto : lottos) { Rank rank = winningNumbers.match(lotto); diff --git a/src/main/java/lotto/domain/LottoTicketsFactory.java b/src/main/java/lotto/domain/LottoTicketsFactory.java new file mode 100644 index 0000000000..025da383a4 --- /dev/null +++ b/src/main/java/lotto/domain/LottoTicketsFactory.java @@ -0,0 +1,59 @@ +package lotto.domain; + +import java.util.ArrayList; +import java.util.List; + +public class LottoTicketsFactory { + public static LottoTickets create(PurchaseAmount purchaseAmount) { + int count = purchaseAmount.getLottoCount(); + List lottos = generateAutoLottos(count); + return new LottoTickets(lottos, new ManualLottos(null)); + } + + public static LottoTickets create(PurchaseAmount purchaseAmount, ManualLottos manualLottos) { + validateManualCount(purchaseAmount, manualLottos); +// List allLottos = createLottos(purchaseAmount, manualLottos); + List lottos = convertToLottos(manualLottos); + List allLottos = createLottos(purchaseAmount, lottos, manualLottos.getCount()); + return new LottoTickets(allLottos, manualLottos); + } + + private static List convertToLottos(ManualLottos manualLottos) { + List lottos = new ArrayList<>(); + for (String input : manualLottos.getManualLottosInput()) { + lottos.add(parseLotto(input)); + } + return lottos; + } + + private static Lotto parseLotto(String input) { + String[] tokens = input.split(","); + List numbers = new ArrayList<>(); + for (String token : tokens) { + numbers.add(Integer.parseInt(token.trim())); + } + return Lotto.from(numbers); + } + + + private static void validateManualCount(PurchaseAmount purchaseAmount, ManualLottos manualLottos) { + if (manualLottos.getCount() > purchaseAmount.getLottoCount()) { + throw new IllegalArgumentException("수동로또 개수가 구입 가능한 개수보다 많습니다."); + } + } + + private static List createLottos(PurchaseAmount purchaseAmount, List manualLottosList, int manualCount) { + List lottos = new ArrayList<>(manualLottosList); + int autoCount = purchaseAmount.getLottoCount() - manualCount; + lottos.addAll(generateAutoLottos(autoCount)); + return lottos; + } + + private static List generateAutoLottos(int count) { + List lottos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + lottos.add(Lotto.from(LottoNumberGenerator.generate())); + } + return lottos; + } +} diff --git a/src/main/java/lotto/domain/ManualLottos.java b/src/main/java/lotto/domain/ManualLottos.java new file mode 100644 index 0000000000..7fc754129f --- /dev/null +++ b/src/main/java/lotto/domain/ManualLottos.java @@ -0,0 +1,22 @@ +package lotto.domain; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class ManualLottos { + private final List manualLottosInput; + + public ManualLottos(List manualLottosInput) { + this.manualLottosInput = Optional.ofNullable(manualLottosInput) + .orElse(Collections.emptyList()); + } + + public int getCount() { + return manualLottosInput.size(); + } + + public List getManualLottosInput() { + return List.copyOf(manualLottosInput); + } +} diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java index 341373ac73..a6888d3104 100644 --- a/src/main/java/lotto/view/InputView.java +++ b/src/main/java/lotto/view/InputView.java @@ -11,8 +11,16 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); public static int inputPurchaseAmount() { - System.out.println("구입금액을 입력해 주세요."); - return Integer.parseInt(scanner.nextLine()); + while (true) { + try { + System.out.println("구입금액을 입력해 주세요."); + String input = scanner.nextLine(); + validateNotEmpty(input); + return Integer.parseInt(input.trim()); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } } public static Lotto inputWinningNumbers() { @@ -33,6 +41,52 @@ private static Lotto parseWinningNumbers(String input) { public static LottoNumber inputBonusNumber() { System.out.println("보너스 볼을 입력해 주세요."); return LottoNumber.of(Integer.parseInt(scanner.nextLine())); + } + + public static int inputManualLottoCount() { + while (true) { + try { + System.out.println("\n수동으로 구매할 로또 수를 입력해 주세요."); + String input = scanner.nextLine(); + validateNotEmpty(input); + return Integer.parseInt(input.trim()); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + public static List inputManualLottos(int count) { + validateCount(count); + if (count == 0) { + return new ArrayList<>(); + } + + System.out.println("수동으로 구매할 번호를 입력해 주세요."); + List manualLottos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + manualLottos.add(inputSingleManualLotto()); + } + return manualLottos; + } + + private static String inputSingleManualLotto() { + String input = scanner.nextLine(); + validateNotEmpty(input); +// +// parseWinningNumbers(input); + return input.trim(); + } + + private static void validateCount(int count) { + if (count < 0) { + throw new IllegalArgumentException("음수를 입력할 수 없습니다."); + } + } + + private static void validateNotEmpty(String input) { + if (input == null || input.trim().isEmpty()) { + throw new IllegalArgumentException("빈 값을 입력할 수 없습니다."); + } } } diff --git a/src/main/java/lotto/view/ResultView.java b/src/main/java/lotto/view/ResultView.java index bc1d85f72f..2292246bf0 100644 --- a/src/main/java/lotto/view/ResultView.java +++ b/src/main/java/lotto/view/ResultView.java @@ -8,8 +8,10 @@ import java.util.List; public class ResultView { - public static void printTicketCount(int count) { - System.out.println(String.format("%d개를 구매했습니다.", count)); + public static void printTicketCount(LottoTickets tickets) { + int manualCount = tickets.getManualCount(); + int autoCount = tickets.getAutoCount(); + System.out.println(String.format("수동으로 %d장, 자동으로 %d개를 구매했습니다.", manualCount, autoCount)); } public static void printLottoTickets(LottoTickets tickets) { diff --git a/src/test/java/lotto/domain/LottoTest.java b/src/test/java/lotto/domain/LottoTest.java index ed1a900dd6..e4259448a1 100644 --- a/src/test/java/lotto/domain/LottoTest.java +++ b/src/test/java/lotto/domain/LottoTest.java @@ -18,14 +18,14 @@ public class LottoTest { void 로또_번호_6개가_아니면_예외발생() { assertThatThrownBy(() -> new Lotto(1, 2, 3, 4, 5)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("로또 번호는 총 6개여야 합니다."); + .hasMessage("로또 번호는 중복없이 6개여야 합니다."); } @Test void 로또_번호가_중복되면_예외발생() { assertThatThrownBy(() -> new Lotto(1, 2, 3, 4, 5, 5)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("로또 번호는 중복될 수 없습니다."); + .hasMessage("로또 번호는 중복없이 6개여야 합니다."); } @Test diff --git a/src/test/java/lotto/domain/LottoTicketsTest.java b/src/test/java/lotto/domain/LottoTicketsTest.java index 4180e31c3d..e9693d0956 100644 --- a/src/test/java/lotto/domain/LottoTicketsTest.java +++ b/src/test/java/lotto/domain/LottoTicketsTest.java @@ -2,29 +2,28 @@ import org.junit.jupiter.api.Test; +import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -//- [ ] 구입 금액을 1000으로 나눈 갯수반큼 로또 티켓을 발행한다. -// - [ ] 1부터 45까지의 숫자 중 중복되지 않는 6개의 숫자를 무작위로 선택한다 - public class LottoTicketsTest { @Test void 구입_금액으로_로또_티켓_생성() { - PurchaseAmount purchaseAmount =new PurchaseAmount(5_000); + PurchaseAmount purchaseAmount = new PurchaseAmount(5_000); - LottoTickets tickets = LottoTickets.create(purchaseAmount); + LottoTickets tickets = LottoTicketsFactory.create(purchaseAmount); assertThat(tickets.size()).isEqualTo(5); } @Test void 모든_로또_티켓_조회() { - PurchaseAmount purchaseAmount =new PurchaseAmount(3_000); + PurchaseAmount purchaseAmount = new PurchaseAmount(3_000); - LottoTickets tickets = LottoTickets.create(purchaseAmount); + LottoTickets tickets = LottoTicketsFactory.create(purchaseAmount); List lottos = tickets.getLottos(); assertThat(lottos).hasSize(3); @@ -32,4 +31,41 @@ public class LottoTicketsTest { assertThat(lotto.getNumbers()).hasSize(6); } } + + @Test + void 구매금액과_수동로또개수가_같은경우_수동로또만_구매() { + PurchaseAmount purchaseAmount = new PurchaseAmount(2000); + ManualLottos manualLottos = new ManualLottos(Arrays.asList("1, 2, 3, 4, 5, 6", "7, 8, 9, 10, 11, 12")); + + LottoTickets tickets = LottoTicketsFactory.create(purchaseAmount, manualLottos); + + assertThat(tickets.size()).isEqualTo(2); + assertThat(tickets.getManualCount()).isEqualTo(2); + assertThat(tickets.getAutoCount()).isEqualTo(0); + } + + @Test + void 수동_자동_혼합_구매() { + PurchaseAmount purchaseAmount = new PurchaseAmount(5000); + ManualLottos manualLottos = new ManualLottos(Arrays.asList("1, 2, 3, 4, 5, 6", "7, 8, 9, 10, 11, 12")); + + LottoTickets tickets = LottoTicketsFactory.create(purchaseAmount, manualLottos); + + assertThat(tickets.size()).isEqualTo(5); + assertThat(tickets.getManualCount()).isEqualTo(2); + assertThat(tickets.getAutoCount()).isEqualTo(3); + } + + @Test + void 수동로또_개수가_구입가능_개수보다_많으면_예외를던진() { + PurchaseAmount purchaseAmount = new PurchaseAmount(2000); + ManualLottos manualLottos = new ManualLottos(Arrays.asList( + "1, 2, 3, 4, 5, 6", + "7, 8, 9, 10, 11, 12", + "13, 14, 15, 16, 17, 18")); + + assertThatThrownBy(() -> LottoTicketsFactory.create(purchaseAmount, manualLottos)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("수동로또 개수가 구입 가능한 개수보다 많습니다."); + } } diff --git a/src/test/java/lotto/domain/ManualLottosTest.java b/src/test/java/lotto/domain/ManualLottosTest.java new file mode 100644 index 0000000000..1619fec1d6 --- /dev/null +++ b/src/test/java/lotto/domain/ManualLottosTest.java @@ -0,0 +1,31 @@ +package lotto.domain; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ManualLottosTest { + + @Test + void 수동_로또_문자열리스트로_생성한다() { + List lottosInputs = Arrays.asList("1, 2, 3, 4, 5, 6", "7, 8, 9, 10, 11, 12"); + + ManualLottos manualLottos = new ManualLottos(lottosInputs); + + assertThat(manualLottos.getCount()).isEqualTo(2); + } + + @Test + void 비어있거나_null_입력시_빈_리스트로_초기화() { + ManualLottos manualLottos = new ManualLottos(null); + ManualLottos emptyManualLottos = new ManualLottos(Collections.emptyList()); + + assertThat(manualLottos.getCount()).isEqualTo(0); + assertThat(emptyManualLottos.getCount()).isEqualTo(0); + } + +} diff --git a/src/test/java/lotto/domain/WinningResultTest.java b/src/test/java/lotto/domain/WinningResultTest.java index 3b1b7ecdee..8b881ddc1f 100644 --- a/src/test/java/lotto/domain/WinningResultTest.java +++ b/src/test/java/lotto/domain/WinningResultTest.java @@ -62,7 +62,7 @@ private LottoTickets createTickets(Lotto... lottos) { for (Lotto lotto : lottos) { lottoList.add(lotto); } - return new LottoTickets(lottoList); + return new LottoTickets(lottoList, new ManualLottos(null)); } }