알고리즘

[삼성 코테 기출 - 시뮬레이션] 루돌프의 반란

김치말이삼겹살 2024. 4. 13. 02:00

 

https://www.codetree.ai/training-field/frequent-problems/problems/rudolph-rebellion/description?page=1&pageSize=20

 

 

import sys

sys.stdin = open('input.txt', 'r')

# rudolph's dxs, and dys
dxs = [0, 1, 1, 1, 0, -1, -1, -1]
dys = [1, 1, 0, -1, -1, -1, 0, 1]

# santa's dxs, and dys
sdxs = [-1, 0, 1, 0]
sdys = [0, 1, 0, -1]


def select_santa(cow):
    '''

    :param cow: 현재 루돌프의 위치를 넣는다. [x,y] 형태
    :return: 선택한 산타의 인덱스와, 방향 리턴하지 않을까? -> 아니네... 산타의 위치를 함께 리턴한다.
    코드 자체는 그리 도움될 것 같지는 않은데... 뭐랄까?
    코드를 읽기 쉽게 짜시려고 한 것 같음. 나라면 방향 리턴할 듯?
    '''
    r1, c1 = cow
    close_dis = int(1e9)
    # 산타 id와, 위치 초기화
    select_santa_num = 0
    select_santa_loc = [0, 0]
    # santa 딕셔너리 순회
    for i in santa:
        # i번째 산타의 row, col 입력받음
        r2, c2 = santa[i]
        # 크기
        dis = (r1 - r2) ** 2 + (c1 - c2) ** 2
        # 작으면
        if dis < close_dis:
            # 현재 번호로 업데이트
            select_santa_num = i
            select_santa_loc = [r2, c2]
            close_dis = dis
        # 같으면
        elif dis == close_dis:
            # 현재의 row가 더 작으면
            if r2 > select_santa_loc[0]:
                select_santa_num = i
                select_santa_loc = [r2, c2]
                close_dis = dis
            # 현재의 col이 더 작으면
            elif r2 == select_santa_loc[0]:
                if c2 > select_santa_loc[1]:
                    select_santa_num = i
                    select_santa_loc = [r2, c2]
                    close_dis = dis

    return select_santa_num, select_santa_loc


def Interaction(crush_new_santa_num, dict_x, dict_y):
    '''
    상호작용을 다룬 함수
    :param crush_new_santa_num: 들이 받혔을 때 산타의 위치 [x,y]
    :param dict_x: x의 위치
    :param dict_y: y의 위치
    :return: 따로 없음
    '''
    #들이 받힌 산타의 위치
    x, y = santa[crush_new_santa_num]
    # 새로운 y로 만들어.
    new_x = x + dict_x
    new_y = y + dict_y

    if not in_range(new_x, new_y):
        del santa[crush_new_santa_num]  # 보드 밖으로 나감
        faint[crush_new_santa_num] = 0
    else:
        TF, p_num = check(crush_new_santa_num, new_x, new_y)  # 새 위치에 산타가 있는지 없는지 확인
        if not TF:  # 있는경우 상호작용
            Interaction(p_num, dict_x, dict_y)

        santa[crush_new_santa_num] = [new_x, new_y]  # 상호작용이랑 상관없이 이동할건 해야지


def cow_crash(cow_loc, x1, y1, p_num, x2, y2):
    '''

    :param cow_loc: 기존 루돌프의 위치: [x,y]
    :param x1: 바뀐 루돌프의 위치
    :param y1: 바뀐 루돌프의 위치
    :param p_num: 들이 받힌 산타의 id
    :param x2: 산타의 위치
    :param y2: 산타의 위치
    :return: 사실 리턴할 것은 없어보인다.
    '''
    # 우선 들히 받힌 산타의 점수 C만큼 올려주고
    santa_score[p_num] += C
    # 방향을 받는다. -> 현재 위치 - 기존 위치
    dict_x = x1 - cow_loc[0]
    dict_y = y1 - cow_loc[1]
    # x2와 y2에 각각 C 만큼 더해준다.
    x2 += dict_x * C
    y2 += dict_y * C
    # 이것도 좋은 함수 인듯? -> 함수명의 중요성
    # x2,y2가 범위내에 없으면
    if not in_range(x2, y2):
        del santa[p_num]  # 보드 밖으로 나감 -> 그냥 딕셔너리에서 해당 value값 삭제해버림.
        # del 문법은 또 처음 보네
        # 죽어랏!! -> faint는 0 아니면 2인가?
        faint[p_num] = 0
    else:
        faint[p_num] = 2  # 기절  # 두턴뒤에 풀림 -> 얘는 산타 턴 돌면 무조건 1 까이니까 2로 둔 것 같다 !!!
        TF, crush_new_santa_num = check(p_num, x2, y2)
        if not TF:
            # 상호작용 crush_new_santa_num
            # 또 들이 받힌 산타가 있을 것 아녀
            Interaction(crush_new_santa_num, dict_x, dict_y)

        santa[p_num] = [x2, y2]  # 상호작용이랑 상관없이 이동할건 해야지


