diff --git a/.gradle/7.5.1/checksums/checksums.lock b/.gradle/7.5.1/checksums/checksums.lock deleted file mode 100644 index 934157a..0000000 Binary files a/.gradle/7.5.1/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/7.5.1/checksums/md5-checksums.bin b/.gradle/7.5.1/checksums/md5-checksums.bin deleted file mode 100644 index 263d11f..0000000 Binary files a/.gradle/7.5.1/checksums/md5-checksums.bin and /dev/null differ diff --git a/.gradle/7.5.1/checksums/sha1-checksums.bin b/.gradle/7.5.1/checksums/sha1-checksums.bin deleted file mode 100644 index cde3792..0000000 Binary files a/.gradle/7.5.1/checksums/sha1-checksums.bin and /dev/null differ diff --git a/.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock b/.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index a6528b3..0000000 Binary files a/.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock and /dev/null differ diff --git a/.gradle/7.5.1/dependencies-accessors/gc.properties b/.gradle/7.5.1/dependencies-accessors/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/7.5.1/executionHistory/executionHistory.bin b/.gradle/7.5.1/executionHistory/executionHistory.bin deleted file mode 100644 index 88fdcb3..0000000 Binary files a/.gradle/7.5.1/executionHistory/executionHistory.bin and /dev/null differ diff --git a/.gradle/7.5.1/executionHistory/executionHistory.lock b/.gradle/7.5.1/executionHistory/executionHistory.lock deleted file mode 100644 index 43c24bb..0000000 Binary files a/.gradle/7.5.1/executionHistory/executionHistory.lock and /dev/null differ diff --git a/.gradle/7.5.1/fileChanges/last-build.bin b/.gradle/7.5.1/fileChanges/last-build.bin deleted file mode 100644 index f76dd23..0000000 Binary files a/.gradle/7.5.1/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/7.5.1/fileHashes/fileHashes.bin b/.gradle/7.5.1/fileHashes/fileHashes.bin deleted file mode 100644 index f2d7079..0000000 Binary files a/.gradle/7.5.1/fileHashes/fileHashes.bin and /dev/null differ diff --git a/.gradle/7.5.1/fileHashes/fileHashes.lock b/.gradle/7.5.1/fileHashes/fileHashes.lock deleted file mode 100644 index 34ba174..0000000 Binary files a/.gradle/7.5.1/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/7.5.1/gc.properties b/.gradle/7.5.1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index c1ca5fe..0000000 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index e2d0866..0000000 --- a/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Wed Apr 26 00:06:57 KST 2023 -gradle.version=7.5.1 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin deleted file mode 100644 index 7ab2265..0000000 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and /dev/null differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe deleted file mode 100644 index 65b22a5..0000000 Binary files a/.gradle/file-system.probe and /dev/null differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/build.gradle b/build.gradle index 54696fe..9f4a182 100644 --- a/build.gradle +++ b/build.gradle @@ -11,9 +11,8 @@ repositories { dependencies { implementation 'org.projectlombok:lombok:1.18.22' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2' + testImplementation platform('org.junit:junit-bom:5.9.1') + testImplementation 'org.junit.jupiter:junit-jupiter' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.0' } diff --git a/out/production/classes/Application.class b/out/production/classes/Application.class deleted file mode 100644 index 1e92083..0000000 Binary files a/out/production/classes/Application.class and /dev/null differ diff --git a/src/main/java/controller/RacingGameController.java b/src/main/java/controller/RacingGameController.java index ae71c24..bc4ae12 100644 --- a/src/main/java/controller/RacingGameController.java +++ b/src/main/java/controller/RacingGameController.java @@ -1,30 +1,45 @@ package controller; import exception.InvalidTryCountException; -import model.Cars; +import model.RacingCars; import properties.ErrorMessage; +import service.Movable; +import service.RandomMoveConstraintsStrategy; import view.RacingGameView; public class RacingGameController { - - private Cars cars; + private RacingCars racingCars; private int tryCount; + private Movable moveStrategy; public void start() { - init(); + readyForRace(); race(); - result(); + printWinners(); + } + + private void readyForRace() { + initMoveStrategy(); + askCarNames(); + askTryCount(); } - private void init() { + private void initMoveStrategy() { + this.moveStrategy = new RandomMoveConstraintsStrategy(); + } + + private void askCarNames() { RacingGameView.askCarNames(); - initCars(RacingGameView.getConsoleInput()); + initRacingCars(RacingGameView.getConsoleInput()); + } + + private void askTryCount() { RacingGameView.askTryCount(); initTryCount(RacingGameView.getConsoleInput()); } - private void initCars(final String carNames) { - cars = new Cars(carNames); + private void initRacingCars(final String carNames) { + this.racingCars = new RacingCars(carNames); } private void initTryCount(final String tryCount) { @@ -46,13 +61,13 @@ private int parsedTryCount(String tryCount) { private void race() { int playCount = 0; while (playCount != tryCount) { - cars.move(); - cars.moveResult(); + racingCars.move(moveStrategy); + racingCars.moveResult(); playCount++; } } - private void result() { - cars.pickWinners(); + private void printWinners() { + racingCars.printWinners(); } } diff --git a/src/main/java/exception/InvalidPositionException.java b/src/main/java/exception/InvalidPositionException.java new file mode 100644 index 0000000..0e72e34 --- /dev/null +++ b/src/main/java/exception/InvalidPositionException.java @@ -0,0 +1,7 @@ +package exception; + +public class InvalidPositionException extends RacingGameException { + public InvalidPositionException(String message) { + super(message); + } +} diff --git a/src/main/java/model/Car.java b/src/main/java/model/Car.java index 4dbc66f..edff2f8 100644 --- a/src/main/java/model/Car.java +++ b/src/main/java/model/Car.java @@ -1,11 +1,12 @@ package model; +import service.Movable; import view.RacingGameView; public class Car { private final CarName name; - private final Position position; + private Position position; public Car(String name) { this(name, 0); @@ -16,26 +17,26 @@ public Car(String name, int position) { this.position = new Position(position); } - public void move() { - if (!position.canMove()) + public void move(Movable moveStrategy) { + if (!moveStrategy.canMove()) return; - position.increasePosition(); + this.position = this.position.increasePosition(); } public void printCurrPosition() { RacingGameView.printText( - name.getName() + " : " + position.indicateCharacterByPosition() + name.getName() + " : " + position.getPosition() ); } public int getCurrentPosition() { - return position.getPosition(); + return position.getPosition().length(); } public String getNameIfWin(int winnerPosition) { - if (position.getPosition() == winnerPosition) - return name.getName() + ", "; + if (position.getPosition().length() == winnerPosition) + return name.getName(); return ""; } diff --git a/src/main/java/model/Cars.java b/src/main/java/model/Cars.java index 06f827a..3bbb9de 100644 --- a/src/main/java/model/Cars.java +++ b/src/main/java/model/Cars.java @@ -2,17 +2,24 @@ import exception.InvalidCarNameException; import properties.ErrorMessage; +import service.Movable; import view.RacingGameView; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class Cars { private final List cars; private static final String CAR_NAME_DELIMITER = ","; + public Cars(List cars) { + this.cars = cars; + } + public Cars(String carNames) { this.cars = makeCars(carNames); } @@ -22,8 +29,8 @@ public List makeCars(String carNames) { return parsedCarNames(carNames).stream().map(Car::new).toList(); } - public void move() { - cars.forEach(Car::move); + public void move(Movable moveStrategy) { + cars.forEach(car -> car.move(moveStrategy)); } public void moveResult() { @@ -32,12 +39,19 @@ public void moveResult() { RacingGameView.printLine(); } - public void pickWinners() { - int winnerPosition = cars.stream().mapToInt(Car::getCurrentPosition).max().getAsInt(); + public int getWinnerPosition() { + return cars.stream().mapToInt(Car::getCurrentPosition).max().getAsInt(); + } + + public List findWinnerNamesByPosition(int winnerPosition) { StringBuilder result = new StringBuilder(); - cars.forEach(car -> result.append(car.getNameIfWin(winnerPosition))); - String winners = result.substring(0, result.length() - 2); - RacingGameView.printText(winners + "가 최종 우승했습니다."); + cars.forEach(car -> + result.append(car.getNameIfWin(winnerPosition)).append(",") + ); + + return Stream.of(result.toString().split(",")) + .filter(winner -> !winner.isBlank()) + .collect(Collectors.toList()); } private void validNames(String carNames) { diff --git a/src/main/java/model/Position.java b/src/main/java/model/Position.java index 45c34ae..054bd58 100644 --- a/src/main/java/model/Position.java +++ b/src/main/java/model/Position.java @@ -1,34 +1,45 @@ package model; -public class Position { +import exception.InvalidPositionException; +import properties.ErrorMessage; + +import java.util.Objects; - private int position; +public class Position { - private static final int MOVE_CONDITION = 4; + private final int position; private static final String DISPLAY_CHARACTER = "-"; - public Position(int position) { - this.position = position; + public Position() { + this(0); } - public int getPosition() { - return position; + public Position(int position) { + if (position < 0) + throw new InvalidPositionException(ErrorMessage.Position.NOT_POSITIVE_NUMBER); + + this.position = position; } - public void increasePosition() { - position++; + public Position increasePosition() { + return new Position(position + 1); } - public boolean canMove() { - return createRandomNumber() >= MOVE_CONDITION; + public String getPosition() { + return DISPLAY_CHARACTER.repeat(Math.max(0, position)); } - public int createRandomNumber() { - return (int) (Math.random() * 10); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Position position1 = (Position) o; + return position == position1.position; } - public String indicateCharacterByPosition() { - return DISPLAY_CHARACTER.repeat(Math.max(0, position)); + @Override + public int hashCode() { + return Objects.hash(position); } } diff --git a/src/main/java/model/RacingCars.java b/src/main/java/model/RacingCars.java new file mode 100644 index 0000000..f8e632f --- /dev/null +++ b/src/main/java/model/RacingCars.java @@ -0,0 +1,34 @@ +package model; + +import org.apache.commons.lang3.StringUtils; +import service.Movable; +import view.RacingGameView; + +import java.util.List; + +public class RacingCars { + + private final Cars cars; + + public RacingCars(String carNames) { + this.cars = new Cars(carNames); + } + + public void move(Movable moveStrategy) { + cars.move(moveStrategy); + } + + public void moveResult() { + cars.moveResult(); + } + + public void printWinners() { + RacingGameView.printText(findWinnerNames() + "가 최종 우승했습니다."); + } + + private String findWinnerNames() { + List winners = cars.findWinnerNamesByPosition(cars.getWinnerPosition()); + return StringUtils.join(winners, ", "); + } + +} diff --git a/src/main/java/properties/ErrorMessage.java b/src/main/java/properties/ErrorMessage.java index 425139d..b1bd194 100644 --- a/src/main/java/properties/ErrorMessage.java +++ b/src/main/java/properties/ErrorMessage.java @@ -14,4 +14,8 @@ public static class CarName { public static class TryCount { public static final String NOT_POSITIVE_NUMBER = "[ERROR] 시도 횟수 입력값은 1 이상의 정수여야만 합니다."; } + + public static class Position { + public static final String NOT_POSITIVE_NUMBER = "[ERROR] 위치 값은 음수일 수 없습니다."; + } } diff --git a/src/main/java/service/Movable.java b/src/main/java/service/Movable.java new file mode 100644 index 0000000..e1b1513 --- /dev/null +++ b/src/main/java/service/Movable.java @@ -0,0 +1,6 @@ +package service; + +@FunctionalInterface +public interface Movable { + boolean canMove(); +} diff --git a/src/main/java/service/RandomMoveConstraintsStrategy.java b/src/main/java/service/RandomMoveConstraintsStrategy.java new file mode 100644 index 0000000..567ccb6 --- /dev/null +++ b/src/main/java/service/RandomMoveConstraintsStrategy.java @@ -0,0 +1,11 @@ +package service; + +public class RandomMoveConstraintsStrategy implements Movable { + + private static final int CONSTRAINTS = 4; + + @Override + public boolean canMove() { + return (int) (Math.random() * 10) >= CONSTRAINTS; + } +} diff --git a/src/test/java/model/CarNameTest.java b/src/test/java/model/CarNameTest.java new file mode 100644 index 0000000..38e4e2c --- /dev/null +++ b/src/test/java/model/CarNameTest.java @@ -0,0 +1,21 @@ +package model; + +import exception.InvalidCarNameException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import properties.ErrorMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class CarNameTest { + + @ParameterizedTest + @ValueSource(strings = {"123456"}) + @DisplayName("잘못된 자동차 이름 에러 테스트") + void test(String parameter) { + InvalidCarNameException invalidCarNameException = assertThrows(InvalidCarNameException.class, () -> new CarName(parameter)); + assertEquals(ErrorMessage.CarName.NOT_EXCEED_CAR_NAME_LENGTH, invalidCarNameException.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/model/CarTest.java b/src/test/java/model/CarTest.java new file mode 100644 index 0000000..84ccbd0 --- /dev/null +++ b/src/test/java/model/CarTest.java @@ -0,0 +1,27 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CarTest { + + @Test + @DisplayName("move전략의 움직임 여부가 true면 위치값이 1 증가한다.") + void test() { + Car car = new Car("seung"); + car.move(() -> true); + + assertEquals(1, car.getCurrentPosition()); + } + + @Test + @DisplayName("move전략의 움직임 여부가 false면 움직이지 않는다.") + void test2() { + Car car = new Car("seung"); + car.move(() -> false); + + assertEquals(0, car.getCurrentPosition()); + } +} diff --git a/src/test/java/model/CarsTest.java b/src/test/java/model/CarsTest.java index 0360d96..e901a97 100644 --- a/src/test/java/model/CarsTest.java +++ b/src/test/java/model/CarsTest.java @@ -3,15 +3,17 @@ import exception.InvalidCarNameException; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import properties.ErrorMessage; +import service.Movable; +import java.util.List; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; class CarsTest { @@ -23,6 +25,34 @@ void test(String parameter, String name) { assertEquals(name, invalidCarNameException.getMessage()); } + @Test + @DisplayName("가장 멀리간 자동차의 position은 winnerPosition이다") + void test2() { + Cars cars = new Cars("ko,ho,seung"); + cars.move(() -> true); + assertEquals(cars.getWinnerPosition(), 1); + } + + @Test + @DisplayName("우승자의 position으로 우승자들의 이름을 가져온다.") + void test3() { + Movable movable = () -> true; + + var porsche = new Car("포르쉐"); + porsche.move(movable); + + var zhivagen = new Car("지바겐"); + zhivagen.move(movable); + zhivagen.move(movable); + + var lamborghini = new Car("람보르기니"); + lamborghini.move(movable); + lamborghini.move(movable); + + var cars = new Cars(List.of(porsche, zhivagen, lamborghini)); + assertTrue(cars.findWinnerNamesByPosition(2).toString().contains("지바겐, 람보르기니")); + } + static Stream invalidParameters() { return Stream.of( Arguments.of("", ErrorMessage.CarName.NOT_ALLOW_EMPTY_OR_CONTAINS_WHITE_SPACE), diff --git a/src/test/java/model/PositionTest.java b/src/test/java/model/PositionTest.java new file mode 100644 index 0000000..235417b --- /dev/null +++ b/src/test/java/model/PositionTest.java @@ -0,0 +1,22 @@ +package model; + +import exception.InvalidPositionException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PositionTest { + + @Test + @DisplayName("위치 값은 음수일 수 없다.") + void test() { + Assertions.assertThrows(InvalidPositionException.class, () -> new Position(-1)); + } + + @Test + @DisplayName("move를 하면 position을 증가시킨다.") + void test2() { + Assertions.assertEquals(new Position(1), new Position().increasePosition()); + } + +} \ No newline at end of file