diff --git a/docs/README.md b/docs/README.md index e69de29bb2d..fc5960cb0de 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,21 @@ +## 구현할 기능 목록 +### 입력 +- [x] 경주에 참여할 자동차들의 목록을 입력 받기 + - 자동차 이름은 쉼표(,)를 기준으로 구분한다. + - 각각의 자동차 이름은 5자 이하로 제한한다. + - 한 대 이상의 자동차 이름을 입력하도록 한다. + - 중복되는 자동차 이름은 허용하지 않는다. +- [x] 시도할 횟수를 입력 받기 + - 1 이상의 정수를 입력 받도록 한다. + - 입력 받은 수가 정수인지 확인한다. + - 양수인지 확인한다. +### 경주 게임 처리 +- [x] 시도 횟수만큼 게임 진행 반복하기 + - 매 게임 진행마다 각각의 자동차 별로 0 ~ 9의 무작위 정수값을 생성한다. + - 무작위 정수값이 4 이상인 경우 앞으로 전진하고, 3 이하라면 정지한다. +### 출력 +- [x] 매게임 진행 마다 각 자동차의 실행 결과를 출력하기 + - 진행 상황은 (-)로 출력한다. + - 진행 상황과 함께 각 자동차의 이름을 출력한다. +- [x] 게임 종료 후, 최종 우승자 출력하기 + - 우승자는 2명 이상일 수 있고, 여러 명인 경우 콤마(,)로 구분한다. \ No newline at end of file diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e7242..2fce0cd7992 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,17 @@ package racingcar; +import racingcar.Controller.RacingGame; + +import static racingcar.view.OutputView.printErrorMessage; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + RacingGame racingGame = new RacingGame(); + + try { + racingGame.start(); + } catch (IllegalArgumentException e) { + printErrorMessage(e.getMessage()); + } } } diff --git a/src/main/java/racingcar/Controller/RacingGame.java b/src/main/java/racingcar/Controller/RacingGame.java new file mode 100644 index 00000000000..ccde7153797 --- /dev/null +++ b/src/main/java/racingcar/Controller/RacingGame.java @@ -0,0 +1,61 @@ +package racingcar.Controller; + +import racingcar.domain.Car; +import racingcar.domain.Cars; +import racingcar.dto.CarDto; +import racingcar.util.AttemptCount; +import racingcar.util.RandomMoving; +import racingcar.view.OutputView; + +import java.util.List; + +import static racingcar.view.InputView.setAttempt; +import static racingcar.view.InputView.setCarNames; +import static racingcar.view.OutputView.printLine; +import static racingcar.view.OutputView.printWinners; + +public class RacingGame { + private Cars cars; + private AttemptCount gameAttemptCount; + private RandomMoving movingStrategy = new RandomMoving(); + + public void start() throws IllegalArgumentException { + initRacingCarGame(); + + while (gameAttemptCount.isContinue()) { + gameAttemptCount.reduceAttemptCount(); + run(); + } + + List winnerNameList = getWinners(); + printWinners(winnerNameList); + } + + private void initRacingCarGame() throws IllegalArgumentException { + List carNames = setCarNames(); + cars = new Cars(carNames); + + String attempt = setAttempt(); + gameAttemptCount = new AttemptCount(attempt); + } + + private void run() { + cars.progressWithAllCar(movingStrategy); + printProgress(); + printLine(); + } + + private void printProgress() { + for (int i = 0; i < cars.getSize(); i++) { + Car car = cars.getCar(i); + + OutputView.printProgress(CarDto.of(car)); + } + } + + private List getWinners() { + int maxPosition = cars.getMaxPosition(); + + return cars.getWinner(maxPosition); + } +} diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java new file mode 100644 index 00000000000..4968b88d81a --- /dev/null +++ b/src/main/java/racingcar/domain/Car.java @@ -0,0 +1,32 @@ +package racingcar.domain; + +import racingcar.util.RandomMoving; + +public class Car { + private final CarName carName; + private int position = 0; + public Car(String name) { + this.carName = new CarName(name); + } + + public String getName() { + return carName.getName(); + } + + public int getPosition() { + return position; + } + + public void progress(RandomMoving movingStrategy) { + if (movingStrategy.isMovable()) { + position++; + } + } + + public boolean isSamePosition(int position) { + if (this.position == position) { + return true; + } + return false; + } +} diff --git a/src/main/java/racingcar/domain/CarName.java b/src/main/java/racingcar/domain/CarName.java new file mode 100644 index 00000000000..95c242536e3 --- /dev/null +++ b/src/main/java/racingcar/domain/CarName.java @@ -0,0 +1,31 @@ +package racingcar.domain; + +public class CarName { + + private final String name; + public CarName(String name) { + checkCarName(name); + this.name = name; + } + + public String getName() { + return name; + } + + private void checkCarName(String carName) { + if (carName == null) { + throw new IllegalArgumentException("err: 자동차 이름은 null이 될 수 없습니다."); + } + + checkCarNameLength(carName); + } + + private void checkCarNameLength(String carName) { + if (carName.length() < 1) { + throw new IllegalArgumentException("err: 자동차 이름은 한 글자 이상이어야 합니다."); + } + if (carName.length() > 5) { + throw new IllegalArgumentException("err: 자동차 이름은 다섯 글자 이하만 가능합니다."); + } + } +} diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java new file mode 100644 index 00000000000..17fe140f52f --- /dev/null +++ b/src/main/java/racingcar/domain/Cars.java @@ -0,0 +1,61 @@ +package racingcar.domain; + +import racingcar.util.RandomMoving; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +public class Cars { + private List cars; + + public Cars(List carNames) { + initCar(carNames); + } + + public int getMaxPosition() { + cars.sort((o1, o2) -> o2.getPosition() - o1.getPosition()); + + return cars.get(0).getPosition(); + } + + public List getWinner(int position) { + return cars.stream() + .filter(car -> car.isSamePosition(position)) + .map(car -> car.getName()) + .collect(Collectors.toList()); + } + + public void progressWithAllCar(RandomMoving movingStrategy) { + for (Car car : cars) { + car.progress(movingStrategy); + } + } + + public int getSize() { + return cars.size(); + } + + public Car getCar(int index) { + return cars.get(index); + } + + private void initCar(List carNames) throws IllegalArgumentException { + checkDuplication(carNames); + + cars = new ArrayList<>(); + + for (String name : carNames) { + cars.add(new Car(name)); + } + } + + private void checkDuplication(List carNames) { + HashSet hashSet = new HashSet<>(carNames); + + if (hashSet.size() < carNames.size()) { + throw new IllegalArgumentException("err: 자동차 이름이 중복되면 안됩니다."); + } + } +} diff --git a/src/main/java/racingcar/dto/CarDto.java b/src/main/java/racingcar/dto/CarDto.java new file mode 100644 index 00000000000..cf8181e5325 --- /dev/null +++ b/src/main/java/racingcar/dto/CarDto.java @@ -0,0 +1,26 @@ +package racingcar.dto; + +import racingcar.domain.Car; + +public class CarDto { + + private final String carName; + private final int position; + + public CarDto(String carName, int position) { + this.carName = carName; + this.position = position; + } + + public String getCarName() { + return carName; + } + + public int getPosition() { + return position; + } + + public static CarDto of(Car car) { + return new CarDto(car.getName(), car.getPosition()); + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/util/AttemptCount.java b/src/main/java/racingcar/util/AttemptCount.java new file mode 100644 index 00000000000..f4a838a03a3 --- /dev/null +++ b/src/main/java/racingcar/util/AttemptCount.java @@ -0,0 +1,38 @@ +package racingcar.util; + +public class AttemptCount { + private int totalAttemptCount; + public AttemptCount(String totalAttemptCount) { + this.totalAttemptCount = checkTranslateInteger(totalAttemptCount); + checkPositiveNumber(this.totalAttemptCount); + } + + public int getTotalAttemptCount() { + return totalAttemptCount; + } + + public boolean isContinue() { + if (totalAttemptCount == 0) { + return false; + } + + return true; + } + + public void reduceAttemptCount() { + totalAttemptCount = totalAttemptCount - 1; + } + + private int checkTranslateInteger(String attempt) { + try { + return Integer.parseInt(attempt); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("err: 시도 횟수는 숫자여야 합니다."); + } + } + private void checkPositiveNumber(int number) { + if (number <= 0) { + throw new IllegalArgumentException("err: 양수를 입력해주세요."); + } + } +} diff --git a/src/main/java/racingcar/util/RandomMoving.java b/src/main/java/racingcar/util/RandomMoving.java new file mode 100644 index 00000000000..b8a15dd5cbf --- /dev/null +++ b/src/main/java/racingcar/util/RandomMoving.java @@ -0,0 +1,15 @@ +package racingcar.util; + +import camp.nextstep.edu.missionutils.Randoms; + +public class RandomMoving { + public boolean isMovable() { + int randomNumber = getRandomNumber(); + + return randomNumber >= 4; + } + + private static int getRandomNumber() { + return Randoms.pickNumberInRange(0,9); + } +} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java new file mode 100644 index 00000000000..5f1b24c3491 --- /dev/null +++ b/src/main/java/racingcar/view/InputView.java @@ -0,0 +1,26 @@ +package racingcar.view; + +import camp.nextstep.edu.missionutils.Console; + +import java.util.Arrays; +import java.util.List; + +public class InputView { + + private static final String SEPARATOR = ","; + private InputView() {} + + public static List setCarNames() { + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + + String input = Console.readLine(); + List carNames = Arrays.asList(input.split(SEPARATOR)); + + return carNames; + } + + public static String setAttempt() { + System.out.println("시도할 회수는 몇회인가요?"); + return Console.readLine(); + } +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java new file mode 100644 index 00000000000..2f74f98b9db --- /dev/null +++ b/src/main/java/racingcar/view/OutputView.java @@ -0,0 +1,40 @@ +package racingcar.view; + +import racingcar.dto.CarDto; + +import java.util.List; + +public class OutputView { + + private static final String DELIMITER = ", "; + private static final String PROGRESS_SYMBOL = "-"; + + private OutputView() {} + + public static void startMessage() { + printLine(); + System.out.println("실행 결과"); + } + + public static void printLine() { + System.out.println(); + } + + public static void printProgress(CarDto car) { + String carName = car.getCarName(); + int position = car.getPosition(); + + String positionSign = PROGRESS_SYMBOL.repeat(position); + + System.out.println(carName + " : " + positionSign); + } + + public static void printWinners(List winners) { + System.out.print(String.join(DELIMITER, winners)); + System.out.println("가 최종 우승했습니다."); + } + + public static void printErrorMessage(String errorMessage) { + System.out.println(errorMessage); + } +} \ No newline at end of file