diff --git a/.gitignore b/.gitignore index 5dca701a77..910fcb8355 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ out/ ### Mac OS ### .DS_Store +/bin/ diff --git a/README.md b/README.md index d0286c859f..ea2dee4b99 100644 --- a/README.md +++ b/README.md @@ -1 +1,163 @@ -# java-racingcar-precourse +# ๐Ÿš— ์ž๋™์ฐจ ๊ฒฝ์ฃผ ๊ฒŒ์ž„ (Java RacingCar) + +## ๐Ÿงฉ ๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ +- ์ฃผ์–ด์ง„ ํšŸ์ˆ˜ ๋™์•ˆ n๋Œ€์˜ ์ž๋™์ฐจ๋Š” ์ „์ง„ ๋˜๋Š” ๋ฉˆ์ถœ ์ˆ˜ ์žˆ๋‹ค. +- ๊ฐ ์ž๋™์ฐจ์— ์ด๋ฆ„์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค. +- ์ „์ง„ํ•˜๋Š” ์ž๋™์ฐจ๋ฅผ ์ถœ๋ ฅํ•  ๋•Œ ์ž๋™์ฐจ ์ด๋ฆ„์„ ๊ฐ™์ด ์ถœ๋ ฅํ•œ๋‹ค. +- ์ž๋™์ฐจ ์ด๋ฆ„์€ ์‰ผํ‘œ(,) ๊ธฐ์ค€์œผ๋กœ ๊ตฌ๋ถ„ํ•˜๋ฉฐ, ์ด๋ฆ„์€ 5์ž ์ดํ•˜๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค. +- ์‚ฌ์šฉ์ž๋Š” ๋ช‡ ๋ฒˆ์˜ ์ด๋™์„ ํ•  ๊ฒƒ์ธ์ง€๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค. +- ์ „์ง„ ์กฐ๊ฑด: 0~9 ์‚ฌ์ด ๋ฌด์ž‘์œ„ ๊ฐ’ ์ค‘ **4 ์ด์ƒ์ผ ๊ฒฝ์šฐ ์ „์ง„**ํ•œ๋‹ค. +- ๊ฒŒ์ž„ ์ข…๋ฃŒ ํ›„ ๋ˆ„๊ฐ€ ์šฐ์Šนํ–ˆ๋Š”์ง€๋ฅผ ์•Œ๋ ค์ค€๋‹ค. (์šฐ์Šน์ž ๋‹ค์ˆ˜ ๊ฐ€๋Šฅ) +- ์ž˜๋ชป๋œ ์ž…๋ ฅ ์‹œ `IllegalArgumentException` ๋ฐœ์ƒ ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ. + +--- + +## ๐Ÿงฑ ํŒจํ‚ค์ง€ ๊ตฌ์กฐ +``` +src + โ”œโ”€โ”€ main + โ”‚ย ย  โ””โ”€โ”€ java + โ”‚ย ย  โ””โ”€โ”€ racingcar + โ”‚ย ย  โ”œโ”€โ”€ Application.java + โ”‚ย ย  โ”œโ”€โ”€ controller + โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ RacingController.java + โ”‚ย ย  โ”œโ”€โ”€ domain + โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ Car.java + โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ Cars.java + โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ RacingGame.java + โ”‚ย ย  โ”œโ”€โ”€ exception + โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ InvalidInputException.java + โ”‚ย ย  โ”œโ”€โ”€ utils + โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ RandomUtil.java + โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ Validator.java + โ”‚ย ย  โ””โ”€โ”€ views + โ”‚ย ย  โ”œโ”€โ”€ InputView.java + โ”‚ย ย  โ””โ”€โ”€ OutputView.java + โ””โ”€โ”€ test + โ””โ”€โ”€ java + โ””โ”€โ”€ racingcar + โ”œโ”€โ”€ ApplicationTest.java + โ”œโ”€โ”€ domain + โ”‚ย ย  โ”œโ”€โ”€ CarsTest.java + โ”‚ย ย  โ””โ”€โ”€ CarTest.java + โ””โ”€โ”€ utils + โ””โ”€โ”€ ValidatorTest.java +``` + +--- +## โœ… ๊ตฌํ˜„ํ•  ๊ธฐ๋Šฅ ๋ชฉ๋ก + +### ๐Ÿ“ฅ ์ž…๋ ฅ (InputView) +- [x] ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ **์ž๋™์ฐจ ์ด๋ฆ„ ๋ชฉ๋ก** ์ž…๋ ฅ๋ฐ›๊ธฐ + - [x] ์‰ผํ‘œ(`,`) ๊ธฐ์ค€์œผ๋กœ ๋ถ„๋ฆฌ + - [x] ๊ฐ ์ด๋ฆ„์˜ ๊ธธ์ด๊ฐ€ 1~5์ž์ธ์ง€ ๊ฒ€์ฆ + - [x] ์ค‘๋ณต ์ด๋ฆ„ ๋ฐฉ์ง€ +- [x] ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ **์‹œ๋„ํ•  ํšŸ์ˆ˜** ์ž…๋ ฅ๋ฐ›๊ธฐ + - [x] ์–‘์˜ ์ •์ˆ˜๋งŒ ํ—ˆ์šฉ + +--- + +### โš™๏ธ ๊ฒ€์ฆ (Validator) +- [x] ์ด๋ฆ„์ด 1~5์ž ๋ฒ”์œ„์ธ์ง€ ํ™•์ธ +- [x] ์ด๋ฆ„์— ๊ณต๋ฐฑ ํฌํ•จ ์—ฌ๋ถ€ ๊ฒ€์‚ฌ +- [x] ์ค‘๋ณต ์ด๋ฆ„ ํ™•์ธ +- [x] ์‹œ๋„ ํšŸ์ˆ˜๊ฐ€ ์ˆซ์ž์ธ์ง€, 1 ์ด์ƒ์ธ์ง€ ๊ฒ€์ฆ + +--- + +### ๐Ÿ ๋„๋ฉ”์ธ ๋กœ์ง (domain) +#### `Car` +- [x] ์ž๋™์ฐจ ์ด๋ฆ„๊ณผ ํ˜„์žฌ ์œ„์น˜ ์ •๋ณด ์ €์žฅ +- [x] ๋ฌด์ž‘์œ„ ๊ฐ’(0~9) ์ค‘ 4 ์ด์ƒ์ผ ๋•Œ ์ „์ง„ +- [x] ์ด๋™ ํ›„ ์ƒํƒœ ๋ฐ˜ํ™˜ + +#### `Cars` +- [x] ์—ฌ๋Ÿฌ ๋Œ€์˜ ์ž๋™์ฐจ ๊ด€๋ฆฌ +- [x] ๋ชจ๋“  ์ž๋™์ฐจ์˜ ์ด๋™ ๋กœ์ง ์ˆ˜ํ–‰ +- [x] ์ตœ๋Œ€ ์œ„์น˜(max position) ๊ณ„์‚ฐ +- [x] ์šฐ์Šน์ž ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜ + +#### `RacingGame` +- [x] ๊ฒŒ์ž„ ์ดˆ๊ธฐํ™” (์ž๋™์ฐจ ๋“ฑ๋ก, ์‹œ๋„ ํšŸ์ˆ˜ ์ž…๋ ฅ) +- [x] ๊ฐ ๋ผ์šด๋“œ๋งˆ๋‹ค ์ž๋™์ฐจ ์ด๋™ +- [x] ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ˆ„์  ๊ด€๋ฆฌ +- [x] ์ตœ์ข… ์šฐ์Šน์ž ๊ฒฐ์ • + +--- + +### ๐ŸŽฎ ์ปจํŠธ๋กค๋Ÿฌ (RacingController) +- [x] ๊ฒŒ์ž„ ์ „์ฒด ์‹คํ–‰ ํ๋ฆ„ ์ œ์–ด + - [x] ์ž…๋ ฅ ๋ฐ›๊ธฐ โ†’ ๊ฒ€์ฆ โ†’ ๊ฒฝ์ฃผ ์ง„ํ–‰ โ†’ ์ถœ๋ ฅ +- [x] ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ +- [x] `IllegalArgumentException` ์žฌ์ „๋‹ฌ (ํ…Œ์ŠคํŠธ ํ†ต๊ณผ์šฉ) + +--- + +### ๐Ÿ–จ ์ถœ๋ ฅ (OutputView) +- [x] ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ผ์šด๋“œ๋ณ„๋กœ ์ถœ๋ ฅ +- [x] ์šฐ์Šน์ž(๋‹จ๋… ๋˜๋Š” ๊ณต๋™) ์•ˆ๋‚ด ๋ฌธ๊ตฌ ์ถœ๋ ฅ +- [x] ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ `[ERROR]` ํ˜•ํƒœ๋กœ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ + +--- + +### ๐Ÿ”ข ๋žœ๋ค ์œ ํ‹ธ (RandomUtil) +- [x] `camp.nextstep.edu.missionutils.Randoms.pickNumberInRange(0, 9)` ์‚ฌ์šฉ +- [x] 4 ์ด์ƒ์ผ ๋•Œ ์ž๋™์ฐจ ์ด๋™ ์ฒ˜๋ฆฌ + +--- + +### ๐Ÿงช ์˜ˆ์™ธ ์ฒ˜๋ฆฌ (exception) +- [x] ์ž˜๋ชป๋œ ์ž…๋ ฅ ์‹œ `InvalidInputException` ๋ฐœ์ƒ +- [x] ๋ฉ”์‹œ์ง€๋Š” `[ERROR] ...` ํ˜•ํƒœ๋กœ ์ถœ๋ ฅ +- [x] `IllegalArgumentException`์œผ๋กœ ๊ฐ์‹ธ ์žฌ๋˜์ง€๊ธฐ + +--- + +### ๐Ÿงฐ ํ…Œ์ŠคํŠธ (ApplicationTest) +- [x] ์ •์ƒ ์ž…๋ ฅ ์‹œ ๊ฒฐ๊ณผ ๊ฒ€์ฆ +- [x] ์ž˜๋ชป๋œ ์ด๋ฆ„(6์ž ์ด์ƒ) ์ž…๋ ฅ ์‹œ `IllegalArgumentException` ๊ฒ€์ฆ +- [x] Random ๊ฐ’ Mocking (`assertRandomNumberInRangeTest`)์œผ๋กœ ๊ฒฐ๊ณผ ์ œ์–ด +- [x] ๊ณต๋™ ์šฐ์Šน์ž ๊ฒ€์ฆ (`pobi, jun`) + +--- + +## ๐Ÿ’ก ์‹คํ–‰ ์˜ˆ์‹œ +``` +๊ฒฝ์ฃผํ•  ์ž๋™์ฐจ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”. (์ด๋ฆ„์€ ์‰ผํ‘œ(,) ๊ธฐ์ค€์œผ๋กœ ๊ตฌ๋ถ„ +doyi,juni,yami +์‹œ๋„ํ•  ํšŸ์ˆ˜๋Š” ๋ช‡ ํšŒ์ธ๊ฐ€์š”? +2 + +์‹คํ–‰ ๊ฒฐ๊ณผ +doyi : +juni : - +yami : - + +doyi : +juni : -- +yami : -- + + +์ตœ์ข… ์šฐ์Šน์ž : juni, yami +``` + +--- + +## ๐Ÿงพ ์ฐธ๊ณ  +- Java 21 +- Gradle 8.7 +- JUnit 5 +- camp.nextstep.edu.missionutils + - Console: ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ฒ˜๋ฆฌ (`readLine()`) + - Randoms: ๋ฌด์ž‘์œ„ ์ˆซ์ž ์ƒ์„ฑ (`pickNumberInRange(0, 9)`) + +--- + +## ๐Ÿ’ป ํšŒ๊ณ  +์ง€๋‚œ ๋‹ค๋ฅธ ๋ถ„๋“ค์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ๋ถ„๋ฆฌํ•˜์—ฌ +ํ•œ ํด๋ž˜์Šค๋‹น ํ•œ ๊ธฐ๋Šฅ์ด ๋˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค. +๋” ๋ถ„๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์•Œ๊ณ ์‹ถ๊ธฐ์— ๋‹ค๋ฅธ ๋ถ„๋“ค์˜ ์ฝ”๋“œ๋„ ๋ฌด์ฒ™ ๊ธฐ๋Œ€๊ฐ€ ๋œ๋‹ค. + +๋˜ํ•œ, ์ง€๋‚œ ์‹œ๊ฐ„์— Stream์„ ์‚ฌ์šฉํ•˜์…”์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์‹  ๋ถ„์ด ๊ณ„์‹œ๋‹ค๊ณ  ํ•˜์…จ๋Š”๋ฐ +์ด๋•Œ๋Š” Stream์ด๋ž€ ๊ฒƒ์„ ๋ชฐ๋ผ์„œ ์™œ ์“ฐ๋Š” ๊ฑด์ง€ ์‹ถ์—ˆ๋‹ค. +์ด๋ฒˆ 2์ฃผ์ฐจ์— Stream์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ์ด๋ฅผ 2์ฃผ์ฐจ์— ์ ์šฉํ•ด๋ณด์•˜๋‹ค. diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..c8b34e2355 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,10 @@ package racingcar; +import racingcar.controller.RacingController; + public class Application { public static void main(String[] args) { - // TODO: ํ”„๋กœ๊ทธ๋žจ ๊ตฌํ˜„ + RacingController racingController = new RacingController(); + racingController.run(); } } diff --git a/src/main/java/racingcar/controller/RacingController.java b/src/main/java/racingcar/controller/RacingController.java new file mode 100644 index 0000000000..c9a5167de7 --- /dev/null +++ b/src/main/java/racingcar/controller/RacingController.java @@ -0,0 +1,34 @@ +package racingcar.controller; + +import java.util.List; + +import racingcar.domain.RacingGame; +import racingcar.views.InputView; +import racingcar.views.OutputView; + +public class RacingController { + private final InputView inputView; + private final OutputView outputView; + private final RacingGame racingGame; + + public RacingController() { + this.inputView = new InputView(); + this.outputView = new OutputView(); + this.racingGame = new RacingGame(); + } + + public void run() { + try { + List carNames = inputView.readCarNames(); + int attemptCount = inputView.readAttemptCount(); + + racingGame.initialize(carNames, attemptCount); + racingGame.start(outputView); + + } catch (IllegalArgumentException e) { + System.out.println("[ERROR] " + e.getMessage()); + throw e; + } + } + +} diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java new file mode 100644 index 0000000000..53cadf2cad --- /dev/null +++ b/src/main/java/racingcar/domain/Car.java @@ -0,0 +1,30 @@ +package racingcar.domain; + + +//ํ•œ๋Œ€์˜ Car ๋ฐ ์ด๋™ ๋กœ์ง +public class Car { + private final String name; + private int position; + + public Car(String name) { + this.name = name; + this.position = 0; + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + //4์ด์ƒ์ด ๋‚˜์˜ฌ๋•Œ๋งŒ ์ „์ง„ > ์ดํ•˜ ๋‚˜์˜ฌ ๊ฒฝ์šฐ ์ „์ง„ํ•  ์ˆ˜ ์—†์Œ + public void move(int randomNumber) { + if (randomNumber >= 4) { + position ++; + } + } +} + + diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java new file mode 100644 index 0000000000..e256d7d161 --- /dev/null +++ b/src/main/java/racingcar/domain/Cars.java @@ -0,0 +1,53 @@ +package racingcar.domain; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import racingcar.utils.RandomUtil; + +//์—ฌ๋Ÿฌ ๋Œ€์˜ Cars ๊ด€๋ฆฌ ๋กœ์ง +public class Cars { + private final List cars; + + public List getCars() { + return cars; + } + + public Cars(List carNames) { + this.cars = new ArrayList<>(); + for (String name : carNames) { + cars.add(new Car(name)); + } + } + + //๋ชจ๋“  ์ž๋™์ฐจ ์ด๋™ ๋กœ์ง ์‹œ๋„ + public void moveAll() { + for(Car car : cars) { + int randomNumber = RandomUtil.getRandomNumber(); + car.move(randomNumber); + } + } + + + //ํ˜„์žฌ ์ตœ๊ณ  ์œ„์น˜ + private int getMaxPosition() { + return cars.stream() + .map(Car::getPosition) + .max(Comparator.naturalOrder()) + .orElse(0); + } + + //ํ˜„์žฌ ์ตœ๊ณ  ์œ„์น˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์šฐ์Šน์ž ๋ฆฌ์ŠคํŠธ Return + public List getWinners() { + int maxPosition = getMaxPosition(); + + return cars.stream() + .filter(car->car.getPosition()==maxPosition) + .map(Car::getName) + .toList(); + } + + + +} diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java new file mode 100644 index 0000000000..30b534e978 --- /dev/null +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -0,0 +1,37 @@ +package racingcar.domain; + +import java.util.List; + +import racingcar.views.OutputView; + +//์ „์ฒด ๊ฒŒ์ž„์˜ ์ง„ํ–‰ ์ œ์–ด ๋กœ์ง ๊ตฌํ˜„ +public class RacingGame { + + private Cars cars; + private int attemptCount; + + + //์ด๋ฆ„ ์‹œ๋„ํšŸ์ˆ˜ ์ดˆ๊ธฐํ™” + public void initialize(List carNames, int attemptCount) { + this.cars = new Cars(carNames); + this.attemptCount = attemptCount; + } + + public void start(OutputView outputView) { + outputView.printStartMessage(); + + for (int i = 0; i < attemptCount; i++) { + cars.moveAll(); + printRoundResult(outputView); + System.out.println(); + } + + outputView.printWinners(cars.getWinners()); + } + + private void printRoundResult(OutputView outputView) { + for(Car car : cars.getCars()) { + outputView.printRoundResult(car.getName(), car.getPosition()); + } + } +} diff --git a/src/main/java/racingcar/exception/InvalidInputException.java b/src/main/java/racingcar/exception/InvalidInputException.java new file mode 100644 index 0000000000..75f6a88f66 --- /dev/null +++ b/src/main/java/racingcar/exception/InvalidInputException.java @@ -0,0 +1,9 @@ +package racingcar.exception; + + +//์˜ˆ์™ธ์ฒ˜๋ฆฌ +public class InvalidInputException extends IllegalArgumentException { + public InvalidInputException(String message) { + super(message); + } +} diff --git a/src/main/java/racingcar/utils/RandomUtil.java b/src/main/java/racingcar/utils/RandomUtil.java new file mode 100644 index 0000000000..3981ee56af --- /dev/null +++ b/src/main/java/racingcar/utils/RandomUtil.java @@ -0,0 +1,9 @@ +package racingcar.utils; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomUtil { + public static int getRandomNumber() { + return Randoms.pickNumberInRange(0,9); + } +} diff --git a/src/main/java/racingcar/utils/Validator.java b/src/main/java/racingcar/utils/Validator.java new file mode 100644 index 0000000000..09bf91f56e --- /dev/null +++ b/src/main/java/racingcar/utils/Validator.java @@ -0,0 +1,44 @@ +package racingcar.utils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import racingcar.exception.InvalidInputException; + +public class Validator { + + //์ด๋ฆ„ 5์ž์ด๋‚ด + public static List validateAndParseNames(String input) { + if(input == null || input.trim().isEmpty()) { + throw new InvalidInputException("์ž๋™์ฐจ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + List names = Arrays.stream(input.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + for (String name : names) { + if(name.isEmpty() || name.length() > 5) { + throw new InvalidInputException("์ž๋™์ฐจ ์ด๋ฆ„์€ 1~5์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + + return names; + } + + + //์‹œ๋„ํšŸ์ˆ˜๊ฐ€ 1ํšŒ์ด์ƒ ๋ฐ ์–‘์ˆ˜ + + public static int validateAndParseAttempts(String input) { + try { + int attempt = Integer.parseInt(input.trim()); + if(attempt <= 0) { + throw new InvalidInputException("์‹œ๋„ ํšŸ์ˆ˜๋Š” 1 ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + return attempt; + } catch (NumberFormatException e) { + throw new InvalidInputException("์‹œ๋„ ํšŸ์ˆ˜๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + +} diff --git a/src/main/java/racingcar/views/InputView.java b/src/main/java/racingcar/views/InputView.java new file mode 100644 index 0000000000..05760e2a5e --- /dev/null +++ b/src/main/java/racingcar/views/InputView.java @@ -0,0 +1,21 @@ +package racingcar.views; + +import java.util.List; + +import camp.nextstep.edu.missionutils.Console; +import racingcar.utils.Validator; + +public class InputView { + //์‚ฌ์šฉ์ž์—์„œ ์ž๋™์ฐจ ์ด๋ฆ„๊ณผ ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š” ์—ญํ•  + public List readCarNames() { + System.out.println("๊ฒฝ์ฃผํ•  ์ž๋™์ฐจ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”. (์ด๋ฆ„์€ ์‰ผํ‘œ(,) ๊ธฐ์ค€์œผ๋กœ ๊ตฌ๋ถ„"); + String input = Console.readLine(); + return Validator.validateAndParseNames(input); + } + + public int readAttemptCount() { + System.out.println("์‹œ๋„ํ•  ํšŸ์ˆ˜๋Š” ๋ช‡ ํšŒ์ธ๊ฐ€์š”?"); + String input = Console.readLine(); + return Validator.validateAndParseAttempts(input); + } +} diff --git a/src/main/java/racingcar/views/OutputView.java b/src/main/java/racingcar/views/OutputView.java new file mode 100644 index 0000000000..86f7064d4b --- /dev/null +++ b/src/main/java/racingcar/views/OutputView.java @@ -0,0 +1,23 @@ +package racingcar.views; + +import java.util.List; + +public class OutputView { + + // ์ฐจ์ˆ˜๋ณ„ ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ฐ ๋‹จ๋…/๊ณต๋™ ์šฐ์Šน์ž ๋ฌธ๊ตฌ + + public void printStartMessage() { + System.out.println(); + System.out.println("์‹คํ–‰ ๊ฒฐ๊ณผ"); + } + + //position : random์œผ๋กœ ๋‚˜์˜จ ํšŸ์ˆ˜ + public void printRoundResult(String name, int position) { + System.out.println(name + " : " + "-".repeat(position)); + } + + public void printWinners(List winners) { + System.out.println(); + System.out.println("์ตœ์ข… ์šฐ์Šน์ž : " + String.join(", ", winners)); + } +} diff --git a/src/test/java/racingcar/domain/CarTest.java b/src/test/java/racingcar/domain/CarTest.java new file mode 100644 index 0000000000..ee112d3f18 --- /dev/null +++ b/src/test/java/racingcar/domain/CarTest.java @@ -0,0 +1,25 @@ +package racingcar.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CarTest { + + @Test + @DisplayName("๋žœ๋ค ๊ฐ’์ด 4 ์ด์ƒ์ด๋ฉด ์ž๋™์ฐจ๊ฐ€ ํ•œ ์นธ ์ „์ง„ํ•œ๋‹ค") + void carMovesWhenRandomAboveOrEqual4() { + Car car = new Car("pobi"); + car.move(4); + assertThat(car.getPosition()).isEqualTo(1); + } + + @Test + @DisplayName("๋žœ๋ค ๊ฐ’์ด 3 ์ดํ•˜์ด๋ฉด ์ž๋™์ฐจ๊ฐ€ ๋ฉˆ์ถ˜๋‹ค") + void carStopsWhenRandomBelow4() { + Car car = new Car("pobi"); + car.move(3); + assertThat(car.getPosition()).isEqualTo(0); + } +} diff --git a/src/test/java/racingcar/domain/CarsTest.java b/src/test/java/racingcar/domain/CarsTest.java new file mode 100644 index 0000000000..2760ac491e --- /dev/null +++ b/src/test/java/racingcar/domain/CarsTest.java @@ -0,0 +1,28 @@ +package racingcar.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CarsTest { + + @Test + @DisplayName("๊ฐ€์žฅ ๋งŽ์ด ์ด๋™ํ•œ ์ž๋™์ฐจ๋ฅผ ์šฐ์Šน์ž๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค") + void returnsCorrectWinner() { + Cars cars = new Cars(List.of("pobi", "woni", "jun")); + + // pobi 2์นธ, woni 1์นธ, jun 2์นธ + cars.getCars().get(0).move(4); + cars.getCars().get(0).move(4); + cars.getCars().get(1).move(4); + cars.getCars().get(2).move(4); + cars.getCars().get(2).move(4); + + List winners = cars.getWinners(); + + assertThat(winners).containsExactlyInAnyOrder("pobi", "jun"); + } +} \ No newline at end of file diff --git a/src/test/java/racingcar/utils/ValidatorTest.java b/src/test/java/racingcar/utils/ValidatorTest.java new file mode 100644 index 0000000000..71af64518c --- /dev/null +++ b/src/test/java/racingcar/utils/ValidatorTest.java @@ -0,0 +1,39 @@ +package racingcar.utils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import racingcar.exception.InvalidInputException; + +class ValidatorTest { + + @Test + @DisplayName("์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ์ด๋ฆ„์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํŒŒ์‹ฑํ•œ๋‹ค") + void parseCarNames() { + List result = Validator.validateAndParseNames("pobi,woni,jun"); + assertThat(result).containsExactly("pobi", "woni", "jun"); + } + + @Test + @DisplayName("์ž๋™์ฐจ ์ด๋ฆ„์ด 5์ž๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ") + void nameTooLongThrowsException() { + assertThatThrownBy(() -> Validator.validateAndParseNames("abcdef")) + .isInstanceOf(InvalidInputException.class) + .hasMessageContaining("์ž๋™์ฐจ ์ด๋ฆ„์€ 1~5์ž"); + } + + @Test + @DisplayName("์‹œ๋„ ํšŸ์ˆ˜๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•˜๋ฉฐ, 0 ์ดํ•˜๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ") + void invalidAttemptThrowsException() { + assertThatThrownBy(() -> Validator.validateAndParseAttempts("-1")) + .isInstanceOf(InvalidInputException.class); + + assertThatThrownBy(() -> Validator.validateAndParseAttempts("abc")) + .isInstanceOf(InvalidInputException.class); + } +} \ No newline at end of file