[Programmers/Lv. 1] 크레인 인형뽑기 게임 (2019 KAKAO 개발자 겨울 인턴십, Python)

2023. 11. 6. 21:54Algorithm

📎 링크


 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

🤔 문제설명, 제한사항, 입출력 예


"크레인 인형뽑기 게임"의 로직을 구현하는 문제다.

게임은 1x1 크기의 칸으로 이루어진 NxN 크기의 정사각 격자로 이루어진 화면에서 이루어지며, 위쪽에 인형을 잡기 위한 크레인과 크레인으로 잡은 인형을 담을 별도의 바구니가 있다.
모든 인형은 1x1 크기의 격자 한 칸을 차지하며, 격자 아래 칸부터 차곡차곡 쌓여있는 모습을 띤다.
사용자는 크레인을 좌우로 움직여서 가장 위에 있는 인형을 하나 집어 올릴 수 있으며, 이 집어 올린 인형은 바구니의 가장 아래 칸부터 차근차근 쌓이게 된다.

바구니에 만약 같은 모양의 인형 두 개가 연속해서 쌓이면, 오른쪽 아래에 보이는 모습처럼 두 인형이 사라지게 된다.

크레인으로 인형을 놓치는 경우는 여기서 고려하지 않으며, 인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 인형을 잡지 못하므로 아무런 일이 일어나지 않는다고 가정한다.
또한, 바구니는 격자와 상관없이 모든 인형이 들어갈 수 있을 만큼 충분히 크다고 가정한다.

게임 화면의 격자 상태는 2차원 배열 board로 주어지며, 인형을 집기 위해 크레인을 작동시킨 위치가 담긴 moves 배열도 주어질 때, 
moves 배열에 담긴 크레인 작동을 모두 완료한 후 터트려져 사라진 인형의 개수를 구하라는 문제다.

*게임 화면의 격자는 5 x 5 이상, 30 x 30 이하이다.
**게임 화면의 격자를 나타낸 2차원 배열 board 안에는 0~100 사이의 정수가 담겨있는데, 0은 빈 공간을 의미하고 1~100 숫자는 각기 다른 인형을 의미한다고 생각하면 되겠다. (만약 같은 숫자일 경우 서로 터트려질 수 있는 같은 인형인 셈이다.)

왼쪽 기본 상태에서 크레인을 [1, 5, 3, 5, 1, 2, 1, 4]에 맞게 작동시키는 경우의 예시 화면이다.

 

🤔 내가 생각한 풀이 과정


그냥 크게 고민할 것 없이 생각나는 순서대로 써보겠다.

moves 배열을 반복하고 (크레인이 어떻게 움직이는지 알아야 하니까),
-> moves 요소에 해당하는 인덱스 열이 0이 아닐 때 (인형이 있을 때까지 아래로 계속 내려가는 느낌)까지 계속 찾아가고,
-> 인형이 있으면 배열 해당 위치의 값을 0으로 바꿔주고 (인형을 위에서 꺼냈으니 해당 위치는 빈 공간이 된다),
-> 꺼낸 인형의 (1~100 사이) 값을 별도의 out_board 배열 (꺼낸 인형을 담아두는 바구니)에 append 해주고,
-> 바구니의 마지막 두 요소를 비교해서 두 값이 같으면, 즉 같은 인형이라면 두 요소를 지우고 return 값을 2씩 더해주는 형태.

단, 여기서 나는 내가 조금 덜 헷갈리기 위해서
[[0,0,0,0,0],[0,0,1,0,3],[0,2,5,0,1],[4,2,4,4,2],[3,5,1,3,1]] 형태로 되어있는 배열을 [[0,0,0,4,3],[0,0,2,2,5],[0,1,5,4,1],[0,0,0,4,3],[0,3,1,2,1]] 형태로 행과 열을 zip과 map를 사용해서 뒤집어줬다.
이렇게 뒤집으면, 크레인이 찾는 위치를 앞 인덱스로, 인형이 있는 곳까지 내려가는 위치를 뒤 인덱스로 풀 수 있었다.
(만약 기존 배열을 그대로 사용하고 싶다면, 앞 인덱스와 뒤 인덱스를 접근하는 로직만 변경하면 되겠다.)

 

👨‍💻 코드


def solution(board, moves):
    # 뽑은 인형을 쌓아두는 공간
    # 첫 경우에 대해 out of range를 방지하며 비교하기 위해 0 요소를 추가해둠
    out_board = [0]
    
    # 행, 열을 뒤집어서 리스트에 다시 저장
    board = list(map(list, zip(*board)))
    answer = 0
    
    for move in moves:
        # 뒤집은 board 리스트에 대해 index값으로 접근
        for i, b in enumerate(board[move-1]):
            # 요소가 0이 아닌 경우에만 인형을 뽑도록 조건을 추가 (요소 0 = 인형이 없다는 뜻)
            if b != 0:
                out_board.append(board[move-1][i])      # 인형을 꺼내서 out_board stack에 추가
                board[move-1][i] = 0                    # 꺼낸 곳은 인형이 없으니 0으로 변경
                
                # out_board 스택 비교
                # 가장 마지막 요소와 마지막에서 두 번째 요소가 같은 경우에만 두 요소를 꺼내고, answer값을 2 더해줌
                if out_board[-1] == out_board[-2]:
                    answer += 2
                    out_board.pop()
                    out_board.pop()
                    
                # 인형을 한번 꺼냈으면 다른 반복으로 넘어가도록 함
                break
                
    # 지금까지 꺼낸 모든 인형의 개수를 반환
    return answer