Skip to content
10 changes: 10 additions & 0 deletions src/main/java/RacingCarApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import controller.RacingGameController;
import domain.RandomNumberGenerateStrategy;

public class RacingCarApplication {

public static void main(String[] args) {
final RacingGameController controller = new RacingGameController();
controller.run(new RandomNumberGenerateStrategy());
}
}
34 changes: 34 additions & 0 deletions src/main/java/controller/RacingGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package controller;

import domain.Cars;
import domain.CarsMapper;
import domain.NumberGenerateStrategy;
import domain.RacingGame;
import java.util.List;
import view.InputView;
import view.OutputView;

public class RacingGameController {

public void run(final NumberGenerateStrategy strategy) {
final List<String> carNames = InputView.readCarNames();
final Cars cars = CarsMapper.mapByCarNames(strategy, carNames);
final RacingGame racingGame = new RacingGame(cars);

final int attemptNumber = InputView.readAttemptNumber();

OutputView.printResult();
race(racingGame, attemptNumber);

final Cars winningCars = racingGame.findWinningCars();
OutputView.printResultOfWinningCars(winningCars);
}

private void race(final RacingGame racingGame,
final int attemptNumber) {
for (int i = 0; i < attemptNumber; i++) {
racingGame.race();
OutputView.printCarsInfo(racingGame.getCars());
}
}
}
40 changes: 40 additions & 0 deletions src/main/java/domain/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package domain;

public class Car {

private final NumberGenerateStrategy strategy;
private final CarName name;
private int position;

public Car(final NumberGenerateStrategy strategy,
final String name) {
this(strategy, name, 0);
}

public Car(final NumberGenerateStrategy strategy,
final String name,
final int position) {
this.strategy = strategy;
this.name = CarName.from(name);
this.position = position;
}

public void move() {
final int value = strategy.generate();
if (value >= 4) {
position++;
}
}

public boolean isPositionSame(final int position) {
return this.position == position;
}

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

public int getPosition() {
return position;
}
}
24 changes: 24 additions & 0 deletions src/main/java/domain/CarName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package domain;

