diff --git a/README.md b/README.md index 9afa7d38..dff5e7fd 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,18 @@ ## 프로젝트 구조 ``` +- domain/ + - Car.java : 자동차 객체 및 관련 메소드 정의 + - CarRegistry.java : 자동차 목록 관리 + - Game.java : 전반적인 게임 실행 담당 + - RoundResult.java : 게임 결과를 담는 객체 - generator/ - - NumberGenerator.java : 숫자 생성기 Interface 정의 - - FixedNumberGenerator.java : 고정 숫자 생성기 - - RandomNumberGenerator.java : 랜덤 숫자 생성기 -- Car.java : 자동차 객체 정의 -- CarRegistry.java : 자동차 목록 관리 -- Game.java : 전반적인 게임 실행 담당 + - NumberGenerator.java : 숫자 생성기 Interface 정의 + - RandomNumberGenerator.java : 랜덤 숫자 생성기 +- view/ + - InputView.java : 사용자로부터 입력 받는 기능 수행 + - ResultView.java : 사용자에게 출력 하는 기능 수행 +- Main.java : Main Entrypoint ``` ## 시작하기 diff --git a/src/main/java/io/suhan/racingcar/Game.java b/src/main/java/io/suhan/racingcar/Game.java deleted file mode 100644 index e7f828c3..00000000 --- a/src/main/java/io/suhan/racingcar/Game.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.suhan.racingcar; - -import java.util.List; - -public class Game { - private final CarRegistry carRegistry; - private final int rounds; - - private Game(int rounds) { - this.carRegistry = CarRegistry.of(); - this.rounds = rounds; - } - - public static Game of() { - return new Game(0); - } - - public static Game of(int rounds) { - return new Game(rounds); - } - - public void start() { - for (int i = 0; i < this.rounds; i++) { - carRegistry.moveCars(); - } - // TODO: 우승자 출력 - } - - public List getWinners() { - int bestPosition = carRegistry.getBestPosition(); - - return carRegistry.getRegisteredCars() - .stream() - .filter((car) -> car.getPosition() == bestPosition) - .toList(); - } - - public CarRegistry getCarRegistry() { - return carRegistry; - } -} diff --git a/src/main/java/io/suhan/racingcar/Main.java b/src/main/java/io/suhan/racingcar/Main.java new file mode 100644 index 00000000..4417e1c9 --- /dev/null +++ b/src/main/java/io/suhan/racingcar/Main.java @@ -0,0 +1,24 @@ +package io.suhan.racingcar; + +import io.suhan.racingcar.domain.Car; +import io.suhan.racingcar.domain.Game; +import io.suhan.racingcar.domain.RoundResult; +import io.suhan.racingcar.view.InputView; +import io.suhan.racingcar.view.ResultView; +import java.util.List; + +public class Main { + public static void main(String[] args) { + List names = InputView.getCarNames(); + int count = InputView.getTrialsCount(); + + Game game = Game.of(count); + game.getCarRegistry().registerCars(names); + + List results = game.execute(); + List winners = game.getWinners(); + + ResultView.printExecutionResult(results); + ResultView.printWinners(winners); + } +} diff --git a/src/main/java/io/suhan/racingcar/Car.java b/src/main/java/io/suhan/racingcar/domain/Car.java similarity index 62% rename from src/main/java/io/suhan/racingcar/Car.java rename to src/main/java/io/suhan/racingcar/domain/Car.java index f4e2d848..82aa3931 100644 --- a/src/main/java/io/suhan/racingcar/Car.java +++ b/src/main/java/io/suhan/racingcar/domain/Car.java @@ -1,10 +1,11 @@ -package io.suhan.racingcar; +package io.suhan.racingcar.domain; import io.suhan.racingcar.generator.NumberGenerator; import io.suhan.racingcar.generator.RandomNumberGenerator; public class Car { - private static final int CAR_MOVE_THRESHOLD = 4; + public static final int CAR_MOVE_THRESHOLD = 4; + public static final int CAR_NAME_MAXIMUM_LENGTH = 5; private final String name; private final NumberGenerator generator; @@ -17,10 +18,14 @@ private Car(String name, NumberGenerator generator) { } public static Car of(String name) { - return new Car(name, new RandomNumberGenerator()); + return Car.of(name, new RandomNumberGenerator()); } public static Car of(String name, NumberGenerator generator) { + if (name.length() > CAR_NAME_MAXIMUM_LENGTH) { + throw new IllegalArgumentException("자동차의 이름은 " + CAR_NAME_MAXIMUM_LENGTH + "자 이하만 가능합니다."); + } + return new Car(name, generator); } @@ -43,4 +48,11 @@ public String getName() { public int getPosition() { return position; } + + public Car copy() { + Car copy = Car.of(this.name, this.generator); + copy.position = this.position; + + return copy; + } } diff --git a/src/main/java/io/suhan/racingcar/CarRegistry.java b/src/main/java/io/suhan/racingcar/domain/CarRegistry.java similarity index 59% rename from src/main/java/io/suhan/racingcar/CarRegistry.java rename to src/main/java/io/suhan/racingcar/domain/CarRegistry.java index 2f01a054..06f9774d 100644 --- a/src/main/java/io/suhan/racingcar/CarRegistry.java +++ b/src/main/java/io/suhan/racingcar/domain/CarRegistry.java @@ -1,4 +1,4 @@ -package io.suhan.racingcar; +package io.suhan.racingcar.domain; import java.util.ArrayList; import java.util.List; @@ -28,6 +28,22 @@ public void moveCars() { } } + public void registerCars(List names) { + for (String name : names) { + Car car = Car.of(name); + cars.add(car); + } + } + + public List getCarsWithBestPosition() { + int bestPosition = getBestPosition(); + + return getRegisteredCars() + .stream() + .filter((car) -> car.getPosition() == bestPosition) + .toList(); + } + public int getBestPosition() { return cars .stream() diff --git a/src/main/java/io/suhan/racingcar/domain/Game.java b/src/main/java/io/suhan/racingcar/domain/Game.java new file mode 100644 index 00000000..9fde4d31 --- /dev/null +++ b/src/main/java/io/suhan/racingcar/domain/Game.java @@ -0,0 +1,48 @@ +package io.suhan.racingcar.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Game { + private static final int GAME_ROUNDS_MINIMUM = 1; + + private final CarRegistry carRegistry; + private final int rounds; + + private Game(int rounds) { + this.carRegistry = CarRegistry.of(); + this.rounds = rounds; + } + + public static Game of() { + return Game.of(1); + } + + public static Game of(int rounds) { + if (rounds < GAME_ROUNDS_MINIMUM) { + throw new IllegalArgumentException("시도 횟수는 " + GAME_ROUNDS_MINIMUM + " 이상이어야 합니다."); + } + + return new Game(rounds); + } + + public List execute() { + List results = new ArrayList<>(); + + for (int i = 0; i < rounds; i++) { + carRegistry.moveCars(); + RoundResult result = RoundResult.of(carRegistry.getRegisteredCars()); + results.add(result); + } + + return results; + } + + public List getWinners() { + return carRegistry.getCarsWithBestPosition(); + } + + public CarRegistry getCarRegistry() { + return carRegistry; + } +} diff --git a/src/main/java/io/suhan/racingcar/domain/RoundResult.java b/src/main/java/io/suhan/racingcar/domain/RoundResult.java new file mode 100644 index 00000000..fd7256fc --- /dev/null +++ b/src/main/java/io/suhan/racingcar/domain/RoundResult.java @@ -0,0 +1,22 @@ +package io.suhan.racingcar.domain; + +import java.util.List; +import java.util.stream.Collectors; + +public class RoundResult { + private final List cars; + + private RoundResult(List cars) { + this.cars = cars; + } + + public static RoundResult of(List cars) { + List copied = cars.stream().map(Car::copy).collect(Collectors.toList()); + + return new RoundResult(copied); + } + + public List getCars() { + return cars; + } +} diff --git a/src/main/java/io/suhan/racingcar/view/InputView.java b/src/main/java/io/suhan/racingcar/view/InputView.java new file mode 100644 index 00000000..c2f96dfb --- /dev/null +++ b/src/main/java/io/suhan/racingcar/view/InputView.java @@ -0,0 +1,22 @@ +package io.suhan.racingcar.view; + +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 getCarNames() { + System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); + String input = scanner.next(); + + return Arrays.stream(input.split(",")).toList(); + } + + public static int getTrialsCount() { + System.out.println("시도할 회수는 몇회인가요?"); + + return scanner.nextInt(); + } +} diff --git a/src/main/java/io/suhan/racingcar/view/ResultView.java b/src/main/java/io/suhan/racingcar/view/ResultView.java new file mode 100644 index 00000000..626bc568 --- /dev/null +++ b/src/main/java/io/suhan/racingcar/view/ResultView.java @@ -0,0 +1,30 @@ +package io.suhan.racingcar.view; + +import io.suhan.racingcar.domain.Car; +import io.suhan.racingcar.domain.RoundResult; +import java.util.List; +import java.util.stream.Collectors; + +public class ResultView { + public static void printExecutionResult(List results) { + System.out.println("\n실행 결과"); + results.forEach((result) -> printRoundResult(result.getCars())); + } + + private static void printRoundResult(List cars) { + cars.forEach(ResultView::printCurrentPosition); + System.out.println(); // newline + } + + public static void printWinners(List winners) { + System.out.println(winners.stream().map(Car::getName).collect(Collectors.joining(", ")) + "가 최종 우승했습니다."); + } + + private static void printCurrentPosition(Car car) { + System.out.println(car.getName() + " : " + buildProgressBar(car.getPosition())); + } + + private static String buildProgressBar(int value) { + return "-".repeat(Math.max(0, value)); + } +} diff --git a/src/test/java/io/suhan/racingcar/CarTest.java b/src/test/java/io/suhan/racingcar/domain/CarTest.java similarity index 77% rename from src/test/java/io/suhan/racingcar/CarTest.java rename to src/test/java/io/suhan/racingcar/domain/CarTest.java index 2f82e606..8f4e9e03 100644 --- a/src/test/java/io/suhan/racingcar/CarTest.java +++ b/src/test/java/io/suhan/racingcar/domain/CarTest.java @@ -1,7 +1,9 @@ -package io.suhan.racingcar; +package io.suhan.racingcar.domain; +import static io.suhan.racingcar.domain.Car.CAR_MOVE_THRESHOLD; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import io.suhan.racingcar.generator.FixedNumberGenerator; import org.junit.jupiter.api.DisplayNameGeneration; @@ -22,7 +24,7 @@ public class CarTest { @Test void 생성된_값이_4_이상일_경우_자동차가_움직일_수_있다() { - FixedNumberGenerator generator = new FixedNumberGenerator(4); + FixedNumberGenerator generator = new FixedNumberGenerator(CAR_MOVE_THRESHOLD); Car car = Car.of("neo", generator); car.move(); @@ -34,7 +36,7 @@ public class CarTest { @Test void 생성된_값이_3_이하일_경우_자동차가_멈춘다() { - FixedNumberGenerator generator = new FixedNumberGenerator(3); + FixedNumberGenerator generator = new FixedNumberGenerator(CAR_MOVE_THRESHOLD - 1); Car car = Car.of("neo", generator); car.move(); @@ -43,4 +45,9 @@ public class CarTest { assertEquals(0, position); } + + @Test + void 자동차의_이름은_5자_이하만_가능하다() { + assertThrows(IllegalArgumentException.class, () -> Car.of("123456")); + } } diff --git a/src/test/java/io/suhan/racingcar/GameTest.java b/src/test/java/io/suhan/racingcar/domain/GameTest.java similarity index 78% rename from src/test/java/io/suhan/racingcar/GameTest.java rename to src/test/java/io/suhan/racingcar/domain/GameTest.java index c0f7d0d2..d0f8bea0 100644 --- a/src/test/java/io/suhan/racingcar/GameTest.java +++ b/src/test/java/io/suhan/racingcar/domain/GameTest.java @@ -1,6 +1,8 @@ -package io.suhan.racingcar; +package io.suhan.racingcar.domain; +import static io.suhan.racingcar.domain.Car.CAR_MOVE_THRESHOLD; import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import io.suhan.racingcar.generator.FixedNumberGenerator; import java.util.List; @@ -17,11 +19,7 @@ public class GameTest { Game game = Game.of(); - for (String name : names) { - Car car = Car.of(name); - - game.getCarRegistry().register(car); - } + game.getCarRegistry().registerCars(names); List registeredNames = game.getCarRegistry().getRegisteredCars().stream().map(Car::getName).toList(); @@ -33,8 +31,8 @@ public class GameTest { // given Game game = Game.of(5); - FixedNumberGenerator forwardGenerator = new FixedNumberGenerator(4); - FixedNumberGenerator stopGenerator = new FixedNumberGenerator(3); + FixedNumberGenerator forwardGenerator = new FixedNumberGenerator(CAR_MOVE_THRESHOLD); + FixedNumberGenerator stopGenerator = new FixedNumberGenerator(CAR_MOVE_THRESHOLD - 1); Car neo = Car.of("neo", forwardGenerator); Car brie = Car.of("brie", stopGenerator); @@ -47,10 +45,15 @@ public class GameTest { List expectedNames = List.of("neo", "brown"); // when - game.start(); + game.execute(); List winnerNames = game.getWinners().stream().map(Car::getName).toList(); // then assertIterableEquals(expectedNames, winnerNames); } + + @Test + void 시도_횟수는_1_이상만_가능하다() { + assertThrows(IllegalArgumentException.class, () -> Game.of(0)); + } }