Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[java-bridge] roki.kim(김경록) 미션 제출합니다. #1

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
83 changes: 83 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 다리 건너기 게임

이 프로젝트는 사용자가 랜덤하게 생성된 다리 위를 위(위: U) 또는 아래(아래: D)로 이동하며, 다리를 끝까지 건너는 게임입니다. 사용자는 실패 시 재시도를 통해 다시 도전할 수 있으며, 게임이 종료되면 결과를 확인할 수 있습니다.

## 기능 요구 사항 파악
* 건너갈 수 있는 다리의 경우의 수 = 2 (위(U), 아래(D))
* like 오징어 게임
* 다리의 길이를 입력 받음
* 3 이상 20 이하의 숫자를 입력
* 다리를 건널 수 있다면 O, 없다면 X로 표시
* 다리를 끝까지 건너면 게임이 종료
* 건너다 실패하면 재시작 or 종료
* 재시작 => 처음에 만든 다리 재사용
* 총 시도한 횟수 = 첫 시도를 포함해 게임을 종료할 때까지 시도한 횟수
* 사용자가 잘못된 값을 입력할 경우: 에러 핸들링(`IllegalArgumentException`)
* 에러 메시지를 출력 후 입력 재요청

## 구현할 기능 목록

### 1. model 패키지

#### 1.1. `BridgeMaker` 클래스
- [x] 다리의 길이를 입력받아 다리를 생성한다.
- [x] 다리의 각 칸은 무작위로 위(`U`) 또는 아래(`D`)로 설정된다.

#### 1.2. `BridgeRandomNumberGenerator` 클래스
- [x] 무작위로 0 또는 1을 반환하여 다리의 위(`U`) 또는 아래(`D`)를 결정한다.

#### 1.3. `BridgeGame` 클래스
- [x] 사용자가 선택한 방향으로 이동했을 때 성공 여부를 판별한다.
- [x] `move()` : 입력한 방향이 다리의 해당 위치와 일치하는지 확인한다.
- [x] 사용자가 게임을 다시 시도할 수 있도록 다리 상태를 초기화한다.
- [x] `retry()` : 사용자가 재시도를 선택할 경우 다리의 현재 상태를 초기화한다.

#### 1.4. `GameResult` 클래스
- [x] 게임의 최종 결과를 저장하고 반환한다.
- [x] `getGameCount()` : 게임 시도 횟수를 반환한다.
- [x] `isSuccess()` : 게임 성공 여부를 반환한다.
- [x] `getCurrBridge()` : 현재까지 이동한 다리 상태를 반환한다.

### 2. view 패키지

#### 2.1. `InputView` 클래스
- [x] 사용자 입력을 처리한다.
- [x] `readBridgeSize()` : 다리의 길이를 입력받는다.
- [x] 예외사항: 다리 길이가 숫자가 아닌 경우 ➔ `IllegalArgumentException`
- [x] `readMoving()` : 사용자가 이동할 칸(위: U, 아래: D)을 입력받는다.
- [x] 예외사항: 잘못된 입력(예: U 또는 D가 아닌 값)을 받으면 ➔ `IllegalArgumentException`
- [x] `readGameCommand()` : 게임 재시도 또는 종료 명령을 입력받는다.
- [x] 예외사항: 잘못된 입력(예: R 또는 Q가 아닌 값)을 받으면 ➔ `IllegalArgumentException`

#### 2.2. `OutputView` 클래스
- [x] 게임 진행 상황과 결과를 출력한다.
- [x] `printMap()` : 현재까지 이동한 다리의 상태를 출력한다.
- [x] `printResult()` : 게임의 최종 결과를 출력한다.
- [x] 게임 성공 여부와 총 시도 횟수를 출력한다.

### 3. 컨텍스트 패키지

#### 3.1. `GameContext` 클래스
- [x] 게임에 필요한 모든 객체들을 초기화하고 관리하는 역할을 한다.
- [x] `createBridgeGame()` : `InputView`, `OutputView`, `BridgeMaker`, `BridgeGame` 객체를 생성하여 반환한다.

