ai五子棋的实现

一、棋型模式分析
构造有’0’和’1’构成的序列,来一一映射棋型如:‘活四’、‘冲三’、‘冲四’、‘活三’、‘活二’、‘冲二’等等。
思考:如何构造这样的序列?
对于一个棋子的得分评估,我是应该用’00001′,’00010′,’00100’类似这样的方式来映射吗?
如果以这样的方式对应,当我遇到活二的棋型时,我评估棋局时会出现这样的情况,计算机有可能会把它当作两个孤立棋子的分数进行评估,即分别对应’00001’和’10000’两个棋型的分数进行相加才是活二的应有的分数。
所以有以下评估棋型的方式,如果我想按棋子数目来分类,并进行棋型分数评估,我以一种对称的方式进行则比较好,五位序列代表冲棋形式,六位代表活四形式。

点击查看代码

//Key-val分值表
std::vector<Pattern> patterns = {
    { "11111",  50000 },
    { "011110", 4320 },
    { "011100", 720 },
    { "001110", 720 },
    { "011010", 720 },
    { "010110", 720 },
    { "11110",  720 },
    { "01111",  720 },
    { "11011",  720 },
    { "10111",  720 },
    { "11101",  720 },
    { "001100", 120 },
    { "001010", 120 },
    { "010100", 120 },
    { "000100", 20 },
    { "001000", 20 },
};

得分函数: 点击查看代码

int aiJudge::getScore(bool color, int x, int y, int g[][N])
{
    int ans = 0;
    int dir = -1;
    int nowcolor = color ? 1 : -1;
    //获取8联通方向的棋子排布
    while (++dir < 4) {
        std::deque<int> S;
        S.push_back(1);
        int sign = -1;
        for (int i = 1; i < 4; ++i) {
            int tx = x + sign * i * dx[dir], ty = y + sign * i * dy[dir];
            if (!posIsLegal(tx, ty)){
                S.push_front(2);
                break;
            }
            if (g[tx][ty] == 0) {
                S.push_front(0);
            }
            if (g[tx][ty] == nowcolor)
                S.push_front(1);
            if (g[tx][ty] == -nowcolor) {
                S.push_front(2);
                break;
            }
        }
        sign = -sign;
        for (int i = 1; i <4; ++i) {
            int tx = x + sign * i * dx[dir], ty = y + sign * i * dy[dir];
            if (!posIsLegal(tx, ty)){
                S.push_back(2);
                break;
            }
            if (g[tx][ty] == 0) {
                S.push_back(0);
            }
            if (g[tx][ty] == nowcolor)
                S.push_back(1);
            if (g[tx][ty] == -nowcolor) {
                S.push_back(2);
                break;
            }
        }
        std::string s = "";
        for(auto i:S){
            s+='0'+i;
        }
        //Ac自动机匹配
        std::vector<int> tmp = acs->ACSearch(s);
        for (int j = 0; j <(int)tmp.size(); j++) {
            ans += patterns[tmp[j]].score;
        }
    }

    return ans;
}

bool color: 当前计算的棋子的颜色,true 表示黑子,false 表示白子。
int x, y: 棋盘上的位置坐标(即当前棋子的位置)。
int g[][N]: 表示棋盘的二维数组,1 表示黑子,-1 表示白子,0 表示空位。
主要变量:
int ans: 当前棋子的局部总得分。
int dir: 用来表示遍历的四个方向(左右、上下、左上右下、右上左下)。
int nowcolor: 当前计算的棋子颜色,1 表示黑子,-1 表示白子。
std::deque S: 用于存储当前方向上,当前棋子周围 4 格内的棋子状态,0 表示空位,1 表示当前棋色的棋子,2 表示对方棋色的棋子。
函数执行过程:
Step 1: 初始化棋子颜色和方向
首先,nowcolor 通过 color 参数设定为 1(黑子)或者 -1(白子),并准备遍历 4 个方向。

Step 2: 遍历四个方向(dir)
dir 代表四个方向:

0 表示左右方向;
1 表示上下方向;
2 表示左上到右下方向;
3 表示右上到左下方向。
Step 3: 获取每个方向的棋子排布
对于每个方向,代码分两次从当前位置往两侧(左/上 和 右/下)扩展,最多查找 4 格范围内的棋子,并将这些信息存入双端队列 S 中。具体步骤如下:

3.1. 向左或向上方向扩展(sign = -1)
从当前棋子的左侧或上方开始,依次检查 4 格内的棋子状态:

posIsLegal(tx, ty): 检查是否越界。如果越界,则往 S 中添加一个 2(表示边界,无法继续扩展),并结束当前方向的扩展。
g[tx][ty] == 0: 如果是空位,则添加 0。
g[tx][ty] == nowcolor: 如果是同色棋子,则添加 1。
g[tx][ty] == -nowcolor: 如果是对方的棋子,则添加 2,并终止继续扩展。
这些信息会被加入 S 的前端(push_front),形成当前方向左侧/上方的棋子序列。

3.2. 向右或向下方向扩展(sign = 1)
接着,代码从当前棋子右侧或下方开始,依次检查 4 格内的棋子状态,采用类似的逻辑,将结果添加到 S 的后端(push_back)。

此时,S 包含了当前方向上当前棋子以及其左右/上下两侧的棋子状态。

Step 4: 将队列 S 转换为字符串
将队列 S 中的内容转换为字符串 s,每个状态(空位、同色、异色)分别用字符 ‘0’、’1’、’2′ 表示:****

来源链接:https://www.cnblogs.com/czx12269/p/18848285

© 版权声明
THE END
支持一下吧
点赞5 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容