diff --git a/docs/README.md b/docs/README.md
index e69de29bb2d..0539a1e77ed 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -0,0 +1,56 @@
+# 기능 목록
+
+## BridgeGameController
+ - [x] 게임 시작 메서드(gameStart)
+
+## InputManager
+- [x] bridge 길이 반환(makeBridge)
+- [x] 게임 성공/실패 여부 반환(isClearMoveBridge)
+- [x] 게임 재시작/종료 여부 반환(isRetryGame)
+
+## InputView
+- [x] 길이 입력 에러 발생 시 이 부분에서 다시 시작(readBridgeSize)
+- [x] 이동할 칸 입력 에러 발생 시 이 부분에서 다시 시작(readMoving)
+- [x] 게임 재시도 여부 입력 에러 발생 시 이 부분에서 다시 시작(readGameCommand)
+- [x] bridge 길이 입력(inputBridgeSize)
+- [x] 사용자가 이동할 칸 입력(inputBridgeMoveStep)
+- [x] 사용자가 게임을 재시도 여부 입력(inputRetryCommand)
+
+## InputValidator
+- [x] bridge 길이 validate(validateBridgeSize)
+- [x] bridge 이동 명령어 validate(validateBridgeMoveStep)
+- [x] bridge 재시작 명령어 validate(validateBridgeStartCommand)
+- [x] bridge 길이 숫자 validate(validateInputNumber)
+- [x] 명령어 대문자 영어 validate(validateInputUpperEnglish)
+
+## BridgeGame
+- [x] 칸 이동 성공/실패 반환(move)
+- [x] bridge 재시도 여부 반환(retry)
+- [x] bridge 길이(단계) 반환(stages)
+
+## BridgeMaker
+- [x] 생성된 bridge 반환(makeBridge)
+
+## BridgeStatus
+- [x] bridge 성공 시 게임화면 추가(successStair)
+- [x] bridge 실패 시 게임 화면 추가(failStair)
+- [x] bridge 중간 괄호 추가(inputDivision)
+- [x] bridge 게임 화면 초기세팅(init)
+- [x] bridge 게임 화면 초기화(reset)
+
+## GameResultManager
+- [x] bridge 실패 시 게임화면 출력(printFailBridge)
+- [x] bridge 성공 시 게임화면 출력(printSuccessBridge)
+- [x] bridge 게임 횟수 증가(upAttemptCount)
+- [x] bridge 최종 게임 결과 출력(printGameResult)
+- [x] bridge 게임 실패 여부(gameClearFail)
+
+## OutputView
+- [x] 진행 상황 게임 화면 출력(printMap)
+- [x] 최종 게임 결과 출력(printResult)
+
+## 열거형 목록
+- [x] 게임 화면 요소(BridgeWindow)
+- [x] 에러 메시지(ErrorMessage)
+- [x] 게임 명령어(GameCommand)
+- [x] 게임 진행 명령어(GameProgressMessage)
\ No newline at end of file
diff --git a/src/main/java/bridge/Application.java b/src/main/java/bridge/Application.java
index 5cb72dfd3de..960777f3630 100644
--- a/src/main/java/bridge/Application.java
+++ b/src/main/java/bridge/Application.java
@@ -1,8 +1,11 @@
package bridge;
+import bridge.controller.BridgeGameController;
+
public class Application {
public static void main(String[] args) {
- // TODO: 프로그램 구현
+ BridgeGameController bridgeGameController = new BridgeGameController();
+ bridgeGameController.gameStart();
}
}
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
deleted file mode 100644
index 27e9f2cfa7f..00000000000
--- a/src/main/java/bridge/BridgeMaker.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package bridge;
-
-import java.util.List;
-
-/**
- * 다리의 길이를 입력 받아서 다리를 생성해주는 역할을 한다.
- */
-public class BridgeMaker {
-
- private final BridgeNumberGenerator bridgeNumberGenerator;
-
- public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) {
- this.bridgeNumberGenerator = bridgeNumberGenerator;
- }
-
- /**
- * @param size 다리의 길이
- * @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다.
- */
- public List makeBridge(int size) {
- return null;
- }
-}
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/constant/BridgeWindow.java b/src/main/java/bridge/constant/BridgeWindow.java
new file mode 100644
index 00000000000..aaa577b7648
--- /dev/null
+++ b/src/main/java/bridge/constant/BridgeWindow.java
@@ -0,0 +1,22 @@
+package bridge.constant;
+
+public enum BridgeWindow {
+
+ START("[ "),
+ SUCCESS("O"),
+ FAIL("X"),
+ DIVISION(" | "),
+ BLANK(" "),
+ END(" ]");
+
+ private final String value;
+
+ BridgeWindow(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+}
diff --git a/src/main/java/bridge/constant/ErrorMessage.java b/src/main/java/bridge/constant/ErrorMessage.java
new file mode 100644
index 00000000000..3765f61e23a
--- /dev/null
+++ b/src/main/java/bridge/constant/ErrorMessage.java
@@ -0,0 +1,19 @@
+package bridge.constant;
+
+public enum ErrorMessage {
+ NOT_RANGE("다리의 길이는 %d~%d 사이의 숫자입니다."),
+ NOT_MOVE_STAGE("이동할 칸은 '%s' 혹은 '%s' 입니다."),
+ NOT_RETRY_COMMAND("게임을 다시 시도할 명령어는 '%s' 혹은 '%s' 입니다."),
+ NOT_NUMBER_STRING("숫자로된 문자열이 아닙니다."),
+ NOT_UPPER_ENGLISH("대문자 영어가 아닙니다.");
+
+ private final String message;
+
+ ErrorMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return "[ERROR]" + message;
+ }
+}
diff --git a/src/main/java/bridge/constant/GameCommand.java b/src/main/java/bridge/constant/GameCommand.java
new file mode 100644
index 00000000000..2faaf7f64dc
--- /dev/null
+++ b/src/main/java/bridge/constant/GameCommand.java
@@ -0,0 +1,31 @@
+package bridge.constant;
+
+public enum GameCommand {
+
+ UP_BRIDGE("U"),
+ DOWN_BRIDGE("D"),
+ RETRY("R"),
+ QUIT("Q");
+
+ private final String command;
+
+ GameCommand(String command) {
+ this.command = command;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public static String bridgeStageCase(int num){
+ if (num == 0){
+ return GameCommand.DOWN_BRIDGE.getCommand();
+ }
+ return GameCommand.UP_BRIDGE.getCommand();
+ }
+
+ @Override
+ public String toString() {
+ return command;
+ }
+}
diff --git a/src/main/java/bridge/constant/GameProgressMessage.java b/src/main/java/bridge/constant/GameProgressMessage.java
new file mode 100644
index 00000000000..586d1c7ceaa
--- /dev/null
+++ b/src/main/java/bridge/constant/GameProgressMessage.java
@@ -0,0 +1,25 @@
+package bridge.constant;
+
+public enum GameProgressMessage {
+
+ START_BRIDGE_GAME("다리 건너기 게임을 시작합니다.\n"),
+ INPUT_BRIDGE_LENGTH("다리의 길이를 입력해주세요."),
+ INPUT_MOVE_STAIR("이동할 칸을 선택해주세요. (위: %s, 아래: %s)"),
+ INPUT_RETRY_GAME("게임을 다시 시도할지 여부를 입력해주세요. (재시도: %s, 종료: %s)"),
+ BLANK(" ");
+
+ private final String message;
+
+ GameProgressMessage(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public String toString() {
+ return message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/bridge/controller/BridgeGameController.java b/src/main/java/bridge/controller/BridgeGameController.java
new file mode 100644
index 00000000000..b8da754b975
--- /dev/null
+++ b/src/main/java/bridge/controller/BridgeGameController.java
@@ -0,0 +1,28 @@
+package bridge.controller;
+
+import bridge.model.BridgeGame;
+import bridge.constant.GameProgressMessage;
+
+public class BridgeGameController {
+
+ private final InputManager inputManager;
+ private final GameResultManager gameResultManager;
+ public BridgeGameController() {
+ this.inputManager = new InputManager();
+ this.gameResultManager = new GameResultManager();
+ }
+
+ public void gameStart(){
+ System.out.println(GameProgressMessage.START_BRIDGE_GAME);
+ BridgeGame bridgeGame = new BridgeGame(inputManager.makeBridge());
+ boolean isProceed = true;
+ while (isProceed) {
+ gameResultManager.upAttemptCount();
+ if (inputManager.isSuccessMoveStage(bridgeGame,gameResultManager)){
+ break;
+ }
+ isProceed = inputManager.isRetryGame(bridgeGame, gameResultManager);
+ }
+ }
+
+}
diff --git a/src/main/java/bridge/controller/GameResultManager.java b/src/main/java/bridge/controller/GameResultManager.java
new file mode 100644
index 00000000000..d1a5afa0fb2
--- /dev/null
+++ b/src/main/java/bridge/controller/GameResultManager.java
@@ -0,0 +1,26 @@
+package bridge.controller;
+
+public class GameResultManager {
+
+ private static final String GAME_SUCCESS = "성공";
+ private static final String GAME_FAIL = "실패";
+ private int attemptCount = 0;
+ private String gameClear = GAME_SUCCESS;
+
+
+ public void upAttemptCount(){
+ attemptCount++;
+ }
+
+ public void gameClearFail(){
+ gameClear = GAME_FAIL;
+ }
+
+ public String getGameClear() {
+ return gameClear;
+ }
+
+ public int getAttemptCount() {
+ return attemptCount;
+ }
+}
diff --git a/src/main/java/bridge/controller/InputManager.java b/src/main/java/bridge/controller/InputManager.java
new file mode 100644
index 00000000000..33a6a8ba21d
--- /dev/null
+++ b/src/main/java/bridge/controller/InputManager.java
@@ -0,0 +1,55 @@
+package bridge.controller;
+
+import bridge.*;
+import bridge.io.view.InputView;
+import bridge.io.view.OutputView;
+import bridge.model.BridgeGame;
+import bridge.model.BridgeMaker;
+import bridge.model.BridgeStatus;
+
+import java.util.List;
+
+public class InputManager {
+
+ private final InputView inputView;
+ private final BridgeMaker bridgeMaker;
+ private final OutputView outputView;
+
+ public InputManager() {
+ this.inputView = new InputView();
+ this.bridgeMaker = new BridgeMaker(new BridgeRandomNumberGenerator());
+ this.outputView = new OutputView();
+ }
+
+ public List makeBridge(){
+ int bridgeSize = inputView.readBridgeSize();
+ return bridgeMaker.makeBridge(bridgeSize);
+ }
+
+ public boolean isSuccessMoveStage(BridgeGame bridgeGame, GameResultManager gameResultManager) {
+ BridgeStatus bridgeStatus = null;
+ for (int stage = 0; stage < bridgeGame.stages(); stage++){
+ String bridgeMoveStep = inputView.readMoving();
+ bridgeStatus = bridgeGame.move(bridgeMoveStep, stage);
+ outputView.printMap(bridgeStatus);
+ if (!bridgeStatus.isNextStage()){
+ return false;
+ }
+ }
+ outputView.printResult(bridgeStatus, gameResultManager);
+ return true;
+
+ }
+
+ public boolean isRetryGame(BridgeGame bridgeGame, GameResultManager gameResultManager){
+ String command = inputView.readGameCommand();
+ BridgeStatus bridgeStatus = bridgeGame.retry(command);
+ if(bridgeStatus.isNextStage()){
+ return true;
+ }
+ gameResultManager.gameClearFail();
+ outputView.printResult(bridgeStatus ,gameResultManager);
+ return false;
+ }
+
+}
diff --git a/src/main/java/bridge/io/InputValidator.java b/src/main/java/bridge/io/InputValidator.java
new file mode 100644
index 00000000000..e6e6a621e1b
--- /dev/null
+++ b/src/main/java/bridge/io/InputValidator.java
@@ -0,0 +1,58 @@
+package bridge.io;
+
+import bridge.constant.ErrorMessage;
+import bridge.constant.GameCommand;
+
+import java.util.Map;
+
+public class InputValidator {
+
+ private static final int MIN_SIZE = 3;
+ private static final int MAX_SIZE = 20;
+
+ public void validateBridgeSize(String bridgeSize){
+ validateInputNumber(bridgeSize);
+ int bridgeLength = Integer.parseInt(bridgeSize);
+ if(bridgeLength < MIN_SIZE || bridgeLength > MAX_SIZE){
+ throw new IllegalArgumentException(String.format(ErrorMessage.NOT_RANGE.getMessage(), MIN_SIZE, MAX_SIZE));
+ }
+ }
+ public void validateBridgeMoveStep(String bridgeMoveStep) {
+ validateInputUpperEnglish(bridgeMoveStep);
+ if (!bridgeMoveStep.equals(GameCommand.UP_BRIDGE.getCommand()) &&
+ !bridgeMoveStep.equals(GameCommand.DOWN_BRIDGE.getCommand())){
+ throw new IllegalArgumentException(String.format(ErrorMessage.NOT_MOVE_STAGE.getMessage()
+ , GameCommand.DOWN_BRIDGE, GameCommand.UP_BRIDGE));
+ }
+ }
+
+ public void validateBridgeStartCommand(String gameCommand) {
+ validateInputUpperEnglish(gameCommand);
+ if (!gameCommand.equals(GameCommand.RETRY.getCommand()) &&
+ !gameCommand.equals(GameCommand.QUIT.getCommand())){
+ throw new IllegalArgumentException(String.format(ErrorMessage.NOT_RETRY_COMMAND.getMessage(),
+ GameCommand.RETRY, GameCommand.QUIT));
+ }
+ }
+
+ private void validateInputNumber(String number) {
+ if (!isNumeric(number)) {
+ throw new IllegalArgumentException(ErrorMessage.NOT_NUMBER_STRING.getMessage());
+ }
+ }
+
+ private boolean isNumeric(String str) {
+ return str.matches("^[0-9]*$"); // 정규표현식을 사용하여 숫자 여부를 확인
+ }
+
+ private void validateInputUpperEnglish(String upperEnglish){
+ if(!isUpperEnglish(upperEnglish)){
+ throw new IllegalArgumentException(ErrorMessage.NOT_UPPER_ENGLISH.getMessage());
+ }
+ }
+
+ private boolean isUpperEnglish(String str) {
+ return str.matches("^[A-Z]*$");
+ }
+
+}
diff --git a/src/main/java/bridge/io/view/InputView.java b/src/main/java/bridge/io/view/InputView.java
new file mode 100644
index 00000000000..b5ef0164a88
--- /dev/null
+++ b/src/main/java/bridge/io/view/InputView.java
@@ -0,0 +1,84 @@
+package bridge.io.view;
+
+import bridge.constant.GameCommand;
+import bridge.constant.GameProgressMessage;
+import bridge.io.InputValidator;
+import camp.nextstep.edu.missionutils.Console;
+
+public class InputView {
+
+ private final InputValidator inputValidator = new InputValidator();
+
+ /**
+ * 다리의 길이를 입력받는다.
+ */
+ public int readBridgeSize() {
+ String input;
+ do {
+ input = inputBridgeSize();
+ } while (input == null);
+
+ return Integer.parseInt(input);
+ }
+
+ private String inputBridgeSize() {
+ System.out.println(GameProgressMessage.INPUT_BRIDGE_LENGTH);
+ String bridgeSize = Console.readLine();
+ System.out.println(GameProgressMessage.BLANK);
+ try {
+ inputValidator.validateBridgeSize(bridgeSize);
+ } catch (IllegalArgumentException e){
+ System.out.println(e.getMessage());
+ return null;
+ }
+ return bridgeSize;
+ }
+
+ /**
+ * 사용자가 이동할 칸을 입력받는다.
+ */
+ public String readMoving(){
+ String input;
+ do{
+ input = inputBridgeMoveStep();
+ }while (input == null);
+ return input;
+ }
+
+ private String inputBridgeMoveStep() {
+ System.out.println(String.format(GameProgressMessage.INPUT_MOVE_STAIR.toString()
+ , GameCommand.UP_BRIDGE, GameCommand.DOWN_BRIDGE));
+ String bridgeMoveStep = Console.readLine();
+ try {
+ inputValidator.validateBridgeMoveStep(bridgeMoveStep);
+ } catch (IllegalArgumentException e){
+ System.out.println(e.getMessage());
+ return null;
+ }
+ return bridgeMoveStep;
+ }
+
+ /**
+ * 사용자가 게임을 다시 시도할지 종료할지 여부를 입력받는다.
+ */
+ public String readGameCommand(){
+ String input;
+ do{
+ input = inputRetryCommand();
+ }while (input == null);
+ return input;
+ }
+
+ private String inputRetryCommand() {
+ System.out.println(String.format(GameProgressMessage.INPUT_RETRY_GAME.toString()
+ , GameCommand.RETRY, GameCommand.QUIT));
+ String retryCommand = Console.readLine();
+ try {
+ inputValidator.validateBridgeStartCommand(retryCommand);
+ } catch (IllegalArgumentException e){
+ System.out.println(e.getMessage());
+ return null;
+ }
+ return retryCommand;
+ }
+}
diff --git a/src/main/java/bridge/io/view/OutputView.java b/src/main/java/bridge/io/view/OutputView.java
new file mode 100644
index 00000000000..5a58c826630
--- /dev/null
+++ b/src/main/java/bridge/io/view/OutputView.java
@@ -0,0 +1,35 @@
+package bridge.io.view;
+
+import bridge.controller.GameResultManager;
+import bridge.model.BridgeStatus;
+
+/**
+ * 사용자에게 게임 진행 상황과 결과를 출력하는 역할을 한다.
+ */
+public class OutputView {
+
+ private static final String RESULT_GAME_MESSAGE = "최종 게임 결과";
+ private static final String WHETHER_GAME_SUCCESS = "게임 성공 여부: ";
+ private static final String ALL_ATTEMPT_COUNT = "총 시도한 횟수: ";
+
+ /**
+ * 현재까지 이동한 다리의 상태를 정해진 형식에 맞춰 출력한다.
+ *
+ * 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
+ */
+ public void printMap(BridgeStatus bridgeStatus) {
+ System.out.println(bridgeStatus);
+ }
+
+ /**
+ * 게임의 최종 결과를 정해진 형식에 맞춰 출력한다.
+ *
+ * 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
+ */
+ public void printResult(BridgeStatus bridgeStatus ,GameResultManager gameResult) {
+ System.out.println(RESULT_GAME_MESSAGE);
+ printMap(bridgeStatus);
+ System.out.println(WHETHER_GAME_SUCCESS + gameResult.getGameClear());
+ System.out.println(ALL_ATTEMPT_COUNT + gameResult.getAttemptCount());
+ }
+}
diff --git a/src/main/java/bridge/model/BridgeGame.java b/src/main/java/bridge/model/BridgeGame.java
new file mode 100644
index 00000000000..151341fadc6
--- /dev/null
+++ b/src/main/java/bridge/model/BridgeGame.java
@@ -0,0 +1,58 @@
+package bridge.model;
+
+import bridge.constant.GameCommand;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 다리 건너기 게임을 관리하는 클래스
+ */
+public class BridgeGame {
+
+ private final List bridge;
+ private final BridgeStatus bridgeStatus;
+
+ public BridgeGame(List bridge) {
+ this.bridge = new ArrayList<>(bridge);
+ this.bridgeStatus = new BridgeStatus();
+ }
+
+ /**
+ * 사용자가 칸을 이동할 때 사용하는 메서드
+ *
+ * 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
+ */
+ public BridgeStatus move(String bridgeMoveStep, int stage) {
+ if(isMoveBridge(bridgeMoveStep, stage)){
+ bridgeStatus.successStair(bridgeMoveStep);
+ return bridgeStatus;
+ }
+ bridgeStatus.failStair(bridgeMoveStep);
+ return bridgeStatus;
+ }
+
+ public boolean isMoveBridge(String bridgeMoveStep, int stage){
+ String floor = bridge.get(stage);
+ return floor.equals(bridgeMoveStep);
+ }
+
+ /**
+ * 사용자가 게임을 다시 시도할 때 사용하는 메서드
+ *
+ * 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
+ */
+ public BridgeStatus retry(String command) {
+ if(command.equals(GameCommand.RETRY.getCommand())){
+ bridgeStatus.reset();
+ return bridgeStatus;
+ }
+ bridgeStatus.stopNextStage();
+ return bridgeStatus;
+ }
+
+ public int stages(){
+ return bridge.size();
+ }
+
+}
diff --git a/src/main/java/bridge/model/BridgeMaker.java b/src/main/java/bridge/model/BridgeMaker.java
new file mode 100644
index 00000000000..0626514d579
--- /dev/null
+++ b/src/main/java/bridge/model/BridgeMaker.java
@@ -0,0 +1,39 @@
+package bridge.model;
+
+import bridge.BridgeNumberGenerator;
+import bridge.constant.GameCommand;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * 다리의 길이를 입력 받아서 다리를 생성해주는 역할을 한다.
+ */
+public class BridgeMaker {
+ private static final int BRIDGE_UPPER_CASE = 1;
+ private static final int BRIDGE_LOWER_CASE = 0;
+ private final BridgeNumberGenerator bridgeNumberGenerator;
+
+ public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) {
+ this.bridgeNumberGenerator = bridgeNumberGenerator;
+ }
+
+ /**
+ * @param size 다리의 길이
+ * @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다.
+ */
+ public List makeBridge(int size) {
+ return IntStream.range(0, size)
+ .map(i -> bridgeNumberGenerator.generate())
+ .filter(this::validateRandomNumber)
+ .boxed()
+ .map(GameCommand::bridgeStageCase)
+ .collect(Collectors.toList());
+ }
+
+ private boolean validateRandomNumber(int number){
+ return number == BRIDGE_UPPER_CASE || number == BRIDGE_LOWER_CASE;
+ }
+
+}
diff --git a/src/main/java/bridge/model/BridgeStatus.java b/src/main/java/bridge/model/BridgeStatus.java
new file mode 100644
index 00000000000..cb835d55e91
--- /dev/null
+++ b/src/main/java/bridge/model/BridgeStatus.java
@@ -0,0 +1,89 @@
+package bridge.model;
+
+import bridge.constant.BridgeWindow;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BridgeStatus {
+
+ private static final int INIT_SIZE = 2;
+ private static final int LENGTH_ONE = 1;
+
+ private final List upStair = new ArrayList<>();
+ private final List downStair = new ArrayList<>();
+ private boolean isNextStage;
+
+ public BridgeStatus(){
+ init();
+ }
+
+ private void init(){
+ upStair.add(BridgeWindow.START);
+ upStair.add(BridgeWindow.END);
+ downStair.add(BridgeWindow.START);
+ downStair.add(BridgeWindow.END);
+ isNextStage = true;
+ }
+
+ public void reset() {
+ upStair.clear();
+ downStair.clear();
+ init();
+ }
+
+ public void successStair(String bridgeMoveStep) {
+ inputDivision();
+ if (bridgeMoveStep.equals("U")){
+ upStair.add(getInsertMiddle(upStair), BridgeWindow.SUCCESS);
+ downStair.add(getInsertMiddle(downStair), BridgeWindow.BLANK);
+ return;
+ }
+ upStair.add(getInsertMiddle(upStair), BridgeWindow.BLANK);
+ downStair.add(getInsertMiddle(downStair) , BridgeWindow.SUCCESS);
+ }
+
+ public void failStair(String bridgeMoveStep) {
+ inputDivision();
+ if (bridgeMoveStep.equals("U")){
+ upStair.add(getInsertMiddle(upStair), BridgeWindow.FAIL);
+ downStair.add(getInsertMiddle(downStair), BridgeWindow.BLANK);
+ stopNextStage();
+ return;
+ }
+ upStair.add(getInsertMiddle(upStair), BridgeWindow.BLANK);
+ downStair.add(getInsertMiddle(downStair), BridgeWindow.FAIL);
+ stopNextStage();
+ }
+
+ private void inputDivision(){
+ if (upStair.size() == INIT_SIZE){
+ return;
+ }
+ upStair.add(getInsertMiddle(upStair), BridgeWindow.DIVISION);
+ downStair.add(getInsertMiddle(downStair), BridgeWindow.DIVISION);
+ }
+
+ private int getInsertMiddle(List stair){
+ return stair.size() - LENGTH_ONE;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ upStair.forEach(stringBuilder::append);
+ stringBuilder.append("\n");
+ downStair.forEach(stringBuilder::append);
+ stringBuilder.append("\n");
+ return stringBuilder.toString();
+ }
+
+ public boolean isNextStage() {
+ return isNextStage;
+ }
+
+ public void stopNextStage() {
+ isNextStage = false;
+ }
+}
+
diff --git a/src/test/java/bridge/ApplicationTest.java b/src/test/java/bridge/ApplicationTest.java
index 1a163ec0a2a..51b833ff850 100644
--- a/src/test/java/bridge/ApplicationTest.java
+++ b/src/test/java/bridge/ApplicationTest.java
@@ -5,6 +5,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.util.Lists.newArrayList;
+import bridge.model.BridgeMaker;
import camp.nextstep.edu.missionutils.test.NsTest;
import java.util.List;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/bridge/BridgeTest.java b/src/test/java/bridge/BridgeTest.java
new file mode 100644
index 00000000000..45c53deeb5d
--- /dev/null
+++ b/src/test/java/bridge/BridgeTest.java
@@ -0,0 +1,69 @@
+package bridge;
+
+import bridge.model.BridgeGame;
+import bridge.model.BridgeMaker;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.List;
+
+class BridgeTest {
+
+
+ @Test
+ @DisplayName("다리가 'U', 'D' 형태로 랜덤 생성 되었는지 확인한다.")
+ void makeBridge(){
+ //given
+ BridgeRandomNumberGenerator bridgeRandomNumberGenerator = new BridgeRandomNumberGenerator();
+ BridgeMaker bridgeMaker = new BridgeMaker(bridgeRandomNumberGenerator);
+
+ //when
+ List bridge = bridgeMaker.makeBridge(3);
+
+ //then
+ assertThat(bridge).contains("U", "D");
+ }
+
+ @Test
+ @DisplayName("이동할 수 있는 칸을 입력했을 때 True를 반환한다.")
+ void moveBridge(){
+ // given
+ BridgeGame bridgeGame = new BridgeGame(List.of("U", "D", "D"));
+
+ //when
+ boolean isMove = bridgeGame.move("D", 1);
+
+ //then
+ assertThat(isMove).isTrue();
+ }
+
+ @Test
+ @DisplayName("생성된 Bridge가 BridgeGame에 저장이 되는지 확인한다.")
+ void saveBridgeInBridgeGame(){
+ //given
+ BridgeGame bridgeGame = new BridgeGame(List.of("U", "D", "D"));
+
+ //when
+ int countStage = bridgeGame.stages();
+
+ //then
+ assertThat(countStage).isEqualTo(3);
+ }
+
+ @Test
+ @DisplayName("재시작/종료 명령어가 R이면 true, Q이면 false를 반환한다.")
+ void retryCommandIsR(){
+ //given
+ BridgeGame bridgeGame = new BridgeGame(List.of("U", "D", "D"));
+
+ //when
+ boolean isRetryTrue = bridgeGame.retry("R");
+ boolean isRetryFalse = bridgeGame.retry("Q");
+
+ //then
+ assertThat(isRetryTrue).isTrue();
+ assertThat(isRetryFalse).isFalse();
+
+ }
+}
diff --git a/src/test/java/bridge/GameResultTest.java b/src/test/java/bridge/GameResultTest.java
new file mode 100644
index 00000000000..5d6695c03a9
--- /dev/null
+++ b/src/test/java/bridge/GameResultTest.java
@@ -0,0 +1,106 @@
+package bridge;
+
+import bridge.controller.GameResultManager;
+import bridge.model.BridgeStatus;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.*;
+
+class GameResultTest {
+
+ GameResultManager gameResult = new GameResultManager();
+ BridgeStatus bridgeStatus = new BridgeStatus();
+
+ @Test
+ @DisplayName("게임 시도 횟수가 하나씩 증가하는지 확인한다.")
+ void upAttemptCount(){
+ // given
+ gameResult.upAttemptCount();
+
+ //when
+ int attemptCount = gameResult.getAttemptCount();
+
+ //then
+ assertThat(attemptCount).isEqualTo(1);
+ }
+
+ @Test
+ @DisplayName("윗 칸을 누르고 실패했을 때 게임 화면이 만들어지는지 확인한다.")
+ void upStairFail(){
+ //given
+ bridgeStatus.failStair("U");
+
+ //when
+ String upStairSuccess = bridgeStatus.toString();
+
+ //then
+ assertThat(upStairSuccess).contains("[ X ]\n[ ]\n");
+
+ }
+
+ @Test
+ @DisplayName("아랫 칸을 누르고 실패했을 때 게임 화면이 만들어지는지 확인한다.")
+ void downStairFail(){
+ //given
+ bridgeStatus.failStair("D");
+
+ //when
+ String upStairSuccess = bridgeStatus.toString();
+
+ //then
+ assertThat(upStairSuccess).contains("[ ]\n[ X ]\n");
+
+ }
+
+ @Test
+ @DisplayName("윗 칸을 누르고 성공했을 때 게임 화면이 만들어지는지 확인한다.")
+ void upStairSuccess(){
+ //given
+ bridgeStatus.successStair("U");
+
+ //when
+ String upStairSuccess = bridgeStatus.toString();
+
+ //then
+ assertThat(upStairSuccess).contains("[ O ]\n[ ]\n");
+
+ }
+
+ @Test
+ @DisplayName("아랫 칸을 누르고 성공했을 때 게임 화면이 만들어지는지 확인한다.")
+ void downStairSuccess(){
+ //given
+ bridgeStatus.successStair("D");
+
+ //when
+ String upStairSuccess = bridgeStatus.toString();
+
+ //then
+ assertThat(upStairSuccess).contains("[ ]\n[ O ]\n");
+
+ }
+
+ @Test
+ @DisplayName("게임 클리어 실패시 '성공'에서 '실패'로 변하는지 확인한다.")
+ void gameClearFail(){
+ assertThat(gameResult.getGameClear()).isEqualTo("성공");
+
+ gameResult.gameClearFail();
+
+ assertThat(gameResult.getGameClear()).isEqualTo("실패");
+ }
+
+ @Test
+ @DisplayName("게임 재시작 시 다리는 초기화된다.")
+ void gameReset(){
+ gameResult.restartGameSet();
+
+ BridgeStatus bridgeStatus = gameResult.getBridgeStatus();
+
+ assertThat(bridgeStatus.toString()).isEqualTo("[ ]\n[ ]\n");
+ }
+
+
+
+}
diff --git a/src/test/java/bridge/InputValidateTest.java b/src/test/java/bridge/InputValidateTest.java
new file mode 100644
index 00000000000..ab13af7392d
--- /dev/null
+++ b/src/test/java/bridge/InputValidateTest.java
@@ -0,0 +1,56 @@
+package bridge;
+
+import bridge.io.InputValidator;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class InputValidateTest {
+
+ InputValidator inputValidator = new InputValidator();
+
+
+ @Test
+ @DisplayName("다리의 길이가 3~20사이의 숫자가 아니면 에러가 발생한다.")
+ void validateBridgeSize(){
+ assertThatThrownBy(() -> inputValidator.validateBridgeSize("1"))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ @DisplayName("다리의 길이가 숫자가 아니면 에러가 발생한다.")
+ void validateBridgeIsNumber(){
+ assertThatThrownBy(() -> inputValidator.validateBridgeSize("ㅁ"))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ @DisplayName("이동할 칸이 'U' 또는 'D'가 아니면 에러가 발생한다.")
+ void validateBridgeMoveStep(){
+ assertThatThrownBy(() -> inputValidator.validateBridgeMoveStep("Z"))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ @DisplayName("게임을 다시 시도 입력이 'R', 'Q'가 아니면 에러가 발생한다.")
+ void validateBridgeRestartCommand(){
+ assertThatThrownBy(() -> inputValidator.validateBridgeStartCommand("Z"))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ @DisplayName("이동할 칸이 대문자 영어가 아니면 에러가 발생한다.")
+ void validateBridgeMoveStepIsUpperEnglish(){
+ assertThatThrownBy(() -> inputValidator.validateBridgeMoveStep("u"))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ @DisplayName("게임을 다시 시도 입력이 대문자 영어가 아니면 에러가 발생한다.")
+ void validateBridgeRestartCommandIsUpperEnglish(){
+ assertThatThrownBy(() -> inputValidator.validateBridgeStartCommand("r"))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+}