diff --git a/docs/README.md b/docs/README.md index e69de29bb2d..3ee733ded75 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,58 @@ +## 다리 건너기 기능 구현 목록 + +**입력 & 출력** +- [x] 게임 시작 안내 출력 +- [x] 다리 길이 입력 (3 ~ 20사이 숫자) +- [x] 이동할 칸 입력 (U / D) +- [x] 재시도 입력 (R / Q) + +**다리 생성** +- 입력한 다리 길이만큼 다리 생성 + - [x] 0(아래), 1(위)형식으로 다리 요소 랜덤 생성 + - [x] 0➝D / 1➝U 변환 + +**게임 진행** +- [x] 생성된 다리와 사용자의 입력 비교 + - [x] 이동 가능한 경우 계속 진행 + - [x] 이동 불가능한 경우 재시작 / 종료 +- [x] 재시작하는 경우 시도 횟수 증가 +현 +**게임 진행 내용 저장** +- [x] 사용자의 입력이 맞는지 저장한다 + - 맞을 경우 O, 틀릴 경우 X 저장 + +**결과 출력** +- 다리 출력 + - [x] `[`로 시작, `]`로 끝, 구분자는 `|` + - [x] 이동 가능한 경우 O / 불가능한 경우 X 출력 +- [x] 게임 성공 여부 +- [x] 총 시도 횟수 + +**예외 처리** +- 이동할 칸 입력 + - [x] U/D 이외의 입력이 들어올 경우 +- 재시도 입력 + - [x] R/Q 이외의 입력이 들어올 경우 +- 다리 길이 입력 + - [x] 숫자가 아닌 입력이 들어온 경우 + - [x] 범위 이외의 숫자가 들어온 경우 + +### 테스트 목록 + +**BridgeMaker** +- [x] 다리 길이 입력시 길이에 맞는 다리를 생성하는가 + +**InputValidator** +- [x] 움직임 입력 시 U/D 이외의 문자를 입력하면 예외가 발생하는가 +- [x] 재시작 입력 시 R/Q 이외의 문자를 입력하면 예외가 발생하는가 +- [x] 다리 길이를 입력할 경우 문자를 입력하면 예외가 발생하는가 +- [x] 다리 길이를 입력할 경우 3~20 이외의 숫자를 입력하면 예외가 발생하는가 + +**LocationConverter** +- [x] 0 = D, 1 = U로 변환을 하는가 + +**BridgeGame** +- [x] 게임 진행 시 사용자가 입력한 내용과 다리의 내용이 일치하는지 확인 +- [x] 정답 계산 이후 사용자 위치 움직였는지 확인 +- [x] 게임 재시작 시 재시작 카운트 증가 +- [x] 게임 재시작 시 사용자 위치, 다리 건넌 기록 초기화 \ No newline at end of file diff --git a/src/main/java/bridge/Application.java b/src/main/java/bridge/Application.java index 5cb72dfd3de..fe1a8545862 100644 --- a/src/main/java/bridge/Application.java +++ b/src/main/java/bridge/Application.java @@ -1,8 +1,13 @@ package bridge; +import bridge.controller.BridgeController; +import bridge.view.InputView; +import bridge.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + BridgeController bridgeController = new BridgeController(new OutputView(), new InputView()); + bridgeController.start(); } } 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..567561e7403 100644 --- a/src/main/java/bridge/BridgeMaker.java +++ b/src/main/java/bridge/BridgeMaker.java @@ -1,5 +1,8 @@ package bridge; +import bridge.model.BridgeConverter; + +import java.util.ArrayList; import java.util.List; /** @@ -18,6 +21,10 @@ public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) { * @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다. */ public List makeBridge(int size) { - return null; + List bridge = new ArrayList<>(); + while (bridge.size() != size) { + bridge.add(BridgeConverter.convert(bridgeNumberGenerator.generate())); + } + 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/BridgeController.java b/src/main/java/bridge/controller/BridgeController.java new file mode 100644 index 00000000000..b6d1f3b5e59 --- /dev/null +++ b/src/main/java/bridge/controller/BridgeController.java @@ -0,0 +1,65 @@ +package bridge.controller; + +import bridge.BridgeMaker; +import bridge.BridgeRandomNumberGenerator; +import bridge.model.AnswerTable; +import bridge.model.Bridge; +import bridge.model.Constant; +import bridge.model.GameStatus; +import bridge.service.BridgeGame; +import bridge.view.InputView; +import bridge.view.OutputView; + +public class BridgeController { + + private final OutputView outputView; + private final InputView inputView; + private final AnswerTable answerTable = new AnswerTable(); + private final GameStatus gameStatus = new GameStatus(); + + public BridgeController(OutputView outputView, InputView inputView) { + this.outputView = outputView; + this.inputView = inputView; + } + + public void start() { + try { + Bridge bridge = makeBridge(); + progressGame(bridge); + printResult(); + } catch (IllegalArgumentException exception) { + outputView.printExceptionMessage(exception.getMessage()); + } + } + + private Bridge makeBridge() { + outputView.printGameStart(); + BridgeMaker bridgeMaker = new BridgeMaker(new BridgeRandomNumberGenerator()); + return new Bridge(bridgeMaker.makeBridge(inputView.readBridgeSize())); + } + + private void progressGame(Bridge bridge) { + BridgeGame bridgeGame = new BridgeGame(bridge, gameStatus, answerTable); + while (gameStatus.getPosition() < bridge.size()) { + boolean isCorrect = bridgeGame.move(inputView.readMoving()); + outputView.printMap(answerTable); + if(!isCorrect && !retry(bridgeGame)) { + gameStatus.setGameResultFailed(); + break; + } + } + } + + private boolean retry(BridgeGame bridgeGame) { + String userInput = inputView.readGameCommand(); + if (userInput.equals(Constant.RETRY.toString())) { + bridgeGame.retry(); + return true; + } + return false; + } + + private void printResult() { + outputView.printResult(answerTable, gameStatus); + } +} diff --git a/src/main/java/bridge/model/AnswerTable.java b/src/main/java/bridge/model/AnswerTable.java new file mode 100644 index 00000000000..ec2d1d5adb5 --- /dev/null +++ b/src/main/java/bridge/model/AnswerTable.java @@ -0,0 +1,59 @@ +package bridge.model; + +import bridge.view.ExceptionMessage; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AnswerTable { + + private final List upper; + private final List lower; + + public AnswerTable() { + this.upper = new ArrayList<>(); + this.lower = new ArrayList<>(); + } + + public void setAnswerTable(String userInput, boolean success) { + setUpperCase(userInput, success); + setLowerCase(userInput, success); + } + + private void setUpperCase(String userInput, boolean success) { + if(userInput.equals(Constant.UPPER_CHAR.toString())) { + MovingResult correctMessage = calculateMovingResult(success); + upper.add(correctMessage.getAnswer()); + lower.add(Constant.BLANK.toString()); + } + } + + private void setLowerCase(String userInput, boolean success) { + if(userInput.equals(Constant.LOWER_CHAR.toString())) { + MovingResult correctMessage = calculateMovingResult(success); + lower.add(correctMessage.getAnswer()); + upper.add(Constant.BLANK.toString()); + } + } + + private MovingResult calculateMovingResult(boolean success) { + return Arrays.stream(MovingResult.values()) + .filter(movingResult -> movingResult.getSuccess() == success) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(ExceptionMessage.MOVING_RESULT_NONE.toString())); + } + + public List getUpper() { + return upper; + } + + public List getLower() { + return lower; + } + + public void clear() { + upper.clear(); + lower.clear(); + } +} diff --git a/src/main/java/bridge/model/Bridge.java b/src/main/java/bridge/model/Bridge.java new file mode 100644 index 00000000000..5e26e3f3685 --- /dev/null +++ b/src/main/java/bridge/model/Bridge.java @@ -0,0 +1,20 @@ +package bridge.model; + +import java.util.List; + +public class Bridge { + + private final List bridge; + + public Bridge(List bridge) { + this.bridge = bridge; + } + + public String getBridgeByPosition(int position) { + return bridge.get(position); + } + + public int size() { + return bridge.size(); + } +} diff --git a/src/main/java/bridge/model/BridgeComparison.java b/src/main/java/bridge/model/BridgeComparison.java new file mode 100644 index 00000000000..a4a3b738404 --- /dev/null +++ b/src/main/java/bridge/model/BridgeComparison.java @@ -0,0 +1,15 @@ +package bridge.model; + +public class BridgeComparison { + + private final Bridge bridge; + + public BridgeComparison(Bridge bridge) { + this.bridge = bridge; + } + + public boolean checkMoving(String moving, int position) { + String correctMoving = bridge.getBridgeByPosition(position); + return correctMoving.equals(moving); + } +} diff --git a/src/main/java/bridge/model/BridgeConverter.java b/src/main/java/bridge/model/BridgeConverter.java new file mode 100644 index 00000000000..43d39cd7f34 --- /dev/null +++ b/src/main/java/bridge/model/BridgeConverter.java @@ -0,0 +1,13 @@ +package bridge.model; + +public class BridgeConverter { + + private static final int LOWER_CASE = 0; + + public static String convert(int position) { + if (position == LOWER_CASE) { + return Constant.LOWER_CHAR.toString(); + } + return Constant.UPPER_CHAR.toString(); + } +} diff --git a/src/main/java/bridge/model/Constant.java b/src/main/java/bridge/model/Constant.java new file mode 100644 index 00000000000..f8ebfeed86b --- /dev/null +++ b/src/main/java/bridge/model/Constant.java @@ -0,0 +1,21 @@ +package bridge.model; + +public enum Constant { + + UPPER_CHAR("U"), + LOWER_CHAR("D"), + BLANK(" "), + RETRY("R"), + QUIT("Q"); + + private final String constant; + + Constant(String constant) { + this.constant = constant; + } + + @Override + public String toString() { + return this.constant; + } +} diff --git a/src/main/java/bridge/model/GameStatus.java b/src/main/java/bridge/model/GameStatus.java new file mode 100644 index 00000000000..cf24fc9c392 --- /dev/null +++ b/src/main/java/bridge/model/GameStatus.java @@ -0,0 +1,55 @@ +package bridge.model; + +public class GameStatus { + + private int position; + private int retry; + private GameResult gameResult; + private static final int START_POSITION = 0; + private static final int INIT_RETRY_COUNT = 1; + + private enum GameResult { + GAME_SUCCESS("성공"), + GAME_FAILED("실패"); + + private final String result; + + GameResult(String result) { + this.result = result; + } + } + + public GameStatus() { + this.position = START_POSITION; + this.retry = INIT_RETRY_COUNT; + this.gameResult = GameResult.GAME_SUCCESS; + } + + public void movePosition() { + position++; + } + + public void increaseRetryCount() { + retry++; + } + + public int getRetry() { + return retry; + } + + public int getPosition() { + return position; + } + + public void clear() { + position = START_POSITION; + } + + public void setGameResultFailed() { + gameResult = GameResult.GAME_FAILED; + } + + public String getGameResult() { + return gameResult.result; + } +} diff --git a/src/main/java/bridge/model/MovingResult.java b/src/main/java/bridge/model/MovingResult.java new file mode 100644 index 00000000000..7f327224a3a --- /dev/null +++ b/src/main/java/bridge/model/MovingResult.java @@ -0,0 +1,23 @@ +package bridge.model; + +public enum MovingResult { + + MOVING_SUCCESS("O", true), + MOVING_FAILED("X", false); + + private final String answer; + private final boolean success; + + MovingResult(String answer, boolean success) { + this.answer = answer; + this.success = success; + } + + public String getAnswer() { + return answer; + } + + public boolean getSuccess() { + return success; + } +} diff --git a/src/main/java/bridge/service/BridgeGame.java b/src/main/java/bridge/service/BridgeGame.java new file mode 100644 index 00000000000..a6ac36f2d86 --- /dev/null +++ b/src/main/java/bridge/service/BridgeGame.java @@ -0,0 +1,46 @@ +package bridge.service; + +import bridge.model.AnswerTable; +import bridge.model.Bridge; +import bridge.model.BridgeComparison; +import bridge.model.GameStatus; + +/** + * 다리 건너기 게임을 관리하는 클래스 + */ +public class BridgeGame { + + private final GameStatus gameStatus; + private final AnswerTable answerTable; + private final BridgeComparison bridgeComparison; + + public BridgeGame(Bridge bridge, GameStatus gameStatus, AnswerTable answerTable) { + this.gameStatus = gameStatus; + this.answerTable = answerTable; + this.bridgeComparison = new BridgeComparison(bridge); + } + + /** + * 사용자가 칸을 이동할 때 사용하는 메서드 + *

