Skip to content
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ java {

test {
useJUnitPlatform()
jvmArgs '-Xshare:off' // JVM 아규먼트 설정
}
23 changes: 23 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
구현해야하는 기능 목록

1. 자동차 이름 입력 (쉼표로 구분)
1-1. 이름 나누기
1-2. 각 이름 검증 (5글자 이하)
2. 시도할 횟수 입력
2-1. 횟수 검증 (10회 이하)

3. 각 라운드 진행
3-1. 각 개체 별 랜덤 넘버 생성
3-2. 라운드 진행
3-3. 각 라운드 별 결과 작성 (이름 : 진행 상황)

4. 최종 우승자 출력
4-1. 각 객체 별 최종 위치 확인
4-2. 우승자 출력 (2명 이상일 수 있음)

유의사항
- 사용자 입력값은 camp.nextstep.edu.missionutils.Console의 readLine() 활용
- Random 값 추출은 camp.nextstep.edu.missionutils.Randoms의 pickNumberInRange() 활용
- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현
- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 구현
- JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인할 것!
66 changes: 66 additions & 0 deletions src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,73 @@
package racingcar;
import camp.nextstep.edu.missionutils.Console;


public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현

// 1. 이름 입력 받기
System.out.println("경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분합니다.)");
String participations = Console.readLine();

// 1-1. 이름 나누기
String[] carNames = participations.split(","); //String을 콤마를 기준으로 나누어 리스트로 변환
int length = carNames.length;

// 객체 배열 생성
Car[] cars = new Car[length];

for (int i = 0; i < carNames.length; i++) {
cars[i] = new Car(carNames[i]);
}
for (Car car : cars) { System.out.println("참가자 : " + car.getName()); }

// 2. 시도 횟수 입력
System.out.println("시도할 회수는 몇 회입니까?");
String tryCounts = Console.readLine(); // readLine은 String으로 받아야 함. (int 불가x)

int counts = Application.validateTryCount(tryCounts); // 변수 시작 소문자로 수정

// 3. 게임 실행
Game game = new Game();
for(int i = 0; i < counts; i++){
System.out.println();
System.out.println( "["+ (i+1) +"번째 시도]");
// 각 라운드 경주 진행
game.runRound(cars);

// 3-1. 라운드 별 결과 출력
game.printRound(cars);
}
// 4. 게임 끝! 결과 출력
// 4-1.
game.checkWinner(cars);
// 4-2.
game.printWinner(cars);
}

private static final int minGameTry = 1;
private static final int maxGameTry = 10;

// 2-1. 시도 횟수 검증(임의 규칙 생성)
public static int validateTryCount(String tryCounts){
// 게임 횟수는 1번 이상일 것
final int num ; // num은 한 게임에서 변하지 않으므로 final 처리

try {
num = Integer.parseInt(tryCounts);
}
catch(NumberFormatException e) {
throw new IllegalArgumentException("시도 횟수는 숫자여야 합니다.");
}
// 게임 횟수는 최소 1번
if (num < minGameTry){
throw new IllegalArgumentException("게임 횟수는 최소 1번이어야 합니다.");
}
// 게임 횟수는 10번 이하일 것
if (num > maxGameTry){
throw new IllegalArgumentException("게임 횟수는 10회 이하여야 합니다.");
}
return num ;
}
}
35 changes: 35 additions & 0 deletions src/main/java/racingcar/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 상태 : 자동차의 이름, 위치
// 행동 : 움직인다/움직이지 않는다

package racingcar;
public class Car {

private final String name; // final로 변경
private int position = 0; // 초기화

// 생성자
public Car(String name) {
this.name = validateName(name);
}

private String validateName(String name) {
// 1. null 체크
if (name == null) throw new IllegalArgumentException("이름을 입력해주세요.");

// 2. 이름이 비었는지 체크(공백만으로 이루어졌는지)
String s = name.strip();
if (s.isEmpty()) throw new IllegalArgumentException("이름은 공백만으로 이루어질 수 없습니다.");

// 3. 길이 > 5 체크
if (s.length() > 5) throw new IllegalArgumentException("이름은 5글자 이하여야 합니다");
return s;
}

public String getName() { return name; }
public int getPosition() { return position; }



//4 이상이면 전진
void moveIf(int num) { if (num >= 4) { position++; } }
}
63 changes: 63 additions & 0 deletions src/main/java/racingcar/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package racingcar;

