diff --git a/README.md b/README.md index f1f49b598b0..c0e2ad1be80 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,40 @@ - [X] 결과 값은 정수로 떨어지는 값으로 한정 - [X] 0으로 나눌경우 IllegalArgumentException throw - [X] 사칙 연산을 모두 포함하는 기능 구현 :: Calculator 클래스 구현 - - [X] 사칙 연산 수행 \ No newline at end of file + - [X] 사칙 연산 수행 + +# 3단계 - 자동차 경주 +## 기능 요구사항 +* 초간단 자동차 경주 게임을 구현한다. +* 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. +* 사용자는 몇 대의 자동차로 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. +* 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4이상일 경우이다. +* 자동차의 상태를 화면에 출력한다. 어느 시점에 출력할 것인지에 대한 제약은 없다. + +## 프로그래밍 요구사항 +* 모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외 + * 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. + * UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. +* 자바 코드 컨벤션을 지키면서 프로그래밍한다. + * 참고문서: https://google.github.io/styleguide/javaguide.html 또는 https://myeonguni.tistory.com/1596 +* else 예약어를 쓰지 않는다. + * 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다. + * else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. + +## To do + +### UI + - [X] 입력값을 받는 InputView 클래스 구현 + - [X] 사용자에게 적절한 UI 메세지를 출력한다 + - [X] 입력값을 받아서 메인 기능 클래스로 위임한다 + - [X] 결과를 출력 할 ResultView 클래스 구현 + - [X] 자동차의 상태를 화면에 출력한다 + +### 기능 + - [X] 자동차는 전진 하거나 멈출 수 있다 + - [X] 0 ~ 9 사이의 random 값을 구한 후 random 값이 4이상일 경우 전진한다 + - [X] 자동차 여러대가 경주를 한다 + - [X] 경기에 참가하는 자동차의 대수는 사용자가 정한다 + - [X] 여러대의 자동차가 이동 요구사항을 만족하면서 이동한다 + - [X] 자동차가 몇 번의 이동을 할지는 사용자가 정한다 + - [X] 각 이동 시점마다 자동차의 상태를 ResultView 클래스를 통해 출력한다 \ No newline at end of file diff --git a/src/main/java/racing/Car.java b/src/main/java/racing/Car.java new file mode 100644 index 00000000000..bae8cd51891 --- /dev/null +++ b/src/main/java/racing/Car.java @@ -0,0 +1,21 @@ +package racing; + +public class Car { + private static final int MOVING_CONDITION_COUNT = 3; + private static final int MOVING_DISTANCE = 1; + private final MovingCondition movingCondition; + + private int position; + + public Car(MovingCondition condition) { + movingCondition = condition; + } + + public void move() { + position = movingCondition.getCondition() > MOVING_CONDITION_COUNT ? position + MOVING_DISTANCE : position; + } + + public int getPosition() { + return position; + } +} diff --git a/src/main/java/racing/Cars.java b/src/main/java/racing/Cars.java new file mode 100644 index 00000000000..5ee4dc12637 --- /dev/null +++ b/src/main/java/racing/Cars.java @@ -0,0 +1,23 @@ +package racing; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Cars { + private final List cars; + + public Cars(int count, MovingCondition condition) { + cars = Stream.generate(() -> new Car(condition)) + .limit(count) + .collect(Collectors.toList()); + } + + public List getCars() { + return cars; + } + + public void move() { + cars.forEach(Car::move); + } +} \ No newline at end of file diff --git a/src/main/java/racing/FixedCondition.java b/src/main/java/racing/FixedCondition.java new file mode 100644 index 00000000000..536adecb3c1 --- /dev/null +++ b/src/main/java/racing/FixedCondition.java @@ -0,0 +1,14 @@ +package racing; + +public class FixedCondition implements MovingCondition { + private final int possibleCondition; + + public FixedCondition(int possibleCondition) { + this.possibleCondition = possibleCondition; + } + + @Override + public int getCondition() { + return possibleCondition; + } +} diff --git a/src/main/java/racing/InputView.java b/src/main/java/racing/InputView.java new file mode 100644 index 00000000000..0257f0c4e75 --- /dev/null +++ b/src/main/java/racing/InputView.java @@ -0,0 +1,19 @@ +package racing; + +import java.util.Scanner; + +public class InputView { + private static final Scanner SCANNER = new Scanner(System.in); + private static final String NUMBER_OF_CARS = "자동차 대수는 몇 대 인가요?"; + private static final String TRY_TIMES = "시도할 회수는 몇 회 인가요?"; + + public static int numberOfCars() { + System.out.println(NUMBER_OF_CARS); + return SCANNER.nextInt(); + } + + public static int tryTimes() { + System.out.println(TRY_TIMES); + return SCANNER.nextInt(); + } +} diff --git a/src/main/java/racing/MovingCondition.java b/src/main/java/racing/MovingCondition.java new file mode 100644 index 00000000000..e8bb6f74f4e --- /dev/null +++ b/src/main/java/racing/MovingCondition.java @@ -0,0 +1,5 @@ +package racing; + +public interface MovingCondition { + int getCondition(); +} diff --git a/src/main/java/racing/Racing.java b/src/main/java/racing/Racing.java new file mode 100644 index 00000000000..e41727f2722 --- /dev/null +++ b/src/main/java/racing/Racing.java @@ -0,0 +1,20 @@ +package racing; + +public class Racing { + private final int tryTimes; + private final Cars cars; + + public Racing(int movingCount, Cars cars) { + this.tryTimes = movingCount; + this.cars = cars; + } + + public void start() { + ResultView.printResultMessage(); + for (int i = 0; i < tryTimes; i++) { + ResultView.printLineBreak(); + cars.move(); + ResultView.printRacingStatus(cars); + } + } +} diff --git a/src/main/java/racing/RacingApplication.java b/src/main/java/racing/RacingApplication.java new file mode 100644 index 00000000000..7fa1e15722b --- /dev/null +++ b/src/main/java/racing/RacingApplication.java @@ -0,0 +1,9 @@ +package racing; + +public class RacingApplication { + public static void main(String[] args) { + Cars cars = new Cars(InputView.numberOfCars(), new RandomCondition()); + Racing racing = new Racing(InputView.tryTimes(), cars); + racing.start(); + } +} diff --git a/src/main/java/racing/RandomCondition.java b/src/main/java/racing/RandomCondition.java new file mode 100644 index 00000000000..3faea1aace4 --- /dev/null +++ b/src/main/java/racing/RandomCondition.java @@ -0,0 +1,16 @@ +package racing; + +import java.util.Random; + +public class RandomCondition implements MovingCondition{ + private final Random random; + + public RandomCondition() { + random = new Random(); + } + + @Override + public int getCondition() { + return random.nextInt(10); + } +} diff --git a/src/main/java/racing/ResultView.java b/src/main/java/racing/ResultView.java new file mode 100644 index 00000000000..546575c61af --- /dev/null +++ b/src/main/java/racing/ResultView.java @@ -0,0 +1,23 @@ +package racing; + +public class ResultView { + private static final String EXECUTION_RESULT = "실행 결과"; + private static final String CAR_SYMBOL = "-"; + + public static void printResultMessage() { + System.out.println(EXECUTION_RESULT); + } + + public static void printRacingStatus(Cars cars) { + cars.getCars().forEach(car -> { + for (int i = 0; i < car.getPosition(); i++) { + System.out.print(CAR_SYMBOL); + } + printLineBreak(); + }); + } + + public static void printLineBreak() { + System.out.println(); + } +} diff --git a/src/test/java/racing/CarTest.java b/src/test/java/racing/CarTest.java new file mode 100644 index 00000000000..23c5a6d48cd --- /dev/null +++ b/src/test/java/racing/CarTest.java @@ -0,0 +1,38 @@ +package racing; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CarTest { + + @DisplayName("자동차는 전진 하거나 멈출 수 있다") + @ParameterizedTest + @CsvSource(value = {"0, 0", "4, 1", "9, 1"}) + public void move(int condition, int expected){ + //given + Car car = new Car(new FixedCondition(condition)); + + //when + car.move(); + + //then + assertEquals(car.getPosition(), expected); + } + + @DisplayName("0 ~ 9 사이의 random 값을 구한 후 random 값이 4이상일 경우 전진한다") + @ParameterizedTest + @CsvSource(value = {"0, 0", "4, 1", "9, 1"}) + public void movingCar(int condition, int expected) { + //given + Car car = new Car(new FixedCondition(condition)); + + //when + car.move(); + + //then + assertEquals(car.getPosition(), expected); + } +} diff --git a/src/test/java/racing/CarsTest.java b/src/test/java/racing/CarsTest.java new file mode 100644 index 00000000000..1d65b74f821 --- /dev/null +++ b/src/test/java/racing/CarsTest.java @@ -0,0 +1,37 @@ +package racing; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CarsTest { + + @DisplayName("경기에 참가하는 자동차의 대수는 사용자가 정한다") + @ParameterizedTest + @CsvSource(value = {"3, 3", "5, 5", "7, 7"}) + public void numberOfCars(int count, int expected) { + //given + Cars cars = new Cars(count, new RandomCondition()); + + //then + assertEquals(expected, cars.getCars().size()); + } + + @DisplayName("여러대의 자동차가 이동 요구사항을 만족하면서 이동한다") + @ParameterizedTest + @CsvSource(value = {"3, 3, 0", "5, 5, 1", "7, 7, 1"}) + public void expectedMove(int count, int condition, int expectedPosition) { + //given + Cars cars = new Cars(count, new FixedCondition(condition)); + + //when + cars.move(); + + //then + for (Car car : cars.getCars()) { + assertEquals(expectedPosition, car.getPosition()); + } + } +} diff --git a/src/test/java/racing/RacingTest.java b/src/test/java/racing/RacingTest.java new file mode 100644 index 00000000000..85185b5a979 --- /dev/null +++ b/src/test/java/racing/RacingTest.java @@ -0,0 +1,27 @@ +package racing; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RacingTest { + + @DisplayName("자동차가 몇 번의 이동을 할지는 사용자가 정한다") + @ParameterizedTest + @CsvSource(value = {"3, 4, 5", " 5, 4, 5"}) + public void carsMove(int numberOfCars, int condition, int movingCount) { + //given + Cars cars = new Cars(numberOfCars, new FixedCondition(condition)); + Racing racing = new Racing(movingCount, cars); + + //when + racing.start(); + + //then + for (Car car : cars.getCars()) { + assertEquals(movingCount, car.getPosition()); + } + } +}