赞
踩
前面介绍了监听机制,窗体使用,重绘机制,画笔使用,对象传递等等概念,接下来我们来谈一点算法的东西,相信大家都玩过五子棋这个游戏吧,在不包括先手禁手等复杂规则之下,我们只考虑最简单的15*15格子的棋盘,规则是只要没有落子的地方均可下子,某一方连成五个棋子则获胜,游戏结束。
权值法在五子棋游戏中是一种很基础的算法,它的思想包括以下几个部分:
评价权值
计算全盘权值
寻找有利点
判断输赢
1.评价权值
既然我们要选择下棋子,那么我们就需要对利弊情况进行一个数字化的分析,假设我们当前有两种情况,己方有三子连线和四子连线,那么肯定优先去下四子连线的部分,让他成为五子,我们不妨把四子的情况用权值1000来代替,把三子的情况用500来代替,通过判断权值的大小我们可以决定优先下哪个位置。那么我们可以统计一下棋盘上的自己摆放总共有多少种情况(我们把两方都没有被围堵的棋叫“活”,把一方被围堵的棋叫“眠”),那么棋局总共有
活1、眠1
活2、眠2
活3、眠3
活4、眠4
以上这几种情况大概概括了所有棋局阵列的情况,我们可以尝试着给这几种情况划分权值如下:
("010", 30);//活1
("020", 60);//活2
("030", 700);//活3
("040", 10000);//活4
("01-1", 15);//眠1
("02-1", 40);//眠2
("03-1", 500);//眠3
("04-1", 10000);//眠4
这种权值划分方式只是我小时牛刀的一种而已,具体的数值该如何分配尚需要具体的商榷,大家可以在实现大体功能之后仔细斟酌和修改,此处不做过多讨论。
2.权值计算
在前面我们已经分配好了各种情况的权值,接下来要做的是根据上述规则计算棋盘上所有点的权值大小,统计棋盘上各点的情况并把情况转化为数字存储起来。我们可以用一个for循环来完成单个点的权值计算过程。假设当前点下标是
(i,j)。我们要对该点的八个方向进行检索,可以写八个函数分别对八个方向进行判断
在这里我选择写两个数组,然后通过数组实现对八个方向的遍历,先判断遍历方向的第一个棋子是何种情况,然后再看他的后面有没有与之相同的棋子,直到最终检索到空白位置或者与之颜色相反的棋子,那么就终止,然后统计在这个方向上的棋子排布,将其转化为数字权值;最后将八个方向的权值累加起来放在权值数组中,代表当前位置的权值
int X[] = {1,1,0,-1,-1,-1,0,1}; int Y[] = {0,1,1,1,0,-1,-1,-1}; for(int i=0;i<X.length;i++){ //ans数组存放的是当前棋盘的落子情况,1代表是己方的黑子,-1代表对方的白子 int tmp_x = x + X[i]; int tmp_y = y + Y[i]; if(ans[tmp_x][tmp_y]==1) { //如果这个坐标为1,说明当前方向第一子为己方 int temp = -2; //一直循环知道后面的子与第一子颜色不同为止 while(ans[tmp_x][tmp_y]==1) { num_positive++; tmp_x+=X[i]; tmp_y+=Y[i]; if((tmp_x<15&&tmp_x>=0)&&(tmp_y>=0&&tmp_y<15)) {}//判断边界问题,不 能超边界 else {temp = -1;break;} } /*-2是初始化的值,如果temp没被改变过,说明没有超边界,就把最终结束的ans赋给temp*/ if(temp==-2) { temp=ans[tmp_x][tmp_y]; } if(temp==-1) { //根据hashmap给最后的tmp_sum加权值 Integer value=map.get("0"+num_positive+"-1"); if(value != null) { tmp_sum+=value; } } else { Integer value=map.get("0"+num_positive+"0"); if(value != null) { tmp_sum+=value; } } } //这里讨论的是另一种情况,即遇上了对方白子的情况 else if(ans[tmp_x][tmp_y]==-1){ int temp=-2; while(ans[tmp_x][tmp_y]==-1) { num_negative++; tmp_x+=X[i]; tmp_y+=Y[i]; if((tmp_x<15&&tmp_x>=0)&&(tmp_y>=0&&tmp_y<15)) { } else { temp=1; break; } } if(temp==-2) { temp=ans[tmp_x][tmp_y]; } //如果遇上了黑子而停止,执行以下操作 if(temp==1) { Integer value=map.get("0-"+num_negative+"1"); if(value!=null) { tmp_sum+=value; } } //如果是遇上了空白棋盘,执行下面操作 else if(temp==0){ Integer value=map.get("0-"+num_negative+"0"); if(value != null) { tmp_sum +=value; } } } } }
执行完上面这一段代码之后,我们就能够计算出(i,j)位置的权值大小了,接下来我们要计算出所有棋盘上空白位置的权值,用双重for循环即可,代码如下:
public void fill() { int max=-1; x0=-1; y0=-1; //双重for循环,进行整个棋盘的遍历 for(int i=0;i<BOARD_SIZE;i++) { for(int j=0;j<BOARD_SIZE;j++) { if(ans[i][j]==0) { //为0代表棋盘上没有棋子,可以计算其权值 weight[i][j]=search(i,j); //填充这个位置上的权值 if(max<weight[i][j]) { //选取权值中最大的一个点 max=weight[i][j]; x0=i; y0=j; } } } } }
选取好了权值最大点的下标之后,就可以将参数传递给处理函数进行调用处理了,在下完权值最大位置的棋子之后,我们可以判断输赢,如果某一方赢得比赛,则弹出一个新窗口游戏。
当然我们还需要做一个窗体面板,做好画棋子的机制,做好重绘机制,判断输赢机制等等
在这里我再可以讲一下悔棋的思路,因为我在重绘的时候是把所有的棋子存放在一个数组中,然后对这个数组进行遍历重绘,如果我要悔棋,只需要让数组的长度减一即可
if("悔棋".equals(zhiling)) {
ms.set_ans(chess_arr[ms.get_index()-1].get_x(), chess_arr[ms.get_index()-1].get_y(), 0); //设置悔棋位置的棋子为0
ms.set_index(-1); //下标-1
this.paint(gf); //悔棋之后重绘一下
ms.reverse_flag(); //将下棋的权利进行翻转
}
讲到这里,大概就是五子棋全职算法的基本实现流程了,大致思路是对棋盘上的各种局势的一种主观性判断,将主观危险的判断用数字来代替从而实现优先选择的机制。
有疑问联系1124397151@qq.com
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。