我正在尝试在用C编写的Tic-Tac-Toe游戏中实现基于Wikipedia pseudocode的MiniMax算法。但是,我无法获得最佳移动。这是我的代码:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
// compile and run: gcc minimax.c -std=c99 && ./a.out
int max(int x, int y) {
return x > y ? x : y;
}
int min(int x, int y) {
return x < y ? x : y;
}
int whoWon(char ch) {
switch (ch) {
case 'O':
return -1;
break;
case 'X':
return 1;
break;
}
}
void printArray(char array[]) {
printf("# START\n"
"%c | %c | %c\n"
"--|---|--\n"
"%c | %c | %c\n"
"--|---|--\n"
"%c | %c | %c\n"
"# END\n\n", array[0], array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8]);
}
int anyWinners(char board[])
{
int i;
/* check every row */
for(i = 0; i < 7; i += 3)
if(board[i] != ' ' && board[i] == board[i+1] && board[i] == board[i+2])
return whoWon(board[i]);
/* check every column */
for(i = 0; i < 3; i++)
if(board[i] != ' ' && board[i] == board[i+3] && board[i] == board[i+6])
return whoWon(board[i]);
/* check diagonals */
if(board[4] != ' ' && ((board[0] == board[4] && board[0] == board[8]) || (board[2] == board[4] && board[2] == board[6])))
return whoWon(board[4]);
return 0;
}
int fullBoard(char board[]) {
for (int i = 0; i < 9; ++i) {
if (board[i] == ' ')
return 0;
}
return 1;
}
int minimax(char node[], int depth, bool maximizingPlayer, int * move) {
int terminalNode = anyWinners(node);
if (depth == 0 || terminalNode || fullBoard(node)) {
printf("################## END OF SUBTREE ##################\n");
return terminalNode;
}
int bestValue, val;
if (maximizingPlayer) {
bestValue = -2;
for (int i = 0; i < 9; ++i) {
if (node[i] == ' ') {
char child[9];
strcpy(child, node);
child[i] = 'X';
// debug
printArray(child);
val = minimax(child, depth - 1, false, move);
// debug
printf("X: ^^ i = %d ^^ depth = %d ^^ val = %d\n", i, depth, val);
//bestValue = max(bestValue, val);
if (val > bestValue) {
bestValue = val;
if (depth == 9) *move = i;
}
}
}
return bestValue;
} else {
bestValue = 2;
for (int i = 0; i < 9; ++i) {
if (node[i] == ' ') {
char child[9];
strcpy(child, node);
child[i] = 'O';
// debug
printArray(child);
val = minimax(child, depth - 1, true, move);
// debug
printf("O: ^^ i = %d ^^ depth = %d ^^ val = %d\n", i, depth, val);
bestValue = min(bestValue, val);
}
}
return bestValue;
}
}
int main() {
int move = -999; // initialize only for debug
// X will always win no matter what, first best move for X is 8
// char board[] = {'O', ' ', ' ',
// ' ', ' ', ' ',
// 'X', 'X', ' '};
// best move for X is 3
char board[] = {'O', 'O', ' ',
' ', 'X', 'X',
' ', ' ', ' '};
// Initial call for maximizing player
int result = minimax(board, 9, true, &move);
printf("minimax returned: %d\n", result);
printf("chosen move: %d\n", move);
return 0;
}
代码使用所有变量的状态为每次移动打印板。主要还有两个失败的测试。现在算法返回坏动作,我找不到错误。
答案 0 :(得分:2)
我看到两个问题:
启发式错误
维基百科的伪代码说:
if depth = 0 or node is a terminal node
return the heuristic value of node
您的实施是这样做的:
if depth = 0 or node is a terminal node
return 1 if X wins, -1 if O wins, 0 if it is a draw
但这不是一个很好的启发式方法。有了这种启发式方法,X可以赢得的所有可能方式都具有相同的权重。因此,如果X在3次移动中找到了获胜的方法,那么加权就像X在2次移动中找到获胜的方式一样,并且加权就像X在1次移动中找到获胜的方式一样。
因此,以下是您的测试用例中发生的情况:
此决策路径的启发式= 1
它遇到的另一种可能性是:
此决策路径的启发式= 1
由于这两种解决方案都具有相同的启发式算法,因此它们具有相同的价值。你可能认为这个解决方案不是最理想的,因为它需要太多的动作才能获胜。我建议根据到达这里所采取的动作数量进行启发式算法,乘以胜者是谁。因此,如果X在1次移动中获胜,则启发式为5000.如果X在2次移动中获胜,则启发式为2500.如果O在2次移动中获胜,则启发式为-2500。这样的事情。
strcpy
存在问题这一行:
strcpy(child, node);
应该是:
memcpy(child, node, 9*sizeof(char));
因为“node”不是以空字符结尾的字符串。当我在VS2013 / Windows 8.1上运行它时,我的输出是垃圾,因为。你的平台可能会很幸运。