JAVA 五子棋实现问题(辛苦)

JAVA 五子棋实现问题(辛苦),第1张

我这儿有个一份大学写的算法,留下邮箱我发给你。太久了,你自己看吧。

具体算法也是网上找的:

五子棋是一种受大众广泛喜爱的游戏,其规则简单,变化多端,非常富有趣味性和消遣性。这里设计和实现了一个人机对下的五子棋程序,采用了博弈树的方法,应用了剪枝和最大最小树原理进行搜索发现最好的下子位置。介绍五子棋程序的数据结构、评分规则、胜负判断方法和搜索算法过程。

一、相关的数据结构

关于盘面情况的表示,以链表形式表示当前盘面的情况,目的是可以允许用户进行悔棋、回退等 *** 作。

CList StepList;

其中Step结构的表示为:

struct Step

{

int m; //m,n表示两个坐标值

int n;

char side; //side表示下子方

};

数组形式保存当前盘面的情况,

目的是为了在显示当前盘面情况时使用:

char FiveArea[FIVE_MAX_LINE][FIVE_MAX_LINE];

其中FIVE_MAX_LINE表示盘面最大的行数。

同时由于需要在递归搜索的过程中考虑时间和空间有效性,只找出就当前情况来说相对比较好的几个盘面,而不是对所有的可下子的位置都进行搜索,这里用变量CountList来表示当前搜索中可以选择的所有新的盘面情况对象的集合:

CList CountList;

其中类CBoardSituiton为:

class CBoardSituation

{

CList StepList; //每一步的列表

char FiveArea[FIVE_MAX_LINE][FIVE_MAX_LINE];

struct Step machineStep; //机器所下的那一步

double value; //该种盘面状态所得到的分数

}

二、评分规则

对于下子的重要性评分,需要从六个位置来考虑当前棋局的情况,分别为:-,¦,/,\,//,\\

实际上需要考虑在这六个位置上某一方所形成的子的布局的情况,对于在还没有子的地方落子以后的当前局面的评分,主要是为了说明在这个地方下子的重要性程度,设定了一个简单的规则来表示当前棋面对机器方的分数。

基本的规则如下:

判断是否能成5, 如果是机器方的话给予100000分,如果是人方的话给予-100000 分;

判断是否能成活4或者是双死4或者是死4活3,如果是机器方的话给予10000分,如果是人方的话给予-10000分;

判断是否已成双活3,如果是机器方的话给予5000分,如果是人方的话给予-5000 分;

判断是否成死3活3,如果是机器方的话给予1000分,如果是人方的话给予-1000 分;

判断是否能成死4,如果是机器方的话给予500分,如果是人方的话给予-500分;

判断是否能成单活3,如果是机器方的话给予200分,如果是人方的话给予-200分;

判断是否已成双活2,如果是机器方的话给予100分,如果是人方的话给予-100分;

判断是否能成死3,如果是机器方的话给予50分,如果是人方的话给予-50分;

判断是否能成双活2,如果是机器方的话给予10分,如果是人方的话给予-10分;

判断是否能成活2,如果是机器方的话给予5分,如果是人方的话给予-5分;

判断是否能成死2,如果是机器方的话给予3分,如果是人方的话给予-3分。

实际上对当前的局面按照上面的规则的顺序进行比较,如果满足某一条规则的话,就给该局面打分并保存,然后退出规则的匹配。注意这里的规则是根据一般的下棋规律的一个总结,在实际运行的时候,用户可以添加规则和对评分机制加以修正。

三、胜负判断

实际上,是根据当前最后一个落子的情况来判断胜负的。实际上需要从四个位置判断,以该子为出发点的水平,竖直和两条分别为 45度角和135度角的线,目的是看在这四个方向是否最后落子的一方构成连续五个的棋子,如果是的话,就表示该盘棋局已经分出胜负。具体见下面的图示:

四、搜索算法实现描述

注意下面的核心的算法中的变量currentBoardSituation,表示当前机器最新的盘面情况, CountList表示第一层子节点可以选择的较好的盘面的集合。核心的算法如下:

