diff --git a/README.md b/README.md index 91744e0bd0..ec13cd0a34 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,68 @@ # java-racingcar + 자동차 경주 게임 미션 저장소 +## 기능 요구사항 + +- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. + +- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. + +- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. + +- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. + +- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 - + 멈춘다. + +- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. + +## 기능 구현 목록 + +- 자동차의 이름을 한 줄로 입력받는 기능 + + - 입력받은 자동차의 이름을 쉼표(,)로 나누는 기능 + + - 이름이 5자를 넘지 않는지 확인하는 기능 + + - 중복된 이름이 존재하는지 여부를 확인하는 기능 + +- 몇 번의 이동을 할 것인지를 입력받는 기능 + + - 이동 횟수 입력에 대한 유효성을 검증하는 기능 + +- 전진하는 조건을 생성하는 기능 + + - 0에서 9까지의 random 값을 생성하는 기능 + + - random 값이 4 이상일 경우 전진하는 기능 + + - random 값이 3 이하인 경우 멈추는 기능 + +- 주어진 횟수에 따라 자동차를 전진하는 기능 + + - 자동차가 전진하는 경우 위치를 1 증가시키는 기능 + +- 여러대의 자동차를 관리하는 기능 + + - 자동차들을 이동시키는 기능 + + - 매번 이동이 끝난 후 자동차의 이름과 이동상황을 출력하는 기능 + +- 우승자를 출력하는 기능 + + - 자동차가 우승한 위치에 있는지 확인하는 기능 + + - 우승한 자동차(들)을 반환해주는 기능 + + - 우승한 자동차(들)을 출력하는 기능 + +## 기여자 + +- [라흐]() + +- [스티치]() + ## 우아한테크코스 코드리뷰 -* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) \ No newline at end of file + +- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) diff --git a/src/main/java/com/woowacourse/racingGame/Application.java b/src/main/java/com/woowacourse/racingGame/Application.java new file mode 100644 index 0000000000..66ee65a2b7 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/Application.java @@ -0,0 +1,69 @@ +package com.woowacourse.racingGame; + +import java.util.ArrayList; +import java.util.List; + +import com.woowacourse.racingGame.domain.Cars; +import com.woowacourse.racingGame.domain.CarsFactory; +import com.woowacourse.racingGame.domain.MovementNumber; +import com.woowacourse.racingGame.domain.RandomMovableStrategy; +import com.woowacourse.racingGame.domain.Result; +import com.woowacourse.racingGame.view.InputView; +import com.woowacourse.racingGame.view.OutputView; + +public class Application { + private static InputView inputview; + private static OutputView outputView; + + public static void main(String[] args) { + inputview = new InputView(); + outputView = new OutputView(); + + final Cars cars = generateCars(); + final MovementNumber movementNumber = receiveInputMovementNumber(); + + // NOTE : Log처럼 기록을 출력하는 것이 맞는지 여부 물어보기. + final List results = playRacingGame(cars, movementNumber); + + outputView.printRacingGameResult(results); + outputView.printWinners(getFinalResult(results, movementNumber)); + } + + private static Cars generateCars() { + try { + final String carName = inputview.inputCarName(); + return CarsFactory.generate(carName, new RandomMovableStrategy()); + } catch (IllegalArgumentException e) { + outputView.printExceptionMessage(e.getMessage()); + return generateCars(); + } + } + + private static MovementNumber receiveInputMovementNumber() { + try { + final String movementNumber = inputview.inputMovementNumber(); + return new MovementNumber(movementNumber); + } catch (IllegalArgumentException e) { + outputView.printExceptionMessage(e.getMessage()); + return receiveInputMovementNumber(); + } + } + + private static List playRacingGame(final Cars cars, final MovementNumber movementNumber) { + List results = new ArrayList<>(); + + for (int i = 0; i < movementNumber.getMovementNumber(); i++) { + attemptMove(cars); + results.add(new Result(cars)); + } + return results; + } + + private static void attemptMove(final Cars cars) { + cars.attemptMove(); + } + + private static Result getFinalResult(final List results, final MovementNumber movementNumber) { + return results.get(movementNumber.getMovementNumber() - 1); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Car.java b/src/main/java/com/woowacourse/racingGame/domain/Car.java new file mode 100644 index 0000000000..c0dc4a3bf6 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Car.java @@ -0,0 +1,55 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Objects; + +public class Car { + private final MovableStrategy movableStrategy; + private final Name name; + private Position position; + + public Car(Name name, MovableStrategy movableStrategy) { + this.position = Position.ZERO; + this.name = name; + this.movableStrategy = movableStrategy; + } + + Car(Name name, Position position, MovableStrategy movableStrategy) { + this.position = position; + this.name = name; + this.movableStrategy = movableStrategy; + } + + public void attemptMoveThrough() { + if (movableStrategy.isMovable()) { + position = position.increaseByMovingUnit(); + } + } + + // NOTE : 불변객체의 이점? + public boolean isSamePosition(final int winnerPosition) { + return position.getPosition() == winnerPosition; + } + + public String getName() { + return name.getName(); + } + + public int getPosition() { + return position.getPosition(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Car car = (Car)o; + return name.equals(car.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Cars.java b/src/main/java/com/woowacourse/racingGame/domain/Cars.java new file mode 100644 index 0000000000..b8712b82fc --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Cars.java @@ -0,0 +1,54 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class Cars { + private final List cars; + + public Cars(final List cars) { + checkDuplicate(cars); + this.cars = cars; + } + + private void checkDuplicate(final List cars) { + if (isDuplicateExist(cars)) { + throw new IllegalArgumentException("중복된 이름이 존재합니다."); + } + } + + private boolean isDuplicateExist(final List cars) { + long distinctCarSize = Arrays.stream(cars.toArray()) + .distinct() + .count(); + return cars.size() != distinctCarSize; + } + + public void attemptMove() { + for (Car car : cars) { + car.attemptMoveThrough(); + } + } + + // NOTE : unmodifiable로 처리해주는 것이 의미를 가지는지 리뷰어님께 여쭤보기. + public List getCars() { + return Collections.unmodifiableList(cars); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Cars cars = (Cars)o; + return Objects.equals(this.cars, cars.cars); + } + + @Override + public int hashCode() { + return Objects.hash(cars); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/CarsFactory.java b/src/main/java/com/woowacourse/racingGame/domain/CarsFactory.java new file mode 100644 index 0000000000..eb2053d314 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/CarsFactory.java @@ -0,0 +1,14 @@ +package com.woowacourse.racingGame.domain; + +import java.util.stream.Collectors; + +import com.woowacourse.racingGame.utils.StringUtil; + +public class CarsFactory { + public static Cars generate(final String inputCarName, MovableStrategy movableStrategy) { + return new Cars(StringUtil.split(inputCarName).stream() + .map(Name::new) + .map(name -> new Car(name, movableStrategy)) + .collect(Collectors.toList())); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/MovableStrategy.java b/src/main/java/com/woowacourse/racingGame/domain/MovableStrategy.java new file mode 100644 index 0000000000..3cf453fd97 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/MovableStrategy.java @@ -0,0 +1,5 @@ +package com.woowacourse.racingGame.domain; + +public interface MovableStrategy { + boolean isMovable(); +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/MovementNumber.java b/src/main/java/com/woowacourse/racingGame/domain/MovementNumber.java new file mode 100644 index 0000000000..831ee46055 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/MovementNumber.java @@ -0,0 +1,31 @@ +package com.woowacourse.racingGame.domain; + +public class MovementNumber { + private static final int ZERO = 0; + + private final int movementNumber; + + public MovementNumber(final String inputMovementNumber) { + final int parsedNumber = checkValid(inputMovementNumber); + checkPositive(parsedNumber); + this.movementNumber = parsedNumber; + } + + private int checkValid(final String number) { + try { + return Integer.parseInt(number); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("입력한 이동 횟수가 숫자가 아닙니다."); + } + } + + private void checkPositive(final int parsedNumber) { + if (parsedNumber <= ZERO) { + throw new IllegalArgumentException("입력한 이동 횟수가 0보다 작은 값입니다."); + } + } + + public int getMovementNumber() { + return this.movementNumber; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Name.java b/src/main/java/com/woowacourse/racingGame/domain/Name.java new file mode 100644 index 0000000000..05020af940 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Name.java @@ -0,0 +1,46 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Objects; + +public class Name { + private static final int MAX_NAME_LENGTH_LIMIT = 5; + + private final String name; + + public Name(final String name) { + checkNullOrEmpty(name); + checkValidLength(name); + this.name = name; + } + + private void checkNullOrEmpty(final String name) { + if (Objects.isNull(name) || name.isEmpty()) { + throw new IllegalArgumentException("null 또는 빈 문자열입니다."); + } + } + + private void checkValidLength(final String name) { + if (name.length() > MAX_NAME_LENGTH_LIMIT) { + throw new IllegalArgumentException("이름은 5자이하만 가능합니다."); + } + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Name name = (Name)o; + return Objects.equals(this.name, name.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/PlannedMovableStrategy.java b/src/main/java/com/woowacourse/racingGame/domain/PlannedMovableStrategy.java new file mode 100644 index 0000000000..4b349c15cf --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/PlannedMovableStrategy.java @@ -0,0 +1,14 @@ +package com.woowacourse.racingGame.domain; + +public class PlannedMovableStrategy implements MovableStrategy { + private Power power; + + public PlannedMovableStrategy(final Power power) { + this.power = power; + } + + @Override + public boolean isMovable() { + return power.isMovable(); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Position.java b/src/main/java/com/woowacourse/racingGame/domain/Position.java new file mode 100644 index 0000000000..1467408d40 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Position.java @@ -0,0 +1,41 @@ +package com.woowacourse.racingGame.domain; + +import java.util.HashMap; +import java.util.Map; + +public class Position { + private static final int INIT_POSITION = 0; + private static final int MOVING_UNIT = 1; + private static final Map cache = new HashMap<>(); + public static final Position ZERO = new Position(INIT_POSITION); + + private final int position; + + public Position(final int position) { + checkValid(position); + this.position = position; + } + + private void checkValid(final int position) { + if (position < INIT_POSITION) { + throw new IllegalArgumentException("위치가 음수가 될 수 없습니다."); + } + } + + public static Position valueOf(final int position) { + if (cache.containsKey(position)) { + return cache.get(position); + } + Position newPosition = new Position(position); + cache.put(position, newPosition); + return newPosition; + } + + public Position increaseByMovingUnit() { + return Position.valueOf(position + MOVING_UNIT); + } + + public int getPosition() { + return position; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Power.java b/src/main/java/com/woowacourse/racingGame/domain/Power.java new file mode 100644 index 0000000000..a44327bd74 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Power.java @@ -0,0 +1,33 @@ +package com.woowacourse.racingGame.domain; + +import java.util.HashMap; +import java.util.Map; + +public class Power { + private static final Map cache = new HashMap<>(); + private static final int MOVABLE_LOWER_BOUND = 4; + public static final int RANDOM_LOWER_BOUND = 0; + public static final int RANDOM_UPPER_BOUND = 9; + + private final int randomNumber; + + public Power(final int randomNumber) { + if (randomNumber < RANDOM_LOWER_BOUND || randomNumber > RANDOM_UPPER_BOUND) { + throw new IllegalArgumentException("유효하지 않은 랜덤 숫자입니다."); + } + this.randomNumber = randomNumber; + } + + public static Power valueOf(final int randomNumber) { + if (cache.containsKey(randomNumber)) { + return cache.get(randomNumber); + } + Power newPower = new Power(randomNumber); + cache.put(randomNumber, newPower); + return newPower; + } + + public boolean isMovable() { + return randomNumber >= MOVABLE_LOWER_BOUND; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/PowerGenerator.java b/src/main/java/com/woowacourse/racingGame/domain/PowerGenerator.java new file mode 100644 index 0000000000..77c9a560b6 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/PowerGenerator.java @@ -0,0 +1,13 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Random; + +public class PowerGenerator { + private static final int RANDOM_UPPER_BOUND = 10; + + public static Power generateRandomNumber() { + Random random = new Random(); + random.setSeed(System.nanoTime()); + return Power.valueOf(random.nextInt(RANDOM_UPPER_BOUND)); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/RandomMovableStrategy.java b/src/main/java/com/woowacourse/racingGame/domain/RandomMovableStrategy.java new file mode 100644 index 0000000000..2ad9f2cda0 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/RandomMovableStrategy.java @@ -0,0 +1,10 @@ +package com.woowacourse.racingGame.domain; + +public class RandomMovableStrategy implements MovableStrategy { + + @Override + public boolean isMovable() { + Power power = PowerGenerator.generateRandomNumber(); + return power.isMovable(); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Result.java b/src/main/java/com/woowacourse/racingGame/domain/Result.java new file mode 100644 index 0000000000..21103fdd81 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Result.java @@ -0,0 +1,46 @@ +package com.woowacourse.racingGame.domain; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class Result { + public static final int INIT_POSITION = 0; + + private final Cars cars; + private final Map racingCarStatus; + + public Result(final Cars cars) { + this.cars = cars; + racingCarStatus = generateRacingCarStatus(); + } + + private Map generateRacingCarStatus() { + return cars.getCars() + .stream() + .collect(Collectors.toMap(Car::getName, Car::getPosition, (a, b) -> b)); + } + + public List getWinners() { + final int maxPosition = getMaxPosition(); + return cars.getCars().stream() + .filter(car -> isWinner(car, maxPosition)) + .map(Car::getName) + .collect(Collectors.toList()); + } + + private int getMaxPosition() { + return cars.getCars().stream() + .map(Car::getPosition) + .max(Integer::compareTo) + .orElse(INIT_POSITION); + } + + private boolean isWinner(final Car car, final int maxPosition) { + return car.isSamePosition(maxPosition); + } + + public Map getRacingCarStatus() { + return racingCarStatus; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/utils/StringUtil.java b/src/main/java/com/woowacourse/racingGame/utils/StringUtil.java new file mode 100644 index 0000000000..a267f7b670 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/utils/StringUtil.java @@ -0,0 +1,29 @@ +package com.woowacourse.racingGame.utils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +// NOTE : 이건 View에 필요한 작업 처리의 보조? View와 같이 있어야하지 않을까? +public class StringUtil { + private static final String DELIMITER = ","; + private static final String DASH = "-"; + public static final String JOIN_DELIMITER = ", "; + + public static List split(final String inputCarName) { + return Arrays.stream(inputCarName.split(DELIMITER)) + .map(String::trim) + .collect(Collectors.toList()); + } + + public static String convertIntoDashBy(final int position) { + return Stream.generate(() -> DASH) + .limit(position) + .collect(Collectors.joining()); + } + + public static String joinNameOf(final List winners) { + return String.join(JOIN_DELIMITER, winners); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/view/InputView.java b/src/main/java/com/woowacourse/racingGame/view/InputView.java new file mode 100644 index 0000000000..488cb439e2 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/view/InputView.java @@ -0,0 +1,17 @@ +package com.woowacourse.racingGame.view; + +import java.util.Scanner; + +public class InputView { + private Scanner scanner = new Scanner(System.in); + + public String inputCarName() { + System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); + return scanner.nextLine(); + } + + public String inputMovementNumber() { + System.out.println("시도할 회수는 몇회인가요?"); + return scanner.nextLine(); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/view/OutputView.java b/src/main/java/com/woowacourse/racingGame/view/OutputView.java new file mode 100644 index 0000000000..bfcee31d0e --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/view/OutputView.java @@ -0,0 +1,34 @@ +package com.woowacourse.racingGame.view; + +import java.util.List; +import java.util.Map; + +import com.woowacourse.racingGame.domain.Result; +import com.woowacourse.racingGame.utils.StringUtil; + +public class OutputView { + public void printExceptionMessage(final String exceptionMessage) { + System.out.println(exceptionMessage); + } + + public void printRacingGameResult(final List results) { + for (Result result : results) { + printRacingCarStatus(result); + } + } + + private void printRacingCarStatus(final Result result) { + final Map racingCarStatus = result.getRacingCarStatus(); + + racingCarStatus.forEach((name, position) -> + System.out.println(name + + " : " + + StringUtil.convertIntoDashBy(position))); + System.out.println(); + } + + public void printWinners(final Result finalResult) { + final String winners = StringUtil.joinNameOf(finalResult.getWinners()); + System.out.println(String.format("%s가 최종 우승했습니다.", winners)); + } +} diff --git a/src/main/java/com/woowacourse/stringCalculator/StringCalculator.java b/src/main/java/com/woowacourse/stringCalculator/StringCalculator.java new file mode 100644 index 0000000000..431e73f6b6 --- /dev/null +++ b/src/main/java/com/woowacourse/stringCalculator/StringCalculator.java @@ -0,0 +1,54 @@ +package com.woowacourse.stringCalculator; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StringCalculator { + private static final int ZERO = 0; + private static final String DELIMITERS = "[:,]"; + private static final Pattern CUSTOM_REGEX = Pattern.compile("//(.)\n(.*)"); + + // TODO : 실행하는 main문이 없는 경우, private 생성자를 만들어 제한을 둬야 함. + + private StringCalculator() { + } + + public static int calculate(final String value) { + if (isNullOrEmpty(value)) { + return 0; + } + return calculateSum(splitByDelimiter(value)); + } + + private static int calculateSum(String[] values) { + int calculateResult = ZERO; + + for (String number : values) { + final int parsedNumber = Integer.parseInt(number); + + checkNegative(parsedNumber); + calculateResult += parsedNumber; + } + return calculateResult; + } + + private static void checkNegative(final int parsedNumber) { + if (parsedNumber < ZERO) { + throw new RuntimeException(); + } + } + + private static boolean isNullOrEmpty(final String value) { + return value == null || value.isEmpty(); + } + + private static String[] splitByDelimiter(final String value) { + Matcher matcher = CUSTOM_REGEX.matcher(value); + + if (matcher.find()) { + final String customDelimiter = matcher.group(1); + return matcher.group(2).split(customDelimiter); + } + return value.split(DELIMITERS); + } +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/test/java/com/woowacourse/racingGame/domain/CarTest.java b/src/test/java/com/woowacourse/racingGame/domain/CarTest.java new file mode 100644 index 0000000000..4540d689b8 --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/CarTest.java @@ -0,0 +1,58 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class CarTest { + private static final int MOVING_UNIT = 1; + + private MovableStrategy movableStrategy; + + @Test + void attemptMoveThrough_랜덤_값이_3_이하() { + final int stoppedRandomNumber = 3; + movableStrategy = new PlannedMovableStrategy(new Power(stoppedRandomNumber)); + final Car car = new Car(new Name("test"), Position.valueOf(4), movableStrategy); + car.attemptMoveThrough(); + + final int actual = car.getPosition(); + + final int expected = 4; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void attemptMoveThrough_랜덤_값이_4_이상() { + final int movedRandomNumber = 7; + final MovableStrategy movableStrategy = new PlannedMovableStrategy(new Power(movedRandomNumber)); + final Car car = new Car(new Name("test"), Position.valueOf(4), movableStrategy); + car.attemptMoveThrough(); + + final int actual = car.getPosition(); + + final int expected = 4 + MOVING_UNIT; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void isSamePosition_우승한_위치의_자동차() { + final int winnerPosition = 7; + final MovableStrategy movableStrategy = new RandomMovableStrategy(); + final Car car = new Car(new Name("test"), Position.valueOf(7), movableStrategy); + + assertThat(car.isSamePosition(winnerPosition)).isTrue(); + } + + @Test + void isSamePosition_우승하지_못한_위치의_자동차() { + final int winnerPosition = 7; + final MovableStrategy movableStrategy = new RandomMovableStrategy(); + final Car car = new Car(new Name("test"), Position.valueOf(6), movableStrategy); + + assertThat(car.isSamePosition(winnerPosition)).isFalse(); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/CarsFactoryTest.java b/src/test/java/com/woowacourse/racingGame/domain/CarsFactoryTest.java new file mode 100644 index 0000000000..dcb246d24b --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/CarsFactoryTest.java @@ -0,0 +1,28 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class CarsFactoryTest { + @Test + void generate_Cars_생성() { + final String inputCarName = "car1,car2,car3,car4"; + final MovableStrategy movableStrategy = new RandomMovableStrategy(); + final List cars = Arrays.asList( + new Car(new Name("car1"), movableStrategy), + new Car(new Name("car2"), movableStrategy), + new Car(new Name("car3"), movableStrategy), + new Car(new Name("car4"), movableStrategy)); + + final Cars actual = CarsFactory.generate(inputCarName, movableStrategy); + + final Cars expect = new Cars(cars); + + assertThat(actual).isEqualTo(expect); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/CarsTest.java b/src/test/java/com/woowacourse/racingGame/domain/CarsTest.java new file mode 100644 index 0000000000..e7324c749b --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/CarsTest.java @@ -0,0 +1,61 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class CarsTest { + private MovableStrategy movableStrategy; + + @Test + void checkDuplicate_유효한_이름() { + movableStrategy = new RandomMovableStrategy(); + List nonDuplicatedCars = Arrays.asList( + new Car(new Name("a"), movableStrategy), + new Car(new Name("bb"), movableStrategy), + new Car(new Name("ccc"), movableStrategy)); + Cars cars = new Cars(nonDuplicatedCars); + + final List actual = cars.getCars(); + + assertThat(actual).isEqualTo(nonDuplicatedCars); + } + + @Test + void checkDuplicate_중복된_이름_존재() { + movableStrategy = new RandomMovableStrategy(); + List duplicatedCars = Arrays.asList( + new Car(new Name("a"), movableStrategy), + new Car(new Name("bb"), movableStrategy), + new Car(new Name("a"), movableStrategy)); + + assertThatThrownBy(() -> new Cars(duplicatedCars)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("중복된 이름이 존재합니다."); + } + + @Test + void attemptMove_전부_이동() { + //given + movableStrategy = new PlannedMovableStrategy(new Power(9)); + List plannedCars = Arrays.asList( + new Car(new Name("a"), new Position(1), movableStrategy), + new Car(new Name("bb"), new Position(2), movableStrategy), + new Car(new Name("ccc"), new Position(3), movableStrategy)); + Cars cars = new Cars(plannedCars); + cars.attemptMove(); + + List actual = cars.getCars().stream() + .map(Car::getPosition) + .collect(Collectors.toList()); + + List expected = Arrays.asList(2, 3, 4); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/MovementNumberTest.java b/src/test/java/com/woowacourse/racingGame/domain/MovementNumberTest.java new file mode 100644 index 0000000000..8fb0cbac68 --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/MovementNumberTest.java @@ -0,0 +1,25 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class MovementNumberTest { + @Test + void checkValid_이동_횟수가_숫자가_아닌_경우() { + final String notMovementNumber = "abc"; + + assertThatThrownBy(() -> new MovementNumber(notMovementNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("입력한 이동 횟수가 숫자가 아닙니다."); + } + + @Test + void checkPositive_이동_횟수가_음수() { + final String invalidMovementNumber = "-1"; + + assertThatThrownBy(() -> new MovementNumber(invalidMovementNumber)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/NameTest.java b/src/test/java/com/woowacourse/racingGame/domain/NameTest.java new file mode 100644 index 0000000000..7ce8a538df --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/NameTest.java @@ -0,0 +1,32 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class NameTest { + @Test + void checkNullOrEmpty_null_또는_빈_문자열() { + final String nullName = null; + + assertThatThrownBy(() -> new Name(nullName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("null 또는 빈 문자열입니다."); + + final String emptyName = ""; + + assertThatThrownBy(() -> new Name(emptyName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("null 또는 빈 문자열입니다."); + } + + @Test + void checkValidLength_자동차_이름의_길이가_5를_초과() { + final String invalidLengthName = "abcdef"; + + assertThatThrownBy(() -> new Name(invalidLengthName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름은 5자이하만 가능합니다."); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/PositionTest.java b/src/test/java/com/woowacourse/racingGame/domain/PositionTest.java new file mode 100644 index 0000000000..8a546ed5fb --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/PositionTest.java @@ -0,0 +1,39 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class PositionTest { + public static final int MOVING_UNIT = 1; + + @Test + void checkValid_위치가_음수() { + final int negativeNumber = -1; + + assertThatThrownBy(() -> Position.valueOf(negativeNumber)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void increaseByMovingUnit_위치가_Moving_Unit만큼_증가() { + int initPosition = 4; + Position position = Position.valueOf(initPosition); + position = position.increaseByMovingUnit(); + + final int actual = position.getPosition(); + + final int expected = initPosition + MOVING_UNIT; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void valueOf_캐싱된_위치() { + Position position1 = Position.valueOf(1); + Position position2 = Position.valueOf(1); + + assertThat(position1).isEqualTo(position2); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/PowerTest.java b/src/test/java/com/woowacourse/racingGame/domain/PowerTest.java new file mode 100644 index 0000000000..92614effbe --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/PowerTest.java @@ -0,0 +1,42 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +@SuppressWarnings("NonAsciiCharacters") +class PowerTest { + @ParameterizedTest + @CsvSource(value = {"-1", "10"}) + void RandomNo_유효한_범위밖(final int invalidValue) { + assertThatThrownBy(() -> Power.valueOf(invalidValue)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 랜덤 숫자입니다."); + } + + @Test + void isMovable_이동_가능() { + final int movableNumber = 4; + Power power = Power.valueOf(movableNumber); + + assertThat(power.isMovable()).isTrue(); + } + + @Test + void isMovable_이동_불가능() { + final int immovableNumber = 3; + Power power = Power.valueOf(immovableNumber); + + assertThat(power.isMovable()).isFalse(); + } + + @Test + void valueOf_캐싱된_범위() { + final Power power1 = Power.valueOf(3); + final Power power2 = Power.valueOf(3); + + assertThat(power1).isEqualTo(power2); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/ResultTest.java b/src/test/java/com/woowacourse/racingGame/domain/ResultTest.java new file mode 100644 index 0000000000..98787e574b --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/ResultTest.java @@ -0,0 +1,52 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class ResultTest { + private final MovableStrategy movableStrategy = new RandomMovableStrategy(); + + @Test + void getRacingCarStatus_게임을_진행한_자동차() { + final List racingCar = Arrays.asList( + new Car(new Name("car1"), Position.valueOf(3), movableStrategy), + new Car(new Name("car2"), Position.valueOf(2), movableStrategy), + new Car(new Name("car3"), Position.valueOf(1), movableStrategy)); + final Result result = new Result(new Cars(racingCar)); + + final Map actual = result.getRacingCarStatus(); + + final Map expected = new HashMap() {{ + put("car1", 3); + put("car2", 2); + put("car3", 1); + }}; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void getWinners_우승한_자동차() { + int winnerPosition = 7; + List inGameCars = Arrays.asList( + new Car(new Name("car1"), Position.valueOf(winnerPosition - 1), movableStrategy), + new Car(new Name("car2"), Position.valueOf(winnerPosition), movableStrategy), + new Car(new Name("car3"), Position.valueOf(winnerPosition - 2), movableStrategy), + new Car(new Name("car4"), Position.valueOf(winnerPosition), movableStrategy)); + Cars cars = new Cars(inGameCars); + Result result = new Result(cars); + + List actual = result.getWinners(); + + List expected = Arrays.asList("car2", "car4"); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/utils/StringUtilTest.java b/src/test/java/com/woowacourse/racingGame/utils/StringUtilTest.java new file mode 100644 index 0000000000..8ee882ca10 --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/utils/StringUtilTest.java @@ -0,0 +1,55 @@ +package com.woowacourse.racingGame.utils; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class StringUtilTest { + @Test + void split_쉼표로_구분되어_입력된_자동차_이름() { + final String inputCarName = "a,b,c,d"; + + List actual = StringUtil.split(inputCarName); + + List expected = Arrays.asList("a", "b", "c", "d"); + + assertThat(actual).isEqualTo(expected); + } + + @Test + void split_공백이_앞뒤로_존재하는_자동차_이름() { + final String inputCarName = "a, b, c, d "; + + List actual = StringUtil.split(inputCarName); + + List expected = Arrays.asList("a", "b", "c", "d"); + + assertThat(actual).isEqualTo(expected); + } + + @Test + void convertIntoDashBy_게임을_진행한_자동차의_상태() { + final int position = 4; + + final String actual = StringUtil.convertIntoDashBy(position); + + final String expected = "----"; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void joinNameOf_우승한_자동차_이름들() { + final List winners = Arrays.asList("toni", "alt"); + + final String actual = StringUtil.joinNameOf(winners); + + final String expected = "toni, alt"; + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/com/woowacourse/stringCalculator/StringCalculatorTest.java b/src/test/java/com/woowacourse/stringCalculator/StringCalculatorTest.java new file mode 100644 index 0000000000..4a6f8d209d --- /dev/null +++ b/src/test/java/com/woowacourse/stringCalculator/StringCalculatorTest.java @@ -0,0 +1,68 @@ +package com.woowacourse.stringCalculator; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +@SuppressWarnings("NonAsciiCharacters") +public class StringCalculatorTest { + @Test + void calculate_null_또는_빈문자() { + final String nullValue = null; + + assertThat(StringCalculator.calculate(nullValue)).isEqualTo(0); + + final String emptyValue = ""; + + assertThat(StringCalculator.calculate(emptyValue)).isEqualTo(0); + } + + @ParameterizedTest + @CsvSource(value = {"1,2|3", "1:2|3", "1,2:3|6"}, delimiter = '|') + void calculate_쉼표_콜론_구분자로_숫자합_계산(final String value, final int expected) { + final int actual = StringCalculator.calculate(value); + + assertThat(actual).isEqualTo(expected); + } + + // NOTE : @CsvSource로 value들을 넘기려고 할 때 \ 문자를 어떻게 넘길 수 있을까? + @Test + void calculate_커스텀_구분자로_숫자합_계산() { + final String value = "//;\n1;2;3"; + + final int actual = StringCalculator.calculate(value); + + final int expected = 6; + + assertThat(actual).isEqualTo(expected); + } + + // NOTE : 위의 @CsvSource와 같은 고민 + @Test + void calculate_숫자_이외의_값() { + final String invalidValue = "1:2,a"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue)) + .isInstanceOf(RuntimeException.class); + + final String invalidValue2 = "//;\n1;2;a"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue2)) + .isInstanceOf(RuntimeException.class); + } + + @Test + void calculate_음수() { + final String invalidValue = "1:2,-2"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue)) + .isInstanceOf(RuntimeException.class); + + final String invalidValue2 = "//;\n1;2;-2"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue2)) + .isInstanceOf(RuntimeException.class); + } +} diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000