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
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ repositories {
}

dependencies {
testImplementation 'org.mockito:mockito-inline:4.8.0'
testImplementation "org.mockito:mockito-junit-jupiter:3.9.0"

testImplementation "org.junit.jupiter:junit-jupiter:5.7.2"
testImplementation "org.assertj:assertj-core:3.19.0"
}
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RacingGame.java
import core.Car;
import core.Race;
import view.InputView;
import view.ResultView;

import java.util.List;

public class RacingGame {
public static void main(String[] args) {
try{
List<Car> cars = InputView.getCars();
int attempts = InputView.getAttempts();

Race race = new Race(cars, attempts);
System.out.println("실행 결과");

for (int i = 0; i < attempts; i++) {
race.start();
ResultView.printRaceProgress(race.getCars());
}

List<Car> winners = race.getWinners();
ResultView.printWinners(winners);
}
catch (Exception e){
e.printStackTrace();
System.exit(1); // Exit with a non-zero status to indicate an error

}

}
}
49 changes: 49 additions & 0 deletions src/main/java/core/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package core;

import message.Message;
import utils.InputValidator;

public class Car {
private final String name;
private int position;

public Car(String name) {
String errorMessage = InputValidator.validateCarName(name);
if (errorMessage != null) {
throw new IllegalArgumentException(errorMessage); // 유효성 검사 실패시 예외 던짐
}
this.name = name;
this.position = 0;
}

// 새로운 생성자 (name과 position을 초기화할 수 있게 수정)
public Car(String name, int position) {
this.name = name;
this.position = position;
}


// 일정 거리 이상일 때만 위치를 1칸 증가시키는 메서드
public void moveRandomly(int randomValue) {
if (randomValue >= CarConstraints.MIN_MOVE_VALUE) {
position++;
}
}

// 주어진 거리만큼 위치를 이동시키는 메서드
public void moveDistance(int distance) {
this.position += distance;
}

public int getPosition() {
return position;
}

public String getName() {
return name;
}

public boolean isWinner(int maxPosition) {
return position == maxPosition;
}
}
12 changes: 12 additions & 0 deletions src/main/java/core/CarConstraints.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package core;

public class CarConstraints {
// 자동차 이름 최대 길이
public static final int MAX_CAR_NAME_LENGTH = 5;

// 자동차 이름 유효성 검사 정규식
public static final String CAR_NAME_REGEX = "^[a-zA-Z]+[0-9]*$";

// 자동차 이동에 필요한 최소값
public static final int MIN_MOVE_VALUE = 4;
}
56 changes: 56 additions & 0 deletions src/main/java/core/Race.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package core;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Race {
private final List<Car> cars;
private final int attempts;
private final Random random;

public Race(List<Car> cars, int attempts) {
this.cars = cars;
this.attempts = attempts;
this.random = new Random();
}

public void start() {
for (int i = 0; i < attempts; i++) {
raceOnce();
}
}

private void raceOnce() {
for (Car car : cars) {
// 이전에는 random.nextInt(10)을 그대로 사용했으나,
// 이제는 moveRandomly 메서드를 사용해 자동차의 위치를 이동시킨다.
car.moveRandomly(random.nextInt(10));
}
}

public List<Car> getWinners() {
int maxPosition = getMaxPosition();
List<Car> winners = new ArrayList<>();
for (Car car : cars) {
if (car.isWinner(maxPosition)) {
winners.add(car);
}
}
return winners;
}

private int getMaxPosition() {
int maxPosition = 0;
for (Car car : cars) {
if (car.getPosition() > maxPosition) {
maxPosition = car.getPosition();
}
}
return maxPosition;
}

public List<Car> getCars() {
return cars;
}
}
24 changes: 24 additions & 0 deletions src/main/java/message/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package message;

public enum Message {
CAR_NAME_PROMPT("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."),
CAR_NAME_INVALID_FORMAT("자동차 이름은 쉼표(,)로 구분해야 합니다."),
CAR_NAME_TOO_LONG("자동차 이름은 5자 이하여야 합니다."),
CAR_NAME_DUPLICATE("자동차 이름이 중복되었습니다. 다시 입력해주세요."),
ATTEMPT_PROMPT("시도할 회수는 몇회인가요?"),
INVALID_NUMBER("숫자만 입력 가능합니다. 다시 입력해주세요."),
CAR_NAME_INVALID("자동차 이름을 올바르게 입력하세요."),
MAX_ATTEMPTS_EXCEEDED("시도할 회수는 최대 10회까지 가능합니다. 다시 입력해주세요.");

private final String message;

// 생성자
Message(String message) {
this.message = message;
}

// 메시지를 반환하는 메서드
public String getMessage() {
return message;
}
}
65 changes: 65 additions & 0 deletions src/main/java/utils/InputValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package utils;

