Skip to content
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/Car/**/build/
!**/src/test/**/build/

### IntelliJ IDEA ###
Expand All @@ -10,7 +10,7 @@ build/
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/Car/**/out/
!**/src/test/**/out/

### Eclipse ###
Expand All @@ -22,7 +22,7 @@ out/
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/Car/**/bin/
!**/src/test/**/bin/

### NetBeans ###
Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 🚗 자동차 경주 게임 1,2단계 (Racing Car Game)

주어진 횟수 동안 n대의 자동차가 무작위 값에 따라 전진 또는 멈추며, 최종적으로 가장 멀리 이동한 우승자를 가려내는 게임입니다.

## 🎯 핵심 요구사항
- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
- 각 자동차는 `0에서 9 사이에서 무작위 값`을 구한 후, 그 값이 **4 이상일 경우 전진**하고 **3 이하의 값이면 멈춘다.**
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. (우승자는 한 명 이상일 수 있다.)

---
Comment on lines +1 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏👏👏👏


## 🛠️ 기능 구현 목록

### 1. 자동차 (Car)
- [x] 자동차는 이름과 현재 위치 상태를 가진다.
- [x] 무작위 값이 4 이상일 경우 위치를 1칸 전진한다.
- [x] 무작위 값이 3 이하일 경우 현재 위치를 유지한다.

### 2. 레이싱 게임 진행 (RacingGame)
- [x] 출전하는 자동차 명단을 관리한다.
- [x] `java.util.Random`을 활용하여 0~9 사이의 무작위 값을 생성한다.
- [x] 입력받은 시도 횟수만큼 각 자동차에게 무작위 값을 전달하여 경주를 진행한다.

### 3. 우승자 판별
- [x] 경주가 끝난 후, 가장 멀리 이동한 자동차의 최대 위치(maxPosition)를 구한다.
- [x] 최대 위치와 동일한 위치에 있는 자동차(우승자)를 찾아 반환한다.
- [x] 우승자가 여러 명일 경우 공동 우승자로 모두 반환한다.

---

## ✅ 테스트 구현 목록

**`CarTest` (개별 자동차 책임 테스트)**
- [x] 무작위 값이 4 이상일 때 자동차가 정상적으로 전진하는가?
- [x] 무작위 값이 3 이하일 때 자동차가 전진하지 않고 멈추는가?

**`RacingTest` (우승자 판별 로직 테스트)**
- [x] 경주 종료 후 우승자가 여러 명일 경우 공동 우승자를 모두 정확히 반환하는가?
29 changes: 29 additions & 0 deletions src/main/java/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
public class Car {
private final String name;
private int position;
Comment on lines +1 to +3
Copy link

@chxghee chxghee Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 name을 final 로 선언한 것은 매우 좋은것 같네요!

final 로 선언을 했을때 무슨 이점이 있을까요??

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미션에서 name은 한번 지정되면 바뀌지 않는다고 했으므로 final로 선언했습니다! final이 이름이 바뀌지 않는다는 것을 보장해줘 코드를 안전하게..? 만들어주는 이점이 있을 것 같습니다!!! 또한 다른 사람이 코드를 봤을 때 final이라는 단어 하나로 자동차 객체의 불변성을 이해할 수 있을 것 같습니다.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final은 객체가 한 번 생성된 이후 상태가 바뀌지 않기 때문에 상태를 예측하기 쉬워진다는 장점이 있습니다

저는 개인적으로 가장 큰 장점은 말씀해주신 것처럼,
다른 사람이 이 코드를 보았을 때 “이 값은 절대 바뀌지 않겠구나"를 바로 이해할 수 있다는 점이라고 생각해요

그 덕분에 코드를 처음 읽는 사람도
상태 변경 가능성을 함께 고민하지 않아도 되어서,
코드를 훨씬 더 쉽게 이해할 수 있는 것 같습니다


private static final int MIN_FORWARD_NUMBER = 4;

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

public void move(int randomValue){
if(randomValue >= MIN_FORWARD_NUMBER){
this.position++;
}
}
Comment on lines +12 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이동 조건 값 4는 상수로 분리하는건 어떨까요?

현재 move() 메서드 안에서 4가 직접 사용되고 있는데,
이런 값은 의미를 담은 상수로 분리해 두면 코드를 읽는 사람이 이 4라는 숫자가 무엇을 의미하는지 더 쉽게 이해할 수 있을 것 같아요.
또 나중에 기준값이 바뀌더라도 여러 곳을 찾지 않고 한 곳만 수정하면 되어서 유지보수도 쉬워지고요.

예를 들면 단순한 숫자 4보다,
전진 가능한 최소 값 같은 의미가 드러나는 이름이 있으면 의도가 훨씬 잘 보일 것 같습니다!


public boolean isAtPosition(int maxPosition){
return this.position== maxPosition;
}

public String getName(){
return this.name;
}

public int getPosition(){
return this.position;
}
}
60 changes: 60 additions & 0 deletions src/main/java/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 final 키워드를 붙여 주셨네요!

그렇다면 퀴즈 입니다
final로 선언된 cars 컬렉션을 cars.remove(0), cars.add(new Car("그리디")) 와 같이 수정할 수 없을까요?


public RacingGame(List<Car> cars){
this.cars = cars;
}

