人机博弈-吃子棋游戏(四)搜索算法

博弈树搜索技术简介:

博弈树的搜索算法,负值极大搜索,alpha-beta搜索,渴望搜索,PVS极窄窗口搜索等。通常来说,搜索算法常常和以下技术联合在一起。

如下:

1.置换表,记录已经搜索过的棋局,避免再次搜索。

2.吃子启发,优先试下能够吃对方棋子的走法。

3.杀手启发,历史启发简化版。

4.历史启发,优先试下历史统计数据得出的比较好的走法。

5.静止期搜索,继续对某些叶子结点搜索,避免水平线效应。

6.迭代加深搜索,根据搜索时间,状态。决定是否继续搜索。

有兴趣的朋友可以深入研究一下上述技术和算法。

吃子棋搜索算法:

我的程序最初的实现使用的负值极大搜索算法,之后改用alpha-beta搜索算法,后来又使用PVS极窄窗口搜索算法。

在我自己的实现里没有使用置换表,历史启发等技术。是因为吃子棋每层的走法数相对较少,所以并没有使用。

但我们知道,这些技术可以很大的提高搜索效率。

吃子棋搜索算法源码:

接下来,看看吃子棋搜索算法的源代码:

负值极大算法

int CNegaMaxEngine::negaMax(int depth)
{
    int currentMaxScore = -20000;//init value mini
    int score;
    int nextMoveCount;
    int overNum = IsGameOver(CurPosition, depth);
    if (overNum != 0)return overNum;
    if (depth <= 0)
        return m_pEval->Eveluate(CurPosition, (m_nMaxDepth - depth ) % 2 );
    nextMoveCount = m_pMG->CreatePossibleMove(CurPosition, depth, (m_nMaxDepth - depth) % 2);
    for (int i = 0; i < nextMoveCount; i++)
    {
        MakeMove(&m_pMG->m_MoveList[depth][i], (m_nMaxDepth - depth) % 2);
        score = -negaMax(depth-1);
        UnMakeMove(&m_pMG->m_MoveList[depth][i]);
        if (score>currentMaxScore)
        {
            currentMaxScore = score;
            if (depth == m_nMaxDepth){

                m_cmBestMove = m_pMG->m_MoveList[depth][i];
            }
        }
    }
    return currentMaxScore;
}

alpha-beta算法

int CAlphtBetaEngine::alphabeta(int depth,int alpha ,int beta)
{

    int score;
    int nextMoveCount;
    int overNum = IsGameOver(CurPosition, depth);

    if (overNum != 0)return overNum;

    int whoTurn = (m_nMaxDepth - depth) % 2;
    if (depth <= 0)
        return m_pEval->Eveluate(CurPosition, whoTurn);
    nextMoveCount = m_pMG->CreatePossibleMove(CurPosition, depth, whoTurn);
    for (int i = 0; i < nextMoveCount; i++)
    {
        MakeMove(&m_pMG->m_MoveList[depth][i], whoTurn);
        score = -alphabeta(depth - 1, -beta, -alpha);
        UnMakeMove(&m_pMG->m_MoveList[depth][i]);
        if (score>alpha)
        {
            alpha = score;
            if (depth == m_nMaxDepth){

                m_cmBestMove = m_pMG->m_MoveList[depth][i];
            }
        }
        if (alpha >= beta)break;
    }
    return alpha;
}

PVS算法:

int CPVS_Engine::PrincipalVariation(int depth, int alpha, int beta)
{
    int score;
    int Count, i;
    BYTE type;
    int best;

    i = IsGameOver(CurPosition, depth);
    if (i != 0)
        return i;

    if (depth <= 0)    //叶子节点取估值
        return m_pEval->Eveluate(CurPosition, false);

    Count = m_pMG->CreatePossibleMove(CurPosition, depth, (m_nMaxDepth - depth ) % 2);

     MakeMove(&m_pMG->m_MoveList[depth][0], (m_nMaxDepth - depth ) % 2);
    best = -PrincipalVariation(depth - 1, -beta, -alpha);
    UnMakeMove(&m_pMG->m_MoveList[depth][0]);
    if (depth == m_nMaxDepth)
        m_cmBestMove = m_pMG->m_MoveList[depth][0];

    for (i = 1; i<Count; i++)
    {

        if (best < beta)
        {
            if (best > alpha)
                alpha = best;
            MakeMove(&m_pMG->m_MoveList[depth][i], (m_nMaxDepth - depth ) % 2);
            score = -PrincipalVariation(depth - 1, -alpha - 1, -alpha);
            if (score > alpha && score < beta)
            {
                best = -PrincipalVariation(depth - 1, -beta, -score);
                if (depth == m_nMaxDepth)
                    m_cmBestMove = m_pMG->m_MoveList[depth][i];
            }
            else if (score > best)
            {
                best = score;
                if (depth == m_nMaxDepth)
                    m_cmBestMove = m_pMG->m_MoveList[depth][i];
            }
            UnMakeMove(&m_pMG->m_MoveList[depth][i]);
        }
    }

    return best;
}
时间: 2024-12-18 19:28:01

人机博弈-吃子棋游戏(四)搜索算法的相关文章

人机博弈-吃子棋游戏(三)走法生成

