前面介绍了监听机制,窗体使用,重绘机制,画笔使用,对象传递等等概念,接下来我们来谈一点算法的东西,相信大家都玩过五子棋这个游戏吧,在不包括先手禁手等复杂规则之下,我们只考虑最简单的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(); //将下棋的权利进行翻转
}
讲到这里,大概就是五子棋全职算法的基本实现流程了,大致思路是对棋盘上的各种局势的一种主观性判断,将主观危险的判断用数字来代替从而实现优先选择的机制。