구름톤챌린지 GameJam 파이썬 195692
❍ 문제
플레이어는 GameJam에 참가했다. GameJam은 현장에서 즉석으로 팀을 꾸려 게임의 규칙을 만든 뒤, 생각한 규칙을 코드로 옮겨서 게임을 만들어내는 대회이다.
플레이어가 속한 팀은 보드로 즐길 수 있는 간단한 보드게임을 만들었다. 게임의 진행 방법과 규칙은 다음과 같다.
- 게임은 한 변의 길이가 N인 격자 보드 위에서 진행한다. 보드는 한 변의 길이가 1인 $N^2$개의 칸으로 나누어져 있다.
- 각 칸에는 <command> 에 해당하는 방향으로 <count> 칸만큼 한 칸씩 이동하라는 지시가 적혀있다.
- 플레이하는 사람은 처음에 보드의 칸 중 하나에 말을 올려놓는다.
- 각 칸에 적힌 지시대로 말을 이동한다. 만약 이동 중에 보드 밖으로 나가게 된다면 반대쪽의 첫 번째 칸으로 이동한다. 예를 들어 맨 오른쪽 칸에서 오른쪽으로 이동해야 한다면 해당 행의 맨 왼쪽 칸으로 이동하고, 맨 아래 칸에서 아래쪽으로 이동해야 한다면 해당 열의 맨 위쪽 칸으로 이동하고, 이동 거리가 남아 있다면 계속 이동한다.
- 만약 이동하다가 자신의 말이 이미 한 번이라도 방문한 칸을 다시 지나야 할 경우에는 게임이 종료된다. 그 외의 경우에는 게임이 종료될 때까지 위의 4번 단계를 반복한다.
- 게임의 점수는 시작한 칸을 포함하여, 게임이 종료되기 전까지 말이 방문한 서로 다른 칸의 개수이다.
플레이어는 GameJam의 최종 발표 때 게임을 시연해 보기 위해, 구름이와 미리 연습 게임을 진행해 보려고 한다. 플레이어와 구름이가 각각 처음에 말을 올려둔 칸이 주어졌을 때, 두 사람 중 누가 더 높은 점수를 획득했는지를 구해보자.
❍ 입력
첫째 줄에 격자 보드의 크기 N이 주어진다.
둘째 줄에는 구름이가 말을 올려둔 칸의 좌표 $R_g . C_g$가 공백을 두고 주어진다.
보드의 $R_g$번째 행, $C_g$번째 열에 말을 올려두었다는 뜻이다.
셋째 줄에는 플레이어가 말을 올려둔 칸의 좌표 $R_p . C_p$가 공백을 두고 주어진다. 보드의 $R_p$번째 행, $C_p$번째 열에 말을 올려두었다는 뜻이다.
다음 N개의 줄에는 보드의 각 칸에 적혀있는 지시 사항이 주어진다. 각 줄마다 N개의 지시 사항이 <count> <command>형식으로 공백을 두고 주어지고, i번째 줄에서 j번째로 주어지는 지시 사항은 보드의 r번째 행, c번째 열의 정보를 의미한다. <count>는 이동 횟수, <command>는 이동 방향을 말한다.
- $3 \leq N \leq 200$
- $1 \leq R_g, C_g, R_p, C_p \leq N$
- <count> 는 1이상 N이하의 정수이다.
- <command> 는 U, D, R, L 중 하나이다. 각각 위쪽, 아래쪽, 오른쪽, 왼쪽을 의미한다. 이 문제에서 위쪽은 행 번호가 감소하는 방향이다.
- 입력에서 주어지는 모든 수는 정수이다.
❍ 출력
구름이가 게임에서 승리하면 goorm 과 구름이가 얻은 점수를 공백을 두고 출력하고, 플레이어가 승리하면 player 와 플레이어가 얻은 점수를 공백을 두고 출력한다. 두 사람이 비기는 테스트 케이스는 주어지지 않는다.
❏ 문제 풀이
구름이와 플레이어는 각각 주어진 이동 지시 사항에 따라 점수를 얻고 이를 비교하는 문제이다.
❍ 예제 #1 이해하기
구름이는 (1, 1)에 말을 두고 시작한다. 해당 칸은 1L로 왼쪽으로 한 칸 이동해야 한다. 그러나 보드를 넘어가므로 오른쪽으로 이동된다.
이동 후에는 1D이므로 아래로 한 칸 이동한다. 이를 반복하면, 이미 방문된 적 있는 곳에 도달하여 게임이 종료된다.
방문된 칸의 수가 4개로, 구름이의 점수는 4이다.
방문된 칸의 수가 2개로, 플레이어의 점수는 2이다.
❍ 변수 설명
import sys
input = sys.stdin.readline
n = int(input()) # 격자 보드 크기
# 구름과 플레이어의 보드
board_g = [[0] * n for _ in range(n)]
board_p = [[0] * n for _ in range(n)]
구름이와 플레이어가 각각 방문한 칸을 체크하기 위해 board_g와 board_p를 정의한다.
# 구름이의 말 위치
a, b = map(int, input().split())
a, b = a - 1, b - 1
board_g[a][b] = 1 # 방문 처리
# 플레이어의 말 위치
x, y = map(int, input().split())
x, y = x - 1, y - 1
board_p[x][y] = 1 # 방문 처리
# 점수
goorm, player = 1, 1
# 상하좌우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# 이동 정보
info = []
for _ in range(n):
info.append(list(input().split()))
인덱스가 0부터 시작되므로 각 말의 위치를 -1 해주었고, board에서의 값을 1로 설정하여 방문 처리되었음을 기록해주었다.
❍ 점수 계산하기
breaker = False
# 구름 계산
while not breaker:
count, command = "", ""
for i in info[a][b]:
if str.isdigit(i):
count += i
else:
command += i
count = int(count)
...
먼저 (a, b)에 놓인 이동 정보를 알아야 하는데, “1L”과 같이 주어진 정보를 “1”과 “L”로 구분지어야 한다. str.isdigit을 이용하여 해당 문자가 숫자인지를 체크하여 count에 덧붙여 준다.
위의 방식 말고도, 맨 끝 문자는 항상 한 글자이니까, 좀 더 간단하게 구현이 될 수 있을 것 같다는 조언을 구해 아래와 같은 코드로 바꾸어 주었다.
breaker = False
# 구름 계산
while not breaker:
# count, command = "", ""
# for i in info[a][b]:
# if str.isdigit(i):
# count += i
# else:
# command += i
# count = int(count)
count, command = int(info[a][b][:-1]), info[a][b][-1]
리스트 인덱스에 음의 정수를 넣으면 거꾸로 탐색하게 된다. 따라서 -1은 가장 마지막 원소를 의미한다.
...
index = {"U": 0, "D": 1, "L": 2, "R": 3}.get(command)
for _ in range(count):
a = (a + dx[index]) % n
b = (b + dy[index]) % n
# 이미 방문한 곳이라면, 게임 종료
if board_g[a][b] == 1:
breaker = True
break
else:
goorm += 1
board_g[a][b] = 1
if breaker:
break
상하좌우 중 이동해야하는 부분을 index로 알아내어 count만큼 이동하기 시작한다.
% n으로 계산하여 보드를 넘어간다면, 맨 처음 혹은 맨 끝을 가리킬 수 있도록 하였다.
board에서의 값이 이미 1로 방문 처리가 된 곳이라면, 모든 반복문을 종료하여 게임이 종료될 수 있도록 하고, 그렇지 않다면, 점수를 +1 해준다.
❍ CODE
import sys
input = sys.stdin.readline
n = int(input()) # 격자 보드 크기
# 구름과 플레이어의 보드
board_g = [[0] * n for _ in range(n)]
board_p = [[0] * n for _ in range(n)]
# 구름이의 말 위치
a, b = map(int, input().split())
a, b = a - 1, b - 1
board_g[a][b] = 1
# 플레이어의 말 위치
x, y = map(int, input().split())
x, y = x - 1, y - 1
board_p[x][y] = 1
# 점수
goorm, player = 1, 1
# 상하좌우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# 이동 정보
info = []
for _ in range(n):
info.append(list(input().split()))
breaker = False
# 구름 계산
while not breaker:
count, command = "", ""
for i in info[a][b]:
if str.isdigit(i):
count += i
else:
command += i
count = int(count)
index = {"U": 0, "D": 1, "L": 2, "R": 3}.get(command)
for _ in range(count):
a = (a + dx[index]) % n
b = (b + dy[index]) % n
if board_g[a][b] == 1:
breaker = True
break
else:
goorm += 1
board_g[a][b] = 1
if breaker:
break
breaker = False
# 플레이어 계산
while not breaker:
count, command = "", ""
for i in info[x][y]:
if str.isdigit(i):
count += i
else:
command += i
count = int(count)
index = {"U": 0, "D": 1, "L": 2, "R": 3}.get(command)
for _ in range(count):
x = (x + dx[index]) % n
y = (y + dy[index]) % n
if board_p[x][y] == 1:
breaker = True
break
else:
player += 1
board_p[x][y] = 1
if player > goorm:
print("player", player)
else:
print("goorm", goorm)
❍ CODE
import sys
def play(x, y):
notEnd = True
visited = [[False] * n for _ in range(n)]
visited[x][y] = True
while notEnd:
count, command = int(info[x][y][:-1]), info[x][y][-1]
dx, dy = direction[command]
for _ in range(count):
x = (x + dx) % n
y = (y + dy) % n
if visited[x][y]:
notEnd = False
break
visited[x][y] = True
return sum([sum(i) for i in visited])
n = int(input())
# 구름이의 말 위치
Rg, Cg = map(int, input().split())
# 플레이어의 말 위치
Rp, Cp = map(int, input().split())
Rg, Cg, Rp, Cp = Rg - 1, Cg - 1, Rp - 1, Cp - 1
# 이동 정보
info = [list(input().split()) for _ in range(n)]
# 상하좌우
direction = {"U": [-1, 0], "D": [1, 0], "L": [0, -1], "R": [0, 1]}
goorm = play(Rg, Cg)
player = play(Rp, Cp)
if goorm > player:
print("goorm", goorm)
else:
print("player", player)
구름톤에서 제공된 해설 코드를 참고하여 다시 코드를 짜주었다.
일단 상하좌우를 direction으로 한 줄로 표현한 점이 좋아서 적용해보았고, 함수도 따로 생성하여 중복 코드를 제거해주었다.
또한, 점수를 합해주기 위해 방문할 때마다 + 1로 표현해주었었는데, sum( ) 함수로 저렇게 행씩 먼저 더하여 list 형식으로 만들고 그 list를 또 sum( ) 함수로 이용한 점이 마음에 들어 적용시켜보았다.
❏ 삽질 기록
❍ Issue #1
import sys
input = sys.stdin.readline
n = int(input()) # 격자 보드 크기
# 구름과 플레이어의 보드
board_g = [[0] * n for _ in range(n)]
board_p = [[0] * n for _ in range(n)]
# 구름이의 말 위치
a, b = map(int, input().split())
a, b = a - 1, b - 1
board_g[a][b] = 1
# 플레이어의 말 위치
x, y = map(int, input().split())
x, y = x - 1, y - 1
board_p[x][y] = 1
# 점수
goorm, player = 1, 1
# 상하좌우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# 이동 정보
info = []
for _ in range(n):
info.append(list(input().split()))
breaker = False
# 구름 계산
while not breaker:
count, command = int(info[a][b][0]), info[a][b][1]
index = {'U' : 0, 'D' : 1, 'L' : 2, 'R' : 3}.get(command)
for _ in range(count):
a = (a + dx[index]) % n
b = (b + dy[index]) % n
if board_g[a][b] == 1:
breaker = True
break
else:
goorm += 1
board_g[a][b] = 1
if breaker:
break
breaker = False
# 플레이어 계산
while not breaker:
count, command = int(info[x][y][0]), info[x][y][1]
index = {'U' : 0, 'D' : 1, 'L' : 2, 'R' : 3}.get(command)
for _ in range(count):
x = (x + dx[index]) % n
y = (y + dy[index]) % n
if board_p[x][y] == 1:
breaker = True
break
else:
player += 1
board_p[x][y] = 1
if player > goorm:
print("player", player)
else:
print("goorm", goorm)
Runtime Error 발생, 어느 부분에 말도 안되는 부분이 존재하기 때문에 발생하는 문제라고 생각했다.
도저히 모르겠어서 알고리즘 왕고수님에게 물어봤는데 오류를 찾을 수 있었다.
count, command = int(info[a][b][0]), info[a][b][1]
단순히 info[a][b][0]과 info[a][b][1]로 숫자와 문자를 구별할 수 있을 거라고 생각했는데, 생각해보면 숫자가 10 단위 100 단위로 주어질 수도 있는 것이기 때문에 오류가 발생했던 것이였다.
따라서 이 부분을 수정하여 해결할 수 있었다!