到如今,写五子棋也有一段时间了,虽然一开始的人人版并没用多长的时间,但后面的人机对战版可没让人头疼。从对思路的一无所知(在此感谢陆亮小盆友
的“精心”指导,呵呵)到了解算法的大概思想,再到真正实现过程中遇到的问题,一路走来,最深的体悟就是,有些错误是想出来而难的调试出来的,而且此种想问题的方法不是对着电脑看着代码想,那样会让人睡觉并且只会感到强烈的挫折感,而是在一个人路上想,去吃饭,去上自习,说不定哪个时候灵光一乍现,突然就发现症结所在了。可惜,经本人不怎么灵泛的大脑,找出问题重见蓝天的同时发现花儿早就谢了,因此也就没有了那种见到新鲜事物时的兴奋激动,唯有的是走出迷雾后的释然。但不管怎么说,现在总算是把那纠结的人机对战五子棋大体完工了。有图有真相,也让自己的思路进入正题。呵呵。
总体来说,它应该算有三岁小孩的智商,会守会攻,而且以攻为主,虽然这攻守都不怎么高级,但一不小心你也是会输的,呵呵。
废话讲了许多,下面进入正题,给这一五子棋来个阶段性的总结:
首先,算法思路:
并没有用到贪心法系统的算法策略,而只是自创的暴力型算法。
首先提供数据结构:一个棋盘二维数组,初始值全为0.用1代表下的黑子,-1代表下的白字。没下一颗子,相应位置上的值改变。 两个权值数组,分别记录玩家的当前棋盘和电脑的当前棋盘。用权值代表该位置的重要性,每放一个棋子,权值数组都重新赋值。
其次就是权值的设置问题,四连的权值最大,其次是活三连,再活二连等等。这里情况分的越细越好,但对本人这类菜鸟,情况越多会把问题搞的越复杂,因此也就作罢。只分了最简单的几种情况:四连,三连,二连,单个子,其中,三连二连每种情况又分双边活和单边活,因此也就六种不同的权值。
最后关键就是找到最大的权值点了。我的思路是,在玩家权值数组中找到权值最大的点,在电脑权值数组中也找到最大的点,再两个比较,谁大电脑就下子在那个权值大的位置。
接下来的问题就是每个权值数组中权值最大点怎么找了。其实无非也就是多次的用两个循环遍历二维数组,在每个点上做四个方向上的判断,求得每个方向上有几个相同 的子,并用四个变量做暂时记录,然后比较这四个变量,在权值数组中赋上这最多棋子个数对应的权值。
代码如下:
else {//人机对战
System.out.println(MenuJPanel.jc.getSelectedItem());
if(chesses[i][j]==0) {//当前无子时才能放
g.setColor(Color.black);
//玩家下黑子
g.fillOval(x - CHESS_SIZE / 2, y - CHESS_SIZE / 2,
CHESS_SIZE, CHESS_SIZE);
//下子之后该点权值赋值为1
chesses[i][j]=-1;
//电脑找权值最大的点放子
g.setColor(Color.white);
//遍历两棋盘
new MaxValue(VALUES_PALYER).lookforMaxValue(-1);//黑子为-1
new MaxValue(VALUES_COM).lookforMaxValue(1);//白字为1
/在两个权值数组中找最大权值的点
MaxValuep=getMAXVALUE(VALUES_PALYER);
MaxValuec=getMAXVALUE(VALUES_COM);
if(VALUES_PALYER[MaxValuep.y][MaxValuep.x]<=VALUES_COM[MaxValuec.y][MaxValuec.x]) {
//进攻
Max=MaxValuec;
}else {//防守
Max=MaxValuep;
}
int xx = X0+Max.x*SIZE;
int yy = Y0+Max.y*SIZE;//最大权值坐在点的坐标(由下标转化而来)chesses[Max.y][Max.x]=1;
g.fillOval(xx - CHESS_SIZE / 2, yy - CHESS_SIZE / 2,
CHESS_SIZE, CHESS_SIZE);
此段代码中注意:若if(VALUES_PALYER[MaxValuep.y][MaxValuep.x]<=VALUES_COM[MaxValuec.y][MaxValuec.x]) 改成<,则表示当玩家的权值点小于电脑的权值点时才取电脑的权值所在点,则此为以电脑防守为主。但<=则表示以电脑进攻为主。
下面是遍历权值数组:
for(int i=0;i<ROWS;i++ ) {
for(int j=0;j<COLUMNS;j++) {
//给空白处的点赋值
if(chesses[i][j]==0) {
//System.out.println(chesses[i++][j]);
//System.out.println("开始"+ count++);
m=i;
n=j;
num[0]=num[1]=num[2]=num[3]=0;
vacant[0]=vacant[1]=vacant[2]=vacant[3]=0;
//横向向右
while(n<COLUMNS-1&&chesses[m][++n]==color) {
num[0]++;
if(n<COLUMNS-1&&chesses[m][n+1]==0) vacant[0]++;
}
if(j>0&&chesses[i][j-1]==0) vacant[0]++;
n=j;
//横向向左
while(n>0&&chesses[m][--n]==color) {
num[0]++;
if(n>0&&chesses[m][n-1]==0) vacant[0]++;
}
if(j<COLUMNS-1&&chesses[i][j+1]==0) vacant[0]++; n=j;
//纵向向下
while(m<ROWS-1&&chesses[++m][n]==color) { num[1]++;
if(m<ROWS-1&&chesses[m+1][n]==0) vacant[1]++;
}
if(i>0&&chesses[i-1][j]==0) vacant[1]++;
m=i;
//纵向向上
while(m>0&&chesses[--m][n]==color) {
num[1]++;
if(m>0&&chesses[m-1][n]==0) vacant[1]++;
}
if(i<ROWS-1&&chesses[i+1][j]==0) vacant[1]++;
m=i;
//斜向左下
while(n>0&&m<ROWS-1&&chesses[++m][--n]==color) {
num[2]++;
if(m<ROWS-1&&n>0&&chesses[m+1][n-1]==0) vacant[2]++;
}
if(i>0&&j<ROWS-1&&chesses[i-1][j+1]==0) vacant[2]++;
m=i;n=j;
//斜向右上
while(n<COLUMNS-1&&m>0&&chesses[--m][++n]==color) {
num[2]++;
if(m>0&&n<COLUMNS-1&&chesses[m-1][n+1]==0) vacant[2]++;
}
if(i<ROWS-1&&j>0&&chesses[i+1][j-1]==0) vacant[2]++;
m=i;n=j;
//斜向右下
while(m<ROWS-1&&n<COLUMNS-1&&chesses[++m][++n]==color) {
num[3]++;
if(m<ROWS-1&&n<COLUMNS-1&&chesses[m+1][n+1]==0) vacant[3]++;
}
if(i>0&&j>0&&chesses[i-1][j-1]==0) vacant[3]++;
m=i;n=j;
//斜向左上
while(m>0&&n>0&&chesses[--m][--n]==color) {
num[3]++;
if(m>0&&n>0&&chesses[m-1][n-1]==0) vacant[3]++;
}
if(i<ROWS-1&&j<COLUMNS-1&&chesses[i+1][j+1]==0) vacant[3]++;
m=i;n=j;
int max = num[0];
int temp=0;
//找出有相同棋子最多的边,并给该位置赋权值
for(int q=0;q<4;q++ ){
if(max<num[q]) {
max=num[q];
temp=q;
}
}
//给该点设置权值
setValue(values,max,vacant[temp],i,j);
}
下面是给点赋权值代码:
public void setValue(int values[][],int num,int vacant,int i,int j) {
if(num==1) {//一连
if(vacant==1) {//一活
values[i][j]=100;
} else if(vacant==2) {//两活
values[i][j]=500;
}
}else if(num==2) {
if(vacant==1) {//一活
values[i][j]= 200;
}else if(vacant==2) {//两活
values[i][j]=2400;
}
}else if(num==3) {
if(vacant==1) {//一活
values[i][j]=1000;
}else if(vacant==2) {//两活
values[i][j]=5000;
}
}else if(num==4) {
values[i][j]=10000;
}else {
values[i][j]=0;
}
}
此处注意:vacant表示空格数,即要判断死活情况,当vacant=2时,表示两边空(相连棋子的一侧和该点的另一侧),vacant=1表示任何一边活。
下面是找权值数组中的最大值:
//在两个权值数组中找最大权值
public Point getMAXVALUE(int values[][]) {
Point max = new Point();
max.x = 0;
max.y = 0;
for(int i=0;i<ROWS;i++) {
for(int j=0;j<COLUMNS;j++) {
if(values[max.y][max.x]<values[i][j]) {
max.y = i;
max.x = j;
}
}
}
return max;
}
注意:此处可能有细心地朋友注意到,若有两个相同的权值怎么办?这时候应该选哪个点?在此,我是粗略的用遇到大的则覆盖,不比前一个位置的权值大就跳过的办法来处理的。当然此种处理不精确,也会影响到了机器的放子,即决定了他的聪明程度。不过这是第一个版本,也就暂且这么处理了。
关键的思路就到这了,最后只剩下一个判断输赢问题。需要注意的是,判断输赢肯定是写在一个判断输赢的类中,此时玩家下子后判断和电脑下子后判断传进来的参数不同,玩家下子判断后传进来的时鼠标所点击的位置交叉点的下标,而电脑下子后判断传进来的是电脑找到的最大权值所在位置的下标。
附: 其实,本人这种思路可能是最笨的一个了。完全没有必要用到两个权值数组,这完全是把简单问题复杂话,甚至引起中间有些大的漏洞,就像遇到相同权值的这种情况。另一种稍微简单点的思路是只用一个权值数组表示当前棋局,电脑计算出每个点白子的权值和黑子的权值,把他们相加,最后找到最大权值所在点放子。这样就省去了中间很多比较的环节。有兴趣的朋友可自己研究下。
最后,说说这段时间写五子棋过程中所收获的经验,互相学习:
1 在代码布局上,记住“一个类对应一个功能,一个方法实现一个功能”的原则。代码的结构很重要。本次虽然完成了人机对战的大体功能,但在代码布局上还有很大的问题,因此还得有下一个版本的完善。
2 通过这次五子棋的开发,进一步了解了重绘。
3 在得到画布(需在setVisible(true)之后)与调用画布顺序出冲突时,可以“曲线救国”,重新写一个方法,在该方法中用到画布。
例如:
//添加棋盘
chessboard = new ChessJPanel();
//得到画布对象
g = chessboard.getGraphics();
chesslistener = new ChessListener(g,this);
//添加监听器
public void addListener() {
chessboard.addMouseListener(chesslistener);
}
4 在一个类中要用到某个接口中的成员,可以直接用接口类.成员,也可以用该类实现该接口,在类中直接使用。
5 学语言得边敲代码边总结,否者像我做玩家对战五子棋中遇到的问题就给忘了。可能下次遇到又得想个老半天了。
码了这么多字,却还不怎么想睡觉。没办法明天还得上课。朋友,晚安。呵呵
- 大小: 72.9 KB
- 大小: 76.5 KB
分享到:
相关推荐
本篇将深入解析“人机对战五子棋源码2012810”,探讨其背后的编程逻辑、算法设计以及源码结构,为对游戏开发感兴趣的读者提供一份详实的学习资料。 一、开发环境与项目背景 该源码是基于Visual Studio 2010(VS...
本篇将基于"五子棋人机对战源码"这一主题,深入剖析其背后的编程技术和策略设计。 首先,我们关注的是"源码"本身。源码是程序员用高级语言编写的应用程序的原始代码,它是程序的最初形态,包含了所有指令和逻辑。在...
本篇将针对提供的"[Android游戏源码]-人机对战五子棋代源码"进行深入探讨,解析其核心机制,帮助开发者理解和学习如何构建一个基于Android平台的人机对战游戏。 首先,我们来看标签中的"android",这表明该源码是为...
本篇文章将深入探讨如何利用Delphi编程语言构建一个简单的人机五子棋游戏。Delphi,作为一款强大的面向对象的集成开发环境(IDE),因其高效的编译器和丰富的组件库而深受开发者喜爱。在这个项目中,我们将关注其在...
总的来说,这篇论文详细阐述了如何利用Android平台的特性,结合Java编程语言,开发出一款功能完备的人机对弈五子棋应用。通过深入研究和实践,作者成功实现了五子棋游戏的人工智能算法,使得游戏具有较高的挑战性和...
本篇文章将围绕"五子棋 人人对战 人机对战"这一主题,详细解析其在软件开发中的实现原理和技术要点。 首先,我们要理解五子棋的基本规则:两位玩家轮流在棋盘上落子,先形成连续五个同色棋子的一方获胜。在电子游戏...
本篇将围绕“VC6.0人机对战五子棋”这一主题,深入探讨如何利用Windows API在Visual C++ 6.0环境下实现这样的游戏,并从中学习API的使用技巧。 首先,我们要理解的是Windows API(Application Programming ...
在计算机科学领域,实现五子棋的人机对战系统是一项有趣的挑战,涉及到算法设计、图形界面开发以及人工智能的应用。本篇文章将深入解析基于VC++实现的五子棋人机对战源码,帮助读者理解其中的关键技术和思路。 首先...
这篇介绍主要涉及的是一个基于Flash CS3和ActionScript 3.0(AS3)编程语言开发的五子棋游戏源码,它包含了人机对战的功能。五子棋是一种广受欢迎的策略游戏,通过这个源码,开发者可以学习到如何在Flash环境中实现...
这篇毕业论文《VC++五子棋人机对战》深入探讨了如何利用C++编程语言构建一个五子棋游戏,并实现人机对战的功能。在本文中,作者不仅展示了编程技术,还融入了人工智能的设计思想,使计算机能够具有较高的智能性,与...
本篇将围绕标题“wuziqi.rar_vc++_五子棋 VC++_五子棋 vc_五子棋 人机”,深入解析如何使用VC++这一经典编程环境来构建五子棋游戏,并实现人机对战模式。 首先,我们要明确的是,VC++是Microsoft公司开发的一款强大...
本篇将深入探讨一个用C++实现的五子棋人机对战源码,旨在帮助开发者理解其工作原理,并提供优化算法的思路。 首先,让我们来看看人机对战的核心——AI算法。在这个源码中,计算机对手的决策过程通常基于搜索算法,...
这篇内容将深入解析《安卓Android源码——游戏源码人机五子棋项目源码》这一主题,旨在为你提供丰富的Android开发以及游戏编程的知识。在分析之前,我们先了解一下核心概念:Android是谷歌主导的开源操作系统,广泛...
本篇将重点讨论如何使用LabVIEW设计并实现一个五子棋小游戏。 五子棋是一种简单却富有策略性的双人对弈游戏,其规则简单明了:任意一方的棋子在棋盘上形成连续的五个,即为胜利。在LabVIEW中实现这样的游戏,主要...
这篇毕业设计论文主要探讨了如何使用Java编程语言实现一个真正的智能人机对战五子棋游戏。五子棋是一种双人对弈的策略棋类游戏,目标是在棋盘上连成五子,横、竖或斜线皆可,先达成者获胜。在Java环境下开发这样的...
本篇文章将深入探讨如何使用MFC创建一个五子棋程序,以及这个过程中的关键知识点。 **一、MFC框架介绍** MFC是基于Windows SDK的,它封装了大量的Windows API函数,提供了许多预定义的C++类,如CWinApp、CWnd、...
《iOS OC版五子棋游戏开发详解》 在iOS应用开发中,我们经常会遇到需要创建各种类型的游戏,其中五子棋作为一款经典智力游戏,深受用户喜爱。本篇将深入探讨如何使用Objective-C(简称OC)语言,在iOS平台上实现一...