通过前面几篇文章的学习,我们的四子棋程序已经有了框架、搜索几大部分,但是还有着不少问题,我们的程序只能迭代很有限的步骤,导致棋力低下,在这一篇我们将通过启发式搜索极大的优化搜索效率。

一、原因

我们之前的产生走子位置的函数很简单,即找到棋盘上的空余位置。它的不合理性体现在两方面:

  1. 没有对结果进行排序,完全是按照数组的遍历顺序的。而Alpha Beta 剪枝的效率是非常依赖节点顺序的,这个我们马上就会讲一下。
  2. 没有排除不需要节点。如果能减少一些不必要的节点,那么其实就是优化了 M^N 中的M,优化效果是非常明显的。

还是前一章的那张图,上面可以看到在第二层中,第一个节点的值是3,因为他其实是本层中的极小值,导致后面的两个节点都可以进行剪枝(这里第二个节点的第二个孩子也可以剪掉的)。这是最好的一种情况,即在MIN层中极小值是第一个节点,那么后序的所有节点都可以根据这个极小值进行剪枝,即使极小值不在第一个节点,只要大致能按照从小到大的顺序排列,也会剪掉很多节点。如果很不幸,这一层的节点是从大到小排列的,那么剪枝就完全没有用。

对于Beta 剪枝也是同样的道理。所以说Alpha Beta剪枝的效率是取决于每一层节点的顺序的。 我们肯定是无法精确排序的,因为每一个节点的值并不能直接计算出来,需要递归计算子节点。 但是我们依然能对节点进行大致的一个排序。前面说过了,只要有一个大致的排序 其实就能很好的提升剪枝效率。

那么如何排序呢?就是给所有待搜索的位置进行打分,按照分数的高低来排序。注意这个打分算法是对某一个空位进行打分,在第一张中我们已经有所提到。

有了打分之后,我们就可以按照分数高低进行排序了。

在实现算法前,我们先回顾一下之前的内容。

struct PicesPos{
int x;
int y;
int z;
chessPicesStatus type;
int value;
};

每个落子位置都有相应的value,我们要做的就是在list中将棋子按照一定顺序排列。

二、实现启发式搜索

我们采用stl自带的排序算法,对每个棋子进行排序:

bool comp(const PicesPos &A, const PicesPos &B)
{
return A.value > B.value;
}

首先,我们按照comp方法的要求自定义一个排序函数。然后在相应的位置插入排序语句。

...
else
{
int maxVal = -;
PicesPosList list = getAvailablePos(board,chessPicesStatus::white);
sort(list.begin(),list.end(),comp);
for(auto iter = list.begin();iter != list.end();iter++)
{
board[iter->x][iter->y][iter->z] = chessPicesStatus::white;
...

至此,整个搜索算法就完成啦。

三、主程序

我们的程序已经完成了80%,最后就剩下把他们连接起来了。

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); ChessBoard cb; cb.init();
cb.printBoard(); int x,y;
while(std::cin>>x>>y)
{
if(!cb.insertPices(x,y,chessPicesStatus::black))
{
std::cout<<"error\n";
continue;
} cb.dfs(cb.chessBoard,);
cout<<endl<<"*******Insert x:"<<cb.targetPos.x<<" y:"<<cb.targetPos.y<<" z:"
<<cb.targetPos.z<<"********"<<endl<<endl; cb.insertPices(cb.targetPos.x, cb.targetPos.y, chessPicesStatus::white); int whiteScore = cb.getSideScore(cb.chessBoard,chessPicesStatus::white);
int blackScore = cb.getSideScore(cb.chessBoard,chessPicesStatus::black);
cout<<"whiteScore:"<<whiteScore<<" blackScore:"<<blackScore<<endl; cout<<"whiteTargetPos x:"<<cb.whiteTargetPos.x<<" y:"<<cb.whiteTargetPos.y<<
" z:"<<cb.whiteTargetPos.z<<endl;
cout<<"blackTargetPos x:"<<cb.blackTargetPos.x<<" y:"<<cb.blackTargetPos.y<<
" z:"<<cb.blackTargetPos.z<<endl; int status = cb.isWin(cb.chessBoard);
if(status == chessPicesStatus::white)
{
cb.printBoard(); cout<<"******White Win!******"<<endl; break;
}
else if(status == chessPicesStatus::black)
{
cb.printBoard();
cout<<"******Black Win!******"<<endl; break;
}
else
{
cb.printBoard();
}
} return a.exec();
}

我们的四子棋程序就这样完成了,事实证明根本下不过啊…

参考文献:

http://blog.csdn.net/lihongxun945/article/details/50668253

http://blog.csdn.net/lihongxun945/article/details/50668622

https://www.zhihu.com/question/27221568/answer/127599152

https://www.cnblogs.com/pangxiaodong/archive/2011/05/26/2058864.html

