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

0 引子

  1. 概念
    位棋盘(Bitboard)是一种在计算机程序中表示棋盘游戏状态的数据结构。它使用单个的二进制数字(bits)来表示棋盘上每一个可能的位置,使得棋盘的整个状态可以通过一个或几个长整型数字来表示。
    位棋盘应用于五子棋AI中有以下优势:

  2. 优势
    空间效率:位棋盘使用二进制位来表示棋盘上的每个位置,这比使用一个字节或更多的数据结构要节省空间得多。
    计算效率:位棋盘允许棋盘游戏的程序使用位操作来快速处理游戏状态。

  3. 做法
    常见的五子棋棋盘大小为15x15,最直观的表示就是一个二维数据。本文一开始使用的是QVector<QVector<int>>的数据,但是在分支因子为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)
{
    //黑子置0
    //白子置1
    int row = position.x();
    int col = position.y();

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

    auto updateBit = [](int& board, bool setOneFlag, int shift) {
        // board在偏移shift更改bit位
        if (setOneFlag) board |= (1 << shift);//置1
        else board &= ~(1 << shift);//置0
    };

    bool isWhite = (player == PLAYER_WHITE);
    bool isNone = (player == PLAYER_NONE);

    // 水平
    updateBit(searchBoard[row], isWhite, col);
    updateBit(searchBoardMask[row], !isNone, col);
    // 竖直
    updateBit(searchBoardVertical[col], isWhite, row);
    updateBit(searchBoardVerticalMask[col], !isNone, row);
    // 主对角线
    bool onMainDiag = abs(col - row) <= boardSize - 5;
    if (onMainDiag) {
        int index = boardSize - 5 - row + col;
        int shift = (row > col) ? col : row;
        updateBit(searchBoardMainDiag[index], isWhite, shift);
        updateBit(searchBoardMainDiagMask[index], !isNone, shift);
    }
    // 副对角线
    bool onSubDiag = (row + col >= 6) && (row + col <= boardSize * 2 - 4);
    if (onSubDiag) {
        int index = row + col - 6;
        int shift = (col < boardSize - row  + 1) ? col : boardSize + 1 - row;
        updateBit(searchBoardSubDiag[index], isWhite, shift);
        updateBit(searchBoardSubDiagMask[index], !isNone, shift);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值