void MainDealFunction()

{

value=-MAXINT; //对初始根节点的value赋值

CalSeveralGoodPlace(currentBoardSituation,CountList);

//该函数是根据当前的盘面情况来比较得到比较好的可以考虑的几个盘面的情况,可以根据实际的得分情况选取分数比较高的几个盘面,也就是说在第一层节点选择的时候采用贪婪算法,直接找出相对分数比较高的几个形成第一层节点,目的是为了提高搜索速度和防止堆栈溢出。

pos=CountListGetHeadPosition();

CBoardSituation* pBoard;

for(i=0;ivalue=Search(pBoard,min,value,0);

Value=Select(value,pBoard->value,max);

//取value和pBoard->value中大的赋给根节点

}

for(i=0;ivalue)

//找出那一个得到最高分的盘面

{

currentBoardSituation=pBoard;

PlayerMode=min; //当前下子方改为人

Break;

}

}

其中对于Search函数的表示如下:实际上核心的算法是一个剪枝过程,其中在这个搜索过程中相关的四个参数为:(1)当前棋局情况;(2)当前的下子方,可以是机器(max)或者是人(min);(3)父节点的值oldValue;(4)当前的搜索深度depth。

double Search(CBoardSituation&

board,int mode,double oldvalue,int depth)

{

CList m_DeepList;

if(deptholdvalue))== TRUE)

{

if(mode==max)

value=select(value,search(successor

Board,min,value,depth+1),max);

else

value=select(value,search(successor

Board,max,value,depth+1),min);

}

return value;

}

else

{

if ( goal(board)<>0)

//这里goal(board)<>0表示已经可以分出胜负

return goal(board);

else

return evlation(board);

}

}

注意这里的goal(board)函数是用来判断当前盘面是否可以分出胜负,而evlation(board)是对当前的盘面从机器的角度进行打分。

下面是Select函数的介绍,这个函数的主要目的是根据 PlayerMode情况,即是机器还是用户来返回节点的应有的值。

double Select(double a,double b,int mode)

{

if(a>b && mode==max)¦¦ (a< b && mode==min)

return a;

else

return b;

}

五、小结

在Windows *** 作系统下,用VC++实现了这个人机对战的五子棋程序。和国内许多只是采用规则或者只是采用简单递归而没有剪枝的那些程序相比,在智力上和时间有效性上都要好于这些程序。同时所讨论的方法和设计过程为用户设计其他的游戏(如象棋和围棋等)提供了一个参考。

发给你了。

一、实验题目

五子棋游戏。

二、问题分析

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

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

三、功能设计

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

1等级设置

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

2悔棋功能

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

3棋面绘制

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

4引入

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

5背景设置

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

6音乐播放

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

7时间显示

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

8其他小功能

支持和棋、认输、开启新游戏、退出游戏等 *** 作。

四、数据结构与算法设计

数据结构部分

1当前棋局的存储结构

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

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

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

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

2实现悔棋 *** 作的数据结构

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

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

算法设计部分

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

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

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

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

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

4chessBoard类:棋盘绘制类,负责棋盘的绘制;

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

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

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

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

9chessList类:栈类,完成悔棋等 *** 作;

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

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

详细设计

1mainFrame类

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

2chessMusic类

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

3chessImage类

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

public static Date begin;//每局开始时间

public static Date cur;//每局结束时间

public static chessOneStep LineLeft;//结束端点1

public static chessOneStep LineRight;//结束端点2

public static boolean IsGameOver;//是否只有一方获胜

public static int ColorOfBackGround[][]= {{255, 227, 132},{0,255,127},{218,165,32}};//背景颜色

public static int ColorOfWindows[][]= {{ 60,179,113},{245,245,245},{122,122,122}};//背景颜色

public static int WitchMatch;//背景搭配

public static String MusicOfBackGround;//背景音乐

public static int CurrentStep;//记录当前步数

public static int Rank;//设置难度等级

public static boolean IsSurrender;//判断是否认输

public static boolean IsTie;//判断是否认输

public static String Message;//输出提示信息

public static Image IconImage;// 图标

