JavaScript中国象棋程序(3) - 电脑自动走棋
“JavaScript中国象棋程序” 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序。这是教程的第3节。
这一节,程序将可以自动行棋。但仅仅是走了一步符合象棋规则的棋,电脑智商为0。
3.1、帅(将)的走法生成
使用一个辅助数值表示这4个方向:
var KING_DELTA = [-16, -1, 1, 16];
已知帅在一维棋局数组中的起点位置sqSrc。生成帅的走法,就是获取帅全部的合法终点sqDes。使用一个数组存储所有可能的走法,伪代码如下:
for (var i = 0; i < 4; i ++) { // 将的4个方向
var sqDst = sqSrc + KING_DELTA[i]; // 得到一个可能的终点位置
if (该位置不位于九宫中) {
// 该走法不合法,执行下一轮循环
continue;
}
var pcDst = 终点位置的棋子; // 如果终点位置没有棋子,那么pcDst=0
if (pcDst不是本方棋子) {
走法合法,保存到步骤数组中
}
}
3.2、仕的走法生成
同样使用辅助数组表示仕的4个方向:
var ADVISOR_DELTA = [-17, -15, 15, 17];
生成仕的走法,伪代码如下:
for (var i = 0; i < 4; i ++) { // 仕的4个方向
var sqDst = sqSrc + ADVISOR_DELTA[i]; // 得到一个可能的终点位置
if (该位置不位于九宫中) {
// 该走法不合法,执行下一轮循环
continue;
}
var pcDst = 终点棋子; // 如果终点位置没有棋子,那么pcDst=0
if (pcDst不是本方棋子) {
走法合法,保存到步骤数组中
}
}
3.3、象的走法生成
我们并不用设置一个类似[-34, -30, 30, 37]的数组保存象的方向。因为仕的方向,跟象眼的方向一致。仕方向的二倍,就是象的方向。
生成象的走法,伪代码如下:
for (var i = 0; i < 4; i ++) { // 象的4个方向
var sqDst = sqSrc + ADVISOR_DELTA[i]; // 获得象眼的位置
if (象眼不在棋盘上,或者象眼位置已过河,或者象眼存在棋子) {
// 位置不合法,执行下一轮循环
continue;
}
sqDst += ADVISOR_DELTA[i]; // 得到一个可能的终点位置
var pcDst = 终点位置的棋子 // 如果终点位置没有棋子,那么pcDst=0
if (pcDst不是本方棋子) {
走法合法,保存到步骤数组中
}
}
3.4、马的走法生成
用辅助数组表示马的方向:
KNIGHT_DELTA = [[-33, -31], [-18, 14], [-14, 18], [31, 33]];
对应马腿的4个方向,与帅的4个方向是一样的。
生成马的走法,伪代码如下:
for (var i = 0; i < 4; i ++) { // 马腿的4个方向
var sqDst = sqSrc + KING_DELTA[i]; // 得到一个马腿的位置
if (马腿位置存在棋子) {
continue;
}
for (var j = 0; j < 2; j ++) { // 1个马腿对应2个马的方向
sqDst = sqSrc + KNIGHT_DELTA[i][j]; // 得到一个马的可能的终点位置
if (该位置不在棋盘上) {
continue;
}
var pcDst = 终点位置的棋子; // 如果终点位置没有棋子,那么pcDst=0
if (pcDst不是本方棋子) {
走法合法,保存到步骤数组中
}
}
}
3.5、车的走法生成
车的方向与帅的方向相同,只不过车可以连续走下去。
生成车的走法,伪代码如下:
for (var i = 0; i < 4; i ++) {
var delta = KING_DELTA[i]; // 得到一个方向
var sqDst = sqSrc + delta; // 从起点sqSrc开始,沿着方向delta走一步
while (sqDst在棋盘上) {
var pcDst = sqDst位置的棋子;
if (pcDst == 0) { // sqDst位置上根本就没有棋子
走法合法,保存到步骤数组中
} else {
if (pcDst是对方的棋子) {
走法合法,保存到步骤数组中
}
// 已经遇到了对方棋子,终止循环
break;
}
sqDst += delta; // 沿着方向delta向前走一步
}
}
3.6、炮的走法生成
炮的走法与车类似,但炮遇到一个棋子后,可以越过去,也就是翻山,并吃掉一个对方棋子。
生成炮的走法,伪代码如下:
for (var i = 0; i < 4; i ++) {
var delta = KING_DELTA[i]; // 得到一个方向
var sqDst = sqSrc + delta; // 从起点sqSrc开始,沿着方向delta走一步
while (sqDst在棋盘上) {
var pcDst = sqDst位置的棋子;
if (pcDst == 0) { // sqDst位置上根本就没有棋子
走法合法,保存到步骤数组中
} else { // 终点存在棋子,炮需要翻山
break;
}
sqDst += delta; // 沿着方向delta向前走一步
}
sqDst += delta; // 沿着方向delta向前走一步
while (IN_BOARD(sqDst)) { // 如果sqDst仍位于棋盘,那么此时炮已经翻山了
var pcDst = sqDst位置的棋子;
if (pcDst > 0) { // 炮翻山后遇到了一个棋子
if (pcDst是对方棋子) {
走法合法,保存到步骤数组中
}
// 炮翻山后,不管遇到的是对方棋子,还是己方棋子,都要结束对当前方向的搜索
break;
}
sqDst += delta;
}
}
七、兵的走法生成
红兵和黑卒向前走的方向是不一样的,分别是-16和16。在上一节,我们已经介绍了下面的函数:
// sp是棋子位置,sd是走棋方(红方0,黑方1)。返回兵(卒)向前走一步的位置。
function SQUARE_FORWARD(sq, sd) {
return sq - 16 + (sd << 5);
}
该函数可以获得兵(卒)前进一步的位置。
生成兵的走法,伪代码如下:
var sqDst = SQUARE_FORWARD(sqSrc, this.sdPlayer); // 得到兵(卒)前进一步的位置
if (sqDst在棋盘上) {
var pcDst = sqDst位置的棋子;
if (pcDst不是本方棋子) {
走法合法,保存到步骤数组中
}
}
if (这个兵(卒)已过河) {
for (var delta = -1; delta <= 1; delta += 2) {
// delta只能取-1和1两个值,这正是兵(卒)的左右两个方向
sqDst = sqSrc + delta;
if (sqDst在棋盘上) {
var pcDst = sqDst位置的棋子;
if (pcDst不是本方棋子) {
走法合法,保存到步骤数组中
}
}
}
}
3.8、电脑先走功能的实现
如果我们选择了“电脑先走”,并点击“重新开始”按钮,那么电脑会执红先走。红棋显示在上方,黑棋显示再下方,并且红棋会先走一步,如下图所示:
这其实就是在视觉上,将原来的棋盘旋转180°。例如,本来显示在左上角的黑车,现在显示在右下角的位置。在一维棋盘数组中,左上角的位置是51,右下角的位置是203。也就是说,要想实现对棋盘旋转180°,只需将sq位置的棋子,显示在254-sq的位置。如下函数就是实现这一功能的:
function SQUARE_FLIP(sq) {
return 254 - sq;
}
当用户点击棋盘时,需要对点击的位置再执行一次SQUARE_FLIP函数,就可以转换为用户点击的棋盘数组中的位置。
3.9、核心代码说明
本节的代码可以在 Github 下载,也可以直接clone
git clone -b step-3 https://github.com/Royhoo/write-a-chinesechess-program
这一节我们引入一个新的对象Search,负责实现搜索算法。目前我们的搜索算法很简单,就是生成全部走法后随机选择一个。
Board中新增或修改的主要属性和方法
(1)、computer
computer = 0,表示电脑执黑;computer = 1,表示电脑执红。在index.html中,会对computer赋初值为0。
(2)、busy
busy默认为false,此时可以响应用户的点击事件。如果电脑正常思考状态下,比如正常执行搜索算法,busy会被置为true,不响应点击事件。
(3)、response()
电脑回一步棋。
(4)、restart(fen)
重新使用fen串初始化棋局。该方法会调用response(),这就实现了在电脑执红的情况下,电脑先走一步棋的功能。
(5)、retract()
悔棋。
Position中新增或修改的主要属性和方法
(1)、mvList[]
这是一个数组,保存每步的走法。悔棋的时候会用到。
(2)、pcList[]
这也是一个数组,保存每步被吃的棋子。如果这一步没有棋子被吃,那么保存的是0。
该数组也会在悔棋的时候用到。
(3)、generateMoves()
生成棋局的所有走法。
(4)、makeMove(mv)
走一步棋,主要需要以下4步:
1、删除终点棋子,并记录吃子。
2、将起点棋子放在终点。
3、保存这一走法。
4、切换走棋方。
Search中主要属性和方法
(1)、pos
Position实例。
(2)、searchMain()
搜索算法。目前非常简单,就是生成所有可能的走法,随机选择一个。
JavaScript中国象棋程序(3) - 电脑自动走棋的更多相关文章
- JavaScript中国象棋程序(0) - 前言
“JavaScript中国象棋程序” 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.希望通过这个系列,我们对博弈程序的算法有一定的了解.同时,我们也将构建出一个不错的中国象棋程序 ...
- JavaScript中国象棋程序(1) - 界面设计
"JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第1节. 这一系列共有9个部分: 0.JavaScript中国象 ...
- JavaScript中国象棋程序(2) - 校验棋子走法
"JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第2节. 这一系列共有9个部分: 0.JavaScript中国象 ...
- JavaScript中国象棋程序(4) - 极大极小搜索算法
"JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第4节. 这一系列共有9个部分: 0.JavaScript中国象 ...
- JavaScript中国象棋程序(5) - Alpha-Beta搜索
"JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第5节. 这一系列共有9个部分: 0.JavaScript中国象 ...
- JavaScript中国象棋程序(6) - 克服水平线效应、检查重复局面
"JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第6节. 这一系列共有9个部分: 0.JavaScript中国象 ...
- JavaScript中国象棋程序(7) - 置换表
"JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第2节. 这一系列共有9个部分: 0.JavaScript中国象 ...
- JavaScript中国象棋程序(8) - 进一步优化
在这最后一节,我们的主要工作是使用开局库.对根节点的搜索分离出来.以及引入PVS(Principal Variation Search,)主要变例搜索. 8.1.开局库 这一节我们引入book.js文 ...
- 中国象棋程序的设计与实现(六)--N皇后问题的算法设计与实现(源码+注释+截图)
八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题. 该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列 ...
随机推荐
- HTML 学习笔记 JQuery(表单,表格 操作)
表单应用 一个表单有3个基本组成部分 (1)表单标签:包含处理表单数据所用的服务器端程序URL 以及数据提交到服务器的方法 (2)表单域:包含文本框 密码框 隐藏框 多行文本框 复选框 单选框 下拉选 ...
- nodejs抓取数据一(列表抓取)
纯属初学...有很多需要改进的地方,请多多指点... 目标是抓取58同城 这个大分类下的列表数据: http://cd.58.com/caishui/?PGTID=14397169455980.924 ...
- Delphi 常用函数(数学函数)round、trunc、ceil和floor
源:Delphi 常用函数(数学函数)round.trunc.ceil和floor Delphi 常用函数(数学) Delphi中怎么将实数取整? floor 和 ceil 是 math unit 里 ...
- Lambda 可以转换成委托或expression树
1.关于C# Lambda Expressions: 一个Lambda Expression (译为Lambda式) 就是一个包含若干表达式和语句的匿名函数.可以被用作创建委托对象或表达式树类型.所 ...
- SVN 备忘录
上传文件夹 svn import distcomp/ svn+ssh://USERNAME@166.120.110.119/mnt/disk1/fserver/svn/distcomp -m &quo ...
- java基础面试
1. String类为什么是final的. 安全性:如果字符串是可变的,那么会引起很严重的安全问题.譬如,数据库的用户名.密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名 ...
- 好的 vim编辑博客
http://www.cnblogs.com/ma6174/archive/2011/12/10/2283393.html 如果你不满足于使用现成的颜色主题的话,那我们来看一下如何修改环境配色.首先要 ...
- windows(win10)下的mysql解压版安装
1. 到mysql官网 下载mysql : http://dev.mysql.com/downloads/mysql/ ,会提示登陆,注册一个就行了,公司名什么的随便填. 注意区分32,64位.我 ...
- HOJ 1003 Max Sum 解题报告
好几年没有做ACM了,感觉忘得差不多了,这个做着做着就打瞌睡了!言归正传,下面是我的解题思路: 首先的话,我们可以画一个函数图,以输入数组的下标为X轴,以数组的和为Y轴,当数组和小于零时,我们使用备用 ...
- js框架Modernizr是什么东西? 他是前端开发HTML5和CSS3的强有力前端js检测类库
最近在研究modernizr的前端框架,发现这个Modernir对前端写页面非常友好,并且能够很快的建立起适应任何设备的html页面哦.在这里分享下基础教程,让大伙对modernizr是什么?做什么用 ...