摘要:棋类游戏一般需要行数列数状态数个状态,以华容道为例,需要为个状态分配编码值。对于华容道游戏来说,这种左右镜像的情况对于滑动棋子寻求结果的影响是一样的。
此为《算法的乐趣》读书笔记,我用javascript(ES6)重新实现算法。
华容道游戏看似简单,但求解需要设计的数据结构比较复杂,还牵涉到棋类游戏的棋局判断,所以整个过程还是挺费劲的。我尽量用面向对象的思想来进行封装,整个过程将分成几个部分记录下来,今天是第二部分,棋局处理Zobrist算法原理及实现。Zobrist算法原理
Zobrist哈希算法是一种适用于棋类游戏的棋局编码方式,通过建立一个特殊的转换表,对棋盘上每一个位置的所有可能状态赋予一个绝不重复的随机编码,通过对不同位置上的随机编码进行异或计算,实现在极低冲突率的前提下将复杂的棋局编码为一个整数类型哈希值的功能。
Zobrist哈希算法步骤:识别出棋局的最小单位(格子或交叉点),确定每个最小单位上的所有可能的状态数。以华容道的棋局为例,最小单位就是20个小格子,每个格子有五种状态,分别是空状态、被横长方形占据、被坚长方形占据、被小方格占据和被大方格占据。
为每个单位上的所有状态都分配一个随机的编码值。棋类游戏一般需要“行数×列数×状态数”个状态,以华容道为例,需要为5×4×5=100个状态分配编码值。
对指定的棋局,对每个单位上的状态用对应的编码值(随机数)做异或运算,最后得到一个哈希值。
Zobrist哈希算法优点:冲突概率小,只要随机编码值的范围够大,棋局哈希冲突的概率非常小,实际应用中基本上不考虑冲突的情况。
棋局发生变化时,不必对整个棋局重新计算哈希值,只需要计算发生变化的那些最小单元的状态变化即可。
Zobrist算法实现 编码表定义编码表定义为一个三维数组。
class Zobrist{ constructor(){ //三维表属性 this.zobHash = [] for(let i = 0; i < HRD_GAME_ROW; i++) //初始化 { this.zobHash.push([]) for(let j = 0; j < HRD_GAME_COL; j++) { this.zobHash[i].push([]) for(let k = 0; k < MAX_WARRIOR_TYPE; k++) { do{ var tmp = Math.random() tmp = Math.floor(tmp * Math.pow(2,15)) //对16位随机整数值 }while(!tmp) //跳过零值 this.zobHash[i][j].push(tmp) } } } } get(i,j,k){ //get接口 return this.zobHash[i][j][k] } }计算棋局Zobrist哈希值
对棋盘的格子逐个处理,根据棋盘格子的武将信息获取武将的类型,从而获取该类型对应的编码值,用此编码值参与哈希值进行异或运算。
function getZobristHash(zobHash, state) { let hash = 0; let heroes = state.heroes; for(let i = 1; i <= HRD_GAME_ROW; i++) { for(let j = 1; j <= HRD_GAME_COL; j++) { let index = state.board[i][j] - 1; //取得格子上武将序号 let type = (index >= 0 && index < heroes.length) ? heroes[index].type : 0; //数组索引值超出范围,定为零 hash ^= zobHash.get(i - 1,j - 1,type); //异或计算 // console.log(index+"--"+type+"--"+zobHash[i - 1][j - 1][type]+"<=>"+hash) } } return hash; }取镜像Zobrist哈希值
棋盘状态左右镜像问题:两个棋局虽然武将的位置不一样,但是如果忽略武将的名字信息,单纯从形状上看是左右对称的镜像结构。对于华容道游戏来说,这种左右镜像的情况对于滑动棋子寻求结果的影响是一样的。
镜像即左右对称,进行一个坐标变换即可得到。
function getMirrorZobristHash(zobHash, state) { let hash = 0; let heroes = state.heroes; for(let i = 1; i <= HRD_GAME_ROW; i++) { for(let j = 1; j <= HRD_GAME_COL; j++) { let index = state.board[i][j] - 1; let type = (index >= 0 && index < heroes.length) ? heroes[index].type : 0; //(HRD_GAME_COL - 1) - (j - 1)) 坐标变换 hash ^= zobHash.get(i - 1,HRD_GAME_COL - j,type); } } return hash; }程序测试
设计了三个棋局,测试目标:同一个棋局的Zobrist哈希与镜像哈希相等,镜像棋局的Zobrist哈希与其镜像哈希相等。
var zobHash = new Zobrist() var gameState = new HrdGameState() var gameStateL = new HrdGameState() var gameStateR = new HrdGameState() var hs = [new Warrior(WARRIOR_TYPE.HT_VBAR,0,0), new Warrior(WARRIOR_TYPE.HT_BOX,1,0), new Warrior(WARRIOR_TYPE.HT_VBAR,3,0), new Warrior(WARRIOR_TYPE.HT_VBAR,0,2), new Warrior(WARRIOR_TYPE.HT_HBAR,1,2), new Warrior(WARRIOR_TYPE.HT_VBAR,3,2), new Warrior(WARRIOR_TYPE.HT_BLOCK,0,4), new Warrior(WARRIOR_TYPE.HT_BLOCK,1,3), new Warrior(WARRIOR_TYPE.HT_BLOCK,2,3), new Warrior(WARRIOR_TYPE.HT_BLOCK,3,4) ] var hsl = [ new Warrior(WARRIOR_TYPE.HT_BOX,0,0), new Warrior(WARRIOR_TYPE.HT_VBAR,2,0), new Warrior(WARRIOR_TYPE.HT_VBAR,3,0), new Warrior(WARRIOR_TYPE.HT_HBAR,0,2), new Warrior(WARRIOR_TYPE.HT_BLOCK,2,2), new Warrior(WARRIOR_TYPE.HT_VBAR,3,2), new Warrior(WARRIOR_TYPE.HT_VBAR,0,3), new Warrior(WARRIOR_TYPE.HT_BLOCK,1,3), new Warrior(WARRIOR_TYPE.HT_BLOCK,1,4), new Warrior(WARRIOR_TYPE.HT_BLOCK,3,4) ] var hsr = [ new Warrior(WARRIOR_TYPE.HT_VBAR,0,0), new Warrior(WARRIOR_TYPE.HT_VBAR,1,0), new Warrior(WARRIOR_TYPE.HT_BOX,2,0), new Warrior(WARRIOR_TYPE.HT_VBAR,0,2), new Warrior(WARRIOR_TYPE.HT_BLOCK,1,2), new Warrior(WARRIOR_TYPE.HT_HBAR,2,2), new Warrior(WARRIOR_TYPE.HT_BLOCK,2,3), new Warrior(WARRIOR_TYPE.HT_VBAR,3,3), new Warrior(WARRIOR_TYPE.HT_BLOCK,0,4), new Warrior(WARRIOR_TYPE.HT_BLOCK,2,4) ] gameState.initState(hs) gameStateL.initState(hsl) gameStateR.initState(hsr) console.dir(getZobristHash(zobHash,gameStateL) + "--" + getMirrorZobristHash(zobHash,gameStateR)) //两值相等完整代码
代码托管在开源中国,其中的hyd.js即华容道解法。
https://gitee.com/zhoutk/test小结
尽量使用面向对象的思想来解决问题,让数据和操作绑定在一起,努力使代码容易看懂。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/79340.html
摘要:华容道游戏看似简单,但求解需要设计的数据结构比较复杂,还牵涉到棋类游戏的棋局判断,所以整个过程还是挺费劲的。 此为《算法的乐趣》读书笔记,我用javascript(ES6)重新实现算法。 华容道游戏看似简单,但求解需要设计的数据结构比较复杂,还牵涉到棋类游戏的棋局判断,所以整个过程还是挺费劲的。我尽量用面向对象的思想来进行封装,整个过程将分成几个部分记录下来,今天是最后一部分,棋局的广...
摘要:还在上班很无聊数字华容道畅玩地址开发源码地址这个叫前言年末了。光随机生成一个乱序数列是不够的,还得保证这个数列的逆序数为偶数,嗦嘎。所以,我们直接将交换的次数,记为数列逆序数个数,就达到了想要的效果。 还在上班?很无聊?数字华容道畅玩地址 开发源码地址 这个叫前言 年末了。哦,不,要过年了。以前只能一路站到公司的我,今早居然是坐着过来的。新的一年,总要学一个新东西来迎接新的未来吧,所以...
摘要:下面,我想用浅显易懂的语言,简述一下区块链游戏的机会。同时这也是第一个从游戏机制到设计来说,整体完成度较高的区块链游戏,其他同期的小游戏基本是在原型或者阶段。 作者:Vincent, DappReview CEO编辑:米芽 复盘过去十年,站在每一个时间节点都有大大小小机会。而往前看,似乎却一片迷茫。 本文以一个深度参与者的身份,用寥寥几千字,试图还原区块链游戏在过去半年的发展,并...
阅读 2830·2021-09-28 09:45
阅读 1506·2021-09-26 10:13
阅读 897·2021-09-04 16:45
阅读 3660·2021-08-18 10:21
阅读 1083·2019-08-29 15:07
阅读 2632·2019-08-29 14:10
阅读 3146·2019-08-29 13:02
阅读 2458·2019-08-29 12:31