Java五子棋Ai-权值法
Java五子棋Ai-权值法
一、 什么是权值法?
权值法顾名思义,即是权衡不同的情况并赋予不同的数值,这个数值即代表着这个情况的重要性。
例如,当我们进行五子棋对战的时候,我们每下一个棋子就要判断这个点横竖斜的情况,对于不同的情况我们有不同的应对措施。而怎样做出最佳的判断,就需要权衡不同点的情况,在对自己最有利的地方下棋。
而我们的这种思考过程其实就是一种权值法,现在我们只是通过代码将自己的下棋思路赋予电脑,让 电脑每下一步棋的时候能够按照我们对不同情况理解来找到最优解,从而实现Ai。
二、实现权值法Ai会面临的问题
1、如何表达棋盘上未下处的横竖斜的棋子情况?(类似01120)
首先,整个棋盘就是一个15*15的二维数组记为doublearray[15][15],当没有下棋的时候,整个二维数组都是0。
然后,每当黑棋下在棋盘上,则记录此位置并将0变为1。同理,白旗也是如此。
那么,整个棋盘就通过包含012三种元素的二维数组将棋局保存了。
接着就是创建另一个二维数组来保存每一个点的权值并记为weightarray[15][15]。
每当我们遍历到doublearray中的一个点的时候,我们首先判断是否是0,因为对于以下的地方我们没有计算权值的必要,我们只需要我们还可以下的地方的权值就行。
如果现在找到了doublearray中的doublarray[i][j],并且这个数为零,那么我们可以对这个地方进行权值的计算。但计算权值之前,我们要记录下这个地方的横竖斜的棋局情况。
那么我们可以通过从这个点出发,向左、向右、向上、向下、向左上、向右下、向右上、向左下来记录下不同方向的棋子分布情况并且保存,然后每一个方向的情况对应一个权值,最后将所有方向的权值相加就是这个点的权值了。
具体代码见(三)(1)
2、如何将未下处的横竖斜情况和权值联系起来?(Hashmap)
HashMap<String, Integer> map = new HashMap<String, Integer>()
这个就是创建了一个HashMap的对象,Java里面的HashMap类就有将字符串和对应的数字联系起来的功能,因此通过将代表棋局的012串和已经设置好的权值进行比较,即可实现权值和棋局的联系。
3、设置权值的思路是什么?
眠 活
一连 12("10") 10("20")
二连 112("30") 110("50")
三连 1112("60") 1110("100")
四连 11112("200") 11111("10")
上面的矩阵图只是一个思路,其中并没有涉及到具体的很多特殊情况和细节,其中眠和活分别指的是相连的同色棋的两端有没有敌方的棋堵住。我们可以根据这样的一个矩阵图思路清晰的写出自己对不同情况的认知,其中不仅仅需要考虑到单种情况的权值,还需要考虑到其中两个的权值和与其它的权值比较,例如当遍历到一个可下地方的时候发现对方存在两个活二,那么这个点就是十分危险的点了,必须要拦截,所以两个活三的和一定要比较大,才能让电脑发现这个情况的紧急。
三、具体代码的实现
1、实现计算机遍历整个棋盘,分析出每个可下点的横竖斜情况
public int[][] GetWeightarray(){
int weightarray[][] = new int[15][15];
//遍历代表棋盘棋局的二维数组
for(int i = 0; i < doublearray.length;i++){
for(int j = 0; j < doublearray[i].length;j++){
//对遍历到的地方判断是否存在棋子
if(doublearray[i][j] == 0){
int color = 0;
String code = "";
//向左求权值
//为何隔两个间隔就不需要继续记录了呢,因为所有相连情况没有两个间隔的
//相连情况有一连、二连、三连、四连
for(int k = j - 1; k >= 0; k--){
//如果此处没有棋子
if(doublearray[i][k] == 0){
if(doublearray[i][k] == doublearray[i][k+1])
break;
else
code += doublearray[i][k];
color = 0;
}
//如果此处有棋子
else if(doublearray[i][k] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[i][k];
code += doublearray[i][k];
}
//记录相连的同色棋子
else if(doublearray[i][k] == color){
code += doublearray[i][k];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[i][k] != color){
color = doublearray[i][k];
code += doublearray[i][k];
}
}
}
//得到向左的权值
if(map.get(code) != null)
weightarray[i][j] += map.get(code);
//初始化颜色以及编码
color = 0;
code = "";
//向右求权值
for(int k = j + 1; k < doublearray.length; k++){
//如果此处没有棋子
if(doublearray[i][k] == 0){
if(doublearray[i][k] == doublearray[i][k-1])
break;
else
code += doublearray[i][k];
color = 0;
}
//如果此处有棋子
else if(doublearray[i][k] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[i][k];
code += doublearray[i][k];
}
//记录相连的同色棋子
else if(doublearray[i][k] == color){
code += doublearray[i][k];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[i][k] != color){
color = doublearray[i][k];
code += doublearray[i][k];
break;
}
}
}
//得到向右的权值
if(map.get(code) != null)
weightarray[i][j] += map.get(code);
//初始化颜色以及编码
color = 0;
code = "";
//向上求权值
for(int k = i - 1; k >= 0; k--){
//如果此处没有棋子
if(doublearray[k][j] == 0){
if(doublearray[k][j] == doublearray[k+1][j])
break;
else
code += doublearray[k][j];
color = 0;
}
//如果此处有棋子
else if(doublearray[k][j] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[k][j];
code += doublearray[k][j];
}
//记录相连的同色棋子
else if(doublearray[k][j] == color){
code += doublearray[k][j];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[k][j] != color){
color = doublearray[k][j];
code += doublearray[k][j];
break;
}
}
}
//得到向上的权值
if(map.get(code) != null)
weightarray[i][j] += map.get(code);
//初始化颜色以及编码
color = 0;
code = "";
//向下求权值
for(int k = i + 1; k < doublearray.length; k++){
//如果此处没有棋子
if(doublearray[k][j] == 0){
if(doublearray[k][j] == doublearray[k-1][j])
break;
else
code += doublearray[k][j];
color = 0;
}
//如果此处有棋子
else if(doublearray[k][j] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[k][j];
code += doublearray[k][j];
}
//记录相连的同色棋子
else if(doublearray[k][j] == color){
code += doublearray[k][j];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[k][j] != color){
color = doublearray[k][j];
code += doublearray[k][j];
break;
}
}
}
if(map.get(code) != null)
//得到向下的权值
weightarray[i][j] += map.get(code);
//初始化颜色以及编码
color = 0;
code = "";
//向左上角求权值
for(int k = i - 1,m = j - 1; k >= 0 && m >= 0; k--,m--){
//如果此处没有棋子
if(doublearray[k][m] == 0){
if(doublearray[k][m] == doublearray[k+1][m+1])
break;
else
code += doublearray[k][m];
color = 0;
}
//如果此处有棋子
else if(doublearray[k][m] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[k][m];
code += doublearray[k][m];
}
//记录相连的同色棋子
else if(doublearray[k][m] == color){
code += doublearray[k][m];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[k][m] != color){
color = doublearray[k][m];
code += doublearray[k][m];
break;
}
}
}
if(map.get(code) != null)
//得到向左上的权值
weightarray[i][j] += map.get(code);
//初始化颜色以及编码
color = 0;
code = "";
//向右下角求权值
for(int k = i + 1,m = j + 1; k < doublearray.length && m < doublearray.length; k++,m++){
//如果此处没有棋子
if(doublearray[k][m] == 0){
if(doublearray[k][m] == doublearray[k-1][m-1])
break;
else
code += doublearray[k][m];
color = 0;
}
//如果此处有棋子
else if(doublearray[k][m] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[k][m];
code += doublearray[k][m];
}
//记录相连的同色棋子
else if(doublearray[k][m] == color){
code += doublearray[k][m];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[k][m] != color){
color = doublearray[k][m];
code += doublearray[k][m];
break;
}
}
}
if(map.get(code) != null)
//得到右下的权值
weightarray[i][j] += map.get(code);
//初始化颜色以及编码
color = 0;
code = "";
//向右上角求权值
for(int k = i - 1,m = j + 1; k >= 0 && m < doublearray.length; k--,m++){
//如果此处没有棋子
if(doublearray[k][m] == 0){
if(doublearray[k][m] == doublearray[k+1][m-1])
break;
else
code += doublearray[k][m];
color = 0;
}
//如果此处有棋子
else if(doublearray[k][m] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[k][m];
code += doublearray[k][m];
}
//记录相连的同色棋子
else if(doublearray[k][m] == color){
code += doublearray[k][m];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[k][m] != color){
color = doublearray[k][m];
code += doublearray[k][m];
break;
}
}
}
if(map.get(code) != null)
//得到右上的权值
weightarray[i][j] += map.get(code);
//初始化颜色以及编码
color = 0;
code = "";
//向左下角求权值
for(int k = i + 1,m = j - 1; k < doublearray.length && m >= 0; k++,m--){
//如果此处没有棋子
if(doublearray[k][m] == 0){
if(doublearray[k][m] == doublearray[k-1][m+1])
break;
else
code += doublearray[k][m];
color = 0;
}
//如果此处有棋子
else if(doublearray[k][m] != 0){
//记录第一个非零的棋子
if(color == 0){
color = doublearray[k][m];
code += doublearray[k][m];
}
//记录相连的同色棋子
else if(doublearray[k][m] == color){
code += doublearray[k][m];
}
//记录到了第一个和之前相连的相同棋子的不同颜色棋子
else if(doublearray[k][m] != color){
color = doublearray[k][m];
code += doublearray[k][m];
break;
}
}
}
if(map.get(code) != null)
//得到左下的权值
weightarray[i][j] += map.get(code);
//下面的不用管这些括号
}
}
}
return weightarray;
}
2、实现权值的设置
这里还有很多特殊情况没有考虑,仅供参考!
//一活
map.put("10", 30);
map.put("1020", 30);
map.put("1021", 30);
//一眠
map.put("12", 10);
//二活
map.put("110", 100);
map.put("11020", 100);
map.put("11021", 100);
map.put("1010", 100);
map.put("101012", 100);
map.put("101020",100);
map.put("101021", 100);
map.put("220", 150);
map.put("22010", 150);
map.put("22012", 150);
map.put("2020", 150);
map.put("202021", 150);
map.put("202010", 150);
map.put("202012", 150);
//二眠
map.put("112", 80);
map.put("1012", 80);
//三活
map.put("1110", 200);
map.put("111010", 200);
map.put("111012", 200);
map.put("10110", 200);
map.put("1011021", 200);
map.put("1011020", 200);
map.put("2220", 250);
map.put("222020", 250);
map.put("222021", 250);
map.put("20220", 250);
map.put("2022012", 250);
map.put("2022010", 250);
//三眠
map.put("1112", 90);
map.put("11012", 90);
map.put("10112", 90);
map.put("2221", 95);
map.put("22021", 95);
map.put("20221", 95);
//四活
map.put("22220", 2000);
map.put("2222010", 2000);
map.put("22220110", 2000);
map.put("2222012", 2000);
map.put("22220", 2000);
//四眠
map.put("11112", 1000);
map.put("22221", 2000);
map.put("22221", 2000);
3、实现根据权值找到最优解
public void drawAI(Graphics g){
g.setColor(Color.WHITE);
int x = 0,y = 0;
//找到权值表上权值最大的点
for(int i = 0; i < weightarray.length;i++){
for(int j = 0; j < weightarray[i].length;j++){
if(weightarray[i][j]>=weightarray[x][y]){
x = i;
y = j;
}
}
}
//在棋盘上画出这个棋子
g.fillOval(y*50+25, x*50+25, 50, 50);
shapearray[count++] = new Shape(Color.white, y*50+25, x*50+25);
//在doublearray棋局上面下一个白子
doublearray[x][y] = 2;
}