import java.util.HashSet;
import java.util.Set;
import core.CarConstraints;
import message.Message;

public class InputValidator {

// 자동차 이름 유효성 검사
public static String validateCarName(String name) {
if (name.length() > CarConstraints.MAX_CAR_NAME_LENGTH || !isValidCarName(name)) {
return Message.CAR_NAME_INVALID.getMessage();
}
return null; // 유효한 이름이면 null 반환
}


private static boolean isValidCarName(String name) {
return name.matches(CarConstraints.CAR_NAME_REGEX);
}

// 자동차 이름들에 대한 유효성 검사
public static String validateCarNames(String[] names) {
if (names.length < 2) {
return Message.CAR_NAME_INVALID_FORMAT.getMessage();
}

if (hasDuplicateName(names)) {
return Message.CAR_NAME_DUPLICATE.getMessage();
}

// 각 자동차 이름에 대해 유효성 검사 수행
for (String name : names) {
String errorMessage = validateCarName(name.trim());
if (errorMessage != null) {
return errorMessage; // 유효하지 않으면 에러 메시지 반환
}
}

return null;
}

// 중복된 자동차 이름이 있는지 확인
private static boolean hasDuplicateName(String[] names) {
Set<String> uniqueNames = new HashSet<>();
for (String name : names) {
String trimmedName = name.trim().toLowerCase();
if (!uniqueNames.add(trimmedName)) {
return true;
}
}
return false;
}

// 시도 횟수가 유효한지 확인
public static boolean isValidAttempt(String input) {
try {
Integer.parseInt(input);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
80 changes: 80 additions & 0 deletions src/main/java/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package view;

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

import core.Car;
import message.Message;
import utils.InputValidator;

public class InputView {
private static final Scanner scanner = new Scanner(System.in);

// 자동차 이름을 입력받는 메서드
public static List<Car> getCars() {
printCarNamePrompt();
return getValidCarNames();
}

private static List<Car> getValidCarNames() {
while (true) { // 반복문을 사용하여 유효한 입력을 받을 때까지 계속 요청
String input = scanner.nextLine();
String[] names = input.split(",");

String errorMessage = InputValidator.validateCarNames(names);
if (errorMessage != null) {
printErrorMessage(errorMessage);
continue; // 유효하지 않으면 다시 입력 받기
}

return createCars(names); // 유효한 이름이 입력되면 반환
}
}

private static void printCarNamePrompt() {
System.out.println(Message.CAR_NAME_PROMPT.getMessage());
}

private static void printErrorMessage(String errorMessage) {
System.out.println(errorMessage);
}

// 자동차 객체 리스트를 생성하는 메서드
private static List<Car> createCars(String[] names) {
return Arrays.stream(names)
.map(String::trim) // 공백 제거
.map(Car::new) // 각 이름으로 Car 객체 생성
.toList();
}

// 시도 횟수를 입력받는 메서드
public static int getAttempts() {
printAttemptPrompt();
return getValidAttempt();
}

private static int getValidAttempt() {
while (true) { // 반복문으로 시도 횟수를 유효하게 받을 때까지 반복
String input = scanner.nextLine();
if (InputValidator.isValidAttempt(input)) {
return parseAttempt(input);
}

printErrorMessage(Message.INVALID_NUMBER.getMessage());
}
}

private static void printAttemptPrompt() {
System.out.println(Message.ATTEMPT_PROMPT.getMessage());
}

private static int parseAttempt(String input) {
int attempts = Integer.parseInt(input);
if (attempts > 10) {
printErrorMessage(Message.MAX_ATTEMPTS_EXCEEDED.getMessage());
return getAttempts(); // 시도 횟수 초과시 다시 입력 받기
}
return attempts;
}
}
22 changes: 22 additions & 0 deletions src/main/java/view/ResultView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// view/ResultView.java
package view;

import core.Car;
import java.util.List;

public class ResultView {
public static void printRaceProgress(List<Car> cars) {
for (Car car : cars) {
System.out.print(car.getName() + " : ");
System.out.println("-".repeat(car.getPosition()));
}
System.out.println();
}

public static void printWinners(List<Car> winners) {
String winnerNames = String.join(", ", winners.stream()
.map(Car::getName)
.toList());
System.out.println(winnerNames + "가 최종 우승했습니다.");
}
}
Loading