+ * 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. + */ + public boolean move(String moving) { + int position = gameStatus.getPosition(); + boolean isCorrect = bridgeComparison.checkMoving(moving, position); + answerTable.setAnswerTable(moving, isCorrect); + gameStatus.movePosition(); + return isCorrect; + } + + /** + * 사용자가 게임을 다시 시도할 때 사용하는 메서드 + *

+ * 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. + */ + public void retry() { + gameStatus.increaseRetryCount(); + gameStatus.clear(); + answerTable.clear(); + } +} diff --git a/src/main/java/bridge/view/BridgeElement.java b/src/main/java/bridge/view/BridgeElement.java new file mode 100644 index 00000000000..b63d7df7a16 --- /dev/null +++ b/src/main/java/bridge/view/BridgeElement.java @@ -0,0 +1,19 @@ +package bridge.view; + +public enum BridgeElement { + BRIDGE_START("[ "), + BRIDGE_END(" ]"), + BRIDGE_DELIMITER(" | "); + + + private final String element; + + BridgeElement(String element) { + this.element = element; + } + + @Override + public String toString() { + return this.element; + } +} diff --git a/src/main/java/bridge/view/ExceptionMessage.java b/src/main/java/bridge/view/ExceptionMessage.java new file mode 100644 index 00000000000..41ee20a3208 --- /dev/null +++ b/src/main/java/bridge/view/ExceptionMessage.java @@ -0,0 +1,21 @@ +package bridge.view; + +public enum ExceptionMessage { + + INCORRECT_MOVING("U 또는 D로만 이동할 수 있습니다."), + INCORRECT_RETRY("R 또는 Q만 입력할 수 있습니다."), + NOT_INTEGER("숫자만 입력할 수 있습니다."), + INCORRECT_RANGE("다리 길이는 3부터 20 사이의 숫자여야 합니다."), + MOVING_RESULT_NONE("해당 방향으로 움직일 수 없습니다.."); + + private static final String prefix = "[ERROR] "; + private final String message; + ExceptionMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return prefix + message; + } +} diff --git a/src/main/java/bridge/view/InputValidator.java b/src/main/java/bridge/view/InputValidator.java new file mode 100644 index 00000000000..2d85c9d4902 --- /dev/null +++ b/src/main/java/bridge/view/InputValidator.java @@ -0,0 +1,51 @@ +package bridge.view; + +import bridge.model.Constant; + +import java.util.regex.Pattern; + +public class InputValidator { + + private static final String NUMBER_REGEXP = "^\\d*$"; + private static final int MAX_BRIDGE_LENGTH = 20; + private static final int MIN_BRIDGE_LENGTH = 1; + + public void validateMoving(String input) { + validateIsMovingInput(input); + } + + private void validateIsMovingInput(String input) { + if (!(input.equals(Constant.LOWER_CHAR.toString()) || input.equals(Constant.UPPER_CHAR.toString()))) { + throw new IllegalArgumentException(ExceptionMessage.INCORRECT_MOVING.toString()); + } + } + + public void validateRetry(String input) { + validateIsRetryInput(input); + } + + private void validateIsRetryInput(String input) { + if (!(input.equals(Constant.RETRY.toString()) || input.equals(Constant.QUIT.toString()))) { + throw new IllegalArgumentException(ExceptionMessage.INCORRECT_RETRY.toString()); + } + } + + public void validateBridgeLength(String input) { + validateIsNumber(input); + validateIsCorrectRange(Integer.parseInt(input)); + } + + private void validateIsNumber(String input) { + if (!Pattern.matches(NUMBER_REGEXP, input)) { + ExceptionMessage exceptionMessage = ExceptionMessage.NOT_INTEGER; + throw new IllegalArgumentException(exceptionMessage.toString()); + } + } + + private void validateIsCorrectRange(int input) { + if (!(MIN_BRIDGE_LENGTH <= input && input <= MAX_BRIDGE_LENGTH)) { + ExceptionMessage exceptionMessage = ExceptionMessage.INCORRECT_RANGE; + throw new IllegalArgumentException(exceptionMessage.toString()); + } + } +} diff --git a/src/main/java/bridge/view/InputView.java b/src/main/java/bridge/view/InputView.java new file mode 100644 index 00000000000..fae5d17cb46 --- /dev/null +++ b/src/main/java/bridge/view/InputView.java @@ -0,0 +1,43 @@ +package bridge.view; + +import camp.nextstep.edu.missionutils.Console; + +/** + * 사용자로부터 입력을 받는 역할을 한다. + */ +public class InputView { + + private final OutputView outputView = new OutputView(); + private final InputValidator inputValidator = new InputValidator(); + + /** + * 다리의 길이를 입력받는다. + */ + public int readBridgeSize() { + outputView.printLengthMessage(); + String userInput = Console.readLine(); + inputValidator.validateBridgeLength(userInput); + outputView.printNewLine(); + return Integer.parseInt(userInput); + } + + /** + * 사용자가 이동할 칸을 입력받는다. + */ + public String readMoving() { + outputView.printMoveLocation(); + String input = Console.readLine(); + inputValidator.validateMoving(input); + return input; + } + + /** + * 사용자가 게임을 다시 시도할지 종료할지 여부를 입력받는다. + */ + public String readGameCommand() { + outputView.printRetryMessage(); + String input = Console.readLine(); + inputValidator.validateRetry(input); + return input; + } +} diff --git a/src/main/java/bridge/view/OutputMessage.java b/src/main/java/bridge/view/OutputMessage.java new file mode 100644 index 00000000000..d641dd579c8 --- /dev/null +++ b/src/main/java/bridge/view/OutputMessage.java @@ -0,0 +1,23 @@ +package bridge.view; + +public enum OutputMessage { + + START_MESSAGE("다리 건너기 게임을 시작합니다."), + LENGTH_MESSAGE("다리의 길이를 입력해주세요."), + MOVE_LOCATION("이동할 칸을 선택해주세요. (위: U, 아래: D)"), + RETRY_MESSAGE("게임을 다시 시도할지 여부를 입력해주세요. (재시도: R, 종료: Q)"), + GAME_END_MESSAGE("최종 게임 결과"), + GAME_STATUS("게임 성공 여부: %s\n"), + RETRY_NUMBERS("총 시도한 횟수: %d\n"); + + private final String message; + + OutputMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return this.message; + } +} \ No newline at end of file diff --git a/src/main/java/bridge/view/OutputView.java b/src/main/java/bridge/view/OutputView.java new file mode 100644 index 00000000000..1c251845a73 --- /dev/null +++ b/src/main/java/bridge/view/OutputView.java @@ -0,0 +1,68 @@ +package bridge.view; + +import bridge.model.AnswerTable; +import bridge.model.GameStatus; + +import java.util.List; +import java.util.StringJoiner; + +/** + * 사용자에게 게임 진행 상황과 결과를 출력하는 역할을 한다. + */ +public class OutputView { + + private static final String NEW_LINE = ""; + + /** + * 현재까지 이동한 다리의 상태를 정해진 형식에 맞춰 출력한다. + *

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

+ * 출력을 위해 필요한 메서드의 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다. + */ + public void printResult(AnswerTable answerTable, GameStatus gameStatus) { + System.out.println(OutputMessage.GAME_END_MESSAGE); + printMap(answerTable); + System.out.printf((OutputMessage.GAME_STATUS.toString()), gameStatus.getGameResult()); + System.out.printf(OutputMessage.RETRY_NUMBERS.toString(), gameStatus.getRetry()); + } + + public void printGameStart() { + System.out.println(OutputMessage.START_MESSAGE); + printNewLine(); + } + + public void printLengthMessage() { + System.out.println(OutputMessage.LENGTH_MESSAGE); + } + public void printMoveLocation() { + System.out.println(OutputMessage.MOVE_LOCATION); + } + + public void printRetryMessage() { + System.out.println(OutputMessage.RETRY_MESSAGE); + } + + public void printBridge(List bridge) { + StringJoiner stringJoiner = new StringJoiner(BridgeElement.BRIDGE_DELIMITER.toString(), BridgeElement.BRIDGE_START.toString(), BridgeElement.BRIDGE_END.toString()); + bridge.forEach(stringJoiner::add); + System.out.println(stringJoiner); + } + + public void printNewLine() { + System.out.println(NEW_LINE); + } + + public void printExceptionMessage(String message) { + System.out.println(message); + } +} diff --git a/src/test/java/bridge/ApplicationTest.java b/src/test/java/bridge/ApplicationTest.java index 1a163ec0a2a..5b965cd93b6 100644 --- a/src/test/java/bridge/ApplicationTest.java +++ b/src/test/java/bridge/ApplicationTest.java @@ -39,6 +39,25 @@ class ApplicationTest extends NsTest { }, 1, 0, 1); } + @Test + void 재시작_테스트() { + assertRandomNumberInRangeTest(() -> { + run("3", "U", "D", "D", "R", "U", "D", "U"); + assertThat(output()).contains( + "게임을 다시 시도할지 여부를 입력해주세요. (재시도: R, 종료: Q)", + "최종 게임 결과", + "[ O | | O ]", + "[ | O | ]", + "게임 성공 여부: 성공", + "총 시도한 횟수: 2" + ); + + int upSideIndex = output().indexOf("[ O | | O ]"); + int downSideIndex = output().indexOf("[ | O | ]"); + assertThat(upSideIndex).isLessThan(downSideIndex); + }, 1, 0, 1); + } + @Test void 예외_테스트() { assertSimpleTest(() -> { diff --git a/src/test/java/bridge/InputValidatorTest.java b/src/test/java/bridge/InputValidatorTest.java new file mode 100644 index 00000000000..1931ba69347 --- /dev/null +++ b/src/test/java/bridge/InputValidatorTest.java @@ -0,0 +1,45 @@ +package bridge; + +import bridge.view.InputValidator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class InputValidatorTest { + + InputValidator inputValidator = new InputValidator(); + String ERROR_MESSAGE = "[ERROR]"; + + @DisplayName("위치 이동 시 U, D 이외 입력이 들어올 경우 예외 발생") + @Test + void movingInputException() { + assertThatThrownBy(() -> inputValidator.validateMoving("K")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } + + @DisplayName("재시작 시 R, Q 이외 입력이 들어올 경우 예외 발생") + @Test + void retryInputException() { + assertThatThrownBy(() -> inputValidator.validateRetry("K")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } + + @DisplayName("다리 길이 입력 시 숫자 외 문자 입력이 들어올 경우 예외 발생") + @Test + void bridgeLengthException() { + assertThatThrownBy(() -> inputValidator.validateBridgeLength("K")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } + + @DisplayName("다리 길이기 3~20 이외의 숫자일 경우 예외 발생") + @Test + void bridgeLengthRangeException() { + assertThatThrownBy(() -> inputValidator.validateBridgeLength("21")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR_MESSAGE); + } +} diff --git a/src/test/java/bridge/model/BridgeConverterTest.java b/src/test/java/bridge/model/BridgeConverterTest.java new file mode 100644 index 00000000000..56eddd62c51 --- /dev/null +++ b/src/test/java/bridge/model/BridgeConverterTest.java @@ -0,0 +1,26 @@ +package bridge.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +import static org.assertj.core.api.Assertions.assertThat; + +public class BridgeConverterTest { + + @DisplayName("0을 입력할 경우 D로 변환한다.") + @Test + void convertToD() { + int bridge = 0; + String convertContent = BridgeConverter.convert(bridge); + assertThat(convertContent).isEqualTo("D"); + } + + @DisplayName("1을 입력할 경우 U로 변환한다.") + @Test + void convertToU() { + int bridge = 1; + String convertContent = BridgeConverter.convert(bridge); + assertThat(convertContent).isEqualTo("U"); + } +} diff --git a/src/test/java/bridge/model/BridgeGameTest.java b/src/test/java/bridge/model/BridgeGameTest.java new file mode 100644 index 00000000000..3d2bd01b6dd --- /dev/null +++ b/src/test/java/bridge/model/BridgeGameTest.java @@ -0,0 +1,82 @@ +package bridge.model; + +import bridge.BridgeNumberGenerator; +import bridge.service.BridgeGame; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.util.Lists.newArrayList; + +public class BridgeGameTest { + + Bridge bridge; + GameStatus gameStatus; + AnswerTable answerTable; + BridgeGame bridgeGame; + + @BeforeEach + void setUpGame() { + BridgeNumberGenerator numberGenerator = new BridgeGameTest.TestNumberGenerator(newArrayList(1, 0, 0)); + bridge.BridgeMaker bridgeMaker = new bridge.BridgeMaker(numberGenerator); + + bridge = new Bridge(bridgeMaker.makeBridge(3)); + gameStatus = new GameStatus(); + answerTable = new AnswerTable(); + bridgeGame = new BridgeGame(bridge, gameStatus, answerTable); + } + + @DisplayName("게임 진행 시 사용자가 입력한 내용과 다리의 내용이 일치하는지 확인") + @Test + void comparisonMoving() { + String moving = "U"; + + boolean movingResult = bridgeGame.move(moving); + + assertThat(movingResult).isTrue(); + } + + @DisplayName("정답 계산 이후 사용자 위치 움직였는지 확인") + @Test + void comparisonPosition() { + String moving = "U"; + bridgeGame.move(moving); + + assertThat(gameStatus.getPosition()).isEqualTo(1); + } + + @DisplayName("게임 재시작 시 재시작 카운트 증가 확인") + @Test + void checkRetryCount() { + bridgeGame.retry(); + + assertThat(gameStatus.getRetry()).isEqualTo(2); + } + + @DisplayName("게임 재시작 시 이전 게임 진행 내용 초기화") + @Test + void checkClear() { + bridgeGame.retry(); + + assertThat(answerTable.getLower().size()).isEqualTo(0); + assertThat(answerTable.getUpper().size()).isEqualTo(0); + assertThat(gameStatus.getPosition()).isEqualTo(0); + } + + static class TestNumberGenerator implements BridgeNumberGenerator { + + private final List numbers; + + TestNumberGenerator(List numbers) { + this.numbers = numbers; + } + + @Override + public int generate() { + return numbers.remove(0); + } + } +} diff --git a/src/test/java/bridge/model/BridgeMakerTest.java b/src/test/java/bridge/model/BridgeMakerTest.java new file mode 100644 index 00000000000..47ca82e76d6 --- /dev/null +++ b/src/test/java/bridge/model/BridgeMakerTest.java @@ -0,0 +1,38 @@ +package bridge.model; + +import bridge.BridgeNumberGenerator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.util.Lists.newArrayList; + +public class BridgeMakerTest { + + @DisplayName("다리 길이 입력시 길이에 맞는 다리를 생성하는가") + @Test + void makeBridge() { + BridgeNumberGenerator numberGenerator = new BridgeMakerTest.TestNumberGenerator(newArrayList(1, 0, 0)); + bridge.BridgeMaker bridgeMaker = new bridge.BridgeMaker(numberGenerator); + + List bridge = bridgeMaker.makeBridge(3); + + assertThat(bridge).hasSize(3); + } + + static class TestNumberGenerator implements BridgeNumberGenerator { + + private final List numbers; + + TestNumberGenerator(List numbers) { + this.numbers = numbers; + } + + @Override + public int generate() { + return numbers.remove(0); + } + } +}