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
11 changes: 10 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package racingcar;

import racingcar.controller.CarRace;

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

carRace.receiveCarNamesInput();
carRace.receiveCarRaceAttemptCountInput();
carRace.makeCarObjects();
carRace.doRace();
carRace.setWinnerCarNames();
carRace.printWinnerCarNames();
}
}
90 changes: 90 additions & 0 deletions src/main/java/racingcar/controller/CarRace.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package racingcar.controller;

import racingcar.model.Car;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;
import java.util.NoSuchElementException;

public class CarRace {
private final List<Car> cars = new ArrayList<>();
private final Scanner scanner = new Scanner(System.in);

private List<String> carNames;
private int raceAttemptCount;
private List<String> winnerCarNames;

public void receiveCarNamesInput() {
System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
String value = scanner.nextLine().trim();

if (value.isEmpty()) {
throw new IllegalArgumentException("경주할 자동차의 이름을 반드시 입력해주세요.");
}

carNames = Arrays.stream(value.split(","))
.map(String::trim)
.collect(Collectors.toList());

if (carNames.stream().anyMatch(name -> name.length() > 5)) {
throw new IllegalArgumentException("자동차 이름은 5글자 이하로 작성해주세요.");
}
}

public void receiveCarRaceAttemptCountInput() {
System.out.println("시도할 횟수는 몇 회인가요?");
raceAttemptCount = scanner.nextInt();
}

public void makeCarObjects() {
for (int i = 0; i < carNames.size(); i++) {
cars.add(new Car(i, carNames.get(i)));
}
}

private void moveCars() {
for (Car car : cars) {
car.tryMove();
}
}

public void doRace() {
System.out.println("\n실행 결과");
for (int i = 0; i < raceAttemptCount; i++) {
moveCars();
printRaceResult();
}
}

private void printRaceResult() {
for (Car car : cars) {
System.out.println(car.getCarName() + " : " + "-".repeat(car.getPosition()));
}
System.out.println();
}

public void setWinnerCarNames() {
int maxPosition = cars.stream()
.max(Comparator.comparingInt(Car::getPosition))
.orElseThrow(NoSuchElementException::new)
.getPosition();

winnerCarNames = cars.stream()
.filter(c -> c.getPosition() == maxPosition)
.map(Car::getCarName)
.collect(Collectors.toList());
}

public void printWinnerCarNames() {
System.out.print("최종 우승자 : ");
System.out.println(String.join(", ", winnerCarNames));
}

public void setCarNames(List<String> names) {
this.carNames = names;
}
}
45 changes: 45 additions & 0 deletions src/main/java/racingcar/model/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package racingcar.model;

public class Car {
int position = 0;
private int carNumber;
private String carName;

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

public Car(int carNumber, String carName) {
this.carNumber = carNumber;
this.carName = carName;
}

public int getRandom() {
double randomValue = Math.random();
return (int) (randomValue * 9);
}

public boolean getStatus() {
return getRandom() > 3;
}

public void tryMove() {
if(getStatus()) {
this.position++;
}
}

public int getPosition() {
return position;
}


public String getCarName() {
return carName;
}

public int getCarNumber() {
return carNumber;
}

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

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import racingcar.model.Car;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

class CarRaceTest {

private CarRace carRace;

@BeforeEach
void setUp() {
carRace = new CarRace();
}

@Test
@DisplayName("자동차 이름을 올바르게 입력받을 경우 carNames 리스트가 초기화된다.")
void receiveCarNamesInput_shouldParseNamesCorrectly() {
// given
List<String> names = List.of("pobi", "woni", "jun");

// when
carRace.setCarNames(names);
carRace.makeCarObjects();

// then
try {
var field = CarRace.class.getDeclaredField("cars");
field.setAccessible(true);
List<Car> cars = (List<Car>) field.get(carRace);
assertEquals(3, cars.size());
assertEquals("pobi", cars.get(0).getCarName());
} catch (Exception e) {
fail("리플렉션 접근 실패: " + e.getMessage());
}
}

@Test
@DisplayName("자동차 객체를 올바르게 생성해야 한다.")
void makeCarObjects_shouldCreateCars() {
carRace = new CarRace();

var names = List.of("pobi", "woni", "jun");

try {
var field = CarRace.class.getDeclaredField("carNames");
field.setAccessible(true);
field.set(carRace, names);
} catch (Exception e) {
fail("리플렉션 설정 실패: " + e.getMessage());
}

// when
carRace.makeCarObjects();

// then
try {
var field = CarRace.class.getDeclaredField("cars");
field.setAccessible(true);
List<Car> cars = (List<Car>) field.get(carRace);
assertEquals(3, cars.size());
assertEquals("pobi", cars.get(0).getCarName());
} catch (Exception e) {
fail("리플렉션 접근 실패: " + e.getMessage());
}
}

@Test
@DisplayName("우승자 이름을 올바르게 설정해야 한다.")
void setWinnerCarNames_shouldFindCorrectWinners() {
try {
var names = List.of("a", "b", "c");
var field = CarRace.class.getDeclaredField("carNames");
field.setAccessible(true);
field.set(carRace, names);
carRace.makeCarObjects();

// 강제로 position 설정
var carsField = CarRace.class.getDeclaredField("cars");
carsField.setAccessible(true);
List<Car> cars = (List<Car>) carsField.get(carRace);

// a는 2칸, b는 1칸, c는 0칸
var posField = Car.class.getDeclaredField("position");
posField.setAccessible(true);
posField.setInt(cars.get(0), 2); // a
posField.setInt(cars.get(1), 1); // b
posField.setInt(cars.get(2), 0); // c

// when
carRace.setWinnerCarNames();

// then
var winnerField = CarRace.class.getDeclaredField("winnerCarNames");
winnerField.setAccessible(true);
List<String> winners = (List<String>) winnerField.get(carRace);

assertEquals(List.of("a"), winners);

} catch (Exception e) {
fail("테스트 실행 중 오류: " + e.getMessage());
}
}
}

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

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class CarTest {

@Test
@DisplayName("자동차 이름과 번호가 올바르게 설정되어야 한다.")
void createCar() {
Car car = new Car(1, "pobi");
assertEquals("pobi", car.getCarName());
assertEquals(0, car.getPosition());
}

@RepeatedTest(10)
@DisplayName("랜덤값이 3 이하이면 이동하지 않는다.")
void tryMove_shouldNotMoveWhenRandomIsLessThanOrEqualTo3() {
Car car = new Car(1, "test");

// 10번 정도 실행해 랜덤 결과의 평균적 이동을 확인
int before = car.getPosition();
car.tryMove();
int after = car.getPosition();

// 랜덤이라 정확히 0 또는 1 단정 불가하지만, 0 이상이어야 함
assertTrue(after >= before);
}

@Test
@DisplayName("자동차의 위치(position)는 canMove가 true일 때 증가한다.")
void positionIncreasesWhenCanMoveIsTrue() {
Car car = new Car(1, "hello");
int before = car.getPosition();

// 강제로 여러 번 이동 시도
for (int i = 0; i < 20; i++) {
car.tryMove();
}

assertTrue(car.getPosition() >= before);
}
}