基于贪心策略直接搜索算法和极大极小博弈树算法的智能人机博弈五子棋游戏

Gameisover

发布日期: 2018-10-06 22:18:20 浏览量: 402
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

一、问题分析

五子棋是双人博弈棋类益智游戏,由围棋演变而来,属纯策略型。棋盘通常15*15,即15行,15列,共225个交叉点,即棋子落点;棋子由黑白两色组成,黑棋123颗,白棋122颗。游戏规则为黑先白后,谁先五子连成一条直线谁赢,其中直线可以是横的、纵的、45度、135度。

本次Java编程我的目的是现实人机对战,即游戏者一方是人,另一方计算机。这就要求程序不仅要具备五子棋的基本界面,还要编程指导计算机与人进行对弈。为了使程序尽可能智能,我采用了贪心策略、传统搜索算法、极大极小博弈树算法,对应游戏玩家的3个等级:简单、中等、困难。

二、功能设计

我的程序基本功能是实现人机对弈五子棋。人和电脑交替下棋,谁先五子连成一条直线谁就赢。下面是我程序的功能模块:

  • 等级设置
    核心功能是实现不同策略与算法的对比运用,纯贪心策略实现简单等级对手,直接搜索算法实现中等等级对手,极大极小博弈树算法实现困难等级对手。对应程序中的3选1单选按钮。

  • 悔棋功能
    模拟栈机制实现人悔棋,不限步长的悔棋。对应程序中的悔棋按钮。

  • 棋面绘制
    根据不同机计算机的屏幕分辨率,绘制逼真的棋盘。

  • 图片引入
    两张古典的人物图片,生动模拟对弈双方。人物图片旁的黑白棋钵图片显示黑白棋归属。

  • 背景设置
    支持用户选择背景,包括棋盘、棋盘边框、窗口边框,彰显个性。

  • 音乐播放
    下棋时有棋子落地的声音,一方胜利时有五子连成一片的声音。同时在设置背景时相应的改变整个对弈过程中的背景音乐。

  • 时间显示
    在棋盘正上方有一模拟文本框显示当前棋局用时。

  • 其他小功能
    支持和棋、认输、开启新游戏、退出游戏等操作。

三、数据结构与算法设计

3.1 数据结构部分

3.1.1 当前棋局的存储结构

我的五子棋程序选择通常用到的15*15棋盘,可以开二维数组PositionFlag = new int[15][15],PositionFlag[i][j]为0表示(i, j)点尚无棋,为1表示(i, j)点是人的棋子,为2表示(i, j)点是机器的棋子。之所以选择二维数组,主要原因有两点:

  • 本程序需要频繁随机访问15*15的交叉点,对应查询该点状态以及改变该点状态,随机访问是数组的特点。

  • 15*15=225开二维数组的内存需求相对现在内存为2G及以上的计算机完全可以接受,且数组实现简单、操作方便。

基于以上两点,尽管创建动态的顺序表—链表可能可以节省少量内存(可以只存当前有棋的点,原数组对应位置为0的点可以不存),但选择数组的优势完全在上述两点体现了出来。

3.1.2 实现悔棋操作的数据结构

由于每次悔棋只需回退当前几步,后进先出原则,这正是栈这种典型数据结构的设计思想,于是我选择栈。我自己先写了用自定义数组模拟的栈,但由于是学Java语言且由于悔棋的存储空间需要随当前步数增大而增大(由于每局最多下225步,即最多要悔225步,所以自己开个225的数组完全可以避免存储空间自增长的问题且内存完全可以接受,之所以不用自定义数组而用ArrayList类主要是为了尝试Java中STL的用法),所有我最终改为用Java类库中的ArrayList类。

确定用ArrayList类实现栈机制后就必须考虑每个ArrayList单元具体存储什么。刚开始我存储的是当前的棋局,即整个局面,而每个局面对应一个二维数组,这样是很占用内存的。试想一下,在最坏情况下,225个ArrayList单元,每个单元存放一个15*15的二维数组,尽管225*15*15在Java的内存管理机制下不会爆栈,但也是极不划算的。之所以说不划算,是因为有更好的解决方案。由于每次悔棋只是在回退倒数一步,多步悔棋只需循环回退,所以可以只存储当前棋局最后一步的下法,对应一个二维点,完全可以自定义一个二维坐标类chessOneStep。

3.2 算法设计部分

Java语言是面向对象的语言。我在进行五子棋游戏编程是总共传创建了11个自定义的类。在编写程序的过程中,我有一个明显的体验就是面向对象编程就是一项有关对象设计和对象接口技术,很多关键的技术就是如何设计自定义的对象。

下面我先概括给出我的所有类的作用:

  • mainFrame类:主框架类,我应用程序的入口

  • chessPositon类:主控类,这个类是我程序的核心类,负责控制双方的下棋,以及调用其他的类完成当前棋局的显示绘制

  • chessPanel类:面板类,调用其他底层类完成当前棋局的显示绘制

  • chessBoard类:棋盘绘制类,负责棋盘的绘制

  • chessImage类:文件类,包含各种资源(背景图片、背景音乐)以及静态全局变量(public static Type)

  • chessButton类:组件类,定义各种组件,包括按钮、单选按钮、文本框等

  • chessMusic类:音乐类,负责调用Java类库完成背景音乐、下棋音乐、取胜音乐等的播放

  • chessPiece类:棋局类,定义棋局二维数组数据结构并完成相关操作

  • chessList类:栈类,完成悔棋等操作

  • chessOneStep类:棋子类,定义每步坐标以及下在该处获得的估价值

  • myCompare类:排序类,完成chessOneStep类的自定义排序

五子棋程序类调用关系图如下所示:

四、详细设计

4.1 mainFrame类

作为我的五子棋程序的主类,mainFrame类主要实例化相关的对象,如chessbutton,chessborad等,从而完成框架的创建。更重要的是实例化chessposition,这是本程序的核心类,控制游戏双方行棋过程完成人机互动下棋,然后将MyChessPosition与鼠标响应addMouseListener()关联起来。

4.2 chessMusic类

一个好的游戏必须给人一种身临其境的感觉,而声音是营造这种氛围的重要因素。参照网上各游戏运行商的音乐配置,我选择相关逼真的声音。包括背景音乐、下棋棋子落到棋盘发出的声音以及一方胜出的配乐。所有这些功能的实现,依赖于自定义的chessMusic类,采用AudioInputStream配合Clip的方式完成音乐播放的软硬件工作,然后定义两个接口chessmusic(String Name)和Stop(),前者完成播放功能,后者完成关闭当前音乐功能。因为音频文件相对较大,而我的程序提供在不同背景乐之间切换的功能,所以在打开另一个音频文件之前必须关闭前一个正在播放的音频文件,防止出现溢出。

4.3 chessImage类

