此为《算法的乐趣》读书笔记,我用javascript(ES6)从新实现算法。javascript
华容道游戏看似简单,但求解须要设计的数据结构比较复杂,还牵涉到棋类游戏的棋局判断,因此整个过程仍是挺费劲的。我尽可能用面向对象的思想来进行封装,整个过程将分红几个部分记录下来,今天是第二部分,棋局处理Zobrist算法原理及实现。
Zobrist哈希算法是一种适用于棋类游戏的棋局编码方式,经过创建一个特殊的转换表,对棋盘上每个位置的全部可能状态赋予一个毫不重复的随机编码,经过对不一样位置上的随机编码进行异或计算,实如今极低冲突率的前提下将复杂的棋局编码为一个整数类型哈希值的功能。java
编码表定义为一个三维数组。git
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] } }
对棋盘的格子逐个处理,根据棋盘格子的武将信息获取武将的类型,从而获取该类型对应的编码值,用此编码值参与哈希值进行异或运算。算法
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; }
棋盘状态左右镜像问题:两个棋局虽然武将的位置不同,可是若是忽略武将的名字信息,单纯从形状上看是左右对称的镜像结构。对于华容道游戏来讲,这种左右镜像的状况对于滑动棋子寻求结果的影响是同样的。
镜像即左右对称,进行一个坐标变换便可获得。数组
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即华容道解法。dom
https://gitee.com/zhoutk/test
尽可能使用面向对象的思想来解决问题,让数据和操做绑定在一块儿,努力使代码容易看懂。测试