diff --git a/docs/README.md b/docs/README.md index e69de29b..a85a12d6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,10 @@ +자동차 이름 입력기능 +쉼표를 기준으로 구분 +5자 이하만 가능 +이름이 조건에 맞지 않을 시 에러 메시지 +이동할 횟수 입력 기능 +랜덤을 사용한 자동차 전진 기능 +전진과 자동차 이름 동시 출력 기능 +완료 후 우승자 표시 기능 +우승자가 여러 명일 경우 쉼표 구분 +잘못된 값을 입력할 시 종료하는 기능 diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java new file mode 100644 index 00000000..087c787a --- /dev/null +++ b/src/main/java/controller/GameController.java @@ -0,0 +1,47 @@ +package controller; + +import service.CarService; + +import view.GameView; + +public class GameController { + + + + private final GameView gameView; + private final CarService carService; + + public GameController() { + gameView = new GameView(); + carService = new CarService(); + } + + /*public static GameController getInstance() { + if(gameController == null) { + gameController = new GameController(); + } + return gameController; + }*/ + + public void raceSet(String[] names) { + gameView.printResult(); + carService.fill(names); + } + + public void race(int count) { + String result = carService.getResult(count); + gameView.printResult(result); + } + + public void raceResult() { + String winners = carService.getWinners(); + gameView.printWinners(winners); + } + + public void close() { + carService.close(); + gameView.close(); + //gameController = null; + } + +} diff --git a/src/main/java/domain/Car.java b/src/main/java/domain/Car.java new file mode 100644 index 00000000..c10bf935 --- /dev/null +++ b/src/main/java/domain/Car.java @@ -0,0 +1,31 @@ +package domain; + +public class Car implements Comparable { + private static final int STOP = 3; + private final String name; + private int distance; + + public Car(final String name, int distance) { + this.name = name; + this.distance = distance; + } + + public Car(final String name) { + this.name = name; + } + + + public void move(final int value) { + if(value > STOP) { + distance++; + } + } + + public int getDistance() { return distance; } + public String getName() { return name; } + + @Override + public int compareTo(Car o) { + return o.distance - this.distance; + } +} diff --git a/src/main/java/exception/GameInputException.java b/src/main/java/exception/GameInputException.java new file mode 100644 index 00000000..3f8afe7e --- /dev/null +++ b/src/main/java/exception/GameInputException.java @@ -0,0 +1,38 @@ +package exception; + +import message.GameMessage; + +public class GameInputException { + + //private static GameInputException defaultGameInputException; + + public GameInputException() { + } + + + + public void validateNameLength(String[] names) { + for(String name : names) { + if(name.length() > 5) { + throw new IllegalArgumentException(GameMessage.nameError.getMessage()); + } + } + } + + public void validateNumber(String number) { + if(!number.matches(GameMessage.REGEX.getMessage())) { + throw new IllegalArgumentException(GameMessage.countError.getMessage()); + } + } + + public void validateNumberZero(String number) { + if(number.length() > 1 && number.charAt(0) == '0') { + throw new IllegalArgumentException(GameMessage.countError.getMessage()); + } + } + + public void close() { + //defaultGameInputException = null; + } + +} diff --git a/src/main/java/game/RacingGame.java b/src/main/java/game/RacingGame.java new file mode 100644 index 00000000..5da077ae --- /dev/null +++ b/src/main/java/game/RacingGame.java @@ -0,0 +1,58 @@ +package game; + +import controller.GameController; + +import exception.GameInputException; + +import view.InputView; + +public class RacingGame { + + // private static RacingGame defaultRacingGame; + private final InputView inputView; + private final GameInputException gameInputException; + private final GameController gameController; + + public RacingGame() { + inputView = new InputView(); + gameInputException = new GameInputException(); + gameController = new GameController(); + } + + /*public static RacingGame getInstance() { + if(defaultRacingGame == null) { + defaultRacingGame = new RacingGame(); + } + return defaultRacingGame; + }*/ + + public void run() { + String[] names = preHandleNames(); + int count = preHandleCount(); + gameController.raceSet(names); + gameController.race(count); + gameController.raceResult(); + } + + private String[] preHandleNames() { + String input = inputView.inputCarNames(); + String[] names = input.split(","); + gameInputException.validateNameLength(names); + return names; + } + + private int preHandleCount() { + String input = inputView.inputRaceCount(); + gameInputException.validateNumber(input); + gameInputException.validateNumberZero(input); + return Integer.parseInt(input); + } + + public void close() { + gameController.close(); + gameInputException.close(); + inputView.close(); + //defaultRacingGame = null; + } + +} diff --git a/src/main/java/message/GameMessage.java b/src/main/java/message/GameMessage.java new file mode 100644 index 00000000..22a5b2c6 --- /dev/null +++ b/src/main/java/message/GameMessage.java @@ -0,0 +1,24 @@ +package message; + +public enum GameMessage { + + start("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"), + count("시도할 회수는 몇회인가요?"), + result("실행 결과"), + winner("최종 우승자"), + equal(" : "), + bar("-"), + nameError("자동차의 이름은 5자리 이내입니다."), + countError("회수는 음수가 아닌 정수로 입력해주세요."), + REGEX("[0-9]+"), + newLine("\n"); + + private final String message; + + GameMessage(final String message) { + this.message = message; + } + + public String getMessage() { return message; } + +} diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e7..abc2431f 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,22 @@ package racingcar; +import camp.nextstep.edu.missionutils.Randoms; +import camp.nextstep.edu.missionutils.Console; +import game.RacingGame; + +import java.util.ArrayList; +import java.util.List; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + // TODO: 프로그램 구현; + RacingGame game =new RacingGame(); + + try { + game.run(); + } + finally { + game.close(); + } } } diff --git a/src/main/java/service/CarService.java b/src/main/java/service/CarService.java new file mode 100644 index 00000000..0bc327a5 --- /dev/null +++ b/src/main/java/service/CarService.java @@ -0,0 +1,89 @@ +package service; + +import camp.nextstep.edu.missionutils.Randoms; + +import domain.Car; + +import message.GameMessage; + +import java.util.ArrayList; +import java.util.Collections; + +public class CarService { + + private final ArrayList carList; + private final StringBuilder sb; + //private static CarService defaultCarService; + + public CarService() { + carList = new ArrayList<>(); + sb = new StringBuilder(); + } + + + + public void close() { + //defaultCarService = null; + } + + public void fill(String[] carNameArr,int[] distance) { + int i = 0; + for(String name : carNameArr) { + carList.add(new Car(name,distance[i++])); + } + } + + public void fill(String[] carNameArr) { + for(String name : carNameArr) { + carList.add(new Car(name)); + } + } + + public String getResult(int count) { + while(count-- > 0) { + race(); + if(count != 0) { + sb.append(GameMessage.newLine.getMessage()); + } + } + return sb.toString(); + } + + private void race() { + for(Car car : carList) { + int randomNumber = Randoms.pickNumberInRange(0,9); + car.move(randomNumber); + raceRecord(car); + } + } + + private void raceRecord(Car car) { + sb.append(car.getName()).append(GameMessage.equal.getMessage()); + buildBar(car.getDistance()); + } + + private void buildBar(int distance) { + while(distance-- > 0) { + sb.append(GameMessage.bar.getMessage()); + } + sb.append(GameMessage.newLine.getMessage()); + } + + public String getWinners() { + Collections.sort(carList); + int winDistance = carList.get(0).getDistance(); + ArrayList winnerList = makeWinnerList(winDistance); + return String.join(", ",winnerList); + } + + private ArrayList makeWinnerList(int winDistance) { + ArrayList winnerList = new ArrayList<>(); + for(Car car : carList) { + int distance = car.getDistance(); + if(winDistance > distance) { break; } + winnerList.add(car.getName()); + } + return winnerList; + } + +} diff --git a/src/main/java/view/GameView.java b/src/main/java/view/GameView.java new file mode 100644 index 00000000..8b27c95c --- /dev/null +++ b/src/main/java/view/GameView.java @@ -0,0 +1,30 @@ +package view; + +import message.GameMessage; + +public class GameView { + + //private static GameView defaultGameView; + + public GameView() { + } + + + + public void printResult() { + System.out.println(GameMessage.newLine.getMessage() + GameMessage.result.getMessage()); + } + + public void printResult(String result) { + System.out.println(result); + } + + public void printWinners(String winners) { + System.out.println(GameMessage.winner.getMessage() + GameMessage.equal.getMessage() + winners); + } + + public void close() { + //defaultGameView = null; + } + +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..ae8876e3 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,31 @@ +package view; + +import camp.nextstep.edu.missionutils.Console; + +import message.GameMessage; + +public class InputView { + + + + public InputView() { + } + + + + public String inputCarNames() { + System.out.println(GameMessage.start.getMessage()); + return Console.readLine(); + } + + public String inputRaceCount() { + System.out.println(GameMessage.count.getMessage()); + return Console.readLine(); + } + + public void close() { + Console.close(); + + } + +} diff --git a/src/test/java/car/CarTest.java b/src/test/java/car/CarTest.java new file mode 100644 index 00000000..5d7dc6ec --- /dev/null +++ b/src/test/java/car/CarTest.java @@ -0,0 +1,55 @@ +package car; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import service.CarService; + +import exception.GameInputException; + +import message.GameMessage; + +import org.junit.jupiter.api.Test; + +public class CarTest { + + @Test + void 우승자_목록_반환() { + String[] carNames = {"a","b","c","d"}; + int[] distanceArr = {2,4,1,0}; + + CarService carService = new CarService(); + carService.fill(carNames,distanceArr); + String winner = carService.getWinners(); + + assertThat(winner).isEqualTo("b"); + carService.close(); + } + + @Test + void 숫자_정규식_확인() { + GameInputException gameException = new GameInputException(); + String input = "123456789!"; + assertThatThrownBy(() -> gameException.validateNumber(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(GameMessage.countError.getMessage()); + } + + @Test + void 이름_길이_확인() { + GameInputException gameException = new GameInputException(); + String[] names = {"a","a23","abc123"}; + assertThatThrownBy(() -> gameException.validateNameLength(names)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(GameMessage.nameError.getMessage()); + } + + @Test + void 회수_첫숫자_0_체크() { + GameInputException gameException = new GameInputException(); + String input = "01"; + assertThatThrownBy(() -> gameException.validateNumberZero(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(GameMessage.countError.getMessage()); + } +} diff --git a/src/test/java/car/ImplTest.java b/src/test/java/car/ImplTest.java new file mode 100644 index 00000000..e8bb9ad5 --- /dev/null +++ b/src/test/java/car/ImplTest.java @@ -0,0 +1,58 @@ +package car; + +import camp.nextstep.edu.missionutils.test.NsTest; +import org.junit.jupiter.api.Test; + +import racingcar.Application; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInRangeTest; +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class ImplTest extends NsTest { + + @Test + void 전체기능_확인() { + assertRandomNumberInRangeTest( + () -> { + run("a,b,c", "3"); + assertThat(output()).contains("a : -", "b : -","c : ", + "a : --", "b : --","c : ", + "a : ---", "b : ---","c : -","최종 우승자 : a, b"); + }, + 4,4,3,4,4,2,4,4,4 + ); + } + + @Test + void 회수가_0일때() { + assertRandomNumberInRangeTest( + () -> { + run("a,b,c", "0"); + assertThat(output()).contains("최종 우승자 : a, b, c"); + }, + -1,-1,-1 + ); + } + + @Test + void 음수에_대한_예외_처리() { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException("pobi,a", "-11")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @Test + void 회수가_0으로_시작할_때_예외_처리() { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException("pobi,a", "01")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + + @Override + protected void runMain() { Application.main(new String[]{}); } +} \ No newline at end of file