我们可以根据吃子棋的规则,创建走法生成器,主要的逻辑是,如果己方存在一气的棋串,则可以无需紧对方的气,也就是可以不用贴着对方的棋子落子.其他情况下,必须贴 着对方的棋子落子,考虑到某些特殊情况,当己方能提对方棋子时,己方就可以下到没有气的地方,并且一般的吃子棋,先提子者胜,无法形成劫争.所以走法生成器就相对非 常简单. 对于如何确定己方是不是存在一气的棋串,可以利用上一节介绍的算气算法. 1 int CMoveGenerator::CreatePossibleMove(BYTE position

人机博弈-吃子棋游戏(二)算气

算法过程如下,输入计算棋气的起始子,检查我方棋子其周边的空白,并查看此空白是否已经计算过气了,如果没有计算过气加一,如果已经计算则略过,进而递归调用计算我方棋子上下左右子的气,最后算法会返回棋串的子数和气数.算法注释十分详尽. 这个计算棋子气的算法,参考http://www.cnblogs.com/tuanzang/archive/2013/02/27/2935861.html,结合我们自身的应用需要优化和修改.这篇博文介绍了更多关于围棋的算法,有兴趣的朋友可以进一步阅读. 原文suanqi函数

人机博弈,吃子棋游戏(一)

(1)什么是吃子棋? 吃子棋,是小孩子学围棋的入门棋,其规则部分取自于围棋,但比围棋简单很多. (2)吃子棋的行棋规则? 1.吃子棋,在行棋时,若一方棋子没有被打吃,则其必须落子在能够紧对方棋子气的位置. 2.吃子棋,当一方棋子处于被打吃的状态,则可以逃命,而不用紧对方棋子的气. 3.吃子棋,当一方棋子被吃提,则输赢即分,吃子方胜. 4.吃子棋,一般有两种赢棋方式,双打吃和征吃. 5.吃子棋,起手棋必须位于天元. (3)应用设计思路 设计此应用的总体思路如下: DATA: 1.棋盘表示 2.棋子

人机博弈,吃子棋游戏(二)如何算气

算法过程如下,输入计算棋气的起始子,检查我方棋子其周边的空白,并查看此空白是否已经计算过气了,如果没有计算过气加一,如果已经计算则略过,进而递归调用计算我方棋子上下左右子的气,最后算法会返回棋串的子数和气数.算法注释十分详尽. 这个计算棋子气的算法,参考http://www.cnblogs.com/tuanzang/archive/2013/02/27/2935861.html,结合我们自身的应用需要优化和修改.这篇博文介绍了更多关于围棋的算法,有兴趣的朋友可以进一步阅读. 原文suanqi函数

人机博弈-吃跳棋游戏(三)代移动

我们能够根据国际象棋的规则吃,创建移动生成器.基本逻辑是,假定一个拉伸己方蠕虫的存在,这是可能没有其他的致密气.这是不是对其他部分可以落子.在其他情况下.必须坚持 另一片落子,考虑到特殊情况,当自己可以提其他片,自己可以下到那里没有气.和一般吃跳棋.胜,无法形成劫争.所以走法生成器就相对非 常简单. 对于怎样确定己方是不是存在一气的棋串,能够利用上一节介绍的算气算法. int CMoveGenerator::CreatePossibleMove(BYTE position[GRID_NUM][G

JAVA安卓植物大战僵尸主题四子棋游戏

@前言 这里使用安卓最基本的API实现双人四子棋游戏(无AI),开发语言为java,开发环境为Android Studio 2.1.2,目标SDK版本为24,最低为15: 界面采用植物大战僵尸主题,图片资源来源于网络,进行了PS加工,非原创: 游戏界面基本可以适配所有安卓手机分辨率,不过在分辨率太大或太小的手机上整体效果会有影响: Github源码: https://github.com/jiangxh1992/FourInRowGame 视频演示: https://vimeo.com/1875

人工智能博弈树算法做的井字棋游戏

不会输,超碉!井字棋这个游戏真是太无聊啦! 算法大概就是,有一个给状况进行估价的函数,深搜每种状况,假设每个人都按对自己最有利的方式走(假设玩家也是不傻),最后让电脑走出最有利的一步. 代码: 1 //#pragma comment(linker, "/STACK:102400000,102400000") 2 #include<cstdio> 3 #include<cmath> 4 #include<iostream> 5 #include<

人工智能之人机博弈(1)

每个程序猿的心中都有一个AI梦,而人机博弈是最简单的一种AI形式. 看了阿尔法狗和李世石之战后,就一直在研究AI的一些知识,包括传统的人机博弈算法,以及阿尔法狗的蒙特卡罗加神经网络的算法.甚至还重温了高等数学的知识. 为了给家里小朋友学棋,花了一个多月的休息的时间,开发了这个国际跳棋软件.支持人机对战,经专家们测试,AI棋力达到中等以上棋手的水平. 对国际跳棋感兴趣的朋友,可以到下面地址下载试用一下.暂时不开源,等我完善了程序,再考虑开源出来.并写一些文章总结和分享开发的过程.  下载地址: h

C++井字棋游戏,DOS界面版

据说有一个能保证不败的算法.明天看看先再写个PVC版的. 正题.今天无聊写了个井字棋游戏,顺便逐渐让自己习惯良好的代码风格,放上来给新手学习学习. jzq2.cpp /* N字棋游戏PVP版,DOS版 本棋盘可扩充,仅仅需调整检測条件就可以,其它接口不需改变. 非人机对战型.PVP类型; @author:天下无双 @date:2014-5-25 @version:1.0 */ #include <iostream> #include <string> #define INVALID