基于Android环境下人机对弈五子棋的设计与实现文档格式.docx
《基于Android环境下人机对弈五子棋的设计与实现文档格式.docx》由会员分享,可在线阅读,更多相关《基于Android环境下人机对弈五子棋的设计与实现文档格式.docx(18页珍藏版)》请在冰豆网上搜索。
201113007501 学生姓名:
薛俊文
指导老师:
刘亮龙讲师
【内容摘要】本文围绕基于Android操作系统,实现五子棋游戏的人人对战和人机对战程序,分析了五子棋的常用人工智能算法,并设计了算法,基于Android平台上以Java为语言开发了一个简单的五子棋小游戏,实现了五子棋的人机对战和人人对战。
【关键词】移动平台;
Android;
五子棋;
人工智能
1项目背景和意义
智能手机有Symbian、WindowsPhone、Android、IOS、BlackBerry10这些系统。
Android系统鹤立鸡群占据着巨大的市场份额,随着人们接触的手机时间越来越长,针对Android系统的程序和游戏层出不穷。
每个手机都有手机游戏,是人们闲暇时间的一种娱乐方式,而因为Android的巨大市场份额,Android平台下的手机游戏更是受到青睐。
手机游戏不仅操作方便,还能开发思维,让用户在休闲的同时拓展了思维增长了见识。
手机的小巧便捷,方便的特性成为我们日常生活中必备通讯工具。
拥有巨大市场空间的手机平台游戏会着智能手机的普及和应用必然会迎来新一轮的热潮。
在新一轮的热潮中,Android手机客户量大,所以手机小游戏的发展空间很可观。
2相关技术介绍
2.1Android体系构架
在AndroidSDK中,定义了让开发者使用与核心应用程序相同API的权限。
但要被应用程序架构的安全机制限制,使用者以用同样的机制来新增、置换组件。
目前所有的应用程序是由服务及系统来组成,包含有:
内容提供者、资源管理器、通知管理器、活动管理器、视窗管理器、位置管理器、电话管理器、包管理器。
具体的框架如图1所示:
图1Android架构的五个部分
一些C/C++的类库被Android使用,使用这些类库的组件各不相同。
这些功能可以通过应用程序框架被开发者使用。
包含以下核心类库:
surfaceManager、3Dlibraries:
SGL、MediaFramework、FreeType、SSL、SQLite:
SQLite、webKit、libc。
虽然Android开发、撰写应用程序是利用Java语言,但不使用Java相应的组件来运行程序,而是利用Google开发的Android组件运行。
核心类库和Dalvik虚拟机是Android运行时的两个重要的部分。
2.2Android系统核心
一个应用程序可以利用其它应用程序的元素是Android的主要特点。
应用程序发出请求的时候,不管它请求的是哪个部分,系统都应该能启动这些进程而且要把它的Java对象实例化。
Android应用程序的入口点可能不止一个。
实例化和运行所需要活动、服务、广播接收者、内容提供者这四种组件。
如图2所示:
图2:
实例化和运行所需组件
不是所有的应用程序都一定需要这四种组件,可能只有一个或者几个组成。
确定了要用什么组件的话,就应该把所需要的组件列在AndroidManifest.xml文件中,在这个文件中你可以声明应用程序组件以及它们的特性和要求。
3软件设计
3.1总体设计
该设计是Android平台下的五子棋游戏。
系统的总体设计模型如图3所示:
图3总体设计模型
图4五子棋类说明图
该游戏又分为以下几个模块:
初始化模块、主控制模块、人工智能模块、落子模块、胜负判断模块。
相应的数据流动如图5所示:
图5系统主流程图
3.2模型建立
五子棋分黑白两种颜色,若那种颜色首先在横、竖、左斜、右斜这任意方向大于等于五个棋子,就胜利了。
所以,该游戏模型的重点就在于棋盘和棋子。
流程如图6所示:
图6模型建立流程
常见的五子棋的棋盘为15×
15,我所绘制的棋盘为了满足不同型号手机的需求,设为根据游戏界面的大小自动调整。
所以就必须设置棋盘上每个点的大小,从而计算出游戏界面上的横竖坐标。
首先画出棋盘的框架,再根据计算出的横竖坐标,产生棋盘上所有的线,这样棋盘模型就建立好了。
棋子就应该利用方法与设置好的点对应,通过对点的属性修改载入相应的棋子模型,棋子模型就建立好了。
3.3控制模块介绍
控制模块分为主控制模块、胜负判断模块、落子模块中的处理分析部分。
控制模块如图7所示:
图7控制模块设计
主控制模块被用来转换下棋的顺序,当下棋顺序到了某一方的时候,把整个数据和流程转到响对应的模块里。
胜负判断模块将根据预先设定的规则,判断游戏胜负。
玩家与电脑落子模块中的处理分析部分是把用户点击界面的事件接收后进行处理分析。
3.4显示模块设计
游戏界面初始化模块、落子模块中的接收点功能与显示棋子功能都属于显示模块。
显示模块设计如图8所示:
图8显示模块设计
运行游戏用户直接进入主界面,初始化模块运行,用户将看到主界面上的棋盘与选择游戏模式的提示窗口。
用户选择游戏模式后,提示框将消失,游戏开始。
落子模块接受用户的触摸,在棋盘上对应的位置显示出棋子。
游戏胜利后再弹出提示窗口说明胜负,并提示如何继续进行游戏。
3.5人工智能设计
本游戏的人工智能是根据五子棋下法的特点从而计算出最佳的落子位置,或防守或进攻,从而达到胜利的目的。
人工智能总体的流程是:
人机对战游戏开始,轮到手机落子的时候先计算搜索范围,清空上一轮搜索结果。
进行第一次搜索,扫描所有空白点是否在搜索范围内,判断是否可能连成五子,是否为活1、半活2,抛弃该点是否有一方即将胜利。
接着进行第二次搜索,在即将胜利位置落子,不再往下分析。
判断是否有一方可以制造出活4是否整理出可以制造活3,半活4等棋型的位置,在制造活4处落子,不再往下分析。
最后进行第三次搜索,找出最佳落子位置落子。
4系统实现
4.1设计描述
启动游戏后直接进入主界面,在主界面上有提示窗口选择游戏模式。
选择游戏模式是根据手机键盘的方向键设定的。
游戏主界面的类图如图9所示:
图9游戏主界面类图
Chessboard继承自IChessboard,在IChessboard中定义了三种方法分别是:
取得棋盘最大纵坐标,取得棋盘最大横坐标,取得当前所有空白点。
有了取得棋盘最大横纵坐标的方法,才可以计算出棋子的位置。
而有了取得当前所有空白点的方法,棋盘上的这些点才可以下。
4.2初始化模块
在游戏初始化模块中,定义所有需要用到的函数和方法,使整个主界面得以运行。
初始化模块又分为显示模块初始化、控制模块初始化、数据模块初始化。
显示模块初始化
(1)创建画笔对象
(2)创建用于提示输赢的文本控件
(3)定义所有未下的空白点
(4)数据模块初始化
(5)设置点的大小
(6)定义屏幕右下角的坐标值,即最大坐标值
(7)初始横线和竖线的数目
(8)控制模块初始化
(9)定义可开局状态
(10)定义已开局状态
(11)定义已结束状态并且输出胜负。
(12)把当前状态默认为可开局状态
(13)初始化空白点集合
(14)创建玩家一并且默认为人类玩家
(15)玩家二根据选择的游戏模式进行初始化,若是电脑玩家调用Ai接口,若是人类玩家则再定义一个人类玩家
(16)创建方法强制使面板元素接收焦点
4.3主控制模块
利用whoRun来设置玩家下棋的状态。
whoRun=1时玩家一下棋,whoRun=2时玩家二下棋。
首先判断玩家是否取得胜利,如果胜利则直接输出结果,若没有胜利就轮到对手下棋。
部分代码如下:
if(!
player1.hasWin()){//如果玩家1没赢
if(player2==computer){//如果玩家2是电脑
//10豪秒后才给玩家2下棋
refreshHputerRunAfter(10);
}else{
setPlayer2Run();
}
}else{
//否则,提示玩家2输了
setMode(PLAYER_TWO_LOST);
}
首先判断玩家一是否胜利,若没胜利则可以落子。
在玩家二落子之前先判断是否是电脑落子,如果是电脑落子则根据接收到的触摸信息进行落子(在落子模块中再详细介绍)否则玩家二落子,而如果玩家一赢了则不再下棋跳到已结束状态并输出胜负结果。
将流程转移到玩家一落子与上述方法基本相同,先判断玩家二是否胜利,若没胜利则玩家一落子,否则不再下棋跳到已结束状态并输出胜负结果。
player2.hasWin()){//玩家2没有胜利
setPlayer1Run();
//玩家1下棋
//否则,提示玩家1输了
setMode(PLAYER_ONE_LOST);
4.4落子模块
在初始化模块中已经设置好点的大小和模型,在落子模块中就要把玩家或者电脑所要下的棋子显示在游戏界面中。
人类玩家落子首先根据触摸点找到棋盘上对应的点,接着再判断这个触摸点是否可以下棋,若可以下棋则正在下棋状态,该状态下不可以再响应其它触摸事件,进入正在下棋状态后利用IPlayer接口里的run方法把棋子数据放入可下棋的触摸点位置,最后刷新一下棋盘,棋子就显示在棋盘上对应的位置了。
privatevoidplayer1Run(MotionEventevent){
Pointpoint=newPoint(event.getX(),event.getY());
if(allFreePoints.contains(point)){//该点为空白点且可落子
//正在下某一步棋过程中,主是电脑下棋时需要较长的计算时间,这期间一定不可以再响应触摸事件
setOnProcessing();
player1.run(player2.getMyPoints(),point);
//刷新一下棋盘
refressCanvas();
而电脑落子首先判断是否是电脑落子,如果是电脑则利用继承自Handler的方法发消息触发handleMessage函数,而在电脑接收到触发消息后,根据人工智能计算出的最佳位置电脑走一步棋子,接着刷新一下棋盘,电脑要下的棋子就显示在棋盘上了。
部分伪代码如下:
//触发handleMessage函数
publicvoidhandleMessage(Messagemsg){
//电脑走一步棋子,
player2.run(player1.getMyPoints(),null);
//刷新一下
refressCanvas();
在刷新棋盘的时候都需要触发onDarw方法,onDarw就是把玩家或者电脑下的棋子显示出来的方法,把玩家一和玩家二的棋子利用canvas方法把对应棋子图片放入棋盘,并把上一次下的棋子和新下的棋子区分开。
4.5胜负判断模块
BasePlayer实现IPlayer这个接口,重写了hasWin()、setChessboard()、List<
Point>
getMyPoints()这三个方法。
hasWin()就是判断判断胜负的方法,即总体设计中的判断胜负模块。
首先定义我已下的棋子protectedList<
myPoints=newArrayList<
(200);
再创建一个boolean类型的函数,判断在横、竖、左斜、右斜四个方向的棋子个数是否大于等于五,若棋子个数大于等于五则胜利。
判断胜负流程如图10所示:
图10判断胜负
5人工智能算法
5.1搜索算法
设想一下玩五子棋时的情形,两人对战,假设现在该A走,那么他有N*N种走法(无论优劣),而B也有与之相对应的若干种走法,然后又轮到A走……如此往复。
一个人考虑怎么走这是搜索,而不仅要考虑自己还要考虑别人这就是对抗性搜索。
在这里可以用一个博弈树来表示五子棋游戏的这个过程,如图11所示,图中省略号表示游戏过程中的大量分枝。
图11五子棋游戏的完整博弈树
从图中可以看出,从游戏开局默认黑子先行的条件下,第一步走法的可能性等于棋盘的大小N*N(不考虑禁手,默认空白点都是可以落子的点),第二步白子有N*N-1种走法,如此往复,直到充满棋盘或者某方获胜。
大家可以看出这无疑是一个十分复杂的过程。
其实对于大多数棋类游戏来说都是无法建立完整的博弈树的,因为很多情形根本无法到达叶子节点,即使能够建立完整的博弈搜索树其计算量也是一个十分庞大的工作量,基本上是难以完成的。
在上面的博弈树中,设置A胜的分值为1,B胜的分值为-1,和局的分值为0。
因此每当轮到A走棋的时候均会选择分值最大的点,反之B每次定会选择分值最小的点。
由此可以得出中间节点落子的一种计算方法:
若该节点层次时该A落子,则此处的分值将是同层次中最大的,若该B落子时,则其分值必是同层节点中最小的。
在实际的游戏过程中,对于局势的好坏并不仅仅只是如上述那般简单。
也就是说可以使用更加细致的方法来评比局面而不是如前面所说的使用“1”,“-1”,“0”。
假设我们有一个函数,可以使A胜利取值为正的无穷大,B胜利取负的无穷大,和局为0,那么对于其余的局面就可以用两者之间的数字来表示,,这样做虽然没有完全搜索那么精确但是胜在这样可以降低实现的难度。
5.2评值算法
评值是通过已有的棋类知识来评判现在棋盘形式的优劣,其中包括了棋子灵活性,棋盘控制,棋子间关系,与搜索算法的配合等。
棋子间关系是棋子价值的一个重要衡量标准,例如五子棋游戏中一个黑子被一圈白子包围了的话,其价值要无限缩小。
我们可以把一颗棋子与其他棋子之间的制约关系看作是一种不利条件,比如说一颗黑子由于被白子制约无法形成“活4”,就减去一定评值分数,而在某一些边界上即使落子无论对对手还是自己都没有什么作用的点,其价值就应该减去一个相当大的分数。
棋子间关系的考虑可以极大的提高评值的精度。
5.3算法的主要设计思路
第一步:
第一步棋不需要太多考虑,如果玩家的第一步在棋盘边界上落子时,则AI的第一步将在棋盘的正中央落子;
否则,AI的第一步将在玩家第一步落子的左侧落子。
第二步:
如果整个棋盘计算,AI每下一步棋都要将整个棋盘上所有的空白点都分析一次,性能太差。
限定电脑计算范围,根据所有已下的棋子的边界值加2。
然后根据搜索范围进行第一次搜索分析,排除不在搜索范围之内的空白点、不可能连成五子即不可能胜利的点、以及活1,半活2点。
搜索游戏双方有没有哪方即将胜利,即双方有没有成五或者可不可以制造出活4。
第三步:
对第一次分析的结果进行再次分析。
找出双方可以制造出活4的点的位置,如果没有的话,就分别整理出双方可以制造出活3的点的位置,半活4的点的位置,双活3的点的位置,半活3的点的位置,双活2的点的位置,活2的点的位置。
第四步:
由前两次的搜索分析得出双方都不可能制造活4,所以只能找活3,再没有的话就找半活4,在没有的话就找单活3,双活2等棋型。
人工智能的流程如图12所示:
图12人工智能流程
5.4具体想法
在玩家落子后计算搜索范围。
具体的计算方法为初始化当前搜索范围为玩家的第一步棋子的边界值加2,然后分别遍历黑白两方已下的棋子,扫描在搜索范围内的所有未落子点,尝试在未落子点上下子,看看可不可以达成连五。
如果游戏双方有即将胜利的点的话就在该位置落子,不再往下搜索分析;
否则,进行第二次搜索。
第一次搜索结果类主要记录每一步棋在各个方向上的可连续数和状态(即是一头通还是两头通)。
为了缩短系统搜索时间节省系统开销,系统将对黑白双方的第一次搜索结果进行分开分析。
第二次分析,主要是是分析第一次搜索结果,第一次搜索结果会把一步棋在四个方向上形成的结果生成最多四个FirstAnalysisResult对象(敌我各四),而第二次搜索把这四个对象组合在一个SencondAnalysisResult对象。
其中compareTowResult函数为是用来确定该如何排序,返回-1则第一个参数优先,1则第二个参数优先,0则按原来顺序。
其主要代码为:
图13
前面提到第二次搜索主要是找出游戏双方是否可以制造出活4,如果有一方可以制造出活4的话,则在能制造出活4的位置落子,不再往下分析搜索,如果没有的话,则整理出游戏双方中可以制造出活3的点的位置,半活4的点的位置,双活3的点的位置,半活3的点的位置,双活2的点的位置,活2的点的位置。
当进行到第三次测试的时候,就表明黑白双方都没有成五和双活四,活四等棋型。
那么我们就只有挑最佳的落子位置落子了,首先看黑白双方有没有双活三,没有的话就找半活四,在没有的话就找活三或者双活二或者活二等棋型。
如何判断的出最佳落子的位置是第三次搜索的核心内容,而且也是设置难度级别的关键。
第三次搜索主要是对第二次搜索的得出的结果进行一个比较分析,比如说当分析得到最佳落子位置不止一个时则挑选出一个对敌人威胁较大的一个位置落子。
而当敌方的的棋型优于我方时,只能堵。
6实现效果
运行程序直接进入游戏主界面,游戏主界面如图14所示:
图14游戏主界面
进入游戏主界面后可以根据提示窗口进行游戏模式的选择。
游戏结束后的效果如图15所示:
图15游戏结束效果
从图15可以看出,当黑色或者白色棋子在横向或者斜向连成大于等于5个棋子的时候,连成的那一方就胜利。
并且游戏界面的边界都得以限制,不会溢出。
7结论
在五子棋的编写中,不但需要Android编程方面的知识,还需要对五子棋有所了解。
在学习了许多五子棋规则及技巧后,设计了一套简单的智能算法。
由于时间有限,能力不足,本算法只能考虑一步棋的优劣,对长远的棋局并不能预读。
设计了根据手机屏幕调整大小的棋盘,但在某些分辨率下,棋盘上每个点对应的位置较小,容易出现下错子的情况。
作为一个手机游戏而言,本设计还有很多不足,界面粗糙,功能简单。
8致谢
在这次毕业设计中,我要感谢我的论文指导老师刘亮龙和中期检查老师陈意山。
他们在我的毕业设计中,给予了我很多意见和启发。
初次接触Android开发,我还要感谢实习期间从基础一步步教我完成设计的组长刘强。
【参考文献】
[1]管玉钢.基于Android平台的五子棋游戏的研究与实现[D].江苏:
江苏大学,2012.
[2]佚名.Android核心技术与实例详解[CP/DK].
[3][美]Mario.Zechner.Android.游戏开发入门][M/CD].北京:
清华大学出版社,2013.
[4]佚名.XX百科五子棋[DB/OL].