TICTACTOE문제 질문입니다.

  • User Avatar
    canuyes

    TICTACTOE문제

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


    ~~~ c++
    #include
    #include
    #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;
    }

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

    12년 전
3개의 댓글이 있습니다.
  • User Avatar
    Being

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


    12년 전 link
  • User Avatar
    canuyes

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


    12년 전 link
  • User Avatar
    Being

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


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