第一篇:人工智能课程设计(五子棋)解读
《人工智能导论》课程报告
课题名称: 五子棋
姓名: X X 学号:114304xxxx 课题负责人名(学号): X X114304xxxx 同组成员名单(学号、角色): x x1143041325 XXX1143041036
指导教师: 张建州 评阅成绩: 评阅意见:
提交报告时间:2014年 1 月 9 日 课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
五子棋
计算机科学与技术 专业 学生 XXX 指导老师
张建州
[摘要] 人类之所以不断在进步,是因为我们人类一直不断的在思考,五子棋游戏程序的开发符合人类进步也是促进人类进步的一大动力之一。五子棋游戏程序让人们方便快捷的可以下五子棋,让人们在何时都能通过下棋来提高逻辑思维能力,同时也培养儿童的兴趣以及爱好,让孩子更加聪明。
同时,五子棋游戏程序的开发也使得五子棋这个游戏得到了广泛的推广,让世界各地的人们知道五子棋,玩上五子棋,这已经不是局限。五子棋游戏程序使得越来越多的人喜欢上了五子棋,热爱下五子棋,它是具有很好的带动性的。
关键词:五子棋
进步
思考
-1-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
目录
《人工智能导论》课程报告..................................................................................0 1 引言.....................................................................................................................3
1.1 五子棋简介...........................................................................................3 1.2 五子棋游戏的发展与现状......................................................................3 2 研究问题描述......................................................................................................4
2.1 问题定义...................................................................................................4 2.2 可行性研究...............................................................................................4 2.3 需求分析...................................................................................................5 2.4 总体设计...................................................................................................5 2.5 详细设计...................................................................................................6 2.6编码和单元测试........................................................................................6 3 人工智能技术......................................................................................................6 4 算法设计.............................................................................................................7
4.1α-β剪枝算法.............................................................................................7 4.2极大极小树................................................................................................7 4.3深度优先搜索(DFS).............................................................................8 4.4静态估值函数............................................................................................9 5 软件设计和实现..................................................................................................9
5.1 数据结构定义...........................................................................................9 5.2 程序流程图.............................................................................................17 6 性能测试...........................................................................................................18
6.1 程序执行结果.........................................................................................18 7 总结...................................................................................................................21 参考文献...............................................................................................................21
-2-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 1 引言
1.1 五子棋简介
五子棋是一种两人对弈的纯策略型汉族棋类益智游戏,棋具与围棋通用,由中国汉族人发明,起源于中国上古时代的传统黑白棋种之一。主要流行于华人和汉字文化圈的国家以及欧美一些地区。
容易上手,老少皆宜,而且趣味横生,引人入胜;不仅能增强思维能力,提高智力,而且富含哲理,有助于修身养性。已在各个游戏平台有应用。
古代五子棋棋盘与围棋棋盘是通用的,汉魏时为十七路(17×17)棋盘,至南北朝时即已流行十九路(19×19)棋盘,直至1931年出现所谓五子棋专用棋盘。
1.2 五子棋游戏的发展与现状
目前,连珠这一棋类运动已迅速在国际上发展起来。外国人都十分看好这一不起眼的智力游戏,并认为五子棋不仅能提高思维、开发智力、手脑并用、修身养性 而且富含哲理,具有东方的神秘和西方的直观,是中西文化的交汇点。许多国家的人对五子棋都有不同的爱称,例如韩国人把五子棋称之为“情侣棋”,言下之意是情人之间下五子棋有利于增加情感的交流;欧洲人称之为“中老年棋”,表示五子棋适合中老年人的生理特点和思维方式;美洲人喜欢将五子棋称之为“商业棋”,就是说商人谈生意时可一边下棋一边谈生意,棋下完了生意也谈成了。由此可见,尽管国度不同,语言各异,但人们都可以借助五子棋这一简单而又深奥的棋艺进行交流、比赛,增进友谊。
当前,有40多个国家和地区都在下五子棋,并有各种规模和级别的比赛。1989年8月在日本京都、1991年8月在俄罗斯联邦的莫斯科、1993年8月在瑞典、1995年8月在爱沙尼亚的塔林分别举行了第一、二、三、四届世界锦标赛。除第三届的冠军是爱沙尼亚人之外,其余三届的冠军都是日本人。五子棋 的世界锦标赛,每两年举办一次,其申国竞争也十分激烈。日本目前拥有自己的五子棋职业棋手,并且对连珠(五子棋)技术的研究也相当普遍和全面,就水平也正在日益增强。同时,五子棋的理论研究与探索也呈现蓬勃发展的势头,从1858年第一部五子棋专著问世以来,目前,全世界有2000多种五子棋的书籍及期刊,分别以日文、俄文、英文、瑞典文及中文出版发行。五子棋在我国的北京、上海、天津、云南、浙江、广东、四川、湖北、辽宁、新疆、河北等省(区)市都有很大的发展。北京多次举办了北京地区的五子棋赛,如“思曼杯”、“京空杯”、“奇奇童杯”、“北京第六届民族团结杯”和“北京第四岂民族运动会”的五子棋比赛。上海地区举办了“上文杯”五子棋大赛。云南省以及其他省市亦举办过许多五子棋比赛。所有这些赛事都越来越多地吸引了无数人的关注,表明了根植于中国的五子棋有着广泛的群众基础,-3-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 是群众喜闻乐见的体育活动。
而现在,很多很多游戏平台上面都有五子棋游戏供我们玩,任何游戏平台上面只要有棋牌类游戏的,那么它就有五子棋在里面,网络五子棋比赛,在联众,263,QQ游戏,UC里进行了10几年了可见,五子棋游戏在网络上面是非常火暴的,而且在棋牌游戏里面玩家人数排名总会占到很前面,不愧是风靡全球的棋牌游戏啊!在未来中,将会有越来越多的人关注五子棋,喜欢五子棋,那么将其变为商业化也会越来越多,而且还可以以教育孩子的方式来将其嵌套进去,或者用来做测试等等,可以说以后的五子棋游戏会是那么的精彩,那么的让人憧憬。那么对于它的游戏开发和发展也将会上升到举足轻重的地位去,它的发展会是相当之快的,就让我们拭目以待吧。研究问题描述
2.1 问题定义
问题定义的一个的关键问题是“要解决的问题是什么”,这个是这个阶段必须要明确要回答的问题。在没将问题定义好,试图准备下个阶段的任务。这明显是不成熟,甚至不可能的事。
本次系统设计中首先明确了需要解决的问题是五子棋AI算法,基本的要求是设计一款能够实现人机对战、人人对战和禁手的五子棋游戏,提供一些基本的操作如退出系统,向后悔棋等操作,重点是放在AI算法的研究。而并不是美工设计,也不是为了提供各种操作丰富的接口。主要是通过这种可视化的界面探讨AI,当然增加可玩性和美工会给系统润色不少。
上面只是很粗略的明确大概的方向,严格按照软件工程的方法这个阶段需要生产一份书面报告。需要通过对系统的实际用户访问调查,扼要地写出他对问题的理解,并在用户和使用部门负责人的会议上认真讨论这份书面报告,澄清含糊不精的地方,改正理解不正确的地方,最后得出一份双方都满意的文档。本系统的需求很少也很明显了。
2.2 可行性研究
这个阶段要回答的关键问题:“对于上一个阶段所确定的问题是否可行?”为了回答这个问题,我们需要进行一次大大压缩和简化了的系统分析和设计的过程,也就是在较抽象的高层次上进行的分析和设计的过程。
可行性研究应该比较简短,这个阶段的任务不是具体解决问题,而是研究问题的范围,探索这个问题是否值得去解,是否有可行的解决办法。
可行性研究应该比较简短,这个阶段的任务不是具体解决问题,而是研究问题的范围,探索这个问题是否值得去解,是否有可行的解决办法。可行性研
-4-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 究以后的那些阶段将需要投入要多的人力物力。及时中止不值得投资的工程项目,可以避免更大的浪费。
根据这些基本的概念,我在技术上主要是通过相关文档资料的查找后确定可行性,凭着大学期间打下厚实的专业科基础,特别是数据结构和算法,能够在这段时间理解通透并应该有所改进,后来证明是对的。利用剩下时间也应该来说也比较充裕的。经济上暂不考虑。
下面主要从技术上进行分析:
工具: Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaSE, JavaEE, JavaME)的总称。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于个人PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。在全球云计算和移动互联网的产业环境下,Java更具备了显著优势和广阔前景。所以用java来编写是一个很好的选择。
算法:在这图论搜索技术这方面,前人已有很成熟的算法。如粗糙的有深度优先算法(DFS)和广度优先算法(BFS)这两个基本的算法,关键需要解决的是能够设计出一种高效的剪枝函数,减小搜索问题的规模。目前博弈类游戏中的人工智能基本都采用极大极小值方法这对我来说是个挑战,而剪枝的则采用Alpha-Beta,通过丰富的文档资料初步了解到这些技术已经很成熟了。我们有信心能解决好这个问题。
2.3 需求分析
人工智能的第一大成就是下棋程序,在下棋程度中应用的某些技术,如向前看几步,把困难的问题分解成一些较容易的子问题,发展成为搜索和问题归纳这样的人工智能基本技术。今天的计算机程序已能够达到下各种方盘棋和国际象棋的锦标赛水平。但是,尚未解决包括人类棋手具有的但尚不能明确表达的能力。如国际象棋大师们洞察棋局的能力。另一个问题是涉及问题的原概念,在人工智能中叫做问题表示的选择,人们常能找到某种思考问题的方法,从而使求解变易而解决该问题。到目前为止,人工智能程序已能知道如何考虑它们要解决的问题,即搜索解答空间,寻找较优解答。
在设计本系统时考虑到用户需要的是一个操作简便界面简单的游戏软件,同时要提供人机和人人这样的功能,特别是人机部分,要考虑到不同级别的用户,电脑智能不能太低需要有一定的智能下棋功能等等。所以采用α-β剪枝法算法时就是为了达到这些目标。
2.4 总体设计
这个阶段必须回答的关键问题是:“概括地说,应该如何解决这个问题?”
-5-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 首先,应该考虑几种可能的解决方案。如,目标系统的一些主要功能是用计算机自动完成还是用人工完成;如果使用计算机,那么是使用批处理方式还是人机交互方式;信息存储使用传统的文件系统还是数据库„„。通常至少应该考虑下述几类可能的方案:
低成本的解决方案。系统只能完成最必要的工作,不能多做一点额处的工作。本系统的最基本要求就是能够实现必要的操作,其他额外的一些工作在后面完成
中等成本的解决方案。这样的系统不仅能够很好地完成预定的任务,使用起来很方便,而且可能还具有用户没有具体指定的某些功能和特点。虽然用户没有提出这些具体要求,但是系统分析员根据自己的知识和经验断定,这些附加的能力在实践中将证明是很有价值的。
这个成本方案在完成上面的低成本方案后添加的。如增加保存棋局,美化界面,实现观看电脑与电脑之间的对战等功能。
高成本的“十全十美”的系统。这样的系统具有用户可能希望有的所有功能和特点。
结构设计的一条基本原理就是程序应该模块化,也就是一个大程序应该由许多规模适中的模块按合理的层次结构组织而成。总体设计阶段的第二项主要任务就是设计软件的结构,也就是确定程序由哪些模块组成以及模块间的关系。通常用层次图或结构图描绘软件的结构。
2.5 详细设计
总体设计阶段以比较抽象概括的方式提出了解决问题的办法。详细设计阶段的任务就是把解法具体化,也就是回答下面这个关键问题:“应该怎样具体地实现这个系统呢?”
这个阶段的任务还不是编写程序,而是设计出程序的详细规格说明。这种规格说明的作用很类似于其他工程领域中工程师经常使用的工程蓝图,它们应该包含必要的细节,程序员可以根据它们写出实际的程序代码。
2.6编码和单元测试
这个阶段的关键任务是根据以上阶段分析的软件模型,编写各个功能模块。
要注意的是程序的扩张性和可读性。以便以后软件的升级修改。同时要仔细的测试每个功能编写好的功能模块。人工智能技术
人工智能也就是所谓的AI(Artificial Intelligence),它是一门很抽象的技术,-6-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 AI程序的编写不需要依据任何既定的思考模式或者规则。尤其是游戏中的AI可以完全依程序设计者本身的思考逻辑制作。我个人认为人工智能的核心应该是使计算机具有自动的处理事件的能力,而我们的所有的研究也应该围绕着这一方向。我们今天讨论的是策略类的人工智能。
策略类人工智能可以说是AI中比较复杂的一种,最常见的策略类AI游戏就是棋盘式游戏。在这类游戏中,通常的策略类AI程序都是使计算机判断目前状况下所有可走的棋与可能的获胜状况,并计算当前计算机可走棋步的获胜分数或者玩家可走棋步的获胜分数,然后再决定出一个最佳走法。本课程设计是基于AI中α-β剪枝算法的五子棋的博弈游戏,下面让我们来具体介绍一下相关的内容。算法设计
4.1α-β剪枝算法
我们的程序主要是用α-β剪枝法实现的。其基本思想或算法是,边生成博弈树边计算评估各节点的倒推值,并且根据评估出的倒推值范围,及时停止扩展那些已无必要再扩展的子节点,即相当于剪去了博弈树上的一些分枝,从而节约了机器开销,提高了搜索效率。具体的剪枝方法如下:
(1)对于一个与节点MIN,若能估计出其倒推值的上确界β,并且这个β值不大于 MIN的父节点(一定是或节点)的估计倒推值的下确界α,即α≥β,则就不必再扩展该 MIN节点的其余子节点了(因为这些节点的估值对MIN父节点的倒推值已无任何影响 了)。这一过程称为α剪枝。
(2)对于一个或节点MAX,若能估计出其倒推值的下确界α,并且这个α值不小于 MAX的父节点(一定是与节点)的估计倒推值的上确界β,即α≥β,则就不必再扩展该MAX节点的其余子节点了(因为这些节点的估值对MAX父节点的倒推值已无任何影响 了)。这一过程称为β剪枝。
4.2极大极小树
目前绝大部分的博弈类游戏中的人工算法都采用这种方法。假设己方为MAX点,对方则为MIN点。如果当层的节点为奇数时那么就为MAX层,同样为偶数时就为MIN层。当在MAX层时,该层的值就应该为下一个MIN层中的最大一个的值。当在MIN层是,该层的值就应该为它子层MAX的最小的一个。通俗的说就是当轮到我方时,我们就应该选择一个最有利于我们的点,预测对方可能下的最有利他方的点(相对我方来说就是最坏的点)。这样反复计算下去就能够得到根节点的最大值,这个点也就是我们最佳下棋点。在计算这个点时可以很明显的看出这是一个不断递归的过程,到达叶子节点时根据相关
-7-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 的计算规则算出该值然后向上一层不断的返回。下图中矩形代表极大层,椭圆代表极小层。
4.3深度优先搜索(DFS)
在图论中有两个很重要的遍历的方法,一个是深度优先搜索(DFS),另外一个是广度优先搜索(BFS).这两个方法的主要区别在于下一个节点的选择。DFS首先选择它的连接节点,若它的下个节点已经全部被遍历过或者不存在的话。则向上返回到上一个节点,在遍历其他的未被访问过的点。很容易想到这要用到堆栈结构,使用一个递归来实现。而BFS则是逐个的遍历它的联接接点,将已经访问过的点放入队列中。然后再依次取出继续这个过程。
DFS遍历过程如下:
首先从A点出发访问它的领接点B,因为B的领接点C,F均未被访问过,所以B点选择C(当然也可以选择F点)作为下一个要访问的点,C点的领接点是D,F选择下个节点D,而D的邻接点只有一个E且未被访问过,就将E作为了它下个节点。这时因为E已经没有可访问的邻点,所以向上一层返回到D,发现D也已经没有可访问的点了,继续向上层返回到C,由于C的邻节点F未被访问过,那么就访问F。所以整个过程的遍历结果为:
-8-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 ABECFD。
BFS的遍历过程为ABECFD。
4.4静态估值函数
当极大极小树到达叶子节点时,需要估算一下当前盘面的值。这个就根据某个计算规则计算也即是估值函数。因为这个值是已经确定的所以称为静态。
当只有一个点时,并且相邻的无对方的棋子或者不是边界等“阻碍物” 就给他50,否则给予10。
当两个点时,并且相邻的无对方的棋子或者不是边界等“阻碍物”给予1000,若存在一方有“阻碍物”则给以100,否则给予10。
当是活三的时候给予3600,当存在一边被堵时,就给予500。否则给予10。当是活四的时候给以500000,当一边被堵时,给予50000。否则给予10 当是五连子的时候就给予最高分1000000。最后判断是否是己方的,若不是则给予负号。
静态估值函数会很严重的影响到算法的智能,所以可根据在下棋的过程中不断的做出调整,使其更加的合理。根据一些测试,这组静态估值函数能够很好的反映棋盘重要性指标。软件设计和实现
5.1 数据结构定义
本程序定义15*15的五子棋棋盘,实现算法,在算法中采用的数据结构包括:int isChessOn[][]描述当前棋盘,0表示黑子,1表示白字,2表示无子;int pre[][]用来记录棋点的x,y坐标。
由于本课程设计是基于Java语言开发的,在Java中只能用类来表示并实现所定义的数据结构。所以下面将用类来描述相应的数据结构及算法:
public class ChessPanel extends JPanel{ private ImageIcon map;
//棋盘背景位图
private ImageIcon blackchess;
//黑子位图
private ImageIcon whitechess;
//白子位图
public int isChessOn [][];
//棋局
protected boolean win = false;
// 是否已经分出胜负
protected int win_bw;
// 胜利棋色
protected int deep = 3, weight = 7;
// 搜索的深度以及广度
public int drawn_num = 110;
// 和棋步数
int chess_num = 0;
// 总落子数目
-9-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
public int[][] pre = new int[drawn_num + 1][2];
// 记录下棋点的x,y坐标
最多(drawn_num + 1)个
public int sbw = 0;
//玩家棋色黑色0,白色1
public int bw = 0;
// 当前应该下的棋色
0:黑色(默认),1:白色
// 边界值,用于速度优化
protected int x_max = 15, x_min = 0;
protected int y_max = 15, y_min = 0;
protected boolean able_flag = true;
// 是否选择禁手标志 0:无禁手
1:有禁手(默认
private int h;
//棋子长
private int w;
//棋子宽
private int insx;
//插入棋子的位置
private int insy;
private Point mousePoint;
//鼠标当前位置
private int winer;
//获胜方
private boolean humanhuman=false;
//是否是人人对弈
private int plast=0;
//走了几步了,public int BLACK_ONE;
//0表黑子
public int WHITE_ONE;
//1表白子
public int NONE_ONE;
//2表无子
public int N;
//棋盘边长
//---------搜索当前搜索状态极大值-//
//alpha 祖先节点得到的当前最小最大值,用于alpha 剪枝
//beta 祖先节点得到的当前最大最小值,用于beta 剪枝。
//step 还要搜索的步数
//return 当前搜索子树极大值
protected int findMax(int alpha, int beta, int step){
int max = alpha;
if(step == 0){
return evaluate();
}
int[][] rt = getBests(1sbw)== 1)
//电脑可取胜
return 100 *(getMark(1)+ step*1000);
isChessOn[x][y] = 11);
isChessOn[x][y] = 2;
// 还原预设边界值
x_min=temp1;
x_max=temp2;
y_min=temp3;
y_max=temp4;
if(t > max)
max = t;
//beta 剪枝
if(max >= beta)
return max;
}
return max;
}
//-----------------------搜索当前搜索状态极小值--//
//alpha 祖先节点得到的当前最小最大值,用于alpha 剪枝
//beta 祖先节点得到的当前最大最小值,用于beta 剪枝
//step 还要搜索的步数
//return 当前搜索子树极小值。
protected int findMin(int alpha, int beta, int step){
int min = beta;
if(step == 0){
return evaluate();
}
int[][] rt = getBests(sbw);
for(int i = 0;i < rt.length;i++){
int x = rt[i][0];
int y = rt[i][1];
int type = getType(x, y, sbw);
if(type == 1)
//玩家成5
return-100 *(getMark(1)+ step*1000);
// 预存当前边界值
int temp1=x_min,temp2=x_max,temp3=y_min,temp4=y_max;
isChessOn[x][y] = sbw;
resetMaxMin(x,y);
int t = findMax(alpha, min, stepbwf);
if(able_flag && bwf==0 &&(type_1 == 20 || type_1 == 21 || type_1 == 22))// 禁手棋位置,不记录
continue;
rt[n][0] = i;
rt[n][1] = j;
rt[n][2] = getMark(type_1)+ getMark(type_2);
n++;
-12-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
}
// 对二维数组排序
Arrays.sort(rt, new ArrComparator());
int size = weight > n? n:weight;
int[][] bests = new int[size][3];
System.arraycopy(rt, 0, bests, 0, size);
return bests;
} //----------------------------计算指定方位上的棋型-------------------//
// x,y 方向线基准一点。
//ex,ey 指定方向步进向量。
// k 棋子颜色,0:黑色,1:白色
// 该方向上的棋子数目 以及 活度
private int[] count(int x, int y, int ex, int ey, int bwf){
// 该方向没意义,返回0
if(!makesense(x, y, ex, ey, bwf))
return new int[] {0, 1};
// 正方向 以及 反方向棋子个数
int rt_1 = 1,rt_2 = 1;
// 总棋子个数
int rt = 1;
// 正方向 以及 反方向连子的活度
int ok_1 = 0,ok_2 =0;
// 总活度
int ok = 0;
// 连子中间有无空格
boolean flag_mid1 =false,flag_mid2 = false;
// 连子中间空格的位置
int flag_i1 = 1,flag_i2 = 1;
if(isChessOn[x][y]!= 2){
throw new IllegalArgumentException(“position x,y must be empty!..”);
}
int i;
// 往正方向搜索
for(i = 1;x + i * ex < 15 && x + i * ex >= 0 && y + i * ey < 15 && y + i * ey >= 0;i++){
if(isChessOn[x + i * ex][y + i * ey] == bwf)
rt_1++;
-13-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
// 位置为空,若中空标志为false,则记为中空并继续搜索
否则,break
else if(isChessOn[x + i * ex][y + i * ey] == 2){
if(!flag_mid1){
flag_mid1 = true;
flag_i1 = i;
}
else
break;
}
// 位置为对方棋子
else
break;
}
// 计算正方向活度,,// 最后一个位置不超过边界
if(x + i * ex < 15 && x + i * ex >= 0 && y + i * ey < 15 && y + i * ey >= 0){
// 最后一个位置为空位 +1活
if(isChessOn[x + i * ex][y + i * ey] == 2){
ok_1++;
// 若是在尾部检测到连续的空格而退出搜索,则不算有中空
if(rt_1 == flag_i1)
flag_mid1 = false;
// 若中空的位置在4以下 且 棋子数>=4,则这一边的4非活
if(flag_mid1 && rt_1 > 3 && flag_i1 < 4){
ok_1--;
}
}
// 最后一个位置不是空格,且搜索了2步以上,若前一个是空格, 则不算中空,且为活的边
else if(isChessOn[x + i * ex][y + i * ey]!= bwf && i >= 2)
if(isChessOn[x +(i-1)* ex][y +(i-1)* ey] == 2){
ok_1++;
flag_mid1 = false;
}
}
// 最后一个位置是边界
搜索了2步以上,且前一个是空格, 则不算中空,且为活的边
else if(i >= 2 && isChessOn[x +(i-1)* ex][y +(i-1)* ey] == 2){
-14-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
ok_1++;
flag_mid1 = false;
}
// 往反方向搜索
for(i = 1;xi * ex < 15 && yi * ey < 15;i++){
if(isChessOn[xi * ey] == bwf)
ey >= 0){
rt_2++;
else if(isChessOn[xi * ey] == 2){
if(!flag_mid2){
flag_mid2 = true;
flag_i2 = i;
}
else
break;
}
else
break;} // 计算反方向活度
if(xi * ex >= 0 && yi * if(isChessOn[xi * ey] == 2){
ok_2++;
if(rt_2 == flag_i2)
flag_mid2 = false;
if(flag_mid2 && rt_2 > 3 && flag_i2 < 4){
ok_2--;
} } else if(isChessOn[xi * ey]!= bwf && i >= 2)
if(isChessOn[x(i-1)* ey] == 2){
ok_2++;
flag_mid2 = false;
} } else if(i >= 2 && isChessOn[x(i-1)* ey] == 2){ ok_2++;flag_mid2 = false;}
课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
//------------------分析棋子类型
// 两边都没中空,直接合成
if(!flag_mid1 &&!flag_mid2){
rt = rt_1 + rt_21;
// 判断中间的纯连子数,在5以上,直接返回;为4,返回活4;
if(temp >= 5)
return new int[] {temp, 2};
if(temp == 4)
return new int[] {temp, 2};
// 先看有没死4,再看有没活3,剩下只能是死3
if(rt_1 + flag_i21 >= 4)
return new int[] {4, 1};
if(rt_1+flag_i2-1 == 3 && ok_1 > 0 || rt_2+flag_i1-1 == 3 && ok_2 > 0)
return new int[] {3, 2};
return new int[] {3, 1};
}
// 有一边有中空
else {
// 总棋子数少于5,直接合成if(rt_1 + rt_21, ok_1 + ok_2};
// 多于5,先找成5,再找活4,剩下的只能是死4
else {
if(flag_mid1 && rt_2 + flag_i11, ok_2 + 1};
if(flag_mid2 && rt_1 + flag_i21, ok_1 + 1};
if(flag_mid1 &&(rt_2 + flag_i11 == 4 && ok_1 == 1 || flag_i2 == 4))
-16-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
}
}
} return new int[] {4, 2};
return new int[] {4, 1};5.2 程序流程图
-17-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 6 性能测试
6.1 程序执行结果
1.初始界面
2.人机博弈
3.人人博弈
-18-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
4.禁手选择
5.悔棋选择
-19-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163
6.规则界面
-20-课程名称:人工智能原理及其应用 学生姓名:何兵 学生学号:1143041163 7 总结
本程序是使用α-β搜索的算法完成的一个简单的五子棋博弈游戏。虽然α-βAlpha-Beta已经尽力做到细致、全面,但由于α-β搜索存在博弈树算法中普遍存在的一个缺点¬一随着搜索层数的增加,算法的效率大大下降。所以该搜索的效率还是不怎么理想,五子棋程序的“智力”也不高。因此可以在上述程序的基础上,针对五子棋本身的特点和规律对α-β搜索算法进行优化与修正,比如用启发式搜索。还有就是虽然使用了禁手思维的算法,但是这并不能平衡先后手之间的差距,依然是“先行必胜”,这个问题还有待进一步解决。
参考文献
[1] 明日科技.Java项目案例分析.清华大学出版社
[2](美)埃克尔著 陈昊鹏 译.Java编程思想.机械工业出版社-21-
读书的好处
1、行万里路,读万卷书。
2、书山有路勤为径,学海无涯苦作舟。
3、读书破万卷,下笔如有神。
4、我所学到的任何有价值的知识都是由自学中得来的。——达尔文
5、少壮不努力,老大徒悲伤。
6、黑发不知勤学早,白首方悔读书迟。——颜真卿
7、宝剑锋从磨砺出,梅花香自苦寒来。
8、读书要三到:心到、眼到、口到
9、玉不琢、不成器,人不学、不知义。
10、一日无书,百事荒废。——陈寿
11、书是人类进步的阶梯。
12、一日不读口生,一日不写手生。
13、我扑在书上,就像饥饿的人扑在面包上。——高尔基
14、书到用时方恨少、事非经过不知难。——陆游
15、读一本好书,就如同和一个高尚的人在交谈——歌德
16、读一切好书,就是和许多高尚的人谈话。——笛卡儿
17、学习永远不晚。——高尔基
18、少而好学,如日出之阳;壮而好学,如日中之光;志而好学,如炳烛之光。——刘向
19、学而不思则惘,思而不学则殆。——孔子
20、读书给人以快乐、给人以光彩、给人以才干。——培根
第二篇:人工智能与专家系统课程设计解读
目录
1.设计任务 1.1 设计题目 1.2设计要求 1.3设计任务 2.方案设计 2.1原理
2.2 具体设计方法 3.系统实施
3.1 系统开发环境 3.2系统主要功能介绍 3.3处理流程图 3.4 核心源程序 3.5系统运行结果 4.开发心得
4.1设计存在的问题
4.2进一步改进提高的设想 4.3经验和体会 5.参考文献 1.设计任务 1.1 设计题目
在一个3*3的方棋盘上放置着1,2,3,4,5,6,7,8八个数码,每个数码占一格,且有一个空格。这些数码可以在棋盘上移动,该问题称八数码难题或者重排九宫问题。
1.2 设计要求
其移动规则是:与空格相邻的数码方格可以移入空格。现在的问题是:对于指定的初始棋局和目标棋局,给出数码的移动序列。
1.3 设计任务
利用人工智能的图搜索技术进行搜索,解决八数码问题来提高在推理中的水平,同时进行新方法的探讨。
2.方案设计 2.1 原理
八数码问题是个典型的状态图搜索问题。搜索方式有两种基本的方式,即树式搜索和线式搜索。搜索策略大体有盲目搜索和启发式搜索两大类。盲目搜索就是无“向导”的搜索,启发式搜索就是有“向导”的搜索。
2.2 具体设计方法
启发式搜索
由于时间和空间资源的限制,穷举法只能解决一些状态空间很小的简单问题,而对于那些大状态空间的问题,穷举法就不能胜任,往往会导致“组合爆炸”。所以引入启发式搜索策略。启发式搜索就是利用启发性信息进行制导的搜索。它有利于快速找到问题的解。由八数码问题的部分状态图可以看出,从初始节点开始,在通向目标节点的路径上,各节点的数码格局同目标节点相比较,其数码不同的位置个数在逐渐减少,最后为零。所以,这个数码不同的位置个数便是标志一个节点到目标节点距离远近的一个启发性信息,利用这个信息就可以指导搜索。即可以利用启发信息来扩展节点的选择,减少搜索范围,提高搜索速度。
启发函数设定。对于八数码问题,可以利用棋局差距作为一个度量。搜索过程中,差距会逐渐减少,最终为零,为零即搜索完成,得到目标棋局。
3.系统实施
3.1 系统开发环境
Windows操作系统、SQL Server 200X
3.2 系统主要功能介绍
该搜索为一个搜索树。为了简化问题,搜索树节点设计如下: struct Chess//棋盘
3.4 核心源程序
#include “stdio.h” #include “stdlib.h” #include “time.h” #include “string.h” #include
const int N=3;//3*3棋盘
const int Max_Step=30;//最大搜索深度
enum Direction{None,Up,Down,Left,Right};//方向 struct Chess//棋盘 { int cell[N][N];//数码数组
int Value;//评估值
Direction BelockDirec;//所屏蔽方向
struct Chess * Parent;//父节点 };
//打印棋盘
void PrintChess(struct Chess *TheChess){ printf(“----------n”);for(int i=0;i printf(“t”); for(int j=0;j { printf(“%dt”,TheChess->cell[i][j]); } printf(“n”);} printf(“tttt差距:%dn”,TheChess->Value);} break;case Left: t_j++; if(t_j>=N) AbleMove=false; break;case Right: t_j--; if(t_j<0) AbleMove=false; break;};if(!AbleMove)//不可以移动则返回原节点 { return TheChess;} if(CreateNewChess){ NewChess=new Chess(); for(int x=0;x { for(int y=0;y NewChess->cell[x][y]=TheChess->cell[x][y]; } } else NewChess=TheChess;NewChess->cell[i][j]=NewChess->cell[t_i][t_j];NewChess->cell[t_i][t_j]=0; return NewChess;} //初始化一个初始棋盘 struct Chess * RandomChess(const struct Chess * TheChess) p=NULL;queue do{ p1=(struct Chess *)Queue1.front(); Queue1.pop(); for(int i=1;i<=4;i++)//分别从四个方向推导出新子节点 { Direction Direct=(Direction)i; if(Direct==p1->BelockDirec)//跳过屏蔽方向 continue; p2=MoveChess(p1,Direct,true);//移动数码 if(p2!=p1)//数码是否可以移动 { Appraisal(p2,Target);//对新节点估价 if(p2->Value<=p1->Value)//是否为优越节点 { p2->Parent=p1; switch(Direct)//设置屏蔽方向,防止往回推 { case Up:p2->BelockDirec=Down;break; case Down:p2->BelockDirec=Up;break; case Left:p2->BelockDirec=Right;break; case Right:p2->BelockDirec=Left;break; } Queue1.push(p2);//存储节点到待处理队列 if(p2->Value==0)//为0则,搜索完成{ p=p2; i=5; } } else { //打印 if(T){ /*把路径倒序*/ Chess *p=T; stack while(p->Parent!=NULL) { Stack1.push(p); p=p->Parent; } printf(“搜索结果:n”); while(!Stack1.empty()) { PrintChess(Stack1.top()); Stack1.pop(); } printf(“n完成!”);}else printf(“搜索不到结果.深度为%dn”,Max_Step); scanf(“%d”,T);} 3.5 系统运行结果 4.开发心得 4.1 设计存在的问题 完全能解决简单的八数码问题,但对于复杂的八数码问题还是无能为力。4.2 进一步改进提高的设想 可以改变数码规模(N),来扩展成N*N的棋盘,即扩展为N数码问题的求解过程。 2、内存泄漏。由于采用倒链表的搜索树结 05.参考文献 [1]王汝传.计算机图形学[M].北京:人民邮电出版社,1999:123-130.[2]刘榴娣,刘明奇,党长民.实用数字图像处理[M].北京:北京理工大学出版,2000:12-25..[3]丁兆海.Delphi基础教程[M].北京:电子工业出版社,1999.[4]王小华.Delphi 5程序设计与控件参考[M].北京:电子工业出版社,1999:70-120.[5]赵子江.多媒体技术基础[M].北京:机械工业出版社,2001:118-130.[6]段来盛,郑城荣,曹恒.Delphi实战演练[M].北京:人民邮政出版社,2002:80-95. 读书的好处 1、行万里路,读万卷书。 2、书山有路勤为径,学海无涯苦作舟。 3、读书破万卷,下笔如有神。 4、我所学到的任何有价值的知识都是由自学中得来的。——达尔文 5、少壮不努力,老大徒悲伤。 6、黑发不知勤学早,白首方悔读书迟。——颜真卿 7、宝剑锋从磨砺出,梅花香自苦寒来。 8、读书要三到:心到、眼到、口到 9、玉不琢、不成器,人不学、不知义。 10、一日无书,百事荒废。——陈寿 11、书是人类进步的阶梯。 12、一日不读口生,一日不写手生。 13、我扑在书上,就像饥饿的人扑在面包上。——高尔基 14、书到用时方恨少、事非经过不知难。——陆游 15、读一本好书,就如同和一个高尚的人在交谈——歌德 16、读一切好书,就是和许多高尚的人谈话。——笛卡儿 17、学习永远不晚。——高尔基 18、少而好学,如日出之阳;壮而好学,如日中之光;志而好学,如炳烛之光。——刘向 19、学而不思则惘,思而不学则殆。——孔子 20、读书给人以快乐、给人以光彩、给人以才干。——培根 姓 名: 刘旭 学 院: 计算机与通信学院 班 级: 通信工程101班 指导老师: 文志诚 目录 一、需求分析..................................................................................................................................3 1.1 开发背景....................................................................................................................................3 2.2 功能简介....................................................................................................................................3 二、系统设计..................................................................................................................................4 2.1 函数一览....................................................................................................................................4 2.2 “封面”的设计........................................................................................................................4 2.3 二维数组与控制台....................................................................................................................5 2.4 键盘操作....................................................................................................................................6 2.5判定.............................................................................................................................................7 2.6 悔棋的实现................................................................................................................................8 三、调试运行..................................................................................................................................9 3.1 进入界面....................................................................................................................................9 3.2 棋盘的初始状态......................................................................................................................10 3.3 激战中……..............................................................................................................................10 3.4 游戏结束..................................................................................................................................11 四、解决问题的关键....................................................................................................................11 五、课设总结................................................................................................................................11 六、附录........................................................................................................................................12 6.1 画图代码..................................................................................................错误!未定义书签。6.2 初始化......................................................................................................错误!未定义书签。6.3 Play函数..................................................................................................错误!未定义书签。 一、需求分析 1.1 开发背景 学习了数据结构该门课程,对于枯燥无味的理论知识,我们是否能够通过所学的知识在课程设计中做出有趣味东西,然后让我们对于数据结构更加的感兴趣呢?于是我和我的室友陈明建开始酝酿着写些什么东西。上个学期就已经写了通讯录那之类的链式结构,这次我们决心有所改变,我们学习了栈、队列、树、图,字典树有人选了,我们就来写一个基于图的小程序,五子棋,对,图的简单应用,于是我们开始着手来写这个小小的程序,祝我们好运! 2.2 功能简介 既然是五子棋,我们要做的是时时刻刻的将整个图(以下称为棋局)的状态呈现出来,那么界面就是必不可少的。MFC不会?没关系,我们就用基于控制台的字符输出来构建这个棋局吧,当然这只是第一步,详细如下: 1拥有一个良好的进入界面,以及必要的选项; ○2拥有一个二维的数组来记录和更新实时的状态,并且能够有一种方法在DOS界面下绘制○出整个棋局的实时状态(包括棋盘和棋子); 3能够通过键盘上的按键完成所选位置的移动和选定操作; ○4能够在每一次的走棋后判定是否游戏结束(棋盘走满或者是一方胜出); ○5能够完成悔棋的功能,并保证这之间的棋局绘图能够与二维数组数据同步,做到真正意○义上的悔棋。 二、详细设计 2.1 函数一览 2.2 “封面”的设计 首先还是讲些题外话,该程序由于与控制台有密切的关系,于是在代码中使用了不少 conio.h 中的函数,当然在显示时又使用了windows.h 中的 Sleep()函数,正是有了这些函数的使用,程序才得以顺利完成,尤其是后面频繁使用的gotoxy()函数。 进入正题,由于是一个小的程序,因此将每一个功能分成一个一个的函数,这样将在以后的修改和完成进度上都有很大的帮助。由上面的函数一览可以知道这个“封面”就是在Logo()函数里面实现的,函数实现过程中使用了Sleep()函数,使之有动态效果: void Logo(){ char Wel[30]= { “Made By Lyush&& Mirs Chen” }; printf(“ttt 欢迎试用五子棋系统n”); printf(“tt ”); for(int i= 0;i< strlen(Wel);++i) { putchar(Wel[i]); Sleep(200);// 可使字符一个一个的输出 } putchar(10);// 换行对应的 ASCII 码值为十进制的 10 } 2.3 二维数组与控制台 二维数组是用来使得整个棋盘的信息全部记录下来,因此在结构体中二维数组的声明是最关键的。 struct { int Status[MAX/2+2][MAX/2+2]; int MINBOX; int Step; char Graph[3][3]; char *FillGraph[9]; Sta Stack;} ChessBoard; 声明全局变量是为了使得各函数能够更方便地使用到这个结构体,现假设某点的坐标为(1, 1),那么如何在屏幕上打印这个点呢?这就利用到了ChangeCoordinates()与gotoxy()函数,前者使坐标进行转换,后者让光标走到所指的那个点,其实主要还是因为类似“┣、╋、●、○”在横向上所占都是两个英文字母的距离,因此在控制台上反映的就是和数组下标倍数关系了。部分代码如下: HANDLE hConsole= GetStdHandle(STD_OUTPUT_HANDLE); void ChangeCoordinates(int _X, int _Y, int *X, int *Y){ *X=(_X-1)* 2; *Y=(_Y-1)* 4;} void gotoxy(int x, int y)//这是光标的函数 { COORD coord; coord.Y= x; // 在实际的应用过程中发现交换x与y的赋值 coord.X= y; // 更好理解,即横行位x,纵行为y。 SetConsoleCursorPosition(hConsole, coord); } 2.4 键盘操作 在刚开始写这个五子棋的时候是以坐标来确定玩家的每一步棋,但后来发现这样操作性实在是差,键盘操作是更好的选择。这里又要用到一个函数 getch(),其作用是无回显的接受从键盘输入的字符,让屏幕不会出现你输入的字符且等待着按回车确定…… 有了这个宝贝函数,马上得到 “↑” 对应的ASCII码为-32和72 两个连着的数值,依次可得其他对应的ASCII码。后面在使玩家一和玩家二分离操作,玩家一则是利用 W、S、A、D + space来操作,玩家二则是 上下左右+ enter。配合ChangeCoordinates()与gotoxy()函数,完成对走棋的控制。部分代码如下: if(Opreat[0]== 13&& Ply== 2|| Opreat[0]== 32&& Ply== 1){ if(ChessBoard.Status[Move_X][Move_Y]== 0) { int TTop= ++ChessBoard.Stack.Top;ChessBoard.Status[Move_X][Move_Y]= Ply;ChessBoard.Stack.Record[TTop][0]= Move_X;ChessBoard.Stack.Record[TTop][1]= Move_Y;printf(“%s”, Graph);return true;// 该次走棋操作有效 } else { … } } if(Opreat[0]==-32&& Opreat[1]== 72|| Opreat[0]== 'w'|| Opreat[0]== 'W'){// 凡是接受了“上操作”,则Move_X的值减一,if(Currect(Move_X-1, Move_Y)) { Move_X-= 1; } } else if(…){ … } // 这是接下来的转换操作 ChangeCoordinates(Move_X, Move_Y, &Temp_X, &Temp_Y);Gotoxy(Temp_X, Temp_Y); 2.5判定 对于每次走棋后,首先应该做的就是判定一否有五个棋子已经连成一线,也是一个简单的搜索过程,由于每次走的点不一定是最外部的点,因此从每次走的点的两头同时搜索,当遇到两端同时结束时,搜索结束。当满足五子时游戏结束。当然,当棋盘被走满时,游戏亦结束。代码如下: bool Legal(int Point){ if(Point< 1|| Point> MAX/ 2+ 1) return false; else return true;} //搜索45度角是否为满足ChessBoard.MINBOX 以X正轴为参考轴 if(!Flag){ Count= 1;for(int i1= X-1, j1= Y+ 1, i2= X+ 1, j2= Y-1;Legal(i1)&& Legal(j1)|| Legal(i2)&& Legal(j2);i1--, j1++, i2++, j2--){ int LastCount= Count; if(Legal(i1)&& Legal(j1)&& ChessBoard.Status[i1][j1]== Ply) { Count++; } if(Legal(i2)&& Legal(j2)&& ChessBoard.Status[i2][j2]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX){ Flag= 1; return true; } } } 2.6 悔棋的实现 虽说下棋悔棋是一种不道义的行为,但是如果双方约定好了,未尝不可。在没写悔棋之前,只是记录了“上一次”的位置,声明了Last_X,Last_Y;当然既然要求悔棋,那么直接调用栈顶元素,即可定位上次走棋的位置。那么悔棋呢,取出“上一次”的位置,判定位置(不同的位置对应不同的填充图形类型)在二维数组中撤销走棋时所赋予的 Ply 值(玩家一走时,其值为1,玩家二走时,其值为2),重新将 ChessBoard.Status[ Last_X ][ Last_Y ] 赋为0。代码如下: int GetFillType(int X, int Y){ if(X== 1) { if(Y== 1) return 0; else if(Y== 16) return 2; else return 1; } else if(X== 16) { if(Y== 1) return 6; else if(Y== 16) return 8; else return 7; } else { if(Y== 1) return 3; else if(Y== 16) return 5; else return 4; } } bool Retract(int *X, int *Y){ int Temp_X, Temp_Y, TTop, FillType; if(!StackEmpty()) { TTop= ChessBoard.Stack.Top--; *X= ChessBoard.Stack.Record[TTop][0]; *Y= ChessBoard.Stack.Record[TTop][1]; ChessBoard.Status[*X][*Y]= 0;// 将该点置为真正意义上的空点 FillType= GetFillType(*X, *Y); ChangeCoordinates(*X, *Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); printf(“%s”, ChessBoard.FillGraph[FillType]); return true; } else { Gotoxy(9, 65); printf(“您已不能悔棋”); Sleep(300); Gotoxy(9, 65); printf(“ ”); return false; } } 三、调试运行 3.1 进入界面 3.2 棋盘的初始状态 3.3 激战中…… 3.4 游戏结束 四、解决问题的关键 这个五子棋的程序并没有什么复杂的算法,只是利用了简单的图知识和一个栈的应用,在这里主要的关键问题就是如何将程序有条理的写下来,有一个好的逻辑思维。将程序分成了多个功能函数,尽量的让一个函数的功能单一,只是在内部调用了其他的函数以辅助改函数功能的实现,比如判定坐标是否越界,坐标是否合法,悔棋的点的位置状态…… 这样便能做到各个击破,程序的形成也就变得畅通许多了。 五、课设总结 刚开始写这个程序,认为一定要用到 graphics.h, 无奈电脑TC不兼容,因此只好强行来画这个界面了,使用输入法里面的制表符,效果还不错,通过一长串的if … else … 最好还是画出来了,这个时候觉得控制台的简单图形还是能够画出来的,并且可以尽量去美化它的界面。后面的附录中将给出画棋盘和棋子的源代码。在程序设计的过程中,尤其是为源程序加上悔棋的功能,这期间总是有许多意想不到的错误,比如加上后,有时走了5个连子棋,但是程序并没有判定输赢,而是可以继续走、有时没有五个却已经结束了,光标没有复位,悔棋后,玩家的走棋顺序没有跟着改变……通过后来的一步步修改终于使得这些问题都一一解决了,比如说对 Prompt(提示)函数引进了返回值,判断该次操作是否成功,如果下了棋则为 true,如果是悔棋就是 false 了,这样便使得后面的操作更规范了和统一了。 六、附录 #include HANDLE hConsole= GetStdHandle(STD_OUTPUT_HANDLE); void HideCursor(){ CONSOLE_CURSOR_INFO cursor_info = {1, 0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);} typedef struct { int Record[260][2]; int Base; int Top;} Sta; struct { int Status[MAX/2+2][MAX/2+2]; int MINBOX; int Step; char Graph[3][3]; char *FillGraph[9]; Sta Stack; } ChessBoard; void Gotoxy(int x, int y)//这是光标的函数 { COORD coord; coord.Y= x; coord.X= y; SetConsoleCursorPosition(hConsole, coord); } void Logo(){ char Wel[30]= { “Made By Lyush&& Mirs Chen” }; printf(“ttt 欢迎试用五子棋系统n”); printf(“tt ”); for(int i= 0;i< strlen(Wel);++i) { putchar(Wel[i]); Sleep(200); } putchar(10);} int Login(){ int Mode, Skip= 0; char Request; if(!Skip) { printf(“nn在这儿你能DIY(Do it youself!)你的棋子,每个棋子接受一个汉字”); printf(“ Y Orz Nn”); scanf(“%c”, &Request); if(Request== 'Y'|| Request== 'y') { printf(“玩家一的 DIY 棋子-->”); scanf(“%s”, ChessBoard.Graph[1]); ChessBoard.Graph[1][2]= ' '; printf(“玩家二的 DIY 棋子-->”); scanf(“%s”, ChessBoard.Graph[2]); ChessBoard.Graph[2][2]= ' '; } } printf(“nn请选择先手玩家:___ nn”); printf(“nntttttt1 对应 玩家一对应 玩家二n”); if(Request== 'Y'|| Request== 'y') Gotoxy(10, 16);//原函数是 第一个参数为列,后一个参数为行,把Gotoxy函数做了更改 else Gotoxy(8, 16); scanf(“%d”, &Mode); if(Mode!= 1&& Mode!= 2) return Mode% 2+ 1; else return Mode;} void InitChessBiard(){ int TTop= ChessBoard.Stack.Top; fflush(stdin); ChessBoard.Step= 0; ChessBoard.Stack.Top= 0; ChessBoard.Stack.Base= 0; ChessBoard.Stack.Record[TTop][0]= 8;// 栈的0号位存储初始化的棋盘位置 ChessBoard.Stack.Record[TTop][1]= 8; ChessBoard.MINBOX= 5; ChessBoard.FillGraph[0]=“┏”; ChessBoard.FillGraph[1]=“┳”; ChessBoard.FillGraph[2]=“┓”; ChessBoard.FillGraph[3]=“┣”; ChessBoard.FillGraph[4]=“╋”; ChessBoard.FillGraph[5]=“┫”; ChessBoard.FillGraph[6]=“┗”; ChessBoard.FillGraph[7]=“┻”; ChessBoard.FillGraph[8]=“┛”; strcpy(ChessBoard.Graph[1], “○”); strcpy(ChessBoard.Graph[2], “●”); memset(ChessBoard.Status, 0, sizeof(ChessBoard.Status));} bool Legal(int Point){ if(Point< 1|| Point> MAX/ 2+ 1) return false; else return true;} bool Currect(int X, int Y){ if(Legal(X)&& Legal(Y)) return true; else return false;} void ChangeCoordinates(int _X, int _Y, int *X, int *Y){ *X=(_X-1)* 2; *Y=(_Y-1)* 4;} void Draw(){ // 画棋盘 for(int i= 1;i<= MAX;++i) { for(int j= 1;j<=MAX;++j) { if(i== 1) { if(j== 1) printf(“┏”); else if(j== MAX) printf(“┓n”); else if(j%2) printf(“┳”);// 横向占两个坐标位,竖向占一个坐标位 else printf(“━”); } else if(i== MAX) { if(j== 1) printf(“┗”); else if(j== MAX) printf(“┛n”); else if(j%2) printf(“┻”); else printf(“━”); } else { if(j== 1) { if(i% 2) printf(“┣”); else printf(“┃”); } else if(j== MAX) { if(i% 2) printf(“┫n”); else printf(“┃n”); } else { if(i% 2) { if(j% 2) printf(“╋”); else printf(“━”); } else { if(j% 2) printf(“┃”); else printf(“ ”); } } } } } // 画棋子 for(int i= 1;i<= MAX/ 2+ 1;++i) { for(int j= 1;j<= MAX/ 2+ 1;++j) { int Temp_X, Temp_Y; ChangeCoordinates(i, j, &Temp_X, &Temp_Y); if(ChessBoard.Status[i][j]== 1) { Gotoxy(Temp_X, Temp_Y); printf(“○”); } else if(ChessBoard.Status[i][j]== 2) { Gotoxy(Temp_X, Temp_Y); printf(“●”); } } } } int GetFillType(int X, int Y){ if(X== 1) { if(Y== 1) return 0; else if(Y== 16) return 2; else return 1; } else if(X== 16) { if(Y== 1) return 6; else if(Y== 16) return 8; else return 7; } else { if(Y== 1) return 3; else if(Y== 16) return 5; else return 4; } } bool StackEmpty() { if(ChessBoard.Stack.Top== ChessBoard.Stack.Base) return true; else return false;} bool Retract(int *X, int *Y){ int Temp_X, Temp_Y, TTop, FillType; if(!StackEmpty()) { TTop= ChessBoard.Stack.Top--; *X= ChessBoard.Stack.Record[TTop][0]; *Y= ChessBoard.Stack.Record[TTop][1]; ChessBoard.Status[*X][*Y]= 0;// 将该点置为真正意义上的空点 FillType= GetFillType(*X, *Y); ChangeCoordinates(*X, *Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); printf(“%s”, ChessBoard.FillGraph[FillType]); return true; } else { Gotoxy(9, 65); printf(“您已不能悔棋”); Sleep(300); Gotoxy(9, 65); printf(“ ”); return false; } } bool Prompt(int Ply, int Last_X, int Last_Y){ int Move_X= Last_X, Move_Y= Last_Y; int Temp_X, Temp_Y; char Opreat[2]; char *Graph= ChessBoard.Graph[Ply]; Gotoxy(1, 65); printf(“按退格键悔棋”); Gotoxy(3, 65); if(Ply== 1) { printf(“玩家一走棋:”); Gotoxy(5, 65); printf(“通过w s a d”); } else { printf(“玩家二走棋:”); Gotoxy(5, 65); printf(“通过↑↓←→”); } Gotoxy(7, 65); printf(“按空格或回车”); ChangeCoordinates(Move_X, Move_Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); while(1) { Opreat[0]= getch(); if(Opreat[0]== 8) { if(Retract(&Move_X, &Move_Y)) return false;// 该次操作为伪操作 else { Gotoxy(Temp_X, Temp_Y); continue; } } else { if(Opreat[0]== 13&& Ply== 2|| Opreat[0]== 32&& Ply== 1) { if(ChessBoard.Status[Move_X][Move_Y]== 0) { int TTop= ++ChessBoard.Stack.Top; ChessBoard.Status[Move_X][Move_Y]= Ply; ChessBoard.Stack.Record[TTop][0]= Move_X; ChessBoard.Stack.Record[TTop][1]= Move_Y; printf(“%s”, Graph); return true;// 该次走棋操作有效 } else { Gotoxy(9, 65); printf(“此步无效”); Sleep(300); Gotoxy(9, 65); printf(“ ”); Gotoxy(Temp_X, Temp_Y); continue; } } if(Ply== 2) { if(Opreat[0]!=-32) continue; Opreat[1]= getch(); } if(Opreat[0]==-32&& Opreat[1]== 72|| Opreat[0]== 'w'|| Opreat[0]== 'W') { if(Currect(Move_X-1, Move_Y)) { Move_X-= 1; } } else if(Opreat[0]==-32&& Opreat[1]== 80|| Opreat[0]== 's'|| Opreat[0]== 'S') { if(Currect(Move_X+ 1, Move_Y)) { Move_X+= 1; } } else if(Opreat[0]==-32&& Opreat[1]== 75|| Opreat[0]== 'a'|| Opreat[0]== 'A') { if(Currect(Move_X, Move_Y-1)) { Move_Y-= 1; } } else if(Opreat[0]==-32&& Opreat[1]== 77|| Opreat[0]== 'd'|| Opreat[0]== 'D') { if(Currect(Move_X, Move_Y+ 1)) { Move_Y+= 1; } } ChangeCoordinates(Move_X, Move_Y, &Temp_X, &Temp_Y); Gotoxy(Temp_X, Temp_Y); } } } bool Win(int Ply, int X, int Y) { // 先找上下的是否存在一条直线连成ChessBoard.MINBOX个 int Count= 1, Flag= 0; for(int i= X-1, k= X+ 1;Legal(i)|| Legal(k);i--, k++) { int LastCount= Count; if(Legal(i)&& ChessBoard.Status[i][Y]== Ply) { Count++; } if(Legal(k)&& ChessBoard.Status[k][Y]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } // 左右查找是否满足条件 if(!Flag) { Count= 1; for(int i= Y-1, k= Y+ 1;Legal(i)|| Legal(k);i--, k++) { int LastCount= Count; if(Legal(i)&& ChessBoard.Status[X][i]== Ply) { Count++; } if(Legal(k)&& ChessBoard.Status[X][k]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } } //搜索45度角是否为满足ChessBoard.MINBOX 以X正轴为参考轴 if(!Flag) { Count= 1; for(int i1= X-1, j1= Y+ 1, i2= X+ 1, j2= Y-1;Legal(i1)&& Legal(j1)|| Legal(i2)&& Legal(j2);i1--, j1++, i2++, j2--) { int LastCount= Count; if(Legal(i1)&& Legal(j1)&& ChessBoard.Status[i1][j1]== Ply) { Count++; } if(Legal(i2)&& Legal(j2)&& ChessBoard.Status[i2][j2]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } } // 搜索135度角是否满足ChessBoard.MINBOX if(!Flag) { Count= 1; for(int i1= X-1, j1= Y-1, i2= X+ 1, j2= Y+ 1;Legal(i1)&& Legal(j1)|| Legal(i2)&& Legal(j2);i1--, j1--, i2++, j2++) { int LastCount= Count; if(Legal(i1)&& Legal(j1)&& ChessBoard.Status[i1][j1]== Ply) { Count++; } if(Legal(i2)&& Legal(j2)&& ChessBoard.Status[i2][j2]== Ply) { Count++; } if(LastCount== Count) break; if(Count== ChessBoard.MINBOX) { Flag= 1; return true; } } } return false;} void Play(int Fir){ system(“cls”); Draw(); //SetConsoleTextAttribute(hConsole, FOREGROUND_RED| FOREGROUND_INTENSITY); int CurPly=Fir; while(1) { int TTop= ChessBoard.Stack.Top; if(ChessBoard.Step>= 256) { Gotoxy(11,65); printf(“游戏结束”); } if(Prompt(CurPly, ChessBoard.Stack.Record[TTop][0], ChessBoard.Stack.Record[TTop][1])) { TTop= ChessBoard.Stack.Top; if(Win(CurPly, ChessBoard.Stack.Record[TTop][0], ChessBoard.Stack.Record[TTop][1])) { Gotoxy(11, 65); if(CurPly== 1) printf(“玩家一胜利n”); else printf(“玩家二胜利n”); break; } CurPly= CurPly% 2+1; ChessBoard.Step++; } else if(ChessBoard.Step) { CurPly= CurPly% 2+1; ChessBoard.Step--; } } } char Reset(){ char Decide; Gotoxy(15, 65); printf(“是否重玩?”); Gotoxy(17, 65); printf(“'y' Orz 'n'n”); Gotoxy(19, 65); Decide= getchar(); return Decide;} int main(){ system(“mode con cols=80 lines=33”); Loop : //system(“color 2f”); InitChessBiard(); Logo(); Play(Login()); if(Reset()== 'Y'|| Reset()== 'y') { system(“cls”); goto Loop; } system(“pause”); return 0;} 人工智能课程设计报告 课 程:人工智能课程设计报告 班 级: 姓 名: 学 号: 指导教师:赵曼 2015年11月 人工智能课程设计报告 人工智能课程设计报告 课程背景 人工智能(Artificial Intelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。人工智能是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器,该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。人工智能从诞生以来,理论和技术日益成熟,应用领域也不断扩大,可以设想,未来人工智能带来的科技产品,将会是人类智慧的“容器”。 人工智能是对人的意识、思维的信息过程的模拟。人工智能不是人的智能,但能像人那样思考、也可能超过人的智能。 人工智能是一门极富挑战性的科学,从事这项工作的人必须懂得计算机知识,心理学和哲学。人工智能是包括十分广泛的科学,它由不同的领域组成,如机器学习,计算机视觉等等,总的说来,人工智能研究的一个主要目标是使机器能够胜任一些通常需要人类智能才能完成的复杂工作。但不同的时代、不同的人对这种“复杂工作”的理解是不同的。 人工智能是计算机学科的一个分支,二十世纪七十年代以来被称为世界三大尖端技术之一(空间技术、能源技术、人工智能)。也被认为是二十一世纪三大尖端技术(基因工程、纳米科学、人工智能)之一。这是因为近三十年来它获得了迅速的发展,在很多学科领域都获得了广泛应用,并取得了丰硕的成果,人工智能已逐步成为一个独立的分支,无论在理论和实践上都已自成一个系统。 人工智能是研究使计算机来模拟人的某些思维过程和智能行为(如学习、推理、思考、规划等)的学科,主要包括计算机实现智能的原理、制造类似于人脑智能的计算机,使计算机能实现更高层次的应用。人工智能将涉及到计算机科学、心理学、哲学和语言学等学科。可以说几乎是自然科学和社会科学的所有学科,其范围已远远超出了计算机科学的范畴,人工智能与思维科学的关系是实践和理论的关系,人工智能是处于思维科学的技术应用层次,是它的一个应用分支。从思维观点看,人工智能不仅限于逻辑思维,要考虑形象思维、灵感思维才能促进人工智能的突破性的发展,数学常被认为是多种学科的基础科学,数学也进入语言、思维领域,人工智能学科也必须借用数学工具,数学不仅在标准逻辑、模糊数学等范围发挥作用,数学进入人工智能学科,它们将互相促进而更快地发展。 人工智能课程设计报告 a[] a[i]=0表示第i行上还没有皇后; b[] b[i]=0表示第i列反斜线/上没有皇后; c[] c[i]=0表示第i列正斜线上没有皇后。 棋盘中同一反斜线/上的方格的行号与列号相同;同一正斜线上的方格的行号与列号之差均相同,这就是判断斜线的依据。 初始时,所有行和斜线上都没有皇后,从第1列的第1行配置第一个皇后开始,在第m列,col[m]行放置了一个合理的皇后,准备考察第m+1列时,在数组a[],b[]和c[]中为第m列,col[m]行的位置设定有皇后的标志;当从第m列回溯到m-1列时,并准备调整第m-1列的皇后配置时,清除在数组a[],b[]和c[]对应位置的值都为1来确定。 2)遗传算法 遗传算法的基本运算过程如下: a)初始化:设置进化代数计数器t=0,设置最大进化代数T,随机生成M个个体作为初始群体P(0)。b)个体评价:计算群体P(t)中各个个体的适应度。遗传算法 遗传算法 c)选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。选择操作是建立在群体中个体的适应度评估基础上的。d)交叉运算:将交叉算子作用于群体。遗传算法中起核心作用的就是交叉算子。 e)变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值作变动。群体P(t)经过选择、交叉、变异运算之后得到下一代群体P(t+1)。 f)终止条件判断:若t=T,则以进化过程中所得到的具有最大适应度个体作为最优解输出,终止计算。 3)csp最小冲突法 (1)初始化N个皇后的一个放置,允许有冲突 (2)考虑某一行的某个皇后,她可能与x个皇后冲突,然后看看将这个皇后移动到这一行的哪个空位能使得与其冲突的皇后个数最少,就移动到那里。(也可以考虑列,是等价的)(3)不断执行(2),直到没有冲突为止 2.数据结构 使用数组结构存储相关数据 一维数组: t + n] == 1||rd[i + t] == 1)continue; //没有冲突 ver[i] = 1;ru[i 人工智能课程设计报告 } } //后退处理 rd[i + t] = 0;ru[i1;i++){ } cout << endl;*/ cout << “row:” << i << “ col:” << this->ChromosomeMatrix[i][0] << endl;g = 1;if(DisplayAllAnsures)this->FillArea(k);this->CostMatrix[k] = this->CostFunc(k);bool DisplayAllAnsures=PrintChessBoard;//是否输出所有棋盘结果 int g = 0, num = 0; //逐个检查第row行的每个位置,看看是否存在冲突数更小的位置 for(int i = 0;i < N;i++){ } if(i == cur_col)continue; int conflict = col[i] + pdiag[GetP(row, i)] + cdiag[GetC(row, i)];if(conflict < min_conflict){ } min_conflict = conflict;optimal_col = i;+ cdiag[GetC(row, optimal_col)] 人工智能课程设计报告 } } col[optimal_col]++;pdiag[GetP(row, optimal_col)]++;cdiag[GetC(row, optimal_col)]++;R[row] = optimal_col;if(col[cur_col] == 1 && col[optimal_col] == 1 } && pdiag[GetP(row, optimal_col)] == 1 && cdiag[GetC(row, optimal_col)] == 1){ return Qualify();//qualify相对更耗时,所以只在满足上面基本条件后才检查 //否则当前点就是最佳点,一切都保持不变 return false;//如果都没变的话,肯定不满足终止条件,否则上一次就应该返回true并终止了 //检查冲突 bool CSP_Queens::Qualify(){ } //最终用户调用函数,numOfQueens为输入皇后数,PrintChessBoard判断是否输出棋盘表示 int CSP_Queens::CSPAlgorithms(bool PrintChessBord){ srand((unsigned)time(NULL));Init();if(Qualify()){//运气很好,初始化后就满足终止条件 } bool end = false;while(!end){ for(int i = 0;i < N;i++){ if(Adjust_row(i)){ end = true;if(PrintChessBord)Print_result();return 0;for(int i = 0;i < N;i++){ } return true;if(col[R[i]]!= 1 || } pdiag[GetP(i, R[i])]!= 1 || cdiag[GetC(i, R[i])]!= 1){ return false; 人工智能课程设计报告 2.遗传算法 3.CSP最小冲突算法 人工智能课程设计报告 总的来说,回溯在n值很小时,效率很高,但其求解范围很小,超过35基本就解不出来,遗传算法求解范围适中。在n值很大(>100)时,前两者都不能再解决,此时,CSP最小冲突法的效率最高,且与n值没有必然的联系。 总结 通过此次课程实习不仅大大加深了我对几种经典搜索算法的理解,而且帮助我很好的复习了队列、堆栈、图、文件读写这几部分的内容,使我对几种基本的数据结构类型的运用更加熟练。在解决这些问题的过程中我不但很好的巩固了数据结构的相关知识,而且提高了编程及程序调试能力,增强了自己编程的信心。 总之,在这次课程实习过程中我是实实在在学到了一些课堂上学不到的东西,同时也提高了实践能力。同时在这个过程中也暴露了自己的不少问题,在今后的学习过程成也会更加有针对性。最后还要感谢老师的悉心指导,解答我编程过程中的疑问、指出我程序中的不足,及提出可行的解决方法,让我的程序的功能更加完善。 CSP算法源代码: //CSPAlgorithms.h #pragma once class CSP_Queens { public: //构造函数,numOfQueens为输入皇后数,CSP_Queens(int numOfQueens);~CSP_Queens(); private: //row[i]表示当前摆放方式下第i行的皇后数,int *row;//col[i]表示当前摆放方式下第i列的皇后冲突数 int *col;int N;//放置N个皇后在N*N棋盘上 //从左上到右下的对角线上row-col值是相同的,但是这个值有可能是负值,最小为 12],2*N-1条,作为对角线编号 //R[]用来存储皇后放置位置,R[row] = col表示(row,col)处,即“第row行第col列”//cdiag[i]表示编号为i的对角线上的皇后数 int *cdiag;//counter diagonal,副对角线 有个皇后 int *R; public: int swap(int &a, int &b); //给定二维矩阵的一个点坐标,返回其对应的左上到右下的对角线编号 int GetP(int row, int col);//给定二维矩阵的一个点坐标,返回其对应的右上到左下的对角线编号 int GetC(int row, int col);//返回begin, begin + 1,..., endbegin个数中的随机的一个 int My_rand(int begin, int end);//左闭右开[begin, end) 人工智能课程设计报告 N = numOfQueens;row = new int[N];col = new int[N];pdiag=new int[2 * N];cdiag=new int[2 * N];R=new int[N];} CSP_Queens::~CSP_Queens(){ if(NULL!= row)delete[]row;if(NULL!= col)delete[]col;if(NULL!= pdiag)delete[]pdiag;if(NULL!= cdiag)delete[]cdiag;if(NULL!= R)delete[]R;} int CSP_Queens::swap(int &a, int &b){ int t = a;a = b;b = t;return 0;} // int CSP_Queens::GetP(int row, int col){ return row1;} int CSP_Queens::GetC(int row, int col){ return row + col;} //返回begin, begin + 1,..., endbegin个数中的随机的一个 int CSP_Queens::My_rand(int begin, int end)//左闭右开[begin, end){ return rand()%(end 人工智能课程设计报告 { } for(int i = begin;i <= end1;i++){ pdiag[i] = 0;cdiag[i] = 0;} //初始化当前棋局的皇后所在位置的各个冲突数 for(int i = 0;i < N;i++){ col[R[i]]++;pdiag[GetP(i, R[i])]++;cdiag[GetC(i, R[i])]++;} //用最小冲突算法调整第row行的皇后的位置(初始化时每行都有一个皇后,调整后仍然在第 + cdiag[GetC(row, optimal_col)] 人工智能课程设计报告 } } //当前点就是最佳点,一切都保持不变 return false;//如果都没变的话,肯定不满足终止条件,否则上一次就应该返回true并终止了 } //检查冲突 bool CSP_Queens::Qualify(){ for(int i = 0;i < N;i++){ if(col[R[i]]!= 1 || pdiag[GetP(i, R[i])]!= 1 || cdiag[GetC(i, R[i])]!= 1){ return false; } } return true;} void CSP_Queens::Print_result(){ } cout << “-------结果为:” << endl;cout << endl;for(int j = 0;j < N;j++){ for(int k = 0;k < N;k++){ if(R[j] == k) cout << “Q”; else cout << “+”; cout << “ ”;} cout << endl;} //最终用户调用函数,numOfQueens为输入皇后数,PrintChessBoard判断是否输出棋盘表 人工智能课程设计报告 int N;cin >> N;int time1 = clock();CSP_Queens myQueens(N);myQueens.CSPAlgorithms(end);int time2 = clock();cout << “---” << N << “皇后问题耗时:” << time2 读书的好处 1、行万里路,读万卷书。 2、书山有路勤为径,学海无涯苦作舟。 3、读书破万卷,下笔如有神。 4、我所学到的任何有价值的知识都是由自学中得来的。——达尔文 5、少壮不努力,老大徒悲伤。 6、黑发不知勤学早,白首方悔读书迟。——颜真卿 7、宝剑锋从磨砺出,梅花香自苦寒来。 8、读书要三到:心到、眼到、口到 9、玉不琢、不成器,人不学、不知义。 10、一日无书,百事荒废。——陈寿 11、书是人类进步的阶梯。 12、一日不读口生,一日不写手生。 13、我扑在书上,就像饥饿的人扑在面包上。——高尔基 14、书到用时方恨少、事非经过不知难。——陆游 15、读一本好书,就如同和一个高尚的人在交谈——歌德 16、读一切好书,就是和许多高尚的人谈话。——笛卡儿 17、学习永远不晚。——高尔基 18、少而好学,如日出之阳;壮而好学,如日中之光;志而好学,如炳烛之光。——刘向 19、学而不思则惘,思而不学则殆。——孔子 20、读书给人以快乐、给人以光彩、给人以才干。——培根 软件工程设计 专 业:班 级:姓 名:学 号:指导老师: I 目录 第一章 需求分析.......................................................1 1.1 总体分析..........................................................1 1.2 初始化............................................................1 1.3 主循环控制模块....................................................1 1.4 玩家下子..........................................................1 1.5 盘面分析填写棋型表................................................2 1.6 对方下子..........................................................2 1.7 胜负判断..........................................................2 第二章 功能描述.......................................................3 2.1 功能模块图........................................................3 2.2 功能说明..........................................................3 第三章 系统设计.......................................................4 3.1 流程图............................................................4 3.2 流程图说明........................................................5 第四章 运行结果.......................................................6 第五章 总结...........................................................7 附录一 源代码.........................................................8 II 软件工程设计 五子棋游戏 第一章 需求分析 1.1 总体分析 软件需求分析是软件开发周期的第一个阶段,也是关系到软件开发成败的关键一步。对于任何一个软件而言,需求分析工作都是至关重要的一步。只有通过软件需求分析,才能把软件的功能和性能由总体的概念性描述转化为具体的规格说明,进而建立软件开发的基础。实践表明,需求分析工作进行得好坏,在很大程度上决定了软件开发的成败。 软件需求分析的任务是:让用户和开发者共同明确将要开发的是一个什么样的软件。具体而言,就是通过对问题及其环境的理解、分析和综合,建立逻辑模型,完成新软件的逻辑方案设计。 基于本游戏,首先得为整个棋盘建立一张表格用以记录棋子信息,我们使用一个15*15的二维数组Table[15][15](15*15是五子棋棋盘的大小),数组的每一个元素对应棋盘上的一个交叉点,用‘0’表示空位、‘1’代表己方的子、‘2’代表对方的子;这张表也是今后分析的基础。在此之后还要为两个玩家双方各建立一张棋型表Computer[15][15][4]和Player[15][15][4],用来存放棋型数据。 1.2 初始化 首先,建立盘面数组Table[15][15]、对战双方的棋型表Computer[15][15][4]和Player[15][15][4]并将它们清零以备使用;然后初始化显示器、键盘、鼠等输入输出设备并在屏幕上画出棋盘(棋盘可以不显示)。 1.3 主循环控制模块 控制下棋顺序,当轮到某方下子时,负责将程序转到相应的模块中去,主要担当一个调度者的角色。 1.4 玩家下子 当轮到玩家下时,您通过键盘或鼠标在棋盘上落子,程序会根据该点的位置,在Table[15][15]数组的相应地方记录‘2’,以表明该子是玩家下的。 软件工程设计 1.5 盘面分析填写棋型表 您在下五子棋时,一定会先根据棋盘上的情况,找出当前最重要的一些点位,如“活三”、“冲四”等;然后再在其中选择落子点。先来分析己方的棋型,我们从棋盘左上角出发,向右逐行搜索,当遇到一个空白点时,以它为中心向左挨个查找,如果遇到己方的子则记录然后继续,如果遇到对方的子、空白点或边界就停止查找。左边完成后再向右进行同样的操作;最后把左右两边的记录合并起来,得到的数据就是该点横向上的棋型,然后把棋型的编号填入到Computer[x][y][n]中就行了(x、y代表坐标,n=0、1、2、3分别代表横、竖、左斜、右斜四个方向)。而其他三个方向的棋型也可用同样的方法得到,当搜索完整张棋盘后,己方棋型表也就填写完毕了。然后再用同样的方法填写对方棋型表。 注意:所有棋型的编号都要事先 定义好,越重要的号数越大! 1.6 对方下子 有了上面填写的两张棋型表,就是遍历棋型表Computer[15][15][4]和Player[15][15][4]找出其中数值最大的一点,在该点下子即可。但这种算法的弱点非常明显,只顾眼前利益,不能顾全大局,这就和许多五子棋初学者一样犯了“目光短浅”的毛病。如果在这儿下子将会形成对手不得不防守的棋型(例如:‘冲四’、‘活三’);那么下一步对手就会照您的思路下子来防守您,如此一来便完成了第一步的预测。这时再调用模块4对预测后的棋进行盘面分析,如果出现了‘四三’、‘双三’或‘双四’等制胜点,那么己方就可以获胜了(当然对黑棋而言‘双三’、‘双四’是禁手,另当别论);否则照同样的方法向下分析,就可预测出第二步、第三步„„ 等一等,要是盘面上没有对手必须防的棋型,哪该怎么办呢?进攻不成的话就得考虑防守了,将自己和对手调换一下位置,然后用上面的方法来预测对手的棋,这样既可以防住对手巧妙的攻击,又能待机发动反击,何乐而不为呢! 1.7 胜负判断 务须多言,某方形成五子连即获胜;若黑棋走出‘双三’、‘双四’或长连即以禁手判负。 软件工程设计 第二章 功能描述 2.1 功能模块图 五子棋游戏判断棋盘是否已满判断是否出错并提示判断那方获胜交替循环双方下棋 图2.1 功能模块图 2.2 功能说明 该五子棋程序基本上实现了五子棋的游戏功能,有双方下棋的界面及最终判定结果的界面。同时该游戏采用二维坐标实现,明了易懂,方便玩家在游戏过程中的基本操作,使游戏更加简便。在细节方面,该系统提供实时存储功能,随时记录为完成的游戏,使用户可以很好的处理意外中断的情况。该游戏基本实现了游戏的一些要求和特征。在游戏的源程序及文档方面,我们也严格遵守软件工程思想,立足实验要求,确定任务,需求分析,设计和编码,每个步骤力求清晰易懂。原代码注释详尽,各功能模块功能分明,可移植性强。当然该系统也有很多不足的地方,第一次进行独立的课程设计,也有很多细节方面是考虑到的,这款游戏也是在不断的调试和修改中产生和完善的。希望老师能够指出不足,帮助我不断提高。 软件工程设计 第三章 系统设计 3.1 流程图 开始棋盘已满是输出平局否“0”方选位置判断该位置是否有棋有另找位置无“0”方落子否判断“0”方是否获胜是输出“0”方获胜否棋盘已满是输出平局结束否“x”方选位置判断该位置是否有棋有另找位置无“x”方落子判断“x”方是否获胜是输出“x”方获胜 图3.1 流程图 软件工程设计 3.2 流程图说明 本程序定义了各种操作函数、各种状态判定宏,思想明确,思路清晰。各个判断选择了不同路径,因此继续进行或输出结果。程序中,“循环”的利用非常直接和清晰,双方交替下棋,因此循环往复。最终决出胜负或最终平局。分析时,也考虑了许多种情况,针对各个情况均作出了相对措施和解决方案。 程序采用循环进行双方交替下棋,并进行了很多判断。首先判断棋盘是否已满,若棋盘已满,则输出平局,结束游戏;若棋盘未满,则继续进行。然后判断“0”方是否胜出,若“0”方获胜,则输出“0”方获胜,结束游戏;若“0”方没有获胜,则继续进行。再判断“x”方是否获胜,若“x”方获胜,则输出“x”方获胜,结束游戏;若“x”方没有获胜,则继续进行。回到“首先”的判断。如此循环„„ 软件工程设计 第四章 运行结果 图4.1 运行结果初始图 图4.2 游戏过程图 图4.3 软件工程设计 图4.4 图4.5 软件工程设计 图4.6 图4.7 软件工程设计 图4.8 游戏进行图 图4.9 “0”方获胜图 软件工程设计 附录一 源代码 #include using namespace std; int Hsheng(char a[][15]); //判断o子是否获胜的函数 int Bsheng(char a[][15]); //判断x子是否获胜的函数 int he(char a[][15]); //判断是否平局(也就是棋盘下满了)的函数 void qipan(char a[15][15]) //执行输出棋盘命令 { cout<<“本游戏采用二维数组实现,棋盘为15X15的二维直角坐标系,均从1到15,祝二位游戏愉快.”;for(int i=0;i<15;i++) //打印棋盘 {第三篇:数据结构课程设计-五子棋
第四篇:人工智能课程设计报告-n皇后问题解读
第五篇:五子棋游戏软件工程课程设计