public static Image blackBoard;//白棋盘

public static Image whiteBoard;//黑棋盘

public static Image blackChess;// 白棋棋子

public static Image whiteChess;// 白棋棋子

public static Image RightPlayer;//白棋棋罐

public static Image LeftPlayer;//白棋玩家头像

public static String path = "src/";// 的保存路径

4chessButton类

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

(1)、按钮组件

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

(2)、单选按钮组件

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

(3)、文本框

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

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

5chessPiece类

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

(1)、SetPositionFlag(int x, int y, int flag)//设置(x,y)处的状态为flag

(2)、GetPositionFlag(int x, int y)//获取(x,y)处的状态

(3)、SetAllFlag(int [][]NewFlag)//设置当前整个棋面的状态为NewFlag

(4)、GetAllFlag()//获取当前整个棋面的状态

(5)、DrawChessPiece(Graphics g)//绘制当前局面的棋子

由于本类比较重要,所以附上了代码,见源代码1。

6chessBoard类

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

方法一:

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

方法二:

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

7chessOneStep类

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

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

主要方法如下:

(1)、GetX()//获得当前对象的x坐标

(2)、GetY()//获得当前对象的y坐标

(3)、GetWeight()//获得当前对象的(x,y)处的估价值

8chessList类

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

(1)、AddStep(chessOneStep OneStep)//添加一步棋到List中

(2)、GetSize()//获得当前List的大小

(3)、ClearList()//清空List

(4)、RemoveLast()//删去List中的最后元素

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

9myCompare类

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

10chessPanel类

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

private chessboard MyChessBoard;//当前显示棋盘

private chesspiece MyChessPiece;//当前显示整个棋面的状态

主要方法如下:

(1)、chesspanel(chessboard MyChessBoard1, chesspiece MyChessPiece1)//构造函数,分别用MyChessBoard1和MyChessPiece1初始化MyChessBoard和MyChessPiece

(2)display(chessboard MyChessBoard1, chesspiece MyChessPiece1)//自定义显示回调函数,调用repaint()完成重新绘制游戏界面

(3)、paintComponent(Graphics g)//核心方法,调用各种函数完成具体的绘制工作

11chessPositon类

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

private static chessboard MyChessBoard;//当前显示棋盘

public static chesspiece MyChessPiece;//当前显示整个棋面的状态

private static chesspanel Mychesspanel;////当前显示面板

public static chesslist MyChessList=new chesslist();//当前下棋集合,用于悔棋

final private static int INF = (1 << 30); // 表示正无穷大的常量,用于极大极小博弈数搜索算法

public static boolean CanGo;//控制当前下棋一方

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

(1)、chessposition(chesspanel , chessboard ,chesspiece ) //带有参数的构造函数

(2)、chessposition()

不带参数的构造函数

(3)、mouseClicked(MouseEvent event)

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

(4)、IsOver(int[][] Array,int x,int y)

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

(5)display()

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

(6)、GetValue(int flag, int num)

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

(7)、GetPredictValue(int flag, int num)

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

(8)、Evaluate(int[][] Array, int x, int y)

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

(9)、GetGreedNext()

计算机下棋方法1,对应难度等级为简单,采用贪心思想。每次下棋前在求得最有利点下棋,而是否最有利只是通过一步评估。算法伪码描述为:

Max取负无穷大

for(行i从0到15)

{

For(列j从0到15)

{

If((i,j)对应的位置无棋)

{

a假设放上一颗由人控制的棋,求估价值;

b假设放上一颗由计算机控制的棋,求估价值;

c取二者中较大值作为(i,j)处的估价值tmp;

d取tmp与Max较大值赋值给Max

}

}

}

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

(10)、GetSearchNext(int LookLength)

derectSearch(int [][]Array,boolean who,int deepth)

计算机下棋方法2:直接搜索法,对应难度等级为中等。

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

a采用深度优先搜索需要递归,递归中状态过多可能会爆栈,我们知道递归是用栈机制来实现的;采用宽度优先搜索又需要存储为扩展的节点,这对内存容量要求很高。

