Skip to content
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,34 @@
# java-racingcar-precourse
# 구현대상 : 자동차 경주

## 기능 목록

1. 입력기능 <br>
1.1 경주할 자동차 이름들을 쉼표(,)를 기준으로 구분하여 입력받는다.<br>
1.2 사용자가 시도할 이동횟수를 입력받는다.

2. 로직1 <br>
2.1 각 자동차는 0~9 사이의 무작위 값을 구한다. 이때의 값은 INT형으로 제한한다.<br>
2.2 무작위 값이 4이상일 경우, 해당 자동차는 1칸 전진한다.<br>
2.3 무작위 값이 3이하일 경우, 해당 자동차는 현재 위치를 유지한다.<br>
2.4 사용자의 입력에서 받은 횟수만큼 위 과정을 반복한다.<br>

3. 출력 기능 <br>
3.1 각 라운드별로 각 자동차의 이름과 현재까지의 전진 상태를 출력한다. (ex: pobi : --)<br>
3.2 모든 라운드가 종료된 후, 가장 많이 전진한 자동차를 최종 우숭자로 선정한다.<br>
3.3 우승자가 여러 대일 경우, 쉼표(,)를 기준으로 구분하여 모두 출력한다.<br>

4. 예외처리<br>
4.1 사용자가 입력한 자동차 이름이 5글자를 초과하는 경우,<br>
IllegalArgumentException을 발생시키고 애플리케이션을 종료한다.<br>
4.2 사용자가 유효하지않은 값을 입력할 경우, IllegalArgumentException을 발생시키고 애플리케이션을 종료한다.<br>

5. 로직2 <br>
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(우승자 출력)을 수행한다.
4 changes: 3 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
RacingRoundController game = new RacingRoundController();
game.startGame();
}

}
61 changes: 61 additions & 0 deletions src/main/java/racingcar/Car.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
112 changes: 112 additions & 0 deletions src/main/java/racingcar/RacingRoundController.java
Original file line number Diff line number Diff line change
@@ -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<Car> cars;
private int rounds;

public void startGame() {
initializeGame();
playGame();
showResult();
}

/*
* 게임을 세팅시작하는 함수,
* 각 자동차이름, 게임횟수를 정한다.
*/
private void initializeGame() {
this.cars = setTheCarFromUser();
this.rounds = setRoundsFromUser();
}

//자동차 이름을 유저에게 입력받는 함수
private List<Car> setTheCarFromUser() {
System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
String nameInput = Console.readLine();
List<String> 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<Car> 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<String> winner = findWinner();
printWinners(winner);
}

//최종 우승자를 출력
public static void printWinners(List<String> winnerName) {
String winner = String.join(", ", winnerName);
System.out.println("최종 우승자 : " + winner);
}

//우승자를 찾는 함수, findMaxPosition()으로 가장 많이 간 자동차를 찾는다.
private List<String> 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;
}
}
58 changes: 58 additions & 0 deletions src/main/java/racingcar/Validator.java
Original file line number Diff line number Diff line change
@@ -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<String> parseCarName(String carName){
if (carName == null || carName.isBlank()) {
throw new IllegalArgumentException("자동차 이름이 입력되지않음");
}

if (carName.endsWith(",")) {
throw new IllegalArgumentException("자동차 이름에 쉼표(,)가 들어감");
}

List<String> names = Arrays.stream(carName.split(","))
.map(String::trim)
.collect(Collectors.toList());

validateNoBlankNames(names);
validateNoDuplicateNames(names);

return names;
}

//자동차 이름내에 공백이 있는지 확인
private static void validateNoBlankNames(List<String> Names) {
for (String name : Names) {
if (name.isBlank()) {
throw new IllegalArgumentException("자동차 이름이 공백 ");
}
}
}

//자동차 이름내에 중복되는게 있는지 확인하는 함수
private static void validateNoDuplicateNames(List<String> 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;
}
}