基于博弈树的开源五子棋AI教程[1 位棋盘]

文章目录

0 引子

常见的五子棋棋盘大小为15x15,最直观的表示就是一个二维数据。本文为了易于拓展一开始使用的是QVector<QVector>的数据,但是在分支因子为10的情况下只能搜索到4层左右,后面深度加深,搜索时间呈指数倍数增长。这种实现方式下,六层搜索深度下搜索时间大于1min。
接着使用二维数组(int[][])来表示一个搜索状态,搜索速度略有加快,时间大约在2倍左右(记忆模糊了)。
目前实现方式中使用的位棋盘,这样可以有效的减少寻址时间,取出一行或者一列只需要从内存中取出一个int32(考虑到17x17或者19x19)。有些读者可能想问一个格子有三种状态(黑/白/空),bool又能如何表示呢?答案就是使用两个int32数组表示,一个数组表示是否有子,另一个表示黑子还是白子。

1 15X15棋盘

1 定义

一般而言一组二维数组就可以充分的表示棋盘信息,但是在后续棋盘静态评估的需求中发现,本文需要对棋盘的四个方向上评估出基础棋型。因而从不同角度冗余的描述棋盘信息就是必要的。

//棋子值的定义[保证0 1为黑子或者白子]
#define PLAYER_BLACK 0
#define PLAYER_WHITE 1
#define PLAYER_NONE  2
//四方向
#define MMainDiagonal 0 //主对角线
#define MSubDiagonal  1 //副对角线
#define MHorizontal   2 //水平
#define MVertical     3 //竖直

这里也简单给出棋盘信息完备表示,为了简化搜索过程的的边界处理,对所有棋盘加墙(白棋搜索时,墙就是黑子)。对角线上只保留了可以构成连五的线。

    //定义含有边界所有连线上的棋子,用于更新棋型
    int searchBoard[boardSize+2];
    int searchBoardMask[boardSize+2];
    int searchBoardVertical[boardSize+2];
    int searchBoardVerticalMask[boardSize+2];
    int searchBoardMainDiag[2*boardSize - 9];
    int searchBoardMainDiagMask[2*boardSize - 9];
    int searchBoardSubDiag[2*boardSize - 9];
    int searchBoardSubDiagMask[2*boardSize - 9];

2 实现

有了棋盘信息的表示,就需要实现如何更新棋盘信息。这里实现可能略微复杂,没有做代码的精简。在象棋百科中有通过棋盘旋转的方式来获取不同方向的信息,那里是使用通过和一个魔法数位运算来实现的,理论上这里也是可以的。
这里具体实现时需要注意三点:一是边界点的判定,二是位运算如何某数位置0或者置1,,三是位移量的求解。

