diff --git a/docs/README.md b/docs/README.md index e69de29bb2d..ff7fd615332 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,80 @@ +- 다리는 왼쪽에서 오른쪽으로 건너야 한다 +- 위 아래 둘 중 하나의 칸만 건널 수 있다 +- 다리의 길이를 숫자로 입력받는다 +- 다리를 생성할 때 위칸과 아래칸 중 건널 수 있는 칸은 0과 1를 이용한다 + - 0인 경우 아래 칸, D로 나타낸다. + - 1인 경우 위칸, U로 나타낸다 +- 다리가 생성되면 플레이어가 이동할 칸을 선택한다 + - 이동할 때 위칸은 대문자 U, 아래칸은 D를 입력한다 + - 이동한 칸을 건널 수 있다면 O, 건널 수 없다면 X로 표시한다 +- 다리를 끝까지 건너면 게임이 종료된다 +- 게임 중 실패 시 게임을 재시작하거나 종료할 수 있다 + - 재시작 해도 처음 만든 다리를 재사용한다 + - 게임 결과에 총 시도한 횟수는 첫 시도를 포함해 게임을 종료할 때까지 시도한 횟수를 나타낸다 +- 사용자가 잘못된 값을 입력할 경우 예외를 발생시키고 [ERROR] 로 시작하는 메세지를 출력한다 + - 그 부분부터 입력을 다시 받는다 + +#### 메서드 네이밍 규칙 +- 메서드명은 동사/전치사, 클래스명은 명사, 다른 타입으로 전환하는 메서드나 빌더 패턴을 구현한 클래스의 메서드는 전치사를 쓸 수 없다 +- 상태 데이터를 가지는 객체에서 데이터를 get하지말고 객체에 메세지를 보낸다 +- init : 데이터를 초기화 하는 메서드 명에 쓰인다 +- is/has/can : boolean 값을 리턴한다. + - is : true,false를 판단하는 메서드 명 + - has : 데이터를 가지고 있는지 확인하는 메서드 명 + - can : 할 수 있는지 , 없는지 확인하는 메서드 명 +- create : 새로운 객체를 만든 후 리턴해주는 메서드 명 +- find : 데이터를 찾는 메서드 명 +- to : 해당 객체를 다른 형태의 객체로 변환해주는 메서드 명 +- A-By-B : B를 기준으로 A를 하겠다는 메서드 명 + +### 입력 +- [x] 다리 길이를 입력받는다 + - [x] 잘못된 값 입력 시 예외처리 +- [x] 이동할 칸을 입력 받는다 + - [x] 잘못된 값 입력 시 예외처리 +- [x] 게임 재시작/종료 여부를 입력받는다 + - [x] 잘못된 값 입력 시 예외처리 +### 출력 +- [x] 게임 시작 문구 +- [] 게임 종료 문구 + - [] 최종 게임 결과 + - [] 게임 성공 여부 + - [] 총 시도한 횟수 +- [] 게임 중 메세지 + - [] 이동할 수 있는 칸을 선택한 경우 O 표시 + - [] 이동할 수 없는 칸을 선택한 경우 X 표시 + - [] 선택하지 않은 칸은 공백 한 칸으로 표시 + + - [] 다리의 시작은 `[`, 다리의 끝은 `]`으로 표시 + - [] 다리 칸의 구분은 ` | `(앞뒤 공백 포함) 문자열로 구분 + - [] 현재까지 건넌 다리를 모두 출력 + - [] 게임을 다시 시도할지 여부를 묻는 메세지 + - [] 이동할 칸 입력을 요구하는 메세지 + - [] 잘못된 값 입력 시 예외처리 + - [] 재입력을 요구하는 메세지 +### 모델(도메인) +- [x] BridgeMaker : 사용자가 입력한 숫자만큼 다리를 생성한다 + - [x] 숫자만큼 리스트에 담아 반환한다 +- [x] GameStatus : 게임 진행 상황. 사용자의 사항을 저장한다 + - [x] getDirection : 방향과 결과 반환 + - [x] isDirection : 입력 방향과 같은 방향인지 확인 + - [x] startGameCount : 게임 시도 횟수 + --- + 추가된 사항 + - [x] playGameCount : 게임 시도 횟수 증가 + - [x] getAttempsGame : 게임 시도 횟수 반환 +- [x] Direction : 방향 표현 + - [x] 0,1 사이의 값을 받아 D,U중 하나 반환 + - [x] D,U를 입력받아 인스턴스 반환 +- [x] InputValidation : 사용자의 입력값을 검증한다 + - [x] 숫자는 3-20 사이여야한다 + - [x] 다리 생성 시 문자열이 입력될 경우 예외 처리 + - [x] 이동 시 문자가 U,D여야한다 + - [x] 요구한 문자열과 다른 값 입력 시 예외처리 + - [x] 게임 재시작/종료는 R,Q여야 한다 + - [x] 요구한 문자열과 다른 값 입력 시 예외처리 +- [] Bridgegeneration : 다리 생성기 + - [] 라운드와 방향을 입력받아 moveResult 반환 + - [] isLastRound : 마지막 라운드인지 확인 +- [x] MoveResult : 성공, 실패를 표현 + - [x] getCommand : O,X 반환 + - [x] isFail : 결과가 실패인지 반환 diff --git a/src/main/java/bridge/Application.java b/src/main/java/bridge/Application.java index 5cb72dfd3de..1bc0abe21ea 100644 --- a/src/main/java/bridge/Application.java +++ b/src/main/java/bridge/Application.java @@ -1,8 +1,12 @@ package bridge; +import bridge.controller.BridgeGameController; + public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + BridgeGameController bridgeGameController = new BridgeGameController(); + bridgeGameController.run(); } } diff --git a/src/main/java/bridge/BridgeGame.java b/src/main/java/bridge/BridgeGame.java deleted file mode 100644 index 834c1c8362b..00000000000 --- a/src/main/java/bridge/BridgeGame.java +++ /dev/null @@ -1,23 +0,0 @@ -package bridge; - -/** - * 다리 건너기 게임을 관리하는 클래스 - */ -public class BridgeGame { - - /** - * 사용자가 칸을 이동할 때 사용하는 메서드 - *