public void race(int tryCount){
for(int i=0; i< tryCount; i++){
moveCars();
}
}
Comment on lines +12 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

빠졌던 요구사항을 잘 구현해 주셨군요!!

이전에는 자동차를 모두 이동시킨 뒤에 RacingGame이 우승자만 판단했는데,
지금처럼 RacingGame이 이동부터 우승자 결정까지 게임 흐름을 함께 책임지도록 바뀌면서
어떤 점이 더 좋아졌다고 느껴지시나요?

좀 더 명확하게 질문을 드리면
게임을 진행하는 책임을 RacingGame이 더 직접적으로 가지게 되면서 어떤 점이 더 나아진것 같나요?


public void moveCars(){
Random random = new Random();
for (Car car : cars){
int randomValue = random.nextInt(10);
car.move(randomValue);
}

}
Comment on lines +18 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moveCars() 를 public 으로 열어둔 이유가 있을까요?? (+ for 반복문 아래 불필요한 줄바꿈은 지워 주세요!)


public List<Car> getWinners(){
int maxPosition = getMaxPosition();
return findCarsAt(maxPosition);
}

private int getMaxPosition(){
int max=0;
Comment on lines +32 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 코드가 = 앞 뒤에 띄어쓰기가 안 되어 있는거 같아요.

인텔리제이에서는 코드 자동 정렬 기능이 있다는 것을 알고 계시나요?

mac : cmd + shift + L
windows: ctrl + shift + L

코드를 제출하기 전에 한 번씩 정렬을 하고 커밋을 하면 더욱 깔끔해 집니다

for (Car car : cars){
max = updateMax(max, car.getPosition());
}
return max;
}

private int updateMax(int currentMax, int carPosition){
if (carPosition > currentMax){
return carPosition;
}
return currentMax;
}
Comment on lines +40 to +45
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 Max 값을 찾기 위한 메서드는 자바 표준 라이브러리(Math)를 이용해서 없애 볼 수 있을 것 같아요


private List<Car> findCarsAt(int maxPosition){
List<Car> winners = new ArrayList<>();
for(Car car : cars){
addIfWinner(winners, car, maxPosition);
}
return winners;
}

private void addIfWinner(List<Car> winners, Car car, int maxPosition){
if (car.isAtPosition(maxPosition)){
winners.add(car);
}
}
}
25 changes: 25 additions & 0 deletions src/test/java/CarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

class CarTest {

@Test
void 무작위_값이_4_이상이면_전진한다(){
Car car = new Car("pobi");

car.move(4);

assertThat(car.getPosition()).isEqualTo(1);
}
Comment on lines +7 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 네이밍이 훨씬 명확해 졌네요!

다만 네이밍에서 무작위 값이라는 표현은 조금 어색하게 느껴 지는것 같아요

현재 테스트는 실제로 무작위 값을 생성해서 검증하는 것이 아니라,
4, 3과 같은 값을 직접 전달해서 Car의 이동 규칙을 검증하고 있습니다

또한 Car 객체 자체는 무작위라는 개념을 알지 않고,
단순히 전달된 값에 따라 이동 여부를 판단하는 역할만 가지고 있기 때문에,
무작위라는 표현은 Car의 책임과도 조금 어긋나는 것 같다 라는 느낌을 받았는데요

그래서 테스트 이름도
무작위 값보다는 전달된 값 또는 주어진 값처럼
실제로 Car가 받는 입력을 기준으로 표현해 주는 것이 더 자연스러운 것 같은데 하은님은 어떻게 생각하시나요??


@Test
void 무작위_값이_3_이하이면_멈춘다(){
Car car = new Car("rabbit");

car.move(3);

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

}
23 changes: 23 additions & 0 deletions src/test/java/RacingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;

public class RacingTest {

Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우리가 이 클래스에서 테스트 하고자 하는 대상이 RacingGame이니까
이를 테스트 하는 클래스의 이름 또한 RacingGameTest 로 맞춰주면 어떤 클래스를 검증하는 테스트인지 더 바로 드러날 것 같아요!

@Test
void 우승자가_여러명일_경우_공동_우승자를_모두_반환한다(){
Comment on lines +8 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RacingGame 에서 테스트를 해 볼만한 것이 공동 우승자 뿐일까요??

현재 테스트가 공동 우승자 상황에만 집중되어 있는데,
게임의 전체 동작을 고려했을 때 다른 케이스들도 함께 검증해보면 좋을 것 같습니다.

예를 들어

  • 단일 우승자가 나오는 경우
  • 경계 상황(모두 이동하지 않는 경우)

특히 테스트에서는 경계 상황에 대해 내가 의도한 동작을 하는지를 검증해 보는게 특히나 중요한 것 같아요!

Car pobi = new Car("pobi");
Car crong = new Car("crong");
Car rabbit = new Car("rabbit");

pobi.move(4);
crong.move(5);
rabbit.move(2);

RacingGame game = new RacingGame(Arrays.asList(pobi, crong, rabbit));
List<Car> winners = game.getWinners();

assertThat(winners).containsExactly(pobi, crong);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

containsExactly()는 내부 원소의 순서까지 검증하는 메서드인데요.
현재 요구사항에서 공동 우승자의 반환 순서까지 중요하게 봐야 하는지는 한 번 더 고민해보셔도 좋을 것 같아요

}
}