diff --git a/README.md b/README.md index 5fa2560..32d00e9 100644 --- a/README.md +++ b/README.md @@ -1 +1,160 @@ # java-lotto-precourse + +## ๐Ÿš€ ๊ธฐ๋Šฅ ์š”๊ตฌ ์‚ฌํ•ญ + +๋กœ๋˜ ๊ฒŒ์ž„ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ๋กœ๋˜ ๊ฒŒ์ž„์€ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ทœ์น™์œผ๋กœ ์ง„ํ–‰๋œ๋‹ค +``` +๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ์ˆซ์ž ๋ฒ”์œ„๋Š” 1~45๊นŒ์ง€์ด๋‹ค. +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์› +``` + +- ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅํ•˜๋ฉด ๊ตฌ์ž… ๊ธˆ์•ก์— ํ•ด๋‹นํ•˜๋Š” ๋งŒํผ ๋กœ๋˜๋ฅผ ๋ฐœํ–‰ํ•ด์•ผ ํ•œ๋‹ค. +- ๋กœ๋˜ 1์žฅ์˜ ๊ฐ€๊ฒฉ์€ 1,000์›์ด๋‹ค. +- ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š”๋‹ค. +- ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๋งคํ•œ ๋กœ๋˜ ๋ฒˆํ˜ธ์™€ ๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋‹น์ฒจ ๋‚ด์—ญ ๋ฐ ์ˆ˜์ต๋ฅ ์„ ์ถœ๋ ฅํ•˜๊ณ  ๋กœ๋˜ ๊ฒŒ์ž„์„ ์ข…๋ฃŒํ•œ๋‹ค. +- ์‚ฌ์šฉ์ž๊ฐ€ ์ž˜๋ชป๋œ ๊ฐ’์„ ์ž…๋ ฅํ•  ๊ฒฝ์šฐ `IllegalArgumentException` ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ณ , "[ERROR]"๋กœ ์‹œ์ž‘ํ•˜๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅ ํ›„ ๊ทธ ๋ถ€๋ถ„๋ถ€ํ„ฐ ์ž…๋ ฅ์„ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค. + - `Exception`์ด ์•„๋‹Œ `IllegalArgumentException`, `IllegalStateException` ๋“ฑ๊ณผ ๊ฐ™์€ ๋ช…ํ™•ํ•œ ์œ ํ˜•์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. + +## ์ž…์ถœ๋ ฅ ์š”๊ตฌ ์‚ฌํ•ญ + +### ์ž…๋ ฅ + +๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. ๊ตฌ์ž… ๊ธˆ์•ก์€ 1,000์› ๋‹จ์œ„๋กœ ์ž…๋ ฅ ๋ฐ›์œผ๋ฉฐ 1,000์›์œผ๋กœ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌํ•œ๋‹ค. + +``` +14000 +``` + +๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. ๋ฒˆํ˜ธ๋Š” ์‰ผํ‘œ(,)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค. +``` +1,2,3,4,5,6 +``` + +๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. +``` +7 +``` + +### ์ถœ๋ ฅ + +๋ฐœํ–‰ํ•œ ๋กœ๋˜ ์ˆ˜๋Ÿ‰ ๋ฐ ๋ฒˆํ˜ธ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์—ฌ ๋ณด์—ฌ์ค€๋‹ค. + +``` +8๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค. +[8, 21, 23, 41, 42, 43] +[3, 5, 11, 16, 32, 38] +[7, 11, 16, 35, 36, 44] +[1, 8, 11, 31, 41, 42] +[13, 14, 16, 38, 42, 45] +[7, 11, 30, 40, 42, 43] +[2, 13, 22, 32, 38, 45] +[1, 3, 5, 14, 22, 45] +``` + +๋‹น์ฒจ ๋‚ด์—ญ์„ ์ถœ๋ ฅํ•œ๋‹ค. + +``` +3๊ฐœ ์ผ์น˜ (5,000์›) - 1๊ฐœ +4๊ฐœ ์ผ์น˜ (50,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜ (1,500,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - 0๊ฐœ +6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - 0๊ฐœ +``` + +์ˆ˜์ต๋ฅ ์€ ์†Œ์ˆ˜์  ๋‘˜์งธ ์ž๋ฆฌ์—์„œ ๋ฐ˜์˜ฌ๋ฆผํ•œ๋‹ค. (ex. 100.0%, 51.5%, 1,000,000.0%) +``` +์ด ์ˆ˜์ต๋ฅ ์€ 62.5%์ž…๋‹ˆ๋‹ค. +``` + +์˜ˆ์™ธ ์ƒํ™ฉ ์‹œ ์—๋Ÿฌ ๋ฌธ๊ตฌ๋ฅผ ์ถœ๋ ฅํ•ด์•ผ ํ•œ๋‹ค. ๋‹จ, ์—๋Ÿฌ ๋ฌธ๊ตฌ๋Š” "[ERROR]"๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค. +``` +[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์˜ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. +``` + +### ์‹คํ–‰ ๊ฒฐ๊ณผ ์˜ˆ์‹œ + +``` +๊ตฌ์ž…๊ธˆ์•ก์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. +8000 + +8๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค. +[8, 21, 23, 41, 42, 43] +[3, 5, 11, 16, 32, 38] +[7, 11, 16, 35, 36, 44] +[1, 8, 11, 31, 41, 42] +[13, 14, 16, 38, 42, 45] +[7, 11, 30, 40, 42, 43] +[2, 13, 22, 32, 38, 45] +[1, 3, 5, 14, 22, 45] + +๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. +1,2,3,4,5,6 + +๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. +7 + +๋‹น์ฒจ ํ†ต๊ณ„ + +3๊ฐœ ์ผ์น˜ (5,000์›) - 1๊ฐœ +4๊ฐœ ์ผ์น˜ (50,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜ (1,500,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - 0๊ฐœ +6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - 0๊ฐœ +์ด ์ˆ˜์ต๋ฅ ์€ 62.5%์ž…๋‹ˆ๋‹ค. +``` + +## ๐ŸŽฏ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์š”๊ตฌ ์‚ฌํ•ญ + +- JDK 17 ๋ฒ„์ „์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค. JDK 17์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ 0์  ์ฒ˜๋ฆฌํ•œ๋‹ค. +- ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰์˜ ์‹œ์ž‘์ ์€ `Application`์˜ `main()`์ด๋‹ค. +- `build.gradle` ํŒŒ์ผ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ณ , ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. +- Java ์ฝ”๋“œ ์ปจ๋ฒค์…˜ ๊ฐ€์ด๋“œ๋ฅผ ์ค€์ˆ˜ํ•˜๋ฉฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•œ๋‹ค. +- ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ ์‹œ `System.exit()`๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค. +- ํ”„๋กœ๊ทธ๋žจ ๊ตฌํ˜„์ด ์™„๋ฃŒ๋˜๋ฉด `ApplicationTest`์˜ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณตํ•ด์•ผ ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ 0์  ์ฒ˜๋ฆฌํ•œ๋‹ค. +- ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์š”๊ตฌ ์‚ฌํ•ญ์—์„œ ๋‹ฌ๋ฆฌ ๋ช…์‹œํ•˜์ง€ ์•Š๋Š” ํ•œ ํŒŒ์ผ, ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ด๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค. +- indent(์ธ๋ดํŠธ, ๋“ค์—ฌ์“ฐ๊ธฐ) depth๋ฅผ 3์ด ๋„˜์ง€ ์•Š๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค. 2๊นŒ์ง€๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. +- ํ•จ์ˆ˜(๋˜๋Š” ๋ฉ”์„œ๋“œ)์˜ ๊ธธ์ด๊ฐ€ 15๋ผ์ธ์„ ๋„˜์–ด๊ฐ€์ง€ ์•Š๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค. +- `else` ์˜ˆ์•ฝ์–ด๋ฅผ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. +- Java Enum์„ ์ ์šฉํ•œ๋‹ค. +- ๋„๋ฉ”์ธ ๋กœ์ง์— ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ๋‹จ, UI(`System.out`, `System.in`, `Scanner`) ๋กœ์ง์€ ์ œ์™ธํ•œ๋‹ค. +- ํ•ต์‹ฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š” ์ฝ”๋“œ์™€ UI๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋กœ์ง์„ ๋ถ„๋ฆฌํ•ด ๊ตฌํ˜„ํ•œ๋‹ค. + +## ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ + +- `camp.nextstep.edu.missionutils`์—์„œ ์ œ๊ณตํ•˜๋Š” `Randoms` ๋ฐ `Console` API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. + +List numbers = Randoms.pickUniqueNumbersInRange(1, 45, 6); + +## Lotto ํด๋ž˜์Šค + +์ œ๊ณต๋œ `Lotto` ํด๋ž˜์Šค๋ฅผ ํ™œ์šฉํ•ด ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. + +- `numbers`์˜ ์ ‘๊ทผ ์ œ์–ด์ž์ธ `private`์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค. +- `Lotto`์— ํ•„๋“œ(์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜)๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†๋‹ค. +- `Lotto`์˜ ํŒจํ‚ค์ง€ ๋ณ€๊ฒฝ์€ ๊ฐ€๋Šฅํ•˜๋‹ค. + +```java +public class Lotto { + private final List numbers; + + public Lotto(List numbers) { + validate(numbers); + this.numbers = numbers; + } + + private void validate(List numbers) { + if (numbers.size() != 6) { + throw new IllegalArgumentException(); + } + } + + // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ +} + diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922..26ccfb9 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,9 @@ package lotto; +import lotto.config.AppConfig; + public class Application { public static void main(String[] args) { - // TODO: ํ”„๋กœ๊ทธ๋žจ ๊ตฌํ˜„ + AppConfig.getLottoController().startGame(); } -} +} \ No newline at end of file diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java deleted file mode 100644 index 88fc5cf..0000000 --- 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/config/AppConfig.java b/src/main/java/lotto/config/AppConfig.java new file mode 100644 index 0000000..80ef6f4 --- /dev/null +++ b/src/main/java/lotto/config/AppConfig.java @@ -0,0 +1,19 @@ +package lotto.config; + +import lotto.controller.LottoController; +import lotto.service.LottoService; +import lotto.view.InputView; +import lotto.view.OutputView; + +public class AppConfig { + private static final InputView INPUT_VIEW = new InputView(); + private static final OutputView OUTPUT_VIEW = new OutputView(); + private static final LottoService LOTTO_SERVICE = new LottoService(); + + private static final LottoController LOTTO_CONTROLLER = + new LottoController(LOTTO_SERVICE, INPUT_VIEW, OUTPUT_VIEW); + + public static LottoController getLottoController() { + return LOTTO_CONTROLLER; + } +} diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java new file mode 100644 index 0000000..1f51110 --- /dev/null +++ b/src/main/java/lotto/controller/LottoController.java @@ -0,0 +1,38 @@ +package lotto.controller; + +import java.util.List; +import lotto.model.dto.LottoResultDto; +import lotto.model.domain.LottoTicket; +import lotto.model.domain.WinningLotto; +import lotto.service.LottoService; +import lotto.view.InputView; +import lotto.view.OutputView; + +public class LottoController { + private final LottoService lottoService; + private final InputView inputView; + private final OutputView outputView; + + public LottoController(LottoService lottoService, InputView inputView, OutputView outputView) { + this.lottoService = lottoService; + this.inputView = inputView; + this.outputView = outputView; + } + + public void startGame() { + int purchaseAmount = inputView.readPurchaseAmount(); + LottoTicket lottoTicket = lottoService.purchaseLottoTickets(purchaseAmount); + outputView.printLottoTickets(lottoTicket); + + WinningLotto winningLotto = getWinningLotto(); + LottoResultDto result = lottoService.calculateResults(lottoTicket, winningLotto); + outputView.printResults(result, purchaseAmount); + } + + private WinningLotto getWinningLotto() { + List winningNumbers = inputView.readWinningNumbers(); // ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ + int bonusNumber = inputView.readBonusNumber(winningNumbers); // ๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ๋„˜๊ฒจ์คŒ + return new WinningLotto(winningNumbers, bonusNumber); + } + +} diff --git a/src/main/java/lotto/enums/LottoPrice.java b/src/main/java/lotto/enums/LottoPrice.java new file mode 100644 index 0000000..2dfb087 --- /dev/null +++ b/src/main/java/lotto/enums/LottoPrice.java @@ -0,0 +1,15 @@ +package lotto.enums; + +public enum LottoPrice { + PRICE_PER_TICKET(1000); + + private final int price; + + LottoPrice(int price) { + this.price = price; + } + + public int getPrice() { + return price; + } +} diff --git a/src/main/java/lotto/enums/LottoRank.java b/src/main/java/lotto/enums/LottoRank.java new file mode 100644 index 0000000..4935d57 --- /dev/null +++ b/src/main/java/lotto/enums/LottoRank.java @@ -0,0 +1,44 @@ +package lotto.enums; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum LottoRank { + 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 static final int BONUS_CRITERIA = 5; + + private final int matchCount; + private final boolean requiresBonus; + private final int prize; + private static final Map RANK_MAP = Arrays.stream(values()) + .filter(rank -> !rank.requiresBonus) + .collect(Collectors.toMap(LottoRank::getMatchCount, rank -> rank)); + + LottoRank(int matchCount, boolean requiresBonus, int prize) { + this.matchCount = matchCount; + this.requiresBonus = requiresBonus; + this.prize = prize; + } + + public int getPrize() { + return prize; + } + + public int getMatchCount() { + return matchCount; + } + + public static LottoRank findRank(int matchCount, boolean hasBonus) { + if (hasBonus && matchCount == BONUS_CRITERIA) { + return SECOND; + } + return RANK_MAP.getOrDefault(matchCount, NONE); + } +} diff --git a/src/main/java/lotto/model/domain/Lotto.java b/src/main/java/lotto/model/domain/Lotto.java new file mode 100644 index 0000000..d49ef10 --- /dev/null +++ b/src/main/java/lotto/model/domain/Lotto.java @@ -0,0 +1,26 @@ +package lotto.model.domain; + +import java.util.HashSet; +import java.util.List; + +public class Lotto { + private final List numbers; + + public Lotto(List numbers) { + validate(numbers); + this.numbers = List.copyOf(numbers); // ๋ถˆ๋ณ€ ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜ + } + + private void validate(List numbers) { + if (numbers.size() != 6 || new HashSet<>(numbers).size() != 6) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + if (numbers.stream().anyMatch(num -> num < 1 || num > 45)) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์˜ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + + public List getNumbers() { + return numbers; + } +} diff --git a/src/main/java/lotto/model/domain/LottoTicket.java b/src/main/java/lotto/model/domain/LottoTicket.java new file mode 100644 index 0000000..7488ca2 --- /dev/null +++ b/src/main/java/lotto/model/domain/LottoTicket.java @@ -0,0 +1,31 @@ +package lotto.model.domain; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.Collectors; + +public class LottoTicket { + private final List tickets; + private static final int LOTTO_MIN_NUMBER = 1; // ๐ŸŽฏ ๋กœ๋˜ ๋ฒˆํ˜ธ ์ตœ์†Œ๊ฐ’ + private static final int LOTTO_MAX_NUMBER = 45; // ๐ŸŽฏ ๋กœ๋˜ ๋ฒˆํ˜ธ ์ตœ๋Œ€๊ฐ’ + private static final int LOTTO_SIZE = 6; + + public LottoTicket(int count) { + this.tickets = IntStream.range(0, count) + .mapToObj(i -> generateLotto()) + .collect(Collectors.toList()); + } + + private Lotto generateLotto() { + List numbers = new ArrayList<>(Randoms.pickUniqueNumbersInRange(LOTTO_MIN_NUMBER, LOTTO_MAX_NUMBER, LOTTO_SIZE)); + Collections.sort(numbers); + return new Lotto(numbers); + } + + public List getTickets() { + return tickets; + } +} diff --git a/src/main/java/lotto/model/domain/WinningLotto.java b/src/main/java/lotto/model/domain/WinningLotto.java new file mode 100644 index 0000000..8b21a7c --- /dev/null +++ b/src/main/java/lotto/model/domain/WinningLotto.java @@ -0,0 +1,24 @@ +package lotto.model.domain; + +import java.util.List; +import java.util.Set; + +public class WinningLotto { + private final Set winningNumbers; + private final int bonusNumber; + + public WinningLotto(List numbers, int bonusNumber) { + this.winningNumbers = Set.copyOf(numbers); + this.bonusNumber = bonusNumber; + } + + public int countMatchingNumbers(Lotto lotto) { + return (int) lotto.getNumbers().stream() + .filter(winningNumbers::contains) + .count(); + } + + public boolean hasBonusNumber(Lotto lotto) { + return lotto.getNumbers().contains(bonusNumber); + } +} \ No newline at end of file diff --git a/src/main/java/lotto/model/dto/LottoResultDto.java b/src/main/java/lotto/model/dto/LottoResultDto.java new file mode 100644 index 0000000..7eb9eeb --- /dev/null +++ b/src/main/java/lotto/model/dto/LottoResultDto.java @@ -0,0 +1,21 @@ +package lotto.model.dto; + +import java.util.EnumMap; +import java.util.Map; +import lotto.enums.LottoRank; +import lotto.utils.NumberUtil; + +public record LottoResultDto(Map resultMap) { + public LottoResultDto(Map resultMap) { + this.resultMap = new EnumMap<>(resultMap); + } + + public double getProfitRate(int purchaseAmount) { + int totalPrize = resultMap.entrySet().stream() + .mapToInt(entry -> entry.getKey().getPrize() * entry.getValue()) + .sum(); + + double profitRate = (double) totalPrize / purchaseAmount * 100; + return NumberUtil.roundToTwoDecimalPlaces(profitRate); + } +} \ No newline at end of file diff --git a/src/main/java/lotto/service/LottoService.java b/src/main/java/lotto/service/LottoService.java new file mode 100644 index 0000000..fb67c38 --- /dev/null +++ b/src/main/java/lotto/service/LottoService.java @@ -0,0 +1,25 @@ +package lotto.service; + +import lotto.enums.LottoPrice; +import lotto.model.dto.LottoResultDto; +import lotto.model.domain.LottoTicket; +import lotto.model.domain.WinningLotto; +import lotto.utils.LottoResultCalculator; + +public class LottoService { + public LottoTicket purchaseLottoTickets(int purchaseAmount) { + validatePurchaseAmount(purchaseAmount); + int ticketCount = purchaseAmount / LottoPrice.PRICE_PER_TICKET.getPrice(); + return new LottoTicket(ticketCount); + } + + private void validatePurchaseAmount(int purchaseAmount) { + if (purchaseAmount <= 0 || purchaseAmount % LottoPrice.PRICE_PER_TICKET.getPrice() != 0) { + throw new IllegalArgumentException("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 1,000์› ๋‹จ์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + + public LottoResultDto calculateResults(LottoTicket lottoTicket, WinningLotto winningLotto) { + return LottoResultCalculator.calculateResults(lottoTicket, winningLotto); + } +} diff --git a/src/main/java/lotto/utils/LottoResultCalculator.java b/src/main/java/lotto/utils/LottoResultCalculator.java new file mode 100644 index 0000000..4c7a9d8 --- /dev/null +++ b/src/main/java/lotto/utils/LottoResultCalculator.java @@ -0,0 +1,20 @@ +package lotto.utils; + +import lotto.enums.LottoRank; +import lotto.model.dto.LottoResultDto; +import lotto.model.domain.LottoTicket; +import lotto.model.domain.WinningLotto; + +import java.util.EnumMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class LottoResultCalculator { + public static LottoResultDto calculateResults(LottoTicket lottoTicket, WinningLotto winningLotto) { + Map results = lottoTicket.getTickets().stream() + .map(ticket -> LottoRank.findRank(winningLotto.countMatchingNumbers(ticket), winningLotto.hasBonusNumber(ticket))) + .collect(Collectors.groupingBy(rank -> rank, () -> new EnumMap<>(LottoRank.class), Collectors.summingInt(e -> 1))); + + return new LottoResultDto(results); + } +} diff --git a/src/main/java/lotto/utils/NumberUtil.java b/src/main/java/lotto/utils/NumberUtil.java new file mode 100644 index 0000000..af65b84 --- /dev/null +++ b/src/main/java/lotto/utils/NumberUtil.java @@ -0,0 +1,12 @@ +package lotto.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class NumberUtil { + public static double roundToTwoDecimalPlaces(double value) { + return new BigDecimal(value) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue(); + } +} diff --git a/src/main/java/lotto/validator/InputValidator.java b/src/main/java/lotto/validator/InputValidator.java new file mode 100644 index 0000000..f6c98d5 --- /dev/null +++ b/src/main/java/lotto/validator/InputValidator.java @@ -0,0 +1,66 @@ +package lotto.validator; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +public class InputValidator { + private static final int LOTTO_SIZE = 6; + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 45; + + public static int validatePurchaseAmount(String input) { + int amount = parseInteger(input); + if (amount <= 0 || amount % 1000 != 0) { + throw new IllegalArgumentException("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 1,000์› ๋‹จ์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + return amount; + } + + public static List validateWinningNumbers(String input) { + List numbers = parseIntegerList(input); + validateLottoNumbers(numbers); + return numbers; + } + + public static int validateBonusNumber(String input, List winningNumbers) { + int bonusNumber = parseInteger(input); + validateNumberRange(bonusNumber, "[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + if (winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋‹ฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + return bonusNumber; + } + + private static void validateLottoNumbers(List numbers) { + if (numbers.size() != LOTTO_SIZE || new HashSet<>(numbers).size() != LOTTO_SIZE) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + numbers.forEach(n -> validateNumberRange(n, "[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")); + } + + private static void validateNumberRange(int number, String errorMessage) { + if (number < MIN_NUMBER || number > MAX_NUMBER) { + throw new IllegalArgumentException(errorMessage); + } + } + + private static int parseInteger(String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] ์˜ฌ๋ฐ”๋ฅธ ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”."); + } + } + + private static List parseIntegerList(String input) { + try { + return Arrays.stream(input.split(",")) + .map(String::trim) + .map(Integer::parseInt) + .toList(); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] ์˜ฌ๋ฐ”๋ฅธ ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”."); + } + } +} \ No newline at end of file diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java new file mode 100644 index 0000000..cecff6b --- /dev/null +++ b/src/main/java/lotto/view/InputView.java @@ -0,0 +1,47 @@ +package lotto.view; + +import camp.nextstep.edu.missionutils.Console; +import lotto.validator.InputValidator; + +import java.util.List; + +public class InputView { + public int readPurchaseAmount() { + return getValidatedInput( + "๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.", + InputValidator::validatePurchaseAmount + ); + } + + public List readWinningNumbers() { + return getValidatedInput( + "๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.", + InputValidator::validateWinningNumbers + ); + } + + public int readBonusNumber(List winningNumbers) { + return getValidatedInput( + "๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.", + input -> InputValidator.validateBonusNumber(input, winningNumbers) + ); + } + + private T getValidatedInput(String message, InputFunction validator) { + while (true) { + try { + System.out.println(message); + T value = validator.apply(Console.readLine()); + System.out.println(); + return value; + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } + + @FunctionalInterface + private interface InputFunction { + R apply(T t); + } +} \ No newline at end of file diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java new file mode 100644 index 0000000..4c0edca --- /dev/null +++ b/src/main/java/lotto/view/OutputView.java @@ -0,0 +1,48 @@ +package lotto.view; + +import java.util.Comparator; +import java.util.stream.Stream; +import lotto.model.dto.LottoResultDto; +import lotto.enums.LottoRank; +import lotto.model.domain.LottoTicket; + +import java.text.NumberFormat; +import java.util.Locale; +import java.util.stream.Collectors; + +public class OutputView { + private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.KOREA); + + public void printLottoTickets(LottoTicket lottoTicket) { + System.out.printf("%d๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.%n", lottoTicket.getTickets().size()); + lottoTicket.getTickets().forEach(lotto -> System.out.println(lotto.getNumbers())); + System.out.println(); + } + + public void printResults(LottoResultDto result, int purchaseAmount) { + System.out.println("๋‹น์ฒจ ํ†ต๊ณ„"); + System.out.println("---"); + System.out.print(formatLottoRank(result)); + printProfitRate(result, purchaseAmount); + } + + private String formatLottoRank(LottoResultDto result) { + return Stream.of(LottoRank.values()) + .filter(rank -> rank != LottoRank.NONE) + .sorted(Comparator.comparingInt(LottoRank::getMatchCount)) + .map(rank -> formatLottoRankString(rank, result)) + .collect(Collectors.joining()); + } + + private String formatLottoRankString(LottoRank rank, LottoResultDto result) { + return String.format("%d๊ฐœ ์ผ์น˜%s (%s์›) - %d๊ฐœ%n", + rank.getMatchCount(), + rank == LottoRank.SECOND ? ", ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜" : "", + NUMBER_FORMAT.format(rank.getPrize()), + result.resultMap().getOrDefault(rank, 0)); + } + + private void printProfitRate(LottoResultDto result, int purchaseAmount) { + System.out.printf("์ด ์ˆ˜์ต๋ฅ ์€ %.1f%%์ž…๋‹ˆ๋‹ค.%n", result.getProfitRate(purchaseAmount)); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 309f4e5..ab43f7f 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -1,5 +1,6 @@ package lotto; +import lotto.model.domain.Lotto; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/lotto/enums/LottoRankTest.java b/src/test/java/lotto/enums/LottoRankTest.java new file mode 100644 index 0000000..684cc8e --- /dev/null +++ b/src/test/java/lotto/enums/LottoRankTest.java @@ -0,0 +1,17 @@ +package lotto.enums; + +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +class LottoRankTest { + + @Test + void ๋‹น์ฒจ_๋“ฑ์ˆ˜_์ฐพ๊ธฐ_ํ…Œ์ŠคํŠธ() { + assertThat(LottoRank.findRank(6, false)).isEqualTo(LottoRank.FIRST); + assertThat(LottoRank.findRank(5, true)).isEqualTo(LottoRank.SECOND); + assertThat(LottoRank.findRank(5, false)).isEqualTo(LottoRank.THIRD); + assertThat(LottoRank.findRank(4, false)).isEqualTo(LottoRank.FOURTH); + assertThat(LottoRank.findRank(3, false)).isEqualTo(LottoRank.FIFTH); + assertThat(LottoRank.findRank(2, false)).isEqualTo(LottoRank.NONE); + } +} diff --git a/src/test/java/lotto/service/LottoServiceTest.java b/src/test/java/lotto/service/LottoServiceTest.java new file mode 100644 index 0000000..cb9e3d0 --- /dev/null +++ b/src/test/java/lotto/service/LottoServiceTest.java @@ -0,0 +1,59 @@ +package lotto.service; + +import lotto.model.dto.LottoResultDto; +import lotto.model.domain.Lotto; +import lotto.model.domain.LottoTicket; +import lotto.model.domain.WinningLotto; +import lotto.enums.LottoRank; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class LottoServiceTest { + private LottoService lottoService; + + @BeforeEach + void setUp() { + lottoService = new LottoService(); + } + + @Test + void ๋กœ๋˜_ํ‹ฐ์ผ“_๊ตฌ๋งค_ํ…Œ์ŠคํŠธ() { + // given + int purchaseAmount = 5000; // 1000์› ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„์–ด 5๊ฐœ ๊ตฌ๋งค + + // when + LottoTicket lottoTicket = lottoService.purchaseLottoTickets(purchaseAmount); + + // then + assertThat(lottoTicket.getTickets()).hasSize(5); + } + + @Test + void ๋กœ๋˜_๊ฒฐ๊ณผ_๊ณ„์‚ฐ_ํ…Œ์ŠคํŠธ() { + // given + LottoTicket lottoTicket = new LottoTicket(2); // 2๊ฐœ์˜ ๋กœ๋˜ ํ‹ฐ์ผ“ ์ƒ์„ฑ + List tickets = List.of( + new Lotto(List.of(1, 2, 3, 4, 5, 6)), // ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋™์ผํ•œ ๋กœ๋˜ + new Lotto(List.of(10, 11, 12, 13, 14, 15)) // ๋‹น์ฒจ๋˜์ง€ ์•Š๋Š” ๋กœ๋˜ + ); + + // LottoTicket ๋‚ด๋ถ€์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ต์ฒดํ•˜์—ฌ ํ…Œ์ŠคํŠธ์— ๋งž์ถค + lottoTicket.getTickets().clear(); + lottoTicket.getTickets().addAll(tickets); + + WinningLotto winningLotto = new WinningLotto(List.of(1, 2, 3, 4, 5, 6), 7); + + // when + LottoResultDto getResultDto = lottoService.calculateResults(lottoTicket, winningLotto); + + // then + Map resultMap = getResultDto.resultMap(); + assertThat(resultMap.get(LottoRank.FIRST)).isEqualTo(1); // ์ฒซ ๋ฒˆ์งธ ๋กœ๋˜๊ฐ€ 6๊ฐœ ์ผ์น˜ + assertThat(resultMap.get(LottoRank.NONE)).isEqualTo(1); // ๋‘ ๋ฒˆ์งธ ๋กœ๋˜๋Š” ๋‹น์ฒจ๋˜์ง€ ์•Š์Œ + } +} diff --git a/src/test/java/lotto/validator/InputValidatorTest.java b/src/test/java/lotto/validator/InputValidatorTest.java new file mode 100644 index 0000000..7582d8b --- /dev/null +++ b/src/test/java/lotto/validator/InputValidatorTest.java @@ -0,0 +1,99 @@ +package lotto.validator; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +class InputValidatorTest { + + @Test + @DisplayName("์ •์ƒ์ ์ธ ๊ตฌ์ž… ๊ธˆ์•ก ๊ฒ€์ฆ") + void validatePurchaseAmount_valid() { + int amount = InputValidator.validatePurchaseAmount("5000"); + assertThat(amount).isEqualTo(5000); + } + + @Test + @DisplayName("๊ตฌ์ž… ๊ธˆ์•ก์ด 1000์› ๋‹จ์œ„๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validatePurchaseAmount_invalid() { + assertThatThrownBy(() -> InputValidator.validatePurchaseAmount("5500")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 1,000์› ๋‹จ์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฐ’์ด ์ž…๋ ฅ๋  ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validatePurchaseAmount_nonNumeric() { + assertThatThrownBy(() -> InputValidator.validatePurchaseAmount("abcd")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์˜ฌ๋ฐ”๋ฅธ ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”."); + } + + @Test + @DisplayName("์ •์ƒ์ ์ธ ๋‹น์ฒจ ๋ฒˆํ˜ธ ๊ฒ€์ฆ") + void validateWinningNumbers_valid() { + List numbers = InputValidator.validateWinningNumbers("1,2,3,4,5,6"); + assertThat(numbers).containsExactly(1, 2, 3, 4, 5, 6); + } + + @Test + @DisplayName("๋‹น์ฒจ ๋ฒˆํ˜ธ๊ฐ€ 6๊ฐœ๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validateWinningNumbers_invalidSize() { + assertThatThrownBy(() -> InputValidator.validateWinningNumbers("1,2,3,4,5")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("์ค‘๋ณต๋œ ๋‹น์ฒจ ๋ฒˆํ˜ธ๊ฐ€ ํฌํ•จ๋  ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validateWinningNumbers_duplicate() { + assertThatThrownBy(() -> InputValidator.validateWinningNumbers("1,2,3,4,5,5")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("1~45 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๋‹น์ฒจ ๋ฒˆํ˜ธ๊ฐ€ ํฌํ•จ๋  ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validateWinningNumbers_outOfRange() { + assertThatThrownBy(() -> InputValidator.validateWinningNumbers("1,2,3,4,5,46")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("์ •์ƒ์ ์ธ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ๊ฒ€์ฆ") + void validateBonusNumber_valid() { + List winningNumbers = List.of(1, 2, 3, 4, 5, 6); + int bonusNumber = InputValidator.validateBonusNumber("7", winningNumbers); + assertThat(bonusNumber).isEqualTo(7); + } + + @Test + @DisplayName("๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋  ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validateBonusNumber_duplicate() { + List winningNumbers = List.of(1, 2, 3, 4, 5, 6); + assertThatThrownBy(() -> InputValidator.validateBonusNumber("5", winningNumbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋‹ฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ 1~45 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚  ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validateBonusNumber_outOfRange() { + List winningNumbers = List.of(1, 2, 3, 4, 5, 6); + assertThatThrownBy(() -> InputValidator.validateBonusNumber("50", winningNumbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + @DisplayName("์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ž…๋ ฅ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ") + void validateBonusNumber_nonNumeric() { + List winningNumbers = List.of(1, 2, 3, 4, 5, 6); + assertThatThrownBy(() -> InputValidator.validateBonusNumber("abc", winningNumbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์˜ฌ๋ฐ”๋ฅธ ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”."); + } +} \ No newline at end of file