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
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'java'
id 'application'
}

group 'camp.nextstep.edu'
Expand All @@ -16,10 +17,14 @@ dependencies {

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
languageVersion = JavaLanguageVersion.of(17)
}
}

test {
useJUnitPlatform()
}

application {
mainClass = 'racingcar.Application'
}
25 changes: 25 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 기능 목록

## 1. 자동차 관련 기능
- [x] 자동차 이름 입력 및 검증 (5자 이하)
- [x] 자동차 위치 관리
- [x] 자동차 전진 로직 (랜덤 값 4 이상일 때)
- [x] 자동차 상태 출력 (이름과 위치)

## 2. 게임 로직
- [x] 자동차 목록 생성
- [x] 시도 횟수 입력 및 검증
- [x] 각 라운드별 자동차 이동 처리
- [x] 우승자 결정 (최대 위치의 자동차들)
- [x] 결과 출력

## 3. 입력/출력
- [x] 자동차 이름 입력 (쉼표로 구분)
- [x] 시도 횟수 입력
- [x] 각 라운드 결과 출력
- [x] 최종 우승자 출력

## 4. 예외 처리
- [x] 잘못된 자동차 이름 (5자 초과)
- [x] 잘못된 시도 횟수 (숫자가 아닌 경우)
- [x] IllegalArgumentException 발생 시 애플리케이션 종료
39 changes: 38 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,44 @@
package racingcar;

import camp.nextstep.edu.missionutils.Console;
import java.util.List;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
try {
List<String> carNames = getCarNames();
int attemptCount = getAttemptCount();

RacingGame game = new RacingGame(carNames);
playGame(game, attemptCount);

} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
throw e;
}
}

private static List<String> getCarNames() {
System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
String input = Console.readLine();
return InputValidator.validateCarNames(input);
}

private static int getAttemptCount() {
System.out.println("시도할 회수는 몇회인가요?");
String input = Console.readLine();
return InputValidator.validateAttemptCount(input);
}

private static void playGame(RacingGame game, int attemptCount) {
System.out.println();
System.out.println("실행 결과");

for (int i = 0; i < attemptCount; i++) {
game.playRound();
game.printRoundResult();
}

game.printWinners();
}
}
51 changes: 51 additions & 0 deletions src/main/java/racingcar/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package racingcar;

import camp.nextstep.edu.missionutils.Randoms;

public class Car {
private static final int MOVE_THRESHOLD = 4;
private static final int RANDOM_MIN = 0;
private static final int RANDOM_MAX = 9;
private static final int MAX_NAME_LENGTH = 5;

private final String name;
private int position;

public Car(String name) {
validateName(name);
this.name = name;
this.position = 0;
}

private void validateName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("자동차 이름은 비어있을 수 없습니다.");
}
if (name.length() > MAX_NAME_LENGTH) {
throw new IllegalArgumentException("자동차 이름은 5자 이하여야 합니다.");
}
}

public void move() {
int randomNumber = Randoms.pickNumberInRange(RANDOM_MIN, RANDOM_MAX);
if (randomNumber >= MOVE_THRESHOLD) {
position++;
}
}

public String getName() {
return name;
}

public int getPosition() {
return position;
}

public String getPositionString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < position; i++) {
sb.append("-");
}
return sb.toString();
}
}
46 changes: 46 additions & 0 deletions src/main/java/racingcar/InputValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package racingcar;

import java.util.Arrays;
import java.util.List;

