본문 바로가기

기타 잡 코딩

폴란드 테트리스를 플레이하는 AI를 만드는 방법(2)

지난 시간에 이어서 글을 쓴다.

 

python으로 얻어진 스크린샷에서 필요한 정보들을 얻어보자.

가장 먼저, 현재 주어진 블록의 종류를 알아내보자.

 

맨 윗줄을 조사하면 주어진 블록의 종류를 알아낼 수 있다.

 

살짝 보이는 맨 윗줄을 crop한다.

 

그냥 생각 나는 대로 블록 종류를 판단하는 알고리즘을 다음과 같이 짰다.

 

1. 한 줄은 10칸으로 이루어져 있다. 쉽게 전처리할 수 있도록 10x1로 리사이즈한다.

2. 리사이즈된 이미지는 RGB이다. RGB의 평균값으로 블록의 종류를 특정할 수도 있지만, 혹시 모르니 HSV 로 바꾸고, Hue(색조)값만 뽑는다.

3. 5번째 칸은 항상 블록의 일부가 포함되어있으므로 5번째 칸의 값을 Hue 값을 블록별로 정리한다. 

color_block_map={
        43:'S_BLOCK',
        20:'L_BLOCK',
        26:'O_BLOCK',
        101:'I_BLOCK',
        143:'T_BLOCK',
        114:'J_BLOCK',
        0:'Z_BLOCK'
    }

4. Hue 값에 따라, 블록의 종류를 결정한다.

 

이제 현재 블록의 종류 정도는 쉽게 구할 수 있게 되었다.

 

 

이제 블록의 종류를 알았으니 다음에 해봐야할 건 어디에, 어떤 모양으로 넣어야하는 지 결정해야하는 일이다.

 

알다시피, 테트리스 게임의 목적은 한 줄을 빼곡히 채우는 것이다.

 

간단한 룰인 만큼, 대충 생각해서 그냥 다음과 같은 알고리즘을 짰었다.

 

1. 현재 게임화면을 전처리해서 20x10짜리 배열로 만들고 채워져있는 칸을 1, 비워져 있는 칸을 0으로 정의한다.

2.  맵을 더욱 단순화하기 위해서 각 보드열의 높이를 저장하는 1x10 배열인 heights를 정의한다.

3. 블록별로 각 heights를 채울 수 있는 위치에 채울 수 있는 모양을 변형하여 블록을 채운다.

 

 

1번 2번은 굉장히 명확하고 쉬운 작업이지만 3번은 약간의 노력이 더 필요하다.

 

"블록을 채운다"는 말을 코딩하기 쉽게 변형하자면, 블록을 아래로 쭉 내렸을때 블록 바로 아래칸들에 빈칸이 남지 않게 채운다는 뜻이 될 수 있다.

I 블록을 이렇게 두면 아래쪽에 빈칸이 생길 수밖에 없다

위와 같이 둔다면 모든 칸을 메울 수 있다.

저 상태의 heights는 2,2,2,3,1,2,0,0,0,0 이라고 볼 수 있다.

일반화하면 연속된 4개 칸의 높이값이 모두 같은 곳에 I block을 눕혀서 놓을 수 있다는 결론.

    if current_block=='I_BLOCK':
        for i in range(7):
            if all(heights[i:i+4]==heights[i]):
                actions.append((0, -3+i, heights[i]))

코딩하면 대충 위처럼 간단하다. (언급안한 부분은 대충 넘어가자;;)

 

나머지 블록의 경우도 I block처럼 일반화할 수 있을 거라고 본다.(매우 심한 노가다)

 

본인은 actions 리스트에 빈칸 없이 블록을 둘 수 있는 모든 경우의 수를 저장했다.

 

action은 (0, -3+i, heights[i]) 처럼, 총 3개의 값으로 이루어져 있다.

 

 

첫번째 0은 회전해야하는 수

 

두번째 -3+i는 오른쪽으로 이동해야하는 수

 

세번째 heights[i]는 블록이 내려갈 열(column)들 중에 가장 낮은 height을 나타낸다.

 

 

세번째 값은 가장 좋은 action을 정하기 위해서 저장했다.

 

기왕이면 가장 낮은 층부터 채우는 게 좋지 않겠는가?

 

그리하여, 세번째 값이 가장 낮은 최고의 action을 구하고 해당하는 command를 실행하도록 만들었다.

 

 

키보드 입력을 시뮬레이션 하기 위해서 keyboard 파이썬 라이브러리가 필요하다.

 

PressKey, ReleaseKey 함수 등을 통해서 키 입력을 시뮬레이션 한다.

 

본인은 키입력에 걸리는 시간을 최적화 하기위해서 ctypes에서 제공하는 함수들을 외부 사이트에서 복사해서 사용했다.

 

 

이제 성능을 확인해보자.

 

 



AI(?)가 아주 잘 플레이하는 것을 확인할 수 있다.

 

사실, 너무 단순한 알고리즘이라 보완해야하는 경우의 수가 종종 생긴다.

 

알고리즘 자체도 문제지만, 스크린샷 입력, 키 입력 부분에서 의외로 상당한 시간이 걸렸기 때문에

 

테트리스 고인물만큼 빠른 속도는 내지 못했다. 

 

아직 부족한 부분이 많다. 해결 방법은 여러 개 생각났지만 공간이 부족하여 적지 않겠다.