如何将完美井字棋Minimax算法修改为非完美AI算法?
如何将完美井字棋Minimax AI修改为非完美AI
要让AI不再总是选择最优落子,核心思路是限制Minimax的搜索深度,或在选步时加入随机概率选择次优解。结合你已添加的depth参数,具体实现步骤如下:
1. 添加启发式评估函数
当搜索达到设定的最大深度时,停止递归,用启发式函数评估当前局面的近似得分,模拟AI“无法预判全局”的情况:
#include <stdlib.h> #include <time.h> #define MAX_DEPTH 3 // 可调参数:数值越小,AI水平越弱 // 启发式评估:计算当前局面得分,AI为1,玩家为-1 int evaluate(int board[9]) { int score = 0; unsigned wins[8][3] = {{0,1,2},{3,4,5},{6,7,8},{0,3,6},{1,4,7},{2,5,8},{0,4,8},{2,4,6}}; for(int i = 0; i < 8; ++i) { int ai_count = 0, player_count = 0; for(int j = 0; j < 3; ++j) { if(board[wins[i][j]] == 1) ai_count++; else if(board[wins[i][j]] == -1) player_count++; } // 根据潜在赢线数量赋值权重 if(ai_count == 3) score += 100; else if(ai_count == 2 && player_count == 0) score += 10; else if(ai_count == 1 && player_count == 0) score += 1; if(player_count == 3) score -= 100; else if(player_count == 2 && ai_count == 0) score -= 10; else if(player_count == 1 && ai_count == 0) score -= 1; } return score; }
2. 修改Minimax函数,加入深度限制
利用depth参数判断是否达到最大搜索深度,停止递归并返回启发式得分:
int minimax(int board[9], int player, int depth) { int winner = win(board); if(winner != 0) return winner * player; // 达到最大深度,返回启发式评估结果 if(depth >= MAX_DEPTH) { return evaluate(board) * player; // 乘以player适配当前玩家视角 } int score = -2; for(int i = 0; i < 9; ++i) { if(board[i] == 0) { board[i] = player; // 传递depth+1而非++depth,避免修改当前函数的depth变量 int thisScore = -minimax(board, player*-1, depth + 1); if(thisScore > score) { score = thisScore; } board[i] = 0; } } return score == -2 ? 0 : score; }
3. 改造ComputerMove函数,加入随机犯错逻辑
让AI有概率选择非最优解,模拟人类的失误:
void computerMove(int board[9]) { // 初始化随机数种子(仅首次调用执行) static int first_run = 1; if(first_run) { srand(time(NULL)); first_run = 0; } int best_score = -2; int best_moves[9]; int best_count = 0; // 先收集所有最优得分的合法移动 for(int i = 0; i < 9; ++i) { if(board[i] == 0) { board[i] = 1; int tempScore = -minimax(board, -1, 0); // 初始depth从0开始 board[i] = 0; if(tempScore > best_score) { best_score = tempScore; best_count = 0; best_moves[best_count++] = i; } else if(tempScore == best_score) { best_moves[best_count++] = i; } } } // 20%概率随机选择合法移动(非最优),80%概率选择最优移动 if(rand() % 100 < 20) { int legal_moves[9]; int legal_count = 0; for(int i = 0; i < 9; ++i) { if(board[i] == 0) legal_moves[legal_count++] = i; } int random_idx = rand() % legal_count; board[legal_moves[random_idx]] = 1; } else { int chosen_idx = rand() % best_count; board[best_moves[chosen_idx]] = 1; } }
4. 修正原代码的换行符问题
将原代码中printf里的 替换为\n,否则编译会报错,例如:
void draw(int b[9]) { printf(" %c | %c | %c\n",gridChar(b[0]),gridChar(b[1]),gridChar(b[2])); printf("---+---+---\n"); printf(" %c | %c | %c\n",gridChar(b[3]),gridChar(b[4]),gridChar(b[5])); printf("---+---+---\n"); printf(" %c | %c | %c\n",gridChar(b[6]),gridChar(b[7]),gridChar(b[8])); }
关键逻辑说明
- 深度限制:通过
MAX_DEPTH控制AI的搜索范围,数值越小,AI对后续局面的预判越短,容易做出次优选择。 - 启发式评估:当无法继续递归时,用潜在赢线数量作为得分依据,模拟AI的“短视”判断。
- 随机犯错:加入概率随机选择非最优解,让AI的行为更接近人类,而非始终完美。
内容的提问来源于stack exchange,提问作者poetisme
相关产品推荐
相关产品推荐

