您的位置:首页 > Web前端 > JavaScript

JS 中国象棋程序(0):界面设计

2017-03-25 13:32 363 查看

“JavaScript中国象棋程序” 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序。希望通过这个系列,我们对博弈程序的算法有一定的了解。同时,我们也将构建出一个不错的中国象棋程序。

程序的最终效果点击这里查看

在进入正题之前,本节是一些闲聊。如果你急切想进入正题,请跳过本节。


我学习中国象棋程序的历程

最初是买了本书《C/C++中国象棋程序入门与提高》。这是本好书,写得细致、透彻,我很愉快地读完了前6章,毫无压力。这6章讲解了局面表示、走法生成、局面评估、基本搜索算法等内容。遗憾的是,第7章我读不下去了。第7章是在讲解,如何使用VC6.0设计图形用户界面。天啊,我是一个web程序员,我只想了解一下象棋程序的设计思想以及算法,我实在不想去使用陈旧的VC6.0来学习windows GUI编程啊。

幸运的是,我在这本书最后一页的参考文献里,发现了象棋百科全书网。在这家网站的github仓库,发现了一个JavaScript版本的中国象棋软件,而且性能还不错。使用HTML
+ JavaScript来设计界面,自然是简单了很多啊,这样就能集中精力去学习象棋程序的算法了。


为什么选择JavaScript

本教程之所以选择JavaScript,讲解JavaScript版本的中国象棋程序,我有以下几个理由吧:

1、这个JavaScript版本的中国象棋程序,性能还不错。

2、界面设计简单,可以把主要精力用在对算法的学习上。

3、不用搭建环境。只要有文本编辑器(比如notepad++)和浏览器(最好是chrome吧),就足够了。

4、程序用到的都是很基础的JavaScript语法,应该没有语法方面的障碍。


项目初衷

曾经读到过一个教程手把手教你构建 C 语言编译器,我觉得很有意思。于是我就仿照这它的格式,写下了这个中国象棋程序教程。本教程的绝大部分思想都来自《C/C++中国象棋程序入门与提高》和象棋百科全书网象棋百科全书网上面还有很多不错的文章,让我受益匪浅。

如果你想了解原版的程序,请前往象棋百科全书网的github下载,这里面有很多个版本,我们使用的是JavaScript版。


这个教程难学吗?

1、至少前4节是不难的吧,都是一些基本的东西。

2、第5节介绍了Alpha-Beta搜素,这个算法很重要,是后面几节教程的基础。

3、如果搞明白了Alpha-Beta搜索算法,随后的3节应该也不算难吧,都是在Alpha-Beta算法的基础上进行优化。

最后,非常感谢象棋百科全书网的前辈以及《C/C++中国象棋程序入门与提高》的作者。

祝你学得愉快。

这一节我们设计图形界面,显示初始化棋局。当点击某棋子时,弹窗提示所点击的具体棋子。效果如下:






1.1、棋盘表示

中国象棋有10行9列,很自然地想到可以用10×9矩阵表示棋盘。事实上,我们使用16×16矩阵来表示一个扩充了的虚拟棋盘。





如上图所示,灰色部分为真实棋盘,置于虚拟棋盘之中。这么做可以快速判断棋子是否走出边界。例如象沿田字走,如果走到真实棋盘之外的虚拟棋盘中,说明走法不合法。

容易想到使用二维数组表示16×16矩阵,这样棋盘上的一个位置需要两个变量表示。一个走法包括起点和终点,就需要四个变量。如果使用长度为256的一维数组表示,一个位置只需一个变量,这就可以减少计算量。因此用一维数组表示16×16矩阵。

一维矩阵和二维矩阵之间的转换也很简单:

JavaScript

1234567891011121314// 将二维矩阵转换为一维矩阵function COORD_XY(x, y) { return x + (y << 4);} // 根据一维矩阵,获取二维矩阵行数function RANK_Y(sq) { return sq >> 4;} // 根据一维矩阵,获取二维矩阵列数function FILE_X(sq) { return sq & 15;}
其中,sq & 15是通过位运算取余,与sq % 16结果相同(可参考篇文章)。再使用一个辅助数组,标识虚拟棋盘中,哪些位置属于真实棋盘:JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

var
IN_BOARD_
=
[

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

];

要判断某位置是否在真实棋盘,可使用函数:

JavaScript

123function IN_BOARD(sq) { return IN_BOARD_[sq] != 0;}

1.2、棋子表示

使用整数表示棋子:
红方891011121314
黑方16171819202122
棋子这样表示,可以快速判断某棋子属于红方还是黑方,如下表所示:
红方棋子黑方棋子
十进制二进制十进制二进制
80000 1000160001 0000
90000 1001170001 0001
100000 1010180001 0010
110000 1011190001 0011
120000 1100200001 0100
130000 1101210001 0101
140000 1110220001 0110
可以看出:红方棋子 & 8 = 1黑方棋子 & 16 = 1

