如何通过最大数量的井字游戏可能性

How to go through the maximum amount of Tic Tac Toe possibilities?

本文关键字:游戏 可能性 何通过 最大数      更新时间:2023-10-16

我想知道有多少种Tic Tac Toe的可能性,所以我在网上搜索了一下,发现了一个数学定理,说Tic Tac Toe有255168种可能的游戏。

网站:http://www.se16.info/hgb/tictactoe.htm

所以我想,我可以写一个程序看看计算机能多快地完成每一种可能性,然后我写了这段代码:

#include <iostream>
#include <time.h>
#include <windows.h>
#include <stdio.h>
using namespace std;
typedef int TTTField[9];
bool checkIfWon(TTTField j){
    if(j[0]==1&&j[1]==1&&j[2]==1) return true;
    if(j[0]==2&&j[1]==2&&j[2]==2) return true;
    if(j[3]==1&&j[4]==1&&j[5]==1) return true;
    if(j[3]==2&&j[4]==2&&j[5]==2) return true;
    if(j[6]==1&&j[7]==1&&j[8]==1) return true;
    if(j[6]==2&&j[7]==2&&j[8]==2) return true;
    if(j[0]==1&&j[3]==1&&j[6]==1) return true;
    if(j[0]==2&&j[3]==2&&j[6]==2) return true;
    if(j[1]==1&&j[4]==1&&j[7]==1) return true;
    if(j[1]==2&&j[4]==2&&j[7]==2) return true;
    if(j[2]==1&&j[5]==1&&j[8]==1) return true;
    if(j[2]==2&&j[5]==2&&j[8]==2) return true;
    if(j[0]==1&&j[4]==1&&j[8]==1) return true;
    if(j[0]==2&&j[4]==2&&j[8]==2) return true;
    if(j[2]==1&&j[4]==1&&j[6]==1) return true;
    if(j[2]==2&&j[4]==2&&j[6]==2) return true;
    return false;
}
bool checkIfItsOver(TTTField j){
    for(int i=0;i<9;i++){
        if(j[i]==0){
            return false;
        }
    }
    return true;
}
bool checkListOfFields(TTTField game, TTTField listOfFields[], int amountAdded){
    int i,j;
    for(j=0;j<amountAdded;j++){
        int temporaryField=0;
        for(i=0;i<9;i++){
            if(game[i]==listOfFields[j][i]) temporaryField++;
        }
        if(temporaryField==9)return true;
    }
    return false;
}
void clearField(TTTField game){
    int i;
    for(i=0;i<9;i++) game[i]=0;
}
void addlistOfFields(TTTField game, TTTField listOfFields[], int amountAdded){
    for(int i=0;i<9;i++) listOfFields[amountAdded][i]=game[i];
}
int main(){
    TTTField listOfFields[50000];
    TTTField temporaryField;
    int amountAdded=0,randA,round=1,roundCounter=0,amountPassed=0,amountOfWins=0,amountOfDraws=0,winWith5=0,winWith6=0,winWith7=0,winWith8=0,winWith9=0,roundAmountFinished=0;
    for(int i=0;roundCounter<100000;i++){
        clearField(temporaryField);
        roundAmountFinished=0;
        do{
            do{
                randA=rand()%9;
            }while(temporaryField[randA]!=0);
            temporaryField[randA]=round;
            if(checkIfWon(temporaryField)){
                break;
            }
            if(checkIfItsOver(temporaryField)){
                break;
            }
            round=round==1?2:1;
            roundAmountFinished++;
        }while(1);
        if(!checkListOfFields(temporaryField,listOfFields,amountAdded)){
            addlistOfFields(temporaryField,listOfFields,amountAdded);
            amountAdded++;
            if(checkIfWon(temporaryField)){
                amountOfWins++;
            }
            if(checkIfItsOver(temporaryField)){
                amountOfDraws++;
            }
            switch(roundAmountFinished){
                case 4:
                    winWith5++;
                    break;
                case 5:
                    winWith6++;
                    break;
                case 6:
                    winWith7++;
                    break;
                case 7:
                    winWith8++;
                    break;
                case 8:
                    winWith9++;
                    break;
            }
        }
        if(amountPassed==amountAdded){
            roundCounter++;
        }else roundCounter=0;
        amountPassed=amountAdded;
    }
    system("cls");
    printf("Total: %d, roundCounter: %dnWins with 5 rounds:%dnWins with 6 rounds:%dnWins with 7 rounds:%dnWins with 8 rounds:%dnWins with 9 rounds:%dnamountOfWins: %d, amountOfDraws: %d",amountAdded,roundCounter,winWith5,winWith6,winWith7,winWith8,winWith9,amountOfWins,amountOfDraws);
    return 0;
}

但是它返回给我的总数是:1916,这和网站上的不一样,我不知道我的代码有什么问题

关于代码的一些信息:

  • 该字段是一个整数数组,有9个索引,其中1表示叉,2表示圆,0表示空字段。
  • 它生成一个从0到9的随机值(如果它之前没有被选中),然后它放一个1或2(取决于它是谁)总是检查是否有人赢了或游戏已经导致平局。
  • 完成游戏后,它将检查该可能性是否已经在列表中,如果是,它将不做任何事情并继续进行另一个。
  • 当它通过100k的可能性而不添加一个到列表中时,它将完成。

问题在哪里?

我刚刚注意到,checkListOfFields计数相同的相同的游戏,而这不是完全真的。你在计算棋盘的结束位置的数量,而不是游戏,这(虽然有趣)是完全不同的事情。

考虑以下两个游戏:

X| |   X|O|   X|O|X
-----  -----  -----
 | |    | |    | | 
-----  -----  -----
 | |    | |    | | 
 | |X   |O|X  X|O|X
-----  -----  -----
 | |    | |    | | 
-----  -----  -----
 | |    | |    | | 

你的checkListOfFields函数检测到这些是相同的游戏,并丢弃一个。因此,也会丢弃在之后的所有潜在移动集的一个副本。

我可能会递归地写这个问题。您的方法将获取一个棋盘状态,添加一个棋子,检查它是否完成(满或赢),如果没有,使用新的棋盘状态再次调用该方法。每次满或赢时,增加一些全局计数器。当该方法返回时,将您的作品移动到新的位置。

我大概会这么做:

recurseBoard(vector<vector<square> >& board, int pieceType) {
    for (int i = 0; i < 3; i++) {
       for (int j = 0; j < 3; j++) {
          // If there's a piece there already, skip
          if (board[i][j] != 0) continue;
          // add the piece
          board[i][j] = pieceType;
          // if it's full or win, increment counter
          if (victory(board)) count++;
          else (recurseBoard(board), otherPieceType);
          // Clear the piece you just added
          board[i][j] = 0;
      }
   }
}

如果我是你,我会首先为平方值创建一个enum,而不是传递和比较数字:

enum square {X, O, EMPTY};

那么你就可以把棋盘保持为2d向量:

vector<vector<square> > board (3, vector<square>(3, EMPTY));

然后通过递归找到可能的游戏数量:

int find_games(vector<vector<square> >& board, square move) {
    if(game_over(board)) return 1;
    int num_moves = 0;
    for(int x = 0; x < board.size(); x++) {
        for(int y = 0; y < board[x].size(); y++) {
            if(board[x][y] != EMPTY) continue;
            board[x][y] = move; //test if player made a move here...
            num_moves += find_games(board, (move == X) ? Y : X);
            board[x][y] = EMPTY; //set space back to empty
        }
    }
    return number_moves;
}