def santa_crash(p_num, x1, y1):
    '''

    :param p_num: 산타의 id
    :param x1: 산타의 위치
    :param y1: 산타의 위치
    :return:
    '''
    # 현재 산타의 점수에 D만큼 더함
    santa_score[p_num] += D
    # 아... 아직 업데이트를 안했구나...
    dict_x = santa[p_num][0] - x1
    dict_y = santa[p_num][1] - y1

    x1 += dict_x * D
    y1 += dict_y * D

    if not in_range(x1, y1):
        del santa[p_num]  # 보드 밖으로 나감
        faint[p_num] = 0
    else:
        faint[p_num] = 1  # 기절  # 두턴뒤에 풀림 -> 근데 얘는 왜 1로 하고, 아까꺼는 2로 했지? -> 얘는 바로 돌며 ㄴ되니까..
        TF, crush_new_santa_num = check(p_num, x1, y1)
        if not TF:
            # 상호작용 crush_new_santa_num
            Interaction(crush_new_santa_num, dict_x, dict_y)

        santa[p_num] = [x1, y1]  # 상호작용이랑 상관없이 이동할건 해야지


def in_range(x, y):
    return 0 < x < N + 1 and 0 < y < N + 1


def check(p_num, x1, y1):
    '''

    :param p_num: 산타 id
    :param x1: 산타 x값
    :param y1: 산타 y값
    :return: 겹치는 다른 산타가 있는지 여부, 그 index
    '''
    for i in santa:
        if i == p_num:
            continue
        if santa[i] == [x1, y1]:
            return False, i
    return True, 0


def santa_move_rule(p_num, x1, y1, x2, y2):
    '''

    :param p_num: 산타 id
    :param x1: 산타 위치
    :param y1: 산타 위치
    :param x2: 루돌프 위치
    :param y2: 루돌프 위치
    :return: 업데이트 한 산타의 위치
    '''
    dis = (x1 - x2) ** 2 + (y1 - y2) ** 2
    new_x, new_y = x1, y1
    # for dx, dy in zip(sdxs, sdys):
    for i in range(4):
        nx = x1 + sdxs[i]
        ny = y1 + sdys[i]

        if in_range(nx, ny):
            new_dis = (nx - x2) ** 2 + (ny - y2) ** 2
            # 거리 줄었나?
            if new_dis < dis:
                # 다른 산타 있는지 없는지 여부
                TF, _ = check(p_num, nx, ny)
                if TF:
                    # 없어야 이동.
                    dis = new_dis
                    new_x, new_y = nx, ny
    return new_x, new_y


def cow_move_rule(x1, y1, x2, y2):
    '''
    갠적으로 미;친 거 아니신지... 하는 코드이다. -> 이게 될 수 있는 이유?? 루돌프가 8개 방향 다 쓸 수 있어서임.
    :param x1: 루돌프 x
    :param y1: 루돌프 y
    :param x2: 산타 x
    :param y2: 산타 y
    :return: 바뀐 루돌프 위치 [x1,y1] 이 되어야 하지 않을까?
    '''
    # 거리 가까워지기 위함. 우선 x부터
    if x1 > x2:
        # 크면 x를 빼고
        x1 -= 1
    elif x1 < x2:
        # 작으면 x를 더해줌
        x1 += 1
    else:
        # 같으면 현상 유지
        x1 = x1
    # y의 경우에도
    if y1 > y2:
        #y1이 크면 y1를 1 뺴줌
        y1 -= 1
    # y1이 작으면
    elif y1 < y2:
        # y1을 1 더해줌.
        y1 += 1
    else:
        y1 = y1

    return x1, y1


# 루돌프 움직임 추적
def cow_move(cow_loc):
    '''

    :param cow_loc: 입력받은 루돌프 위치 [x,y] 리스트 형태로 되어 있음
    :return: 변경한 루돌프 위치. [x,y] 형태.
    '''
    # cow 위치 받고
    x1, y1 = cow_loc
    # 산타 고른다.
    p_num, [x2, y2] = select_santa(cow_loc)
    # 고른 산타대로 이동 -> x1,y1 => 루돌프 위치, x2,y2 => 산타 위치
    # 바뀐 루돌프 위치 받고
    x1, y1 = cow_move_rule(x1, y1, x2, y2)

    # 만약 루돌프가 산타랑 충돌할때
    if [x1, y1] == [x2, y2]:
        # 루돌프로 충돌 조건.
        cow_crash(cow_loc, x1, y1, p_num, x2, y2)
    # 충돌 -> 상호작용까지 마무리 후 종료
    return [x1, y1]


