[Python] 뱀

허창원·2024년 3월 12일
0
post-custom-banner

문제 설명

[Gold IV] 뱀 - 3190

문제 링크

성능 요약

메모리: 31120 KB, 시간: 64 ms

분류

자료 구조, 덱, 구현, 큐, 시뮬레이션

제출 일자

2024년 3월 13일 04:26:27

문제 설명

'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다.

게임은 NxN 정사각 보드위에서 진행되고, 몇몇 칸에는 사과가 놓여져 있다. 보드의 상하좌우 끝에 벽이 있다. 게임이 시작할때 뱀은 맨위 맨좌측에 위치하고 뱀의 길이는 1 이다. 뱀은 처음에 오른쪽을 향한다.

뱀은 매 초마다 이동을 하는데 다음과 같은 규칙을 따른다.

  • 먼저 뱀은 몸길이를 늘려 머리를 다음칸에 위치시킨다.
  • 만약 벽이나 자기자신의 몸과 부딪히면 게임이 끝난다.
  • 만약 이동한 칸에 사과가 있다면, 그 칸에 있던 사과가 없어지고 꼬리는 움직이지 않는다.
  • 만약 이동한 칸에 사과가 없다면, 몸길이를 줄여서 꼬리가 위치한 칸을 비워준다. 즉, 몸길이는 변하지 않는다.

사과의 위치와 뱀의 이동경로가 주어질 때 이 게임이 몇 초에 끝나는지 계산하라.

입력

첫째 줄에 보드의 크기 N이 주어진다. (2 ≤ N ≤ 100) 다음 줄에 사과의 개수 K가 주어진다. (0 ≤ K ≤ 100)

다음 K개의 줄에는 사과의 위치가 주어지는데, 첫 번째 정수는 행, 두 번째 정수는 열 위치를 의미한다. 사과의 위치는 모두 다르며, 맨 위 맨 좌측 (1행 1열) 에는 사과가 없다.

다음 줄에는 뱀의 방향 변환 횟수 L 이 주어진다. (1 ≤ L ≤ 100)

다음 L개의 줄에는 뱀의 방향 변환 정보가 주어지는데, 정수 X와 문자 C로 이루어져 있으며. 게임 시작 시간으로부터 X초가 끝난 뒤에 왼쪽(C가 'L') 또는 오른쪽(C가 'D')로 90도 방향을 회전시킨다는 뜻이다. X는 10,000 이하의 양의 정수이며, 방향 전환 정보는 X가 증가하는 순으로 주어진다.

출력

첫째 줄에 게임이 몇 초에 끝나는지 출력한다.

문제 풀이

from collections import deque

n = int(input())
k = int(input())
apples = []
move = []
for i in range(k):
    a, b = map(int, input().split())
    apples.append((a, b))
l = int(input())
for i in range(l):
    x, c = input().split()
    move.append([int(x), c])
move_seconds = [move_time[0] for move_time in move]

board = [[0] * (n + 2) for _ in range(n + 2)]  # 보드판 넘어서는 것 고려하여 n+2 설정

# 보드위에 사과 두기
for apple in apples:
    a, b = apple
    board[a][b] = 1
second = 0
direction = [(0, 1), (1, 0), (0, -1), (-1, 0)]  # 동남서북
d = 0
snake = deque([(1, 1)])

while True:
    moved_snake = deque()
    cur_direction = direction[d]
    second += 1
    tail_x, tail_y = snake[-1]
    for body in snake:  # 뱀머리 1칸 이동
        x, y = body
        nx, ny = x + cur_direction[0], y + cur_direction[1]
        moved_snake.append((nx, ny))

    snake = moved_snake
    head_x, head_y = snake.popleft()

    if (
        head_x == 0 or head_x == n + 1 or head_y == 0 or head_y == n + 1
    ):  # 뱀 머리 보드판 부딪힘?
        print(second)
        break

    if (head_x, head_y) in snake or (
        head_x == tail_x and head_y == tail_y
    ):  # 뱀 머리 몸통에 부딪힘?
        print(second)
        break
    else:
        snake.appendleft((head_x, head_y))

    if board[head_x][head_y] == 1:  # 뱀머리가 사과와 만남
        snake.append((tail_x, tail_y))

    if second in move_seconds:  # 뱀의 머리 방향 조정
        if move[move_seconds.index(second)][1] == "L":
            d -= 1
            if d == -1:
                d = 3
        elif move[move_seconds.index(second)][1] == "D":
            d += 1
            if d == 4:
                d = 0

아이디어는 뱀이 움직이는 board를 n+2로 초기화한다. 그 이유는 벽을 넘는 경우를 생각해서 상하좌우에 1줄씩 추가 했기 때문이다. 이 코드의 문제점은 머리가 방향 전환을 할때, 머리만 좌표가 변해야하는데, 나머지 몸통까지 평행이동해버리는 문제점이 발생했다. 또한, 보드판에 변형을 가했을 때, 사과의 좌표또한 변경시켜줘야하는 일이 생기므로 실수 할 수 있다.

개선 방안

N = int(input())
K = int(input())
apples = []
for i in range(K):
    a, b = map(int, input().split())
    apples.append((a, b))
L = int(input())
directions = [input().split() for _ in range(L)]
directions = [(int(x), c) for x, c in directions]

# 뱀의 초기 위치 및 방향 설정 (오른쪽을 바라보고 있음)
snake = [(1, 1)]
dx, dy = 0, 1  # 초기 이동 방향
time = 0  # 게임이 진행된 시간
direction_idx = 0  # 다음에 적용할 방향 변환의 인덱스

# 방향 전환 함수
def turn(direction, C):
    if C == 'L':
        return -direction[1], direction[0]
    else:
        return direction[1], -direction[0]

# 게임 시작
while True:
    time += 1
    # 뱀의 머리 위치 갱신
    head = (snake[0][0] + dx, snake[0][1] + dy)
    # 벽 또는 자기 자신과 부딪히면 게임 종료
    if head in snake or not(1 <= head[0] <= N) or not(1 <= head[1] <= N):
        break
    snake.insert(0, head)
    # 사과가 있으면 꼬리를 움직이지 않음
    if head not in apples:
        snake.pop()
    else:  # 사과를 먹으면 사과 목록에서 제거
        apples.remove(head)
    # 방향 전환 시간이 되면 방향 전환
    if direction_idx < L and time == directions[direction_idx][0]:
        dx, dy = turn((dx, dy), directions[direction_idx][1])
        direction_idx += 1

print(time)
  • head 변수만 변화함으로써 몸통 전체의 평행이동이 발생하지 않는다.
  • 방향전환 로직을 단순화하였다.
  • 충돌 검사 로직을 단순화하였다. 벽과의 충돌 검사를 복잡하게 하고 있다. 보드의 크기 내에 존재하지 않으면이란 조건문으로 판별할 수 있다.

배운점

  • 방향 전환하는 함수를 단순하게 표현할 수 있다는 것을 배웠다.
  • 보드판을 2차 배열로 초기화해서 풀어야하는줄 알았지만 더 간단한 방법으로 head 좌표만 수정하면서 뱀의 길이를 리스트 형태에서 수정해나갔다.
  • 방향 전환 시간이 되면 방향 전환 이 부분을 익혀두자
post-custom-banner

0개의 댓글