适当的动画或图片能给游戏玩家带来美的体验。所以我的五子棋程序界面在不失和谐的前提下引入了尽可能多的图片,包括对弈双方、棋钵等。图片引入的具体工作通过语句import javax.imageio.ImageIO完成。同时,由于图片要在用到它的类中被访问,为了避免频繁调用函数,我直接将图片相关联的对象定义为public static,表明是公用的、静态的。进一步引申开去,我将程序中用到的静态全局变量都定义在chessImage类中。具体如下:

  1. public static Date begin;//每局开始时间
  2. public static Date cur;//每局结束时间
  3. public static chessOneStep LineLeft;//结束端点1
  4. public static chessOneStep LineRight;//结束端点2
  5. public static boolean IsGameOver;//是否只有一方获胜
  6. public static int ColorOfBackGround[][]=
  7. {{255, 227, 132},{0,255,127},{218,165,32}};//背景颜色
  8. public static int ColorOfWindows[][]=
  9. {{ 60,179,113},{245,245,245},{122,122,122}};//背景颜色
  10. public static int WitchMatch;//背景搭配
  11. public static String MusicOfBackGround;//背景音乐
  12. public static int CurrentStep;//记录当前步数
  13. public static int Rank;//设置难度等级
  14. public static boolean IsSurrender;//判断是否认输
  15. public static boolean IsTie;//判断是否认输
  16. public static String Message;//输出提示信息
  17. public static Image IconImage;// 图标
  18. public static Image blackBoard;//白棋盘
  19. public static Image whiteBoard;//黑棋盘
  20. public static Image blackChess;// 白棋棋子图片
  21. public static Image whiteChess;// 白棋棋子图片
  22. public static Image RightPlayer;//白棋棋罐图片
  23. public static Image LeftPlayer;//白棋玩家头像图片
  24. public static String path = "src/";// 图片的保存路径

4.4 chessButton类

这个是程序的组件类。定义了各种功能键,完善程序功能,营造逼真的人机对战游戏效果。分为3类:

4.4.1 按钮组件

本程序有5个按钮,支持和棋、认输、新游戏、退出、悔棋等。认输和和棋按钮终止当前的棋局,给出相应的提示信息;退出按钮调用系统System.exit(0)的函数正常返回;悔棋按钮调用后面要介绍的chessList类实现悔棋;新游戏按钮则刷新当前棋局准备下一轮,要将记录当前棋局的二维数组全部置0,刷新当前棋局开始时间等。

4.4.2 单选按钮组件

游戏界面支持设置个性化界面,包括背景颜色与背景音乐,跟重要的一点是设置难度(简单、中等、困难)。单选按钮只能多选一。背景颜色主要是存储相关颜色搭配方案的RGB颜色,开2维数组,即对应RGB3原色数组的一维数组,然后通过改变WitchMatch全局变量的值来有用户自己选择颜色搭配,不同的颜色搭配对应不同的背景音乐表达一致的主题。难度设置主要是改变计算机的下棋算法,不同难度通过Rank判断进入不同的程序分支,实现不同智能等级的计算机下棋水平。

4.4.3 文本框

在不同的单选按钮前添加相应的文本框,提示用户可以实现的功能。同时我用颜色模拟出显示当前棋局耗用时间的文本框。

不论按钮还是单选按钮都要关联相应的消息,把相应功能的实现放在消息响应处理函数理。这些主要是实现Java库提供的消息响应接口里的方法。

4.5 chessPiece类

主要完成当前棋面的存储,存储棋面的数据结构为二维数组int [][]PositionFlag;然后定义获取、设置某点以及整个棋面的状态的方法。

  1. SetPositionFlag(intx, int y, int flag) //设置(x,y)处的状态为flag
  2. GetPositionFlag(intx, int y) //获取(x,y)处的状态
  3. SetAllFlag(intNewFlag) //设置当前整个棋面的状态为NewFlag
  4. GetAllFlag() //获取当前整个棋面的状态
  5. DrawChessPiece(Graphicsg) //绘制当前局面的棋子

4.6 chessBoard类

功能为绘制棋盘线。由于围棋的棋盘比较复杂,横线、竖线较多,且为了使棋盘美观,还要自定义窗口边框、棋盘边框、对弈双方边框等,对线宽、线型也有一定要求。有时要单像素线条,有时要多像素线条。对于多像素线条,我主要用了2种方法。

4.6.1 方法一

在需要绘制多像素线条处首先绘制一条单像素线,然后根据线宽要求上下平移适当像素达到绘制多像素的目的。这样的方法适合绘制水平线或竖直线,绘制其他斜率的线条容易造成走样。在没有想到比较好的反走样编程思想后我选择了调用Java库中已经封装好的函数。

4.6.2 方法二

为了克服方法一绘制非水平或竖直线时造成的走样,同时也为了更进一步学习Java语言,我猜想肯定会有类似OpenGL中设置线宽的画刷,于是上网百度找到了相应的画刷Stroke类。通过Java库实现绘制不同线宽的直线,达到了反走样效果。

4.7 chessOneStep类

这个类是为了配合chessList类实现悔棋以及在计算机下棋算法实现返回有效状态点而设计的。主要数据成员为

  1. private int x, y, weight; //其中x,y表示点坐标,weight表示将棋下到该点获得的估价值

主要方法如下:

  1. GetX() //获得当前对象的x坐标
  2. GetY() //获得当前对象的y坐标
  3. GetWeight() //获得当前对象的(x,y)处的估价值

4.8 chessList类

程序支持悔棋功能,为了实现悔棋,自定义了chessList类。这个类主要通过引入java.util.ArrayList和java.util.List实现集合的数据类型。然后自定义一些方法,如下:

  1. AddStep(chessOneStep OneStep) //添加一步棋到List中
  2. GetSize() //获得当前List的大小
  3. ClearList() //清空List
  4. RemoveLast() //删去List中的最后元素

由于每次删除当前List中的最后一个元素,实现后进先出,所以可以模拟栈的功能实现悔棋。

4.9 myCompare类

由于在计算机下棋的极大极小博弈树算法中需要对自定义对象chessOneStep按weight进行排序,所以引入了myCompare类,通过实现Comparator接口中的compare方法完成自定义对象排序。

4.10 chessPanel类

程序的自定义面板类,主要负责完成当前框架内容的显示。这是一个重要的与框架和图形显示密切相关的类。主要数据成员为

  1. private chessboard MyChessBoard; //当前显示棋盘
  2. private chesspiece MyChessPiece; //当前显示整个棋面的状态

主要方法如下:

  1. chesspanel(chessboard MyChessBoard1, chesspiece MyChessPiece1)//构造函数,分别用MyChessBoard1和MyChessPiece1初始化MyChessBoard和MyChessPiece
  2. display(chessboard MyChessBoard1, chesspieceMyChessPiece1)//自定义显示回调函数,调用repaint()完成重新绘制游戏界面
  3. paintComponent(Graphics g)//核心方法,调用各种函数完成具体的绘制工作

4.11 chessPositon类

程序算法核心类,总的功能是控制人和计算机轮流下棋,以及调用chessPanel类中的display(chessboard, chesspiece )方法完成界面的实时刷新。关于chessPositon类,我在此将重点介绍。chessPosition类的主要数据成员如下:

  1. privatestatic chessboard MyChessBoard; //当前显示棋盘
  2. publicstatic chesspiece MyChessPiece; //当前显示整个棋面的状态
  3. privatestatic chesspanel Mychesspanel; //当前显示面板
  4. publicstatic chesslist MyChessList=new chesslist();//当前下棋集合,用于悔棋
  5. finalprivate static int INF = (1 << 30);// 表示正无穷大的常量,用于极大极小博弈数搜索算法
  6. publicstatic boolean CanGo; //控制当前下棋一方

类的设计集中体现在成员方法的设计上。实现人机对战,只有语言是远远不够的,还要加入算法,用算法引导计算机下棋。下面介绍该类的方法成员:

  1. chessposition(chesspanel , chessboard ,chesspiece ) //带有参数的构造函数
  2. chessposition() // 不带参数的构造函数
  3. mouseClicked(MouseEvent event)