public record CarName(
String value
) {

private static final int MAX_LENGTH = 5;


public static CarName from(final String name) {
validateName(name);
return new CarName(name);
}

private static void validateName(final String name) {
if (name.isBlank()) {
throw new IllegalArgumentException("[ERROR] 자동차 이름은 공백일 수 없습니다.");
}

if (name.length() > MAX_LENGTH) {
throw new IllegalArgumentException("[ERROR] 자동차 이름은 5자 이하여야 합니다.");
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/domain/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package domain;

import java.util.List;

public class Cars {

private final List<Car> cars;

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

public void move() {
cars.forEach(Car::move);
}

public List<Car> findCarsByPosition(final int position) {
return cars.stream()
.filter(car -> car.isPositionSame(position))
.toList();
}

public int findMaxPosition() {
return cars.stream()
.mapToInt(Car::getPosition)
.max()
.orElse(0);
}

public List<Car> getCars() {
return List.copyOf(cars);
}
}
15 changes: 15 additions & 0 deletions src/main/java/domain/CarsMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package domain;

import java.util.List;
import java.util.stream.Collectors;

public class CarsMapper {

public static Cars mapByCarNames(final NumberGenerateStrategy strategy,
final List<String> carNames) {
final List<Car> cars = carNames.stream()
.map(carName -> new Car(strategy, carName))
.collect(Collectors.toList());
return new Cars(cars);
}
}
6 changes: 6 additions & 0 deletions src/main/java/domain/NumberGenerateStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package domain;

public interface NumberGenerateStrategy {

int generate();
}
26 changes: 26 additions & 0 deletions src/main/java/domain/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package domain;

import java.util.List;

public class RacingGame {

private final Cars cars;

public RacingGame(final Cars cars) {
this.cars = cars;
}

public void race() {
cars.move();
}

public Cars findWinningCars() {
final int maxPosition = cars.findMaxPosition();
final List<Car> winningCars = cars.findCarsByPosition(maxPosition);
return new Cars(winningCars);
}

public Cars getCars() {
return cars;
}
}
12 changes: 12 additions & 0 deletions src/main/java/domain/RandomNumberGenerateStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package domain;

import java.util.Random;

public class RandomNumberGenerateStrategy implements NumberGenerateStrategy {

@Override
public int generate() {
final Random random = new Random();
return random.nextInt(10);
}
}
22 changes: 22 additions & 0 deletions src/main/java/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package view;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class InputView {

private static final Scanner scanner = new Scanner(System.in);

public static List<String> readCarNames() {
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).");
final String carNames = scanner.nextLine();
return new ArrayList<>(Arrays.asList(carNames.split(",")));
}

public static int readAttemptNumber() {
System.out.println("시도할 회수는 몇회인가요?");
return scanner.nextInt();
}
}
38 changes: 38 additions & 0 deletions src/main/java/view/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package view;

import domain.Car;
import domain.Cars;
import java.util.List;

public class OutputView {

public static void printResult() {
System.out.println("실행 결과");
}

public static void printCarsInfo(final Cars cars) {
for (final Car car : cars.getCars()) {
final String name = car.getName();
final String position = getPositionFormat(car.getPosition());
System.out.printf("%s : %s\n", name, position);
}
System.out.println();
}

private static String getPositionFormat(final int position) {
final StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < position; i++) {
stringBuilder.append("-");
}
return stringBuilder.toString();
}

public static void printResultOfWinningCars(final Cars winningCars) {
final List<Car> cars = winningCars.getCars();
final List<String> carNames = cars.stream()
.map(Car::getName)
.toList();
final String winningCarNames = String.join(", ", carNames);
System.out.printf("%s가 최종 우승했습니다.\n", winningCarNames);
}
}
43 changes: 43 additions & 0 deletions src/test/java/domain/CarNameTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package domain;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

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

@SuppressWarnings("NonAsciiCharacters")
@DisplayName("자동차 이름 테스트")
class CarNameTest {

@Test
void 자동차_이름_생성() {
// given
final String carName = "코코닥";

// when & then
assertThatCode(() -> CarName.from(carName)).doesNotThrowAnyException();
}

@Test
void 자동차_이름의_길이가_5보다_크면_생성에_실패한다() {
// given
final String carName = "A".repeat(6);

// when & then
assertThatThrownBy(() -> CarName.from(carName))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("[ERROR] 자동차 이름은 5자 이하여야 합니다.");
}

@Test
void 자동차_이름은_공백일_수_없다() {
// given
final String carName = " ";

// when & then
assertThatThrownBy(() -> CarName.from(carName))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("[ERROR] 자동차 이름은 공백일 수 없습니다.");
}
}
82 changes: 82 additions & 0 deletions src/test/java/domain/CarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package domain;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

@SuppressWarnings("NonAsciiCharacters")
@DisplayNameGeneration(ReplaceUnderscores.class)
class CarTest {

@Test
void 자동차는_4이상일때_움직인다() {
// given
NumberGenerateStrategy strategy = new TestNumberGenerateStrategy(
new ArrayList<>(Arrays.asList(4))
);
final Car car = new Car(strategy, "코코닥");

// when
car.move();

// then
assertThat(car.getPosition()).isEqualTo(1);
}

@Test
void 자동차는_4미만일때_움직이지_않는다() {
// given
NumberGenerateStrategy strategy = new TestNumberGenerateStrategy(
new ArrayList<>(Arrays.asList(3))
);
final Car car = new Car(strategy, "코코닥");

// when
car.move();

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

@ParameterizedTest
@CsvSource({
"8, true",
"4, false"
})
void 임의의_위치가_자동차의_현재_위치와_같은지_판단할_수_있다(final int position, final boolean expected) {
// given
final Car car = new Car(new RandomNumberGenerateStrategy(),
"코코닥",
8);

// when
final boolean actual = car.isPositionSame(position);

// then
assertThat(actual).isEqualTo(expected);
}

static class TestNumberGenerateStrategy implements NumberGenerateStrategy {

private final List<Integer> numbers;

public TestNumberGenerateStrategy(final List<Integer> numbers) {
this.numbers = new ArrayList<>(numbers);
}

@Override
public int generate() {
if (numbers.isEmpty()) {
throw new IllegalStateException("더 이상 숫자를 생성할 수 없습니다.");
}
return numbers.remove(0);
}
}
}
Loading