[PS] BOJ 15671 / 오델로

[PS] BOJ 15671 / 오델로
Thumbnail: Photo by Celest So (Unsplash)
문제 링크: https://www.acmicpc.net/problem/15671

구현 문제는 항상 재밌는 것 같습니다.

풀이

오델로?

6x6의 보드 판 위에서, 매 차례에 돌을 놓았을 때 상대의 돌을 자신의 돌로 감싸게 된다면, 자신의 돌로 감싼 상대의 돌을 모두 뒤집을 수 있는 게임입니다.

이렇게 서로의 차례를 반복하며, 보드판 전체를 자신의 돌로 만들거나, 더 이상 서로 뒤집을 수 있는 돌이 없을 때 게임이 종료됩니다.

구현하기

사실 규칙은 크게 중요하지 않습니다. 이 문제는 직접 돌을 놓아야 하는 문제가 아니고, 돌을 놓은 위치를 입력으로 받아 게임의 결과를 계산하기만 하면 되기 때문입니다.

따라서, 매 차례에 돌을 놓을 때 상대의 돌을 뒤집을 수 있다면 뒤집어주는 부분만 구현해주면 됩니다.

1) 초기 상태

# 타일 상수
EMPTY = 0
WHITE = 1
BLACK = 2
TILE_STR = [".", "W", "B"]

board = [[EMPTY for _ in range(7)] for _ in range(7)]
queue = deque()

board[3][3] = board[4][4] = WHITE
board[3][4] = board[4][3] = BLACK

초기 상태 정의하기

보드판의 (3,3), (4,4) 에는 흰 돌이, (3,4), (4,3)에는 검은 돌이 미리 놓여있습니다.

타일 종류는 빈 칸을 0, 흰 돌을 1, 검은 돌을 2로 사용했는데, 코드의 가독성을 위해 각각을 상수 변수로 정의해 사용했습니다. 또, 이 상수값을 인덱스로 가지는 배열 TILE_STR에 대응하는 타일 문자열을 저장해 보드판을 출력하기에 용이하게끔 구현했습니다.

2) 상대 돌 뒤집기

DIRECTIONS = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))

def is_inside(r, c):
    return 1 <= r <= 6 and 1 <= c <= 6

def check_board(r, c, stone):
    for dr, dc in DIRECTIONS:
        nr = r + dr
        nc = c + dc
        queue.clear()

        while is_inside(nr, nc):
            if board[nr][nc] == EMPTY:
                break
            elif board[nr][nc] == stone:
                while queue:
                    nr, nc = queue.popleft()
                    board[nr][nc] = stone
                break
            else:
                queue.append((nr, nc))
            nr += dr
            nc += dc

for i in range(1, int(input()) + 1):
    r, c = map(int, input().split())
    cur_turn = BLACK if i & 1 else WHITE
    board[r][c] = cur_turn
    check_board(r, c, cur_turn)

상대의 돌을 뒤집는 코드

  • is_inside는 매개변수로 받은 좌표 (r, c)가 보드판 내부의 좌표인지를 판단하는 함수입니다.
  • check_board는 현재 놓은 돌의 좌표 (r, c)와 놓은 돌의 종류 stone을 매개변수로 받아 자신이 상대의 돌을 뒤집을 수 있는지 확인합니다.

오델로의 규칙에 따르면, 자신이 돌을 놓은 위치를 기준으로 8방향 (가로, 세로, 대각선)을 확인해 기존에 놓여있던 자신의 다른 돌과 지금 놓은 돌이 상대의 돌을 감싸고 있다면 뒤집을 수 있습니다.

check_board에서는 DIRECTIONS에 미리 정의해 둔 8개 방향의 좌표 변화량 (dr, dc)를 사용해 각 방향으로 보드를 탐색하며, 조건에 맞다면 상대의 돌을 뒤집습니다.

3) 보드판 및 결과 출력하기

def print_answer():
    white = 0
    black = 0
    t = [""] * 6
    for r in range(1, 7):
        for c in range(1, 7):
            if board[r][c] == WHITE:
                white += 1
            elif board[r][c] == BLACK:
                black += 1
            t[c - 1] = TILE_STR[board[r][c]]
        print("".join(t))
    if white > black:
        print("White")
    else: # 비기는 입력은 주어지지 않는다.
        print("Black")

결과 출력

전체 코드

from collections import deque
input = open(0).readline

# 타일 상수
EMPTY = 0
WHITE = 1
BLACK = 2
TILE_STR = [".", "W", "B"]
DIRECTIONS = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))

board = [[EMPTY for _ in range(7)] for _ in range(7)]
queue = deque()

board[3][3] = board[4][4] = WHITE
board[3][4] = board[4][3] = BLACK

def print_answer():
    white = 0
    black = 0
    t = [""] * 6
    for r in range(1, 7):
        for c in range(1, 7):
            if board[r][c] == WHITE:
                white += 1
            elif board[r][c] == BLACK:
                black += 1
            t[c - 1] = TILE_STR[board[r][c]]
        print("".join(t))
    if white > black:
        print("White")
    else: # 비기는 입력은 주어지지 않는다.
        print("Black")

def is_inside(r, c):
    return 1 <= r <= 6 and 1 <= c <= 6

def check_board(r, c, stone):
    for dr, dc in DIRECTIONS:
        nr = r + dr
        nc = c + dc
        queue.clear()

        while is_inside(nr, nc):
            if board[nr][nc] == EMPTY:
                break
            elif board[nr][nc] == stone:
                while queue:
                    nr, nc = queue.popleft()
                    board[nr][nc] = stone
                break
            else:
                queue.append((nr, nc))
            nr += dr
            nc += dc

for i in range(1, int(input()) + 1):
    r, c = map(int, input().split())
    cur_turn = BLACK if i & 1 else WHITE
    board[r][c] = cur_turn
    check_board(r, c, cur_turn)

print_answer()

solution.py