鼠标响应函数,负责人的下棋,根据鼠标点击的位置转换得到所在棋盘的相对位置。如果该位置不合法,即超出棋盘有效范围,点击无响应;如果该位置上已有棋,弹出消息框给出提示。这二者都要求重新给出下棋位置,即当前鼠标响应无效…直到点击到棋盘有效区域。

  1. IsOver(int Array,int x,int y)

判断当前int[][]Array对应的棋局是否结束,即一方五子连成一条直线。此处有两种思路,一种对当前棋面上的所有棋子都进行一次判断,具体为水平方向、竖直方向、与水平线成45度方向、与水平线成135度方向,只要有一个方向五子连成一条直线就说明有一方获胜,游戏结束;另一种思路为只在当前下棋的4个方向进行判断,我的程序采用的是第二种,所以IsOver方法除了int[][]Array参数外,还有x, y参数,(x, y)表示当前下棋的坐标点。

  1. display()

通过调用自定义面板类的显示回调函数用于重新显示游戏界面,达到每下一步棋及时更新游戏界面的目的。

  1. GetValue(int flag, int num)

估值函数,根据经验把棋局分成只有1颗棋相连,2颗棋相连且两端被封死,2颗棋相连且一端封死另一端活的,2颗棋相连且两端都是活的,同理3颗棋、4颗棋也各自可分3种情况。不同的情况对应不同的估价值。估价值的设定是决定计算机一方是否智能的一个关键因素。

  1. GetPredictValue(int flag, int num)

对未连成一片但通过再下一颗子就能连成一片的局面进行估值,这在双方下棋的有限步骤内是能产生重要影响的。如果每局棋仅考虑当前一步,是不可取的。

  1. Evaluate(int Array, int x, int y)

根据棋面具体情况以及预先设定的估值函数,对某个点对应的局面进行评估。由于每次双方只能下一颗棋,所以可以每次取当前局面的所有点中对应估值最大值点的估值作为整个局面的估值。

计算机下棋方法1

  1. GetGreedNext()

对应难度等级为简单,采用贪心思想。每次下棋前在求得最有利点下棋,而是否最有利只是通过一步评估。算法伪码描述为(Max取负无穷大):

  1. for(行i015)
  2. {
  3. For(列j015)
  4. {
  5. If((ij)对应的位置无棋)
  6. {
  7. a.假设放上一颗由人控制的棋,求估价值;
  8. b.假设放上一颗由计算机控制的棋,求估价值;
  9. c.取二者中较大值作为(i,j)处的估价值tmp
  10. d.取tmpMax较大值赋值给Max.
  11. }
  12. }
  13. }

最终Max对应的点就是当前整个局面中最大的估值点。至于上述为什么要考虑双方都在该点下棋的情况呢?主要原因为下五子棋是个攻防兼备的过程,不仅要考虑自己对自己最有利,还要考虑对对手最不利,通俗来讲就是在自己赢的时候不能让对手先赢。

计算机下棋方法2

  1. GetSearchNext(int LookLength)
  2. derectSearch(int [][]Array,boolean who,int deepth)

直接搜索法,对应难度等级为中等。每步棋最多有225个不同下法,若采用直接搜索法则对应的孩子节点有225个(在下棋过程中会逐渐减少),即每层有最多225个节点待扩展,这就决定了直接搜索进行不超过2次,主要原因有两点:

  • 采用深度优先搜索需要递归,递归中状态过多可能会爆栈,我们知道递归是用栈机制来实现的;采用宽度优先搜索又需要存储为扩展的节点,这对内存容量要求很高。
  • 不管深搜还是广搜,在时间复杂度为O(N^m)的情况下都是不能接受的。其中N为当前棋局的待扩展节点,最大225;m为搜索的深度。

综上所述,在采用直接搜索法时搜索深度不能太深,严格来说是应该控制在2层以内,在计算机运算速度在10^7次每秒的情况下,理论和实验都表明超过2层就会变得很慢且这种趋势成指数级增长。直接搜索算法伪代码为:

  1. GetSearch(boolean flagint deep)
  2. {
  3. 如果deep等于0,返回当前棋局估值;
  4. for(行i015)
  5. {
  6. For(列j015)
  7. {
  8. If((ij)对应的位置无棋)
  9. {
  10. 如果轮到计算机下棋,置标志位为2
  11. GetSearch(!flagdeep-1);
  12. 如果轮到人下棋,置标志位为1
  13. GetSearch(!flagdeep-1);
  14. }
  15. }
  16. }
  17. }

计算机下棋方法3

  1. GetMinMaxsearchNext(int LookLength)
  2. MinMaxsearch(int [][]Array,boolean who, int deepth)

极大极小博弈树法,对应难度等级为困难。五子棋是个博弈游戏,当前在寻找对自己最有利的下棋点时要尽可能保证对对手最不利,这种思想可以用极大极小博弈树完美阐释。关于极大极小博弈树在这里就不多说了。就算用极大极小博弈树,关于每次扩展节点与扩展深度的问题也是无法回避的。每次扩展节点不能太多,扩展深度不能太深,但若简单限制二者,就会使计算机游戏方表示出的智能水平有限。在每次扩展节点时可以采用贪心策略,首先对当前所有可扩展节点进行一次估值,选择其中估价值最大的节点进行扩展,这是一个很好的优化。好的开始很可能产生好的结果,良性循环。本题我是选择最好的5个节点进行扩展,扩展深度为4层,因为五子连成一条直线就可以取胜,故选择4层是合理的。

下面给出极大极小博弈树算法的伪代码

  1. MinMaxsearch(int [][]Array,boolean who, int deepth)
  2. {
  3. 如果到达了搜索深度,返回当前棋局估值;
  4. for(行i015)
  5. {
  6. For(列j015)
  7. {
  8. If((ij)对应的位置无棋)
  9. {
  10. 对当前棋局进行估值,结果存在List表中;
  11. }
  12. }
  13. }
  14. 取其中最大的5个节点进行MinMaxsearch()估值。
  15. }

由于取到对方最大的可以干扰对方,即在对方最有利的坐标点下上自己的棋可以“损人利己”,何乐不为?每次都取最大的这与极大极小博弈树算法有些不太一样,但对于本题是有效的。

五、源程序清单

由于整个程序比较长,在这里只贴出我认为较为重要的代码。

  • 源程序1 每局棋数据结构
  • 源程序2 控制人机交互下棋
  • 源程序3 完成面板的绘制

5.1 源程序1 每局棋数据结构

  1. import java.awt.Graphics;
  2. public class chesspiece {
  3. private int[][] PositionFlag = new int[15][15];// 表示格子上的棋子类型,0表示黑子,1表示白字
  4. public void SetPositionFlag(int x, int y, int flag) {
  5. PositionFlag[x][y] = flag;
  6. }
  7. public void SetAllFlag(int[][] NewFlag) {
  8. PositionFlag = NewFlag;
  9. }
  10. public int GetPositionFlag(int x, int y) {
  11. return PositionFlag[x][y];
  12. }
  13. public int[][] GetAllFlag() {
  14. return PositionFlag;
  15. }
  16. public void DrawChessPiece(Graphics g) {// 画棋子
  17. for (int i = 0; i < 15; i++) {// 扫描棋盘中所有的棋子
  18. for (int j = 0; j < 15; j++) {
  19. int x = (int) (chessboard.Left) + i * (int) (chessboard.Inc)
  20. - 15;// 把棋子在棋盘中对应的下标转化成在游戏中的坐标
  21. int y = 25 + j * (int) (chessboard.Inc) - 15;
  22. if (GetPositionFlag(i, j) == 1) {// 如果指定位置的棋子是黑色棋子
  23. g.drawImage(chessimage.whiteChess, x, y, null);
  24. } else if (GetPositionFlag(i, j) == 2) {// 如果指定位置的棋子是白色棋子,
  25. g.drawImage(chessimage.blackChess, x, y, null);
  26. }
  27. }
  28. }
  29. }
  30. }

