From f6f8dda58a0c3df502108055d3e417555c307b8e Mon Sep 17 00:00:00 2001 From: anmin Date: Mon, 27 Oct 2025 23:56:14 +0900 Subject: [PATCH 1/2] =?UTF-8?q?-=20Car=20=EC=9D=B4=EB=A6=84=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(5=EC=9E=90=20=EC=B4=88=EA=B3=BC=20=EC=8B=9C=20IllegalArgumentE?= =?UTF-8?q?xception)=20-=20Application.main()=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EB=A5=BC=20=EB=8D=98=EC=A7=80=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=ED=95=98=EC=97=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=97=90=EC=84=9C=20assertThatThrownBy=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=20-=20InputView=EC=99=80=20RacingGame?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=9E=85=EB=A0=A5=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EA=B0=95=ED=99=94=20-=20ApplicationTest=20=EA=B8=B0=EB=8A=A5/?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=86=B5?= =?UTF-8?q?=EA=B3=BC=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/Application.java | 12 ++++- src/main/java/racingcar/domain/Car.java | 41 +++++++++++++++++ .../java/racingcar/domain/RacingGame.java | 46 +++++++++++++++++++ src/main/java/racingcar/utils/RandomUtil.java | 9 ++++ src/main/java/racingcar/view/InputView.java | 25 ++++++++++ src/main/java/racingcar/view/OutputView.java | 25 ++++++++++ 6 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/main/java/racingcar/domain/Car.java create mode 100644 src/main/java/racingcar/domain/RacingGame.java create mode 100644 src/main/java/racingcar/utils/RandomUtil.java create mode 100644 src/main/java/racingcar/view/InputView.java create mode 100644 src/main/java/racingcar/view/OutputView.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..cd5c6c6a11 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,17 @@ package racingcar; +import racingcar.domain.RacingGame; +import racingcar.view.InputView; +import racingcar.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + String[] names = InputView.readCarNames(); + int count = InputView.readRoundCount(); + + RacingGame racingGame = new RacingGame(names); + racingGame.race(count); + OutputView.printWinners(racingGame.getWinners()); } + } diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java new file mode 100644 index 0000000000..2b02a76ed8 --- /dev/null +++ b/src/main/java/racingcar/domain/Car.java @@ -0,0 +1,41 @@ +package racingcar.domain; + +public class Car { + private static final int MOVE_CONDITION = 4; + private final String name; + private int position = 0; + + public Car(String name) { + validateName(name); + this.name = name; + } + + private void validateName(String name) { + if (name == null || name.isBlank() || name.length() > 5) { + throw new IllegalArgumentException("자동차 이름은 1~5자 이하여야 합니다."); + } + } + + public void move(int randomNumber) { + if (randomNumber >= MOVE_CONDITION) { + position++; + } + } + + public String getName() { + return name; + } + + public int getPosition() { + return position; + } + + public boolean isWinner(int maxPosition) { + return this.position == maxPosition; + } + + @Override + public String toString() { + return name + " : " + "-".repeat(position); + } +} diff --git a/src/main/java/racingcar/domain/RacingGame.java b/src/main/java/racingcar/domain/RacingGame.java new file mode 100644 index 0000000000..926c32bfd9 --- /dev/null +++ b/src/main/java/racingcar/domain/RacingGame.java @@ -0,0 +1,46 @@ +package racingcar.domain; + +import racingcar.utils.RandomUtil; +import racingcar.view.OutputView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class RacingGame { + private final List cars; + + public RacingGame(String[] names) { + this.cars = Arrays.stream(names) + .map(String::trim) + .map(Car::new) + .collect(Collectors.toList()); + } + + public void race(int roundCount) { + OutputView.printResultTitle(); + for (int i = 0; i < roundCount; i++) { + playRound(); + OutputView.printRoundResult(cars); + } + } + + private void playRound() { + for (Car car : cars) { + car.move(RandomUtil.getRandomNumber()); + } + } + + public List getWinners() { + int maxPosition = cars.stream() + .mapToInt(Car::getPosition) + .max() + .orElse(0); + + return cars.stream() + .filter(car -> car.isWinner(maxPosition)) + .map(Car::getName) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/racingcar/utils/RandomUtil.java b/src/main/java/racingcar/utils/RandomUtil.java new file mode 100644 index 0000000000..05ee33094c --- /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/view/InputView.java b/src/main/java/racingcar/view/InputView.java new file mode 100644 index 0000000000..25f830fae7 --- /dev/null +++ b/src/main/java/racingcar/view/InputView.java @@ -0,0 +1,25 @@ +package racingcar.view; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + + public static String[] readCarNames() { + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + String input = Console.readLine(); + if (!input.contains(",")) { + throw new IllegalArgumentException("자동차는 최소 2대 이상이어야 합니다."); + } + return input.split(","); + } + + public static int readRoundCount() { + System.out.println("시도할 횟수는 몇 회인가요?"); + String input = Console.readLine(); + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("시도 횟수는 숫자여야 합니다."); + } + } +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java new file mode 100644 index 0000000000..b7c44eca6f --- /dev/null +++ b/src/main/java/racingcar/view/OutputView.java @@ -0,0 +1,25 @@ +package racingcar.view; + +import racingcar.domain.Car; + +import java.util.List; +import java.util.stream.Collectors; + +public class OutputView { + + public static void printResultTitle() { + System.out.println("\n실행 결과"); + } + + public static void printRoundResult(List cars) { + for (Car car : cars) { + System.out.println(car); + } + System.out.println(); + } + + public static void printWinners(List winners) { + String result = String.join(", ", winners); + System.out.println("최종 우승자 : " + result); + } +} From 07c23d304aa80acb06efc4c8d659d1b094d1eb2d Mon Sep 17 00:00:00 2001 From: AnMin <100991171+AnMin2000@users.noreply.github.com> Date: Tue, 28 Oct 2025 00:04:53 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Update=20README=20for=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=B0=A8=20=EA=B2=BD=EC=A3=BC=20=EA=B2=8C=EC=9E=84=20project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d0286c859f..21dc80508a 100644 --- a/README.md +++ b/README.md @@ -1 +1,127 @@ -# java-racingcar-precourse +# 🚗 자동차 경주 게임 + +## 프로젝트 개요 + +* **목적**: 사용자가 입력한 자동차 이름으로 경주를 진행하고, 최종 우승자를 출력하는 콘솔 기반 게임 +* **언어/환경**: Java 21 +* **라이브러리**: `camp.nextstep.edu.missionutils` (Randoms, Console) +* **테스트**: JUnit 5, AssertJ + +--- + +## 기능 요구사항 + +1. 자동차 이름 입력 + + * 쉼표(,)로 구분 + * 이름 5자 이하 + * 잘못된 입력 시 `IllegalArgumentException` 발생 + +2. 시도 횟수 입력 + + * 숫자만 허용 + * 잘못된 입력 시 `IllegalArgumentException` 발생 + +3. 자동차 이동 + + * 랜덤 값(0~9) ≥ 4 → 전진 + * 이동 결과 매 라운드 출력 (`-`로 표시) + +4. 우승자 출력 + + * 최종 우승자 1명 또는 공동 우승자 + * 쉼표(,)로 구분 + +--- + +## 패키지 구조 + +``` +racingcar + ├── Application.java // 실행 진입점 + ├── domain + │ ├── Car.java + │ └── RacingGame.java + ├── view + │ ├── InputView.java + │ └── OutputView.java + └── utils + └── RandomUtil.java +``` + +--- + +## 사용 방법 + +1. 프로젝트 빌드 및 실행 + +```bash +./gradlew run +``` + +2. 콘솔 입력 + +``` +경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분) +pobi,woni,jun + +시도할 횟수는 몇 회인가요? +5 +``` + +3. 실행 결과 예시 + +``` +실행 결과 +pobi : - +woni : +jun : - + +pobi : -- +woni : - +jun : -- + +pobi : --- +woni : -- +jun : --- + +pobi : ---- +woni : --- +jun : ---- + +pobi : ----- +woni : ---- +jun : ----- + +최종 우승자 : pobi, jun +``` + +--- + +## 예외 처리 + +* 자동차 이름 5자 초과 → `IllegalArgumentException` +* 이름 공백/빈값 → `IllegalArgumentException` +* 시도 횟수 숫자 아님 → `IllegalArgumentException` + +--- + +## 테스트 + +* **ApplicationTest** + + * 기능 테스트: 랜덤 값에 따라 자동차 이동 및 우승자 출력 검증 + * 예외 테스트: 잘못된 입력 시 IllegalArgumentException 발생 확인 + +* **테스트 실행** + +```bash +./gradlew test +``` + +--- + +## 참고 + +* 랜덤 값은 `camp.nextstep.edu.missionutils.Randoms.pickNumberInRange(0, 9)` 사용 +* 입력은 `camp.nextstep.edu.missionutils.Console.readLine()` 사용