中国象棋博弈系统设计
中国象棋对弈系统
XXXX
【摘要】象棋是我们国家特有的一项棋类益智游戏,中国象棋在中国有着三千多年的历史,属于二人对抗性游戏的一种。由于用具简单,趣味性强,成为流行极为广泛的棋艺活动。随着科技的不断进步,人们不再局限于现实中在棋盘上双人面对面对弈,象棋的 “下法”也在改变。网络上流传的关于象棋的小游戏自然而然也就多了起来。从开始的键盘控制走棋到之后的鼠标控制走棋。人们对这个系统的需求越来越高了,现在人们已经不满足于手动的控制进行游戏了,随着语音识别的出现人们更倾向与语音操作实现走棋了。语音是现下最先进的操控方式,他可以不通过手动控制就能够实现我们之前手动操作的任务,就如之前说的能简单就简单。这是我们现下的最终状态了,能少动手就少动手,能不动手就更好的理念之下我们着手了这次的中国象棋游戏编程,我们采用了Microsoft Speech SDK来实现了我们的语音操作功能来实现了最新的走棋功能。
【关键词】中国象棋;语音操控;打谱,悔棋;
目录
1 概述 ....................................................................... 1
1.1项目背景 ............................................................... 1
1.2系统简介 ............................................................... 1
1.3开发环境 ............................................................... 1
1.4主要技术 ............................................................... 2
1.5小组分工 ............................................................... 2
2 需求分析 ................................................................... 3
2.1用户分析 ............................................................... 3
2.1.1系统定位 ............................................................. 3
2.1.2用户面临的问题 ....................................................... 3
2.1.3系统的运行环境 ....................................................... 3
2.1.4时间约束与资源约束 ................................................... 3
2.2用户设计流程 ........................................................... 3
2.2.1用户的观察与分析 ..................................................... 3
2.2.2设计 ................................................................. 3
2.3任务分析 ............................................................... 5
3 概要设计 ................................................................... 7
3.1界面设计原则 ........................................................... 7
3.2设计模型 ............................................................... 7
3.3.系统描述 .............................................................. 10
3.4使用的交互技术 ........................................................ 11
4 详细设计与实现 ............................................................ 12
4.1界面设计: ............................................................ 12
4.2棋盘的绘制: .......................................................... 12
4.2.1竖线的绘制代码: .................................................... 12
4.2.2横线的绘制代码: .................................................... 13
4.2.3兵处折线的绘制: .................................................... 13
4.2.4炮处折线的绘制: .................................................... 16
4.2.5楚河汉界的绘制: .................................................... 17
4.3棋子的规则实现: ...................................................... 18
4.3.1兵/卒的走棋规则: ................................................... 18
4.3.2车的走棋规则: ...................................................... 20
4.3.3马的走棋规则实现: .................................................. 21
4.3.4炮的走棋规则实现: .................................................. 22
4.3.5士的规则: .......................................................... 23
4.3.6相的规则: .......................................................... 24
4.3.7帅的规则: .......................................................... 25
4.4悔棋: ................................................................ 27
4.5播放/关闭背景音乐: ................................................... 37
4.6残局: ................................................................ 38
4.7帮助与关于: .......................................................... 40
5 可用性评估 ................................................................ 42
5.1评估指标体系 .......................................................... 42
5.1.1对整个系统的内容功能界面等方面进行评估 .............................. 42
5.1.2对整个系统运行效率方面进行评估 ...................................... 42
5.1.3对整个系统给用户的体验感满意度方面进行评估 .......................... 42
5.2具体步骤 .............................................................. 42
5.3结果分析 .............................................................. 43
6 结论 ...................................................................... 44
6.1总结: ................................................................ 44
6.2不足之处: ............................................................ 44
6.3心得体会: ............................................................ 44
1 概述
1.1项目背景
总所周知,随着互联网技术迅猛的发展,在各行各业得到了很广泛的应用。同时,随着时代的进步,产业的发展,生活质量的提高,人们的业余时间在很大方面的空闲了出来,各种各样建立在互联网基础上的游戏也是不断的出现,在人们空余的时间填充他们的生活,网络游戏是一类,但是网络游戏要具备有网络的情况下进行操作,在这种条件受限的情况下,单机游戏的推广更是休闲娱乐一族玩家们的主流,而且贴近生活所设计的单机游戏更是大众的喜爱。中国象棋这类的游戏第一,贴近生活,从小耳濡目染的游戏,更是深受大众的喜爱,成为许多人在平时闲暇时间娱乐消遣的一款游戏,为了满足广大玩家的需求,扩大中国象棋的应用范围是针对目前社会形式可以进行的一项,所以针对网上象棋这类游戏的开发对于我们作为计算机科学与技术专业的学生,更是我们应该是重点去面对的一项。
1.2系统简介
整个系统采用C#语言编写,之所以使用C#,因为C#在带来对应用程序的快速开发能力的同时,并没有牺牲C与C++程序员所关心的各种特性。它忠实地继承了C和C++的优点。里面包括简洁的语法。精心地面向对象设计、与Web的紧密结合、完整的安全性与错误处理、版本处理技术、灵活性与兼容性等。在整个系统功能中有最基本的新建一个棋盘,新建棋盘相当于我们对弈时的重新开局,如果中间出现胜负,或者有人中途离场改变对弈对手等等,使用新建棋盘来解决。系统的走棋是最基本的对弈操作。系统的悔棋功能实现的是对上一个走棋步骤的访问,使棋盘回到上一个走棋时的状态。系统的语言识别包括对走棋中间落棋的语音播报等等。这几个是系统最基本功能的实现。整个系统运行过程较为流畅,可以实现基本的双方对弈操作。
1.3开发环境
计算机可使用WindowsXP/Windows7/Windows8系统运行,这些系统都是个人电脑上最忌泵具备的系统,针对开发过程中使用的软件所支持的硬件条件,这些是开发系统的必须前提。编码使用Visual studio 2010软件进行编码,Visual studio 2010是一个很强大的软件,他是集合最基本的所有编程语言为一体的一个软件,使用Visual studio 2010的最大意义在于它的简易操作性,和对错误点的及时提示功能,Visual studio 2010让整个系统在开发的过程中少走了很多弯路,很多问题漏洞都能很快的发现和解决。使用Panel绘画软件进行棋盘的绘制,Panel绘画软件是一款针对性很强的软件,对于绘画这一块有着相当方便的操作界面,使得整个棋盘的绘制没有花费过多时间。
1.4主要技术
主要使用了堆栈技术,堆栈是一种抽象数据结构,其操作机理是后进先出。当你把新条目推进堆栈时,已经在堆栈内的任何条目都会压到堆栈的深处。同样的,把一个条目从堆栈移出则会让堆栈内的其他条目都向堆栈的顶部移动。只有堆栈最顶端的条目能从堆栈中取出,条目离开堆栈的顺序和它们被推进堆栈的顺序一样。
类的定义,类实际上就是抽象的概念,把某些具有类似或者相同属性的东西归整一起,类的定义包括对系统中各个类型的设置和命名,以及在类定义下对他们功能进行编写。
Speech SDK的使用,这是一个微软开发的实现语音识别的语音引擎,利用它提供的接口,我们可以用任何语言编写一种语音识别软件或者文字转换声音文件的软件。
1.5小组分工
我们小组主要分工如下:
小组成员XX主要负责走棋,悔棋等功能代码生成这一部分,对于这一部分应该是整个开发系统最困难的一部分,针对每一个步骤的构思,紧密的连接性等等,都要从整体上面进行考虑。
小组成员XX主要负责棋盘的绘制和基本界面的设计,这一块属于系统基本用户界面设计模块,对于整个界面的设计第一个要考虑用户的惯性视觉,怎么样的界面和棋盘会让人觉得贴近生活,而且对于一些细节的东西也要进行考虑设计。
小组成员XX主要负责检验和修改系统和后期的文档整合,检验和修改系统漏洞需要通过在运行测试中发现的问题进行改进,文档的整合,要根据每个组员在整个系统编写过程中的一些想法和重要部分信息进行整
2 需求分析
2.1用户分析
2.1.1系统定位
具有语音功能简单的单机象棋小游戏。
2.1.2用户面临的问题
需要一个简单的双人对弈棋局;
需要我们实现悔棋功能和打谱的功能操作;
需要我们实现简单的语音识别操作
2.1.3系统的运行环境
用户需求要在XP、WIN7、WIN8系统上运行。
2.1.4时间约束与资源约束
系统需要在2015年6月22日前完成,需要用C#来完成象棋的简单操作功能。
2.2用户设计流程
2.2.1用户的观察与分析
这是一个中国象棋对弈系统,可以采取人人单机对弈。需要有棋盘棋子的局面、鼠标响应控制棋子移动、通过语音识别放置棋子。用户可以根据自己的需要悔棋,用户还可以选择打谱,把对弈双方之前的棋局一步一步还原,还原时可以在将棋局动态显示并使用语音合成将每一步的操作度出来。
2.2.2设计
图.1 初步—系统结构图
图.2 中国象棋游戏活动图
2.3任务分析
(1)首先我们要绘制出整体的框架布局
(2)开始着手在Panel框体中间把棋盘的绘制写出来,将棋盘绘制出来
(3)在绘制好的棋盘中写入棋子的放置语句,将棋子摆放到绘制好了的棋盘上
(4)实现棋局开始行走后棋局能够按照活动图的流程进行
(5)继而在可以实现基本的走棋功能之后我们能够在原本的基础上实现悔棋功能
(6)利用悔棋功能的功能栈来实现打谱的功能,利用下棋的文字信息来实现语音合成的读谱功能。
图.3 中国象棋用例图
图.4 中国象棋状态图
图.5 中国象棋序列图
图.6 中国象棋活动图
3 概要设计
3.1界面设计原则
用户界面可以分为命令行界面、图形界面和多通道用户界面三种基本界面。
命令界面:这是第一人机界面,在我们的中国象棋对弈程序中有选择棋子、复选棋子替换原先选中棋子、重新放置选中棋子、悔棋让棋子回到上一个状态位置。
图形界面:可以看作是第二人机界面,是基于图形方式的人机界面。由于引入了图片的话可以大量的减少中国象棋系统中绘制中国象棋棋盘的庞大的代码量,减少程序的代码复杂度。
多通道用户界面:进一步综合采用了语音、视觉、设备和交互技术,使用户利用多个通道的实现人机交互。更好的的捕捉到用户的行为方式和动作与想法。
3.2设计模型
图.7 中国象棋设置LOTOS图
实现的中国象棋基本功能简化图应当如上述LOTOS图所示,形成一个简单的功能结构模块,在这个简述设计中我们可以看出我们实现的基础功能都有下棋 悔棋和打谱三项功能模块。
图.8 象棋棋盘设计模型
图.9 象棋棋子放置模型
图.10 象棋残局模型
3.3.系统描述
我们的中国象棋系统中实现了棋盘和棋子的布局,我们的布局实现操作采用的是命令行界面设计的方式来实现的,通过代码来绘制棋盘,之后在采用循环语句将棋子一一摆放在棋盘上实现棋盘和棋子的布局。
实现了棋盘和棋子的布局之后我们开始着手棋局中棋子的规则的描述,通过定义类定义出七种类型的棋子,每种棋子有着自己的走棋规则,完善了规则之后我们就要考虑棋局的走势了。
中国象棋是双方对弈的一种游戏,一人一步。所以我们在主界面中加入了规则,这就实现了基本的下棋功能了。
能实现下棋之后就是简单的悔棋,和回放式的打谱功能。
3.4使用的交互技术
在中国象棋系统中我们使用了语音交互技术实现了语音合成功能。在下棋落子的时候会读出象棋的行走路径。
还使用到了笔交互技术来实现了棋盘和棋子的绘制。使用循环语句达成利用笔交互技术的使用来绘制基础棋盘。
使用基本交互技术来实现了背景音乐的播放棋子的定位选择以及帮助信息等框架。 。
4 详细设计与实现
4.1界面设计:
使用panel控件(绘制棋盘)、menuStrip控件(菜单栏)、axWindowsMediaPlayer(用于播放mp3背景音乐)、time控件(用于记录红黑各方的用时)等。
图.11 初始界面设计
4.2棋盘的绘制:
4.2.1竖线的绘制代码:
(利用DrawLine函数绘制棋盘的竖向线以及红黑双方的交叉线,需注意竖线在楚河汉界处有中断)
#region 创建绘画对象
#region 竖向线
//两边的竖向线
g.DrawLine(p, point.X, point.Y, point.X, point.Y + wei * 9);
g.DrawLine(p, point.X + wei * 8, point.Y, point.X + wei * 8, point.Y + wei * 9);
//上半边的竖向线
for (int i = 1; i
{
g.DrawLine(p, point.X + wei * i, point.Y, point.X + wei * i, point.Y + wei * 4);
}
//下半边的竖向线
for (int i = 1; i
{
g.DrawLine(p, point.X + wei * i, point.Y + wei * 5, point.X + wei * i, point.Y + wei * 9);
}
#endregion
//两边的交叉线
g.DrawLine(p, point.X + wei * 3, point.Y, point.X + wei * 5, point.Y + wei * 2);
g.DrawLine(p, point.X + wei * 5, point.Y, point.X + wei * 3, point.Y + wei * 2);
g.DrawLine(p, point.X + wei * 3, point.Y + wei * 9, point.X + wei * 5, point.Y + wei * 7);
g.DrawLine(p, point.X + wei * 5, point.Y + wei * 9, point.X + wei * 3, point.X + wei * 7);
4.2.2横线的绘制代码:
(利用DrawLine函数绘制棋盘的横向线)
#region 创建绘画对象
int wei = 50;
Point point = new Point(25, 25);
Graphics g = this.panel1.CreateGraphics();
Pen p = new Pen(Color.Black, 2);
//横向线
for (int i = 0; i
{
g.DrawLine(p, point.X, point.Y + wei * i, point.X + wei * 8, point.Y + wei * i);
}
4.2.3兵处折线的绘制:
(利用DrawLine函数绘制兵处的折线)
#region
for (int i = Convert.ToInt16(wei * 0.1); i
//右上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.7), point.X + i, point.Y + Convert.ToInt16(wei
* 2.9));
//右下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 3.1), point.X + i, point.Y + Convert.ToInt16(wei
* 3.3));
//右上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.9), point.X + i + Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 2.9));
//右下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 3.1), point.X + i + Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 3.1));
//右上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 5.7), point.X + i, point.Y + Convert.ToInt16(wei
* 5.9));
//右下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.1), point.X + i, point.Y + Convert.ToInt16(wei
* 6.3));
//右上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 5.9), point.X + i + Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 5.9));
//右下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.1), point.X + i + Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 6.1));
}
for (int i = Convert.ToInt16(wei * 1.9); i
//左上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.7), point.X + i, point.Y + Convert.ToInt16(wei
* 2.9));
//左下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 3.1), point.X + i, point.Y + Convert.ToInt16(wei
* 3.3));
//左上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.9), point.X + i - Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 2.9));
//左下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 3.1), point.X + i - Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 3.1));
//左上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 5.7), point.X + i, point.Y + Convert.ToInt16(wei
* 5.9));
//左下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.1), point.X + i, point.Y + Convert.ToInt16(wei
* 6.3));
//左上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 5.9), point.X + i - Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 5.9));
//左下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.1), point.X + i - Convert.ToInt16(wei * 0.2),
point.Y + Convert.ToInt16(wei * 6.1));
}
#endregion
4.2.4炮处折线的绘制:
(利用DrawLine函数绘制炮处的折线)
#region 炮的绘制线
for (int i = Convert.ToInt16(wei * 1.1); i
{
//上方右上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 1.7), point.X + i, point.Y + Convert.ToInt16(wei * 1.9));
//上方右上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 1.9), point.X + i + Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 1.9));
////上方右下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.1), point.X + i, point.Y + Convert.ToInt16(wei * 2.3));
//上方右下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.1), point.X + i + Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 2.1));
//下方右上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.7), point.X + i, point.Y + Convert.ToInt16(wei * 6.9));
//下方右上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.9), point.X + i + Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 6.9));
//下方右下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 7.1), point.X + i, point.Y + Convert.ToInt16(wei * 7.3));
//下方右下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 7.1), point.X + i + Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 7.1));
}
for (int i = Convert.ToInt16(wei * 0.9); i
{
//上方左上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 1.7), point.X + i, point.Y + Convert.ToInt16(wei * 1.9));
//上方左上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 1.9), point.X + i - Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 1.9));
//上方左下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.1), point.X + i, point.Y + Convert.ToInt16(wei * 2.3));
//上方左下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 2.1), point.X + i - Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 2.1));
//下方左下角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 7.1), point.X + i, point.Y + Convert.ToInt16(wei * 7.3));
//下方左下角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 7.1), point.X + i - Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 7.1));
//下方左上角、纵线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.7), point.X + i, point.Y + Convert.ToInt16(wei * 6.9));
//下方左上角、横线
g.DrawLine(p, point.X + i, point.Y + Convert.ToInt16(wei * 6.9), point.X + i - Convert.ToInt16(wei * 0.2), point.Y + Convert.ToInt16(wei * 6.9));
}
#endregion
4.2.5楚河汉界的绘制:
在棋盘的中间添加楚河汉界,以及设置其基本属性
//
FontFamily fm = new FontFamily(
Font f = new Font(fm, 35);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Near;
g.DrawString(
g.Dispose();
#endregion
}
Point Grid = new Point(25, 25);
IChessItem.IChess selectChess;
IChessItem.Enums.ChessType _type = IChessItem.Enums.ChessType.Black;
图.12 棋盘绘制效果图
4.3棋子的规则实现:
4.3.1兵/卒的走棋规则:
移动范围:任何位置。
移动规则:每步只能向前移动一点。过河以后,它便增加了向左右移动的能力,兵不允许向后移动。
private Shuai shuai1;
protected override bool LimitPoint(int x, int y)
{
if (base.LimitPoint(x, y))
{
//个性条件
//判断哪一方
//判断必须是向前行
//判断是否过河
//过河以后,可以横行
//未过河,只能直行
//直线行驶
if (x == GridX || y == GridY)
{
if (type == IChessItem.Enums.ChessType.Black)
{
if (((x == GridX) && (GridY - y) == -1))//没过河之前不能横走和倒走
return true;
else if ((y > 4) && (Math.Abs(GridX - x) == 1))//过河之后
return true;
else
return false;
}
else if (type == IChessItem.Enums.ChessType.Red)
{
if ((x == GridX) && (y - GridY == -1))//没过河之前不能横走和倒走
return true;
else if ((y
return true;
else
return false;
}
//return base.LimitPoint(x, y);
}
return false;
//return true;
//返回真假
}
else
{
return false;
}
}
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager
(typeof(Bing));
this.shuai1 = new ChessItem.Shuai();
this.SuspendLayout();
//
// shuai1
//
this.shuai1.AutoSize = true;
this.shuai1.BackColor = System.Drawing.SystemColors.Menu; this.shuai1.Font = new System.Drawing.Font(
System.Drawing.FontStyle.Bold);
this.shuai1.ForeColor = System.Drawing.Color.Black;
this.shuai1.GridX = 0;
this.shuai1.GridY = 0;
this.shuai1.Image =
((System.Drawing.Image)(resources.GetObject(
this.shuai1.ImageAlign = System.Drawing.ContentAlignment.TopLeft; this.shuai1.IsChecked = false;
this.shuai1.Location = new System.Drawing.Point(0, 0);
this.shuai1.Name =
this.shuai1.Size = new System.Drawing.Size(51, 51);
this.shuai1.TabIndex = 0;
this.shuai1.Text =
this.shuai1.TextAlign =
System.Drawing.ContentAlignment.MiddleCenter;
this.shuai1.type = IChessItem.Enums.ChessType.Black;
this.shuai1.Click += new System.EventHandler(this.shuai1_Click); this.ResumeLayout(false);
}
4.3.2车的走棋规则:
移动范围:任何位置。
移动规则:可以水平或垂直方向移动任意个无阻碍的点。
public class Ju : ChessBase
{
protected override bool LimitPoint(int x, int y)
{
if (base.LimitPoint(x, y))
{
//个性条件
int count = 0;
Panel panel = (Panel)this.Parent;
try
{
count = GetChildOnPoint(panel, GridX, GridY, x, y).Count;
}
catch { }
if ((GridX == x || GridY == y) && count == 0)
return true;
else return false;
}
else
{
return false;
}
}
}
4.3.3马的走棋规则实现:
移动范围:任何位置。
移动规则:每一步只可以水平或垂直移动一点,再按对角线方面向左或者右移动。另移动的过程中不能够穿越障碍。即遵循“马走日”的原则,不能绊马腿。
public class Ma:ChessBase
{
protected override bool LimitPoint(int x, int y)
{
if (base.LimitPoint(x, y))
{
if (this.type == IChessItem.Enums.ChessType.Black)
{
//个性化判断
if ((Math.Abs(GridX - x) == 2 && Math.Abs(GridY - y) == 1)) {
if (!HasChessOnPoint((GridX + x) / 2, GridY ))
{
return true;
}
else return false;
}
if (((Math.Abs(GridX - x) == 1 && Math.Abs(GridY - y) == 2))) {
if (!HasChessOnPoint(GridX, (GridY + y) / 2))
{
return true;
}
else return false;
}
}
else if (this.type == IChessItem.Enums.ChessType.Red)
{
//个性化判断
if (((Math.Abs(GridX - x) == 1 && Math.Abs(GridY - y) == 2))) {
if (!HasChessOnPoint(GridX , (GridY + y) / 2))
{
return true;
}
else return false;
}
if ((Math.Abs(GridX - x) == 2 && Math.Abs(GridY - y) == 1)) {
if (!HasChessOnPoint((GridX + x) / 2, GridY))
{
return true;
}
else return false;
}
}
return false;
}
return false;
}
}
4.3.4炮的走棋规则实现:
移动范围:任何位置。
移动规则:移动起来和车很相似,但它必须跳过一个棋子来吃掉对方的一个棋子。 public class Pao:ChessBase
{
protected override bool LimitPoint(int x, int y)
{
if (base.LimitPoint(x, y))
{
int count = 0;
//个性条件
if (GridX == x)
{
if (GridY
y++;
if (GridY > y)
y--;
}
if (GridY == y)
{
if (GridX
x++;
if (GridX > x)
x--;
}
try
{
count = GetChildOnPoint((Panel)this.Parent, GridX, GridY, x, y).Count;
}
catch { }
if ((GridX == x || GridY == y) && count == 0)
return true;
else if (count == 2)
return true;
else return false;
}
else
{
return false;
}
}
}
4.3.5士的规则:
移动范围:它只能在九宫内移动。
移动规则:它每一步只可以沿对角线方向移动一点。
public class Shi:ChessBase
{
protected override bool LimitPoint(int x, int y)
{
if (base.LimitPoint(x, y))
{
//个性化判断
if (Math.Abs(x - GridX) == 1 && Math.Abs(y - GridY) == 1)
{
if (this.type == IChessItem.Enums.ChessType.Red)
{
if (x 5 || y
return false;
else
return true;
}
else
{
if (x 5 || y > 2)
return false;
else
return true;
}
}
else
return false;
//移动为斜线
//x,y和GridX,GridY的差值为1或者-1
//判断属于哪一方
//如果是上面一方
//可以Y移动到0,2
//如果是下面一方
//可以Y移动到7-9
}
return false;
}
}
4.3.6相的规则:
移动范围:河界的一侧。
移动规则:它每一步只可以沿对角线方向移动两点,另外,在移动的过程中不能够穿越障碍。即遵循“象走田”的原则,不能绊象腿。
public class Xiang:ChessBase
{
protected override bool LimitPoint(int x, int y)
{
if (base.LimitPoint(x, y))
{
//移动为斜线
//x,y和GridX,GridY的差值为2或者-2
if (this.type == IChessItem.Enums.ChessType.Red && y >= 5)
{
}
}
4.3.7帅的规则:
if (Math.Abs(GridX - x) == 2 && Math.Abs(GridY - y) == 2) { if (HasChessOnPoint((x + GridX) / 2, (y + GridY) / 2)) { return false; } else { return true; } } return false; } else if (this.type == IChessItem.Enums.ChessType.Black && y
移动范围:它只能在九宫内移动。
移动规则:它每一步只可以水平或垂直移动一点。
public class Shuai:ChessBase
{
protected override bool LimitPoint(int x, int y)
{
if ((Math.Abs(x - GridX) == 0 && Math.Abs(y - GridY) == 1) || (Math.Abs(x - GridX) == 1 && Math.Abs(y - GridY) == 0))
{
if (this.type == IChessItem.Enums.ChessType.Red)
{
if (x 5 || y
return false;
else
return true;
}
else
{
if (x 5 || y > 2)
return false;
else
return true;
}
}
else
return false;
//return base.LimitPoint(x, y);
}
public override void Remove()
{
base.Remove();
IChessItem.Enums.ChessType winer;
if (type == IChessItem.Enums.ChessType.Black)
{
winer = IChessItem.Enums.ChessType.Red;
MessageBox.Show(
}
else
{
winer = IChessItem.Enums.ChessType.Black;
MessageBox.Show(
}
}
}
4.4悔棋:
(先使用堆栈记录下整个棋盘棋子的初始位置,以后棋子的每一次移动后的位置都存入堆栈中,因为堆栈是先进后出,只需将之前移动的那个棋子的位置返还回去即可。若有吃子,从吃子类中返还被吃棋子。)
功能代码:
public void HuiQi(QiPu hh)
{
Point tempbeichi = beichi.Pop();
Point tempchi = chi.Pop();
// MessageBox.Show(tempbeichi.X +
if (tempbeichi != new Point(10, 10))
{
for (int i = 0; i
{
if (this.panel1.Controls[i] is IChess)
{
IChess ic = (IChess)this.panel1.Controls[i];
if (ic.GridX == tempbeichi.X && ic.GridY == tempbeichi.Y) {
panel1.Controls.Remove(this.panel1.Controls[i]);
}
if (ic.GridX == tempchi.X && ic.GridY == tempchi.Y)
{
panel1.Controls.Remove(this.panel1.Controls[i]);
}
}
}
for (int i = 0; i
{
if ((hh.pxy[i].X == tempchi.X && hh.pxy[i].Y == tempchi.Y) || (hh.pxy[i].X == tempbeichi.X && hh.pxy[i].Y == tempbeichi.Y))
{
if (hh.Tname[i] ==
{
if (hh.Ttype[i] == IChessItem.Enums.ChessType.Black) {
Shuai shuai = new Shuai();
shuai.type = IChessItem.Enums.ChessType.Black; shuai.GridX = hh.pxy[i].X;
shuai.GridY = hh.pxy[i].Y;
shuai.Text =
shuai.Name =
//shuai.Image = Image.FromFile(@
shuai.BackColor = Color.Transparent;
this.panel1.Controls.Add(shuai);
shuai.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
else
{
Shuai jiang = new Shuai();
jiang.type = IChessItem.Enums.ChessType.Red; jiang.GridX = hh.pxy[i].X;
jiang.GridY = hh.pxy[i].Y;
jiang.Text =
jiang.Name =
//jiang.Image = Image.FromFile(@
jiang.BackColor = Color.Transparent;
this.panel1.Controls.Add(jiang);
jiang.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
}
else if (hh.Tname[i] ==
{
if (hh.Ttype[i] == IChessItem.Enums.ChessType.Black) {
Bing bing = new Bing();
bing.type = IChessItem.Enums.ChessType.Black; bing.GridX = hh.pxy[i].X;
bing.GridY = hh.pxy[i].Y;
bing.Text =
bing.Name =
//bing.Image = Image.FromFile(@
bing.BackColor = Color.Transparent;
this.panel1.Controls.Add(bing);
bing.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
else
{
Bing bing = new Bing();
bing.type = IChessItem.Enums.ChessType.Red; bing.GridX = hh.pxy[i].X;
bing.GridY = hh.pxy[i].Y;
bing.Text =
bing.Name =
//bing.Image = Image.FromFile(@
bing.InitChess();
bing.BackColor = Color.Transparent;
this.panel1.Controls.Add(bing);
bing.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
}
else if (hh.Tname[i] ==
{
if (hh.Ttype[i] == IChessItem.Enums.ChessType.Black) {
Pao pao = new Pao();
pao.type = IChessItem.Enums.ChessType.Black; pao.GridX = hh.pxy[i].X;
pao.GridY = hh.pxy[i].Y;
pao.Text =
pao.Name =
//pao.Image = Image.FromFile(@
pao.BackColor = Color.Transparent;
this.panel1.Controls.Add(pao);
pao.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
else
{
Pao pao = new Pao();
pao.type = IChessItem.Enums.ChessType.Red; pao.GridX = hh.pxy[i].X;
pao.GridY = hh.pxy[i].Y;
pao.Text =
pao.Name =
//pao.Image = Image.FromFile(@
pao.BackColor = Color.Transparent;
this.panel1.Controls.Add(pao);
pao.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
}
else if (hh.Tname[i] ==
{
if (hh.Ttype[i] == IChessItem.Enums.ChessType.Black) {
Ma ma = new Ma();
ma.type = IChessItem.Enums.ChessType.Black; ma.GridX = hh.pxy[i].X;
ma.GridY = hh.pxy[i].Y;
ma.Text =
ma.Name =
//ma.Image = Image.FromFile(@
ma.InitChess();
this.panel1.Controls.Add(ma);
ma.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
else
{
Ma ma = new Ma();
ma.type = IChessItem.Enums.ChessType.Red;
ma.GridX = hh.pxy[i].X;
ma.GridY = hh.pxy[i].Y;
ma.Text =
ma.Name =
//ma.Image = Image.FromFile(@
ma.BackColor = Color.Transparent;
this.panel1.Controls.Add(ma);
ma.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
}
else if (hh.Tname[i] ==
{
if (hh.Ttype[i] == IChessItem.Enums.ChessType.Black) {
Ju ju1 = new Ju();
ju1.type = IChessItem.Enums.ChessType.Black; ju1.GridX = hh.pxy[i].X;
ju1.GridY = hh.pxy[i].Y;
ju1.Text =
ju1.Name =
//ju1.Image = Image.FromFile(@
ju1.InitChess();
ju1.BackColor = Color.Transparent;
this.panel1.Controls.Add(ju1);
ju1.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
else
{
Ju ju1 = new Ju();
ju1.type = IChessItem.Enums.ChessType.Red; ju1.GridX = hh.pxy[i].X;
ju1.GridY = hh.pxy[i].Y;
ju1.Text =
ju1.Name =
//ju1.Image = Image.FromFile(@
ju1.BackColor = Color.Transparent;
this.panel1.Controls.Add(ju1);
ju1.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
}
else if (hh.Tname[i] ==
{
if (hh.Ttype[i] == IChessItem.Enums.ChessType.Black) {
Shi shi = new Shi();
shi.type = IChessItem.Enums.ChessType.Black; shi.GridX = hh.pxy[i].X;
shi.GridY = hh.pxy[i].Y;
shi.Text =
shi.Name =
//shi.Image = Image.FromFile(@
shi.BackColor = Color.Transparent;
this.panel1.Controls.Add(shi);
shi.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
else
{
Shi shi = new Shi();
shi.type = IChessItem.Enums.ChessType.Red; shi.GridX = hh.pxy[i].X;
shi.GridY = hh.pxy[i].Y;
shi.Text =
shi.Name =
//shi.Image = Image.FromFile(@
shi.BackColor = Color.Transparent;
this.panel1.Controls.Add(shi);
shi.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
}
else if (hh.Tname[i] ==
{
if (hh.Ttype[i] == IChessItem.Enums.ChessType.Black) {
Xiang xiang = new Xiang();
xiang.type = IChessItem.Enums.ChessType.Black; xiang.GridX = hh.pxy[i].X;
xiang.GridY = hh.pxy[i].Y;
xiang.Text =
xiang.Name =
//xiang.Image = Image.FromFile(@
xiang.BackColor = Color.Transparent;
this.panel1.Controls.Add(xiang);
xiang.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
else
{
Xiang xiang = new Xiang();
xiang.type = IChessItem.Enums.ChessType.Red; xiang.GridX = hh.pxy[i].X;
xiang.GridY = hh.pxy[i].Y;
xiang.Text =
xiang.Name =
//xiang.Image = Image.FromFile(@
xiang.BackColor = Color.Transparent;
this.panel1.Controls.Add(xiang);
xiang.MouseClick += new
MouseEventHandler(ju1_MouseClick);
}
}
}
}
}
}
Button按钮内的代码:
private void button1_Click(object sender, EventArgs e)//悔棋
{
if (_type == Enums.ChessType.Black)
{
label1.Text =
_type = Enums.ChessType.Red;
}
else
{
label1.Text =
_type = Enums.ChessType.Black;
}
qipu.Pop();
if (qipu.Count == 0)
{
MessageBox.Show(
return;
}
// ReBack rb = stack.Pop();
QiPu bb = qipu.Peek();
HuiQi(bb);
}
悔棋索使用到的栈的定义:
Stack qipu = new Stack();
Stack beichi = new Stack();
Stack chi = new Stack();
图.13 红黑双方各走一子
图.14 悔棋一次,黑方后退一步
图.15 再次悔棋,红方后退一步
图.16 继续点击悔棋,提示已经是第一步
4.5播放/关闭背景音乐:
图.17 axWindowsMediaPlayer
播放音乐所使用到的控件,支持播放mp3格式音乐。从
代码如下
private void music1ToolStripMenuItem_Click(object sender, EventArgs e) {
axWindowsMediaPlayer1.URL = @
}
使用相对路径来播放mp3音乐
关闭背景音乐:
private void 关闭音乐ToolStripMenuItem_Click(object sender, EventArgs e) {
axWindowsMediaPlayer1.close();
}
4.6残局:
清空棋盘上的棋子,重新定义棋子的位置
代码如下
private void 饿死台城ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.panel1.Controls.Clear();
timer1.Enabled = true;
timer1.Start();
Shuai shuai = new Shuai();
shuai.type = IChessItem.Enums.ChessType.Black;
shuai.GridX = 4;
shuai.GridY = 0;
shuai.Text =
shuai.Name =
shuai.InitChess();
shuai.BackColor = Color.Transparent;
this.panel1.Controls.Add(shuai);
shuai.MouseClick += new MouseEventHandler(ju1_MouseClick);
Shi shi = new Shi();
shi.type = IChessItem.Enums.ChessType.Black;
shi.GridX = 4;
shi.GridY = 1;
shi.Text =
shi.Name =
shi.InitChess();
shi.BackColor = Color.Transparent;
this.panel1.Controls.Add(shi);
shi.MouseClick += new MouseEventHandler(ju1_MouseClick);
Ju ju1 = new Ju();
ju1.type = IChessItem.Enums.ChessType.Black;
ju1.GridX = 4;
ju1.GridY = 2;
ju1.Text =
ju1.Name =
ju1.InitChess();
ju1.BackColor = Color.Transparent;
this.panel1.Controls.Add(ju1);
ju1.MouseClick += new MouseEventHandler(ju1_MouseClick);
Bing bing = new Bing();
bing.type = IChessItem.Enums.ChessType.Black;
bing.GridX = 8;
bing.GridY = 0;
bing.Text =
bing.Name =
bing.InitChess();
bing.BackColor = Color.Transparent;
this.panel1.Controls.Add(bing);
bing.MouseClick += new MouseEventHandler(ju1_MouseClick);
Bing bing1 = new Bing();
bing1.type = IChessItem.Enums.ChessType.Red;
bing1.GridX = 3;
bing1.GridY = 1;
bing1.Text =
bing1.Name =
bing1.InitChess();
bing1.BackColor = Color.Transparent;
this.panel1.Controls.Add(bing1);
bing1.MouseClick += new MouseEventHandler(ju1_MouseClick);
Shuai jiang = new Shuai();
jiang.type = IChessItem.Enums.ChessType.Red;
jiang.GridX = 5;
jiang.GridY = 9;
jiang.Text =
jiang.Name =
jiang.InitChess();
jiang.BackColor = Color.Transparent;
this.panel1.Controls.Add(jiang);
jiang.MouseClick += new MouseEventHandler(ju1_MouseClick);
Ju ju = new Ju();
ju.type = IChessItem.Enums.ChessType.Red;
ju.GridX = 5;
ju.GridY = 7;
ju.Text =
ju.Name =
ju.InitChess();
ju.BackColor = Color.Transparent;
this.panel1.Controls.Add(ju);
PushChess(this.panel1);
beichi = new Stack();
chi = new Stack();
}
图.18 残局-饿死台城
4.7帮助与关于:
简要说明该中国象棋对弈系统的基本操作方式与功能。
图.19 关于
图.20 帮助操作
图.21 帮助操作
5 可用性评估
5.1评估指标体系
5.1.1对整个系统的内容功能界面等方面进行评估
图.22 内容功能界面评估表
5.1.2对整个系统运行效率方面进行评估
5.1.3对整个系统给用户的体验感满意度方面进行评估
说明该系统设计的优劣?(从版面设计,系统的可观察性,系统可恢复性,系统响应性,系统的任务规范五个部分分析)
你觉得系统的哪些地方需要改进?
你在访问系统的过程中感到不便吗?若有,是在执行哪些任务时有这种感觉? 图.23 系统运行效率评估表
5.2具体步骤
(1)小组成员在老师和全班同学面前进行整个系统的演示。
(2)将组员做的象棋系统发给班级其他小组的同学们。
(3)各个其他小组针对我们的演示过程以及内容进行各项标准满意程度进行评分,然后提出针对整个系统有需要改进的问题或者建议。
(4)对各个小组的评分进行整合
5.3结果分析
图.24 内容功能界面评估表平均得分
用户反馈的错误:
(1) 为何将帅被吃还能继续走棋?
(2) 为什么没有语音识别?
(3) 界面设计不够完美
用户认可的优点:
(1) 基本功能都有实现
(2) 版面简介
(3) 系统可观察性与可会恢复性较强。
6 结论
6.1总结:
本次中国象棋对弈系统的开发,实现了一个具有打谱,悔棋,语音操控的中国象棋博弈系统。在实现中采用了前人的研究成果,并在很多次的错误、失败和成功后,对前人的理论和方法有了深刻的理解,明白了其中的原理,对如何开发计算机象棋程序有了自己实际的经验。在系统的设计实现过程中,受益颇多:
理解了数据结构的重要性。在堆栈的使用上会直接影响到后续的悔棋与打谱操作。 还有一个体会是要有先进的软件设计理念,如果一开始系统搭建得不好,以后的每一步改动都会很麻烦。本系统采用面向对象的体系结构,这个结构最大化的优点是:有利系统的复用,各模块之间独立性强,某一模块的改变只需要修改其他模块的很小部分甚至不用修改。 失败并不可怕,可怕的是不能从失败中吸取教训。毕业设计不是一天两天就能完成的工作,在这个过程中我遇到了很多困难,刚一开始不知道怎么解决,结果工作一直停滞不前。多向老师和同学请教,多看一些前人已经做出来的东西,从中吸收他们的经验,为己所用。
6.2不足之处:
我们小组所做的C# 语音中国象棋功能不是很齐全,前期只有新建一局,残局,播放背景音乐等功能。语音识别,悔棋,打谱均没有完成,后期上网查询资料,知道了悔棋怎么实现。但是因为悔棋的功能实现需要使用到栈,但是我们对于栈的使用不是很熟悉,所以在定义栈时,定义的太多,导致数据冗余,程序运行起来很卡。同时对于象棋的规则在最后一步上我个人认为还有缺陷,就是当象棋中一方胜利之后,会弹出一个窗口说明是哪方胜利,当点击确认之后,棋子又可以继续移动!此处我认为我们需要继续完善代码。另外,因为这个程序是我们做的,所以对于整个中国象棋的操作方式,我们都十分了解,所以我们忽略了帮助说明。后来老师提醒我们应加入帮助信息栏,后期我们完善了这个功能。一些控制上没有使用快捷键,后期回去完善。新开一局,加载新一局时间消耗太多,代码不够完善,存在某些bug。
6.3心得体会:
通过这次做C#语音中国象棋对弈系统的开发,我们明显感觉到了我们在C#以及数据结构的掌握上欠缺了很多。对于堆栈的使用以及使用panel绘制图形等有很多都已遗忘,只能通过上网查找相关资料,以及翻阅以前C#与数据结构的书籍来重新掌握他们的使用方式。虽然重新掌握的过程很痛苦,不过我们最后还是将象棋的基本功能实现了。
在对于整个程序的开发过程中,我学习到了不少知识,而且更重要的是一种编程的逻辑、一种思维。就拿在堆栈中存贮数字来表示棋子位置这一部分中,对于整个堆栈的使用是十分
需要技巧的,设置得当的话,对后来的编程起到事半功倍的效果,这点在和周围的同学对比后,让我们深有体会。