Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
af3a1a2
style : utf-8 인코딩 설정
Jan 15, 2023
7e1b37d
docs : 구현할 기능 목록 정리
Jan 15, 2023
b247aec
feat : 사용자에게 자동차 이름을 입력받는 기능 구현
Jan 15, 2023
cc2a2ed
feat : 일급 컬렉션을 이용해서 Cars 클래스 생성
Jan 15, 2023
5dda093
feat : RaceGameController 구조 설계
Jan 15, 2023
f6791dc
feat : 자동차 이름 유효성 검증
Jan 15, 2023
4d17780
test : 자동차 이름 유효성 검증 테스트 작성
Jan 15, 2023
4d91330
refactor : carTest 삭제 후 NameValidatorTest로 이동
Jan 15, 2023
c0b8942
test : 자동차 이름 생성 테스트 작성
Jan 15, 2023
c86d6eb
feat : 시도할 횟수 입력받는 기능 구현
Jan 15, 2023
77c982c
refactor : 시도 횟수 예외처리 리팩토링
Jan 15, 2023
fc31f62
test : 시도 횟수 테스트 구현
Jan 15, 2023
cc88871
refactor : 시도 횟수 테스트 수정
Jan 15, 2023
d5a28d6
refactor : 유효성 검사 시점 변경
Jan 16, 2023
cd25f2c
feat : 우승자와 가장 멀리 간 자동차를 찾는 로직 구현
Jan 16, 2023
ab772dc
feat : 자동차를 전진시키는 기능 구현
Jan 16, 2023
87e06d5
feat : 자동차 경주 게임을 완료한 후 누가 우승했는지를 출력하는 기능 구현
Jan 16, 2023
19f03ab
test : 랜덤값에 따른 자동차 이동 테스트
Jan 16, 2023
a5b4798
refactor : 0부터 9 사이의 랜덤 숫자를 상수로 분리
Jan 16, 2023
d6ad19e
refactor : Argument.arguments 변경
Jan 17, 2023
4893403
feat : 자동차 생성 테스트 추가
Jan 17, 2023
3c830d2
refactor : RaceGameController run() 메서드의 출력 코드 제거
Jan 17, 2023
58fd695
refactor : IntStream을 이용해서 박싱 비용을 줄이기
Jan 18, 2023
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
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ java {
test {
useJUnitPlatform()
}
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
14 changes: 14 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 구현할 기능 목록

+ 경주할 자동차 이름을 입력받습니다.
+ 이름은 쉼표(,)를 기준으로 구분합니다.
+ `예외 처리` : 이름은 5자 이하만 가능합니다.(1글자 이상 ~ 5글자 이하)
+ `예외 처리` : 자동차 이름이 비어있는 경우
+ `예외 처리` : 자동차 이름이 중복되는 경우
+ 시도할 횟수를 입력받습니다.
+ `예외 처리` : 문자를 입력했을 경우
+ `예외 처리` : 1회 미만의 숫자를 입력했을 경우
+ 자동차를 전진시킵니다.
+ 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우입니다.
+ 자동차 경주 게임을 완료한 후 누가 우승했는지를 출력합니다. 우승자는 한 명 이상일 수 있습니다.
+ 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분합니다.
11 changes: 8 additions & 3 deletions src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package racingcar;

import racingcar.controller.RaceGameController;

public class Application {
public static void main(String[] args) {
// TODO 구현 진행
}
public static void main(String[] args) {
RaceGameController raceGameController = new RaceGameController();
raceGameController.ready();
raceGameController.run();
raceGameController.announceWinner();
}
}
12 changes: 0 additions & 12 deletions src/main/java/racingcar/Car.java

This file was deleted.

63 changes: 63 additions & 0 deletions src/main/java/racingcar/controller/RaceGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package racingcar.controller;

import java.util.List;

import racingcar.model.Cars;
import racingcar.util.generator.NameGenerator;
import racingcar.util.generator.NumberGenerator;
import racingcar.view.InputData;
import racingcar.view.OutputData;

public class RaceGameController {
private Cars cars;
private int attemptNumber;

public void ready() {
// 자동차 경주 게임 준비
cars = new Cars();
addCarNamesToCarList();
attemptNumber = getAttemptNumber();
printResultHeader();
}

public void run() {
// 자동차 경주 게임 시작
moveCarsForwardByAttemptNumber();
}

public void announceWinner() {
// 자동차 경주 게임 최종 우승자 발표
OutputData.printWinners(cars);
}

private static void printResultHeader() {
OutputData.printBlankLine();
OutputData.printGameResultHeader();
}

private void moveCarsForwardByAttemptNumber() {
for (int i = 0; i < attemptNumber; ++i) {
cars.moveForward();
OutputData.printCarsPosition(cars.getCarList());
}
}

private void addCarNamesToCarList() {
try {
List<String> carNamesList = NameGenerator.generateCarNames(InputData.getCarNames());
cars.addCarNames(carNamesList);
} catch (IllegalArgumentException e) {
OutputData.printErrorMessage(e.getMessage()); // [ERROR] + 메시지 출력
addCarNamesToCarList();
}
}

private int getAttemptNumber() {
try {
return NumberGenerator.generateNumber(InputData.getAttemptNumber());
} catch (IllegalArgumentException e) {
OutputData.printErrorMessage(e.getMessage());
return getAttemptNumber();
}
}
}
45 changes: 45 additions & 0 deletions src/main/java/racingcar/model/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package racingcar.model;

import camp.nextstep.edu.missionutils.Randoms;
import racingcar.util.validator.NameValidator;

public class Car {
private final String name;
private int position = 0;
private final static int START_RANDOM_NUMBER = 0;
private final static int END_RANDOM_NUMBER = 9;

public Car(String name) {
this.name = name;
NameValidator.validateCarName(name);
}

public String getName() {
return name;
}

public int getPosition() {
return position;
}

public void moveForward() {
int randomNumber = Randoms.pickNumberInRange(START_RANDOM_NUMBER, END_RANDOM_NUMBER);
if (shouldMove(randomNumber)) {
this.position++;
}
}

public boolean shouldMove(int randomNumber) {
if (randomNumber >= 4) {
return true;
}
return false;
}

public boolean isWinner(int maxPosition) {
if (this.getPosition() == maxPosition) {
return true;
}
return false;
}
}
46 changes: 46 additions & 0 deletions src/main/java/racingcar/model/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package racingcar.model;

import static java.util.stream.Collectors.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import racingcar.util.validator.NameValidator;

public class Cars {
private List<Car> carList = new ArrayList<>();

public void addCarNames(List<String> carNamesList) {
NameValidator.validateCarNames(carNamesList.toString());
for (String carName : carNamesList) {
NameValidator.validateCarName(carName);
this.carList.add(new Car(carName));
}
}

public List<Car> getCarList() {
return carList.stream()
.collect(collectingAndThen(toList(), Collections::unmodifiableList));
}

public List<String> findWinnersName() {
return carList.stream()
.filter(car -> car.isWinner(findMaxPosition()))
.map(Car::getName)
.collect(toList());
}

public int findMaxPosition() {
return carList.stream()
.mapToInt(Car::getPosition)
.max()
.orElse(0);
}

public void moveForward() {
for (Car car : carList) {
car.moveForward();
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/racingcar/util/generator/NameGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package racingcar.util.generator;

import java.util.Arrays;
import java.util.List;

public class NameGenerator {
private static final String SEPARATE_BY_COMMA = ",";

public static List<String> generateCarNames(String carNames) {
return Arrays.asList(carNames.split(SEPARATE_BY_COMMA));
}
}
11 changes: 11 additions & 0 deletions src/main/java/racingcar/util/generator/NumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package racingcar.util.generator;

import racingcar.util.validator.NumberValidator;

public class NumberGenerator {

public static int generateNumber(String attemptNumber) {
NumberValidator.validateNumber(attemptNumber);
return Integer.parseInt(attemptNumber);
}
}
65 changes: 65 additions & 0 deletions src/main/java/racingcar/util/validator/NameValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package racingcar.util.validator;

import java.util.Arrays;
import java.util.List;

public class NameValidator {
private static final int MIN_LENGTH_CAR_NAME = 1;
private static final int MAX_LENGTH_CAR_NAME = 5;
private static final String SEPARATE_BY_COMMA = ",";
private static final String NAME_LENGTH_EXCEPTION_MESSAGE = "자동차의 이름은 1자 이상 5자 이하여야 합니다.";
private static final String EMPTY_CAR_NAME_MESSAGE = "자동차의 이름은 비어있을 수 없습니다.";
private static final String NULL_CAR_NAME_MESSAGE = "자동차의 이름은 NULL일 수 없습니다.";
private static final String DUPLICATE_CAR_NAME_MESSAGE = "자동차의 이름은 중복일 수 없습니다.";

public static void validateCarNames(String carNames) {
/**
* 자동차 이름 예외처리
* 1. Null
* 2. empty
* 3. Duplicate
* pobi,chan,dong
* Length는 validateCarName 에서 검증할 수 있다.
*/
checkCarNameIsNull(carNames);
checkCarNameIsEmpty(carNames);
checkCarNameIsDuplicate(carNames);
}

public static void validateCarName(String carName) {
checkCarNameIsNull(carName);
checkCarNameIsEmpty(carName);
checkCarNameLength(carName);
}

private static void checkCarNameIsDuplicate(String carNames) {
List<String> carNameList = Arrays.asList(carNames.split(SEPARATE_BY_COMMA));
if (carNameList.size() != carNameList.stream().distinct().count()) {
throw new IllegalArgumentException(DUPLICATE_CAR_NAME_MESSAGE);
}
}

private static void checkCarNameIsNull(String carName) {
if (carName == null) {
throw new IllegalArgumentException(NULL_CAR_NAME_MESSAGE);
}
}

private static void checkCarNameIsEmpty(String carName) {
if (isBlank(carName)) {
throw new IllegalArgumentException(EMPTY_CAR_NAME_MESSAGE);
}
}

private static boolean isBlank(String carName) {
// 문자열이 비어 있거나, 빈 공백으로만 이루어져 있으면 true를 return하는 메서드
return carName.isEmpty() || carName.trim().isEmpty();
}

private static void checkCarNameLength(String carName) {
if (carName.length() < MIN_LENGTH_CAR_NAME || carName.length() > MAX_LENGTH_CAR_NAME) {
throw new IllegalArgumentException(NAME_LENGTH_EXCEPTION_MESSAGE);
}
}

}
32 changes: 32 additions & 0 deletions src/main/java/racingcar/util/validator/NumberValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package racingcar.util.validator;

public class NumberValidator {

private static final String MIN_ATTEMPT_NUMBER_MESSAGE = "시도 횟수는 1회 이상이어야 합니다.";
private static final String ATTEMPT_NUMBER_IS_NOT_INTEGER = "시도 횟수는 숫자여야 한다.";

public static void validateNumber(String attemptNumber) {
/**
* 시도 횟수 예외처리
* 1. 1 미만의 숫자 입력했을 경우
* 2. 숫자를 입력하지 않았을 경우
*/
checkNumberIsNotInteger(attemptNumber);
checkNumberRange(attemptNumber);
}

private static void checkNumberIsNotInteger(String attemptNumber) {
try {
Integer.parseInt(attemptNumber);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(ATTEMPT_NUMBER_IS_NOT_INTEGER);
}
}

private static void checkNumberRange(String attemptNumber) {
int attemptNumberToInteger = Integer.parseInt(attemptNumber);
if (attemptNumberToInteger < 1) {
throw new IllegalArgumentException(MIN_ATTEMPT_NUMBER_MESSAGE);
}
}
}
18 changes: 18 additions & 0 deletions src/main/java/racingcar/view/InputData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package racingcar.view;

import camp.nextstep.edu.missionutils.Console;

public class InputData {
private static final String CAR_NAME_ALERT_MESSAGE = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)";
private static final String NUMBER_OF_ATTEMPT_ALERT_MESSAGE = "시도할 회수는 몇회인가요?";

public static String getCarNames() {
System.out.println(CAR_NAME_ALERT_MESSAGE);
return Console.readLine();
}

public static String getAttemptNumber() {
System.out.println(NUMBER_OF_ATTEMPT_ALERT_MESSAGE);
return Console.readLine();
}
}
44 changes: 44 additions & 0 deletions src/main/java/racingcar/view/OutputData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package racingcar.view;

import java.util.List;

import racingcar.model.Car;
import racingcar.model.Cars;

public class OutputData {
private static final String ERROR_MESSAGE = "[ERROR] ";
private static final String GAME_RESULT_HEADER = "실행 결과";
private static final String FINAL_WINNER_MESSAGE = "최종 우승자 : ";

public static void printErrorMessage(String message) {
System.out.println(ERROR_MESSAGE + message);
}

public static void printGameResultHeader() {
System.out.println(GAME_RESULT_HEADER);
}

public static void printBlankLine() {
System.out.println();
}

public static void printCarsPosition(List<Car> carList) {
for (Car car : carList) {
System.out.println(car.getName() + " : " + printCarsCurrentPosition(car));
}
printBlankLine();
}

public static void printWinners(Cars cars) {
List<String> winnersName = cars.findWinnersName();
System.out.println(FINAL_WINNER_MESSAGE + String.join(",", winnersName));
}

private static StringBuilder printCarsCurrentPosition(Car car) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < car.getPosition(); ++i) {
builder.append("-");
}
return builder;
}
}
Loading