[SWEA] - 상호의 배틀필드 / 1873번 (Java)

[SWEA] - 상호의 배틀필드 / 1873번 (Java)

1. 문제 접근

초기 게임 맵이 주어지고, 전차의 움직임을 통해 이후 변화된 게임 맵의 상태를 출력하면 된다.

 

주어진 명령어 배열`char[] command`를 통해 각각의 명령에 대해 주어진 맵을 수정하면 되는 문제였다.

 

초기 맵 초기화를 하고 전차의 상태를 함께 초기화한다.

 

for (int row = 0; row < H; ++row) {
    for (int col = 0; col < W; ++col) {
        if (map[row][col] == '>') {
            // dir = 3: 우
            tankStatus = new Tank(row, col, 3);
        } else if (map[row][col] == '<') {
            // dir = 2: 좌
            tankStatus = new Tank(row, col, 2);
        } else if (map[row][col] == '^') {
            // dir = 0: 상
            tankStatus = new Tank(row, col, 0);
        } else if (map[row][col] == 'v') {
            // dir = 1: 하
            tankStatus = new Tank(row, col, 1);
        }
    }
}

 

이 때, 전차의 포가 바라보고 있는 방향은 `int`형을 통해 델타배열과 함께 사용하기 쉽도록 설정하였다.

 

상, 하, 좌, 우 순서대로 0, 1, 2, 3이며 이는 델타배열의 인덱스로 사용하여 Shoot 명령 시 포탄의 날아가는 방향을 쉽게 갱신할 수 있게 해준다.

 

// 탱크 정보
static class Tank {
    Pos pos;
    int dir;

    public Tank(int xPos, int ypos, int dir) {
        super();
        this.pos = new Pos(xPos, ypos);
        this.dir = dir;
    }
}

 

이후, 각 명령을 살펴보면 다음과 같다.

 

`U`, `D`, `L`, `R` 명령: 이동

  • 주어진 방향으로 전차를 회전 시킨다. 
  • 이동할 수 있다면(이동할 곳이 평지라면) 해당 위치로 전차를 이동시킨다.
  • 이전에 전차가 있던 위치를 평지로 변경한다.

`S` 명령: 발사

  • 맵의 범위를 벗어나거나, 벽돌 벽(`*`) 또는 강철 벽(`#`)을 만날 때까지 현재 전차의 위치부터 전차의 방향으로 조회한다.
  • 벽돌 벽(`*`)을 만난다면, 해당 벽돌 벽의 위치를 평지로 바꾸고 종료한다.
  • 강철 벽(`#`)을 만난다면, 종료한다.

2. 코드

package swea;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

/**
 * 상호의 배틀필드
 * @author JinHxxxxKim
 * 
 * 1. 테스트 케이스의 수 TC를 입력받는다.
 * 2. 두 정수 H, W (2 ≤ H, W ≤ 20)를 입력받는다.
 * 3. 사용자가 넣을 입력의 개수를 입력받는다.
 * 4. 길이가 N인 문자열을 입력받는다.
 * 
 * <, >, ^, v 의 위치를 전차 위치로 방향과 함께 설정한다
 * 
 * 각 명령어에 따라 진행한다.
 */
public class Sol_1873 {
	// 상, 하, 좌, 우 순서
	private static final int[] dx = new int[] { -1, 1, 0, 0 };
	private static final int[] dy = new int[] { 0, 0, -1, 1 };

	private static final char SHOOT = 'S';
	private static final char UP = 'U';
	private static final char DOWN = 'D';
	private static final char LEFT = 'L';
	private static final char RIGHT = 'R';

	private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	private static StringBuilder sb = new StringBuilder();
	private static StringTokenizer st;

	private static int H, W; // 행, 열의 크기
	private static char[][] map; // 지도
	private static int commandCnt; // 명령어 개수
	private static char[] command; // 명령어
	private static Tank tankStatus; // 탱크 상태

