GitHub

https://github.com/Backcoder-June

BackCoder 기록 그리고 숙달

Coding Test

2022 우테코 4th Week java-bridge 리뷰

Backcoder 2022. 11. 18. 21:03

2022 우테코에서 진행하는 공개된 사전과제 코딩테스트 문제 입니다.

 

https://github.com/Backcoder-June/java-bridge

 

GitHub - Backcoder-June/java-bridge

Contribute to Backcoder-June/java-bridge development by creating an account on GitHub.

github.com

( 상세한 문제 설명은 링크를 타고 들어가서 확인해주세요! )

 

Bridge Application

드라마 오징어게임에서 나왔던 다리건너기 게임처럼, 

2개의 다리가 있고, 한 칸씩 왼쪽, 오른쪽을 선택하며 다리를 건너는 게임을 Console 에서 구현해보는 과제였습니다. 

왼쪽, 오른쪽 중 건널 수 있는 다리는 랜덤으로 정해지게 되고, 사용자의 입력을 받아 다리를 건널 때 마다 해당 칸까지의 다리 모습을 콘솔에 출력합니다.

실패 시, 재시도/중단을 선택할 수 있으며 다리를 모두 통과하면 도전 횟수 및 다리 모양을 최종적으로 출력해줍니다. 

 

전체적으로 메소드 라인 제한이 10줄 이하여야 하고, 

미리 정해진 class 별로 각 역할에 맞게 구현해야 합니다. 

10줄 제한은 Java 코드 컨벤션 가이드를 준수하면서 구현하다보면 생각보다 매우 짧았습니다.

최소 단위로 기능들을 구현하고 활용하는 능력이 많이 중요시 된다는 메세지가 담겨있지 않나 합니다.

 

=> Class 별로 주어진 역할만 하도록 하고, 한 Class 에서 다른 Class 를 가져다 사용할 수 있는가 

=> 일정한 형태를 가지는 String 에서, index 규칙을 찾아 반복문 등에 활용할 수 있는가 

=> String에서 특정 index의 char 만을 바꿀 수 있는가 

=> 인터페이스와 클래스의 상속, 부모 자식 개념의 이해

 

 

1. BridgeMaker

- 다리를 만드는 클래스

- [ O | O | O ... | O ] 와 같은 형태가 두 line 으로 만들어져야 하므로, 각 Line을 String으로 List<String> 에 담는다. 

=> List<String> 에서 .get(0) 을 하면 위쪽 다리, .get(1) 은 아랫 쪽 다리를 의미한다. 

 

- makeBridge 는 랜덤으로 생성되는 0,1 숫자에 따라 대응되는 U, D 로 바꾸어 List 에 담아놓는다. ( 다리 답안지 ) 

- makeBridgeForm 은 전체적인 다리모양을 만들어 놓는다. 위는 U 로 채우고, 아래는 D로 채워두고 equals 를 사용한다.

- makeAnswerBridge 는 답안지를 가져다가, 만들어놓은 BridgeForm 을 '다리형태의 답안지' 로 만든다.

=> 이후, 이를 이용해 사용자의 입력값을 다리형태의 답안지와 비교하며 O, X 처리를 해준다. 

=> 답안지에 O,X 처리를 함과 동시에, 거기까지의 String만 substring 해서 출력해주었다. 

 

- String 에서 특정 index 의 char 을 특정 char로 바꾸는 것이 아직 익숙하지 않았다.

다른 방법이 있겠지만, 일단 아는 선에서 구현해보기 위해 

String => char[] 배열로 바꿔서 해당 index의 char 을 바꿔주고, 다시 char[] 배열을 String 으로 바꿔서 return 하는 메소드를 만들어 사용했다.

 - 회수 count에 따른 index 규칙 ( i *4 + 2 ) 를 사용한다. 

 

