首页 » 技术分享 » 人机对战五子棋(权值法)

人机对战五子棋(权值法)

 

前面介绍了监听机制,窗体使用,重绘机制,画笔使用,对象传递等等概念,接下来我们来谈一点算法的东西,相信大家都玩过五子棋这个游戏吧,在不包括先手禁手等复杂规则之下,我们只考虑最简单的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


转载自原文链接, 如需删除请联系管理员。

原文链接:人机对战五子棋(权值法),转载请注明来源!

0