Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2fe0b12
docs: 기본 기능 정리
sjiwon Jul 29, 2023
00d98dc
move: InputView/OutputView 패키지 이동
sjiwon Jul 29, 2023
d1f4e39
feat: 다리 방향 정보에 대한 BridgeDirection 구현
sjiwon Jul 29, 2023
f69cb38
feat: BridgeMaker makeBridge 구현 및 테스트 코드 작성
sjiwon Jul 29, 2023
8514194
docs: README model 컴포넌트 작성
sjiwon Jul 29, 2023
c087c3a
move: BridgeDirection 패키지 이동
sjiwon Jul 29, 2023
b802c65
move: BridgeGame 패키지 이동
sjiwon Jul 29, 2023
f276f18
feat: Bridge 구현 및 테스트 코드 작성
sjiwon Jul 29, 2023
ecf6fe1
docs: `BridgeLine` docs 추가
sjiwon Jul 29, 2023
e3a41c9
feat: 각 라운드별 다리를 건넌 상태에 대한 GameRoundStatus
sjiwon Jul 29, 2023
e780eab
feat: BridgeLine 구현 및 테스트 코드 작성
sjiwon Jul 29, 2023
420d594
refactor: BridgeLine `getLine() -> toString() override`로 수정
sjiwon Jul 29, 2023
a9e171f
refactor: BridgeLineTest 각 라인 결과 생성 구조 수정
sjiwon Jul 29, 2023
de69041
feat: BridgeMap 구현 및 테스트 코드 작성
sjiwon Jul 29, 2023
3b4cece
refactor: 결과 String Format 초기 선언 제거
sjiwon Jul 29, 2023
d816401
refactor: GameRoundStatus Enum Constant 네이밍 수정
sjiwon Jul 29, 2023
426aea5
feat: 게임 상태 관리를 위한 GameStatus
sjiwon Jul 29, 2023
b4a263a
feat: 최종 게임 결과 출력을 위한 GameResultStatus
sjiwon Jul 29, 2023
f9a7110
feat: 게임 재시작/종료를 위한 GameProcessDecisionCommand
sjiwon Jul 29, 2023
3c68caf
feat: 게임 진행과 관련된 `BridgeMap, GameStatus, attemptCount`를 묶은 GameTracke…
sjiwon Jul 29, 2023
15a30d9
refactor: GameRoundStatus `NONE -> ROUND_NONE`
sjiwon Jul 29, 2023
9dbedde
test: BridgeFixture를 통한 중복 로직 제거
sjiwon Jul 29, 2023
fe0ff30
feat: BridgeGame move, retry 로직 구현 및 테스트 코드 작성
sjiwon Jul 29, 2023
d052cd0
feat: 사용자 Input에 대한 Validator 구현
sjiwon Jul 29, 2023
8fcc576
feat: InputView/OutputView 구현
sjiwon Jul 29, 2023
dd004ff
refactor: 삼항 연산자 제거
sjiwon Jul 29, 2023
6216c34
refactor: InputView/OutputView 메소드 static
sjiwon Jul 29, 2023
3db12e0
fix: BridgeDirection Up/Down 숫자 커맨드 오류 수정
sjiwon Jul 30, 2023
950b0f7
feat: 다리 건너기 게임을 진행하는 BridgeGame 컴포넌트 로직 구현
sjiwon Jul 30, 2023
778abbd
test: 다리 건너기 기능 테스트 케이스 추가
sjiwon Jul 30, 2023
f2f1a27
docs: README 수정
sjiwon Jul 30, 2023
cdeb018
refactor: 다리 길이에 대한 상수 분리 및 BridgeMaker validation 추가
sjiwon Jul 31, 2023
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
110 changes: 110 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
## Feature

> 입력 예외가 발생한 경우 `[Error] {message}` 형태로 출력 후 다시 입력 진행

- 다리의 길이를 `입력`받고 입력받은 Length(N)만큼 다리를 설치한다
- 다리의 길이가 `3..20`이 아닌 경우 예외 처리
- 각 길이 별 숫자가 0이면 위 칸(U), 1이면 아래 칸(D)을 건널 수 있다

- 플레이어는 이동할 칸을 입력한다
- U or D가 아닌 경우 예외 처리
- 이동을 하고 난 후 해당 칸을 건널 수 있으면 O, 없으면 X를 다리에 표시한다