package bridge;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

    /**
     * @param size 다리의 길이
     * @return 입력받은 길이에 해당하는 다리 모양. 위 칸이면 "U", 아래 칸이면 "D"로 표현해야 한다.
     */
    public List<String> makeBridge(int size) {
        List<String> bridgeList = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            String randomBridge = String.valueOf(new BridgeMaker(bridgeNumberGenerator).bridgeNumberGenerator.generate());
            if (randomBridge.equals("1")) {
                bridgeList.add("U");
            }
            if (randomBridge.equals("0")) {
                bridgeList.add("D");
            }
        }
        return bridgeList;
    }

    public List<String> makeBridgeForm(int size) {
        String eachLine = "[ ";
        for (int i = 0; i < size-1; i++) {
            eachLine += "U | ";
        }
        eachLine += "U ]";
        List<String> bridge = new ArrayList<>();
        bridge.add(eachLine);
        bridge.add(eachLine.replace("U", "D"));
        return bridge;
    }


    public static Map<Integer, String> makeAnswerBridge(List<String> bridge, List<String> randomBridge) {
        Map<Integer, String> bridgeMap = convertListToMap(bridge);
        for (int i = 0; i < randomBridge.size(); i++) {
            if (randomBridge.get(i).equals("U")) {
                bridgeMap.put(1, indexBlankChanger(bridgeMap.get(1), i));
            }
            if (randomBridge.get(i).equals("D")) {
                bridgeMap.put(0, indexBlankChanger(bridgeMap.get(0), i));
            }
        }
        return bridgeMap;
    }

    public static String indexBlankChanger(String eachLine, int index) {
        char[] chars = eachLine.toCharArray();
        chars[index * 4 + 2] = ' ';
        return String.valueOf(chars);
    }

    public static String indexOXChanger(String eachLine, int index, String checker) {
        char[] chars = eachLine.toCharArray();
        if (checker.equals("O")) {
            chars[index * 4 + 2] = 'O';
        }
        if (checker.equals("X")) {
            chars[index * 4 + 2] = 'X';
        }
        return String.valueOf(chars);
    }

    public static Map<Integer, String> convertListToMap(List<String> bridge) {
        Map<Integer, String> bridgeMap = new HashMap<>();
        for (int i = 0; i < bridge.size(); i++) {
            bridgeMap.put(i, bridge.get(i));
        }
        return bridgeMap;
    }

}

 

 

 

2. BridgeGame

- 사용자가 다리를 건너는 move 를 했을 시, 실패했을 시, 재시도, break 등 게임 진행에 대한 부분을 만들었다.

- 다리 답안지와 사용자의 입력값을 비교하여 O, X 로 String 의 일부만을 바꿔주어야 하기 때문에

Map 을 이용해서, 조건에 해당 할 경우, 바꾼 String을 다시 put 해서 바꿔주는 방법을 이용했다. 

 

- move 에서는 O 표시만, failChecker 에서 X 표시를 해주었다. (10줄 제한)

- 다리건너기에 실패할 경우, retry 에서는 시도 횟수, updown 입력 횟수, OX로 변경된 map을 초기화해주었다.  

package bridge;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 다리 건너기 게임을 관리하는 클래스
 */
public class BridgeGame {
    Application api = new Application();

    /**
     * 사용자가 칸을 이동할 때 사용하는 메서드
     * <p>
     * 이동을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
     */
    public Map<Integer,String> move(Map<Integer, String> answerBridge, String updown, int count) {
        if (updown.equals(String.valueOf(answerBridge.get(0).charAt(count*4+2)))) {
            answerBridge.put(0, BridgeMaker.indexOXChanger(answerBridge.get(0), count, "O"));
        }
        if (updown.equals(String.valueOf(answerBridge.get(1).charAt(count*4+2)))) {
            answerBridge.put(1, BridgeMaker.indexOXChanger(answerBridge.get(1), count, "O"));
        }
        return answerBridge;
    }

    public Map<Integer, String> failChecker(Map<Integer, String> moveResult, String updown, int count) {
        if (moveResult.get(0).charAt(count * 4 + 2) != 'O' && moveResult.get(1).charAt(count * 4 + 2) != 'O') {
            moveResult = failBridgeMaker(moveResult, updown, count);
        }
        return moveResult;
    }

    public Map<Integer, String> failBridgeMaker(Map<Integer, String> moveResult, String updown, int count) {
        if (updown.equals("U")) {
            moveResult.put(0, BridgeMaker.indexOXChanger(moveResult.get(0), count, "X"));
            moveResult.put(1, BridgeMaker.indexBlankChanger(moveResult.get(1), count));
        }
        if (updown.equals("D")) {
            moveResult.put(1, BridgeMaker.indexOXChanger(moveResult.get(1), count, "X"));
            moveResult.put(0, BridgeMaker.indexBlankChanger(moveResult.get(0), count));
        }
        return moveResult;
    }

