五子棋人机

来源:互联网 发布:帝国文明源码下载 编辑:程序博客网 时间:2024/05/07 18:22

 

权值法是个什么玩意呢?其实他是基于枚举的暴力的方法。之前说过五子棋的棋盘是15*15的。并且赢得情况是5个相同的棋子连成一块,那么大家就可以想到,我们能枚举出所有赢得情况,大约几千种,在我们看来这个数字比较大,但是对于一秒就可以处理数量级到8的计算机来说,小case是不是?那么有了这个理解我们就可以实现两个表,分别是机器和人的胜利表,也就是储存赢得情况的表,那么有了这个表,最终机器决定下棋是要通过权值判断的,至于权值怎么来,先不用管,我们也要用两个表分别储存机器和人的权值的表,贴出代码:

[java] view plain copy print?
  1.        private GoBangPanel panel; 
  2. private Graphics2D g; 
  3. private int num; 
  4. private Judge j; 
  5. public class struct 
  6.     int last; 
  7.     //计算x,y坐标 
  8.     int[] x = newint[5]; 
  9.     int[] y = newint[5]; 
  10.     //记录每个取胜表的棋子相连的情况 
  11.     int[] wn = newint[5]; 
  12. struct[] winlistp = new struct[800]; 
  13. struct[] winlistc = new struct[800]; 
  14. int[][] scorep = newint[Row][Coloum]; 
  15. int[][] scorec = new int[Row][Coloum]; 
说一下这几个东西的意思,scorep就是储存人的权值的表,scorec就是储存机器的权值的表。winlist同理是储存赢的情况的表。它是一个结构体数组,其中的x,y分别储存的是五个棋子相连的坐标,last表示这个情况是不是有效,说明一下,比如这是储存白棋赢的一种情况,五个棋子的坐标都已经储存了,但是在下棋的过程中,有一个黑色的棋子下在了这个五个坐标的其中一个,那么白棋就不可能通过这个情况赢了,那么就是无效的。那么大家就可以发现,随着下棋越来越多,无论是计算机还是人的winlist,无效的就会越来越多。

理解了winlist的作用,我们就来说一下score的作用,他就是储存权值的表,每次遍历查找出黑棋和白棋的权值的最大值,我们领白棋是计算机,黑棋是人,那么如果权值最大的就是白棋那么就体现了进攻,如果是黑棋就体现的防守,因为我们现在所说的是针对计算机的,因为我们编这套程序是为了让计算机知道怎么下棋。

怎么计算权值呢?我们先不跳着讲,先看看winlist怎么初始化和更新,初始化就是把所有赢的情况全部储存嘛,贴出代码:

[java] view plain copy print?
  1. public GoBangAn(GoBangPanel panel)  
  2.     //初始化黑棋和白棋各自的评分 
  3.     for(int i =0; i < Row; i++) 
  4.     { 
  5.         for(int j =0; j < Coloum; j++) 
  6.         { 
  7.             scorep[i][j] = 0
  8.             scorec[i][j] = 0
  9.         } 
  10.     } 
  11.     //初始化黑棋和白棋数组的各个元素 
  12.     for(int i =0; i < 800; i++) 
  13.     { 
  14.         winlistp[i] = new struct(); 
  15.         winlistc[i] = new struct(); 
  16.     } 
  17.     this.panel = panel; 
  18.     g = (Graphics2D)this.panel.getGraphics(); 
  19.     num = 0
  20.     //枚举各种情况(有4个大方向) 
  21.     //分别计算人和计算机行的赢的情况 
  22.     for(int i =0; i < Row; i++) 
  23.     { 
  24.         for(int j =0; j < 11; j++) 
  25.         { 
  26.             for(int k = j; k < j+5; k++) 
  27.             { 
  28.                 winlistp[num].x[k-j] = i; 
  29.                 winlistp[num].y[k-j] = k; 
  30.                 winlistc[num].x[k-j] = i; 
  31.                 winlistc[num].y[k-j] = k; 
  32.                 winlistp[num].last = 0
  33.                 winlistp[num].wn[k-j] = 0
  34.                 winlistc[num].last = 0
  35.                 winlistc[num].wn[k-j] = 0
  36.             } 
  37.             num++; 
  38.         } 
  39.     } 
  40.     //分别计算人和计算机列的赢的情况 
  41.     for(int i =0; i < Coloum; i++) 
  42.     { 
  43.         for(int j =0; j < 11; j++) 
  44.         { 
  45.             for(int k = j; k < j+5; k++) 
  46.             { 
  47.                 winlistp[num].x[k-j] = k; 
  48.                 winlistp[num].y[k-j] = i; 
  49.                 winlistc[num].x[k-j] = k; 
  50.                 winlistc[num].y[k-j] = i; 
  51.                 winlistp[num].last = 0
  52.                 winlistp[num].wn[k-j] = 0
  53.                 winlistc[num].last = 0
  54.                 winlistc[num].wn[k-j] = 0
  55.             } 
  56.             num++; 
  57.         } 
  58.     } 
  59.  
  60.     //分别计算人和计算机右上对角线赢的情况 
  61.     for(int i =1; i < 11; i++) 
  62.     { 
  63.         for(int x = i,y =0; x < 11; x++,y++) 
  64.         { 
  65.             for(int kx = x,ky = y; kx < x+5; kx++,ky++) 
  66.             { 
  67.                 winlistp[num].x[kx-x] = ky; 
  68.                 winlistp[num].y[kx-x] = kx; 
  69.                 winlistc[num].x[kx-x] = ky; 
  70.                 winlistc[num].y[kx-x] = kx; 
  71.                 winlistp[num].last = 0
  72.                 winlistp[num].wn[kx-x] = 0
  73.                 winlistc[num].last = 0
  74.                 winlistc[num].wn[kx-x] = 0
  75.             } 
  76.             num++;; 
  77.         } 
  78.     }  
  79.     //分别计算人和计算机左下对角线的情况 
  80.     for(int i =1; i < 11; i++) 
  81.     { 
  82.         for(int y = i,x =0; y < 11; y++,x++) 
  83.         { 
  84.             for(int kx = x,ky =y; ky < y+5; kx++,ky++) 
  85.             { 
  86.                 winlistp[num].x[kx-x] = ky; 
  87.                 winlistp[num].y[kx-x] = kx; 
  88.                 winlistc[num].x[kx-x] = ky; 
  89.                 winlistc[num].y[kx-x] = kx; 
  90.                 winlistp[num].last = 0
  91.                 winlistp[num].wn[kx-x] = 0
  92.                 winlistc[num].last = 0
  93.                 winlistc[num].wn[kx-x] = 0
  94.             } 
  95.             num++; 
  96.         } 
  97.     } 