b不管深搜还是广搜,在时间复杂度为O(N^m)的情况下都是不能接受的。其中N为当前棋局的待扩展节点,最大225;m为搜索的深度。

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

直接搜索算法伪代码为

GetSearch(boolean flag,int deep)

{

如果deep等于0,返回当前棋局估值;

for(行i从0到15)

{

For(列j从0到15)

{

If((i,j)对应的位置无棋)

{

如果轮到计算机下棋,置标志位为2

GetSearch(!flag,deep-1);

如果轮到人下棋,置标志位为1;

GetSearch(!flag,deep-1);

}

}

}

}

(11)、GetMinMaxsearchNext(int LookLength)

MinMaxsearch(int [][]Array,boolean who, int deepth)

计算机下棋算法3:极大极小博弈树法,对应难度等级为困难。五子棋是个博弈游戏,当前在寻找对自己最有利的下棋点时要尽可能保证对对手最不利,这种思想可以用极大极小博弈树

import javaawtButton;

import javaawtColor;

import javaawtGraphics;

import javaawteventActionEvent;

import javaawteventActionListener;

import javaawteventKeyAdapter;

import javaawteventKeyEvent;

import javaawteventMouseEvent;

import javaawteventMouseListener;

import javautilArrayList;

import javautilRandom;

import javaxswingBoxLayout;

import javaxswingJFrame;

import javaxswingJOptionPane;

import javaxswingJPanel;

import javaxswingTimer;