- 플레이어는 다리를 완전히 건너면 게임을 클리어한다

- 플레이어는 다리 건너기에 실패하면 `재시작(R) / 종료(Q)`중에 하나를 선택할 수 있다
- 재시작(R) -> 처음 생성한 다리 그대로 다시 건너기
- 종료(Q) -> 게임 종료

- 플레이어는 게임을 클리어 했거나 건너기 실패 후 종료하면 게임 결과를 받을 수 있다
1. 사용자가 진행한 게임 결과
2. 게임 성공 여부 (성공 / 실패)
3. 총 시도 횟수 (다리 건넌 횟수)

<br>
<hr>

## Model

### `bridge/BridgeDirection`

- 다리 이동 방향과 관련된 Enum 컴포넌트

### `bridge/Bridge`

- Input Size 길이만큼 랜덤 생성된 Bridge 컴포넌트
- 사용자가 건너갈 Bridge의 정답 기준
- `BridgeMaker -> List<String>`을 기준으로 Bridge 생성

### `bridge/BridgeLine`

- 위 아래 각각의 Bridge를 나타내는 컴포넌트

### `bridge/BridgeMap`

- 사용자가 건너는 전체 다리 정보와 관련된 Map 컴포넌트
- `BridgeLine upLine, BridgeLine downLine`을 포함

### `game/GameStatus`

- 게임 상태와 관련된 Enum 컴포넌트
- `IN_PROGRESS` -> 게임 진행
- `GAME_CLEAR` -> 게임 성공
- `GAME_FAIL` -> 게임 실패

### `game/GameRoundStatus`

- 각 라운드별 다리 건넌 상태를 표현하기 위한 컴포넌트
- `ROUND_SUCCESS` -> 건너기 성공
- `ROUND_FAIL` -> 건너기 실패
- `ROUND_NONE` -> 건너지 않음

### `game/GameResultStatus`

- 게임 종료 후 출력 관련된 Enum 컴포넌트
- `CLEAR` -> 성공
- `FAIL` -> 실패

### `game/GameTracker`

- 게임 진행과 관련된 컴포넌트를 묶은 컴포넌트
- 다리 전체 맵
- 게임 현재 상태
- 총 시도 횟수

<br>
<hr>

## Utils

### `ExceptionConstants`

- 전역 예외 메시지 통합 컴포넌트

### `validator/Validator`

- 사용자 Input에 대한 기본 검증 컴포넌트

<br>
<hr>

## View

### `InputView`

- 사용자 Input을 받기 위한 컴포넌트

### `OutputView`

- 다리 건너기 게임 진행과 관련된 출력 컴포넌트

<br>
<hr>

## Controller

### `BridgeGame`

- 다리 건너기 게임을 관리하는 컴포넌트

<br>
5 changes: 3 additions & 2 deletions src/main/java/bridge/Application.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package bridge;

public class Application {
import bridge.controller.BridgeGame;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
new BridgeGame().run();
}
}
23 changes: 0 additions & 23 deletions src/main/java/bridge/BridgeGame.java

This file was deleted.

31 changes: 27 additions & 4 deletions src/main/java/bridge/BridgeMaker.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,46 @@
package bridge;

import bridge.model.bridge.BridgeDirection;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static bridge.utils.BridgeConstants.MAX_VALUE;
import static bridge.utils.BridgeConstants.MIN_VALUE;
import static bridge.utils.ExceptionConstants.BridgeMakerException.BRIDGE_SIZE_IS_OUT_OF_RANGE;