5.2 源程序2 控制人机交互下棋

  1. import java.awt.event.MouseAdapter;
  2. import java.awt.event.MouseEvent;
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import javax.swing.JOptionPane;
  6. public class chessposition extends MouseAdapter {
  7. private static chessboard MyChessBoard;
  8. public static chesspiece MyChessPiece;
  9. private static chesspanel Mychesspanel;
  10. public static chesslist MyChessList = new chesslist();
  11. final private static int INF = (1 << 30); // 表示正无穷大的常量
  12. public static boolean CanGo;
  13. public chessposition(chesspanel Mychesspanel1, chessboard MyChessBoard1,
  14. chesspiece MyChessPiece1) {
  15. chessposition.Mychesspanel = Mychesspanel1;
  16. chessposition.MyChessBoard = MyChessBoard1;
  17. chessposition.MyChessPiece = MyChessPiece1;
  18. CanGo = true;
  19. }
  20. public chessposition() {
  21. }
  22. public void mouseClicked(MouseEvent event) {
  23. if (!CanGo)
  24. return;
  25. // 获取鼠标点击的棋盘相对位置
  26. int x = event.getX() - (int) (chessboard.Left);
  27. int y = event.getY() - 25;
  28. int Max = (int) (chessboard.Inc * 14) + 39;// 棋盘最右、下边
  29. int Min = 0;// 棋盘最左、上边
  30. if ((x < Min) || (x > Max) || (y < Min) || (y > Max))// 无效棋子
  31. return;
  32. int Inc = (int) chessboard.Inc;
  33. int NextX = x / Inc;
  34. int NextY = y / Inc;
  35. new chessmusic("下棋.wav");
  36. if (0 != MyChessPiece.GetPositionFlag(NextX, NextY)) {
  37. JOptionPane.showMessageDialog(JOptionPane.getRootFrame(),
  38. "您的走法违规,该处已有棋子!", "温馨提示", JOptionPane.ERROR_MESSAGE);
  39. return;
  40. }
  41. chessOneStep OneStep = new chessOneStep(NextX, NextY, 0);
  42. MyChessList.AddStep(OneStep);// 保留当前下得棋
  43. MyChessPiece.SetPositionFlag(NextX, NextY, 1);
  44. chessimage.CurrentStep++;
  45. if (chessimage.CurrentStep == 225) {
  46. chessimage.Message = "伯仲之间 ,胜负难分!";
  47. CanGo = false;
  48. }
  49. if (IsOver(MyChessPiece.GetAllFlag(), NextX, NextY))
  50. new chessmusic("取胜.wav");
  51. display();
  52. if (0 == chessimage.Rank)
  53. GetGreedNext();
  54. else if (1 == chessimage.Rank)
  55. GetSearchNext(1);
  56. else if (2 == chessimage.Rank)
  57. GetMinMaxsearchNext(3);
  58. }
  59. // 计算机采用一步攻防贪心策略下棋
  60. public void GetGreedNext() {
  61. int NextX, NextY;
  62. if (!CanGo)
  63. return;
  64. // 完全裸下
  65. int MaxWei = -INF;
  66. int idX = -1;
  67. int idY = -1;
  68. for (int i = 0; i < 15; i++) {
  69. for (int j = 0; j < 15; j++) {
  70. if (0 == MyChessPiece.GetPositionFlag(i, j)) {
  71. MyChessPiece.SetPositionFlag(i, j, 2);
  72. int tmp = Evaluate(MyChessPiece.GetAllFlag(), i, j);
  73. if (tmp >= MaxWei) {
  74. MaxWei = tmp;
  75. idX = i;
  76. idY = j;
  77. }
  78. MyChessPiece.SetPositionFlag(i, j, 1);
  79. tmp = Evaluate(MyChessPiece.GetAllFlag(), i, j);
  80. if (tmp > MaxWei) {
  81. MaxWei = tmp;
  82. idX = i;
  83. idY = j;
  84. }
  85. MyChessPiece.SetPositionFlag(i, j, 0);
  86. }
  87. }
  88. }
  89. NextX = idX;
  90. NextY = idY;
  91. new chessmusic("下棋.wav");
  92. if (-1 == NextX && -1 == NextY) {
  93. if (chessimage.CurrentStep == 225) {
  94. chessimage.Message = "伯仲之间 ,胜负难分!";
  95. CanGo = false;
  96. }
  97. return;
  98. }
  99. chessimage.CurrentStep++;
  100. chessOneStep OneStep = new chessOneStep(NextX, NextY, 0);
  101. MyChessList.AddStep(OneStep);// 保留当前下得棋
  102. MyChessPiece.SetPositionFlag(NextX, NextY, 2);
  103. if (IsOver(MyChessPiece.GetAllFlag(), NextX, NextY))
  104. new chessmusic("取胜.wav");
  105. display();
  106. }
  107. public void display() {// 用于重新显示游戏界面
  108. Mychesspanel.display(MyChessBoard, MyChessPiece);
  109. }
  110. // 添加棋子后只需判断水平、竖直、成45、135度角上是否连成5个
  111. public boolean IsOver(int[][] Array, int x, int y) {
  112. boolean flag = false;
  113. int num = 1;
  114. int k = 1;
  115. while (x - k >= 0 && Array[x][y] == Array[x - k][y]) {
  116. num++;
  117. k++;
  118. }
  119. chessimage.LineLeft = new chessOneStep(x - k + 1, y, 0);
  120. k = 1;
  121. while (x + k < 15 && Array[x][y] == Array[x + k][y]) {
  122. num++;
  123. k++;
  124. }
  125. chessimage.LineRight = new chessOneStep(x + k - 1, y, 0);
  126. if (num >= 5)
  127. flag = true;
  128. if (!flag) {
  129. num = 1;
  130. k = 1;
  131. while (y - k >= 0 && Array[x][y] == Array[x][y - k]) {
  132. num++;
  133. k++;
  134. }
  135. chessimage.LineLeft = new chessOneStep(x, y - k + 1, 0);
  136. k = 1;
  137. while (y + k < 15 && Array[x][y] == Array[x][y + k]) {
  138. num++;
  139. k++;
  140. }
  141. chessimage.LineRight = new chessOneStep(x, y + k - 1, 0);
  142. if (num >= 5)
  143. flag = true;
  144. }
  145. if (!flag) {
  146. num = 1;
  147. k = 1;
  148. while (y - k >= 0 && x - k >= 0
  149. && Array[x][y] == Array[x - k][y - k]) {
  150. num++;
  151. k++;
  152. }
  153. chessimage.LineLeft = new chessOneStep(x - k + 1, y - k + 1, 0);
  154. k = 1;
  155. while (y + k < 15 && x + k < 15
  156. && Array[x][y] == Array[x + k][y + k]) {
  157. num++;
  158. k++;
  159. }
  160. chessimage.LineRight = new chessOneStep(x + k - 1, y + k - 1, 0);
  161. if (num >= 5)
  162. flag = true;
  163. }
  164. if (!flag) {
  165. num = 1;
  166. k = 1;
  167. while (y + k < 15 && x - k >= 0
  168. && Array[x][y] == Array[x - k][y + k]) {
  169. num++;
  170. k++;
  171. }
  172. chessimage.LineLeft = new chessOneStep(x - k + 1, y + k - 1, 0);
  173. k = 1;
  174. while (y - k >= 0 && x + k < 15
  175. && Array[x][y] == Array[x + k][y - k]) {
  176. num++;
  177. k++;
  178. }
  179. chessimage.LineRight = new chessOneStep(x + k - 1, y - k + 1, 0);
  180. if (num >= 5)
  181. flag = true;
  182. }
  183. if (flag) {
  184. chessimage.IsGameOver = true;
  185. if (1 == Array[x][y])
  186. chessimage.Message = "获胜了,恭喜您!";
  187. else
  188. chessimage.Message = "失败了,振作点!";
  189. CanGo = false;
  190. }
  191. return flag;
  192. }
  193. public int GetMax(int a, int b) {
  194. return a < b ? b : a;
  195. }
  196. // 预先设定一些规则估值,对已连成一片的
  197. public int GetValue(int flag, int num) {
  198. int ret = 0;
  199. if (1 == num)
  200. ret = 0;
  201. if (2 == num) {
  202. if (0 == flag)// 死2
  203. ret = 3;
  204. else if (1 == flag)// 单活2
  205. ret = 50;
  206. else
  207. ret = 100;// 双活2
  208. } else if (3 == num) {
  209. if (0 == flag)// 死3
  210. ret = 5;
  211. else if (1 == flag)// 单活3
  212. ret = 200;
  213. else
  214. ret = 5000;// 双活3
  215. } else if (4 == num) {
  216. if (0 == flag)// 死4
  217. ret = 10;
  218. else if (1 == flag)// 单活4
  219. ret = 8000;
  220. else
  221. ret = 500000;
  222. } else if (5 == num) {
  223. ret = 10000000;
  224. }
  225. return ret;
  226. }
  227. // 对未连成一片但通过再下一颗子就能连成一片的局面进行估值
  228. public int GetPredictValue(int flag, int num) {
  229. int ret = 0;
  230. if (0 == flag || num <= 2)
  231. ret = 0;
  232. else {
  233. if (1 == flag) {
  234. if (3 == num)
  235. ret = 10;
  236. else if (4 == num)
  237. ret = 50;
  238. else
  239. ret = 200;
  240. } else {
  241. if (3 == num)
  242. ret = 100;
  243. else if (4 == num)
  244. ret = 5000;
  245. else
  246. ret = 8000;
  247. }
  248. }
  249. return ret;
  250. }
  251. // 以下棋点为中心,查看总得分,此评判方法为贪心法
  252. public int Evaluate(int[][] Array, int x, int y) {
  253. int ret = 0;
  254. int num, k, tag;
  255. boolean lflag, rflag;
  256. // 先估值一连成一片的
  257. // 水平线
  258. k = 1;
  259. num = 1;
  260. lflag = true;
  261. rflag = true;
  262. while (x - k >= 0 && Array[x][y] == Array[x - k][y]) {
  263. num++;
  264. k++;
  265. }
  266. if (!(x - k >= 0 && 0 == Array[x - k][y]))
  267. lflag = false;
  268. k = 1;
  269. while (x + k < 15 && Array[x][y] == Array[x + k][y]) {
  270. num++;
  271. k++;
  272. }
  273. if (!(x + k < 15 && 0 == Array[x + k][y]))
  274. rflag = false;
  275. num = (num < 5 ? num : 5);
  276. if (lflag && rflag) {
  277. tag = 2;
  278. } else {
  279. if (lflag || rflag)
  280. tag = 1;
  281. else
  282. tag = 0;
  283. }
  284. ret += GetValue(tag, num);
  285. // 竖直线
  286. k = 1;
  287. num = 1;
  288. lflag = true;
  289. rflag = true;
  290. while (y - k >= 0 && Array[x][y] == Array[x][y - k]) {
  291. num++;
  292. k++;
  293. }
  294. if (!(y - k >= 0 && 0 == Array[x][y - k]))
  295. lflag = false;
  296. k = 1;
  297. while (y + k < 15 && Array[x][y] == Array[x][y + k]) {
  298. num++;
  299. k++;
  300. }
  301. if (!(y + k < 15 && 0 == Array[x][y + k]))
  302. rflag = false;
  303. num = (num < 5 ? num : 5);
  304. if (lflag && rflag) {
  305. tag = 2;
  306. } else {
  307. if (lflag || rflag)
  308. tag = 1;
  309. else
  310. tag = 0;
  311. }
  312. ret += GetValue(tag, num);
  313. // 135度
  314. k = 1;
  315. num = 1;
  316. lflag = true;
  317. rflag = true;
  318. while (y - k >= 0 && x - k >= 0 && Array[x][y] == Array[x - k][y - k]) {
  319. num++;
  320. k++;
  321. }
  322. if (!(y - k >= 0 && x - k >= 0 && 0 == Array[x - k][y - k]))
  323. lflag = false;
  324. k = 1;
  325. while (y + k < 15 && x + k < 15 && Array[x][y] == Array[x + k][y + k]) {
  326. num++;
  327. k++;
  328. }
  329. if (!(y + k < 15 && x + k < 15 && 0 == Array[x + k][y + k]))
  330. rflag = false;
  331. num = (num < 5 ? num : 5);
  332. if (lflag && rflag) {
  333. tag = 2;
  334. } else {
  335. if (lflag || rflag)
  336. tag = 1;
  337. else
  338. tag = 0;
  339. }
  340. ret += GetValue(tag, num);
  341. // 45度
  342. k = 1;
  343. num = 1;
  344. lflag = true;
  345. rflag = true;
  346. while (y + k < 15 && x - k >= 0 && Array[x][y] == Array[x - k][y + k]) {
  347. num++;
  348. k++;
  349. }
  350. if (!(y + k < 15 && x - k >= 0 && 0 == Array[x - k][y + k]))
  351. lflag = false;
  352. k = 1;
  353. while (y - k >= 0 && x + k < 15 && Array[x][y] == Array[x + k][y - k]) {
  354. num++;
  355. k++;
  356. }
  357. if (!(y - k >= 0 && x + k < 15 && 0 == Array[x + k][y - k]))
  358. rflag = false;
  359. num = (num < 5 ? num : 5);
  360. if (lflag && rflag) {
  361. tag = 2;
  362. } else {
  363. if (lflag || rflag)
  364. tag = 1;
  365. else
  366. tag = 0;
  367. }
  368. ret += GetValue(tag, num);
  369. // 能成连成一片的
  370. // 水平线
  371. int add;
  372. int leftadd, rightadd;
  373. boolean leftflag, rightflag;
  374. int lvalue, rvalue;
  375. k = 1;
  376. num = 1;
  377. lflag = true;
  378. rflag = true;
  379. leftflag = true;
  380. rightflag = true;
  381. leftadd = 0;
  382. rightadd = 0;
  383. while (x - k >= 0 && Array[x][y] == Array[x - k][y]) {
  384. num++;
  385. k++;
  386. }
  387. if (!(x - k >= 0 && 0 == Array[x - k][y]))
  388. lflag = false;
  389. else {
  390. add = k + 1;// 跳过空格
  391. while (x - add >= 0 && Array[x][y] == Array[x - add][y]) {
  392. leftadd++;
  393. add++;
  394. }
  395. if (!(x - add >= 0 && 0 == Array[x - add][y]))// 堵死了
  396. leftflag = false;
  397. }
  398. k = 1;
  399. while (x + k < 15 && Array[x][y] == Array[x + k][y]) {
  400. num++;
  401. k++;
  402. }
  403. if (!(x + k < 15 && 0 == Array[x + k][y]))
  404. rflag = false;
  405. else {
  406. add = k + 1;// 跳过空格
  407. while (x + add < 15 && Array[x][y] == Array[x + add][y]) {
  408. rightadd++;
  409. add++;
  410. }
  411. if (!(x + add < 15 && 0 == Array[x + add][y]))// 堵死了
  412. rightflag = false;
  413. }
  414. if (leftflag && rflag) {
  415. tag = 2;
  416. } else {
  417. if (leftflag || rflag)
  418. tag = 1;
  419. else
  420. tag = 0;
  421. }
  422. lvalue = GetPredictValue(tag, num + 1 + leftadd);
  423. if (lflag && rightflag) {
  424. tag = 2;
  425. } else {
  426. if (lflag || rightflag)
  427. tag = 1;
  428. else
  429. tag = 0;
  430. }
  431. rvalue = GetPredictValue(tag, num + 1 + rightadd);
  432. ret += GetMax(lvalue, rvalue);
  433. // 竖直线
  434. k = 1;
  435. num = 1;
  436. lflag = true;
  437. rflag = true;
  438. leftflag = true;
  439. rightflag = true;
  440. leftadd = 0;
  441. rightadd = 0;
  442. while (y - k >= 0 && Array[x][y] == Array[x][y - k]) {
  443. num++;
  444. k++;
  445. }
  446. if (!(y - k >= 0 && 0 == Array[x][y - k]))
  447. lflag = false;
  448. else {
  449. add = k + 1;// 跳过空格
  450. while (y - add >= 0 && Array[x][y] == Array[x][y - add]) {
  451. leftadd++;
  452. add++;
  453. }
  454. if (!(y - add >= 0 && 0 == Array[x][y - add]))// 堵死了
  455. leftflag = false;
  456. }
  457. k = 1;
  458. while (y + k < 15 && Array[x][y] == Array[x][y + k]) {
  459. num++;
  460. k++;
  461. }
  462. if (!(y + k < 15 && 0 == Array[x][y + k]))
  463. rflag = false;
  464. else {
  465. add = k + 1;// 跳过空格
  466. while (y + add < 15 && Array[x][y] == Array[x][y + add]) {
  467. rightadd++;
  468. add++;
  469. }
  470. if (!(y + add < 15 && 0 == Array[x][y + add]))// 堵死了
  471. rightflag = false;
  472. }
  473. if (leftflag && rflag) {
  474. tag = 2;
  475. } else {
  476. if (leftflag || rflag)
  477. tag = 1;
  478. else
  479. tag = 0;
  480. }
  481. lvalue = GetPredictValue(tag, num + 1 + leftadd);
  482. if (lflag && rightflag) {
  483. tag = 2;
  484. } else {
  485. if (lflag || rightflag)
  486. tag = 1;
  487. else
  488. tag = 0;
  489. }
  490. rvalue = GetPredictValue(tag, num + 1 + rightadd);
  491. ret += GetMax(lvalue, rvalue);
  492. // 135度
  493. k = 1;
  494. num = 1;
  495. lflag = true;
  496. rflag = true;
  497. leftflag = true;
  498. rightflag = true;
  499. leftadd = 0;
  500. rightadd = 0;
  501. while (y - k >= 0 && x - k >= 0 && Array[x][y] == Array[x - k][y - k]) {
  502. num++;
  503. k++;
  504. }
  505. if (!(y - k >= 0 && x - k >= 0 && 0 == Array[x - k][y - k]))
  506. lflag = false;
  507. else {
  508. add = k + 1;// 跳过空格
  509. while (y - add >= 0 && x - add >= 0
  510. && Array[x][y] == Array[x - add][y - add]) {
  511. rightadd++;
  512. add++;
  513. }
  514. if (!(y - add >= 0 && x - add >= 0 && 0 == Array[x - add][y - add]))// 堵死了
  515. rightflag = false;
  516. }
  517. k = 1;
  518. while (y + k < 15 && x + k < 15 && Array[x][y] == Array[x + k][y + k]) {
  519. num++;
  520. k++;
  521. }
  522. if (!(y + k < 15 && x + k < 15 && 0 == Array[x + k][y + k]))
  523. rflag = false;
  524. else {
  525. add = k + 1;// 跳过空格
  526. while (y + add < 15 && x + add < 15
  527. && Array[x][y] == Array[x + add][y + add]) {
  528. rightadd++;
  529. add++;
  530. }
  531. if (!(y + add < 15 && x + add < 15 && 0 == Array[x + add][y + add]))// 堵死了
  532. rightflag = false;
  533. }
  534. if (leftflag && rflag) {
  535. tag = 2;
  536. } else {
  537. if (leftflag || rflag)
  538. tag = 1;
  539. else
  540. tag = 0;
  541. }
  542. lvalue = GetPredictValue(tag, num + 1 + leftadd);
  543. if (lflag && rightflag) {
  544. tag = 2;
  545. } else {
  546. if (lflag || rightflag)
  547. tag = 1;
  548. else
  549. tag = 0;
  550. }
  551. rvalue = GetPredictValue(tag, num + 1 + rightadd);
  552. ret += GetMax(lvalue, rvalue);
  553. k = 1;
  554. num = 1;
  555. leftflag = true;
  556. rightflag = true;
  557. leftadd = 0;
  558. rightadd = 0;
  559. while (y + k < 15 && x - k >= 0 && Array[x][y] == Array[x - k][y + k]) {
  560. num++;
  561. k++;
  562. }
  563. if (!(y + k < 15 && x - k >= 0 && 0 == Array[x - k][y + k]))
  564. lflag = false;
  565. else {
  566. add = k + 1;// 跳过空格
  567. while (y + add < 15 && x - add >= 0
  568. && Array[x][y] == Array[x - add][y + add]) {
  569. rightadd++;
  570. add++;
  571. }
  572. if (!(y + add < 15 && x - add >= 0 && 0 == Array[x - add][y + add]))// 堵死了
  573. rightflag = false;
  574. }
  575. k = 1;
  576. while (y - k >= 0 && x + k < 15 && Array[x][y] == Array[x + k][y - k]) {
  577. num++;
  578. k++;
  579. }
  580. if (!(y - k >= 0 && x + k < 15 && 0 == Array[x + k][y - k]))
  581. rflag = false;
  582. else {
  583. add = k + 1;// 跳过空格
  584. while (y - add >= 0 && x + add < 15
  585. && Array[x][y] == Array[x + add][y - add]) {
  586. rightadd++;
  587. add++;
  588. }
  589. if (!(y - add >= 0 && x + add < 15 && 0 == Array[x + add][y - add]))// 堵死了
  590. rightflag = false;
  591. }
  592. if (leftflag && rflag) {
  593. tag = 2;
  594. } else {
  595. if (leftflag || rflag)
  596. tag = 1;
  597. else
  598. tag = 0;
  599. }
  600. lvalue = GetPredictValue(tag, num + 1 + leftadd);
  601. if (lflag && rightflag) {
  602. tag = 2;
  603. } else {
  604. if (lflag || rightflag)
  605. tag = 1;
  606. else
  607. tag = 0;
  608. }
  609. rvalue = GetPredictValue(tag, num + 1 + rightadd);
  610. ret += GetMax(lvalue, rvalue);
  611. return ret;
  612. }
  613. // 计算机人工智能中直接搜索下棋,向前看LookLength步
  614. public void GetSearchNext(int LookLength) {
  615. if (!CanGo)
  616. return;
  617. chessOneStep Option = derectSearch(MyChessPiece.GetAllFlag(), true,
  618. LookLength);
  619. int NextX = Option.GetX();
  620. int NextY = Option.GetY();
  621. new chessmusic("下棋.wav");
  622. if (-1 == NextX && -1 == NextY) {
  623. if (chessimage.CurrentStep == 225) {
  624. chessimage.Message = "伯仲之间 ,胜负难分!";
  625. CanGo = false;
  626. }
  627. return;
  628. }
  629. chessimage.CurrentStep++;
  630. chessOneStep OneStep = new chessOneStep(NextX, NextY, 0);
  631. MyChessList.AddStep(OneStep);// 保留当前下得棋
  632. MyChessPiece.SetPositionFlag(NextX, NextY, 2);
  633. if (IsOver(MyChessPiece.GetAllFlag(), NextX, NextY))
  634. new chessmusic("取胜.wav");
  635. display();
  636. }
  637. // 直接暴搜
  638. public chessOneStep derectSearch(int[][] Array, boolean who, int deepth) {
  639. if (0 == deepth)// 返回当前局面的评估函数值
  640. {
  641. int MaxWei = -INF;
  642. int idX = -1, idY = -1;
  643. for (int i = 0; i < 15; i++) {
  644. for (int j = 0; j < 15; j++) {
  645. // 5000,8000
  646. if (0 == Array[i][j]) {
  647. Array[i][j] = 2;
  648. int tmp1 = Evaluate(Array, i, j);
  649. Array[i][j] = 1;
  650. int tmp2 = Evaluate(Array, i, j);
  651. if (tmp2 >= 10000000 && MaxWei < 10000000)// 机器未到死四且人到了活3
  652. {
  653. MaxWei = tmp2 + 10000000;
  654. idX = i;
  655. idY = j;
  656. } else if (tmp2 >= 500000 && MaxWei < 500000) {
  657. MaxWei = tmp2 + 500000;
  658. idX = i;
  659. idY = j;
  660. } else if (tmp2 >= 10000 && MaxWei < 10000) {
  661. MaxWei = tmp2 + 10000;
  662. idX = i;
  663. idY = j;
  664. }
  665. else if (tmp1 > tmp2 && tmp1 > MaxWei) {
  666. MaxWei = tmp1;
  667. idX = i;
  668. idY = j;
  669. } else if (tmp2 > tmp1 && tmp2 > MaxWei) {
  670. MaxWei = tmp2;
  671. idX = i;
  672. idY = j;
  673. }
  674. Array[i][j] = 0;
  675. }
  676. }
  677. }
  678. return new chessOneStep(idX, idY, MaxWei);
  679. }
  680. chessOneStep ret = new chessOneStep(-1, -1, -INF);
  681. for (int i = 0; i < 15; i++) {
  682. for (int j = 0; j < 15; j++) {
  683. if (0 == Array[i][j]) {
  684. if (who)
  685. Array[i][j] = 2;
  686. else
  687. Array[i][j] = 1;
  688. chessOneStep tmp = derectSearch(Array, !who, deepth - 1);
  689. Array[i][j] = 0;
  690. if (tmp.GetWeight() > ret.GetWeight())
  691. ret = tmp;
  692. }
  693. }
  694. }
  695. return ret;
  696. }
  697. // 计算机人工智能中极大极小法搜索下棋,向前看LookLength步
  698. public void GetMinMaxsearchNext(int LookLength) {
  699. chessOneStep Option = MinMaxsearch(MyChessPiece.GetAllFlag(), true,
  700. LookLength);
  701. int NextX = Option.GetX();
  702. int NextY = Option.GetY();
  703. new chessmusic("下棋.wav");
  704. if (-1 == NextX && -1 == NextY) {
  705. if (chessimage.CurrentStep == 225) {
  706. chessimage.Message = "伯仲之间 ,胜负难分!";
  707. CanGo = false;
  708. }
  709. return;
  710. }
  711. chessimage.CurrentStep++;
  712. chessOneStep OneStep = new chessOneStep(NextX, NextY, 0);
  713. MyChessList.AddStep(OneStep);// 保留当前下得棋
  714. MyChessPiece.SetPositionFlag(NextX, NextY, 2);
  715. if (IsOver(MyChessPiece.GetAllFlag(), NextX, NextY))
  716. new chessmusic("取胜.wav");
  717. display();
  718. }
  719. // 极大极小博弈搜索
  720. public chessOneStep MinMaxsearch(int[][] Array, boolean who, int deepth) {
  721. if (0 == deepth)// 返回当前局面的评估函数值
  722. {
  723. int MaxWei = -INF;
  724. int idX = -1, idY = -1;
  725. for (int i = 0; i < 15; i++) {
  726. for (int j = 0; j < 15; j++) {
  727. if (0 == Array[i][j]) {
  728. Array[i][j] = 2;
  729. int tmp = Evaluate(Array, i, j);
  730. if (tmp >= MaxWei) {
  731. MaxWei = tmp;
  732. idX = i;
  733. idY = j;
  734. }
  735. Array[i][j] = 1;
  736. tmp = Evaluate(Array, i, j);
  737. if (tmp > MaxWei) {
  738. MaxWei = tmp;
  739. idX = i;
  740. idY = j;
  741. }
  742. Array[i][j] = 0;
  743. }
  744. }
  745. }
  746. return new chessOneStep(idX, idY, MaxWei);
  747. }
  748. if (who)// 轮到己方,取极大值
  749. {
  750. chessOneStep ret = new chessOneStep(-1, -1, -INF);
  751. ArrayList<chessOneStep> TmpList = new ArrayList<chessOneStep>();
  752. for (int i = 0; i < 15; i++) {
  753. for (int j = 0; j < 15; j++) {
  754. if (0 == Array[i][j]) {
  755. Array[i][j] = 2;
  756. TmpList.add(new chessOneStep(i, j,
  757. Evaluate(Array, i, j)));
  758. Array[i][j] = 0;
  759. }
  760. }
  761. }
  762. Collections.sort(TmpList, new MyCompare());
  763. int num = TmpList.size() < 5 ? TmpList.size() : 5;
  764. for (int i = 0; i < num; i++) {
  765. chessOneStep t = TmpList.get(i);
  766. Array[t.GetX()][t.GetY()] = 2;
  767. chessOneStep tmp = MinMaxsearch(Array, !who, deepth - 1);
  768. if (tmp.GetWeight() > ret.GetWeight())
  769. ret = tmp;
  770. Array[t.GetX()][t.GetY()] = 0;
  771. }
  772. return ret;
  773. } else // 轮到对手,取极小值
  774. {
  775. chessOneStep ret = new chessOneStep(-1, -1, INF);
  776. ArrayList<chessOneStep> TmpList = new ArrayList<chessOneStep>();
  777. for (int i = 0; i < 15; i++) {
  778. for (int j = 0; j < 15; j++) {
  779. if (0 == Array[i][j]) {
  780. Array[i][j] = 1;
  781. TmpList.add(new chessOneStep(i, j,
  782. Evaluate(Array, i, j)));
  783. Array[i][j] = 0;
  784. }
  785. }
  786. }
  787. Collections.sort(TmpList, new MyCompare());
  788. int num = TmpList.size() < 5 ? TmpList.size() : 5;
  789. for (int i = 0; i < num; i++) {
  790. chessOneStep t = TmpList.get(i);
  791. Array[t.GetX()][t.GetY()] = 1;
  792. chessOneStep tmp = MinMaxsearch(Array, !who, deepth - 1);
  793. if (tmp.GetWeight() < ret.GetWeight())
  794. ret = tmp;
  795. Array[t.GetX()][t.GetY()] = 0;
  796. }
  797. return ret;
  798. }
  799. }
  800. }