public class ChessGame extends JFrame implements MouseListener{

private int blackcount=0;

private int whitecount=0;

private boolean flag=true;

private boolean whitewin;

private boolean blackwin;

private boolean flash=false;

public Timer time;

private ArrayList<mypoint>blacklist;

private ArrayList<mypoint>whitelist;

public ChessGame(){

setSize(450,500);

setDefaultCloseOperation(JFrameEXIT_ON_CLOSE);

blacklist=new ArrayList<mypoint>();

whitelist=new ArrayList<mypoint>();

thisaddMouseListener(this);

setVisible(true);

}

@Override

public void paint(Graphics g) {

if(flash==true){

superpaint(g);

}

flash=false;

gsetColor(ColorWHITE);

gfillRect(0, 0, 420, 420);

gsetColor(ColorDARK_GRAY);

gdrawLine(5, 5, 420, 5);

gdrawLine(5, 5, 5, 420);

gdrawLine(420, 5, 420, 420);

gdrawLine(5, 420, 420, 420);

for(int i=1;i<=21;i++){

gdrawLine(5, 20i, 420, 20i);

gdrawLine(20i, 5, 20i, 420);

}

gsetColor(ColorBLACK);

for(int i=0;i<blacklistsize();i++){

gfillOval(blacklistget(i)getX()20+12,blacklistget(i)getY()20+32, 16, 16);

}

gsetColor(ColorRED);

for(int i=0;i<whitelistsize();i++){

gfillOval(whitelistget(i)getX()20+12,whitelistget(i)getY()20+32, 16, 16);

}

thischeckwin(blacklist, 0, 0, 0,0);

thischeckwin(whitelist, 0, 0, 0,1);

gsetColor(Colorblack);

String str1 = "黑方胜利局数为:" + blackcount;

gdrawString(str1, 10, 450);

String str2 = "红方胜利局数为:" + whitecount;

gdrawString(str2, 150, 450);

}

public static void main(String[] args){

ChessGame snake=new ChessGame();

}

public boolean checkExist(mypoint a){

for(int i=0;i<whitelistsize();i++){

if(whitelistget(i)getX()==agetX()&&whitelistget(i)getY()==agetY()){

return true;

}

}

for(int i=0;i<blacklistsize();i++){

if(blacklistget(i)getX()==agetX()&&blacklistget(i)getY()==agetY()){

return true;

}

}

return false;

}

@Override

public void mouseClicked(MouseEvent e) {

// TODO Auto-generated method stub

double x=egetX();

double y=egetY();

if(x>=5&&x<=410&&y>=5&&y<=410){

double k=20;

x=x/k-1;

y=y/k-2;

int ax=(int) Mathround(x);

int ay=(int) Mathround(y);

if(checkExist(new mypoint(ax,ay))){

JOptionPaneshowMessageDialog(null, "该位置已有棋子!");

repaint();

}else{

if(flag==true){

blacklistadd(new mypoint(ax,ay));

}

if(flag==false){

whitelistadd(new mypoint(ax,ay));

}

flag=!flag;

repaint();

}

}

}

public void checkwin(ArrayList<mypoint> list,int pos,int direct,int count,int type){

if(count==0){

for(int i=0;i<listsize();i++){

for(int j=0;j<listsize();j++){

if(listget(i)getY()==listget(j)getY()&&(listget(j)getX()-listget(i)getX()==-1)){

direct=2;

count++;

checkwin(list,j,2,count,type);

count=0;

}

if(listget(i)getX()==listget(j)getX()&&(listget(j)getY()-listget(i)getY()==-1)){

direct=0;

count++;

checkwin(list,j,0,count,type);

count=0;

}

if(listget(j)getY()==listget(i)getY()-1&&(listget(j)getX()-listget(i)getX()==1)){

direct=4;

count++;

checkwin(list,j,4,count,type);

count=0;

}

if(listget(j)getY()==listget(i)getY()+1&&(listget(j)getX()-listget(i)getX()==1)){

direct=5;

count++;

checkwin(list,j,5,count,type);

count=0;

}

}

}

}

else{

if(direct==4){

for(int i=0;i<listsize();i++){

if(i!=pos){

if(listget(i)getX()==listget(pos)getX()+1&&(listget(i)getY()-listget(pos)getY()==-1)){

count=count+1;

if(count==4){

if(type==0){

Systemoutprintln("blackwin!");

blackcount++;

JOptionPaneshowMessageDialog(null, "黑方赢了!");

restart();

}if(type==1){

Systemoutprintln("whitewin!");

whitecount++;

JOptionPaneshowMessageDialog(null, "红方赢了!");

restart();

}

}else{

checkwin(list,i,direct,count,type);

}

}

}

}

}

if(direct==5){

for(int i=0;i<listsize();i++){

if(i!=pos){

if(listget(i)getX()==listget(pos)getX()+1&&(listget(i)getY()-listget(pos)getY()==1)){

count=count+1;

if(count==4){

if(type==0){

Systemoutprintln("blackwin!");

blackcount++;

JOptionPaneshowMessageDialog(null, "黑方赢了!");

restart();

}if(type==1){

Systemoutprintln("whitewin!");

whitecount++;

JOptionPaneshowMessageDialog(null, "红方赢了!");

restart();

}

}else{

checkwin(list,i,direct,count,type);

}

}

}

}

}

if(direct==0){

for(int i=0;i<listsize();i++){

if(i!=pos){

if(listget(pos)getX()==listget(i)getX()&&(listget(i)getY()-listget(pos)getY()==-1)){

count=count+1;

if(count==4){

if(type==0){

Systemoutprintln("blackwin!");

blackcount++;

JOptionPaneshowMessageDialog(null, "黑方赢了!");

restart();

}if(type==1){

Systemoutprintln("whitewin!");

whitecount++;

JOptionPaneshowMessageDialog(null, "红方赢了!");

restart();

}

}else{

checkwin(list,i,direct,count,type);

}

}

}

}

}

if(direct==2){

for(int i=0;i<listsize();i++){

if(i!=pos){

if(listget(pos)getY()==listget(i)getY()&&(listget(i)getX()-listget(pos)getX()==-1)){

count=count+1;

if(count==4){

if(type==0){

Systemoutprintln("blackwin!");

blackcount++;

JOptionPaneshowMessageDialog(null, "黑方赢了!");

restart();

}if(type==1){

Systemoutprintln("whitewin!");

whitecount++;

JOptionPaneshowMessageDialog(null, "红方赢了!");

restart();

}

}else{

checkwin(list,i,direct,count,type);

}

}

}

}

}

}

}

public void restart(){

whitelist=new ArrayList<mypoint>();

blacklist=new ArrayList<mypoint>();

flash=true;

repaint();

}

@Override

public void mousePressed(MouseEvent e) {

// TODO Auto-generated method stub

}

@Override

public void mouseReleased(MouseEvent e) {

// TODO Auto-generated method stub

}

@Override

public void mouseEntered(MouseEvent e) {

// TODO Auto-generated method stub

}

@Override

public void mouseExited(MouseEvent e) {

// TODO Auto-generated method stub

}

}

