diff --git a/README.md b/README.md index d0286c859f..c4d8e7ddc1 100644 --- a/README.md +++ b/README.md @@ -1 +1,143 @@ # java-racingcar-precourse +# 자동차 경주 + +## 과제 진행 요구 사항 + +-   미션은 [자동차 경주](https://github.com/woowacourse-precourse/java-racingcar-7) 저장소를 포크하고 클론하는 것으로 시작한다. + +-   **기능을 구현하기 전 `README.md`에 구현할 기능 목록을 정리**해 추가한다. + +-   Git의 커밋 단위는 앞 단계에서 `README.md`에 정리한 기능 목록 단위로 추가한다. +     + +-   [AngularJS Git Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153)을 참고해 커밋 메시지를 작성한다. + +-   자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다. + +## 기능 요구 사항 + +초간단 자동차 경주 게임을 구현한다. + +-   주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. + +-   각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. + +-   자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. + +-   사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. + +-   전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. + +-   자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. + +-   우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. + +-   사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다. + +### 입출력 요구 사항 + +#### 입력 + +-   경주할 자동차 이름(이름은 쉼표(,) 기준으로 구분) + +```autohotkey +pobi,woni,jun +``` + +-   시도할 횟수 + +```undefined +5 +``` + +#### 출력 + +-   차수별 실행 결과 + +```ada +pobi : -- woni : ---- jun : --- +``` + +-   단독 우승자 안내 문구 + +```ada +최종 우승자 : pobi +``` + +-   공동 우승자 안내 문구 + +```ada +최종 우승자 : pobi, jun +``` + +#### 실행 결과 예시 + +```ada +경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분) pobi,woni,jun 시도할 횟수는 몇 회인가요? 5 실행 결과 pobi : - woni : jun : - pobi : -- woni : - jun : -- pobi : --- woni : -- jun : --- pobi : ---- woni : --- jun : ---- pobi : ----- woni : ---- jun : ----- 최종 우승자 : pobi, jun +``` + +## 프로그래밍 요구 사항 1 + +-   JDK 21 버전에서 실행 가능해야 한다. + +-   프로그램 실행의 시작점은 `Application`의 `main()`이다. + +-   `build.gradle` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** + +-   프로그램 종료 시 `System.exit()`를 호출하지 않는다. + +-   프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. + +-   자바 코드 컨벤션을 지키면서 프로그래밍한다. +     + +-   기본적으로 [Java Style Guide](https://github.com/woowacourse/woowacourse-docs/blob/main/styleguide/java)를 원칙으로 한다. + +## 프로그래밍 요구 사항 2 + +-   indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. +     + +-   예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. +     + +-   힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. + +-   3항 연산자를 쓰지 않는다. + +-   함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. + +-   JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. +     + +-   테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다. +         + +-   [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide) +         + +-   [AssertJ User Guide](https://assertj.github.io/doc) +         + +-   [AssertJ Exception Assertions](https://www.baeldung.com/assertj-exception-assertion) +         + +-   [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5) + +### 라이브러리 + +-   `camp.nextstep.edu.missionutils`에서 제공하는 `Randoms` 및 `Console` API를 사용하여 구현해야 한다. +     + +-   Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInRange()`를 활용한다. +     + +-   사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다. + +#### 사용 예시 + +-   0에서 9까지의 정수 중 한 개의 정수 반환 + +```java +Randoms.pickNumberInRange(0, 9); +``` diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..0ce46d65be 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,11 @@ package racingcar; +import racingcar.Controller.RacingCarController; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + + RacingCarController racingcarController = new RacingCarController(); + racingcarController.run(); } } diff --git a/src/main/java/racingcar/Controller/RacingCarController.java b/src/main/java/racingcar/Controller/RacingCarController.java new file mode 100644 index 0000000000..6cf4d208df --- /dev/null +++ b/src/main/java/racingcar/Controller/RacingCarController.java @@ -0,0 +1,54 @@ +package racingcar.Controller; + +import racingcar.Model.Cars; +import racingcar.Service.*; +import racingcar.View.InputView; +import racingcar.View.OutputView; + +import java.util.List; + +public class RacingCarController { + private final ClassificationService classificationService; + private final MovementService movementService; + private final RandomService randomService; + private final InputView inputView; + private final OutputView outputView; + private final NameValidationService nameValidationService; + private final NameSplitterService nameSplitterService; + private final ParseService parseService; + private final ResultService resultService; + private final GameService gameService; + private final CreateCarService createCarService; + + public RacingCarController() { + this.classificationService = new ClassificationService(); + this.movementService = new MovementService(); + this.randomService = new RandomService(); + this.inputView = new InputView(); + this.outputView = new OutputView(); + this.nameValidationService = new NameValidationService(); + this.nameSplitterService = new NameSplitterService(); + this.parseService = new ParseService(); + this.resultService = new ResultService(); + this.gameService = new GameService(randomService, classificationService, movementService); + this.createCarService = new CreateCarService(); + } + + public void run() { + List input = inputView.input(); + List carNames = + nameValidationService.validateName(nameSplitterService.nameSplit(input.getFirst())); + + Cars carRacers = createCarService.crateCar(carNames); + + int roundCount = parseService.parseInt(input.getLast()); + + for (int round = 0; round < roundCount; round++) { + gameService.gameRound(carRacers); + outputView.output_round(carRacers); + } + List winnerNames = resultService.findWinners(carRacers); + + outputView.output_winner(winnerNames); + } +} diff --git a/src/main/java/racingcar/Model/Car.java b/src/main/java/racingcar/Model/Car.java new file mode 100644 index 0000000000..b653976962 --- /dev/null +++ b/src/main/java/racingcar/Model/Car.java @@ -0,0 +1,23 @@ +package racingcar.Model; + +public class Car { + private String racerName; + private int position; + + public String getName() { + return racerName; + } + public void setName(String racerName) { + this.racerName = racerName; + } + public int getPosition() { + return position; + } + public void setPosition(int position) { + this.position = position; + } + + public void move() { + ++position; + } +} diff --git a/src/main/java/racingcar/Model/Cars.java b/src/main/java/racingcar/Model/Cars.java new file mode 100644 index 0000000000..cdb6cb268a --- /dev/null +++ b/src/main/java/racingcar/Model/Cars.java @@ -0,0 +1,29 @@ +package racingcar.Model; + +import java.util.List; + +public class Cars { + private List racers; + + public void setCarRacers(List racers) { + this.racers = racers; + } + + public Car getCar(int index) { + return racers.get(index); + } + + public int size() { + return racers.size(); + } + + public int getMaxPosition() { + int maxPosition = 0; + for (Car racer : racers) { + if (maxPosition < racer.getPosition()) { + maxPosition = racer.getPosition(); + } + } + return maxPosition; + } +} diff --git a/src/main/java/racingcar/Service/ClassificationService.java b/src/main/java/racingcar/Service/ClassificationService.java new file mode 100644 index 0000000000..d1fea17df1 --- /dev/null +++ b/src/main/java/racingcar/Service/ClassificationService.java @@ -0,0 +1,21 @@ +package racingcar.Service; + +import java.util.ArrayList; +import java.util.List; + +public class ClassificationService { + public List classifyValue(List racingCars) { + List result = new ArrayList<>(); + + for (Integer racingCarRandomValue : racingCars) { + if (racingCarRandomValue > 3) { + result.add(1); + } else { + result.add(0); + } + } + return result; + } + + +} diff --git a/src/main/java/racingcar/Service/CreateCarService.java b/src/main/java/racingcar/Service/CreateCarService.java new file mode 100644 index 0000000000..8390cea870 --- /dev/null +++ b/src/main/java/racingcar/Service/CreateCarService.java @@ -0,0 +1,23 @@ +package racingcar.Service; + +import racingcar.Model.Car; +import racingcar.Model.Cars; + +import java.util.ArrayList; +import java.util.List; + +public class CreateCarService { + public Cars crateCar(List carNames) { + List cars = new ArrayList<>(); + for (String carName : carNames) { + Car car = new Car(); + car.setName(carName); + car.setPosition(0); + cars.add(car); + } + + Cars racers = new Cars(); + racers.setCarRacers(cars); + return racers; + } +} diff --git a/src/main/java/racingcar/Service/GameService.java b/src/main/java/racingcar/Service/GameService.java new file mode 100644 index 0000000000..3369afb521 --- /dev/null +++ b/src/main/java/racingcar/Service/GameService.java @@ -0,0 +1,24 @@ +package racingcar.Service; + +import racingcar.Model.Cars; + +import java.util.List; + +public class GameService { + private final RandomService randomService; + private final ClassificationService classificationService; + private final MovementService movementService; + + public GameService(RandomService randomService, ClassificationService classificationService, + MovementService movementService) { + this.randomService = randomService; + this.classificationService = classificationService; + this.movementService = movementService; + } + + public void gameRound(Cars racers) { + List classifyCars = + classificationService.classifyValue(randomService.randomValue(racers.size())); + movementService.playRound(classifyCars, racers); + } +} diff --git a/src/main/java/racingcar/Service/MovementService.java b/src/main/java/racingcar/Service/MovementService.java new file mode 100644 index 0000000000..40ca47f8de --- /dev/null +++ b/src/main/java/racingcar/Service/MovementService.java @@ -0,0 +1,17 @@ +package racingcar.Service; + +import racingcar.Model.Cars; + +import java.util.List; + +public class MovementService { + + public void playRound(List sortedRacingCars, Cars racers) { + for (int index = 0; index < sortedRacingCars.size(); index++) { + if (sortedRacingCars.get(index).equals(1)) { + racers.getCar(index).move(); + racers.getCar(index).setPosition(racers.getCar(index).getPosition()); + } + } + } +} diff --git a/src/main/java/racingcar/Service/NameSplitterService.java b/src/main/java/racingcar/Service/NameSplitterService.java new file mode 100644 index 0000000000..6c40b39d9b --- /dev/null +++ b/src/main/java/racingcar/Service/NameSplitterService.java @@ -0,0 +1,10 @@ +package racingcar.Service; + +import java.util.List; + +public class NameSplitterService { + + public List nameSplit(String names) { + return List.of(names.split(",")); + } +} diff --git a/src/main/java/racingcar/Service/NameValidationService.java b/src/main/java/racingcar/Service/NameValidationService.java new file mode 100644 index 0000000000..7dbbd854ae --- /dev/null +++ b/src/main/java/racingcar/Service/NameValidationService.java @@ -0,0 +1,20 @@ +package racingcar.Service; + +import java.util.List; + +public class NameValidationService { + public List validateName(List racerNames) { + for (String name : racerNames) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("이름은 비어있을 수 없습니다. 애플리케이션 종료."); + } + if (name.matches(".*\\s.*")) { + throw new IllegalArgumentException("이름에는 공백을 입력할 수 없습니다. 애플리케이션 종료."); + } + if (name.length() > 5) { + throw new IllegalArgumentException(name + "의 길이가 5글자 이상입니다. 애플리케이션 종료."); + } + } + return racerNames; + } +} diff --git a/src/main/java/racingcar/Service/ParseService.java b/src/main/java/racingcar/Service/ParseService.java new file mode 100644 index 0000000000..3ab0df883c --- /dev/null +++ b/src/main/java/racingcar/Service/ParseService.java @@ -0,0 +1,7 @@ +package racingcar.Service; + +public class ParseService { + public int parseInt(String roundCount) { + return Integer.parseInt(roundCount); + } +} diff --git a/src/main/java/racingcar/Service/RandomService.java b/src/main/java/racingcar/Service/RandomService.java new file mode 100644 index 0000000000..f8b2af69c7 --- /dev/null +++ b/src/main/java/racingcar/Service/RandomService.java @@ -0,0 +1,16 @@ +package racingcar.Service; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.ArrayList; +import java.util.List; + +public class RandomService { + public List randomValue(int numberOfCars) { + List carsRandomValue = new ArrayList<>(); + for (int index = 0; index < numberOfCars; index++) { + carsRandomValue.add(Randoms.pickNumberInRange(0, 9)); + } + return carsRandomValue; + } +} diff --git a/src/main/java/racingcar/Service/ResultService.java b/src/main/java/racingcar/Service/ResultService.java new file mode 100644 index 0000000000..8c375dd226 --- /dev/null +++ b/src/main/java/racingcar/Service/ResultService.java @@ -0,0 +1,20 @@ +package racingcar.Service; + +import racingcar.Model.Cars; + +import java.util.ArrayList; +import java.util.List; + +public class ResultService { + + public List findWinners(Cars racers) { + List winnerNames = new ArrayList<>(); + int maxValue = racers.getMaxPosition(); + for (int index = 0; index < racers.size(); index++) { + if (racers.getCar(index).getPosition() == maxValue) { + winnerNames.add(racers.getCar(index).getName()); + } + } + return winnerNames; + } +} diff --git a/src/main/java/racingcar/View/InputView.java b/src/main/java/racingcar/View/InputView.java new file mode 100644 index 0000000000..26e5f7352d --- /dev/null +++ b/src/main/java/racingcar/View/InputView.java @@ -0,0 +1,24 @@ +package racingcar.View; + +import camp.nextstep.edu.missionutils.Console; + +import java.util.ArrayList; +import java.util.List; + +public class InputView { + private static final String FIRST_MESSAGE = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; + private static final String LAST_MESSAGE = "시도할 횟수는 몇 회 인가요?"; + + public List input() { + System.out.println(FIRST_MESSAGE); + String input = Console.readLine(); + System.out.println(LAST_MESSAGE); + String count = Console.readLine(); + + List inputValue = new ArrayList<>(); + inputValue.add(input); + inputValue.add(count); + + return inputValue; + } +} diff --git a/src/main/java/racingcar/View/OutputView.java b/src/main/java/racingcar/View/OutputView.java new file mode 100644 index 0000000000..97a1950102 --- /dev/null +++ b/src/main/java/racingcar/View/OutputView.java @@ -0,0 +1,28 @@ +package racingcar.View; + +import racingcar.Model.Cars; + +import java.util.List; + +public class OutputView { + private static final String MESSAGE_WINNER = "최종 우승자 : "; + + public void output_winner(List names) { + System.out.print(MESSAGE_WINNER); + System.out.println(String.join(", ", names)); + + } + + public void output_round(Cars racers) { + for (int index = 0; index < racers.size(); index++) { + System.out.print(racers.getCar(index).getName() + " : " ); + if (racers.getCar(index).getPosition() != 0) { + for (int position = 0; position < racers.getCar(index).getPosition(); position++) { + System.out.print("-"); + } + } + System.out.print(" "); + } + System.out.println(); + } +}