    public int breaker(List<String> bridgeList, int count, int size) {
        int breakChecker= 0;
        if (bridgeList.get(0).contains("X") || bridgeList.get(1).contains("X")) {
            breakChecker = 1;
        }
        if (count == size - 1 && !bridgeList.get(0).contains("X") && !bridgeList.get(1).contains("X")) {
            breakChecker = 2;
        }
        return breakChecker;
    }

    /**
     * 사용자가 게임을 다시 시도할 때 사용하는 메서드
     * <p>
     * 재시작을 위해 필요한 메서드의 반환 타입(return type), 인자(parameter)는 자유롭게 추가하거나 변경할 수 있다.
     */
    public Map<Integer,String> retry(String readRetry,List<String> bridge, List<String> randomBridge) {
        Map<Integer, String> answerBridge = new HashMap<>();
            api.tryCount++;
            api.updownCount = 0;
            answerBridge = BridgeMaker.makeAnswerBridge(bridge, randomBridge);
        return answerBridge;
    }


}

 

3. Application

- tryCount, updownCount 등은 다른 class 에서 그대로 공유해서 사용해야 하므로, public static 으로 사용했다.

- 다리를 건너면서, X 를 밟거나, 통과할 때까지 계속해서 반복실행되야 하므로 

while(true) 를 사용하고, 위의 멈추어여할 경우를 if - break 문을 줘서 구현했다.

package bridge;

import java.util.List;
import java.util.Map;

public class Application {
    public static int tryCount = 1;
    public static int updownCount = 0;

    public static void main(String[] args) {
        // TODO: 프로그램 구현
        try {
            OutputView outputView = new OutputView();
            InputView inputView = new InputView();
            BridgeMaker bridgeMaker = new BridgeMaker(new BridgeRandomNumberGenerator());
            BridgeGame bridgeGame = new BridgeGame();

            outputView.askBridgeSize();
            int size = inputView.readBridgeSize();
            List<String> bridge = bridgeMaker.makeBridgeForm(size);
            List<String> randomBridge = bridgeMaker.makeBridge(size);

            Map<Integer, String> answerBridge = bridgeMaker.makeAnswerBridge(bridge, randomBridge);


            while (true) {
                outputView.askUpDown();
                String updown = inputView.readMoving();
                Map<Integer, String> moveResult = bridgeGame.move(answerBridge, updown, updownCount);
                Map<Integer, String> moveFinalResult = bridgeGame.failChecker(moveResult, updown, updownCount);
                List<String> bridgeList = outputView.printMap(moveFinalResult, updownCount);
                if (bridgeGame.breaker(bridgeList, updownCount, size) == 1) {
                    outputView.askRetry();
                    String readRetry = inputView.readGameCommand();
                    if (readRetry.equals("R")) {
                        answerBridge = bridgeGame.retry(readRetry, bridge, randomBridge);
                        continue;
                    }
                    if (readRetry.equals("Q")) {
                        outputView.printResult(0, bridgeList);
                        break;
                    }
                }
                if (bridgeGame.breaker(bridgeList, updownCount, size) == 2) {
                    outputView.printResult(1, bridgeList);
                    break;
                }
                updownCount++;
            }
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }
}

 

 

처음 과제를 이해 할 때, U, D 로 표현된 다리를 생성하라는 문구를 U U U / D D D 로 꽉 채워진 다리모양을 만들고 시작하라는 의미로 이해한 부분이 있어서, ( 근데 이건 다시봐도 설명이 다소 부족하지 않나... )  답안지모양의 다리를 만드는데 한번 더 돌아간 듯 하지만, 학습의 과정이니 매칭연습 할겸 진행했습니다. 

 

전체적으로 클래스 별로 역할이 제한되어있어서 많이 왔다갔다 하면서 구현하게 끔 유도하고 있었고, 

이건 실제로 프로젝트를 할 때도, 필요한 역량이었어서 다시금 도움이 되었습니다. 

메소드 10 줄 제한은... 메소드 선언부, } 마침표 찍는 line, if 문 한 번 쓰면 if 문 닫는 } 라인 등을 제 하고나니 많이 부족한 감이 있었지만, 라인을 줄이려는 과정에서 효율적으로 간결하게 코드를 짜는 방법을 좀 더 생각 해 볼 수 있어서 좋았습니다.