class mypoint{

int x;

int y;

public mypoint(int a,int b){

thisx=a;

thisy=b;

}

public int getX(){

return thisx;

}

public int getY(){

return thisy;

}

}

很sb的电脑五子棋:

import javaio;

import javautil;

public class Gobang {

// 定义一个二维数组来充当棋盘

private String[][] board;

// 定义棋盘的大小

private static int BOARD_SIZE = 15;

public void initBoard() {

// 初始化棋盘数组

board = new String[BOARD_SIZE][BOARD_SIZE];

// 把每个元素赋为"╋",用于在控制台画出棋盘

for (int i = 0; i < BOARD_SIZE; i++) {

for (int j = 0; j < BOARD_SIZE; j++) {

// windows是一行一行来打印的。坐标值为(行值, 列值)

board[i][j] = "╋";

}

}

}

// 在控制台输出棋盘的方法

public void printBoard() {

// 打印每个数组元素

for (int i = 0; i < BOARD_SIZE; i++) {

for (int j = 0; j < BOARD_SIZE; j++) {

// 打印数组元素后不换行

Systemoutprint(board[i][j]);

}

// 每打印完一行数组元素后输出一个换行符

Systemoutprint("\n");

}

}

// 该方法处理电脑下棋:随机生成2个整数,作为电脑下棋的坐标,赋给board数组。

private void compPlay() {

// 构造一个随机数生成器

Random rnd = new Random();

// Random类的nextInt(int n))方法:随机地生成并返回指定范围中的一个 int 值,

// 即:在此随机数生成器序列中 0(包括)和 n(不包括)之间均匀分布的一个int值。

int compXPos = rndnextInt(15);

int compYPos = rndnextInt(15);

// 保证电脑下的棋的坐标上不能已经有棋子(通过判断对应数组元素只能是"╋"来确定)

while (board[compXPos][compYPos]equals("╋") == false) {

compXPos = rndnextInt(15);

compYPos = rndnextInt(15);

}

Systemoutprintln(compXPos);

Systemoutprintln(compYPos);

// 把对应的数组元素赋为"○"。

board[compXPos][compYPos] = "○";

}

// 该方法用于判断胜负:进行四次循环扫描,判断横、竖、左斜、右斜是否有5个棋连在一起

private boolean judgeWin() {

// flag表示是否可以断定赢/输

boolean flag = false;

// joinEle:将每一个横/竖/左斜/右斜行中的元素连接起来得到的一个字符串

String joinEle;

// 进行横行扫描

for (int i = 0; i < BOARD_SIZE; i++) {

// 每扫描一行前,将joinEle清空

joinEle = "";

for (int j = 0; j < BOARD_SIZE; j++) {

joinEle += board[i][j];

}

// String类的contains方法:当且仅当该字符串包含指定的字符序列时,返回true。

if (joinElecontains("●●●●●")) {

Systemoutprintln("您赢啦!");

flag = true;

// 停止往下继续执行,提前返回flag。

// 如果执行了这个return,就直接返回该方法的调用处;

// 不会再执行后面的任何语句,包括最后那个return语句。

// (而break仅仅是完全跳出这个for循环,还会继续执行下面的for循环。)

return flag;

} else if (joinElecontains("○○○○○")) {

Systemoutprintln("您输啦!");

flag = true;

// 提前返回flag

return flag;

}

}

// 进行竖行扫描

for (int i = 0; i < BOARD_SIZE; i++) {

joinEle = "";

for (int j = 0; j < BOARD_SIZE; j++) {

// 竖行的元素是它们的列值相同

joinEle += board[j][i];

}

if (joinElecontains("●●●●●")) {

Systemoutprintln("您赢啦!");

flag = true;

return flag;

} else if (joinElecontains("○○○○○")) {

Systemoutprintln("您输啦!");

flag = true;

return flag;

}

}

// 进行左斜行扫描

for (int i = -(BOARD_SIZE - 2); i < BOARD_SIZE - 1; i++) {

joinEle = "";

for (int j = 0; j < BOARD_SIZE; j++) {

int line = i + j;

// 只截取坐标值没有越界的点

if (line >= 0 && line < 15) {

joinEle += board[j][line];

}

}

if (joinElecontains("●●●●●")) {

Systemoutprintln("您赢啦!");

flag = true;

return flag;

} else if (joinElecontains("○○○○○")) {

Systemoutprintln("您输啦!");

flag = true;

return flag;

}

}

// 进行右斜行扫描

for (int i = 1; i < 2 (BOARD_SIZE - 1); i++) {

joinEle = "";

for (int j = 0; j < BOARD_SIZE; j++) {

int line = i - j;

if (line >= 0 && line < 15) {

joinEle += board[j][line];

}

}

if (joinElecontains("●●●●●")) {

Systemoutprintln("您赢啦!");

flag = true;

return flag;

} else if (joinElecontains("○○○○○")) {

Systemoutprintln("您输啦!");

flag = true;

// 最后这个return可省略

}

}

// 确保该方法有返回值(如果上面条件都不满足时)

return flag;

}

public static void main(String[] args) throws Exception, IOException {

Gobang gb = new Gobang();

gbinitBoard();

gbprintBoard();

// BufferedReader类:带缓存的读取器————从字符输入流中读取文本,并缓存字符。可用于高效读取字符、数组和行。

// 最好用它来包装所有其 read() *** 作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。

// 下面构造一个读取器对象。

BufferedReader br = new BufferedReader(new InputStreamReader(Systemin));

// 定义输入字符串

String inputStr = null;

// brreadLine():每当在键盘上输入一行内容按回车,刚输入的内容将被br(读取器对象)读取到。

// BufferedReader类的readLine方法:读取一个文本行。

// 初始状态由于无任何输入,brreadLine()会抛出异常。因而main方法要捕捉异常。

while ((inputStr = brreadLine()) != null) {

// 将用户输入的字符串以逗号(,)作为分隔符,分隔成2个字符串。

// String类的split方法,将会返回一个拆分后的字符串数组。

String[] posStrArr = inputStrsplit(",");

// 将2个字符串转换成用户下棋的坐标

int xPos = IntegerparseInt(posStrArr[0]);

int yPos = IntegerparseInt(posStrArr[1]);

// 校验用户下棋坐标的有效性,只能是数字,不能超出棋盘范围

if (xPos > 15 || xPos < 1 || yPos > 15 || yPos < 1) {

Systemoutprintln("您下棋的坐标值应在1到15之间,请重新输入!");

continue;

}

// 保证用户下的棋的坐标上不能已经有棋子(通过判断对应数组元素只能是"╋"来确定)

// String类的equals方法:比较字符串和指定对象是否相等。结果返回true或false。

if (gbboard[xPos - 1][yPos - 1]equals("╋")) {

// 把对应的数组元素赋为"●"。

gbboard[xPos - 1][yPos - 1] = "●";

} else {

Systemoutprintln("您下棋的点已有棋子,请重新输入!");

continue;

}

// 电脑下棋

gbcompPlay();

gbprintBoard();

// 每次下棋后,看是否可以断定赢/输了

if (gbjudgeWin() == false) {

Systemoutprintln("请输入您下棋的坐标,应以x,y的格式:");

} else {

// 完全跳出这个while循环,结束下棋

break;

}

}

}

}

以上就是关于JAVA 五子棋实现问题(辛苦)全部的内容,包括:JAVA 五子棋实现问题(辛苦)、系统框图如下 java实现五子棋程序 可以实现人人对战 人机对战 简单功能 悔棋 认输、java编写五子棋程序,不要图形界面的那种等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/9303786.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存