权值法是个什么玩意呢?其实他是基于枚举的暴力的方法。之前说过五子棋的棋盘是15*15的。并且赢得情况是5个相同的棋子连成一块,那么大家就可以想到,我们能枚举出所有赢得情况,大约几千种,在我们看来这个数字比较大,但是对于一秒就可以处理数量级到8的计算机来说,小case是不是?那么有了这个理解我们就可以实现两个表,分别是机器和人的胜利表,也就是储存赢得情况的表,那么有了这个表,最终机器决定下棋是要通过权值判断的,至于权值怎么来,先不用管,我们也要用两个表分别储存机器和人的权值的表,贴出代码:
- private GoBangPanel panel;
- private Graphics2D g;
- private int num;
- private Judge j;
- public class struct
- {
- int last;
- //计算x,y坐标
- int[] x = new int[5];
- int[] y = new int[5];
- //记录每个取胜表的棋子相连的情况
- int[] wn = new int[5];
- }
- struct[] winlistp = new struct[800];
- struct[] winlistc = new struct[800];
- int[][] scorep = new int[Row][Coloum];
- int[][] scorec = new int[Row][Coloum];
private GoBangPanel panel;
private Graphics2D g;
private int num;
private Judge j;
public class struct
{
int last;
//计算x,y坐标
int[] x = new int[5];
int[] y = new int[5];
//记录每个取胜表的棋子相连的情况
int[] wn = new int[5];
}
struct[] winlistp = new struct[800];
struct[] winlistc = new struct[800];
int[][] scorep = new int[Row][Coloum];
int[][] scorec = new int[Row][Coloum];
说一下这几个东西的意思,scorep就是储存人的权值的表,scorec就是储存机器的权值的表。winlist同理是储存赢的情况的表。它是一个结构体数组,其中的x,y分别储存的是五个棋子相连的坐标,last表示这个情况是不是有效,说明一下,比如这是储存白棋赢的一种情况,五个棋子的坐标都已经储存了,但是在下棋的过程中,有一个黑色的棋子下在了这个五个坐标的其中一个,那么白棋就不可能通过这个情况赢了,那么就是无效的。那么大家就可以发现,随着下棋越来越多,无论是计算机还是人的winlist,无效的就会越来越多。
理解了winlist的作用,我们就来说一下score的作用,他就是储存权值的表,每次遍历查找出黑棋和白棋的权值的最大值,我们领白棋是计算机,黑棋是人,那么如果权值最大的就是白棋那么就体现了进攻,如果是黑棋就体现的防守,因为我们现在所说的是针对计算机的,因为我们编这套程序是为了让计算机知道怎么下棋。
怎么计算权值呢?我们先不跳着讲,先看看winlist怎么初始化和更新,初始化就是把所有赢的情况全部储存嘛,贴出代码:
- public GoBangAn(GoBangPanel panel)
- {
- //初始化黑棋和白棋各自的评分
- for(int i = 0; i < Row; i++)
- {
- for(int j = 0; j < Coloum; j++)
- {
- scorep[i][j] = 0;
- scorec[i][j] = 0;
- }
- }
- //初始化黑棋和白棋数组的各个元素
- for(int i = 0; i < 800; i++)
- {
- winlistp[i] = new struct();
- winlistc[i] = new struct();
- }
- this.panel = panel;
- g = (Graphics2D)this.panel.getGraphics();
- num = 0;
- //枚举各种情况(有4个大方向)
- //分别计算人和计算机行的赢的情况
- for(int i = 0; i < Row; i++)
- {
- for(int j = 0; j < 11; j++)
- {
- for(int k = j; k < j+5; k++)
- {
- winlistp[num].x[k-j] = i;
- winlistp[num].y[k-j] = k;
- winlistc[num].x[k-j] = i;
- winlistc[num].y[k-j] = k;
- winlistp[num].last = 0;
- winlistp[num].wn[k-j] = 0;
- winlistc[num].last = 0;
- winlistc[num].wn[k-j] = 0;
- }
- num++;
- }
- }
- //分别计算人和计算机列的赢的情况
- for(int i = 0; i < Coloum; i++)
- {
- for(int j = 0; j < 11; j++)
- {
- for(int k = j; k < j+5; k++)
- {
- winlistp[num].x[k-j] = k;
- winlistp[num].y[k-j] = i;
- winlistc[num].x[k-j] = k;
- winlistc[num].y[k-j] = i;
- winlistp[num].last = 0;
- winlistp[num].wn[k-j] = 0;
- winlistc[num].last = 0;
- winlistc[num].wn[k-j] = 0;
- }
- num++;
- }
- }
- //分别计算人和计算机右上对角线赢的情况
- for(int i = 1; i < 11; i++)
- {
- for(int x = i,y = 0; x < 11; x++,y++)
- {
- for(int kx = x,ky = y; kx < x+5; kx++,ky++)
- {
- winlistp[num].x[kx-x] = ky;
- winlistp[num].y[kx-x] = kx;
- winlistc[num].x[kx-x] = ky;
- winlistc[num].y[kx-x] = kx;
- winlistp[num].last = 0;
- winlistp[num].wn[kx-x] = 0;
- winlistc[num].last = 0;
- winlistc[num].wn[kx-x] = 0;
- }
- num++;;
- }
- }
- //分别计算人和计算机左下对角线的情况
- for(int i = 1; i < 11; i++)
- {
- for(int y = i,x = 0; y < 11; y++,x++)
- {
- for(int kx = x,ky =y; ky < y+5; kx++,ky++)
- {
- winlistp[num].x[kx-x] = ky;
- winlistp[num].y[kx-x] = kx;
- winlistc[num].x[kx-x] = ky;
- winlistc[num].y[kx-x] = kx;
- winlistp[num].last = 0;
- winlistp[num].wn[kx-x] = 0;
- winlistc[num].last = 0;
- winlistc[num].wn[kx-x] = 0;
- }
- num++;
- }
- }
public GoBangAn(GoBangPanel panel)
{
//初始化黑棋和白棋各自的评分
for(int i = 0; i < Row; i++)
{
for(int j = 0; j < Coloum; j++)
{
scorep[i][j] = 0;
scorec[i][j] = 0;
}
}
//初始化黑棋和白棋数组的各个元素
for(int i = 0; i < 800; i++)
{
winlistp[i] = new struct();
winlistc[i] = new struct();
}
this.panel = panel;
g = (Graphics2D)this.panel.getGraphics();
num = 0;
//枚举各种情况(有4个大方向)
//分别计算人和计算机行的赢的情况
for(int i = 0; i < Row; i++)
{
for(int j = 0; j < 11; j++)
{
for(int k = j; k < j+5; k++)
{
winlistp[num].x[k-j] = i;
winlistp[num].y[k-j] = k;
winlistc[num].x[k-j] = i;
winlistc[num].y[k-j] = k;
winlistp[num].last = 0;
winlistp[num].wn[k-j] = 0;
winlistc[num].last = 0;
winlistc[num].wn[k-j] = 0;
}
num++;
}
}
//分别计算人和计算机列的赢的情况
for(int i = 0; i < Coloum; i++)
{
for(int j = 0; j < 11; j++)
{
for(int k = j; k < j+5; k++)
{
winlistp[num].x[k-j] = k;
winlistp[num].y[k-j] = i;
winlistc[num].x[k-j] = k;
winlistc[num].y[k-j] = i;
winlistp[num].last = 0;
winlistp[num].wn[k-j] = 0;
winlistc[num].last = 0;
winlistc[num].wn[k-j] = 0;
}
num++;
}
}
//分别计算人和计算机右上对角线赢的情况
for(int i = 1; i < 11; i++)
{
for(int x = i,y = 0; x < 11; x++,y++)
{
for(int kx = x,ky = y; kx < x+5; kx++,ky++)
{
winlistp[num].x[kx-x] = ky;
winlistp[num].y[kx-x] = kx;
winlistc[num].x[kx-x] = ky;
winlistc[num].y[kx-x] = kx;
winlistp[num].last = 0;
winlistp[num].wn[kx-x] = 0;
winlistc[num].last = 0;
winlistc[num].wn[kx-x] = 0;
}
num++;;
}
}
//分别计算人和计算机左下对角线的情况
for(int i = 1; i < 11; i++)
{
for(int y = i,x = 0; y < 11; y++,x++)
{
for(int kx = x,ky =y; ky < y+5; kx++,ky++)
{
winlistp[num].x[kx-x] = ky;
winlistp[num].y[kx-x] = kx;
winlistc[num].x[kx-x] = ky;
winlistc[num].y[kx-x] = kx;
winlistp[num].last = 0;
winlistp[num].wn[kx-x] = 0;
winlistc[num].last = 0;
winlistc[num].wn[kx-x] = 0;
}
num++;
}
}
因为代码很长但是不难,所以就贴出一部分,大家懂了之后剩下的代码自己补上绝对不是问题。看一下代码应该清楚,枚举所有赢的情况。分别是横着的方向和竖着的方向以及两个斜着的方向,这样就可以通过for循环直接全部初始化。score数组的初始化当然是0啦。
那么怎么更新winlist数组呢,很简单,假如更新白棋,那么就遍历棋盘找黑棋的位置,然后遍历winlist表,看看这个黑棋的坐标被哪些情况包含了,那么这个情况就无效了,贴出代码:
- //分别更新人和计算机的取胜表的情况
- public void update(int sitx,int sity)
- {
- if(array[sitx][sity]%2 == 0)
- {
- for(int i = 0; i < num; i++)
- {
- if(winlistp[i].last == 0)
- {
- for(int j = 0; j < 5; j++)
- {
- if((winlistp[i].x[j] == sitx) && (winlistp[i].y[j] == sity))
- {
- winlistp[i].last = 1;
- break;
- }
- }
- }
- if(winlistc[i].last == 0)
- {
- for(int j = 0; j < 5; j++)
- {
- if((winlistc[i].x[j] == sitx) && (winlistc[i].y[j] == sity))
- {
- winlistc[i].wn[j] = 1;
- break;
- }
- }
- }
- }
- }else
- {
- for(int i = 0; i < num; i++)
- {
- if(winlistc[i].last == 0)
- {
- for(int j = 0; j < 5; j++)
- {
- if((winlistc[i].x[j] == sitx) && (winlistc[i].y[j] == sity))
- {
- winlistc[i].last = 1;
- }
- }
- }
- if(winlistp[i].last == 0)
- {
- for(int j = 0; j < 5; j++)
- {
- if((winlistp[i].x[j] == sitx) && (winlistp[i].y[j] == sity))
- {
- winlistp[i].wn[j] = 1;
- }
- }
- }
- }
- }
//分别更新人和计算机的取胜表的情况
public void update(int sitx,int sity)
{
if(array[sitx][sity]%2 == 0)
{
for(int i = 0; i < num; i++)
{
if(winlistp[i].last == 0)
{
for(int j = 0; j < 5; j++)
{
if((winlistp[i].x[j] == sitx) && (winlistp[i].y[j] == sity))
{
winlistp[i].last = 1;
break;
}
}
}
if(winlistc[i].last == 0)
{
for(int j = 0; j < 5; j++)
{
if((winlistc[i].x[j] == sitx) && (winlistc[i].y[j] == sity))
{
winlistc[i].wn[j] = 1;
break;
}
}
}
}
}else
{
for(int i = 0; i < num; i++)
{
if(winlistc[i].last == 0)
{
for(int j = 0; j < 5; j++)
{
if((winlistc[i].x[j] == sitx) && (winlistc[i].y[j] == sity))
{
winlistc[i].last = 1;
}
}
}
if(winlistp[i].last == 0)
{
for(int j = 0; j < 5; j++)
{
if((winlistp[i].x[j] == sitx) && (winlistp[i].y[j] == sity))
{
winlistp[i].wn[j] = 1;
}
}
}
}
}
根据我说的再去看看代码就很简单了是不是?
那么接下来说一下怎么更新score数组,一个很显然的情况就是如果这个坐标被winlist里面的情况包含的越多那么下棋在这个位置的用处就越大。这是一个方面,为什么winlist还有一个wn数组呢,那就是判断是不是3个棋子连在一起了?4个棋子连在一起了??等等。因为是权值,每种情况都要有一定的数值,比如活三就给的大一点,活四直接认输就好了等等,权值怎么给确实很大的影响结果,自己到现在也没有找到很好的分配方式。这个怎么给大家自己决定吧,剩下的我就不多说了,每次更行权值列表,挑出最大的位置下棋就好了,不过因为把所有的情况都考虑了,之前有一个人说过,如果计算机先手不能保证赢但是能保证不输。哈哈哈不过我没有实现的这个程度,还是要加强的。那五子棋就讲到这里了。