diff --git a/README.md b/README.md index 5fa2560b46..5c1178d36a 100644 --- a/README.md +++ b/README.md @@ -1 +1,230 @@ -# java-lotto-precourse +# πŸ’Έ 둜또 + +- - - + +## πŸš€ κΈ°λŠ₯ μš”κ΅¬ 사항 + +- - - + +### <κ°„λ‹¨ν•œ 둜또 발맀기 κ΅¬ν˜„> + +- 둜또 번호의 숫자 λ²”μœ„λŠ” 1~45 +- 둜또 κ΅¬μž… κΈˆμ•‘ μž…λ ₯ μ‹œ κ΅¬μž… κΈˆμ•‘μ— ν•΄λ‹Ήν•˜λŠ” 만큼 둜또 λ°œν–‰ + - 둜또 1μž₯의 가격은 1,000원 + - 1,000μ›μœΌλ‘œ λ‚˜λˆ„μ–΄ 떨어지지 μ•ŠλŠ” 경우 μ˜ˆμ™Έ 처리 + - 1μž₯의 둜또 λ°œν–‰ μ‹œ μ€‘λ³΅λ˜μ§€ μ•ŠλŠ” 6개의 숫자 μ„ μ • + - 둜또 λ²ˆν˜ΈλŠ” μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬ν•˜μ—¬ 좜λ ₯ +- 당첨 번호 좔첨 + - μ€‘λ³΅λ˜μ§€ μ•ŠλŠ” 6개의 μˆ«μžμ™€ λ³΄λ„ˆμŠ€ 번호 1개 μ„ μ • +- λ‹Ήμ²¨μ—λŠ” 1~5λ“± 쑴재 + - 당첨 κΈ°μ€€κ³Ό κΈˆμ•‘ + - 1λ“±: 6개 번호 일치 / 2,000,000,000원 + - 2λ“±: 5개 번호 + λ³΄λ„ˆμŠ€ 번호 일치 / 30,000,000원 + - 3λ“±: 5개 번호 일치 / 1,500,000원 + - 4λ“±: 4개 번호 일치 / 50,000원 + - 5λ“±: 3개 번호 일치 / 5,000원 +- 당첨 λ²ˆν˜Έμ™€ λ³΄λ„ˆμŠ€ 번호λ₯Ό μž…λ ₯λ°›μŒ +- μ‚¬μš©μžκ°€ κ΅¬λ§€ν•œ 둜또 λ²ˆν˜Έμ™€ 당첨 번호 비ꡐ + - 당첨 λ‚΄μ—­ 및 수읡λ₯ μ„ 좜λ ₯ν•œ λ’€ 둜또 κ²Œμž„ μ’…λ£Œ + - 수읡λ₯ μ€ μ†Œμˆ˜μ  λ‘˜μ§Έ μžλ¦¬μ—μ„œ 반올림 +- μ‚¬μš©μžκ°€ 잘λͺ»λœ 값을 μž…λ ₯ν•  경우 `IllegalArgumentException` λ°œμƒ + - `[ERROR]`둜 μ‹œμž‘ν•˜λŠ” μ—λŸ¬ λ©”μ‹œμ§€ 좜λ ₯ + - κ·Έ λΆ€λΆ„λΆ€ν„° μž…λ ₯을 λ‹€μ‹œ λ°›μŒ + - `Exception`이 μ•„λ‹Œ `IllegalArgumentException`, `IllegalStateException` λ“±κ³Ό 같은 λͺ…ν™•ν•œ 처리 +- `camp.nextstep.edu.missionutils`μ—μ„œ μ œκ³΅ν•˜λŠ” `Randoms`, `Console API` μ‚¬μš© +- Random κ°’ μΆ”μΆœμ€ `camp.nextstep.edu.missionutils.Randoms`의 `pickUniqueNumbersInRange()` ν™œμš© +- μ‚¬μš©μž μž…λ ₯ 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 ν™œμš© + +## πŸ“ƒ μΆ”κ°€λœ ν”„λ‘œκ·Έλž˜λ° μš”κ΅¬ 사항 + +- - - + +- λ©”μ„œλ“œμ˜ 길이가 15라인을 λ„˜μ–΄κ°€μ§€ μ•Šλ„λ‘ κ΅¬ν˜„ +- λ©”μ„œλ“œκ°€ ν•œ 가지 일만 잘 ν•˜λ„λ‘ κ΅¬ν˜„ +- `else`, `switch`/`case` κΈˆμ§€ + - `if` μ‘°κ±΄μ ˆμ—μ„œ 값을 `return`ν•˜λŠ” λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν•˜λ©΄ 됨 +- Java Enum 적용 +- κ΅¬ν˜„ν•œ κΈ°λŠ₯에 λŒ€ν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈ μž‘μ„± + - 단, UI(`System.out`, `System.in`, `Scanner`) 둜직 μ œμ™Έ + - λ‹¨μœ„ ν…ŒμŠ€νŠΈ μž‘μ„±μ΄ μ΅μˆ™ν•˜μ§€ μ•Šλ‹€λ©΄ `LottoTest` μ°Έκ³  + +## πŸ“‚ νŒ¨ν‚€μ§€ ꡬ쑰 + +- - - + +``` +🌐 src.main.java.lotto +β”‚ +β”œβ”€β”€ πŸ“¦ constants +β”‚ └── LottoConstantNumbers +β”‚ +β”œβ”€β”€ πŸ“¦ controller +β”‚ └── LottoController +β”‚ +β”œβ”€β”€ πŸ“¦ domain +β”‚ β”œβ”€β”€ Lotto +β”‚ β”œβ”€β”€ LottoResult +β”‚ β”œβ”€β”€ Prize +β”‚ └── WinningNumber +β”‚ +β”œβ”€β”€ πŸ“¦ exception +β”‚ β”‚ +β”‚ β”œβ”€β”€πŸ“¦ lottoticketexception +β”‚ β”‚ β”œβ”€β”€ DuplicateException +β”‚ β”‚ └── LottoNumberSizeException +β”‚ β”‚ +β”‚ β”œβ”€β”€πŸ“¦ numberexception +β”‚ β”‚ β”œβ”€β”€ InvalidNumberException +β”‚ β”‚ └── OutOfRangeNumberException +β”‚ β”‚ +β”‚ β”œβ”€β”€πŸ“¦ purchaseamountexception +β”‚ β”‚ β”œβ”€β”€ InvalidPurchaseAmountException +β”‚ β”‚ β”œβ”€β”€ MaxPurchaseExceedException +β”‚ β”‚ β”œβ”€β”€ NegativePurchaseAmountException +β”‚ β”‚ └── NotDivisibleByLottoPriceException +β”‚ β”‚ +β”‚ β”œβ”€β”€ ErrorConstants +β”‚ └── ErrorMessage +β”‚ +β”œβ”€β”€ πŸ“¦ factory +β”‚ └── LottoTicketStore +β”‚ +β”œβ”€β”€ πŸ“¦ service +β”‚ β”œβ”€β”€ LottoGameService +β”‚ β”œβ”€β”€ LottoPurchaseService +β”‚ β”œβ”€β”€ LottoResultCalculator +β”‚ └── LottoStatisticsService +β”‚ +β”œβ”€β”€ πŸ“¦ util +β”‚ β”œβ”€β”€ DuplicateValidator +β”‚ β”œβ”€β”€ LottoNumberSorter +β”‚ β”œβ”€β”€ NumberValidator +β”‚ β”œβ”€β”€ PurchaseAmountValidator +β”‚ β”œβ”€β”€ RandomNumberGenerator +β”‚ └── WinningNumberSeparator +β”‚ +β”œβ”€β”€ πŸ“¦ view +β”‚ β”œβ”€β”€ ConsoleMessage +β”‚ β”œβ”€β”€ InputView +β”‚ └── OutputView +β”‚ +└── Application +``` + +## πŸ“Œ κ³„νš + +- - - + +- 둜또의 흐름 νŒŒμ•… +- μ–΄μšΈλ¦¬λŠ” λ””μžμΈ νŒ¨ν„΄ μ„ μ • +- νŒ¨ν‚€μ§€ ꡬ쑰 섀계 +- κ΅¬ν˜„ν•  κΈ°λŠ₯ λͺ©λ‘ 정리 +- README.md μž‘μ„± +- ν”„λ‘œμ νŠΈ 초기 μ„€μ • +- κ΅¬ν˜„ +- μ˜ˆμ™Έ μ²˜λ¦¬μ— λŒ€ν•œ κ²€ν†  +- ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± +- λ¦¬νŒ©ν† λ§ + +## πŸ’‘ κ΅¬ν˜„ν•  κΈ°λŠ₯ λͺ©λ‘ + +- - - + +### 1. μž…λ ₯ + +- [x] 둜또 κ΅¬μž… κΈˆμ•‘ μž…λ ₯ λ°›κΈ° + - [x] 1,000원 λ‹¨μœ„λ‘œ μž…λ ₯ λ°›μ•„μ•Ό 함 + - [x] μ–‘μ˜ μ •μˆ˜κ°€ μ•„λ‹Œ 경우 μ˜ˆμ™Έ λ°œμƒ + - [x] 1,000으둜 λ‚˜λˆ„μ–΄ 떨어지지 μ•ŠλŠ” 경우 μ˜ˆμ™Έ λ°œμƒ + - [x] 1회 μ΅œλŒ€ ꡬ맀 κ°€λŠ₯ κΈˆμ•‘μ„ μ΄ˆκ³Όν•  μ‹œ μ˜ˆμ™Έ λ°œμƒ + - [x] 둜또 ꡬ맀 개수 좜λ ₯ +- [x] 당첨 번호 μž…λ ₯ + - [x] μ‰Όν‘œλ‘œ ꡬ뢄 + - [x] μ˜¬λ°”λ₯΄κ²Œ ꡬ뢄할 수 없을 μ‹œ μ˜ˆμ™Έ λ°œμƒ + - [x] 6개 μž…λ ₯ λ°›μŒ + - [x] 6κ°œκ°€ 아닐 경우 μ˜ˆμ™Έ λ°œμƒ + - [x] 1~45 λ²”μœ„μ˜ μ •μˆ˜λ§Œ μžˆλŠ”μ§€ 검증 + - [x] λ²”μœ„ μ™Έμ˜ 수 μž…λ ₯ μ‹œ μ˜ˆμ™Έ λ°œμƒ + - [x] 쀑볡 없이 μž…λ ₯λ˜μ—ˆλŠ”μ§€ 검증 + - [x] μ€‘λ³΅λ˜μ—ˆμ„ μ‹œ μ˜ˆμ™Έ λ°œμƒ +- [x] λ³΄λ„ˆμŠ€ 번호 μž…λ ₯ + - [x] 1개 μž…λ ₯ λ°›μŒ + - [x] 1~45 λ²”μœ„μ˜ μ •μˆ˜μΈμ§€ 검증 + - [x] λ²”μœ„ μ™Έμ˜ 수 μž…λ ₯ μ‹œ μ˜ˆμ™Έ λ°œμƒ + +### 2. 둜또 λ°œν–‰ + +- [x] λ°œν–‰ν•  둜또 μˆ˜λŸ‰ 계산 +- [x] 둜또 번호 μ„ μ • + - [x] 1~45 λ²”μœ„μ˜ μ •μˆ˜ 쀑 쀑볡 없이 λ¬΄μž‘μœ„ 6개의 숫자 + - [x] 각 λ‘œλ˜λ§ˆλ‹€ μ˜€λ¦„μ°¨μˆœμœΌλ‘œ 번호 μ •λ ¬ +- [x] 둜또 리슀트 생성 + - [x] κ΅¬μž… κΈˆμ•‘μ— 따라 κ³„μ‚°λœ 수만큼의 둜또 생성 + - [x] λ¦¬μŠ€νŠΈμ— μ €μž₯ + - [x] μƒμ„±λœ 둜또 리슀트λ₯Ό ν˜•μ‹μ— 맞게 좜λ ₯ + +### 3. 당첨 κ²°κ³Ό 계산 + +- [x] 각 둜또의 λ²ˆν˜Έμ™€ 당첨 번호λ₯Ό 비ꡐ + - [x] μΌμΉ˜ν•˜λŠ” 번호의 개수 계산 +- [x] 당첨 λ“±μˆ˜ νŒλ‹¨ + - [x] μΌμΉ˜ν•˜λŠ” 번호의 κ°œμˆ˜μ— 따라 λ“±μˆ˜ κ²°μ • +- [x] 각 λ“±μˆ˜μ˜ 당첨 횟수 μΉ΄μš΄νŒ… +- [x] 당첨 횟수 μ €μž₯ + +### 4. 수읡λ₯  계산 + +- [x] 총 λ‹Ήμ²¨κΈˆ 계산 (각 λ“±μˆ˜λ³„ 당첨 횟수 * λ‹Ήμ²¨κΈˆ) +- [x] 수읡λ₯  계산 (총 λ‹Ήμ²¨κΈˆ / 총 κ΅¬μž… κΈˆμ•‘) * 100 + - [x] μ†Œμˆ˜μ  λ‘˜μ§Έ μžλ¦¬μ—μ„œ 반올림 + +### 5. κ²°κ³Ό 좜λ ₯ + +- [x] 둜또 ꡬ맀 λ‚΄μ—­(μƒμ„±λœ 둜또 μˆ˜μ™€ 각 둜또의 번호λ₯Ό μ˜€λ¦„μ°¨μˆœ μ •λ ¬) 좜λ ₯ +- [x] 당첨 톡계(λ“±μˆ˜λ³„ 당첨 νšŸμˆ˜μ™€ λ‹Ήμ²¨κΈˆ) 좜λ ₯ +- [x] κ³„μ‚°λœ 수읡λ₯  좜λ ₯ + +### 6. λ‹¨μœ„ ν…ŒμŠ€νŠΈ μž‘μ„± + +- [x] κ΅¬ν˜„ν•œ κΈ°λŠ₯에 λŒ€ν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈ μž‘μ„± + +### 7. μœ ν‹Έλ¦¬ν‹° + +- [x] 숫자 검증 μœ ν‹Έλ¦¬ν‹° +- [x] 숫자 랜덀 μΆ”μΆœ μœ ν‹Έλ¦¬ν‹° +- [x] μ˜€λ¦„μ°¨μˆœ μ •λ ¬ μœ ν‹Έλ¦¬ν‹° + +### 8. ꡬ쑰 κ°œμ„  및 λ¦¬νŒ©ν† λ§ + +- [ ] λ©”μ„œλ“œ 뢄리 + - [ ] λ©”μ„œλ“œμ˜ 길이가 15라인을 λ„˜μ–΄κ°€μ§€ μ•Šλ„λ‘ κ΅¬ν˜„ + - [ ] λ©”μ„œλ“œκ°€ ν•œ 가지 일만 잘 ν•˜λ„λ‘ κ΅¬ν˜„ +- [ ] μ±…μž„ 뢄리: 각 ν΄λž˜μŠ€κ°€ 단일 μ±…μž„ 원칙을 λ”°λ₯΄λ„둝 ꡬ쑰 κ°œμ„  +- [x] else, switch/caseλ¬Έ κΈˆμ§€ +- [x] Enum의 적용 + +## πŸ€” μƒκ°μ˜ 흔적 + +- - - + +### 둜또의 흐름 + +1. 둜또 κ΅¬μž… κΈˆμ•‘ μž…λ ₯ λ°›μŒ +2. 당첨 번호 μž…λ ₯ λ°›μŒ +3. λ³΄λ„ˆμŠ€ 번호 μž…λ ₯ λ°›μŒ +4. λ°œν–‰ν•œ 둜또의 μˆ˜λŸ‰ 좜λ ₯ +5. λ°œν–‰ν•œ 둜또의 번호λ₯Ό μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬ν•˜μ—¬ 좜λ ₯ +6. 당첨 λ‚΄μ—­ 좜λ ₯ +7. 수읡λ₯  좜λ ₯ + +- μ˜ˆμ™Έ 상황 μ‹œ μ—λŸ¬ 문ꡬ 좜λ ₯ ν›„ λ‹€μ‹œ μž…λ ₯ λ°›μŒ + +### κ³ λ―Όν•œ λΆ€λΆ„ + +- 둜또 ꡬ맀 κ°€λŠ₯ κΈˆμ•‘μ„ intλ‚˜ long으둜 ν•  것인가, ν˜„μ‹€μ²˜λŸΌ 10만 μ›κΉŒμ§€ μ œν•œν•  것인가 + - 10만 μ›μœΌλ‘œ μ œν•œ +- Validator.validateκ°€ λ§˜μ— μ•ˆ λ“œλŠ”λ° μ–΄μ©”κΉŒ.. + +### 자기 λ°˜μ„± + +- 3μ‹œκ°„ λ§Œμ— ν•˜λ €λ‹ˆκΉŒ λ„ˆλ¬΄ μ΄‰λ°•ν–ˆλ‹€. λ‹€μŒμ—” μ‹œκ°„μ„ 쑰금 더 λ‚΄μ„œ ν•˜μž.. \ No newline at end of file diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba4..f9c2e1a513 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,42 @@ package lotto; +import lotto.controller.LottoController; +import lotto.factory.LottoTicketStore; +import lotto.service.LottoGameService; +import lotto.service.LottoPurchaseService; +import lotto.service.LottoResultCalculator; +import lotto.service.LottoStatisticsService; +import lotto.util.DuplicateValidator; +import lotto.util.LottoNumberSorter; +import lotto.util.NumberValidator; +import lotto.util.PurchaseAmountValidator; +import lotto.util.RandomNumberGenerator; +import lotto.util.WinningNumberSeparator; +import lotto.view.InputView; +import lotto.view.OutputView; + public class Application { - public static void main(String[] args) { - // TODO: ν”„λ‘œκ·Έλž¨ κ΅¬ν˜„ - } + public static void main(String[] args) { + RandomNumberGenerator randomNumberGenerator = new RandomNumberGenerator(); + LottoNumberSorter lottoNumberSorter = new LottoNumberSorter(); + LottoTicketStore lottoTicketStore = new LottoTicketStore(randomNumberGenerator, lottoNumberSorter); + + NumberValidator numberValidator = new NumberValidator(); + DuplicateValidator duplicateValidator = new DuplicateValidator(); + WinningNumberSeparator winningNumberSeparator = new WinningNumberSeparator(); + PurchaseAmountValidator purchaseAmountValidator = new PurchaseAmountValidator(numberValidator); + + OutputView outputView = new OutputView(); + InputView inputView = new InputView(outputView, purchaseAmountValidator, winningNumberSeparator, + duplicateValidator); + + LottoPurchaseService lottoPurchaseService = new LottoPurchaseService(inputView, outputView, lottoTicketStore); + LottoGameService lottoGameService = new LottoGameService(inputView); + LottoResultCalculator lottoResultCalculator = new LottoResultCalculator(); + LottoStatisticsService lottoStatisticsService = new LottoStatisticsService(lottoResultCalculator, outputView); + LottoController lottoController = new LottoController(lottoGameService, lottoPurchaseService, + lottoStatisticsService); + + lottoController.play(); + } } diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java deleted file mode 100644 index 88fc5cf12b..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("[ERROR] 둜또 λ²ˆν˜ΈλŠ” 6κ°œμ—¬μ•Ό ν•©λ‹ˆλ‹€."); - } - } - - // TODO: μΆ”κ°€ κΈ°λŠ₯ κ΅¬ν˜„ -} diff --git a/src/main/java/lotto/constants/LottoConstantNumbers.java b/src/main/java/lotto/constants/LottoConstantNumbers.java new file mode 100644 index 0000000000..7c380c25c0 --- /dev/null +++ b/src/main/java/lotto/constants/LottoConstantNumbers.java @@ -0,0 +1,19 @@ +package lotto.constants; + +public enum LottoConstantNumbers { + LOTTO_PRICE(1_000), + MAX_PURCHASE_AMOUNT(100_000), + LOTTO_NUMBERS_COUNT(6), + MIN_LOTTO_NUMBER(1), + MAX_LOTTO_NUMBER(45); + + private final int value; + + LottoConstantNumbers(int value) { + this.value = value; + } + + public int getValue() { + return value; + } +} \ No newline at end of file diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java new file mode 100644 index 0000000000..883bccab36 --- /dev/null +++ b/src/main/java/lotto/controller/LottoController.java @@ -0,0 +1,31 @@ +package lotto.controller; + +import java.util.List; + +import lotto.domain.Lotto; +import lotto.domain.LottoResult; +import lotto.domain.WinningNumber; +import lotto.service.LottoGameService; +import lotto.service.LottoPurchaseService; +import lotto.service.LottoStatisticsService; + +public class LottoController { + private final LottoGameService lottoGameService; + private final LottoPurchaseService lottoPurchaseService; + private final LottoStatisticsService lottoStatisticsService; + + public LottoController(LottoGameService lottoGameService, LottoPurchaseService lottoPurchaseService, + LottoStatisticsService lottoStatisticsService) { + this.lottoGameService = lottoGameService; + this.lottoPurchaseService = lottoPurchaseService; + this.lottoStatisticsService = lottoStatisticsService; + } + + public void play() { + int purchaseAmount = lottoPurchaseService.purchaseForLottos(); + List lottos = lottoPurchaseService.buyLottos(purchaseAmount); + WinningNumber winningNumber = lottoGameService.setWinningNumber(); + LottoResult lottoResult = lottoGameService.playLottoGame(lottos, winningNumber); + lottoStatisticsService.summarizeStatistics(purchaseAmount, lottos, winningNumber); + } +} diff --git a/src/main/java/lotto/domain/Lotto.java b/src/main/java/lotto/domain/Lotto.java new file mode 100644 index 0000000000..e6876278fe --- /dev/null +++ b/src/main/java/lotto/domain/Lotto.java @@ -0,0 +1,53 @@ +package lotto.domain; + +import static lotto.constants.LottoConstantNumbers.*; + +import java.util.List; + +import lotto.exception.lottoticketexception.DuplicateException; +import lotto.exception.lottoticketexception.LottoNumberSizeException; +import lotto.exception.numberexception.OutOfRangeNumberException; + +public class Lotto { + private final List numbers; + + public Lotto(List numbers) { + validateNumbers(numbers); + this.numbers = numbers; + } + + private void validateNumbers(List numbers) { + validateNumbersCount(numbers); + validateNumberRange(numbers); + validateNoDuplicates(numbers); + } + + private void validateNumbersCount(List numbers) { + if (numbers.size() != LOTTO_NUMBERS_COUNT.getValue()) { + throw new LottoNumberSizeException(); + } + } + + private void validateNumberRange(List numbers) { + for (Integer number : numbers) { + if (number < MIN_LOTTO_NUMBER.getValue() || MAX_LOTTO_NUMBER.getValue() < number) { + throw new OutOfRangeNumberException(); + } + } + } + + private void validateNoDuplicates(List numbers) { + if (numbers.stream().distinct().count() != numbers.size()) { + throw new DuplicateException(); + } + } + + @Override + public String toString() { + return numbers.toString(); + } + + public List getNumbers() { + return numbers; + } +} diff --git a/src/main/java/lotto/domain/LottoResult.java b/src/main/java/lotto/domain/LottoResult.java new file mode 100644 index 0000000000..51d181ad00 --- /dev/null +++ b/src/main/java/lotto/domain/LottoResult.java @@ -0,0 +1,27 @@ +package lotto.domain; + +import java.util.EnumMap; +import java.util.Map; + +public class LottoResult { + private final Map prizeCountMap; + + public LottoResult() { + prizeCountMap = new EnumMap<>(Prize.class); + for (Prize prize : Prize.values()) { + prizeCountMap.put(prize, 0); + } + } + + public void addPrize(Prize prize) { + prizeCountMap.put(prize, prizeCountMap.get(prize) + 1); + } + + public int getPrizeCount(Prize prize) { + return prizeCountMap.get(prize); + } + + public Map getPrizeCountMap() { + return prizeCountMap; + } +} diff --git a/src/main/java/lotto/domain/Prize.java b/src/main/java/lotto/domain/Prize.java new file mode 100644 index 0000000000..d448902aa9 --- /dev/null +++ b/src/main/java/lotto/domain/Prize.java @@ -0,0 +1,33 @@ +package lotto.domain; + +import java.util.Arrays; + +public enum Prize { + FIRST(6, false, 2_000_000_000), + SECOND(5, true, 30_000_000), + THIRD(5, false, 1_500_000), + FOURTH(4, false, 50_000), + FIFTH(3, false, 5_000), + NONE(0, false, 0); + + private final int matchCount; + private final boolean matchBonus; + private final int prizeAmount; + + Prize(int matchCount, boolean matchBonus, int prizeAmount) { + this.matchCount = matchCount; + this.matchBonus = matchBonus; + this.prizeAmount = prizeAmount; + } + + public int getPrizeAmount() { + return prizeAmount; + } + + public static Prize findPrize(int matchCount, boolean matchBonus) { + return Arrays.stream(values()) + .filter(prize -> prize.matchCount == matchCount && prize.matchBonus == matchBonus) + .findFirst() + .orElse(NONE); + } +} diff --git a/src/main/java/lotto/domain/WinningNumber.java b/src/main/java/lotto/domain/WinningNumber.java new file mode 100644 index 0000000000..54028a3d09 --- /dev/null +++ b/src/main/java/lotto/domain/WinningNumber.java @@ -0,0 +1,21 @@ +package lotto.domain; + +import java.util.List; + +public class WinningNumber { + private final List winningNumbers; + private final int bonusNumber; + + public WinningNumber(List winningNumbers, int bonusNumber) { + this.winningNumbers = winningNumbers; + this.bonusNumber = bonusNumber; + } + + public List getWinningNumbers() { + return winningNumbers; + } + + public int getBonusNumber() { + return bonusNumber; + } +} diff --git a/src/main/java/lotto/exception/ErrorConstants.java b/src/main/java/lotto/exception/ErrorConstants.java new file mode 100644 index 0000000000..72193343f7 --- /dev/null +++ b/src/main/java/lotto/exception/ErrorConstants.java @@ -0,0 +1,16 @@ +package lotto.exception; + +public enum ErrorConstants { + ERROR_MESSAGE_PREFIX("[ERROR]"), + SPACE(" "); + + private final String value; + + ErrorConstants(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/lotto/exception/ErrorMessage.java b/src/main/java/lotto/exception/ErrorMessage.java new file mode 100644 index 0000000000..0d417cb662 --- /dev/null +++ b/src/main/java/lotto/exception/ErrorMessage.java @@ -0,0 +1,23 @@ +package lotto.exception; + +import static lotto.constants.LottoConstantNumbers.*; + +public enum ErrorMessage { + INVALID_PURCHASE_AMOUNT("κ΅¬μž… κΈˆμ•‘μ€ " + LOTTO_PRICE.getValue() + " λ‹¨μœ„μ˜ μ–‘μ˜ μ •μˆ˜λ₯Ό μž…λ ₯ν•΄μ•Ό ν•©λ‹ˆλ‹€."), + NOT_DIVISIBLE_BY_LOTTO_PRICE("κ΅¬μž… κΈˆμ•‘μ€ " + LOTTO_PRICE.getValue() + "원 λ‹¨μœ„μ—¬μ•Ό ν•©λ‹ˆλ‹€."), + MAX_PURCHASE_EXCEED("1회 μ΅œλŒ€ ꡬ맀 κ°€λŠ₯ κΈˆμ•‘μ€ " + MAX_PURCHASE_AMOUNT.getValue() + "μž…λ‹ˆλ‹€."), + INVALID_LOTTO_NUMBERS_COUNT("둜또 λ²ˆν˜ΈλŠ” " + LOTTO_NUMBERS_COUNT.getValue() + "κ°œμ—¬μ•Ό ν•©λ‹ˆλ‹€."), + OUT_OF_RANGE_NUMBER( + "둜또 λ²ˆν˜ΈλŠ” " + MIN_LOTTO_NUMBER.getValue() + "λΆ€ν„° " + MAX_LOTTO_NUMBER.getValue() + "κΉŒμ§€μ˜ λ²”μœ„μ—¬μ•Ό ν•©λ‹ˆλ‹€."), + DUPLICATE_NUMBER("λ²ˆν˜ΈλŠ” 쀑볡될 수 μ—†μŠ΅λ‹ˆλ‹€."); + + private final String message; + + ErrorMessage(String message) { + this.message = message; + } + + public String getMessage() { + return ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + message; + } +} \ No newline at end of file diff --git a/src/main/java/lotto/exception/lottoticketexception/DuplicateException.java b/src/main/java/lotto/exception/lottoticketexception/DuplicateException.java new file mode 100644 index 0000000000..dca04a70da --- /dev/null +++ b/src/main/java/lotto/exception/lottoticketexception/DuplicateException.java @@ -0,0 +1,11 @@ +package lotto.exception.lottoticketexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class DuplicateException extends IllegalArgumentException { + public DuplicateException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.DUPLICATE_NUMBER.getMessage()); + } +} diff --git a/src/main/java/lotto/exception/lottoticketexception/LottoNumberSizeException.java b/src/main/java/lotto/exception/lottoticketexception/LottoNumberSizeException.java new file mode 100644 index 0000000000..0430eb7eae --- /dev/null +++ b/src/main/java/lotto/exception/lottoticketexception/LottoNumberSizeException.java @@ -0,0 +1,11 @@ +package lotto.exception.lottoticketexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class LottoNumberSizeException extends IllegalArgumentException { + public LottoNumberSizeException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.INVALID_LOTTO_NUMBERS_COUNT.getMessage()); + } +} diff --git a/src/main/java/lotto/exception/numberexception/InvalidNumberException.java b/src/main/java/lotto/exception/numberexception/InvalidNumberException.java new file mode 100644 index 0000000000..a9dbad0153 --- /dev/null +++ b/src/main/java/lotto/exception/numberexception/InvalidNumberException.java @@ -0,0 +1,11 @@ +package lotto.exception.numberexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class InvalidNumberException extends NumberFormatException { + public InvalidNumberException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.OUT_OF_RANGE_NUMBER.getMessage()); + } +} diff --git a/src/main/java/lotto/exception/numberexception/OutOfRangeNumberException.java b/src/main/java/lotto/exception/numberexception/OutOfRangeNumberException.java new file mode 100644 index 0000000000..932b4d8928 --- /dev/null +++ b/src/main/java/lotto/exception/numberexception/OutOfRangeNumberException.java @@ -0,0 +1,11 @@ +package lotto.exception.numberexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class OutOfRangeNumberException extends IllegalArgumentException { + public OutOfRangeNumberException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.OUT_OF_RANGE_NUMBER.getMessage()); + } +} diff --git a/src/main/java/lotto/exception/purchaseamountexception/InvalidPurchaseAmountException.java b/src/main/java/lotto/exception/purchaseamountexception/InvalidPurchaseAmountException.java new file mode 100644 index 0000000000..8b15c5b3ed --- /dev/null +++ b/src/main/java/lotto/exception/purchaseamountexception/InvalidPurchaseAmountException.java @@ -0,0 +1,11 @@ +package lotto.exception.purchaseamountexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class InvalidPurchaseAmountException extends NumberFormatException { + public InvalidPurchaseAmountException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.INVALID_PURCHASE_AMOUNT.getMessage()); + } +} diff --git a/src/main/java/lotto/exception/purchaseamountexception/MaxPurchaseExceedException.java b/src/main/java/lotto/exception/purchaseamountexception/MaxPurchaseExceedException.java new file mode 100644 index 0000000000..dc32e48f46 --- /dev/null +++ b/src/main/java/lotto/exception/purchaseamountexception/MaxPurchaseExceedException.java @@ -0,0 +1,11 @@ +package lotto.exception.purchaseamountexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class MaxPurchaseExceedException extends IllegalStateException { + public MaxPurchaseExceedException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.MAX_PURCHASE_EXCEED.getMessage()); + } +} \ No newline at end of file diff --git a/src/main/java/lotto/exception/purchaseamountexception/NegativePurchaseAmountException.java b/src/main/java/lotto/exception/purchaseamountexception/NegativePurchaseAmountException.java new file mode 100644 index 0000000000..0a60a54f6f --- /dev/null +++ b/src/main/java/lotto/exception/purchaseamountexception/NegativePurchaseAmountException.java @@ -0,0 +1,11 @@ +package lotto.exception.purchaseamountexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class NegativePurchaseAmountException extends IllegalArgumentException { + public NegativePurchaseAmountException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.INVALID_PURCHASE_AMOUNT.getMessage()); + } +} diff --git a/src/main/java/lotto/exception/purchaseamountexception/NotDivisibleByLottoPriceException.java b/src/main/java/lotto/exception/purchaseamountexception/NotDivisibleByLottoPriceException.java new file mode 100644 index 0000000000..84b7ecb6b5 --- /dev/null +++ b/src/main/java/lotto/exception/purchaseamountexception/NotDivisibleByLottoPriceException.java @@ -0,0 +1,11 @@ +package lotto.exception.purchaseamountexception; + +import lotto.exception.ErrorConstants; +import lotto.exception.ErrorMessage; + +public class NotDivisibleByLottoPriceException extends IllegalArgumentException { + public NotDivisibleByLottoPriceException() { + super(ErrorConstants.ERROR_MESSAGE_PREFIX.getValue() + ErrorConstants.SPACE.getValue() + + ErrorMessage.NOT_DIVISIBLE_BY_LOTTO_PRICE.getMessage()); + } +} diff --git a/src/main/java/lotto/factory/LottoTicketStore.java b/src/main/java/lotto/factory/LottoTicketStore.java new file mode 100644 index 0000000000..60ab9a0af6 --- /dev/null +++ b/src/main/java/lotto/factory/LottoTicketStore.java @@ -0,0 +1,31 @@ +package lotto.factory; + +import java.util.List; +import java.util.stream.Stream; + +import lotto.domain.Lotto; +import lotto.util.LottoNumberSorter; +import lotto.util.RandomNumberGenerator; + +public class LottoTicketStore { + + private final RandomNumberGenerator randomNumberGenerator; + private final LottoNumberSorter lottoNumberSorter; + + public LottoTicketStore(RandomNumberGenerator randomNumberGenerator, LottoNumberSorter lottoNumberSorter) { + this.randomNumberGenerator = randomNumberGenerator; + this.lottoNumberSorter = lottoNumberSorter; + } + + public Lotto createLottoTicket() { + List numbers = randomNumberGenerator.pickLottoNumbers(); + lottoNumberSorter.sortInAscendingOrder(numbers); + return new Lotto(numbers); + } + + public List createLottoTickets(int buyCount) { + return Stream.generate(this::createLottoTicket) + .limit(buyCount) + .toList(); + } +} diff --git a/src/main/java/lotto/service/LottoGameService.java b/src/main/java/lotto/service/LottoGameService.java new file mode 100644 index 0000000000..88f353d5d4 --- /dev/null +++ b/src/main/java/lotto/service/LottoGameService.java @@ -0,0 +1,44 @@ +package lotto.service; + +import java.util.List; + +import lotto.domain.Lotto; +import lotto.domain.LottoResult; +import lotto.domain.Prize; +import lotto.domain.WinningNumber; +import lotto.view.InputView; + +public class LottoGameService { + private final InputView inputView; + + public LottoGameService(InputView inputView) { + this.inputView = inputView; + } + + public WinningNumber setWinningNumber() { + List winningNumbers = inputView.getWinningNumbers(); + int bonusNumber = inputView.getBonusNumber(winningNumbers); + return new WinningNumber(winningNumbers, bonusNumber); + } + + public LottoResult playLottoGame(List lottos, WinningNumber winningNumber) { + + LottoResult lottoResult = new LottoResult(); + + for (Lotto lotto : lottos) { + Prize prize = getPrize(lotto, winningNumber.getWinningNumbers(), winningNumber.getBonusNumber()); + lottoResult.addPrize(prize); + } + + return lottoResult; + } + + private Prize getPrize(Lotto lotto, List winningNumbers, int bonusNumber) { + long matchCount = lotto.getNumbers().stream() + .filter(winningNumbers::contains) + .count(); + boolean matchBonus = lotto.getNumbers().contains(bonusNumber); + + return Prize.findPrize((int)matchCount, matchBonus); + } +} diff --git a/src/main/java/lotto/service/LottoPurchaseService.java b/src/main/java/lotto/service/LottoPurchaseService.java new file mode 100644 index 0000000000..221e449ab7 --- /dev/null +++ b/src/main/java/lotto/service/LottoPurchaseService.java @@ -0,0 +1,37 @@ +package lotto.service; + +import static lotto.constants.LottoConstantNumbers.*; + +import java.util.List; + +import lotto.domain.Lotto; +import lotto.factory.LottoTicketStore; +import lotto.view.InputView; +import lotto.view.OutputView; + +public class LottoPurchaseService { + + private final InputView inputView; + private final OutputView outputView; + private final LottoTicketStore lottoTicketStore; + + public LottoPurchaseService(InputView inputView, OutputView outputView, LottoTicketStore lottoTicketStore) { + this.inputView = inputView; + this.outputView = outputView; + this.lottoTicketStore = lottoTicketStore; + } + + public int purchaseForLottos() { + return inputView.getPurchaseAmount(); + } + + public List buyLottos(int purchaseAmount) { + int purchaseCount = purchaseAmount / LOTTO_PRICE.getValue(); + + outputView.printPurchaseCountMessage(purchaseCount); + List lottos = lottoTicketStore.createLottoTickets(purchaseCount); + outputView.printLottoList(lottos); + + return lottos; + } +} diff --git a/src/main/java/lotto/service/LottoResultCalculator.java b/src/main/java/lotto/service/LottoResultCalculator.java new file mode 100644 index 0000000000..3bb964073f --- /dev/null +++ b/src/main/java/lotto/service/LottoResultCalculator.java @@ -0,0 +1,39 @@ +package lotto.service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lotto.domain.Lotto; +import lotto.domain.Prize; +import lotto.domain.WinningNumber; + +public class LottoResultCalculator { + + public Map calculateResults(List lottos, WinningNumber winningNumber) { + Map prizeCountMap = initializePrizeCount(); + + for (Lotto lotto : lottos) { + int matchCount = getMatchCount(lotto.getNumbers(), winningNumber.getWinningNumbers()); + boolean hasBonus = lotto.getNumbers().contains(winningNumber.getBonusNumber()); + Prize prize = Prize.findPrize(matchCount, hasBonus); + prizeCountMap.put(prize, prizeCountMap.get(prize) + 1); + } + + return prizeCountMap; + } + + private Map initializePrizeCount() { + Map prizeCountMap = new HashMap<>(); + for (Prize prize : Prize.values()) { + prizeCountMap.put(prize, 0); + } + return prizeCountMap; + } + + private int getMatchCount(List lottoNumbers, List winningNumbers) { + return (int)lottoNumbers.stream() + .filter(winningNumbers::contains) + .count(); + } +} diff --git a/src/main/java/lotto/service/LottoStatisticsService.java b/src/main/java/lotto/service/LottoStatisticsService.java new file mode 100644 index 0000000000..4ff0563e9d --- /dev/null +++ b/src/main/java/lotto/service/LottoStatisticsService.java @@ -0,0 +1,38 @@ +package lotto.service; + +import java.util.List; +import java.util.Map; + +import lotto.domain.Lotto; +import lotto.domain.Prize; +import lotto.domain.WinningNumber; +import lotto.view.OutputView; + +public class LottoStatisticsService { + private final LottoResultCalculator lottoResultCalculator; + private final OutputView outputView; + + public LottoStatisticsService(LottoResultCalculator lottoResultCalculator, OutputView outputView) { + this.lottoResultCalculator = lottoResultCalculator; + this.outputView = outputView; + } + + public void summarizeStatistics(int purchaseAmount, List lottos, WinningNumber winningNumber) { + Map prizeCounts = lottoResultCalculator.calculateResults(lottos, winningNumber); + outputView.printPrizeStatistics(prizeCounts); + + long totalEarnings = calculateTotalEarnings(prizeCounts); + double totalYield = calculateTotalYield(purchaseAmount, totalEarnings); + outputView.printTotalYield(totalYield); + } + + private long calculateTotalEarnings(Map prizeCounts) { + return prizeCounts.entrySet().stream() + .mapToLong(entry -> (long)entry.getKey().getPrizeAmount() * entry.getValue()) + .sum(); + } + + private double calculateTotalYield(int purchaseAmount, long totalEarnings) { + return (double)totalEarnings / purchaseAmount * 100; + } +} diff --git a/src/main/java/lotto/util/DuplicateValidator.java b/src/main/java/lotto/util/DuplicateValidator.java new file mode 100644 index 0000000000..08c4b15587 --- /dev/null +++ b/src/main/java/lotto/util/DuplicateValidator.java @@ -0,0 +1,24 @@ +package lotto.util; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import lotto.exception.lottoticketexception.DuplicateException; + +public class DuplicateValidator { + public List validateNoDuplicates(List numbers) { + Set uniqueNumbers = new HashSet<>(numbers); + + if (uniqueNumbers.size() != numbers.size()) { + throw new DuplicateException(); + } + return uniqueNumbers.stream().toList(); + } + + public void validateNoOverlapWithWinningNumbers(int bonusNumber, List winningNumbers) { + if (winningNumbers.contains(bonusNumber)) { + throw new DuplicateException(); + } + } +} diff --git a/src/main/java/lotto/util/LottoNumberSorter.java b/src/main/java/lotto/util/LottoNumberSorter.java new file mode 100644 index 0000000000..c096c19d18 --- /dev/null +++ b/src/main/java/lotto/util/LottoNumberSorter.java @@ -0,0 +1,11 @@ +package lotto.util; + +import java.util.Collections; +import java.util.List; + +public class LottoNumberSorter { + public List sortInAscendingOrder(List numbers) { + Collections.sort(numbers); + return numbers; + } +} diff --git a/src/main/java/lotto/util/NumberValidator.java b/src/main/java/lotto/util/NumberValidator.java new file mode 100644 index 0000000000..71610e5d11 --- /dev/null +++ b/src/main/java/lotto/util/NumberValidator.java @@ -0,0 +1,18 @@ +package lotto.util; + +import lotto.exception.purchaseamountexception.InvalidPurchaseAmountException; + +public class NumberValidator { + + public int parseStringToInt(String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new InvalidPurchaseAmountException(); + } + } + + public boolean isNegativeNumber(int purchaseAmount) { + return purchaseAmount < 0; + } +} diff --git a/src/main/java/lotto/util/PurchaseAmountValidator.java b/src/main/java/lotto/util/PurchaseAmountValidator.java new file mode 100644 index 0000000000..a73214ce08 --- /dev/null +++ b/src/main/java/lotto/util/PurchaseAmountValidator.java @@ -0,0 +1,37 @@ +package lotto.util; + +import lotto.constants.LottoConstantNumbers; +import lotto.exception.purchaseamountexception.InvalidPurchaseAmountException; +import lotto.exception.purchaseamountexception.MaxPurchaseExceedException; +import lotto.exception.purchaseamountexception.NotDivisibleByLottoPriceException; + +public class PurchaseAmountValidator { + + private final NumberValidator numberValidator; + + public PurchaseAmountValidator(NumberValidator numberValidator) { + this.numberValidator = numberValidator; + } + + public int validateInput(String input) { + int parsedInput = numberValidator.parseStringToInt(input); + if (numberValidator.isNegativeNumber(parsedInput)) { + throw new InvalidPurchaseAmountException(); + } + if (!canDivideWithPrice(parsedInput)) { + throw new NotDivisibleByLottoPriceException(); + } + if (isExceedMaxPurchaseAmount(parsedInput)) { + throw new MaxPurchaseExceedException(); + } + return parsedInput; + } + + private boolean canDivideWithPrice(int purchaseAmount) { + return purchaseAmount % LottoConstantNumbers.LOTTO_PRICE.getValue() == 0; + } + + private boolean isExceedMaxPurchaseAmount(int purchaseAmount) { + return purchaseAmount > LottoConstantNumbers.MAX_PURCHASE_AMOUNT.getValue(); + } +} diff --git a/src/main/java/lotto/util/RandomNumberGenerator.java b/src/main/java/lotto/util/RandomNumberGenerator.java new file mode 100644 index 0000000000..efe1dae2a7 --- /dev/null +++ b/src/main/java/lotto/util/RandomNumberGenerator.java @@ -0,0 +1,14 @@ +package lotto.util; + +import static lotto.constants.LottoConstantNumbers.*; + +import java.util.List; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomNumberGenerator { + public List pickLottoNumbers() { + return Randoms.pickUniqueNumbersInRange(MIN_LOTTO_NUMBER.getValue(), MAX_LOTTO_NUMBER.getValue(), + LOTTO_NUMBERS_COUNT.getValue()); + } +} diff --git a/src/main/java/lotto/util/WinningNumberSeparator.java b/src/main/java/lotto/util/WinningNumberSeparator.java new file mode 100644 index 0000000000..989ec1bced --- /dev/null +++ b/src/main/java/lotto/util/WinningNumberSeparator.java @@ -0,0 +1,15 @@ +package lotto.util; + +import java.util.Arrays; +import java.util.List; + +public class WinningNumberSeparator { + + public List splitByComma(String input) { + List numbers = Arrays.stream(input.split(",")) + .map(String::trim) + .map(Integer::parseInt) + .toList(); + return numbers; + } +} diff --git a/src/main/java/lotto/view/ConsoleMessage.java b/src/main/java/lotto/view/ConsoleMessage.java new file mode 100644 index 0000000000..57c233e2f4 --- /dev/null +++ b/src/main/java/lotto/view/ConsoleMessage.java @@ -0,0 +1,26 @@ +package lotto.view; + +public enum ConsoleMessage { + INPUT_PURCHASE_AMOUNT("κ΅¬μž…κΈˆμ•‘μ„ μž…λ ₯ν•΄ μ£Όμ„Έμš”."), + PURCHASE_COUNT_MESSAGE("%d개λ₯Ό κ΅¬λ§€ν–ˆμŠ΅λ‹ˆλ‹€."), + INPUT_WINNING_NUMBERS("당첨 번호λ₯Ό μž…λ ₯ν•΄ μ£Όμ„Έμš”."), + INPUT_BONUS_NUMBER("λ³΄λ„ˆμŠ€ 번호λ₯Ό μž…λ ₯ν•΄ μ£Όμ„Έμš”."), + WINNING_STATISTICS("당첨 톡계"), + DIVIDER("---"), + MATCH_3("3개 일치 (5,000원) - %d개"), + MATCH_4("4개 일치 (50,000원) - %d개"), + MATCH_5("5개 일치 (1,500,000원) - %d개"), + MATCH_5_BONUS("5개 일치, λ³΄λ„ˆμŠ€ λ³Ό 일치 (30,000,000원) - %d개"), + MATCH_6("6개 일치 (2,000,000,000원) - %d개"), + TOTAL_YIELD("총 수읡λ₯ μ€ %.2f%%μž…λ‹ˆλ‹€."); + + private final String message; + + ConsoleMessage(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 0000000000..5381bab31e --- /dev/null +++ b/src/main/java/lotto/view/InputView.java @@ -0,0 +1,92 @@ +package lotto.view; + +import java.util.List; + +import camp.nextstep.edu.missionutils.Console; +import lotto.exception.lottoticketexception.DuplicateException; +import lotto.exception.lottoticketexception.LottoNumberSizeException; +import lotto.exception.numberexception.OutOfRangeNumberException; +import lotto.exception.purchaseamountexception.InvalidPurchaseAmountException; +import lotto.exception.purchaseamountexception.MaxPurchaseExceedException; +import lotto.exception.purchaseamountexception.NegativePurchaseAmountException; +import lotto.exception.purchaseamountexception.NotDivisibleByLottoPriceException; +import lotto.util.DuplicateValidator; +import lotto.util.PurchaseAmountValidator; +import lotto.util.WinningNumberSeparator; + +public class InputView { + + private final OutputView outputView; + private final PurchaseAmountValidator purchaseAmountValidator; + private final WinningNumberSeparator winningNumberSeparator; + private final DuplicateValidator duplicateValidator; + + public InputView(OutputView outputView, PurchaseAmountValidator purchaseAmountValidator, + WinningNumberSeparator winningNumberSeparator, DuplicateValidator duplicateValidator) { + this.outputView = outputView; + this.purchaseAmountValidator = purchaseAmountValidator; + this.winningNumberSeparator = winningNumberSeparator; + this.duplicateValidator = duplicateValidator; + } + + public String readLine() { + return Console.readLine(); + } + + public int getPurchaseAmount() { + while (true) { + outputView.printPurchaseAmountMessage(); + String input = readLine(); + + try { + return purchaseAmountValidator.validateInput(input); + } catch (InvalidPurchaseAmountException e) { + outputView.printInvalidPurchaseAmountMessage(); + } catch (NegativePurchaseAmountException e) { + outputView.printInvalidPurchaseAmountMessage(); + } catch (NotDivisibleByLottoPriceException e) { + outputView.printNotDivisibleByLottoPriceMessage(); + } catch (MaxPurchaseExceedException e) { + outputView.printMaxPurchaseExceedMessage(); + } + } + } + + public List getWinningNumbers() { + while (true) { + outputView.printToGetWinningNumbers(); + String input = readLine(); + List numbers = winningNumberSeparator.splitByComma(input); + try { + return duplicateValidator.validateNoDuplicates(numbers); + } catch (NumberFormatException e) { + outputView.printOutOfRangeNumberMessage(); + } catch (LottoNumberSizeException e) { + outputView.printInvalidLottoNumberSizeMessage(); + } catch (OutOfRangeNumberException e) { + outputView.printOutOfRangeNumberMessage(); + } catch (DuplicateException e) { + outputView.printDuplicateNumberMessage(); + } + } + } + + public int getBonusNumber(List winningNumbers) { + while (true) { + outputView.printToGetBonusNumbers(); + String input = readLine(); + + try { + int bonusNumber = Integer.parseInt(input); + duplicateValidator.validateNoOverlapWithWinningNumbers(bonusNumber, winningNumbers); + return bonusNumber; + } catch (NumberFormatException e) { + outputView.printOutOfRangeNumberMessage(); + } catch (OutOfRangeNumberException e) { + outputView.printOutOfRangeNumberMessage(); + } catch (DuplicateException e) { + outputView.printDuplicateNumberMessage(); + } + } + } +} diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java new file mode 100644 index 0000000000..6eeb0411a7 --- /dev/null +++ b/src/main/java/lotto/view/OutputView.java @@ -0,0 +1,83 @@ +package lotto.view; + +import static lotto.constants.LottoConstantNumbers.*; +import static lotto.exception.ErrorMessage.*; +import static lotto.view.ConsoleMessage.*; + +import java.util.List; +import java.util.Map; + +import lotto.domain.Lotto; +import lotto.domain.Prize; + +public class OutputView { + + public void printPurchaseAmountMessage() { + System.out.println(INPUT_PURCHASE_AMOUNT.getMessage()); + } + + public void printInvalidPurchaseAmountMessage() { + System.out.println(INVALID_PURCHASE_AMOUNT.getMessage()); + } + + public void printNotDivisibleByLottoPriceMessage() { + System.out.println(NOT_DIVISIBLE_BY_LOTTO_PRICE.getMessage()); + } + + public void printMaxPurchaseExceedMessage() { + System.out.println(MAX_PURCHASE_EXCEED.getMessage()); + } + + public void printInvalidLottoNumberSizeMessage() { + System.out.println(String.format(INVALID_LOTTO_NUMBERS_COUNT.getMessage(), LOTTO_NUMBERS_COUNT)); + } + + public void printPurchaseCountMessage(int purchaseCount) { + System.out.println(String.format(PURCHASE_COUNT_MESSAGE.getMessage(), purchaseCount)); + } + + public void printOutOfRangeNumberMessage() { + System.out.println(String.format(OUT_OF_RANGE_NUMBER.getMessage(), MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER)); + } + + public void printToGetWinningNumbers() { + System.out.println(INPUT_WINNING_NUMBERS.getMessage()); + } + + public void printDuplicateNumberMessage() { + System.out.println(DUPLICATE_NUMBER.getMessage()); + } + + public void printToGetBonusNumbers() { + System.out.println(INPUT_BONUS_NUMBER.getMessage()); + } + + public void printLottoList(List totalLottos) { + for (Lotto lotto : totalLottos) { + System.out.println(lotto.toString()); + } + } + + public void printPrizeStatistics(Map prizeCounts) { + StringBuilder statisticBuilder = new StringBuilder(); + statisticBuilder.append(WINNING_STATISTICS.getMessage()) + .append('\n') + .append(DIVIDER.getMessage()) + .append('\n') + .append(String.format(ConsoleMessage.MATCH_3.getMessage(), prizeCounts.get(Prize.FIFTH))) + .append('\n') + .append(String.format(ConsoleMessage.MATCH_4.getMessage(), prizeCounts.get(Prize.FOURTH))) + .append('\n') + .append(String.format(ConsoleMessage.MATCH_5.getMessage(), prizeCounts.get(Prize.THIRD))) + .append('\n') + .append(String.format(ConsoleMessage.MATCH_5_BONUS.getMessage(), prizeCounts.get(Prize.SECOND))) + .append('\n') + .append(String.format(ConsoleMessage.MATCH_6.getMessage(), prizeCounts.get(Prize.FIRST))) + .append('\n'); + System.out.print(statisticBuilder); + } + + public void printTotalYield(double yield) { + System.out.println(String.format(TOTAL_YIELD.getMessage(), yield)); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 309f4e50ae..17c15ac618 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -1,25 +1,27 @@ package lotto; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; import java.util.List; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import lotto.domain.Lotto; class LottoTest { - @Test - void 둜또_번호의_κ°œμˆ˜κ°€_6κ°œκ°€_λ„˜μ–΄κ°€λ©΄_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) - .isInstanceOf(IllegalArgumentException.class); - } + @Test + void 둜또_번호의_κ°œμˆ˜κ°€_6κ°œκ°€_λ„˜μ–΄κ°€λ©΄_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) + .isInstanceOf(IllegalArgumentException.class); + } - @DisplayName("둜또 λ²ˆν˜Έμ— μ€‘λ³΅λœ μˆ«μžκ°€ 있으면 μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€.") - @Test - void 둜또_λ²ˆν˜Έμ—_μ€‘λ³΅λœ_μˆ«μžκ°€_있으면_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) - .isInstanceOf(IllegalArgumentException.class); - } + @DisplayName("둜또 λ²ˆν˜Έμ— μ€‘λ³΅λœ μˆ«μžκ°€ 있으면 μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€.") + @Test + void 둜또_λ²ˆν˜Έμ—_μ€‘λ³΅λœ_μˆ«μžκ°€_있으면_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) + .isInstanceOf(IllegalArgumentException.class); + } - // TODO: μΆ”κ°€ κΈ°λŠ₯ κ΅¬ν˜„μ— λ”°λ₯Έ ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± + // TODO: μΆ”κ°€ κΈ°λŠ₯ κ΅¬ν˜„μ— λ”°λ₯Έ ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± }