致谢!

新手立体四子棋AI教程(4)——启发式搜索与主程序的更多相关文章

  1. 新手立体四子棋AI教程(1)——基础扫盲

    一.引言 最近身边好几个朋友开始玩立体四子棋,激起了我的好奇心.那么首先来说什么是[立体四子棋],规则又是如何呢? 上图即为立体四子棋,规则类似于五子棋四子连在一起,但是四子棋更加多样.丰富.不仅可以 ...

  2. 新手立体四子棋AI教程(3)——极值搜索与Alpha-Beta剪枝

    上一篇我们讲了评估函数,这一篇我们来讲讲立体四子棋的搜索函数. 一.极值搜索 极值搜索是game playing领域里非常经典的算法,它使用深度优先搜索(因为限制最大层数,所以也可以称为迭代加深搜索) ...

  3. 新手立体四子棋AI教程(2)——价值评估函数

    上一篇我们完成了整个程序的基础框架,那么在讲到真正的搜索算法前,我们先来看看五子棋如何评估当前局势,以及如何计算某个位置的价值. 一.五子棋 在五子棋中,包括成五,活三,活二等定势,下图为山东师范大学 ...

  4. swing桌面四子棋程序开发过程中遇到的一些问题记录(二)

    第二个遇到的问题是将JButton按钮设置成透明的按钮.首先UI给我一张透明的图片,如果我直接给Button按钮设置背景图片的话,是没有透明的效果的,只会留下白色的底,设置前后的效果如下图 制作透明的 ...

  5. codevs1004四子连棋[BFS 哈希]

    1004 四子连棋   时间限制: 1 s   空间限制: 128000 KB   题目等级 : 黄金 Gold   题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗 ...

  6. Codevs p1004 四子连棋

                          四子连棋 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向 ...

  7. 【宽度优先搜索】神奇的状态压缩 CodeVs1004四子连棋

    一.写在前面 其实这是一道大水题,而且还出在了数据最水的OJ上,所以实际上这题并没有什么难度.博主写这篇blog主要是想写下一个想法--状态压缩.状态压缩在记录.修改状态以及判重去重等方面有着极高的( ...

  8. codevs 1004 四子连棋

    1004 四子连棋  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold     题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白 ...

  9. codevs 1004 四子连棋 BFS、hash判重

    004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold       题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋 ...

随机推荐

  1. python 多进程读写文件

    import time from multiprocessing import Process, JoinableQueue, cpu_count import csv ####处理一条数据的方法 d ...

  2. 006-unity3d GUI初识、贴图、自定义鼠标指针

    一.gui概念 无论摄像机拍摄到的图像怎么变换,GUI永远显示在屏幕上,不受变形.碰撞.光照的影响.对话框.战斗值.能量等.示例:用手机录像,摄像的参数不会随着拍摄场景变换.GUI基础GUI部分是每帧 ...

  3. JMeter性能测试入门-不同类型线程组的使用

    jmeter不同线程组的详解 在做性能测试之前,我们来了解一下JMeter多个不同线程组的应用.首先,JMeter提供了三个基本的线程组,分别为: Thread Group setUp Thread ...

  4. TNS-01106: Listener using listener name LISTENER has already been started

    -- 启动监听,提示已经启动. [oracle@sh ~]$ lsnrctl startLSNRCTL for Linux: Version 12.1.0.2.0 - Production on 06 ...

  5. JS和C#后台获取网站URL

    例:网页URL :  http://localhost:8086/index.aspx?topicId=361 1.设置或获取 href 属性中跟在问号后面的部分:window.location.se ...

  6. Tomcat控制台

    一般在安装完成Tomcat之后,我们需要验证tomcat是否安装成功,在浏览器的url中输入:http://127.0.0.1:8080/,就会进入如下的页面(表示安装成功): 在上面的左侧顶部,有一 ...

  7. xml与Properties的区别

    1.properties配置文件,是一个属性对应于一个值(key = value)这样的键值匹对模式: 每一行properties配置文件的键值,对应着一次赋值: 特殊点: 在前后两行properti ...

  8. 牛客小白月赛16 A 小石的签到题 ( 博弈)

    链接:https://ac.nowcoder.com/acm/contest/949/A来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...

  9. Codeforces 1047C (线性筛+因数分解)

    题面 传送门 分析 1.暴力做法 首先先把每个数除以gcd(a1,a2-,an)gcd(a_1,a_2 \dots,a_n )gcd(a1​,a2​-,an​) 可以O(namax)O(n\sqrt ...

  10. 网页中<a>标签新窗口和location.href 新窗口打开

    在网页制作过程中,经常遇到新窗口打开,一般是a超级链接或者location.href 新窗口打开形式,下面分别讲述两种之间的不同方式 1,a标签 新窗口 添加属性 target="_blan ...