- * 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. - */ - public void move() { - } - - /** - * 사용자가 게임을 다시 시도할 때 사용하는 메서드 - *

- * 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. - */ - public void retry() { - } -} diff --git a/src/main/java/bridge/BridgeMaker.java b/src/main/java/bridge/BridgeMaker.java index 27e9f2cfa7f..74b99e5ca3b 100644 --- a/src/main/java/bridge/BridgeMaker.java +++ b/src/main/java/bridge/BridgeMaker.java @@ -1,5 +1,8 @@ package bridge; +import bridge.model.Direction; + +import java.util.LinkedList; import java.util.List; /** @@ -18,6 +21,14 @@ public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) { * @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다. */ public List makeBridge(int size) { - return null; + List bridge = new LinkedList<>(); + while (bridge.size() < size) { + Direction direction = Direction.toCommand(bridgeNumberGenerator.generate()); + bridge.add(direction.getCommand()); + } + return bridge; } + + + } diff --git a/src/main/java/bridge/InputView.java b/src/main/java/bridge/InputView.java deleted file mode 100644 index c3911c8a8e7..00000000000 --- a/src/main/java/bridge/InputView.java +++ /dev/null @@ -1,28 +0,0 @@ -package bridge; - -/** - * 사용자로부터 입력을 받는 역할을 한다. - */ -public class InputView { - - /** - * 다리의 길이를 입력받는다. - */ - public int readBridgeSize() { - return 0; - } - - /** - * 사용자가 이동할 칸을 입력받는다. - */ - public String readMoving() { - return null; - } - - /** - * 사용자가 게임을 다시 시도할지 종료할지 여부를 입력받는다. - */ - public String readGameCommand() { - return null; - } -} diff --git a/src/main/java/bridge/OutputView.java b/src/main/java/bridge/OutputView.java deleted file mode 100644 index 69a433a6285..00000000000 --- a/src/main/java/bridge/OutputView.java +++ /dev/null @@ -1,23 +0,0 @@ -package bridge; - -/** - * 사용자에게 게임 진행 상황과 결과를 출력하는 역할을 한다. - */ -public class OutputView { - - /** - * 현재까지 이동한 다리의 상태를 정해진 형식에 맞춰 출력한다. - *

- * 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. - */ - public void printMap() { - } - - /** - * 게임의 최종 결과를 정해진 형식에 맞춰 출력한다. - *

- * 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. - */ - public void printResult() { - } -} diff --git a/src/main/java/bridge/controller/BridgeGameController.java b/src/main/java/bridge/controller/BridgeGameController.java new file mode 100644 index 00000000000..c1e96360c0d --- /dev/null +++ b/src/main/java/bridge/controller/BridgeGameController.java @@ -0,0 +1,40 @@ +package bridge.controller;// @ author ninaaano + +import bridge.BridgeMaker; +import bridge.model.Bridge; +import bridge.model.GameStatus; +import bridge.model.MoveResult; +import bridge.view.InputView; +import bridge.view.OutputView; + +import java.util.List; + +public class BridgeGameController { + + private InputView inputView = new InputView(); + private OutputView outputView = new OutputView(); + private BridgeMaker bridgeMaker = new BridgeMaker(); + + public BridgeGameController() { + inputView.readBridgeSize(); + + + } + + + + public void run(){ + Bridge bridge = initGame(); + while(GameStatus.isPlay){ + } + + } + + + private void createBrigde(){ + inputView.readBridgeSize(); + + + } + +} diff --git a/src/main/java/bridge/model/Bridge.java b/src/main/java/bridge/model/Bridge.java new file mode 100644 index 00000000000..964ce6c27ca --- /dev/null +++ b/src/main/java/bridge/model/Bridge.java @@ -0,0 +1,21 @@ +package bridge.model;// @ author ninaaano + +import java.util.List; + +public class Bridge { + + private List bridgePath; + + public Bridge(List bridgePath) { + this.bridgePath = bridgePath; + } + + public MoveResult correctPath(String command, int count){ + boolean match = bridgePath.get(count).equals(command); + return MoveResult.toMatch(command,match); + } + + public boolean isEnd(int count){ + return bridgePath.size() == count; + } +} diff --git a/src/main/java/bridge/model/BridgeGame.java b/src/main/java/bridge/model/BridgeGame.java new file mode 100644 index 00000000000..e900170a02f --- /dev/null +++ b/src/main/java/bridge/model/BridgeGame.java @@ -0,0 +1,70 @@ +package bridge.model; + +import java.util.LinkedList; +import java.util.List; + +/** + * 다리 건너기 게임을 관리하는 클래스 + */ +public class BridgeGame { + + private List upCommand; + private List downCommand; + private int moveCount; + private int gameCount; + + public BridgeGame() { + this.upCommand = new LinkedList<>(); + this.downCommand = new LinkedList<>(); + this.gameCount = 1; + this.moveCount = 0; + } + + public int getMoveCount() { + return moveCount; + } + + public int getGameCount() { + return gameCount; + } + + /** + * 사용자가 칸을 이동할 때 사용하는 메서드 + *

