Мне хотелось узнать, сколько там возможностей Tic Tac Toes, поэтому я обыскал в Интернете и нашел математическую теорему, в которой говорится, что в 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: %d\nWins with 5 rounds:%d\nWins with 6 rounds:%d\nWins with 7 rounds:%d\nWins with 8 rounds:%d\nWins with 9 rounds:%d\namountOfWins: %d, amountOfDraws: %d",amountAdded,roundCounter,winWith5,winWith6,winWith7,winWith8,winWith9,amountOfWins,amountOfDraws);
return 0;
}
Но он возвращает мне общее количество: 1916, что отличается от того, что было на веб-сайте, и мне интересно, что не так с моим кодом.
Некоторая информация о коде:
В чем проблема?
Я только что заметил, checkListOfFields
считает одинаковые платы одинаковыми играми, но это не совсем так. Вы вычисляете количество конечных позиций досок, а не игр, которые (хотя и интересны) - совсем другое.
Рассмотрим эти две игры:
X| | X|O| X|O|X
----- ----- -----
| | | | | |
----- ----- -----
| | | | | |
| |X |O|X X|O|X
----- ----- -----
| | | | | |
----- ----- -----
| | | | | |
Функция checkListOfFields
определяет, что это одна и та же игра, и отбрасывает ее. Поэтому он также отбрасывает одну копию всех возможных перемещений, которые появляются после этого.
if(!checkListOfFields(temporaryField,listOfFields,amountAdded))
и, возможно, это сработает.
Я бы, вероятно, написал эту проблему рекурсивно. Ваш метод будет принимать состояние платы, добавить часть, проверить, закончится ли она (полная или выиграть), а если нет, снова вызовите метод с новым состоянием платы. И каждый раз, когда вы полны или выигрываете, увеличивайте количество глобальных счетчиков. Когда метод вернется, переместите свой кусок на новое место.
Вот приблизительное представление о том, что я сделал бы:
recurseBoard(vector<vector<square> >& board, int pieceType) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// If there a piece there already, skip
if (board[i][j] != 0) continue;
// add the piece
board[i][j] = pieceType;
// if it 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;
}