五子棋 您所在的位置:网站首页 我想知道五子棋 五子棋

五子棋

2023-05-26 02:54| 来源: 网络整理| 查看: 265

上一篇文章 五子棋 - JavaScript 实现 - 两人对战 我们介绍了人与人之间下棋,还挖了个坑:讲人机交互下棋。不知不觉中,把自己打包给卖了,本文就是来补坑的。

没反应过来.png

我们一步步来讲解,详细的代码,请跳转到文末。

基本术语

我们先来了解一下五子棋的基本术语。因为之前是介绍人和人玩,只要形成五子相连就行了,可以对概念不理解。但是这是人机娱乐,总得让机器知道五子棋的规则,不然机器乱下就没意思了。

1 代表黑子,-1 代表白子,0 代表空格。以黑子为主要说明,白子同理

连五:五个同色的棋子连成一条线,则有 [1, 1, 1, 1, 1] 活四:有两个可以形成的五子连珠的点,并且连续的四子,则有 [0, 1, 1, 1, 1, 0] 冲四:有且只有一个点可以形成连五的四,则有跳冲 [-1, 1, 0, 1, 1, 1],[-1, 1, 1, 0, 1, 1],[-1, 1, 1, 1, 0, 1],[1, 0, 1, 1, 1, -1],[1, 1, 0, 1, 1, -1],[1, 1, 1, 0, 1, -1],和连冲 [-1, 1, 1, 1, 1, 0],[0, 1, 1, 1, 1, -1] 活三:能够形成活四的三个点,则有连活三 [0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0],和跳活三 [0, 1, 0, 1, 1, 0],[0, 1, 1, 0, 1, 0] 眠三:能够形成冲四而不能形成活四的三,类似冲四,则有 [-1, 1, 1, 1, 0, 0],[-1, 1, 1, 0, 1, 0], [-1, 1, 0, 1, 1, 0],[0, 0, 1, 1, 1, -1],[0, 1, 0, 1, 1, -1], [0, 1, 1, 0, 1, -1],[-1, 1, 0, 1, 0, 1, -1],[-1, 0, 1, 1, 1, 0, -1],[-1, 1, 1, 0, 0, 1, -1], [-1, 1, 0, 0, 1, 1, -1] 活二:能够形成活三的二子,类似活三,则有 [0, 0, 1, 1, 0, 0], [0, 1, 0, 1, 0, 0],[0, 0, 1, 0, 1, 0],[0, 1, 1, 0, 0, 0],[0, 0, 0, 1, 1, 0],[0, 1, 0, 0, 1, 0]。 眠二:能够形成眠三而不能形成活三的二子,意义不大,不做计算。当然,读者可以添加 活一:同理,能形成活二的一子 眠一:同理,能形成眠二而不能形成活二的一子 天元:指棋盘中间的点。这里人机交互,默认是机器执黑子先落子。棋盘预设是 15 * 15,所以,天元的位置是 [7, 7] 的坐标。

这里的代码有点长,不贴代码。可进入文末的项目查看。好了,机器知道了必要的棋局(这里计算了关键的连五、活四、活三、活二、冲四、眠三)。

pexels-karolina-grabowska-5902271.jpg

关键得分

棋局知道了,那么,我们得知道对应棋局的分值,来计算玩家和机器的目前得分情况,以便机器明确自己要进攻还是防守。赋分如下:

/* * 预设不同的组合对应的得分 * @param { number } w 连五 * @param { number } u2 活二 * @param { number } u3 活三 * @param { number } u4 活四 * @param { number } c3 眠三 * @param { number } c4 眠四 * @return { number } 当前棋局的得分情况 */ function valueCombo(w, u2, u3, u4, c3, c4) { // ... return 0; } 复制代码

棋局的评分是针对四个方向进行统计,也就是对 横线,竖线,正斜线(角度45。45^。45。)和反斜线(角度135。135^。135。)四条线上的数据统计。

/* * 获取当前的组合 * @param { array[][] } node 棋盘节点情况 * @param EnumRoles.BLACK | EnumRoles.WHITE curPlayer 当前玩家 * @param { number } i 棋盘横轴遍历 * @param { number } y 棋盘横轴遍历 * @param { number } dx 棋盘横轴偏移位置 * @param { number } dy 棋盘纵轴偏移位置 * @return { array[] } combo 返回当前方向的当前玩家的节点情况,比如 [0, 0, 0, 0, -1, 0, 0, 0, 0]。combo.length 最长为 9 = 2 * gameSize - 1 */ function getCombo(node, curPlayer, i, j, dx, dy) { let combo = [curPlayer]; // ... return combo } 复制代码

当然,我们也可以使用这种方法来判断输赢。

/* * 检查输赢,针对四个方向进行判断 */ function checkWin() { for (let i = 0; i < cellsCount; i++) { for (let j = 0; j < cellsCount; j++) { if (curState[i][j] == 0) continue; let playerVal = combinations.valuePosition( // 水平方向 getCombo(curState, curState[i][j], i, j, 1, 0), // 竖直方向 getCombo(curState, curState[i][j], i, j, 0, 1), // 正斜线方向 getCombo(curState, curState[i][j], i, j, 1, 1), // 反斜线方向 getCombo(curState, curState[i][j], i, j, 1, -1) ); if (playerVal === combinations.winValue) { win = true; } } } }; 复制代码

或者读者可以使用五子棋 - JavaScript 实现 - 两人对战 中判断输赢的方法

机器落子

人机模式下棋,初始化机器先落子于天元的位置。

// 实例化 let gobangMachine2Person = new GobangMachine2Person({ role: EnumRoles.WHITE, gobangStyle: { count: cellsCount, borderColor: '#bfbfbf' } }) // 落子于天元的位置 gobangMachine2Person.drawChessman({ x: 7, y: 7 }, true); // 设置当前角色 gobangMachine2Person.setCurrentRole(); // 设置提示信息 gobangMachine2Person.setResultMsgHint(); 复制代码

然后监听人落子后,基于其落子位置,机器思考最优落子位置:

listenDownChessman() { this.checkerboardDom.onclick = event => { // ... // 基于白子的落子位置,算出机器的落子位置 let answer = login.makeAnswer(x, y); // 绘制黑子 this.drawChessman({ x: answer[0], y: answer[1] }, true); } } 复制代码

那么,机器的最优子落子位置 login.makeAnswer(x, y) 是如何算出来的呢?

这里的最优,是相对而言;并不是整个棋盘最合适的那个落子位置,是绝对而言。最合适这个位置需要遍历整个棋盘,会很耗电脑,得不偿失,具体可以参考文章深度优先搜索实现 AI 井字游戏

我们通过极大极小值算法,算出最最优位置。我们先对极大极小值算法有个概念:

Minmax 算法又名极小化极大算法,是一种找出失败的最大可能性中的最小值的算法(即最小化对手的最大得益)。通常以递归的形式来实现。

先挖个坑,后面有文章详细讲解这个搜索算法。还有 Alpha-beta 剪枝这个搜索算法。不知不觉又挖了两个坑...本文,读者有个概念就行了~ 最主要捋清楚人机的整个流程...

/* * 获取最优的落子 * param { number } x 白子落点 x 轴 * param { number } y 白子落点 y 轴 * return { array[] } 返回最优落子位置 */ getLogic.makeAnswer = function(x, y) { let answ = [-1, -1]; // 预设的最佳位置,在棋盘外,这个随便 // 获取候选值 let c = getChilds(curState, maxPlayer); let maxChild = -1; let maxValue = Number.MIN_VALUE; // 最小的正值 for (let k = 0; k < c.length; k++) { // 计算当前的得分值 let curValue = miniMax(c[k], 0, -maxPlayer, curState); if (maxValue < curValue) { maxValue = curValue; maxChild = k; // 获取最大值的索引 } } // ... return answ; } 复制代码 完整项目

项目可以进行人机,双人娱乐。当然,读者可以根据实际情况,添加诸如 悔棋,复盘 等辅助功能。

参考文章 极大极小值算法 对抗搜索 五子棋基本棋形及特点 五子棋AI进阶:极大极小值搜索 五子棋算法设计


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有