因为代码很长但是不难,所以就贴出一部分,大家懂了之后剩下的代码自己补上绝对不是问题。看一下代码应该清楚,枚举所有赢的情况。分别是横着的方向和竖着的方向以及两个斜着的方向,这样就可以通过for循环直接全部初始化。score数组的初始化当然是0啦。

那么怎么更新winlist数组呢,很简单,假如更新白棋,那么就遍历棋盘找黑棋的位置,然后遍历winlist表,看看这个黑棋的坐标被哪些情况包含了,那么这个情况就无效了,贴出代码:

[java] view plain copy print?
  1. //分别更新人和计算机的取胜表的情况 
  2.    public void update(int sitx,int sity) 
  3.    { 
  4.     if(array[sitx][sity]%2 ==0
  5.     { 
  6.         for(int i =0; i < num; i++) 
  7.         { 
  8.             if(winlistp[i].last == 0
  9.             { 
  10.                 for(int j =0; j < 5; j++) 
  11.                 { 
  12.                     if((winlistp[i].x[j] == sitx) && (winlistp[i].y[j] == sity)) 
  13.                     { 
  14.                         winlistp[i].last = 1
  15.                         break
  16.                     }                            
  17.                      
  18.                 } 
  19.             } 
  20.             if(winlistc[i].last == 0
  21.             { 
  22.                 for(int j =0; j < 5; j++) 
  23.                 { 
  24.                     if((winlistc[i].x[j] == sitx) && (winlistc[i].y[j] == sity)) 
  25.                     { 
  26.                         winlistc[i].wn[j] = 1
  27.                         break
  28.                     } 
  29.                 } 
  30.             } 
  31.         } 
  32.     }else  
  33.     { 
  34.         for(int i =0; i < num; i++) 
  35.         { 
  36.             if(winlistc[i].last == 0
  37.             { 
  38.                 for(int j =0; j < 5; j++) 
  39.                 { 
  40.                     if((winlistc[i].x[j] == sitx) && (winlistc[i].y[j] == sity)) 
  41.                     { 
  42.                         winlistc[i].last = 1
  43.                     } 
  44.                 } 
  45.             } 
  46.             if(winlistp[i].last == 0
  47.             { 
  48.                 for(int j =0; j < 5; j++) 
  49.                 { 
  50.                     if((winlistp[i].x[j] == sitx) && (winlistp[i].y[j] == sity)) 
  51.                     { 
  52.                         winlistp[i].wn[j] = 1
  53.                     } 
  54.                 } 
  55.             } 
  56.         } 
  57.     } 

根据我说的再去看看代码就很简单了是不是?

那么接下来说一下怎么更新score数组,一个很显然的情况就是如果这个坐标被winlist里面的情况包含的越多那么下棋在这个位置的用处就越大。这是一个方面,为什么winlist还有一个wn数组呢,那就是判断是不是3个棋子连在一起了?4个棋子连在一起了??等等。因为是权值,每种情况都要有一定的数值,比如活三就给的大一点,活四直接认输就好了等等,权值怎么给确实很大的影响结果,自己到现在也没有找到很好的分配方式。这个怎么给大家自己决定吧,剩下的我就不多说了,每次更行权值列表,挑出最大的位置下棋就好了,不过因为把所有的情况都考虑了,之前有一个人说过,如果计算机先手不能保证赢但是能保证不输。哈哈哈不过我没有实现的这个程度,还是要加强的。那五子棋就讲到这里了。