/**
* 다리의 길이를 입력 받아서 다리를 생성해주는 역할을 한다.
*/
public class BridgeMaker {

private final BridgeNumberGenerator bridgeNumberGenerator;

public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) {
public BridgeMaker(final BridgeNumberGenerator bridgeNumberGenerator) {
this.bridgeNumberGenerator = bridgeNumberGenerator;
}

/**
* @param size 다리의 길이
* @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다.
*/
public List<String> makeBridge(int size) {
return null;
public List<String> makeBridge(final int size) {
validateBridgeSize(size);

return IntStream.generate(bridgeNumberGenerator::generate)
.limit(size)
.mapToObj(BridgeDirection::fromNumber)
.map(BridgeDirection::getCommand)
.collect(Collectors.toList());
}

private void validateBridgeSize(final int size) {
if (isOutOfRange(size)) {
throw new IllegalArgumentException(BRIDGE_SIZE_IS_OUT_OF_RANGE.message);
}
}

private boolean isOutOfRange(final int size) {
return size < MIN_VALUE || size > MAX_VALUE;
}
}
1 change: 0 additions & 1 deletion src/main/java/bridge/BridgeNumberGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@

@FunctionalInterface
public interface BridgeNumberGenerator {

int generate();
}
28 changes: 0 additions & 28 deletions src/main/java/bridge/InputView.java

This file was deleted.

23 changes: 0 additions & 23 deletions src/main/java/bridge/OutputView.java

This file was deleted.

129 changes: 129 additions & 0 deletions src/main/java/bridge/controller/BridgeGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package bridge.controller;

import bridge.BridgeMaker;
import bridge.BridgeRandomNumberGenerator;
import bridge.model.bridge.Bridge;
import bridge.model.bridge.BridgeDirection;
import bridge.model.game.GameProcessDecisionCommand;
import bridge.model.game.GameRoundStatus;
import bridge.model.game.GameTracker;
import bridge.view.InputView;
import bridge.view.OutputView;

import java.util.List;

import static bridge.model.game.GameRoundStatus.ROUND_FAIL;
import static bridge.model.game.GameRoundStatus.ROUND_SUCCESS;
import static bridge.model.game.GameStatus.GAME_CLEAR;
import static bridge.model.game.GameStatus.GAME_FAIL;

/**
* 다리 건너기 게임을 관리하는 클래스
*/
public class BridgeGame {
private Bridge bridge;
private GameTracker gameTracker;

public void run() {
try {
initializeGame();
startGame();
printGameResult();
} catch (final IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
}
}

private void initializeGame() {
OutputView.printStartGame();
initializeBridge();
initalizeGameTracker();
}

private void initializeBridge() {
final BridgeMaker bridgeMaker = new BridgeMaker(new BridgeRandomNumberGenerator());
final List<String> bridgeDirections = bridgeMaker.makeBridge(InputView.readBridgeSize());
bridge = new Bridge(bridgeDirections);
}

private void initalizeGameTracker() {
gameTracker = new GameTracker();
}

private void startGame() {
while (gameTracker.isGameInProgress()) {
processEachRound();
handleGameProcess();
}
}

private void processEachRound() {
final int currentOrder = gameTracker.getCurrentOrder();
final GameRoundStatus roundStatus = moveEachRound(currentOrder);

if (roundStatus.isRoundFail()) {
gameTracker.updateGameStatus(GAME_FAIL);
return;
}

if (roundStatus.isRoundSuccess() && bridge.isEndOfBridge(currentOrder)) {
gameTracker.updateGameStatus(GAME_CLEAR);
}
}

private GameRoundStatus moveEachRound(final int currentOrder) {
final BridgeDirection bridgeDirection = bridge.getBridgeDirectionByIndex(currentOrder);
final BridgeDirection playerMoveCommand = BridgeDirection.fromCommand(InputView.readMoving());
return move(bridgeDirection, playerMoveCommand);
}

/**
* 사용자가 칸을 이동할 때 사용하는 메서드
* <p>
* 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
private GameRoundStatus move(
final BridgeDirection bridgeDirection,
final BridgeDirection playerDirection
) {
final GameRoundStatus roundStatus = judgeRoundByDirection(bridgeDirection, playerDirection);
gameTracker.updateMap(playerDirection, roundStatus);
return roundStatus;
}

private GameRoundStatus judgeRoundByDirection(
final BridgeDirection bridgeDirection,
final BridgeDirection playerDirection
) {
if (bridgeDirection == playerDirection) {
return ROUND_SUCCESS;
}
return ROUND_FAIL;
}

private void handleGameProcess() {
if (gameTracker.isGameFail()) {
handleRetryProcess();
}
}

private void handleRetryProcess() {
final GameProcessDecisionCommand decisionCommand = GameProcessDecisionCommand.from(InputView.readGameCommand());
if (decisionCommand.isRetryDecision()) {
retry();
}
}

/**
* 사용자가 게임을 다시 시도할 때 사용하는 메서드
* <p>
* 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
private void retry() {
gameTracker.retryGame();
}

private void printGameResult() {
OutputView.printResult(gameTracker);
}
}
Loading