Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
# java-racingcar-precourse

## 기능 요구 사항
1. 경주할 **자동차 이름**과 **시도할 횟수**를 입력받는다.

> **[입력 형식]**
경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
chae,morol
시도할 횟수는 몇 회인가요?
3</code>

1-1. 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 종료되어야 한다.


2. 각 자동차마다 **0에서 9** 사이의 난수를 생성한다.
3. 생성된 난수가 **4 이상**이면 해당 자동차를 **전진**시킨다.
4. 모든 자동차의 1회 시도가 끝나면, 자동차 이름과 함께 **누적 전진**한 칸만큼 "-"를 출력한다.
> **[출력 형식]**
chae : ---
morol : --

*(입력받은 시도 횟수만큼 **2, 3, 4**의 과정을 반복한다.)*


5. 경주한 자동차 중 최종적으로 **전진한 값**이 가장 높은 자동차를 우승자로 선정한다.
5-1. 여러 자동차가 동시에 가장 높은 값을 가졌을 경우 **공동 우승자**로 선정한다.


6. 우승한 자동차의 이름을 **우승자 안내** 문구와 함께 출력한다.
> **[출력 형식]**
최종 우승자 : chae, morol


## 예외 케이스

**[자동차 이름 입력]**
- 쉼표가 포함되지 않은 문자열
- 구분한 결과가 5자 초과
- 비어 있는 값

**[시도할 횟수 입력]**
- 0 또는 음수
- 정수가 아닌 모든 수
- 문자, 문자열 등 숫자가 아닌 값
- 비어 있는 값
12 changes: 10 additions & 2 deletions src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package racingcar;

import racingcar.controller.RacingGameController;
import racingcar.model.RacingGame;
import racingcar.view.RacingGameView;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
RacingGameView view = new RacingGameView();
RacingGame model = new RacingGame();
RacingGameController controller = new RacingGameController(model, view);

controller.runGame();
}
}
}
52 changes: 52 additions & 0 deletions src/main/java/racingcar/controller/RacingGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package racingcar.controller;

import racingcar.model.RacingGame;
import racingcar.view.RacingGameView;
import camp.nextstep.edu.missionutils.Console;

import java.util.List;

public class RacingGameController {
private final RacingGame racingGame;
private final RacingGameView racingGameView;

public RacingGameController(RacingGame racingGame, RacingGameView racingGameView) {
this.racingGame = racingGame;
this.racingGameView = racingGameView;
}

public void runGame() {
initializeGame();
playGame();
announceWinners();
}

private void initializeGame() {
racingGameView.printCarNameInputPrompt();
String carNames = Console.readLine();
racingGameView.printRoundCountPrompt();
int roundCount = getRoundCount();

racingGame.initialize(carNames, roundCount);
}

private int getRoundCount() {
try {
return Integer.parseInt(Console.readLine());
} catch (NumberFormatException e) {
throw new IllegalArgumentException();
}
}

private void playGame() {
for (int round = 0; round < racingGame.getRoundCount(); round++) {
racingGame.moveAllCars();
racingGameView.printRoundResult(racingGame.getCarStates());
}
}

private void announceWinners() {
List<String> winners = racingGame.getWinners();
racingGameView.printWinners(winners);
}
}
32 changes: 32 additions & 0 deletions src/main/java/racingcar/model/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package racingcar.model;

import camp.nextstep.edu.missionutils.Randoms;

public class Car {
private static final int MOVE_THRESHOLD = 4;
private static final int RANDOM_BOUND = 9;

private final String name;
private int position;

public Car(String name) {
Validator.validateCarName(name);
this.name = name;
this.position = 0;
}

public void move() {
Validator.validateCarPosition(position);
if (Randoms.pickNumberInRange(0, RANDOM_BOUND) >= MOVE_THRESHOLD) {
position++;
}
}

public String getName() {
return name;
}

public int getPosition() {
return position;
}
}
54 changes: 54 additions & 0 deletions src/main/java/racingcar/model/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package racingcar.model;

import java.util.List;
import java.util.stream.Collectors;

public class RacingGame {
private List<Car> cars;
private int roundCount;

public void initialize(String carNames, int roundCount) {
validate(carNames, roundCount);
this.cars = createCars(carNames);
this.roundCount = roundCount;
}

private void validate(String carNames, int roundCount) {
Validator.validateInputString(carNames);
Validator.validateRoundCount(roundCount);
}

private List<Car> createCars(String carNames) {
return List.of(carNames.split(","))
.stream()
.map(String::trim)
.map(Car::new)
.collect(Collectors.toList());
}

public void moveAllCars() {
cars.forEach(Car::move);
}

public List<String> getCarStates() {
return cars.stream()
.map(car -> car.getName() + " : " + "-".repeat(car.getPosition()))
.collect(Collectors.toList());
}

public List<String> getWinners() {
int maxPosition = cars.stream()
.mapToInt(Car::getPosition)
.max()
.orElse(0);

return cars.stream()
.filter(car -> car.getPosition() == maxPosition)
.map(Car::getName)
.collect(Collectors.toList());
}

public int getRoundCount() {
return roundCount;
}
}
29 changes: 29 additions & 0 deletions src/main/java/racingcar/model/Validator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package racingcar.model;

public class Validator {
private static final int MAX_NAME_LENGTH = 5;

public static void validateInputString(String carNames) {
if (!carNames.contains(",")) {
throw new IllegalArgumentException();
}
}

public static void validateCarName(String carName) {
if (carName.isEmpty() || carName.length() > MAX_NAME_LENGTH) {
throw new IllegalArgumentException();
}
}

public static void validateRoundCount(int roundCount) {
if (roundCount <= 0) {
throw new IllegalArgumentException();
}
}

public static void validateCarPosition(int position) {
if (position < 0) {
throw new IllegalArgumentException();
}
}
}
22 changes: 22 additions & 0 deletions src/main/java/racingcar/view/RacingGameView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package racingcar.view;

import java.util.List;

public class RacingGameView {
public void printCarNameInputPrompt() {
System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
}

public void printRoundCountPrompt() {
System.out.println("시도할 횟수는 몇 회인가요?");
}

public void printRoundResult(List<String> carStates) {
carStates.forEach(System.out::println);
System.out.println();
}

public void printWinners(List<String> winners) {
System.out.println("최종 우승자 : " + String.join(", ", winners));
}
}
32 changes: 32 additions & 0 deletions src/test/java/racingcar/ApplicationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,38 @@ class ApplicationTest extends NsTest {
);
}

@Test
void 이름_길이_초과_예외_테스트() {
assertSimpleTest(() ->
assertThatThrownBy(() -> runException("pobi,woni,javaji,longname,tooLongName", "1"))
.isInstanceOf(IllegalArgumentException.class)
);
}

@Test
void 빈_이름_예외_테스트() {
assertSimpleTest(() ->
assertThatThrownBy(() -> runException("pobi,,woni", "1"))
.isInstanceOf(IllegalArgumentException.class)
);
}

@Test
void 음수_시도_횟수_예외_테스트() {
assertSimpleTest(() ->
assertThatThrownBy(() -> runException("pobi,woni", "-1"))
.isInstanceOf(IllegalArgumentException.class)
);
}

@Test
void 시도_횟수_예외_테스트() {
assertSimpleTest(() ->
assertThatThrownBy(() -> runException("pobi,woni", "abc"))
.isInstanceOf(IllegalArgumentException.class)
);
}

@Override
public void runMain() {
Application.main(new String[]{});
Expand Down