diff --git a/README.md b/README.md index c550c4c2a09..368beaaea4f 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,17 @@ * 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다. ## 온라인 코드 리뷰 과정 -* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview) \ No newline at end of file +* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview) + +# 3단계 - 자동차 경주 +* 초간단 자동차 경주 게임을 구현한다. +* 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. +* 사용자는 몇 대의 자동차로 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. +* 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4이상일 경우이다. +* 자동차의 상태를 화면에 출력한다. 어느 시점에 출력할 것인지에 대한 제약은 없다. + +# 4단계 - 자동차 경주 (우승자) +* 각 자동차에 이름을 부여할 수 있다. 자동차 이름은 5자를 초과할 수 없다. +* 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. +* 자동차 이름은 쉼표(,)를 기준으로 구분한다. +* 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한명 이상일 수 있다. \ No newline at end of file diff --git a/src/main/java/Car.java b/src/main/java/Car.java deleted file mode 100644 index acf10887a63..00000000000 --- a/src/main/java/Car.java +++ /dev/null @@ -1,22 +0,0 @@ -import java.util.Random; - -public class Car { - private int distance; - - public Car() { - this.distance = 0; - } - - public int getDistance() { - return distance; - } - - public void move(int randomNumber) { - if (isMove(randomNumber)) distance++; - } - - public boolean isMove(int randomNumber) { - return randomNumber >= 4; - } - -} diff --git a/src/main/java/InputView.java b/src/main/java/InputView.java deleted file mode 100644 index 53a4aec13fa..00000000000 --- a/src/main/java/InputView.java +++ /dev/null @@ -1,17 +0,0 @@ -import java.util.Scanner; - -public class InputView { - - private static final Scanner scanner = new Scanner(System.in); - - public static int getAnswerToInteger(String question) { - while (true) { - System.out.println(question); - try { - return scanner.nextInt(); - } catch (Exception e) { - System.out.println("유효하지 않은 타입입니다."); - } - } - } -} diff --git a/src/main/java/RacingCar.java b/src/main/java/RacingCar.java deleted file mode 100644 index 6fb4abe50d3..00000000000 --- a/src/main/java/RacingCar.java +++ /dev/null @@ -1,36 +0,0 @@ -import java.util.ArrayList; -import java.util.List; - -public class RacingCar { - - public static void main(String[] args) { - - int carCount = InputView.getAnswerToInteger("자동차 대수는 몇 대 인가요?"); - - List cars = generateCars(carCount); - - int tryTimes = InputView.getAnswerToInteger("시도할 회수는 몇 회 인가요?"); - - ResultView.println("실행 결과"); - - for (int j = 0; j < tryTimes; j++) { - moveCars(cars); - ResultView.println(""); - } - } - - private static void moveCars(List carList) { - for (Car car : carList) { - car.move(RandomUtils.generateRandomNumber(10)); - ResultView.printResult(car); - } - } - - private static List generateCars(int carCount) { - List carList = new ArrayList<>(); - for (int i = 0; i < carCount; i++) { - carList.add(new Car()); - } - return carList; - } -} diff --git a/src/main/java/ResultView.java b/src/main/java/ResultView.java deleted file mode 100644 index cd7805b2dae..00000000000 --- a/src/main/java/ResultView.java +++ /dev/null @@ -1,10 +0,0 @@ -public class ResultView { - - public static void printResult(Car car) { - println("-".repeat(car.getDistance())); - } - - public static void println(String str) { - System.out.println(str); - } -} diff --git a/src/main/java/StringAddCalculator.java b/src/main/java/StringAddCalculator.java index 5c66ceaf18e..97121c162b5 100644 --- a/src/main/java/StringAddCalculator.java +++ b/src/main/java/StringAddCalculator.java @@ -1,15 +1,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class StringAddCalculator -{ +public class StringAddCalculator { private static final String DELIMITER = ",|:"; private static final int DELIMITER_GROUP_ID = 1; private static final int STRING_GROUP_ID = 2; - public int add(String str){ + public int add(String str) { - if(isBlank(str)){ + if (isBlank(str)) { str = "0"; } @@ -20,7 +19,7 @@ public int add(String str){ private int[] toIntArr(String[] strArr) { int[] intArr = new int[strArr.length]; - for(int i = 0; i < strArr.length; i++){ + for (int i = 0; i < strArr.length; i++) { intArr[i] = toInt(strArr[i]); } return intArr; @@ -28,7 +27,7 @@ private int[] toIntArr(String[] strArr) { private int toInt(String str) { int num = Integer.parseInt(str); - if (num < 0){ + if (num < 0) { throw new IllegalArgumentException(num + "음수는 허용하지 않습니다."); } return num; @@ -36,7 +35,7 @@ private int toInt(String str) { private static int getSum(int[] numbers) { int sum = 0; - for(int num : numbers){ + for (int num : numbers) { sum += num; } return sum; diff --git a/src/main/java/racing/RacingMain.java b/src/main/java/racing/RacingMain.java new file mode 100644 index 00000000000..28f24cdd384 --- /dev/null +++ b/src/main/java/racing/RacingMain.java @@ -0,0 +1,29 @@ +package racing; + +import racing.domain.Car; +import racing.domain.Judgement; +import racing.domain.RacingGame; +import racing.views.InputView; +import racing.views.ResultView; + +import java.util.List; + +public class RacingMain { + + public static void main(String[] args) { + + String[] carNames = InputView.inputCarNames(); + + RacingGame racingGame = new RacingGame(carNames); + + int tryTimes = InputView.inputTryTimes(); + + ResultView.showResult(); + + for (int j = 0; j < tryTimes; j++) { + racingGame.moveCars(); + } + + ResultView.printWinner(new Judgement(racingGame.getCars()).getWinnerCars()); + } +} diff --git a/src/main/java/racing/data/Messages.java b/src/main/java/racing/data/Messages.java new file mode 100644 index 00000000000..29ca34ef12a --- /dev/null +++ b/src/main/java/racing/data/Messages.java @@ -0,0 +1,13 @@ +package racing.data; + +public class Messages { + + public static final String ERROR_VALID_TYPE = "유효한 타입이 아닙니다."; + public static final String ERROR_CAR_NAME_LENGTH = " 자동차 이름은 5자를 초과할 수 없습니다."; + public static final String ASK_CAR_NAMES = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; + public static final String ASK_CAR_COUNT = "자동차 대수는 몇 대 인가요?"; + public static final String ASK_TRY_TIMES = "시도할 회수는 몇 회 인가요?"; + public static final String RACE_RESULT = "실행 결과"; + public static final String RACE_WINNER = "가 최종 우승 했습니다."; + public static final String INVALID_NUMBER_RANGE = "음수가 될 수 없습니다."; +} diff --git a/src/main/java/racing/domain/Car.java b/src/main/java/racing/domain/Car.java new file mode 100644 index 00000000000..29715cf3ad1 --- /dev/null +++ b/src/main/java/racing/domain/Car.java @@ -0,0 +1,53 @@ +package racing.domain; + +import racing.data.Messages; + +public class Car { + + public static final int MOVE_THRESHOLD = 4; + public static final int CAR_NAME_THRESHOLD = 5; + + private final Position position; + private String carName; + + public Car() { + this.position = new Position(0); + } + + public Car(String carName, int position) { + this.carName = carName; + this.position = new Position(position); + } + + public Car(String carName) { + if (carName == null || carName.isBlank() || carName.length() > CAR_NAME_THRESHOLD) { + throw new IllegalArgumentException(Messages.ERROR_CAR_NAME_LENGTH); + } + this.position = new Position(0); + this.carName = carName; + } + + public int getPosition() { + return this.position.getValue(); + } + + public String getCarName() { + return this.carName; + } + + public void move(int randomNumber) { + if (isMovable(randomNumber)) this.position.increase(); + } + + public boolean isMovable(int randomNumber) { + return randomNumber >= MOVE_THRESHOLD; + } + + public int max(int maxPosition) { + return this.position.max(maxPosition); + } + + public boolean isSamePosition(int position) { + return this.position.equals(new Position(position)); + } +} diff --git a/src/main/java/racing/domain/Cars.java b/src/main/java/racing/domain/Cars.java new file mode 100644 index 00000000000..7ad0dc4b0da --- /dev/null +++ b/src/main/java/racing/domain/Cars.java @@ -0,0 +1,26 @@ +package racing.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Cars { + + List cars; + + public Cars() { + this.cars = new ArrayList(); + } + + public List getCars() { + return cars; + } + + public Car getCar(int index) { + return cars.get(index); + } + + public Cars addCar(Car car) { + this.cars.add(car); + return this; + } +} diff --git a/src/main/java/racing/domain/Judgement.java b/src/main/java/racing/domain/Judgement.java new file mode 100644 index 00000000000..b4490acb23d --- /dev/null +++ b/src/main/java/racing/domain/Judgement.java @@ -0,0 +1,31 @@ +package racing.domain; + +import java.util.List; +import java.util.stream.Collectors; + +public class Judgement { + + private final Cars cars; + + public Judgement(Cars cars){ + this.cars = cars; + } + + public List getWinnerCars() { + + return cars.getCars() + .stream() + .filter(car -> car.isSamePosition(getMaxPosition())) + .collect(Collectors.toList()); + } + + private int getMaxPosition() { + int maxPosition = 0; + + for (Car car : cars.getCars()) { + maxPosition = car.max(maxPosition); + } + + return maxPosition; + } +} diff --git a/src/main/java/racing/domain/Position.java b/src/main/java/racing/domain/Position.java new file mode 100644 index 00000000000..994777bcb9f --- /dev/null +++ b/src/main/java/racing/domain/Position.java @@ -0,0 +1,41 @@ +package racing.domain; + +import racing.data.Messages; + +import java.util.Objects; + +public class Position { + + int value = 0; + + public Position(int value) { + if (value < 0) { + throw new IllegalArgumentException(Messages.INVALID_NUMBER_RANGE); + } + this.value = value; + } + + public int getValue() { + return this.value; + } + + public void increase() { + this.value++; + } + + public int max(int position) { + return Math.max(this.value, position); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Position position = (Position) o; + return value == position.value; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +} diff --git a/src/main/java/racing/domain/RacingGame.java b/src/main/java/racing/domain/RacingGame.java new file mode 100644 index 00000000000..15d74277ec6 --- /dev/null +++ b/src/main/java/racing/domain/RacingGame.java @@ -0,0 +1,45 @@ +package racing.domain; + +import racing.utils.RandomUtils; +import racing.views.ResultView; + +public class RacingGame { + + private final Cars cars; + + public Cars getCars() { + return this.cars; + } + + public RacingGame(int carCounts) { + this.cars = generateCars(carCounts); + } + + public RacingGame(String[] carNames) { + this.cars = generateCarsWithName(carNames); + } + + public void moveCars() { + for (Car car : cars.getCars()) { + car.move(RandomUtils.generateRandomNumber(10)); + ResultView.printResultWithName(car); + } + ResultView.println(""); + } + + private Cars generateCars(int carCount) { + Cars cars = new Cars(); + for (int i = 0; i < carCount; i++) { + cars.addCar(new Car()); + } + return cars; + } + + private Cars generateCarsWithName(String[] carNames) { + Cars cars = new Cars(); + for(String carName : carNames){ + cars.addCar(new Car(carName)); + } + return cars; + } +} \ No newline at end of file diff --git a/src/main/java/RandomUtils.java b/src/main/java/racing/utils/RandomUtils.java similarity index 90% rename from src/main/java/RandomUtils.java rename to src/main/java/racing/utils/RandomUtils.java index 0208e7d2fc6..e394a303fc9 100644 --- a/src/main/java/RandomUtils.java +++ b/src/main/java/racing/utils/RandomUtils.java @@ -1,3 +1,5 @@ +package racing.utils; + import java.util.Random; public class RandomUtils { diff --git a/src/main/java/racing/views/InputView.java b/src/main/java/racing/views/InputView.java new file mode 100644 index 00000000000..ea14259c4b9 --- /dev/null +++ b/src/main/java/racing/views/InputView.java @@ -0,0 +1,43 @@ +package racing.views; + +import racing.data.Messages; + +import java.util.Scanner; + +public class InputView { + + private static final Scanner scanner = new Scanner(System.in); + + public static int inputTryTimes() { + while (true) { + ResultView.println(Messages.ASK_TRY_TIMES); + try { + return scanner.nextInt(); + } catch (Exception e) { + ResultView.printTypeError(); + } + } + } + + public static String[] inputCarNames() { + while (true) { + ResultView.askCarNames(); + try { + return scanner.next().split(","); + } catch (Exception e) { + ResultView.printTypeError(); + } + } + } + + public static String[] inputCarCounts() { + while (true) { + ResultView.askCarCounts(); + try { + return scanner.next().split(","); + } catch (Exception e) { + ResultView.printTypeError(); + } + } + } +} diff --git a/src/main/java/racing/views/ResultView.java b/src/main/java/racing/views/ResultView.java new file mode 100644 index 00000000000..e5944aec5ab --- /dev/null +++ b/src/main/java/racing/views/ResultView.java @@ -0,0 +1,42 @@ +package racing.views; + +import racing.domain.Car; +import racing.data.Messages; + +import java.util.*; +import java.util.stream.Collectors; + +public class ResultView { + + public static void printResult(Car car) { + println("-".repeat(car.getPosition())); + } + + public static void println(String str) { + System.out.println(str); + } + + public static void printResultWithName(Car car) { + println(car.getCarName() + " : " + "-".repeat(car.getPosition())); + } + + public static void printTypeError() { + println(Messages.ERROR_VALID_TYPE); + } + + public static void askCarNames() { + println(Messages.ASK_CAR_NAMES); + } + + public static void askCarCounts() { + println(Messages.ASK_CAR_COUNT); + } + + public static void printWinner(List cars) { + println(cars.stream().map(Car::getCarName).collect(Collectors.joining(", ")) + Messages.RACE_WINNER); + } + + public static void showResult() { + println(Messages.RACE_RESULT); + } +} diff --git a/src/test/java/CarTest.java b/src/test/java/CarTest.java deleted file mode 100644 index a3410216cca..00000000000 --- a/src/test/java/CarTest.java +++ /dev/null @@ -1,35 +0,0 @@ -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class CarTest { - - Car car = new Car(); - - @Test - @DisplayName(value = "Random Number 생성 테스트") - void generateRandomNumber() { - assertThat(RandomUtils.generateRandomNumber(10)).isBetween(0, 9); - } - - @Test - @DisplayName(value = "랜덤 함수가 0~9 사이의 값이 나오는지 확인") - void 차량_전진조건_테스트() { - assertThat(RandomUtils.generateRandomNumber(10)).isBetween(0, 9); - } - - @Test - @DisplayName(value = "랜덤 함수가 4 이상인 경우 true / 미만이면 false 인지 검증") - void 차량_전진여부_테스트() { - int randomNum = RandomUtils.generateRandomNumber(10); - assertThat(randomNum >= 4).isEqualTo(car.isMove(randomNum)); - } - - @Test - @DisplayName(value = "차량이 전진하는지 여부 확인") - void 차량_전진_테스트() { - car.move(RandomUtils.generateRandomNumber(10)); - assertThat("-".repeat(car.getDistance())).isIn("-", ""); - } -} \ No newline at end of file diff --git a/src/test/java/racing/CarTest.java b/src/test/java/racing/CarTest.java new file mode 100644 index 00000000000..c9423a35960 --- /dev/null +++ b/src/test/java/racing/CarTest.java @@ -0,0 +1,68 @@ +package racing; + +import racing.domain.Car; +import racing.domain.Cars; +import racing.domain.Judgement; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racing.utils.RandomUtils; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class CarTest { + + Car car = new Car(); + + @Test + @DisplayName(value = "Random Number 생성 테스트") + void generateRandomNumber() { + Assertions.assertThat(RandomUtils.generateRandomNumber(10)).isBetween(0, 9); + } + + @Test + @DisplayName(value = "랜덤 함수가 0~9 사이의 값이 나오는지 확인") + void 차량_전진조건_테스트() { + Assertions.assertThat(RandomUtils.generateRandomNumber(10)).isBetween(0, 9); + } + + @Test + @DisplayName(value = "랜덤 함수가 4 이상인 경우 true / 미만이면 false 인지 검증") + void 차량_전진여부_테스트() { + int randomNum = RandomUtils.generateRandomNumber(10); + assertThat(randomNum >= Car.MOVE_THRESHOLD).isEqualTo(car.isMovable(randomNum)); + } + + @Test + @DisplayName(value = "차량이 전진하는지 여부 확인") + void 차량_전진_테스트() { + car.move(RandomUtils.generateRandomNumber(10)); + assertThat("-".repeat(car.getPosition())).isIn("-", ""); + } + + @Test + @DisplayName(value = "승자 테스트") + void 승자_출력_테스트() { + + Cars cars = new Cars() + .addCar(new Car("A",4)) + .addCar(new Car("B",4)) + .addCar(new Car("C",2)); + + Judgement judgement = new Judgement(cars); + + assertThat(judgement.getWinnerCars()).isEqualTo(List.of(cars.getCar(0), cars.getCar(1))); + } + + @Test + @DisplayName(value = "차량 이름 5글자 넘어간 경우 에러 케이스") + void 차량명_초과_테스트() { + assertThatThrownBy(() -> { + new Car("가나다라마바"); + }).isInstanceOf(IllegalArgumentException.class); + } + +} \ No newline at end of file