使用QT creator实现一个五子棋AI包括GUI实现(8K字超详细) (5)

在可接受的范围内只能搜到\(depth=2\)的情况,相当于只预测了一轮(MAX下了一次,然后MIN下了一次,再轮到MAX下的时候就要算分了)。这样的层数下算法肯定还是不够聪明,因此得想办法加快。

alpha-beta剪枝算法加速: 前剪枝:

在五子棋中,落子的点一般都在别的已经落子的棋子旁边,很容易看出,在当前棋盘的局面下,如果落子在红圈所在的地方肯定是没有意义的,因为其对评估函数的结果不会变得更好。

使用QT creator实现一个五子棋AI包括GUI实现(8K字超详细)

所以,在遍历的时候,我们可以只考虑目前在棋盘上所有棋子的附近的棋子。换句话说,对于某一个位置,如果其四周都没有棋子的话,就不考虑它,将其剪枝。因此,在上图中只用考虑黑圈中的棋子。这样就达到了前剪枝,节省了大量的时间。

启发式搜索: 启发式搜索的定义:

用于生成待搜索位置的函数,在朴素的alpha-beta剪枝中,节点搜索的顺序是按照从下到上,从左往右的顺序进行的。但是深入理解alpha-beta剪枝会发现:节点的值的排列顺序非常的影响alpha-beta剪枝的效率:对于某个节点来说,如果第一个孩子节点就是所有孩子节点的分数的最小值,而该节点又是MIN节点,就可以在第一个节点满足alpha-beta剪枝的条件:\(\alpha>=\beta\),这样就可以大大增加剪枝的效率。

例如:对于下图中黑圈内的区域,如果叶子节点值为4的节点能和叶子节点值为7的节点顺序互换,那么就可以将叶子节点值为7的节点剪掉,增加了剪枝的效率。

使用QT creator实现一个五子棋AI包括GUI实现(8K字超详细)

因此,我们需要一个启发式搜索的函数,该函数可以有效的评估哪些落子的点的评估函数值大,哪些落子的点评估函数的值小,这样就可以大大加快剪枝的效率了。

本实验设计的启发式搜索函数:

将该函数命名为\(h(x)\),针对每一个落子的点,遍历经过这个点的行,列,斜线的四个数组,找出相应的对我方有利的棋型然后加分,如果遇到两个以上的三活以及四活,则将\(h(x)\)加一个很大的值,表示优先遍历。

这样的好处是AI可以有效的识别两个三活以及死四以及活四这种必杀棋的情况,并且针对其进行进攻和防守。

实现代码如下:

int partern_count[type_count]={0};//用来存每一个棋子的数量 int sum_score = 0; int b_lr = r+10-l; int b_rl = r+l-4; for(int i = 0;i<type_count;i++)//查找每一种棋型 { string temp_rl; string temp_lr; if(b_rl>=0&&b_rl<=20)//如果不存在,则不考虑 temp_rl = &edge_rl[b_rl][(b_rl-10<0)?0:b_rl-10]; if(b_lr>=0&&b_lr<=20) temp_lr = &edge_lr[b_lr][(10-b_lr<0)?0:10-b_lr]; int count1 = find_count(rows[r],chess_type_all_my[i].ptn.c_str());//行 int count2 = find_count(cols[l],chess_type_all_my[i].ptn.c_str());//列 int count3 = find_count(temp_rl.c_str(),chess_type_all_my[i].ptn.c_str());//左下到右上 int count4 = find_count(temp_lr.c_str(),chess_type_all_my[i].ptn.c_str());//左上到右下 if(i==8) { count1 -= find_count(rows[r],chess_type_all_my[7].ptn.c_str()); count2 -= find_count(cols[l],chess_type_all_my[7].ptn.c_str());//列 count3 -= find_count(temp_rl.c_str(),chess_type_all_my[7].ptn.c_str());//左下到右上 count4 -= find_count(temp_lr.c_str(),chess_type_all_my[7].ptn.c_str());//左上到右下 } if(i==19) { count1 -= find_count(rows[r],chess_type_all_my[18].ptn.c_str()); count2 -= find_count(cols[l],chess_type_all_my[18].ptn.c_str());//列 count3 -= find_count(temp_rl.c_str(),chess_type_all_my[18].ptn.c_str());//左下到右上 count4 -= find_count(temp_lr.c_str(),chess_type_all_my[18].ptn.c_str());//左上到右下 } sum_score+=(count1+count2+count3+count4)*chess_type_all_my[i].my_score; int count5 = find_count(rows[r],chess_type_all_opt[i].ptn.c_str());//行 int count6 = find_count(cols[l],chess_type_all_opt[i].ptn.c_str());//列 int count7 = find_count(temp_rl.c_str(),chess_type_all_opt[i].ptn.c_str());//左下到右上 if(deep==0&&r==6&&l==5) { cout<<count1<<" "<<count2<<" "<<count3<<" "<<count4<<" "<<endl; } int count8 = find_count(temp_lr.c_str(),chess_type_all_opt[i].ptn.c_str());//左上到右下 if(i==8) { count5 -= find_count(rows[r],chess_type_all_opt[7].ptn.c_str());//行 count6 -= find_count(cols[l],chess_type_all_opt[7].ptn.c_str());//列 count7 -= find_count(temp_rl.c_str(),chess_type_all_opt[7].ptn.c_str());//左下到右上 count8 -= find_count(temp_lr.c_str(),chess_type_all_opt[7].ptn.c_str());//左上到右下 } if(i==19) { count5 -= find_count(rows[r],chess_type_all_opt[18].ptn.c_str());//行 count6 -= find_count(cols[l],chess_type_all_opt[18].ptn.c_str());//列 count7 -= find_count(temp_rl.c_str(),chess_type_all_opt[18].ptn.c_str());//左下到右上 count8 -= find_count(temp_lr.c_str(),chess_type_all_opt[18].ptn.c_str());//左上到右下 } sum_score-=(count5+count6+count7+count8)*(chess_type_all_my[i].opt_score); partern_count[i] = count1+count2+count3+count4; } int winner = 0; for(int i=17;i<=23;i++)//如果存在两个以上的两个活三和活四 winner += partern_count[i]; if(winner>1) sum_score+=3000000; h_score.push_back(node{r,l,sum_score}); clear_temp_chess(r,l);//注意要回溯 } 待实现的优化方法: 启发式搜索方法改进

能不能在现在得分的基础上加上当前局面的评估函数得分,以此为依据来对要遍历的落子点进行排序。然后只选择最好的前10个节点进行拓展。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzddzf.html