5.3 源程序3 完成面板的绘制

  1. import java.awt.BasicStroke;
  2. import java.awt.Color;
  3. import java.awt.Font;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.Stroke;
  7. import java.awt.geom.Line2D;
  8. import java.util.Date;
  9. import javax.swing.JPanel;
  10. public class chesspanel extends JPanel {
  11. private static final long serialVersionUID = 1L;
  12. private chessboard MyChessBoard = new chessboard();
  13. private chesspiece MyChessPiece = new chesspiece();
  14. public chesspanel(chessboard MyChessBoard1, chesspiece MyChessPiece1) {
  15. MyChessBoard = MyChessBoard1;
  16. MyChessPiece = MyChessPiece1;
  17. }
  18. //自定义显示回调函数
  19. public void display(chessboard MyChessBoard1, chesspiece MyChessPiece1) {
  20. MyChessBoard = MyChessBoard1;
  21. MyChessPiece = MyChessPiece1;
  22. this.repaint();
  23. }
  24. //Java库刷新函数
  25. public void paintComponent(Graphics g) {// paint(Graphics g)//
  26. // {//此时遇到的问题是只有鼠标经过是才显示button
  27. super.paintComponent(g);
  28. setBackground(new Color(
  29. chessimage.ColorOfBackGround[chessimage.WitchMatch][0],
  30. chessimage.ColorOfBackGround[chessimage.WitchMatch][1],
  31. chessimage.ColorOfBackGround[chessimage.WitchMatch][2]));// 设置背景色
  32. //画棋盘、棋子
  33. if (MyChessBoard != null && MyChessPiece != null) {
  34. MyChessBoard.DrawChessBoard(g);// 绘制棋盘背景
  35. MyChessPiece.DrawChessPiece(g);// 绘制盘面棋子
  36. }
  37. // 绘制两个玩家
  38. g.drawImage(chessimage.LeftPlayer, 25, 25, this);
  39. g.drawImage(chessimage.RightPlayer, (int) (chessboard.scrSize.width
  40. - chessboard.Left + 50), 25, this);
  41. //画棋盘
  42. g.drawImage(chessimage.whiteBoard, 25, 25, this);
  43. g.drawImage(chessimage.blackBoard, (int) (chessboard.scrSize.width
  44. - chessboard.Left + 250), 25, this);
  45. //显示文字提示信息
  46. if (chessimage.Message != "") {
  47. if (chessimage.IsGameOver) {
  48. Graphics2D g2d = (Graphics2D) g;
  49. Stroke stroke = g2d.getStroke();
  50. g2d.setStroke(new BasicStroke(10, BasicStroke.CAP_SQUARE,
  51. BasicStroke.JOIN_ROUND));
  52. g2d.setColor(Color.pink);
  53. g2d.draw(new Line2D.Float(
  54. (float) (chessboard.Left + chessboard.Inc
  55. * chessimage.LineLeft.GetX()),
  56. (float) (25 + chessboard.Inc
  57. * chessimage.LineLeft.GetY()),
  58. (float) (chessboard.Left + chessboard.Inc
  59. * chessimage.LineRight.GetX()),
  60. (float) (25 + chessboard.Inc
  61. * chessimage.LineRight.GetY())));// 五子连线
  62. g2d.setStroke(stroke);
  63. }
  64. g.setColor(Color.red);
  65. g.setFont(new Font("楷体", Font.BOLD, 86));
  66. g.drawString(chessimage.Message, (int) (chessboard.Left - 50),
  67. (int) (chessboard.Low - 7 * chessboard.Inc));
  68. }
  69. // 设置游戏时间
  70. g.setColor(Color.blue);
  71. g.fillRect((int) (chessboard.Left + 260), 0, 140, 20);// 左边人的下角
  72. g.setColor(Color.yellow);
  73. g.setFont(new Font("楷体", Font.BOLD, 20));
  74. chessimage.cur = new Date();
  75. Long m = chessimage.cur.getTime() - chessimage.begin.getTime();
  76. Long H = m / (60 * 60 * 1000);
  77. m = m % (60 * 60 * 1000);
  78. Long M = m / (60 * 1000);
  79. m = m % (60 * 1000);
  80. m = m / 1000;
  81. String dif = "时间" + H + ":" + M + ":" + m;
  82. g.drawString(dif, (int) (chessboard.Left + 280), 20);
  83. }
  84. }