	public static void main(String[] args) throws Exception {
		int TC = Integer.parseInt(br.readLine().trim());
		for (int testCase = 1; testCase <= TC; testCase++) {
			// H, W 입력받기
			st = new StringTokenizer(br.readLine().trim());
			H = Integer.parseInt(st.nextToken());
			W = Integer.parseInt(st.nextToken());

			// 지도 정보 입력받기
			map = new char[H][W];
			for (int row = 0; row < H; ++row) {
				map[row] = br.readLine().toCharArray();
			}

			// 명령어 입력받기
			commandCnt = Integer.parseInt(br.readLine().trim());
			command = br.readLine().toCharArray();

			// 전차 위치 찾기
			for (int row = 0; row < H; ++row) {
				for (int col = 0; col < W; ++col) {
					if (map[row][col] == '>') {
						// dir = 3: 우
						tankStatus = new Tank(row, col, 3);
					} else if (map[row][col] == '<') {
						// dir = 2: 좌
						tankStatus = new Tank(row, col, 2);
					} else if (map[row][col] == '^') {
						// dir = 0: 상
						tankStatus = new Tank(row, col, 0);
					} else if (map[row][col] == 'v') {
						// dir = 1: 하
						tankStatus = new Tank(row, col, 1);
					}
				}
			}

			// 명령어 실행
			for (int commandIdx = 0; commandIdx < commandCnt; ++commandIdx) {
				char currCommand = command[commandIdx];
				switch (currCommand) {
				case UP:
					move(0);
					break;
				case DOWN:
					move(1);
					break;
				case LEFT:
					move(2);
					break;
				case RIGHT:
					move(3);
					break;
				case SHOOT:
					shoot();
					break;
				}
			}

			sb.append(String.format("#%d ", testCase));
			for (int row = 0; row < H; ++row) {
				for (int col = 0; col < W; ++col) {
					sb.append(map[row][col]);
				}
				sb.append("\n");
			}
		}
		System.out.println(sb);
	}

	private static void shoot() {
		// 탱크의 방향으로 쭉 탐색
		Pos currPos = tankStatus.pos;
		int currRow = currPos.xPos;
		int currCol = currPos.yPos;
		int dir = tankStatus.dir;

		while (true) {
			// 범위 검증
			if (currRow < 0 || currCol < 0 || currRow >= H || currCol >= W)
				break;

			if (map[currRow][currCol] == '*') {
				// 벽돌 벽일 경우, 평지로 바꾸고 종료
				map[currRow][currCol] = '.';
				break;
			}
			if (map[currRow][currCol] == '#') {
				// 강철 벽일 경우, 그냥 종료
				break;
			}
			currRow += dx[dir];
			currCol += dy[dir];
		}
	}

	private static void move(int dir) {
		tankStatus.dir = dir;

		Pos currPos = tankStatus.pos;
		int currRow = currPos.xPos;
		int currCol = currPos.yPos;

		// // 탱크의 방향을 회전시킨다.
		if (dir == 0) {
			// 상
			map[currRow][currCol] = '^';
		} else if (dir == 1) {
			// 하
			map[currRow][currCol] = 'v';
		} else if (dir == 2) {
			// 좌
			map[currRow][currCol] = '<';
		} else if (dir == 3) {
			// 우
			map[currRow][currCol] = '>';
		}

		int tempRow = currRow + dx[dir];
		int tempCol = currCol + dy[dir];

		// 범위 검증
		if (tempRow < 0 || tempCol < 0 || tempRow >= H || tempCol >= W)
			return;

		// 평지 검증
		if (map[tempRow][tempCol] == '.') {
			tankStatus.pos.xPos = tempRow;
			tankStatus.pos.yPos = tempCol;

			// 탱크의 이동 후 방향을 회전시킨다.
			if (dir == 0) {
				// 상
				map[tempRow][tempCol] = '^';
			} else if (dir == 1) {
				// 하
				map[tempRow][tempCol] = 'v';
			} else if (dir == 2) {
				// 좌
				map[tempRow][tempCol] = '<';
			} else if (dir == 3) {
				// 우
				map[tempRow][tempCol] = '>';
			}
			// 기존 탱크 위치를 평지로
			map[currRow][currCol] = '.';
		}
	}

	// 탱크 정보
	static class Tank {
		Pos pos;
		int dir;

		public Tank(int xPos, int ypos, int dir) {
			super();
			this.pos = new Pos(xPos, ypos);
			this.dir = dir;
		}
	}

	// 위치 정보
	static class Pos {
		int xPos;
		int yPos;

		public Pos(int xPos, int yPos) {
			super();
			this.xPos = xPos;
			this.yPos = yPos;
		}
	}
}

3. 디버깅

전차 이동 시, 이동 가능성만 판단하고 전차를 회전시키지 않아서 결과가 다르게 나왔다. 

 

전차 이동이 불가능하더라도 전차의 방향을 회전 시켜야한다는 것과 전차를 이동시킨 뒤 원래 전차가 있던 곳을 평지로 만들어줘야 한다는 점을 간과하였었다.


4. 결과