public class InputValidator {

public static List<String> validateCarNames(String input) {
if (input == null || input.trim().isEmpty()) {
throw new IllegalArgumentException("자동차 이름을 입력해주세요.");
}

List<String> carNames = Arrays.asList(input.split(",", -1));
for (String name : carNames) {
String trimmedName = name.trim();
validateCarName(trimmedName);
}

return carNames;
}

private static void validateCarName(String name) {
if (name.isEmpty()) {
throw new IllegalArgumentException("자동차 이름은 비어있을 수 없습니다.");
}
if (name.length() > 5) {
throw new IllegalArgumentException("자동차 이름은 5자 이하여야 합니다.");
}
}

public static int validateAttemptCount(String input) {
if (input == null || input.trim().isEmpty()) {
throw new IllegalArgumentException("시도할 회수를 입력해주세요.");
}

try {
int count = Integer.parseInt(input.trim());
if (count <= 0) {
throw new IllegalArgumentException("시도할 회수는 1 이상이어야 합니다.");
}
return count;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("시도할 회수는 숫자여야 합니다.");
}
}
}
62 changes: 62 additions & 0 deletions src/main/java/racingcar/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package racingcar;

import java.util.ArrayList;
import java.util.List;

public class RacingGame {
private final List<Car> cars;

public RacingGame(List<String> carNames) {
this.cars = createCars(carNames);
}

private List<Car> createCars(List<String> carNames) {
List<Car> cars = new ArrayList<>();
for (String name : carNames) {
cars.add(new Car(name));
}
return cars;
}

public void playRound() {
for (Car car : cars) {
car.move();
}
}

public void printRoundResult() {
for (Car car : cars) {
System.out.println(car.getName() + " : " + car.getPositionString());
}
System.out.println();
}

public List<String> getWinners() {
int maxPosition = getMaxPosition();
List<String> winners = new ArrayList<>();

for (Car car : cars) {
if (car.getPosition() == maxPosition) {
winners.add(car.getName());
}
}

return winners;
}

private int getMaxPosition() {
int maxPosition = 0;
for (Car car : cars) {
if (car.getPosition() > maxPosition) {
maxPosition = car.getPosition();
}
}
return maxPosition;
}

public void printWinners() {
List<String> winners = getWinners();
System.out.print("최종 우승자 : ");
System.out.println(String.join(", ", winners));
}
}
74 changes: 74 additions & 0 deletions src/test/java/racingcar/CarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package racingcar;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.assertj.core.api.Assertions.*;

class CarTest {

@Test
@DisplayName("자동차 이름이 5자를 초과하면 예외가 발생한다")
void validateName_TooLongName_ThrowsException() {
assertThatThrownBy(() -> new Car("abcdef"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("자동차 이름은 5자 이하여야 합니다.");
}

@Test
@DisplayName("자동차 이름이 비어있으면 예외가 발생한다")
void validateName_EmptyName_ThrowsException() {
assertThatThrownBy(() -> new Car(""))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("자동차 이름은 비어있을 수 없습니다.");
}

@Test
@DisplayName("자동차 이름이 null이면 예외가 발생한다")
void validateName_NullName_ThrowsException() {
assertThatThrownBy(() -> new Car(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("자동차 이름은 비어있을 수 없습니다.");
}

@Test
@DisplayName("자동차 이름이 공백만 있으면 예외가 발생한다")
void validateName_BlankName_ThrowsException() {
assertThatThrownBy(() -> new Car(" "))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("자동차 이름은 비어있을 수 없습니다.");
}

@Test
@DisplayName("유효한 자동차 이름으로 생성할 수 있다")
void createCar_ValidName_Success() {
Car car = new Car("pobi");

assertThat(car.getName()).isEqualTo("pobi");
assertThat(car.getPosition()).isEqualTo(0);
}

@Test
@DisplayName("자동차 이름이 5자일 때 생성할 수 있다")
void createCar_FiveCharacterName_Success() {
Car car = new Car("abcde");

assertThat(car.getName()).isEqualTo("abcde");
assertThat(car.getPosition()).isEqualTo(0);
}

@Test
@DisplayName("자동차의 초기 위치는 0이다")
void getPosition_InitialPosition_IsZero() {
Car car = new Car("pobi");

assertThat(car.getPosition()).isEqualTo(0);
}

@Test
@DisplayName("자동차의 초기 위치 문자열은 빈 문자열이다")
void getPositionString_InitialPosition_IsEmpty() {
Car car = new Car("pobi");

assertThat(car.getPositionString()).isEqualTo("");
}
}
Loading