Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 127 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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()` 사용
12 changes: 11 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -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());
}

}
41 changes: 41 additions & 0 deletions src/main/java/racingcar/domain/Car.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
46 changes: 46 additions & 0 deletions src/main/java/racingcar/domain/RacingGame.java
Original file line number Diff line number Diff line change
@@ -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<Car> 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<String> 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());
}
}
9 changes: 9 additions & 0 deletions src/main/java/racingcar/utils/RandomUtil.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
25 changes: 25 additions & 0 deletions src/main/java/racingcar/view/InputView.java
Original file line number Diff line number Diff line change
@@ -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("시도 횟수는 숫자여야 합니다.");
}
}
}
25 changes: 25 additions & 0 deletions src/main/java/racingcar/view/OutputView.java
Original file line number Diff line number Diff line change
@@ -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<Car> cars) {
for (Car car : cars) {
System.out.println(car);
}
System.out.println();
}

public static void printWinners(List<String> winners) {
String result = String.join(", ", winners);
System.out.println("최종 우승자 : " + result);
}
}