void GameBoard::setSearchBoardPiece(const MPoint &position, MPlayerType player)
{
   

    int row = position.x();
    int col = position.y();

    if(!isValidSearchPosition(row,col)) return ;

    if(player == PLAYER_WHITE)
    {
   
        //player == white(1)
        searchBoard[row] |= (1 << col);
        searchBoardMask[row] |= (1 << col);

        searchBoardVertical[col] |= (1 << row);
        searchBoardVerticalMask[col] |= (1<<row);

        //主对角线[右下]
        if(abs(col-row)<=boardSize-5)
        {
   
            if(row>col){
   
                searchBoardMainDiag[boardSize- 5 - row + col] |=(1 << col);
                searchBoardMainDiagMask[boardSize- 5 - row + col] |=(1 << col);
            }
            else{
   
                searchBoardMainDiag[boardSize- 5 + col - row] |= (1 << row);
                searchBoardMainDiagMask[boardSize- 5 + col - row] |= (1 << row);
            }
        }

        //副对角线[右上]
        if(row+col>=6 && row+col<= boardSize*2-4)
        {
   
            if(col < boardSize -row  + 1){
   
                searchBoardSubDiag[row + col - 6] |= (1 << col);
                searchBoardSubDiagMask[row + col - 6] |= (1 << col);
            }
            else{
   
                searchBoardSubDiag[row+col-6] |= (1 << (boardSize+1-row));
                searchBoardSubDiagMask[row+col-6] |= (1 << (boardSize+1-row));
            }
        }
    }
    else if(player == PLAYER_BLACK)
    {
   
        //player == black(0)
        searchBoard[row] &= ~(1 << col);
        searchBoardMask[row] |= (1 << col);

        searchBoardVertical[col] &= ~(1 << row);
        searchBoardVerticalMask[col] |= (1<<row);

        //主对角线
        if(abs(col-row)<=boardSize-5)
        {
   
            if(row>col){
   
                searchBoardMainDiag[boardSize- 5 - row + col] &= ~(1 << col);
                searchBoardMainDiagMask[boardSize- 5 - row + col] |=(1 << col);
            }
            else{
   
                searchBoardMainDiag[boardSize- 5 + col - row] &= ~(1 << row);
                searchBoardMainDiagMask[boardSize- 5 + col - row] |= (1 << row);
            }
        }

        //副对角线
        if(row+col>=6)
        {
   
            if(col < boardSize -row  + 1){
   
                searchBoardSubDiag[row + col - 6] &= ~(1 << col);
                searchBoardSubDiagMask[row + col - 6] |= (1 << col);
            }
            else{
   
                searchBoardSubDiag[row+col-6] &= ~(1 << (boardSize+1-row));
                searchBoardSubDiagMask[row+col-6] |= (1 << (boardSize+1-row));
            }
        }
    }
    else
    {
   
        //player==none(2)
        searchBoardMask[row] &= ~(1 << col);
        searchBoardVerticalMask[col] &= ~(1 << row);

        searchBoard[row] &= ~(1 << col);
        searchBoardVertical[col] &= ~(1 << row);


        //主对角线
        if(abs(col-row)<=10)
        {
   
            if(row>col){
   
                searchBoardMainDiagMask[boardSize- 5 - row + col] &= ~(1 << col);
                searchBoardMainDiag[boardSize- 5 - row + col] &= ~(1 << col);
            }
            else{
   
                searchBoardMainDiagMask[boardSize- 5 + col - row] &= ~(1 << row);
                searchBoardMainDiag[boardSize- 5 + col - row] &= ~(1 << row);
            }
        }

        //副对角线
        if(row+col>=6)
        {
   
            if(col < boardSize -row  + 1){
   
                searchBoardSubDiagMask[row + col - 6] &= ~(1 << col);
                searchBoardSubDiag[row + col - 6] &= ~(1 << col);
            }
            else{
   
                searchBoardSubDiagMask[row+col-6] &= ~(1 << (boardSize+1-row));
                searchBoardSubDiag[row+col-6] &= ~(1 << (boardSize+1-row));
            }
        }
    }
}

相关推荐

  1. 基于博弈开源五子棋AI教程[5 启发式搜索]

    2023-12-22 13:38:03       40 阅读
  2. 基于博弈开源五子棋AI教程[6 置换表]

    2023-12-22 13:38:03       38 阅读
  3. 基于博弈开源五子棋AI教程[7 多线程搜索]

    2023-12-22 13:38:03       24 阅读
  4. Python项目开发实战:五子棋游戏(案例教程)

    2023-12-22 13:38:03       13 阅读
  5. 基于Pythonpygame库五子棋游戏

    2023-12-22 13:38:03       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-22 13:38:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-22 13:38:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-22 13:38:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-22 13:38:03       20 阅读

热门阅读

  1. Redis的缓存持久化以及缓存淘汰策略

    2023-12-22 13:38:03       41 阅读
  2. C/C++ 基础函数

    2023-12-22 13:38:03       38 阅读
  3. GO设计模式——24、策略模式(行为型)

    2023-12-22 13:38:03       41 阅读
  4. python 条件控制语句(基础)学习笔记

    2023-12-22 13:38:03       36 阅读
  5. js获取年月日的格式

    2023-12-22 13:38:03       30 阅读
  6. Flink系列之:Elasticsearch SQL 连接器

    2023-12-22 13:38:03       38 阅读
  7. git如何修改提交代码时的名字和邮箱?

    2023-12-22 13:38:03       39 阅读