我之前在这个论坛上发布了一个类似的问题,但由于旧帖子的内容有点长,而且我重写了我的算法,所以我开始了这个新帖子。旧帖子可以在这里找到。
所以我只是试图为我的井字游戏实现一个极小化-极大化算法,但结果证明这相当困难,即使在尝试了几天后,我仍然找不到错误。你可以在下面找到我的代码。首先,我有一些定义、类型定义和声明:
typedef signed char s8;typedef unsigned char u8;typedef s8 score;#define STATE_00 getBoardState(0, 0)#define STATE_01 getBoardState(0, 1)#define STATE_02 getBoardState(0, 2)#define STATE_10 getBoardState(1, 0)#define STATE_11 getBoardState(1, 1)#define STATE_12 getBoardState(1, 2)#define STATE_20 getBoardState(2, 0)#define STATE_21 getBoardState(2, 1)#define STATE_22 getBoardState(2, 2)typedef enum { EPlayerX = 1, EPlayerO} EPlayer;typedef enum { EMinimizing = 0, EMaximizing} EMinMax;static u8 g_boardState[3][3] = { {0, 0, 0,}, {0, 0, 0,}, {0, 0, 0,},};
接下来是一些函数:
u8 getBoardState(u8 row, u8 column);EPlayer isWon(void){ EPlayer winningBoards[8][3] = { {STATE_00, STATE_01, STATE_02}, {STATE_10, STATE_11, STATE_12}, {STATE_20, STATE_21, STATE_22}, {STATE_00, STATE_10, STATE_20}, {STATE_01, STATE_11, STATE_21}, {STATE_02, STATE_12, STATE_22}, {STATE_00, STATE_11, STATE_22}, {STATE_20, STATE_11, STATE_02}, }; u8 i; for(i=0; i<8; i++){ if( (winningBoards[i][0] != 0) && (winningBoards[i][0] == winningBoards[i][1]) && (winningBoards[i][0] == winningBoards[i][2])){ return winningBoards[i][0]; } } return 0;}u8 getBoardState(u8 row, u8 column){ return g_boardState[row][column];}void setBoardState(u8 row, u8 column, u8 state){ g_boardState[row][column] = state;}u8 isDraw(void){ u8 i, j; for(i=0; i<3; i++){ for(j=0; j<3; j++){ if(getBoardState(i, j) == 0){ return 0; } } } return 1;}void dumpTable(score table[3][3]){ int i, j; for(i=0; i<3; i++) { printf("\n"); for(j=0; j<3; j++){ printf("%6i ", table[i][j]); } } printf("\n");}EPlayer playerSwitch(EPlayer player){ if(player == EPlayerO) return EPlayerX; if(player == EPlayerX) return EPlayerO; return 0;}EMinMax modeSwitch(EMinMax mode){ if(mode == EMaximizing) return EMinimizing; if(mode == EMinimizing) return EMaximizing; return 0;}
然后是实际的极小化-极大化算法,这里称为scoring
:
score scoring(EMinMax mode, EPlayer player, u8 depth){ score thisScore, tempScore; if(mode == EMaximizing){ thisScore = -20; if(isWon()) return 15-depth; } if(mode == EMinimizing){ thisScore = 20; if(isWon()) return depth-15; } if(isDraw()){ return 0; } u8 i, j; for(i=0; i<3; i++){ for(j=0; j<3; j++){ if(getBoardState(i, j) == 0){ setBoardState(i, j, player); tempScore = scoring(modeSwitch(mode), playerSwitch(player), depth+1); if((mode == EMaximizing) && (tempScore > thisScore)){ thisScore = tempScore; } if((mode == EMinimizing) && (tempScore < thisScore)){ thisScore = tempScore; } setBoardState(i, j, 0); } } } return thisScore;}
最后是一个打印分数表的函数以及main
函数:
void printSocredBoards(EPlayer player){ score thisScore[3][3] = { {STATE_00, STATE_01, STATE_02}, {STATE_10, STATE_11, STATE_12}, {STATE_20, STATE_21, STATE_22}, }; int i, j; if((isWon() == 0) && (isDraw() == 0)){ for(i=0; i<3; i++){ for(j=0; j<3; j++){ if(getBoardState(i, j) == 0){ setBoardState(i, j, player); thisScore[i][j] = scoring(EMaximizing, playerSwitch(player), 0); setBoardState(i, j, 0); } } } } dumpTable(thisScore);}int main(int argc, char **argv){ printSocredBoards(EPlayerO); return 0;}
据我所知,这个算法应该可以正常工作,但它却给我输出了无意义的结果:
7 7 7 7 0 7 7 7 7
我错过了什么?提前感谢任何帮助。
回答:
我认为问题出在scoring
函数中的这段代码,你的案例与正确的返回值相反:
if(mode == EMaximizing){ thisScore = -20; if(isWon()) return 15-depth;}if(mode == EMinimizing){ thisScore = 20; if(isWon()) return depth-15;}
直观上,问题在于当scoring
到达代码的这个点时,对isWon
的调用是在评估前一个棋子放置的结果,而前一个棋子放置是用另一个mode
选择的。
例如,当scoring
以EMaximizing
模式调用,并且棋盘状态已经获胜时,这意味着EMinimizing
模式的玩家在这个状态下获胜,返回的分数应该反映这一点(即应该为负数)。由于深度最大值为8,当mode == EMaximizing
时,你的返回值总是正数,这不是你想要的。
当案例反转时,你的程序输出全是零,这看起来更合理,因为完美的玩家应该总是平局。我还测试了代码,在printScoredBoards
函数的顶部添加了以下一行,以硬编码第一步棋到左上角:
setBoardState(0, 0, playerSwitch(player));
这产生了以下结果:
0 10 10 10 0 10 10 10 10
正确地识别出第二个玩家不能选择左上角,并且如果他们在开局时选择除中心位置之外的任何位置都会输。