diff --git a/README.md b/README.md
index d0286c859f..fac32052e6 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,34 @@
# java-racingcar-precourse
+# 구현대상 : 자동차 경주
+
+## 기능 목록
+
+1. 입력기능
+ 1.1 경주할 자동차 이름들을 쉼표(,)를 기준으로 구분하여 입력받는다.
+ 1.2 사용자가 시도할 이동횟수를 입력받는다.
+
+2. 로직1
+ 2.1 각 자동차는 0~9 사이의 무작위 값을 구한다. 이때의 값은 INT형으로 제한한다.
+ 2.2 무작위 값이 4이상일 경우, 해당 자동차는 1칸 전진한다.
+ 2.3 무작위 값이 3이하일 경우, 해당 자동차는 현재 위치를 유지한다.
+ 2.4 사용자의 입력에서 받은 횟수만큼 위 과정을 반복한다.
+
+3. 출력 기능
+ 3.1 각 라운드별로 각 자동차의 이름과 현재까지의 전진 상태를 출력한다. (ex: pobi : --)
+ 3.2 모든 라운드가 종료된 후, 가장 많이 전진한 자동차를 최종 우숭자로 선정한다.
+ 3.3 우승자가 여러 대일 경우, 쉼표(,)를 기준으로 구분하여 모두 출력한다.
+
+4. 예외처리
+ 4.1 사용자가 입력한 자동차 이름이 5글자를 초과하는 경우,
+ IllegalArgumentException을 발생시키고 애플리케이션을 종료한다.
+ 4.2 사용자가 유효하지않은 값을 입력할 경우, IllegalArgumentException을 발생시키고 애플리케이션을 종료한다.
+
+5. 로직2
+ 1,2,3,4를 수행하는 로직을 구성한다.
+
+ 5.1 - 입력기능(자동차의 이름과 이동횟수)을 오케스트레이션한다.
+ 5.2 - 3.1에서 입력받은 정보대로 자동차들을 구현한다. 이때, 5.1(예외처리)를 처리한다.
+ 5.3 - 3.1에서 입력받은 정보대로 게임을 구성한다. 이때 5.2(예외처리)를 처리한다.
+ 5.4 - 4.1(출력기능)을 오케스트레이션한다.
+ 5.5 - 3.4가 종료될때, 4.2(우승자 선정)을 수행한다.
+ 5.6 - 3.5가 종료될 때, 4.3(우승자 출력)을 수행한다.
\ No newline at end of file
diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java
index a17a52e724..1f13363e0d 100644
--- a/src/main/java/racingcar/Application.java
+++ b/src/main/java/racingcar/Application.java
@@ -2,6 +2,8 @@
public class Application {
public static void main(String[] args) {
- // TODO: 프로그램 구현
+ RacingRoundController game = new RacingRoundController();
+ game.startGame();
}
+
}
diff --git a/src/main/java/racingcar/Car.java b/src/main/java/racingcar/Car.java
new file mode 100644
index 0000000000..f6bfc13ae4
--- /dev/null
+++ b/src/main/java/racingcar/Car.java
@@ -0,0 +1,61 @@
+package racingcar;
+
+import camp.nextstep.edu.missionutils.Randoms;
+
+public class Car {
+ private static final int MOVE_STANDARD = 4;
+ private static final int MAX_NAME_LENGTH = 5;
+ private static final int RAMDOM_MIN = 0;
+ private static final int RAMDOM_MAX = 9;
+
+ private String name;
+ private int position;
+
+ public Car(String name) {
+ validationName(name);
+ this.name = name;
+ this.position = 0;
+ }
+
+ private void validationName(String name) {
+ if (name == null || name.isBlank()) {
+ throw new IllegalArgumentException("자동차 이름이 공백");
+ }
+ if (name.length() > MAX_NAME_LENGTH) {
+ throw new IllegalArgumentException("자동차글자가 5글자 이상");
+ }
+ }
+
+ //RAMDOM_MIN~RAMDOM_MAX 사이의 무작위 값 생성 후, MOVE_STANDARD에 부합하는지 판단 후 전진함수 오케스트레이션 시키는 함수
+ public void HowMuchDoTheCarHave2Leave() {
+ int randomNumber = Randoms.pickNumberInRange(RAMDOM_MIN, RAMDOM_MAX);
+ if (randomNumber >= MOVE_STANDARD){
+ move();
+ }
+ }
+
+ //자동차 전진 함수
+ private void move() {
+ this.position++;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getPositions() {
+ return position;
+ }
+
+ public String getPositionBar() {
+ return "-".repeat(this.position);
+ }
+ /*
+ * 현재 자동차가 주어진 위치(position)에 있는지 확인하는 함수
+ * position : 비교할 위치
+ * return : 일치여부
+ */
+ public boolean isAtPosition(int position) {
+ return this.position == position;
+ }
+}
diff --git a/src/main/java/racingcar/RacingRoundController.java b/src/main/java/racingcar/RacingRoundController.java
new file mode 100644
index 0000000000..3952530667
--- /dev/null
+++ b/src/main/java/racingcar/RacingRoundController.java
@@ -0,0 +1,112 @@
+package racingcar;
+
+import camp.nextstep.edu.missionutils.Console;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class RacingRoundController {
+
+ private List cars;
+ private int rounds;
+
+ public void startGame() {
+ initializeGame();
+ playGame();
+ showResult();
+ }
+
+ /*
+ * 게임을 세팅시작하는 함수,
+ * 각 자동차이름, 게임횟수를 정한다.
+ */
+ private void initializeGame() {
+ this.cars = setTheCarFromUser();
+ this.rounds = setRoundsFromUser();
+ }
+
+ //자동차 이름을 유저에게 입력받는 함수
+ private List setTheCarFromUser() {
+ System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
+ String nameInput = Console.readLine();
+ List carNames = Validator.parseCarName(nameInput);
+
+ return carNames.stream()
+ .map(Car::new)
+ .collect(Collectors.toList());
+ }
+
+ //게임을 세팅하는 함수
+ private int setRoundsFromUser() {
+ System.out.println("시도할 횟수는 몇 회인가요?");
+ return Validator.parseRoundCount(readRoundCount());
+ }
+
+ public static String readRoundCount() {
+ return Console.readLine();
+ }
+
+ /*
+ * 게임을 진행하는 함수
+ */
+ private void playGame() {
+ for (int i=0; i< rounds; i++){
+ calculateRound();
+ printRoundResult(cars); //application에 구현된 함수, 추후 옳겨야겠네
+ }
+ }
+
+ public static void printRoundResult(List cars) {
+ for (Car car : cars) {
+ System.out.println(car.getName() + " : " + car.getPositionBar());
+ }
+ System.out.println();
+ }
+
+ //현재 라운드를 진행하는 함수
+ private void calculateRound() {
+ for (Car car : cars) {
+ car.HowMuchDoTheCarHave2Leave();
+ }
+ }
+
+ /*
+ * 게임의 결과를 출력하는 함수
+ */
+ private void showResult() {
+ List winner = findWinner();
+ printWinners(winner);
+ }
+
+ //최종 우승자를 출력
+ public static void printWinners(List winnerName) {
+ String winner = String.join(", ", winnerName);
+ System.out.println("최종 우승자 : " + winner);
+ }
+
+ //우승자를 찾는 함수, findMaxPosition()으로 가장 많이 간 자동차를 찾는다.
+ private List findWinner() {
+ int maxPosition = findMaxPosition();
+ return cars.stream()
+ .filter(car -> car.isAtPosition(maxPosition))
+ .map(Car::getName)
+ .collect(Collectors.toList());
+ }
+
+ //가장 큰 position을 찾는 함수
+ private int findMaxPosition() {
+ int maxPosition = 0;
+ for (Car car : cars) {
+ maxPosition = compareAfterGetMaxNumber(maxPosition, car.getPositions());
+ }
+ return maxPosition;
+ }
+
+ //현재 MaxPosition과 newPosition중 어떤게 더 큰지 비교 후 가장 큰걸 리턴하는 함수
+ private int compareAfterGetMaxNumber(int currentMax, int newPosition) {
+ if (newPosition > currentMax) {
+ return newPosition;
+ }
+ return currentMax;
+ }
+}
diff --git a/src/main/java/racingcar/Validator.java b/src/main/java/racingcar/Validator.java
new file mode 100644
index 0000000000..93decd7521
--- /dev/null
+++ b/src/main/java/racingcar/Validator.java
@@ -0,0 +1,58 @@
+package racingcar;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class Validator {
+ public static List parseCarName(String carName){
+ if (carName == null || carName.isBlank()) {
+ throw new IllegalArgumentException("자동차 이름이 입력되지않음");
+ }
+
+ if (carName.endsWith(",")) {
+ throw new IllegalArgumentException("자동차 이름에 쉼표(,)가 들어감");
+ }
+
+ List names = Arrays.stream(carName.split(","))
+ .map(String::trim)
+ .collect(Collectors.toList());
+
+ validateNoBlankNames(names);
+ validateNoDuplicateNames(names);
+
+ return names;
+ }
+
+ //자동차 이름내에 공백이 있는지 확인
+ private static void validateNoBlankNames(List Names) {
+ for (String name : Names) {
+ if (name.isBlank()) {
+ throw new IllegalArgumentException("자동차 이름이 공백 ");
+ }
+ }
+ }
+
+ //자동차 이름내에 중복되는게 있는지 확인하는 함수
+ private static void validateNoDuplicateNames(List names) {
+ long uniqueNameCount = names.stream().distinct().count();
+ if (uniqueNameCount != names.size()) {
+ throw new IllegalArgumentException("자동차 이름이 중복");
+ }
+ }
+
+ public static int parseRoundCount(String input) {
+ int rounds;
+ try {
+ rounds = Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("시도 횟수 입력은 숫자이어야함");
+ }
+
+ if (rounds <= 0) {
+ throw new IllegalArgumentException("시도 횟수는 1이상");
+ }
+
+ return rounds;
+ }
+}