[SWEA][D3][#01873] 상호의 배틀필드

작성:    

업데이트:

카테고리:

태그: , ,

출처

학습용 포스트입니다. 본 포스트가 문제가 될 시 수정 또는 삭제하겠습니다.


문제

상호는 전차로 시가전을 하는 것을 테마로 한 새로운 게임 “배틀 필드”를 개발하기로 했다.

그래서 먼저 간단하게 프로토 타입 게임을 만들었다.

이 프로토 타입에서 등장하는 전차는 사용자의 전차 하나뿐이며, 적이나 아군으로 만들어진 전차는 등장하지 않는다.

사용자의 전차는 사용자의 입력에 따라 격자판으로 이루어진 게임 맵에서 다양한 동작을 한다.

다음 표는 게임 맵의 구성 요소를 나타낸다.

문자 의미

. 평지(전차가 들어갈 수 있다.)
- 벽돌로 만들어진 벽
# 강철로 만들어진 벽
- 물(전차는 들어갈 수 없다.)
^ 위쪽을 바라보는 전차(아래는 평지이다.)
v 아래쪽을 바라보는 전차(아래는 평지이다.)
< 왼쪽을 바라보는 전차(아래는 평지이다.)
> 오른쪽을 바라보는 전차(아래는 평지이다.)

다음 표는 사용자가 넣을 수 있는 입력의 종류를 나타낸다.

문자 동작

U Up : 전차가 바라보는 방향을 위쪽으로 바꾸고, 한 칸 위의 칸이 평지라면 위 그 칸으로 이동한다.
D Down : 전차가 바라보는 방향을 아래쪽으로 바꾸고, 한 칸 아래의 칸이 평지라면 그 칸으로 이동한다.
L Left : 전차가 바라보는 방향을 왼쪽으로 바꾸고, 한 칸 왼쪽의 칸이 평지라면 그 칸으로 이동한다.
R Right : 전차가 바라보는 방향을 오른쪽으로 바꾸고, 한 칸 오른쪽의 칸이 평지라면 그 칸으로 이동한다.
S Shoot : 전차가 현재 바라보고 있는 방향으로 포탄을 발사한다.
전차가 이동을 하려고 할 때, 만약 게임 맵 밖이라면 전차는 당연히 이동하지 않는다.

전차가 포탄을 발사하면, 포탄은 벽돌로 만들어진 벽 또는 강철로 만들어진 벽에 충돌하거나 게임 맵 밖으로 나갈 때까지 직진한다.

만약 포탄이 벽에 부딪히면 포탄은 소멸하고, 부딪힌 벽이 벽돌로 만들어진 벽이라면 이 벽은 파괴되어 칸은 평지가 된다.

강철로 만들어진 벽에 포탄이 부딪히면 아무 일도 일어나지 않는다.

게임 맵 밖으로 포탄이 나가면 아무런 일도 일어나지 않는다.

초기 게임 맵의 상태와 사용자가 넣을 입력이 순서대로 주어질 때, 모든 입력을 처리하고 나면 게임 맵의 상태가 어떻게 되는지 구하는 프로그램을 작성하라.


입력

첫 번째 줄에 테스트 케이스의 수 T가 주어진다.

각 테스트 케이스의 첫 번째 줄에는 두 정수 H, W (2 ≤ H, W ≤ 20) 이 공백으로 구분되어 주어진다.

이는 게임 맵의 높이가 H, 너비가 W임을 나타낸다.

즉, 게임 맵은 H x W크기의 격자판이다.

다음 H개의 각각의 줄에는 길이가 W인 문자열이 주어진다.

각각의 문자는 위의 게임 맵 구성 요소 표에 있는 문자들만 포함하며, 전차는 단 하나만 있다.

다음 줄에는 사용자가 넣을 입력의 개수를 나타내는 정수 N(0 < N ≤ 100) 이 주어진다.

다음 줄에는 길이가 N인 문자열이 주어진다.

각각의 문자는 위의 사용자가 넣을 수 있는 입력의 종류를 나타내는 표에 있는 문자들만 포함된다.


출력

각 테스트 케이스마다 ‘#x’(x는 테스트케이스 번호를 의미하며 1부터 시작한다)를 출력하고 한 칸을 띄운 후, 모든 입력을 처리하고 난 후의 게임 맵을 H개의 줄에 걸쳐 출력한다.


예제

입력

1
3 7
***....
*-..#**
#<.****
23
SURSSSSUSLSRSSSURRDSRDS


출력

#1 **....v
.-..#..
#......


My Sol

# 함수 정의
def Shoot(mat, i, j, d):
    di, dj = D_dict[d]
    if mat[i+di][j+dj] == '#':
        return mat
    elif mat[i+di][j+dj] == '*':
        mat[i+di][j+dj] = '.'
        return mat
    else:
        return Shoot(mat, i+di, j+dj, d)


def Move(mat, i, j, p):
    k = p_dict[p]
    mat[i][j] = k
    di, dj = D_dict[k]
    if mat[i+di][j+dj]=='.':
        mat[i][j] = '.'
        i, j = i+di, j+dj
        mat[i][j] = k
    return mat, i, j, k


# 입력 처리
T = int(input())
for tc in range(T):
    H, W = map(int, input().split())
    mat = [['#']*(W+2)]
    mat += [['#']+list(input())+['#'] for _ in range(H)]
    mat += [['#']*(W+2)]
    P = int(input())
    p_lst = input()

    D = ['^', 'v', '<', '>']
    p_dict = {'U':'^', 'D':'v', 'L':'<', 'R':'>'}
    D_dict = {
            '^': (-1, 0),
            'v': (1, 0),
            '<': (0, -1),
            '>': (0, 1)
        }

    # 전차의 현재 위치 파악
    for i in range(1, H+1):
        for j in range(1, W+1):
            for d in D:
                if mat[i][j] == d:
                    ni, nj, nd = i, j, d

    # 문자동작 시작
    for p in p_lst:
        if p == 'S':
            mat = Shoot(mat, ni, nj, nd)
        else:
            mat, ni, nj, nd = Move(mat, ni, nj, p)

    # 출력
    print(f'#{tc + 1}', end=' ')
    mat.pop(0)
    mat.pop(-1)

    for i in range(H):
        mat[i].pop(0)
        mat[i].pop(-1)
        print(*mat[i], sep='')

우선 입력을 처리해주는데, 이후의 설명을 보면 2차원 배열 외부로 포탄이나 전차가 이동할 수도 있다. 이때 인덱스의 범위를 이용하여 범위 외로의 이동이나 작용에 대해서는 무효화해줄 수 있겠지만, 나는 본 문제에서 절대적인 요소인 #를 가장자리에 모두 씌워 범위 외에서는 전차가 이동하지도, 포탄이 이동하지도 않도록 하였다.

자판의 입력 개수를 P, 입력 리스트를 p_lst에 저장하였다. mat 위에서 전차의 방향과 위치를 표시하는 4개의 표시 ['^', 'v', '<', '>']를 D에 저장하고, 딕셔너리 p_dict와 D_dict를 정의하여 위의 함수에서 사용하고자 하였다. p_dict는 p_lst에 들어온 텍스트 입력값에 대한 좌표 모양을 결정하고, D_dict는 좌표 모양에 따른 벡터를 의미한다.

코드는 전차의 현재 위치를 파악하면서 시작된다. 기준점 (1,1)에서 (H,W)까지 이 내부에서 D 안의 각각의 표시자에 대해 특정 좌표에 이것이 있다면 해당 좌표와 표시자를 ni, nj, nd에 저장하였다. 이후엔 함수를 사용하여 본문 자체는 간결하게 표현하였다.

입력은 포탄 발사를 의미하는 ‘S’과 이동을 의미하는 ‘U, D, R, L’로 구성되는데, 이들을 모아둔 p_lst의 각각의 p들을 꺼내어 조사한다.

만약 S라면 Shoot() 함수를 호출하는데 Shoot함수는 mat과 현재 위치, 방향을 입력받아 딕셔너리를 통해 포탄의 이동 벡터를 결정하고 이동시킨다. 한 번 이동한 위치에서의 값에 따라 ‘#’를 만나면 무효이므로 mat을 반환하고, ‘*‘를 만나면 이 칸의 값을 평지인 ‘.’로 바꾼 뒤, mat을 반환한다. 그것이 아니라면 포탄이 통과하게 되고, 재귀를 사용하여 다음 위치에서 같은 확인을 하도록 하였다.

만약 S가 아닌 이동하는 입력이라면 Move() 함수를 호출하는데, Move함수 역시 mat과 현재 위치, 그리고 이동방향입력을 입력받아 처리한다. 딕셔너리를 이용해 이동방향 입력 텍스트로부터 매칭되는 표시자를 추출하고, 전차의 현재 위치를 이 표시자로 바꾼다. 만약 전차가 이동하지 못하는 상황이라면 방향을 바꾼 그대로 mat과 현재 위치, 표시자를 반환하는데 전차의 이동방향의 칸이 전차가 이동할 수 있는 ‘.’이라면 전차가 있던 위치를 ‘.’로 표시하고 이동방향의 칸에 전차의 표시자를 입력한다. 이는 mat위에서 전차가 이동방향으로 한 칸 이동하였음을 의미한다.

이렇게 매 입력 p에 따라 mat 또는 mat과 현재 위치, 표시자를 반환받으며 p를 모두 조회하면 이동을 마친다. 편의를 위해 가장자리를 둘러주었던 ‘#’를 제거하고 출력 양식에 맞게 출력하면 되겠다.


결과

PASS

댓글남기기