新手立体四子棋AI教程(4)——启发式搜索与主程序

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

 

一、原因

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

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

20160215143459509

还是前一章的那张图,上面可以看到在第二层中,第一个节点的值是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 = -1000000;
        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,2);
        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

致谢!

posted on 2018-03-28 13:37  Scobbing  阅读(1830)  评论(0编辑  收藏  举报

导航