java象棋论文.docx
- 文档编号:10519177
- 上传时间:2023-02-17
- 格式:DOCX
- 页数:35
- 大小:440.75KB
java象棋论文.docx
《java象棋论文.docx》由会员分享,可在线阅读,更多相关《java象棋论文.docx(35页珍藏版)》请在冰豆网上搜索。
java象棋论文
1绪论3
2概述4
2.1棋子的名称4
2.2棋谱的记录方法4
2.2.1中文纵线记法5
2.2.2符号纵线记法7
2.3历史局面的表示及存储7
2.4棋谱记录文件的格式8
2.4.1标签部分8
2.4.2棋谱记录部分9
2.4.3XML格式10
3基本数据结构——位棋盘11
3.1什么是位棋盘11
3.2.1位棋盘类的实现11
3.2.2位棋盘的初始化12
3.2.3位棋盘的更新14
4基本数据结构——Zobrist键值15
4.1比较局面的方法15
4.2Zobrist键值的实现方法15
4.3Zobrist键值的工作原理及用途15
4.3.1Zobrist键值的工作原理15
4.3.2Zobrist键值的用途16
4.4Java中实现Zobrist键值16
5着法生成19
5.1伪合法着法的生成19
5.1.1数组及其下标的含义20
5.1.2算法示例——车炮的伪合法着法生成23
5.2合法着法的生成26
6程序的设计及实现31
6.1搜索引擎的实现(engine包)31
6.2信息传输机制(message包)32
6.3棋子(pieces包)32
6.4主控模块(main包)33
1绪论
中国象棋(通称"象棋")是人们国家喻户晓的棋类运动项目。
“象棋”一词,早在春秋战国时代的<<楚辞>>中就已出现。
现在使用的棋盘、棋子,业型于九百年前的北宋末年。
中国象棋运动可谓源远流长。
它同围棋和国际象棋一样,对提高智慧,陶冶性格,调剂身心,交流文化艺术都起着积极作用;在人们国是一项有着广泛群众基础的比智慧、比技巧、比体力的体育项目。
象棋集智谋与娱乐为一体,对弈双方宛如统率千军的将领,执红黑双方,厮杀于棋盘之上,虽不见刀光剑影,激烈程度并不输于两国交兵。
象棋集中了几千年中华文化的精髓,“红先黑后”,说的是礼让;“观棋不语真君子,举棋无悔大丈夫”,说的是为人处世;“胜败乃兵家常事”,讲的是心态;而“当局者迷,旁观者清”,则适用于世间一切事物,有启迪与警示的作用。
象棋棋子不多,各自走法也不相同,不管是车马炮兵,还是相士帅,虽然力量有大小,但可以相互捉杀,可见创造象棋的人不但追求博弈的公平,更表达了人格平等的理念。
小小棋盘,在娱乐之余,还可以对人的一生起到借鉴与总结。
象棋水平的发展是需要靠信息技术来推动的,国际象棋有两个很好的范例,一个是象棋棋谱编辑和对弈程序的公共平台——WinBoard平台,另一个是商业的国际象棋数据库和对弈软件——ChessBase,人们们为国际象棋爱好者和研究者提供了极大的便利。
国际象棋软件有着成功的商业运作,已发展成一种产业。
然而,电脑在中国象棋上的运用还刚刚起步,尽管国内涌现出一大批中国象棋的专业网站和专业软件,但是由于缺乏必要的基础工作,电脑技术在中国象棋上的应用优势还无法体现出来。
在设计中国象棋软件过程中,国际象棋软件有很多值得借鉴的成功经验和优秀的思想。
例如B.Moreland,微软(Microsoft)的程序设计师,业余从事国际象棋引擎Ferret的开发,人们的一系列关于国际象棋程序设计的文章非常值得其人们棋类程序设计人员借鉴。
然而,中国象棋与国际象棋存在着很大的差异,因此国际象棋的某些成熟技术,无法直接应用于中国象棋,需要对其加以改进和创新。
本文针对中国象棋程序设计的一系列问题,总结出一些搜索引擎的设计方法,并给出java语言的实现。
2概述
中国象棋是由两人下的。
一方称为红方(或白方),一方称为黑方。
对局时由红方先走,黑方后走,一次一着,双方轮流走棋,直到对局结束为止。
棋子的走法及详细规则见《中国象棋竞赛规则》,本章只描述计算机实现象棋对弈程序时所涉及的一些概念及相关的表示方法。
2.1棋子的名称
为方便表示,中国象棋的棋子名称除了用汉字以外,还可以用字母,字母可从国际象棋中稍加改动得到,而数字是为了方便着法的输入(以便用在数字小键盘上)见表2-1所示:
表2-1棋子的名称及表达方式
红方
黑方
字母
英文单词
数字
帅
将
K
King
1
仕
士
A
Advisor
2
相
象
B
Bishop
3
马
马
N
Knight
4
车
车
R
Rook
5
炮
炮
C
Cannon
6
兵
卒
P
Pawn
7
2.2棋谱的记录方法
现以如下一般的开局为例说明各种记谱方法,如图2-1所示的局面,记法如下:
(1)炮二平五炮8平5
(2)炮五进四士4进5
(3)马二进三马8进7
(4)炮八平五马2进3
(5)前炮退二车9平8
图2-1一般开局的走法
2.2.1中文纵线记法
这种格式是中国象棋棋谱的常规记法,在各类出版物中最为普遍。
但是这里还是要说明两个重要的细节。
(1)仕(士)和相(象)如果在同一纵线上,不用“前”和“后”区别,因为能退的一定在前,能进的一定在后。
(2)兵要按情况讨论:
①三个兵在一条纵线上:
用“前”、“中”和“后”来区别;
②三个以上兵在一条纵线上:
最前面的兵用“一”代替“前”,以后依次是“二”、“三”、“四”和“五”;
③在有两条纵线,每条纵线上都有一个以上的兵:
按照“先从右到左,再从前到后”(即先看最左边一列,从前到后依次标记为“一”和“二”,可能还有“三”,再看右边一列)的顺序,把这些兵的位置标依次标记为“一”、“二”、“三”、“四”和“五”,不在这两条纵线上的兵不参与标记。
如图2-2所示局面,四个兵分别位于四线和六线,见表2-2列举了几种走法的坐标格式和纵线格式。
图2-2残局的记法
表2-2残局的坐标记法
中文纵线格式
符号纵线格式
坐标格式
一兵平五
Pa.5
Pf8-e8
二兵平五
Pb.5
Pf6-e6
兵五进一
P5+1
Pe7-e8
三兵平五
Pc.5
Pd8-e8
四兵平五
Pd.5
Pd6-e6
另外需要注意的是:
(1)如果黑方出现数字,不管数字代表纵线标号还是前进或后退的格数,都用阿拉伯数字表示,在计算机中显示全角的数字。
但是代表同一纵线上不同兵的“一二三四五”(它们类似于“前中后”的作用)例外,例如例局面红黑互换,那么某步着法就应该写成“一卒平5”。
(2)在传统的象棋记谱中,如果发生以上这种情况,通常用五个字来表示,例如“前兵四平五”等,在计算机处理过程中就比较麻烦,因为4个汉字(一个汉字占16位)的着法可以储存在一个64位的字当中(在C语言中数据类型为__int64或longlong),而增加到5个汉字就比较麻烦了。
黑方用全角的数字是同一个道理。
2.2.2符号纵线记法
这种格式仅仅是用字母和数字代替汉字,其中“进”、“退”和“平”分别用符号“+”、“-”和“.”表示,“前”、“中”和“后”也分别用符号“+”、“-”和“.”表示,并且写在棋子的后面(例如“前炮退二”写成“C+-2”而不是“+C-2”),多个兵位于一条纵线时,代替“前中后”的“一二三四五”分别用“abcde”表示(这种情况极少发生)。
另外,代表棋子名称的第一个字母,还可以用数字1到7表示,这是为了方便数字小键盘的输入,例如“炮二平五”可以记作“62.5“(6代表炮)选用符号“+”、“-”和“.”也是出于这个考虑。
2.3历史局面的表示及存储
中国象棋的一个局面可以用一个“FEN格式串”来表示。
FEN格式串是由4段ASCII字符串组成的代码(彼此3个空格隔开),这4段代码的意义依次是:
(1)棋盘上的棋子,这是FEN格式串的主要部分;
(2)当前局面轮到哪一方走子;
(3)最近一次吃子或者进兵后棋局进行的步数(半回合数),用来判断“50回合自然限着”;
(4)棋局的回合数。
现以最初局面为例详细说明如下:
rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNRw01
第一部分(w前面的字符):
表示棋盘布局,小写表示黑方棋子,大写表示红方棋子。
例如前九个字母rnbakabnr表示棋盘第一行的棋子分别为黑方的“车马象士将士象马车”,“/”为棋盘行与行之间的分割;数字“9(5,1)”表示该行从该位置起连续9(5,1)个位置无棋子。
第二部分(w):
表示轮到哪一方走棋,如果是w表示轮到红方(白方)走,是b表示轮到黑方走。
第三部分(w后的数字0):
表示自然限着。
第四部分(w后的数字1):
表示当前局面的回合数。
2.4棋谱记录文件的格式
存放棋谱的文件分为两个部分:
标签部分和棋谱部分。
标签部分是指在棋谱的记录中对战双方的比赛名称等双方的英文简称记录方法;棋谱部分是指在对战的双方所走的每一步文字记录。
2.4.1标签部分
有如下标签:
①Event:
比赛名;
②Site:
比赛地点;
③Date:
比赛日期,格式统一为"yyyy.mm.dd";
④Red:
红方棋手;
⑤Black:
黑方棋手;
⑥Result:
比赛结果,“红先胜”用“1-0”表示,“黑先胜”用“0-1”表示,和棋用“1/2-1/2”
⑦FenStr:
起始局面。
如果空缺,表示起始局面是最初局面。
2.4.2棋谱记录部分
棋谱记录部分必须在标签部分的后面,棋谱部分当中不能插入标签;每一回合都由“回合数”、“红方着法”和“黑方着法”三部分组成,回合数后面要跟“.”(句点),三者之间用两个分隔符隔开(回合数后面的句点也不例外),回合之间也用分隔符隔开。
现举一个例子如下:
例1:
[Event"第19届五羊杯全国象棋冠军邀请赛"]
[Date"1998.12.?
?
"]
[Site"广州"]
[Red"徐天红"]
[Black"许银川"]
[Result"1/2-1/2"]
(1)炮二平五马8进7
(2)马二进三车9平8
(3)车一平二马2进3(4)兵七进一卒7进1
(5)车二进六炮8平9(6)车二平三炮9退1
(7)马八进九车8进5(8)兵五进一马3退5
(9)炮八进四炮2平5(10)马九进七炮9平7
2.4.3XML格式
3基本数据结构——位棋盘
3.1什么是位棋盘
在中国象棋中,棋盘有90个交叉点。
位棋盘其实就是一个长度为90位的变量,每个位对应一个交叉点,用来记录棋盘上的某些布尔值。
在Java中,用3个int类型数据(每个32位,共96位多余的6位不用)表示一个位棋盘。
3.2Java中位棋盘的实现
3.2.1位棋盘类的实现
Java中,位棋盘用3个int型的数据表示,最高六位不用。
Java中“与、或、非、异或、左位移,右位移(注意,位棋盘的右位移是无符号位移)”分别是“&、|、^、<<、>>>”。
代码摘要(详细代码见附件)及相关说明如下:
publicclassBitBoard{
privateintLow,Mid,Hi//用3个int字段表示位棋盘,最高位Hi的高
//6位不用
publicBitBoard(intArg1,intArg2,intArg3){//构造函数
Low=Arg1;
Mid=Arg2;
Hi=Arg3;
}
publicstaticBitBoardopAnd(BitBoardarg1,BitBoardarg2){
//位棋盘的“与”操作,保存结果。
intlow=arg1.Low&arg2.Low;
intmid=arg1.Mid&arg2.Mid;
inthi=arg1.Hi&arg2.Hi;
returnnewBitBoard(low,mid,hi);
}
publicstaticBitBoardopOr(BitBoardarg1,BitBoardarg2)
//位棋盘的“或”操作,保存结果。
publicstaticBitBoardopXor(BitBoardarg1,BitBoardarg2)
//位棋盘的“异或”操作,保存结果。
publicstaticintcount(BitBoardarg)//计算位棋盘中非零位的个数
publicstaticBitBoardduplicate(intarg)//复制位棋盘
publicstaticbooleanequals(BitBoardarg1,BitBoardarg2)
//位棋盘是否相等(所有90位对应的位相同即//为相等)
publicstaticBitBoardleftShift(BitBoardarg,intnum)
//位棋盘arg左位移num位
publicstaticrightShift(BitBoard,intnum)//位棋盘右位移num位
publicstaticintLSB(BitBoardArg)//位棋盘最低非0位的位置(从0
//开始计数)
publicstaticintMSB(BitBoardArg)//位棋盘最高非0位的位置(从0
//开始计数)
publicstaticbooleannotZero(BitBoardArg)//是否非“0”。
当90位//中有非0位时返回true。
}
3.2.2位棋盘的初始化
某些位棋盘从程序开始运行到结束都不会改变。
例如上面所述的那个位棋盘数组“knight[90]”。
(人们实际上记录了当马在任意格子上时,它下一步可以走的格子。
)这个数组将在程序开始执行的时候被初始化并且不再改变。
其余的位棋盘将不断变化。
例如“AllPieces”位棋盘。
当中国象棋棋盘变化时,它也跟着变化。
然而,人们们的初始化方式相同。
对于诸如knight[90]这样不变化的位棋盘的初始化,将在“伪着法生成”章节详述。
此处叙述走棋过程中随棋局变化的诸多位棋盘的初始化及相关操作。
首先,初始化“BitBoardbitMask[90]”数组:
BitBoardb=newBitBoard(0,0,1);
for(intc=0;c<90;c++){
mask[c]=BitBoard.leftShift(b,c);
}
其次,用一个叫ChessPosition类记录棋盘上某一状态的所有有用信息。
它包含了一个整型数组intpiece_in_square[90],还包含了一些位棋盘。
publicclassChessPosition{
intpiece_in_square[90];
intplayer;//轮到哪方走棋,0:
红方走,1:
黑方走
BitBoardallPieces;
BitBoardredKing;//红帅
BitBoardblackKing;//黑将
BitBoardredRooks;//红车
BitBoardblackRooks;//黑车
BitBoardredKnights;//红马
BitBoardblackKnights;//黑马
BitBoardredCannon;//红炮
BitBoardredCannon;//黑炮
BitBoardredBishops;//红相
BitBoardblackBishops;//黑象
BitBoardredAdvisor;//红仕
BitBoardblackAdvisor;//黑士
BitBoardredPawns;//红兵
BitBoardblackPawns;//黑卒
BitBoardredPieces;//所有红棋子
BitBoardblackPieces;//所有黑棋子
};
初始化“piece_in_square[]”数组。
piece_in_square[0]=RED_ROOK;
piece_in_square[1]=RED_KNIGHT;
piece_in_square[2]=RED_BISHOP;
…
piece_in_square[89]=BLACK_ROOK;
现在初始化其人们一些位棋盘:
for(c=0;c<90;c++){
switch(piece_in_square[c]){
case:
RED_ROOK
position.redPieces=BitBoard.opOr(position.redPieces,bitMask[c]);
position.redRooks
BitBoard.opOr(position.redRooks,bitMask[c]);
break;
}
}
3.2.3位棋盘的更新
当棋盘局面变动后,某些位棋盘就需要被更新。
例如记录白子所在位置的“WhitePieces”位棋盘。
假如人们把h2格的红炮移动到h9格(炮二进七),吃掉黑棋的一个马,需要更新如下位棋盘:
allPieces
redPieces
redCannons
blackpieces
blackKnights
首先,要把redPieces,redCannons位棋盘的“h2”位清零,然后把人们们的“h9”位置1。
/*clearabitwiththe"XOR"operation*/
position.allPieces=BitBoard.opXor(position.allPieces,bitMask[h2];
position.redPieces=BitBoard.opXor(position.redPieces,bitMask[h2]);
position.redCannons=BitBoard.opXor(position.redCannons,bitMask[h2];
/*setabitwiththe"OR"operation*/
position.redPieces=BitBoard.opOr(position.redPieces,bitMask[h9]);
position.redCannons=BitBoard.opOr(position.redCannons,bitMask[h9]);
4基本数据结构——Zobrist键值
4.1比较局面的方法
在写中国象棋程序时,需要比较两个局面看它们是否相同。
如果比较每个棋子的位置,或许不需要花很多时间,但是实战中每秒种需要做成千上万次比较,因此这样会使比较操作变成瓶颈的。
另外,需要比较的局面数量多得惊人,要存储每个棋子的位置,需要占用非常大的空间。
一个解决方案是建立一个标签,通常是64位。
由于64位不足以区别每个局面,所以仍然存在冲突的标签,但实战中这种情况非常罕见。
4.2Zobrist键值的实现方法
实现Zobrist必须从多维的64位数组开始,每个数组含有一个随机数。
在Java中,“rand.nextLong()”函数返回一个64位的随机数值。
这个函数用来填满一个long型(64位)的三维数组:
棋子的类型、棋子的颜色和棋子的位置:
longzobrist[pcMAX][coMAX][sqMAX];
程序启动时就把这个数组用随机数填满。
要为一个局面产生Zobrist键值,首先把键值设成零,然后找棋盘上的每个子,并且让键值跟“zobrist[pc][co][sq]”做异或(通过“^”运算符)运算。
如果局面由白方走,那么别去动它,如果是黑方走,人们还要在键值上异或一个64位的随机常数。
4.3Zobrist键值的工作原理及用途
4.3.1Zobrist键值的工作原理
用Zobrist技术产生的键值,表面上跟局面没什么关系。
如果一个棋子动过了,就会得到完全不同的键值,所以这两个键值不会挤在一块儿或者冲突。
当把它们用作散列表键值的时候会非常有效。
另一个优点在于,键值的产生是可以逐步进行的。
例如,红马在e5格,那么键值里一定异或过一个“zobrist[KNIGHT][RED][E5]”。
如果再次异或这个值,那么根据异或的工作原理,这个马就从键值里删除了。
这就是说,如果有当前局面的键值,并且需要把红马从e5移到f7,人们只要异或一个“红马在e5”的键值,把马从e5格移走,并且异或一个“红马在f7”的键值,把红马放在f7上。
比起从头开始一个个棋子去异或,这样做可以得到同样的键值。
如果要改变着子的一方,只要异或一个“改变着子方”的键值就可以了。
用这种方法,可以在搜索根结点的时候构造一个Zobrist键值,在搜索时通过走子函数“MakeMove()”来更新键值,一直让它保持和当前局面同步。
4.3.2Zobrist键值的用途
Zobrist键值通常用在散列键值当中,而散列键值在象棋程序里有以下几个作用:
(1)用Zobrist键值来实现置换表。
置换表是一个巨大的散列表,来保存以前搜索过的局面,这样可以节省很多搜索的时间。
如果需要对某个局面搜索9层,可以从置换表中查找该局面,如果它已经搜索过9层,那么不必去重复搜索。
置换表的另一个并不起眼的作用是,它可以帮助人们改善着法的顺序。
(2)可以用Zobrist键值制造一个很小的散列表,来检测当前着法路线中有没有重复局面,以便发现长将或其人们导致和局的着法。
(3)可以用Zobrist键值创建支持置换的开局库。
4.4Java中实现Zobrist键值
本系统使用一个key和一个lock结合来区分每个局面,这样发生冲突(即两个局面对应的key和lock一样)的概率几乎为0。
示例代码及相关说明如下
(1)填充数组。
上述的三维数组现在改变为二维(将颜色与棋子兵种类型合并)
publicstaticlongZobristKeyPlayer;//改变走子方的key
publicstaticlongZobristLockPlayer;//改变走子方的lock
publicstaticlong[][]ZobristKeyTable=newlong[14][90];
publicstaticlong[][]ZobristLockTable=newlong[14][90];
static{
zobristGen();
}
publicstaticvoidzobristGen(){
inti,j;
Randomrand=newRandom();
longRandSeed;
RandSeed=1;
rand.setSeed(RandS
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- java 象棋 论文
![提示](https://static.bdocx.com/images/bang_tan.gif)