# 산타 움직임
def santa_move(cow_loc, k):
    '''

    :param cow_loc: 루돌프의 위치 [x2,y2]
    :param k: 의미 없음.
    :return: 없음. -> 산타 배열로 처리하므로.
    '''
    x2, y2 = cow_loc
    for i in range(1, P + 1):
        # santa의 해당 인덱스에 머가 없으면
        if santa.get(i) == None:
            continue
        # faint위치에 서 1 빼주고. 근데 이러면 1 빼줘야 하는 거 아닌가?
        if faint[i] != 0:  # 기절한 산타는 움직임 없음
            faint[i] -= 1
            continue
        # 현 산타의 위치
        x1, y1 = santa[i]
        # 산타의 move_rule 보러 가자 -> 바뀐 산타 위치
        x1, y1 = santa_move_rule(i, x1, y1, x2, y2)

        # 산타가 움직였는데 루돌프랑 부딪칠 경우
        if [x1, y1] == [x2, y2]:
            santa_crash(i, x1, y1)
        else:
            # 아니면 걍 산타 위치~
            santa[i] = [x1, y1]


if __name__ == "__main__":
    # main 문 안으로 들어옴으로써, 얘네가 전역 변수로 인정 받게 되는 느낌인건가보네
    # 이런 코드 패턴 짜야겠다 !!! -> c에서의 메인 함수 느낌인가보네...
    # 만약 이거 없었으면, santa dictionary, n, m, p, c, d, faint, santa_score 다 전역 변수로 받아야겠네??
    # N:게임격자, M: 게임턴수, P:산타개수, C:루돌프 힘, D: 산타의 힘
    N, M, P, C, D = map(int, input().split())
    # 루돌프 위치 받기 -> 루돌프 위치를 리스트로 받은 이유는?
    # 쌍으로 관리하고자 + 값 변경 용이하게 하고자 !!
    cow_loc = list(map(int, input().split()))
    # 딕셔너리로 관리
    santa = {}
    # 이건 좀 좋은 아이디어 같음 -> 키에 대한 값들을 쉽게 받을 수 있어서
    for _ in range(P):
        p, p_x, p_y = map(int, input().split())
        santa[p] = [p_x, p_y]

    # 기절 상태 기록 -> P + 1인 이유? 머겠나요 1번부터 p번까지 저장하려고지
    faint = [0] * (P + 1)
    santa_score = [0] * (P + 1)

    # 이건 왜한거지?? 의미 없어보이는 변수 선언이다.
    # total_score = 0
    # 게임 플레이수 M
    for k in range(1, M + 1):
        # 루돌프 움직이는 것.
        cow_loc = cow_move(cow_loc)

        if len(santa) == 0:
            break

        # 1번 산타부터 P번산타까지 산타들 움직임 -> 산타 움직임
        santa_move(cow_loc, k)

        if len(santa) == 0:
            break
        # 모든 산타에 대해 점수+1
        for pid in santa:
            santa_score[pid] += 1

    print(*santa_score[1:])

 

 

내가 손 댄 코드는 1도 없어서 부끄러운데... 그래도 일단 올립니다...

 

모듈화가 진짜 중요하다는 것을 느낀 코드였고...

 

작년 오후 1번이었다는데 나 이거 풀 수 있으려나 ???

 

input, output 바로 팅팅 안 나오는 게 당연함 !! 그런거 디버깅하라고 4시간이나 주는 거겠지...

 

if __name__ == "__main__" 기억해두고 전역변수 최대한 안 쓰는 방향으로 코드 짜보면 좋을 것 같기도 ???? ㅎㅎㅎㅎ

 

내일 일찍 일어나서 코테 공부해야하니까 오늘은 이만... 자야겠당

 

내일은 아침 9시부터 코테 시작해서 오후 1시까지 코드트리 기출 1문제 붙잡는 게 목표 !!

 

그리고 그 이후는 기초 dfs, bfs 문제랑 구현 1문제씩 풀고 풀었던 거 복습하고 !!

 

마지막으로 순열, 조합 구현하는 거 코드짜기 4번 연습하고 자야겠당 ㅎㅎ

반응형