1.3、字符串表示局面

使用数组表示局面,程序处理起来很方便,但是再网上传递棋局很不方便。我们可以用一行字符串表示一个局面,这就是FEN格式串,一种使用ASCII码字符描述国际象棋局面的标准,当然也可应用于中国象棋。中国象棋的初始局面可表示为:rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w – – 0 1(1)、红色区域,表示棋盘布局,小写表示黑方,大写表示红方。一个字母表示一个棋子,对应关系如下。
红方字母黑方字母对应单词
Kkking
Aaadvisor
Bbbishop
Nnknight
Rrrook
Cccannon
Pppawn
至于为什么马不用H(horse),象不用E(elephant),这是为了与国际象棋相对应。如果没有棋子,则用数字表示出相邻连续的空位数。中国象棋共有十行,每行都用一个字符串表示,行间使用正斜杠分割。例如:rnbakabnr表示:

9表示:第二行都是空格。1c5c1表示:

(2)、绿色区域,表示轮到哪一方走子,“w”表示红方,“b”表示黑方。(没有用r表示红方,我想也是为了与国际象棋对应吧,毕竟国际象棋是黑白两色。)(3)、深紫色区域,在中国象棋中没有意义,始终用“-”表示。(4)、紫红色区域,在中国象棋中没有意义,始终用“-”表示。(5)、蓝色区域,表示双方没有吃子的走棋步数(半回合数),通常该值达到120就要判和(六十回合自然限着),一旦形成局面的上一步是吃子,这里就标记“0”。(6)、棕色区域,表示当前的回合数。我们的程序就是使用FEN串初始化棋局的,这就涉及到了将FEN串转化为一维棋局数组。暂时不考虑哪方走子,只解析红色部分,伪代码如下:JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

//
将FEN串转为一维数组

行变量
y
=
3

列变量
x
=
3

var
c
=
FEN串第一个字符;

while
(c
!=
" ")
{

if
(c
==
"/")
{
//
换行

x
=
3;

y
++;

if
(y
>
12)
{

break;

}

}
else
if
(c
>=
"1"
&&
c
<=
"9")
{ //
出现空位

列向量x增加c

}
else
if
(c
>=
"A"
&&
c
<=
"Z")
{ //
红方棋子

将字符表示的棋子转换为整数,并放入数组x
+
(y
<<
4)的位置

}
else
if
(c
>=
"a"
&&
c
<=
"z")
{

将字符表示的棋子转换为整数,并放入数组x
+
(y
<<
4)的位置

}

c
=
FEN串的下一个字符;

}


1.4、棋盘前端设计思路

由于棋盘有90个交叉点,我们把棋盘划分为的90个小正方形区域,交叉点是小正方形的中心。每个区域都会定义一个img标签。





上图使用红色方框,标识出了4个小正方形区域。

这些img标签有两个作用:

(1)、显示棋子图片

如果某个区域存在棋子,就会显示相应的棋子图片;否则,显示一张透明图片(也就是oo.gif)。

(2)、响应点击事件

每个img标签都会绑定onmousedown事件。点击不同的img标签时,会传递不同的参数给响应函数,这样就知道点击的具体是哪个区域了。

给你们推荐一下 这是一个大牛直播传授知识 你们可以学习一下人家怎么学习的 想学习的可以来

直播时间: 晚8点

听课方式: 感兴趣的同学可以加Q群:439107211 点击链接加入群【前端技术交流群】:https://jq.qq.com/?_wv=1027&k=4696DcP

直播方式: 实时观看直播者屏幕,实时互动

是否免费: 免费


1.5、核心代码说明

本节的代码可以在 Github 下载,也可以直接clone

git clone -b step-1 https://github.com/Royhoo/write-a-chinesechess-program
程序中定义了两个对象:Board和Position。Board表示一个棋盘,主要功能是初始化棋局,显示棋盘、棋子,响应棋盘上的点击事件。Position存储了一维棋局数组,并定义了很多对该数组进行操作的方法。

Board对象实例化的代码位于index.html中。

通过prototype属性,我们为这两个对象添加了很多的属性和方法。

Board的主要属性和方法:

(1)、pos

这是Position对象的一个实例。

(2)、flushBoard()

刷新棋盘,也就是重新显示棋盘上的棋子。

(3)、drawSquare(sq)

显示sq位置的棋子图片。如果该位置没棋子,则显示一张透明的图片。

(4)、clickSquare(sq_)

点击棋盘的响应函数。点击棋盘(棋子或者空位置),就会调用该函数。sq_是点击的位置。

Position的主要属性和方法:

(1)、squares

这就是一维棋局数组。

(2)、fromFen(fen)

通过FEN串初始化棋局,也就是将参数fen表示的棋局,转化为一维棋局数组squares表示的棋局。

(3)、addPiece(sq, pc)

将棋子pc添加进棋局中的sp位置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: