문제

https://www.codetree.ai/training-field/frequent-problems/problems/battle-ground/description?page=3&pageSize=5

 

코드트리 | 코딩테스트 준비를 위한 알고리즘 정석

국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.

www.codetree.ai

 

 

플레이어 이동

  • 각 플레이어는 자신이 향하고 있는 방향으로 한 칸씩 이동합니다.
  • 만약 격자를 벗어나면 반대 방향으로 방향을 바꾸고 한 칸 이동합니다.

 

이동 후 처리

  • 다른 플레이어가 없을 경우: 해당 칸에 총이 있는지 확인하여, 총을 획득합니다.
    • 이미 총을 가지고 있다면, 더 공격력이 높은 총을 획득하고 기존 총은 내려둡니다.
  • 다른 플레이어가 있을 경우: 두 플레이어가 싸움을 벌입니다.
    • 싸움 순서: 총을 줍기 전에 먼저 싸움을 진행합니다.

전투 규칙

  • 플레이어의 초기 능력치와 총의 공격력 합산이 더 높은 플레이어가 승리합니다.
  • 승리한 플레이어는 두 능력치의 차이만큼 포인트를 획득합니다.
  • 패배한 플레이어는 총을 해당 위치에 내려놓고, 본인이 향한 방향으로 한 칸 이동합니다.
  • 이동하려는 칸에 다른 플레이어가 있거나 격자를 벗어나면, 오른쪽으로 90도씩 회전하여 빈 칸이 보이면 그쪽으로 이동합니다.

 

전투 종료 후 

  • 패배한 플레이어는 이동한 칸에 총이 있다면 가장 공격력이 높은 총을 획득합니다.
  • 이긴 플레이어는 그 칸에 떨어진 총 중 가장 공격력이 높은 총을 선택하고, 나머지 총은 내려둡니다.
    • 패배한 플레이어의 총이 더 공격력이 높을 경우, 승리한 플레이어가 그 총을 가져갈 수 있습니다.

 

 

 

풀이

우선 플레이어와 총을 구분해서 관리하였다.

플레이어는 딕셔너리를 사용하여 관리하였고,

player = {idx: [y - 1, x - 1, d, s, 0] for idx, (x, y, d, s) in enumerate(temp_player)}

총은 한 좌표에 여러개의 총이 있을 수 있기 때문에 3차원 리스트를 이용하여 관리하였다.

arr = [[[num] for num in row] for row in temp_arr]

그리고 해당 문제에서 설명하는 (x,y) 좌표는 우리가 이해하는 (x,y)와 반대라고 생각하면 된다. (덕분에 헷갈림)

 

문제에서 설명하는대로 구현하면 되는데, 관리해야 할 변수가 너무 많다보니까 디버깅 하는데 오래걸렸다. 

구현 문제를 풀때는 정신을 똑바로 차리고, ctrl +c ctrl + v 는 사용하지 않아야겠다는 걸 또 한 번 느꼈다.

 

1. 플레이어 이동

move_player() 함수에서 구현됩니다. 각 플레이어를 순서대로 이동시키고, 이동 후 다른 플레이어와의 충돌 여부를 확인합니다.

def move_player():
    # 중략...
    for idx, (x, y, d, s, g) in player.items():
        # 이동 로직
        # 충돌 확인
        user_idx = check_user(player, nx, ny, idx)
        if user_idx != -1:
            player, arr, results = fight_player(player, idx, user_idx, arr, results)
        else:
            player[idx], arr = get_gun(player[idx], arr)

 

2. 총 휙득

get_gun() 함수에서 처리됩니다. 플레이어가 이동한 위치에 총이 있으면 가장 강력한 총을 획득합니다.

def get_gun():

	# 중략...
	
    if arr[y][x] and g < arr[y][x][0]:
        temp_ = arr[y][x]
        if g != 0:
            temp_.append(g)
        g = arr[y][x][0]
        arr[y][x] = sorted(temp_[1:], reverse=True)

 

3. 플레이어간 전투

fight_player() 함수에서 구현됩니다. 두 플레이어의 전투력을 비교하여 승자를 결정하고, 점수를 갱신합니다.

def fight_player():
	
    # 중략...

    user_1_power = user_1[3] + user_1[4]
    user_2_power = user_2[3] + user_2[4]
    if user_1_power > user_2_power:
        results[idx_1] += (user_1_power - user_2_power)
        player, arr = end_game(idx_1, idx_2, arr, player)

 

4. 전투 후 처리 

end_game() 함수에서 승자와 패자를 처리합니다. 패자는 총을 내려놓고 이동하며, 승자는 가장 강력한 총을 획득합니다.

def end_game():

	# 중략..

    # 패배자 총 내려놓기
    loser_gun = loser[4]
    loser[4] = 0
    guns = arr[y][x]
    if loser_gun:
        guns.append(loser_gun)

    # 패배자 이동
    while True:
        nx, ny = x + DX[d], y + DY[d]
        if 0 <= nx < n and 0 <= ny < n:
            if check_user(nx, ny):
                # 이동 및 총 획득
                break
        d = (d + 1) % 4

    # 승리자 총 줍기
    winner, arr = get_gun(winner, arr)

 

코드

from collections import deque
from typing import List, Tuple, Dict

# 방향 상수 (상, 우, 하, 좌)
DX = [0, 1, 0, -1]
DY = [-1, 0, 1, 0]


