[Algospot]BLOCKGAME

1 분 소요

해결

무작정 시도

현재 차례의 사람이 이기면 1을 반환하고 아니면 0을 반환하는 식으로 코드를 구현하려 했다. 하나라도 이기는 경우가 있으면 이기는 것, 즉 이기는 경우가 없으면 지는것이다.

기본값을 0로 두었다가 반복문에서 이기는 경우가 안나오면 지도록 설정했다. 그리고 비트 마스크 없이 배열로 맵을 표현한뒤 해결하려 했다. 한 칸씩 움직이며 현재 위치가 비었을때 블록을 배치하려 했으나 코드가 너무 복잡해져서 포기하고 비트 마스크를 이용하기로 했다.

비트마스크 이용

비트 마스크를 이용하면 반복문을 이용하여 한칸씩 비교할 필요가 없어진다. 또한 맵 전체에 대한 블록들을 만들어 놓고 현재 맵과 & 연산자를 이용하여 배치 가능한지 쉽게 파악할 수 있다.

실제 구현시 아래와 같은 방법을 이용했다.

  • 맵 전체에 대해 놓을 수 있는 블록들을 비트로 표현하여 moves에 저장해 놓는다.
  • L자 블록을 만들때 2x2 셀에서 한칸씩 제거하며 블록들을 저장한다.
for(int y=0;y<4;++y)
    for(int x=0;x<4;++x)
    {
        vector<int> cells;
        for(int dy=0;dy<2;++dy)
            for(int dx=0;dx<2;++dx)
                cells.push_back(cell(y+dy,x+dx));
        //2X2 꽉차있는 셀에서 하나씩 빼며 만든다.
        int square=cells[0]+cells[1]+cells[2]+cells[3];
        for(int i=0;i<4;++i)
            moves.push_back(square-cells[i]);
    }

승리조건

  • 현재 차례에 둘 곳이 더이상 없으면 패배한다.
  • 상대방이 지면 내가 승리한다.
  • 현재 차례에 신의한수가 존재하면 이긴다.

즉, 현재 하나의 경우라도 승리하는 경우가 있다면 승리하는 것이다.

구현

코드는 아래와 같다.

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

vector<int> moves;
int n;
char cache[1<<25];

// 셀의 위치(y,x)를 비트로 변환(y*5+x) 해준다.
inline int cell(int y,int x)
{
    return 1<<(y*5+x);
}

// 블록이 있으면 비트가 켜진다.
void init(int& board)
{
    for(int y=0;y<5;++y)
        for(int x=0;x<5;++x)
        {
            char tmp; cin>>tmp;
            if(tmp=='#')
                board|=cell(y,x);
        }
    memset(cache,-1,sizeof(cache));
}

void preCalc(void)
{
    //4칸 블록 계산
    for(int y=0;y<4;++y)
        for(int x=0;x<4;++x)
        {
            vector<int> cells;
            for(int dy=0;dy<2;++dy)
                for(int dx=0;dx<2;++dx)
                    cells.push_back(cell(y+dy,x+dx));
            //2X2 꽉차있는 셀에서 하나씩 빼며 만든다.
            int square=cells[0]+cells[1]+cells[2]+cells[3];
            for(int i=0;i<4;++i)
                moves.push_back(square-cells[i]);
        }
    //2칸 블록 계산
    for(int i=0;i<5;++i)
        for(int j=0;j<4;++j)
        {
            moves.push_back(cell(i,j)+cell(i,j+1));
            moves.push_back(cell(j,i)+cell(j+1,i));
        }
}

char play(int board)
{
    char& ret=cache[board];
    if(ret!=-1)
        return ret;
    
    ret=0;
    for(int i=0;i<moves.size();++i)
        // 배치가능할때
        if((board & moves[i]) == 0)
            // 상대가 이길 수 없으면 내가 이긴 것이다.
            if(!play(board | moves[i]))
            {
                ret=1;
                break;
            }
    
    return ret;
}

int main(void)
{
    int testCase; cin>>testCase;
    
    while(testCase--)
    {
        int board=0;
        
        init(board);
        preCalc();
        
        if(play(board))
            cout<<"WINNING"<<endl;
        else
            cout<<"LOSING"<<endl;
    }
    
    return 0;
}

회고

교재에 있는 코드는 정말 예술적이다. 미리 블록들을 배치시켜 놓는다는 발상은 코드를 완전 뒤바꿨다.

댓글남기기