[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. 결과
'알고리즘' 카테고리의 다른 글
[BOJ] - 치즈 / 2636 (Java) (0) | 2024.02.20 |
---|---|
[BOJ] - 줄세우기 / 2252번 (Java) (0) | 2024.02.20 |
[SWEA] - 최적 경로 / 1247번 (Java) (0) | 2024.02.15 |
[SWEA] - 무선 충전 / 5644번 (Java) (0) | 2024.02.15 |
[BOJ] - 신기한 소수/2023번 (JAVA) (0) | 2024.02.14 |