基于Android平台的五子棋小游戏------AI篇

来源:互联网 发布:域名紧急更新 编辑:程序博客网 时间:2024/04/30 13:59

前言

从2015年十一月开始,本渣渣对Android应用程序开发产生了很浓厚的兴趣,在极客学院和慕课网学习了Java和Android基础课程,但是当我自己想做一个小应用出来的时候,发现脑袋一片空白,知识点都是零零碎碎的,根本就没有办法形成体系,就更不谈做出小应用了。于是本渣渣再一次的去知乎求救,发现众多大神的回答是,首先把Java 的基础打好,接下来很多事情都会顺利很多,并且在前辈的推荐之下,我去百度到了传智播客毕向东毕老师的Java基础视频,于是我又开始了一轮潜修,在众多专业课程把时间排得满满的情况下,花了一个月的时间看完了毕老师的35天视频,看完之后才发现,之前那学的Java哪能叫Java,完全只是学了个皮毛,现在也基本上算是可以入门了。然后在论坛上闲逛的时候,看到很多关于郭霖大神的《Android:第一行代码》的消息,很多人都高度的评价这本Android入门书籍,于是抱着好奇的想法,我去淘宝买了一本(正版哦~,对于技术型的书籍本渣渣还是很舍得投资的),顿时又发现,原来那些Android基础,哪里算得上是基础,于是花了一个月的时间读完了这本书,代码全部敲了一遍。在看完毕老师的Java和郭大神的第一行代码之后,我才觉得,我才真正的算得上是入门了。后期也看了很多关于Android的书,记忆最深刻的还是《Android权威编程指南》,它将零零碎碎的知识点全部整合起来,在一个个小的Demo里,将知识点串联。虽然嘴上说老美不好,但不得不承认的是,老美出的书确实含金量高!再接下来的学习经历,就不多说了, 总之和大部分的人都一样。
这个学期选修课程选的是“Android应用开发”,本想着去升华下自己,后来去上课才发现,报名的人层次鱼龙混杂,老师不得不从最基础的Java是什么开始讲起,所以深度这块并不是我想要的,于是选修课也没有去过几次,都是自己在图书馆敲代码,琢磨。在选修课结束的时候,选修课老师X老师,让我们把作业发送到他邮箱里,于是我将前段时间我做的五子棋小游戏发了过去,收到了老师的青睐,他邀请我和他的研究生一起参加“全国研究生APP大赛”,就使用我做的五子棋小应用去参赛,我也是受宠若惊,感恩涕零。写这篇博文,也算是结束我仅仅转载别人大牛博文,自己从不写博文的阶段吧,开始我的第一篇博文。别的也不多说了,直接进入正题

一.五子棋小游戏的AI算法概括

在五子棋应用的“人机对战”功能中,AI算法可以说是最核心的部分,通过该算法,计算机可以根据目前场上的棋子分布情况, 来计算出适合自己下棋的最佳点,从而达到拦截和进攻的目的。该算法在computerAI(…)方法中实现,如下所示:

public static Point computerAI(ArrayList mWhiteArray,ArrayListmBlackArray,
int[]myWin,int[]computerWin,boolean[][][]wins) {

本方法传入了五个参数,分别是:
ArrayList mWhiteArray;
ArrayList mBlackArray;
int[] myWin;
int[] computerWin;
boolean[][][] wins;
下面详细说明本方法。

二.computerAI(…)方法参数数据结构及作用说明

2.1 ArrayList mWhiteArray;白棋的统计List

ArrayList的泛型指定为Point类型,Point类型参数由唯一的(x,y)值对所组成,由于棋盘上的每一粒棋子也都有一个确定的(x,y)值对,所以在本应用里,每一个point都代表棋盘上的一个具体的点,然后将棋子存入ArrayList中。其中mWhiteArray的作用是存储当前棋盘上所有白色棋子的位置。

2.2 ArrayList mBlackArray;黑棋的统计List

mBlackArray作用和mWhiteArray一致。区别是,用于存储当前棋盘上所有黑色棋子的位置。

2.3 boolean[][][] wins;赢法数组

本数组为“赢法数组”(假定用i,j,k分别代表数组的三个维度,count代表赢法的种类),作用是
统计出在15*15的棋盘上,一共有多少种赢法count,并且记录下每一种赢法所对应的是哪五对i,j。下面做详细说明:
就可以理解为count的索引。那么每一个确定的count都会有对应的五对i,j,我们将这五对i,j的值赋值为true,其余的位置不赋值,就是boolean类型的默认值false,那么这五个“true”一定是在一条直线上,这个直线只有四种情况:横线、竖线、斜线、反斜线。
基于以上的分析,首先我们要确定,在15*15的棋盘上,总共有多少种赢的方法,也就是要确定count值,并且要求得一个确定的count所对应的是哪五对i,j值为true。所以我们从横线,竖线,斜线,和反斜线四个方向上去初始化这个数组:
横向:

for (int i = 0; i < 15; i++) {            for (int j = 0; j < 11; j++) {                for (int k = 0; k < 5; k++) {                    wins[i + k][j][count] = true;                }                count++;            }        }

竖向:

for (int i = 0; i < 15; i++) {            for (int j = 0; j < 11; j++) {                for (int k = 0; k < 5; k++) {                    wins[i][j + k][count] = true;                }                count++;            }        }

斜向

for (int i = 0; i < 11; i++) {            for (int j = 0; j < 11; j++) {                for (int k = 0; k < 5; k++) {                    wins[i + k][j + k][count] = true;                }                count++;            }        }

反斜向:

for (int i = 0; i < 11; i++) {            for (int j = 14; j > 3; j--) {                for (int k = 0; k < 5; k++) {                    wins[i + k][j - k][count] = true;                }                count++;            }        }

当上述四个方向的统计工作做完了之后,根据上诉统计的顺序,可以得出wins[][][0]和wins[][][1],win[][][10]的情况如下图所示:
wins[][][0]

wins[][][1]

win[][][10]

由此也可以推得其他赢法所对应的五粒棋子的坐标,并且最后可以得出count的值为572,也就是说,在15*15的五子棋盘上,总有有572种不同的赢法,并且每一种赢法所对应的五粒棋子的坐标也都存储在了wins[][][]数组里,到此时,赢法数组已经初始化完毕。

2.4 int[] myWin;我方赢法统计数组

该数组是一维数组,数组的长度就是count值572。该数组的目的是用于记录,我方在某一确定的count种类的赢法下,已经实现了几粒棋子。当实现了5粒棋子的时候,即使存在一个k值,使myWin[k]=5,那么我方获得胜利。
基于以上秒描述,该数组的初始值都赋为0即可,在程序中的用处如下:

这里写图片描述

一旦我方在某一位置上下了一粒棋子p(x,y),那么首先遍历所有的赢法,判断p存在于哪一种赢法k0中,找到这个k之后,wins[p.x][p.y][k0]就会返回true,那么myWin[k0]++,即在第k0种赢法的方向上,我们又迈出了一步,距离以第k0种方法获胜又近了一点,与此同时,计算机一方是不可能以k0方法获胜的,因为以这种方法获胜的五粒棋子中,已经有一粒是我方的棋子了,于是computerWin[k0]==6,给这种赢法的计算机赢法统计数组赋一个异常值(myWin[]和computerWin[]的值只可能是0、1、2、3、4、5)。当myWin[]中存在一个K,使mywin[k]==5,那么就说明我方已经用第k种方法获胜了,比赛结束。

2.5 int[] computerWin;计算机方赢法统计数组

该数组的结构和作用和myWin[]数组相同,区别在于,本数组是用于统计计算机一方在第k0种方法上,已经实现的程度。在程序中的用处如下:
这里写图片描述

此程序逻辑和myWin[]相同,不再做赘述。

2.6 int[] computerWin;int[] myWin;boolean[][][] wins 三者之间的关系

1.首先我们将情况简化一下,假设五子棋只有两种赢法,并且我方是白棋:
这里写图片描述

2.在左上角下一粒棋子,我方的赢法统计数组情myWin[]况如下:
这里写图片描述

3.往右方再下一粒棋子,我方的赢法统计数组myWin[]如下:
这里写图片描述

4.再接着下,当myWin[0]==5时候,我方就获取了胜利。

5.再将赢法的情况一般化,count==572,有572种赢法,由上述说明可以退的其余情况下,myWin[]数组的各种情况。

6.myComputer[]数组也同上述一直,此处不再赘述。

这就是int[] computerWin;int[] myWin;boolean[][][] wins三个数组的运行原理。

三.computerAI(…)算法的具体实现

computerAI(…)方法的总体思路是要对棋盘上所有空闲的点(在该点处还没有棋子落下)赋分,然后得到的分数最大的点,就是bestPoint,也就是计算机所需要落子处。下面详细分析如何对空闲点赋分:
1.首先我们需要在该方法里定义如下几个局部变量并初始化:
这里写图片描述

myScore[15][15]:该数组用于记录我方在空闲点处的分值情况,且初始化为0。
computerScore[15][15]:该数组用于记录计算机方在空闲点处的赋分情况,初始化 为0。
maxScore:用于记录目前棋盘上所有空闲点的最大分数。
u,v:用于记录最大分数处点的坐标。

2.遍历棋盘上的所有点,并且判断在该位置是否是空闲点:
这里写图片描述

3.如果在(i,j)处是空闲点(即p既不存在与mWhiteArray中,也不存在与mBlackArray
中), 那么就遍历所有的赢法(即count种赢法),如果在(i,j)这个点wins[][][]==true,
那么这个点就是有价值的,就需要对这个点进行赋分,赋分的大myWin[],computerWin[]
赢法统计数组在(i,j)处的值的不同,而有所改变,具体如下:
这里写图片描述
这里写图片描述

当myWin[k]=1时,我们需要赋给这个点一个分数,当 myWin[k]=2,时,我们需要赋给这个点一个更高的分数值,因为这种情况下,这个点更加的紧迫,更有可能成为myWin[k]=5。
myWin[k]和computerWin[k]其余的情况由上可以推知。总的来说就是,赢法统计数组的值越高,那么在该处赋分的值就越大!
并且此处计算机是先对myWin[k]进行赋分,再对computerWin[k]进行赋分,这说明的是计算机首先是考虑棋盘上人类的当前的情况,再考虑自己当前的情况,通俗的说就是:先防守,后进攻。

4.接下来就是需要从myScore[][]和computerScore[][]中找出分数最高的点,并且赋值给(u,v)
计算机首先拿当前(i,j)点处的分数myScore[i][j]和maxScore作比较,如果myScore[i][j]>maxScore,那么就将myScore[i][j]的值赋给maxScore,且将(i,j)的值赋给(u,v);如果myScore[i][j]=maxScore,那那么就比较计算机自己一方在(i,j)点处分数的大小,和目前(u, v)点处的大小,如果computerScore[i][j]>computerScore[u][v],那么仍然将myScore[i][j]的值赋给maxScore,且将(i,j)的值赋给(u,v)。然后再到computerScore[]中去找分数最大的点,过程同上,也不再赘述。程序如下:
这里写图片描述

5.当遍历完了棋盘上所有的点时,得到的(u,v)就是bestPoint,也就是计算机需要落点的位置处。然后我们将(u,v)打包成Point类型,返回到其对应的统计List中,然后在棋盘上描绘出即可。

四.后记

第一篇博文就完成了,这是五子棋小游戏的核心算法,也就是AI算法,其他模块有点难度的就是,画出棋盘,棋子,更新棋子,判断输赢,响应输入。可以通过控制这个算法,从而给游戏划定不同的难度,目前也就做出来了单机的版本,所以后期再想往网络版本方向优化,将当前棋子的数据送至服务区,然后宁外一个客户端从服务器拿到数据,解析到自己的棋盘中,然后下棋,然后再传送到服务器,依次循环。。。目前只是一个思路,由于本渣渣还有好多考试和比赛,这一块一时半会估计是做不出来了。等有时间的时候,再完善。 接下里的博文就总结这个小游戏的其他模块,毕竟不能太监。。。

0 0
原创粉丝点击