TICTACTOE문제 질문입니다.

  • canuyes
    canuyes

    TICTACTOE문제

    안녕하세요.
    현재 TICTACTOE문제를 풀고 있습니다.
    책을 보면서 문제를 풀고, 혼자 힘으로 풀어보고 있습니다.
    책에 나와 있는 bijection 계산 부분이 맘에 들지 않아(?)
    보드의 상태를 0~19682 범위의 숫자만을 전달하고, 풀어냅니다.

    #include<iostream>
    #include<cstring>
    #define THREE_9 19683
    
    using namespace std;
    
    int V,CACHE[THREE_9],POW3[9]={1,3,9,27,81,243,729,2187,6561};
    
    //보드의 초기 상태를 받아서 그 상태를 숫자로 리턴합니다.(0~19682 범위)
    //player 에는 보드의 초기 상태에서 이번이 누구의 차례인지가 결정됩니다.
    //o : 1, x : 2 입니다.
    int input(int& player){
        int i,value=1,ret=0,c=0;
        char temp;
        for(i=0;i<9;i++){
            cin>>temp;
            if(temp=='.'){ret+=(0*value);}
            else if(temp=='o'){ret+=(1*value);c++;}
            else{ret+=(2*value);c++;}
            value*=3;
        }
        if(c%2==1){player=1;}
        else{player=2;}
        return ret;
    }
    
    //다음 플레이어를 리턴합니다.
    //1->2, 2->1로 리턴합니다.
    int nexp(int player){if(player==1){return 2;}return 1;}
    
    //승자가 결정됬는지를 검사합니다.
    //승자가 결정됬다면 true, 아니라면 false 입니다.
    bool decision(int S){
        if((S/POW3[0])%10!=0&&(S/POW3[0])%10==(S/POW3[1])%10&&(S/POW3[0])%10==(S/POW3[2])%10){return true;}
        if((S/POW3[3])%10!=0&&(S/POW3[3])%10==(S/POW3[4])%10&&(S/POW3[3])%10==(S/POW3[5])%10){return true;}
        if((S/POW3[6])%10!=0&&(S/POW3[6])%10==(S/POW3[7])%10&&(S/POW3[6])%10==(S/POW3[8])%10){return true;}
        if((S/POW3[0])%10!=0&&(S/POW3[0])%10==(S/POW3[3])%10&&(S/POW3[0])%10==(S/POW3[6])%10){return true;}
        if((S/POW3[1])%10!=0&&(S/POW3[1])%10==(S/POW3[4])%10&&(S/POW3[1])%10==(S/POW3[7])%10){return true;}
        if((S/POW3[2])%10!=0&&(S/POW3[2])%10==(S/POW3[5])%10&&(S/POW3[2])%10==(S/POW3[8])%10){return true;}
        if((S/POW3[0])%10!=0&&(S/POW3[0])%10==(S/POW3[4])%10&&(S/POW3[0])%10==(S/POW3[8])%10){return true;}
        if((S/POW3[2])%10!=0&&(S/POW3[2])%10==(S/POW3[4])%10&&(S/POW3[2])%10==(S/POW3[6])%10){return true;}
        return false;
    }
    
    //보드가 꽉 찼는지를 검사합니다.
    //보드가 꽉 찼다면 true, 아니라면 false 입니다.
    bool isFull(int S){
        int i,ret=true;
        for(i=0;i<9;i++){if((S/POW3[i])%10==0){ret=false;break;}}
        return ret;
    }
    
    //보드의 상태가 S이고 이번이 P의 차례일때, 이길 수 있는지를 리턴합니다.
    //TIE : 0, WIN : 1, LOSE : 2 입니다.
    int canWin(int P,int S){
        int i,temp,&ret=CACHE[S];
        if(ret==-1){
            //승자가 결정 된 상태라면 이번 차례의 플레이어는 무조건 집니다.
            if(decision(S)){ret=2;}
            //승자가 경정 되지 않은 상태에서, 보드가 꽉 차있다면 비긴 경우입니다.
            else if(isFull(S)){ret=0;}
            //그 외의 경우들 입니다.
            //보드의 각 빈 위치에 수를 두었을때,
            //다음 차례의 플레이어 필패 경우 -> 필승이므로 ret에 WIN을 메모이제이션한 후 for문을 나옵니다.
            //다음 차례의 플레이어의 최적 수가 비기는 경우 -> ret에 TIE를 저장 후 for문을 마저 순회 합니다.
            //그 외의 경우 -> LOSE 입니다.
            else{
                ret=2;
                for(i=0;i<9;i++){
                    if((S/POW3[i])%10==0){
                        temp=canWin(nexp(P),S+P*POW3[i]);
                        if(temp==2){ret=1;break;}
                        if(temp==0){ret=0;}
        }}}}
        return ret;
    }
    
    int main(void){
        int T,P;
        cin>>T;
        while(T--){
            V=input(P);
            memset(CACHE,-1,sizeof(int)*THREE_9);
            cout<<canWin(P,V)<<endl;
        }
        return 0;
    }
    

    다른 함수들의 간단한 오류는 확인하였고, 아마 canWin함수 부분에 오류가 있는 듯 합니다.
    책과 다른 점이라고는,
    1. 보드 대신 숫자를 전달하는 점,
    2. 승무패를 min함수가 아닌 for문 순회로 찾는점.
    뿐인데 자꾸 틀린 값을 내놓는 이유가 무었인가요?
    답변 기다립니다.


    10년 전
3개의 댓글이 있습니다.
  • Being
    Being

    decision()에서 왜 10으로 나눈 나머지를 쓰는지 잘 모르겠는데 의도를 설명해 주실 수 있나요?


    10년 전 link
  • canuyes
    canuyes

    답변 정말 감사드립니다.
    decision부분에 오류가 있었네요 ㅠㅠ.
    모듈로 10이 아니라 모듈로 3을 해줬어야 하네요...
    잘못 생각한부분
    감사합니다 ^^


    10년 전 link
  • Being
    Being

    네 ㅎㅎ 저렇게 인코딩된 정수를 가지고 직접 조작하는 것보다 접근하기 쉬운 판의 상태로 디코딩해 쓰셨다면 실수가 줄어들었으리라 생각합니다.


    10년 전 link
  • 정회원 권한이 있어야 커멘트를 다실 수 있습니다. 정회원이 되시려면 온라인 저지에서 5문제 이상을 푸시고, 가입 후 7일 이상이 지나셔야 합니다. 현재 문제를 푸셨습니다.