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 문 닫는 } 라인 등을 제 하고나니 많이 부족한 감이 있었지만, 라인을 줄이려는 과정에서 효율적으로 간결하게 코드를 짜는 방법을 좀 더 생각 해 볼 수 있어서 좋았습니다.
'Coding Test' 카테고리의 다른 글
2022 우테코 3rd Week java-Lotto 리뷰 (0) | 2022.11.11 |
---|---|
2022 우테코 onboarding 7번 리뷰 (0) | 2022.11.09 |
2022 우테코 onboarding 5~6번 리뷰 (0) | 2022.11.09 |
2022 우테코 onboarding 3~4번 리뷰 (0) | 2022.11.08 |
2022 우테코 onboarding 1~2번 리뷰 (0) | 2022.11.08 |