import camp.nextstep.edu.missionutils.Randoms;

public class Game { // 게임 진행 클래스
// 3-1. 랜덤 넘버 생성
public int pickRandomNumber(){
return Randoms.pickNumberInRange(0,9);
}

// 3-2. 라운드 진행
public void runRound(Car[] cars){
// 모든 차에 대해 랜덤 수를 뽑고, 전진 여부를 판단하기
for (Car car : cars) {
int n = pickRandomNumber();
car.moveIf(n);
}
}

// 3-3. 라운드 결과 출력(라운드마다 실행!)
public void printRound(Car[] cars){
for (Car car : cars) {
// 단순히 getPosition으로 숫자만 출력하는게 아님! 현재 위치를 '-' 이용해서 출력하기
String result = printPosition(car.getPosition());
System.out.println(car.getName() + " : " + result);
}
}

// SRP(단일책임원칙) : 메서드 위치 변경 (Car -> Game)
public String printPosition(int position) { // 생각해보니 buffer를 사용하지 않아도 됨... return을 String으로 변경
return "-".repeat(position);
}
// 4. 게임 끝!

// 4-1. 마지막 위치 확인
// - 객체 배열 순회하면서 최종 위치 + 우승자 갱신
public StringBuilder checkWinner(Car[] cars){
StringBuilder winners = new StringBuilder(); // winner는 2명 이상일 수 있으므로 stringbuilder로 생성
int maxPosition = 0;

// <1> 1번 순회하기 (max값과 같으면 string에 추가, 더 크면 초기화)
for (Car car : cars) {
int p = car.getPosition(); // 변수화

// [1] max보다 크면 StringBuilder 초기화 후 우승자 후보 정보 입력
if ((p) > maxPosition) {
maxPosition = p;
winners.setLength(0); // 초기화
winners.append(car.getName());
}
// [2] max와 동일하면 초기화는 하지 않고 우승자 후보 추가 입력
else if (p == maxPosition) {
if (winners.length() > 0) { winners.append(", ");}
winners.append(car.getName());
}
}
return winners;
}
public void printWinner(Car[] cars){
System.out.println("최종 우승자 : " + checkWinner(cars));

}
}
54 changes: 54 additions & 0 deletions src/test/java/racingcar/CarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package racingcar;

import org.junit.jupiter.api.Test;

import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInRangeTest;
import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;


class CarTest {
//1. 자동차 이름 입력 (쉼표로 구분)
@Test
void 자동차의_이름은_5글자_넘으면_예외발생() {
// 이름이 6자 이상인 자동차를 만들면 예외 발생
assertThatThrownBy(() -> new Car("abcdef"))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 이름이_비었으면_예외발생() {
assertThatThrownBy(() -> new Car(" "))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 이름이_null이면_예외발생() {
assertThatThrownBy(() -> new Car(null))
.isInstanceOf(IllegalArgumentException.class);
}

// //2번 - 시도할 횟수 입력
// @Test
// void 게임횟수는_1미만이면_예외발생() {
// assertThatThrownBy(() -> Application.validateTryCount("0")
// .isInstanceOf(IllegalArgumentException.class));
// }
//3. 전진 규칙
@Test
void 자동차는_숫자가_4이상일때_전진() { //테스트는 경계값을 기준으로 설정
Car car = new Car("test1");
car.moveIf(4);
assertThat(car.getPosition()).isEqualTo(1);
}

@Test
void 자동차는_숫자가_3이하면_정지() {
Car car = new Car("test1");
car.moveIf(3); // 3 이하 → 멈춤
assertThat(car.getPosition()).isEqualTo(0); // 자동차의 위치가 0인지 검증
}


}
20 changes: 20 additions & 0 deletions src/test/java/racingcar/TryCountValidateTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package racingcar;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.junit.jupiter.api.Test;

public class TryCountValidateTest {
//2번 - 시도할 횟수 입력 테스트
@Test
void 게임횟수가_0이하면_예외처리(){
assertThatThrownBy(() -> Application.validateTryCount("0"))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 게임횟수가_10초과면_예외처리(){
assertThatThrownBy(() -> Application.validateTryCount("11"))
.isInstanceOf(IllegalArgumentException.class);
}

}