六、运行结果截图

游戏界面1(背景乐《高山流水》)

游戏界面2(背景乐《赛马》)

游戏界面3(背景乐《笑傲江湖琴箫合奏》)

用户一方取胜界面

计算机一方取胜界面

七、课程总结

总的感觉,跟着老师学习Java很容易上手。Java与C、C++是当今最为流行的语言,而Java是其中产生最晚、最充满活力的编程语言。这也就促使我对学习Java有着浓厚的兴趣。

学习Java与学习其他语言一样,首先是掌握基本语法与控制结构。Java是C语言的升华版,没有指针、没有运算符重载、没有头文件等,而且在编程过程中不用用户手动管理内存,Java提供垃圾回收站自动收回运行过程中释放过的内存空间。Java不提供boolean型与整型数据的自动转换,如if(int)会报语法错,这在C系语言中是完全可以的。而且不同数据类型的转换都要强制进行,如Int x=1.0会报语法错误,必须强制类型转换。这样的风格使Java编程简单、规范。

Java是一种面向对象的编程语言。对象定义为相关数据与方法的集合。Java编程实现可视化比其他语言方便易懂,只需new 实例化该类型对象,然后添加自定义的界面。Java不支持多继承,多继承会使程序结构较为复杂,Java中单继承与接口的引入可以实现多继承的功能。接口是一些共用的、抽象的数据与方法,但实现接口时必须实现接口中所有的抽象方法;为了避免这种问题,可以引入事件适配器类,这是一种抽象类,但在继承他们创建新类时,可以不现实所有的方法,只需实现需要实现的方法即可。