## 개발 환경

- 언어: Java
- 빌드 도구: Gradle

## 프로젝트 구조

- `bridge/`: 게임의 주요 로직을 담당하는 클래스들이 위치합니다.
- `Application.java`: 게임의 전체 흐름을 관리하는 클래스
- `BridgeMaker.java`: 다리 생성 로직을 처리하는 클래스
- `BridgeGame.java`: 게임 로직을 담당하는 클래스
- `InputView.java`: 사용자 입력을 처리하는 클래스
- `OutputView.java`: 게임 진행 및 결과를 출력하는 클래스

## 실행 방법

1. 프로젝트를 빌드하고 실행합니다.
```bash
./gradlew build
./gradlew run
57 changes: 56 additions & 1 deletion src/main/java/bridge/Application.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,63 @@
package bridge;

import java.util.ArrayList;
import java.util.List;

import static bridge.GameContext.*;

public class Application {

private static List<String> finalResult;

public static void main(String[] args) {
// TODO: 프로그램 구현
GameContext context = createBridgeGame();

System.out.println("다리 건너기 게임을 시작합니다.\n");
System.out.println("다리의 길이를 입력해주세요.");
int bridgeSize = context.inputView.readBridgeSize();
finalResult = context.bridgeMaker.makeBridge(bridgeSize);

GameResult result = playGame(context, bridgeSize, finalResult);

context.outputView.printResult(result.getCurrBridge(), result.isSuccess(), result.getGameCount());
}

/**
* 다리 건너기 게임의 전체 진행을 담당
*/
private static GameResult playGame(GameContext context, int bridgeSize, List<String> finalResult) {
List<String> currBridge = new ArrayList<>();
int gameCount = 1;

for(int currPosition = 0; currPosition < bridgeSize; currPosition++) {
if (processMove(context, currBridge, currPosition)) continue;
if (!retryGame(context, currBridge)) {
return new GameResult(gameCount, false, currBridge);
}
currPosition = -1;
gameCount++;
}
return new GameResult(gameCount, true, currBridge);
}

/**
* 이동을 처리하는 함수
*/
private static boolean processMove(GameContext context, List<String> currBridge, int currPosition) {
System.out.println("이동할 칸을 선택해주세요. (위: U, 아래: D)");
String moving = context.inputView.readMoving();
currBridge.add(moving);
boolean success = context.bridgeGame.move(finalResult, currPosition, moving);
context.outputView.printMap(currBridge, success);
return success;
}

/**
* 게임 재시도를 처리하는 함수
*/
private static boolean retryGame(GameContext context, List<String> currBridge) {
System.out.println("게임을 다시 시도할지 여부를 입력해주세요. (재시도: R, 종료: Q)");
String gameCommand = context.inputView.readGameCommand();
return context.bridgeGame.retry(currBridge, gameCommand);
}
}
17 changes: 11 additions & 6 deletions src/main/java/bridge/BridgeGame.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package bridge;

import java.util.List;

/**
* 다리 건너기 게임을 관리하는 클래스
*/
public class BridgeGame {

/**
* 사용자가 칸을 이동할 때 사용하는 메서드
* <p>
* 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
public void move() {
public boolean move(List<String> finalResult, int currPosition, String moving) {
return finalResult.get(currPosition).equals(moving);
}

/**
* 사용자가 게임을 다시 시도할 때 사용하는 메서드
* <p>
* 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
*/
public void retry() {
public boolean retry(List<String> currBridge, String gameCommand) {
if (gameCommand.equals("Q")) {
return false;
}
currBridge.clear();
return true;
}

}
11 changes: 10 additions & 1 deletion src/main/java/bridge/BridgeMaker.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package bridge;

import java.util.ArrayList;
import java.util.List;

/**
Expand All @@ -18,6 +19,14 @@ public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) {
* @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다.
*/
public List<String> makeBridge(int size) {
return null;
List<String> result = new ArrayList<>();
for(int i = 0; i < size; i++) {
if(bridgeNumberGenerator.generate() == 0) {
result.add("D");
} else {
result.add("U");
}
}
return result;
}
}
31 changes: 31 additions & 0 deletions src/main/java/bridge/BridgeValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package bridge;

public class BridgeValidator {

/**
* 다리 길이가 3 이상 20 이하인지 검증
*/
public static void validateBridgeSize(int size) {
if (size < 3 || size > 20) {
throw new IllegalArgumentException("[ERROR] 다리 길이는 3부터 20 사이의 숫자여야 합니다.");
}
}

/**
* 이동할 칸이 U 또는 D인지 검증
*/
public static void validateMoving(String moving) {
if (!moving.equals("U") && !moving.equals("D")) {
throw new IllegalArgumentException("[ERROR] 이동할 칸은 U(위), D(아래) 중 하나여야 합니다.");
}
}

/**
* 게임 명령어가 R 또는 Q인지 검증
*/
public static void validateGameCommand(String command) {
if (!command.equals("R") && !command.equals("Q")) {
throw new IllegalArgumentException("[ERROR] 게임 명령어는 R(재시작), Q(종료) 중 하나여야 합니다.");
}
}
}
31 changes: 31 additions & 0 deletions src/main/java/bridge/GameContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package bridge;

/**
* 게임의 다양한 컴포넌트를 하나의 컨텍스트로 묶어서 사용하기 쉽게 관리하는 클래스
*/
public class GameContext {
InputView inputView;
OutputView outputView;
BridgeMaker bridgeMaker;
BridgeGame bridgeGame;

public GameContext (InputView inputView, OutputView outputView, BridgeMaker bridgeMaker, BridgeGame bridgeGame) {
this.inputView = inputView;
this.outputView = outputView;
this.bridgeMaker = bridgeMaker;
this.bridgeGame = bridgeGame;
}

/**
* 게임의 모든 컴포넌트를 초기화하고 GameContext 객체를 생성하는 팩토리 메서드
*/
public static GameContext createBridgeGame() {
InputView inputView = new InputView();
OutputView outputView = new OutputView();
BridgeRandomNumberGenerator numberGenerator = new BridgeRandomNumberGenerator();
BridgeMaker bridgeMaker = new BridgeMaker(numberGenerator);
BridgeGame bridgeGame = new BridgeGame();

return new GameContext(inputView, outputView, bridgeMaker, bridgeGame);
}
}
27 changes: 27 additions & 0 deletions src/main/java/bridge/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package bridge;

import java.util.List;

public class GameResult {
private final int gameCount;
private final boolean success;
private final List<String> currBridge;

public GameResult(int gameCount, boolean success, List<String> currBridge) {
this.gameCount = gameCount;
this.success = success;
this.currBridge = currBridge;
}

public int getGameCount() {
return gameCount;
}

public boolean isSuccess() {
return success;
}

public List<String> getCurrBridge() {
return currBridge;
}
}
37 changes: 34 additions & 3 deletions src/main/java/bridge/InputView.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package bridge;

import camp.nextstep.edu.missionutils.Console;

import static bridge.BridgeValidator.*;

/**
* 사용자로부터 입력을 받는 역할을 한다.
*/
Expand All @@ -9,20 +13,47 @@ public class InputView {
* 다리의 길이를 입력받는다.
*/
public int readBridgeSize() {
return 0;
while(true) {
try {
String bridgeSize = Console.readLine();
int size = Integer.parseInt(bridgeSize);
validateBridgeSize(size);
return size;
} catch (NumberFormatException e) {
System.out.println("[ERROR] 다리 길이는 숫자로 입력해야 합니다.");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}

/**
* 사용자가 이동할 칸을 입력받는다.
*/
public String readMoving() {
return null;
while (true) {
try {
String moving = Console.readLine();
validateMoving(moving);
return moving;
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}

/**
* 사용자가 게임을 다시 시도할지 종료할지 여부를 입력받는다.
*/
public String readGameCommand() {
return null;
while (true) {
try {
String gameCommand = Console.readLine();
validateGameCommand(gameCommand);
return gameCommand;
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
}
Loading