+ * 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. + */ + public List> move(MoveResult moveResult) { + if (moveResult.getCommand().equals(Direction.UP.getCommand())) { + return addUpCount(moveResult); + } + return addDownCount(moveResult); + } + + private List> addDownCount(MoveResult moveResult) { + downCommand.add(moveResult.getCommand()); + upCommand.add(" "); + return List.of(upCommand, downCommand); + } + + private List> addUpCount(MoveResult moveResult) { + upCommand.add(moveResult.getCommand()); + downCommand.add(" "); + return List.of(upCommand, downCommand); + } + + /** + * 사용자가 게임을 다시 시도할 때 사용하는 메서드 + *

+ * 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. + */ + public void retry() { + upCommand.clear(); + downCommand.clear(); + gameCount++; + } + + public int countMove() { + return moveCount++; + } + +} diff --git a/src/main/java/bridge/model/Direction.java b/src/main/java/bridge/model/Direction.java new file mode 100644 index 00000000000..113e98d9b1d --- /dev/null +++ b/src/main/java/bridge/model/Direction.java @@ -0,0 +1,45 @@ +package bridge.model;// @ author ninaaano + +import java.util.Arrays; +import java.util.stream.Stream; + +public enum Direction { + UP(1,"U"), + DOWN(0,"D") + ; + + private static final String INVALID_INPUT_COMMAND_MESSAGE = "U와 D중 입력해주세요."; + + private final Integer code; + private final String command; + + Direction(Integer code, String command) { + this.code = code; + this.command = command; + } + + public String getCommand(){ + return command; + } + + public static Direction toCommand(int code) { + return Stream.of(values()) + .filter(direction -> direction.code == code) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(INVALID_INPUT_COMMAND_MESSAGE)); + } +// +// public static Direction getCommand(String command){ +// return Arrays.stream(values()) +// .filter(direction -> direction.command.equals(command)) +// .findFirst() +// .orElseThrow(() -> new IllegalArgumentException(INVALID_INPUT_COMMAND_MESSAGE)); +// } + + public boolean isNotEqualsDirection(Direction direction){ + return this != direction; + } + + + +} diff --git a/src/main/java/bridge/model/GameCommand.java b/src/main/java/bridge/model/GameCommand.java new file mode 100644 index 00000000000..dcf25e72ca9 --- /dev/null +++ b/src/main/java/bridge/model/GameCommand.java @@ -0,0 +1,16 @@ +package bridge.model;// @ author ninaaano + +public enum GameCommand { + PLAY("R"), + STOP("Q"); + + private String command; + + GameCommand(String command) { + this.command = command; + } + + public String getProgerss(){ + return command; + } +} diff --git a/src/main/java/bridge/model/GameStatus.java b/src/main/java/bridge/model/GameStatus.java new file mode 100644 index 00000000000..431678a0ed5 --- /dev/null +++ b/src/main/java/bridge/model/GameStatus.java @@ -0,0 +1,9 @@ +package bridge.model;// @ author ninaaano + +public class GameStatus { + public static boolean isPlay = true; + + public static void isStop(){ + isPlay = false; + } +} diff --git a/src/main/java/bridge/model/MoveResult.java b/src/main/java/bridge/model/MoveResult.java new file mode 100644 index 00000000000..aedf94b9bb0 --- /dev/null +++ b/src/main/java/bridge/model/MoveResult.java @@ -0,0 +1,38 @@ +package bridge.model;// @ author ninaaano + +import java.util.stream.Stream; + +public enum MoveResult { + UP_SUCCESS("U","O",true), + UP_FAIL("U","X",false), + DOWN_SUCCESS("D","O",true), + DOWN_FAIL("D","X",false); + + private final String command; + private final String mark; + private final boolean check; + + MoveResult(String command, String mark, boolean check) { + this.command = command; + this.mark = mark; + this.check = check; + } + + public boolean isCheck(){ + return check; + } + + public static MoveResult toMatch(String command, boolean match) { + return Stream.of(MoveResult.values()) + .filter(direction -> direction.getCommand().equals(command)) + .filter(count -> count.isCheck() == match) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException()); + } + + public static String getCommand() { + return command; + } +} + + diff --git a/src/main/java/bridge/view/InputValidator.java b/src/main/java/bridge/view/InputValidator.java new file mode 100644 index 00000000000..632b968415d --- /dev/null +++ b/src/main/java/bridge/view/InputValidator.java @@ -0,0 +1,49 @@ +package bridge.view;// @ author ninaaano + +import java.util.List; + +public class InputValidator { + private static final int MIN_SIZE = 3; + private static final int MAX_SIZE = 20; + private static final List VALID_MOVING = List.of("U", "D"); + private static final List VALID_COMMAND = List.of("R","Q"); + + // 숫자 이외, 요구한 문자 이외, 범위 외 + private static final String INVALID_RANGE_BRIGDE_MESSAGE = "다리 길이는 3에서 20사이의 숫자여야합니다."; + private static final String INVALID_NUMBER_FORMAT_MESSAGE = "숫자 이외 값이 들어올 수 없습니다."; + private static final String INVALID_MOVING_MESSAGE = "U:(위) / D:(아래)중 하나의 값만 입력 가능합니다."; + private static final String INVALID_COMMAND_MESSAGE = "R:(게임재시작) / Q:(종료)중 하나의 값만 입력 가능합니다."; + + public void validateBridgeSize(String inputBridgeSize){ + Integer size = toInteger(inputBridgeSize); + validateBridgeSizeCheck(size); + } + + private Integer toInteger(String input) { + try { + return Integer.valueOf(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(INVALID_NUMBER_FORMAT_MESSAGE); + } + } + + private void validateBridgeSizeCheck(Integer size) { + if(size < MIN_SIZE || size > MAX_SIZE){ + throw new IllegalArgumentException(INVALID_RANGE_BRIGDE_MESSAGE); + } + } + + public void validateMoving(String input){ + if(!VALID_MOVING.contains(input)){ + throw new IllegalArgumentException(INVALID_MOVING_MESSAGE); + } + } + + public void validateCommand(String input){ + if(!VALID_COMMAND.contains(input)){ + throw new IllegalArgumentException(INVALID_COMMAND_MESSAGE); + } + } + + +} diff --git a/src/main/java/bridge/view/InputView.java b/src/main/java/bridge/view/InputView.java new file mode 100644 index 00000000000..4871db23311 --- /dev/null +++ b/src/main/java/bridge/view/InputView.java @@ -0,0 +1,45 @@ +package bridge.view; + +import camp.nextstep.edu.missionutils.Console; + +/** + * 사용자로부터 입력을 받는 역할을 한다. + */ +public class InputView { + + private static final String INPUT_BRIDGE_SIZE_MESSAGE = "다리의 길이를 입력해주세요."; + private static final String INPUT_MOVE_MESSAGE = "이동할 칸을 선택해주세요. (위: U, 아래: D)"; + private static final String INPUT_COMMAND_MESSAGE = "게임을 다시 시도할지 여부를 입력해주세요. (재시도: R, 종료: Q)"; + + InputValidator inputValidator = new InputValidator(); + + /** + * 다리의 길이를 입력받는다. + */ + public int readBridgeSize() { + System.out.println(INPUT_BRIDGE_SIZE_MESSAGE); + String userInput = Console.readLine(); + inputValidator.validateBridgeSize(userInput); + return Integer.parseInt(userInput); + } + + /** + * 사용자가 이동할 칸을 입력받는다. + */ + public String readMoving() { + System.out.println(INPUT_MOVE_MESSAGE); + String userInput = Console.readLine(); + inputValidator.validateMoving(userInput); + return userInput; + } + + /** + * 사용자가 게임을 다시 시도할지 종료할지 여부를 입력받는다. + */ + public String readGameCommand() { + System.out.println(INPUT_COMMAND_MESSAGE); + String userInput = Console.readLine(); + inputValidator.validateCommand(userInput); + return userInput; + } +} diff --git a/src/main/java/bridge/view/OutputView.java b/src/main/java/bridge/view/OutputView.java new file mode 100644 index 00000000000..5019a88b402 --- /dev/null +++ b/src/main/java/bridge/view/OutputView.java @@ -0,0 +1,64 @@ +package bridge.view; + +import bridge.model.MoveResult; + +import java.util.List; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +/** + * 사용자에게 게임 진행 상황과 결과를 출력하는 역할을 한다. + */ +public class OutputView { + + private static final String GAME_START_MESSAGE = "다리 건너기 게임을 시작합니다.\n"; + private static final String GAME_END_MESSAGE = "최종 게임 결과\n"; + private static final String GAME_SUCCESS_OR_NOT = "게임 성공 여부: {0}"; + private static final String GAME_TRY_COUNT = "총 시도한 횟수: {0}"; + private static final String GAME_OUTPUT_MESSAGE = "[ + {0} + ]"; + private static final String GAME_DELIMITER_MESSAGE = " | "; + private List> path; + + + public void printGameStart() { + System.out.println(GAME_START_MESSAGE); + } + + /** + * 현재까지 이동한 다리의 상태를 정해진 형식에 맞춰 출력한다. + *

+ * 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. + */ + public void printMap(List> path) { + path.forEach(this::getResultMessage); + } + + private String getResultMessage(List moveResults) { + return format(GAME_OUTPUT_MESSAGE,moveResults.stream() + .map(i-> MoveResult.getCommand()) + .collect(Collectors.joining(GAME_DELIMITER_MESSAGE))); + + } + + /** + * private String getResultMessage(List moveResults) { + * return format(MAP_MESSAGE_FORMAT, moveResults.stream() + * .map(MoveResult::getCommand) + * .collect(Collectors.joining(MAP_MESSAGE_DELIMITER)) + * ); + * } + */ + + /** + * 게임의 최종 결과를 정해진 형식에 맞춰 출력한다. + *

+ * 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. + */ + public void printResult(List> path,int count) { + System.out.println(GAME_END_MESSAGE); + printMap(path); + System.out.printf(GAME_SUCCESS_OR_NOT,GAME_TRY_COUNT, path, count); + + } +}