Java提供功能齐全的异常类,处理程序运行时的出错问题。Try块监视程序段,catch进行异常处理。用户可以自定义异常类及异常处理机制。

此外,Java在web网页应用方面有着很重要的应用。由于目前我没有进行网络方面的编程,所以在此就不展开。

此外,我觉得老师的授课方式很好:在结课后要求每人上交自己的程序,这就能充分调动学生的编程积极性。之前我就对游戏有着浓厚的兴趣,尤其是棋类游戏,在老师布置任务后,我选择了五子棋来挑战自己。虽说之前做ACM,对算法比较敏感,但做游戏需要可视化,而且是自己不熟悉的Java语言,这是一个很大的挑战。但我眼中的IT从业人员应该是勇于挑战、善于学习、迎难而上的,于是我毅然选择了五子棋,最终达到了自己想要的效果。

上传的附件 cloud_download Java智能人机博弈五子棋游戏-程序和报告.7z ( 29.69mb, 4次下载 )
error_outline 下载需要8点积分

keyboard_arrow_left上一篇 : 基于汇编语言的音乐盒设计与实现 基于WIN32 API界面编程实现的华容道小游戏 : 下一篇keyboard_arrow_right



Gameisover
2018-10-06 22:19:48
使用了贪心算法、直接搜索算法、极大极小博弈树算法,分别对应游戏容易、中等、困难三种难度

发送私信

修行的路总是孤独的,因为智慧必然来自孤独

8
文章数
10
评论数
最近文章
eject