def read_input() -> Tuple[int, int, int, List[List[int]], List[List[int]]]:
    """입력을 받아 배열의 크기, 플레이어 정보를 반환하는 함수"""
    n, m, k = map(int, input().split())  # n: 격자 크기, m: 플레이어 수, k: 턴 수
    arr = [list(map(int, input().split())) for _ in range(n)]  # 격자 상태
    player = [list(map(int, input().split())) for _ in range(m)]  # 플레이어 상태
    return n, m, k, arr, player


def check_user(player: Dict[int, List[int]], x: int, y: int, u_idx: int) -> int:
    """같은 위치에 있는 다른 플레이어가 있는지 확인하는 함수"""
    for idx, (xx, yy, d, s, g) in player.items():
        if idx == u_idx:
            continue
        if (xx, yy) == (x, y):
            return idx
    return -1


def move_player(arr: List[List[List[int]]], player: Dict[int, List[int]], results: List[int]) -> Tuple[List[List[List[int]]], Dict[int, List[int]], List[int]]:
    """플레이어를 이동시키는 함수"""
    n = len(arr)

    for idx, (x, y, d, s, g) in player.items():
        # 한 칸 이동
        nx, ny = x + DX[d], y + DY[d]

        # 이동 가능한 경우
        if 0 <= nx < n and 0 <= ny < n:
            player[idx] = [nx, ny, d, s, g]
        # 이동 불가능한 경우 반대 방향으로 이동
        else:
            d = (d + 2) % 4
            nx, ny = x + DX[d], y + DY[d]
            player[idx] = [nx, ny, d, s, g]

        # 다른 플레이어 확인 및 싸움 처리
        user_idx = check_user(player, nx, ny, idx)
        if user_idx != -1:
            player, arr, results = fight_player(player, idx, user_idx, arr, results)
        else:
            # 총을 줍는 처리
            player[idx], arr = get_gun(player[idx], arr)

    return arr, player, results


def end_game(idx_1: int, idx_2: int, arr: List[List[List[int]]], player: Dict[int, List[int]]) -> Tuple[Dict[int, List[int]], List[List[List[int]]]]:
    """게임에서 승자와 패자를 처리하는 함수"""
    winner = player[idx_1]
    loser = player[idx_2]
    x, y = winner[0], winner[1]
    n = len(arr)

    # 패배자는 총을 내려둠
    loser_gun = loser[4]
    loser[4] = 0
    guns = arr[y][x]

    if loser_gun:
        guns.append(loser_gun)

    arr[y][x] = sorted(guns, reverse=True)

    def check_user(nx: int, ny: int) -> bool:
        """해당 좌표에 다른 플레이어가 있는지 확인"""
        for idx, (xx, yy, _, _, _) in player.items():
            if idx == idx_2:
                continue
            if (xx, yy) == (nx, ny):
                return False
        return True

    # 패배자 이동 처리
    x, y, d, s, g = loser
    while True:
        nx, ny = x + DX[d], y + DY[d]
        if 0 <= nx < n and 0 <= ny < n:
            if check_user(nx, ny):
                loser = [nx, ny, d, s, arr[ny][nx][0]] if arr[ny][nx] else [nx, ny, d, s, 0]
                arr[ny][nx] = arr[ny][nx][1:] if arr[ny][nx] else []
                break
        d = (d + 1) % 4

    # 승리자 총 줍기
    winner, arr = get_gun(winner, arr)

    player[idx_1], player[idx_2] = winner, loser
    return player, arr


def fight_player(player: Dict[int, List[int]], idx_1: int, idx_2: int, arr: List[List[List[int]]], results: List[int]) -> Tuple[Dict[int, List[int]], List[List[List[int]]], List[int]]:
    """플레이어 간 싸움 처리"""
    user_1 = player[idx_1]
    user_2 = player[idx_2]

    user_1_power = user_1[3] + user_1[4]  # 사용자 1의 총합 전투력
    user_2_power = user_2[3] + user_2[4]  # 사용자 2의 총합 전투력

    if user_1_power > user_2_power:
        results[idx_1] += (user_1_power - user_2_power)
        player, arr = end_game(idx_1, idx_2, arr, player)
    elif user_1_power < user_2_power:
        results[idx_2] += (user_2_power - user_1_power)
        player, arr = end_game(idx_2, idx_1, arr, player)
    else:
        if user_1[3] > user_2[3]:
            player, arr = end_game(idx_1, idx_2, arr, player)
        else:
            player, arr = end_game(idx_2, idx_1, arr, player)

    return player, arr, results


def get_gun(user: List[int], arr: List[List[List[int]]]) -> Tuple[List[int], List[List[List[int]]]]:
    """플레이어가 해당 위치에서 총을 줍는 함수"""
    x, y, d, s, g = user

    if arr[y][x] and g < arr[y][x][0]:
        temp_ = arr[y][x]
        if g != 0:
            temp_.append(g)
        g = arr[y][x][0]
        arr[y][x] = sorted(temp_[1:], reverse=True)
        user = [x, y, d, s, g]

    return user, arr


def main() -> None:
    """메인 함수: 시뮬레이션 실행 및 결과 출력"""
    n, m, k, temp_arr, temp_player = read_input()
    results = [0] * m

    # 총이 여러 개 있을 수 있으므로 arr 배열을 다차원 리스트로 변환
    arr = [[[num] for num in row] for row in temp_arr]

    # 플레이어를 dict로 관리
    player = {idx: [y - 1, x - 1, d, s, 0] for idx, (x, y, d, s) in enumerate(temp_player)}

    # k번의 턴 동안 플레이어 이동 및 처리
    for _ in range(k):
        arr, player, results = move_player(arr, player, results)

    print(" ".join(map(str, results)))


if __name__ == "__main__":
    main()

 

ariz1623