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; + } +}