之前搞了个五子棋的算法,我在写好算法之前参考网上很多的五子棋算法像是博弈树算法这些的一开始写起来不太好理解,今天就分享一个简单的五子棋算法——评分法。
原理:
评分法,有的也叫评分表法,不管叫什么,名字不重要,重要的是思路。我们写棋总是下在对自己优势最大的位置上,但是可以下的位置有很多啊,电脑怎么知道下在哪里最好啊?评分法就是给每个可下的位置评分,电脑通过分数高低来判断下在哪个位置。
方法:
评分法的原理是一致的,但方法各种各样,我的方法是这样的(仅供参考)。
1、棋型分析
五子棋的棋型很多很多,在这里不详细介绍,只介绍一些简单的棋型。
连五棋型:有五个相同颜色的棋子连成一线;
活四棋型:有四个相同颜色的棋子连成一线且两端均可下棋;
眠四棋型:有四个相同颜色的棋子连成一线但两端只有一端可下棋;
类似的还有活三、眠三、活二等棋型。
下图,便是我以1为黑棋,2为白棋,0为空位置排列组合所得到的棋型示例,
图一是以所选位置为中心判断的,
图二则是以所选位置为起点判断的,
我们可以把这些棋型分别用一个二维数组来保存,方便与所选位置可组成的棋型比较。
图一
图二
2、根据棋型打分(可以自己设置评分标准)
算法代码怎么写:
每次下棋前扫描棋盘上所有可以下的位置,把它们保存起来,然后用循环得出每一个位置八个方向可组成的棋型,再根据评分标准给位置评分,再判断最高的评分(要考虑有多个的情况)。
1、创建position类作为下棋的位置,代码如下:
package chess;
public class position {
private int x;//棋子位置横坐标
private int y;//棋子位置纵坐标
//构造方法
public position(int x,int y) {
this.x = x;
this.y = y;
}
//通过set和get方法赋值和访问
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setY(int y) {
this.y = y;
}
public int getY() {
return y;
}
}
2、创建chess类
这里是核心,其中包含五子棋游戏初始化、下棋、棋盘情况判断、评分算法等方法,详情见下面代码:
package chess;
import java.util.ArrayList;
import java.util.Arrays;
public class chess {
private int size;//棋盘大小
private int[][] chessboard;//棋盘
private int color=1;//1为黑子,2为白子
/*
* 以所选位置为中心的各种棋型,有连5,活4,眠4,活3,眠3,活2,眠2;
* 连5:1111,2222;
* 活4:1110,0111,2220,0222;
* 眠4:1112,2111,2221,1222;
* 活3:1100,0011,0110,2200,0022,0220;
* 眠3:1120,1121,0211,1211,2110,0112,2210,2211,0122,2122,1220,0221;
* 活2:0100,0010,0200,0020;
* 眠2:2100,0012,1200,0021;
*/
private int[][] stiuation = { { 1, 1, 1, 1 }, { 2, 2, 2, 2 }, { 1, 1, 1, 0 }, { 0, 1, 1, 1 }, { 2, 2, 2, 0 },
{ 0, 2, 2, 2 }, { 1, 1, 1, 2 }, { 2, 1, 1, 1 }, { 2, 2, 2, 1 }, { 1, 2, 2, 2 }, { 1, 1, 0, 0 },
{ 0, 0, 1, 1 }, { 0, 1, 1, 0 }, { 2, 2, 0, 0 }, { 0, 0, 2, 2 }, { 0, 2, 2, 0 }, { 1, 1, 2, 0 },
{ 1, 1, 2, 1 }, { 0, 2, 1, 1 }, { 1, 2, 1, 1 }, { 2, 1, 1, 0 }, { 0, 1, 1, 2 }, { 2, 2, 1, 0 },
{ 2, 2, 1, 1 }, { 0, 1, 2, 2 }, { 2, 1, 2, 2 }, { 1, 2, 2, 0 }, { 0, 2, 2, 1 }, { 0, 1, 0, 0 },
{ 0, 0, 1, 0 }, { 0, 2, 0, 0 }, { 0, 0, 2, 0 }, { 2, 1, 0, 0 }, { 0, 0, 1, 2 }, { 1, 2, 0, 0 },
{ 0, 0, 2, 1 } };
/*
* 以所选位置为起点的棋型,连5,活4,眠4,活3,眠3;
* 连5:1111,2222;
* 活4:1110,2220;
* 眠4:1112,2221;
* 活3:1100,1101,1102,2200,2201,2202;
* 眠3:1120,1121,1122,2210,2211,2122;
*/
private int[][] stiuation2 = { { 1, 1, 1, 1 }, { 2, 2, 2, 2 }, { 1, 1, 1, 0 }, { 2, 2, 2, 0 }, { 1, 1, 1, 2 },
{ 2, 2, 2, 1 }, { 1, 1, 0, 0 }, { 1, 1, 0, 1 }, { 1, 1, 0, 2 }, { 2, 2, 0, 0 }, { 2, 2, 0, 1 },
{ 2, 2, 0, 2 }, { 1, 1, 2, 0 }, { 1, 1, 2, 1 }, { 1, 1, 2, 2 }, { 2, 2, 1, 0 }, { 2, 2, 1, 1 },
{ 2, 2, 1, 2 } };
//设置游戏,size为棋盘大小,color为电脑所选择棋子的颜色
public void setGame(int size,int color) {
this.size=size;
this.color=color;
this.chessboard=new int[this.size][this.size];
//初始化棋盘
for (int i = 0; i < chessboard.length; i++) {
for (int j = 0; j < chessboard.length; j++) {
this.chessboard[i][j]=0;
}
}
}
//下棋,x和y分别是人下棋的横坐标和纵坐标,电脑根据坐标来判断下棋位置下棋
public void getMove(int x,int y) {
//人下棋
if(this.color==1)
this.chessboard[x][y]=2;
else
this.chessboard[x][y]=1;
System.out.println("人下棋:("+x+","+y+")");
print();
if(isWin(x,y)) {
System.out.println("人获胜");
System.exit(0);
}
else if(isFull()) {
System.out.println("平局");
System.exit(0);
}
//存储所有未下棋的位置
ArrayList<position> positions=new ArrayList<>();
//存储每个位置的评分
ArrayList<Integer> numbers=new ArrayList<>();
for (int i = 0; i < chessboard.length; i++) {
for (int j = 0; j < chessboard.length; j++) {
positions.add(new position(i,j));
numbers.add(numJudge(i,j));
}
}
int index = 0;// 最高评分在numbers中的下标
for (int i = 0; i < numbers.size(); i++) {
if (numbers.get(i) > numbers.get(index)) {
index = i;
}
}
// 储存所有最高评分在numbers中的下标(当最高评分有多个时)
ArrayList<Integer> max = new ArrayList<>();
for (int i = 0; i < numbers.size(); i++) {
if (numbers.get(i) == numbers.get(index)) {
max.add(i);
}
}
//电脑下棋
if(max.size()==1){
System.out.println("电脑下棋:("+positions.get(max.get(0)).getX()+","+positions.get(max.get(0)).getY()+")");
if(this.color==1)
this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=1;
else
this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=2;
if(isWin(positions.get(max.get(0)).getX(), positions.get(max.get(0)).getY())) {
System.out.println("平局");
System.exit(0);
}
}
else{
index = (int) (Math.random() * max.size());
System.out.println("电脑下棋:("+positions.get(max.get(index)).getX()+","+positions.get(max.get(index)).getY()+")");
if(this.color==1)
this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=1;
else
this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=2;
if(isWin(positions.get(max.get(0)).getX(), positions.get(max.get(0)).getY())) {
System.out.println("电脑获胜");
System.exit(0);
}
else if(isFull()) {
System.out.println("平局");
System.exit(0);
}
}
print();
}
//评分算法
private int numJudge(int x, int y) {
int sum = 0;
sum += mark1(x, y);// 左右方向
sum += mark2(x, y);// 上下方向
sum += mark3(x, y);// 主对角线方向
sum += mark4(x, y);// 副对角线方向
sum += mark5(x, y);// 八个方向
return sum;
}
// 给分标准1,根据棋型给分
private int calculate(int[] position) {
int sum = 0;
int index = -1;
for (int i = 0; i < this.stiuation.length; i++) {
if (Arrays.equals(position, this.stiuation[i])) {
index = i;
if (i < 2) {
sum += 100000;// 连5棋型
if (color == 1) {
if (i == 0) {
sum += 10000000;// 我方胜利加分
}
} else {
if (i == 1) {
sum += 10000000;// 我方胜利加分
}
}
} else if (i < 6) {
sum += 10000;// 活4棋型
if(color==1){
if(i<=3){
sum+=50000;//我方先连4子加分
}
}else{
if(i>3){
sum+=50000;//我方先连4子加分
}
}
} else if (i < 10) {
sum += 5000;// 眠4棋型
} else if (i < 16) {
sum += 1000;// 活3棋型
if(color==1){
if(i<=12){
sum+=3000;//我方先连3子加分
}
}else{
if(i>12){
sum+=3000;//我方先连3子加分
}
}
} else if (i < 28) {
sum += 500;// 眠3棋型
} else if (i < 32) {
sum += 100;// 活2棋型
} else {
sum += 50;// 眠2棋型
}
}
}
if (index < 0) {
sum += 1;// 其它情况
}
return sum;
}
// 评分标准2
private int calculate2(int[] position) {
int sum = 0;
for (int i = 0; i < this.stiuation2.length; i++) {
if (Arrays.equals(position, this.stiuation2[i])) {
if (i < 2) {
sum += 100000;// 连5棋型
if (color == 1) {
if (i == 0) {
sum += 10000000;// 一子定胜负加分
}
} else {
if (i == 1) {
sum += 10000000;// 一子定胜负加分
}
}
} else if (i < 4) {
sum += 10000;// 活4棋型
if(color==1){
if(i==2){
sum+=50000;//我方先连4子加分
}
}else{
if(i==3){
sum+=50000;//我方先连4子加分
}
}
} else if (i < 6) {
sum += 5000;// 眠4棋型
} else if (i < 12) {
sum += 1000;// 活3棋型
if(color==1){
if(i<=8){
sum+=3000;//我方先连3子加分
}
}else{
if(i>8){
sum+=3000;//我方先连3子加分
}
}
} else {
sum += 500;// 眠3棋型
}
}
}
return sum;
}
// 左右方向评分
private int mark1(int x, int y) {
int[] position = new int[4];
if (x - 2 >= 0) {
position[0] = this.chessboard[x - 2][y];
} else {
position[0] = 0;
}
if (x - 1 >= 0) {
position[1] = this.chessboard[x - 1][y];
} else {
position[1] = 0;
}
if (x + 1 < this.chessboard.length) {
position[2] = this.chessboard[x + 1][y];
} else {
position[2] = 0;
}
if (x + 2 < this.chessboard.length) {
position[3] = this.chessboard[x + 2][y];
} else {
position[3] = 0;
}
int sum = 0;
sum += calculate(position);
return sum;
}
// 上下方向评分
private int mark2(int x, int y) {
int[] position = new int[4];
if (y - 2 >= 0) {
position[0] = this.chessboard[x][y - 2];
} else {
position[0] = 0;
}
if (y - 1 >= 0) {
position[1] = this.chessboard[x][y - 1];
} else {
position[1] = 0;
}
if (y + 1 < this.chessboard.length) {
position[2] = this.chessboard[x][y + 1];
} else {
position[2] = 0;
}
if (y + 2 < this.chessboard.length) {
position[3] = this.chessboard[x][y + 2];
} else {
position[3] = 0;
}
int sum = 0;
sum += calculate(position);
return sum;
}
// 主对角线方向评分
private int mark3(int x, int y) {
int[] position = new int[4];
if (x - 2 >= 0 && y - 2 >= 0) {
position[0] = this.chessboard[x - 2][y - 2];
} else {
position[0] = 0;
}
if (x - 1 >= 0 && y - 1 >= 0) {
position[1] = this.chessboard[x - 1][y - 1];
} else {
position[1] = 0;
}
if (x + 1 < this.chessboard.length && y + 1 < this.chessboard.length) {
position[2] = this.chessboard[x + 1][y + 1];
} else {
position[2] = 0;
}
if (x + 2 < this.chessboard.length && y + 2 < this.chessboard.length) {
position[3] = this.chessboard[x + 2][y + 2];
} else {
position[3] = 0;
}
int sum = 0;
sum += calculate(position);
return sum;
}
// 副对角线方向评分
private int mark4(int x, int y) {
int[] position = new int[4];
if (x - 2 >= 0 && y + 2 < this.chessboard.length) {
position[0] = this.chessboard[x - 2][y + 2];
} else {
position[0] = 0;
}
if (x - 1 >= 0 && y + 1 < this.chessboard.length) {
position[1] = this.chessboard[x - 1][y + 1];
} else {
position[1] = 0;
}
if (x + 1 < this.chessboard.length && y - 1 >= 0) {
position[2] = this.chessboard[x + 1][y - 1];
} else {
position[2] = 0;
}
if (x + 2 < this.chessboard.length && y - 2 >= 0) {
position[3] = this.chessboard[x + 2][y - 2];
} else {
position[3] = 0;
}
int sum = 0;
sum += calculate(position);
return sum;
}
// 八个方向评分
private int mark5(int x, int y) {
int sum = 0;
int[] position = new int[4];
// 上方向
if (x - 1 >= 0) {
position[0] = this.chessboard[x - 1][y];
} else {
position[0] = 0;
}
if (x - 2 >= 0) {
position[1] = this.chessboard[x - 2][y];
} else {
position[1] = 0;
}
if (x - 3 >= 0) {
position[2] = this.chessboard[x - 3][y];
} else {
position[2] = 0;
}
if (x - 4 >= 0) {
position[3] = this.chessboard[x - 4][y];
} else {
position[3] = 0;
}
sum += calculate2(position);
// 下方向
if (x + 1 < this.chessboard.length) {
position[0] = this.chessboard[x + 1][y];
} else {
position[0] = 0;
}
if (x + 2 < this.chessboard.length) {
position[1] = this.chessboard[x + 2][y];
} else {
position[1] = 0;
}
if (x + 3 < this.chessboard.length) {
position[2] = this.chessboard[x + 3][y];
} else {
position[2] = 0;
}
if (x + 4 < this.chessboard.length) {
position[3] = this.chessboard[x + 4][y];
} else {
position[3] = 0;
}
sum += calculate2(position);
// 左方向
if (y - 1 >= 0) {
position[0] = this.chessboard[x][y - 1];
} else {
position[0] = 0;
}
if (y - 2 >= 0) {
position[1] = this.chessboard[x][y - 2];
} else {
position[1] = 0;
}
if (y - 3 >= 0) {
position[2] = this.chessboard[x][y - 3];
} else {
position[2] = 0;
}
if (y - 4 >= 0) {
position[3] = this.chessboard[x][y - 4];
} else {
position[3] = 0;
}
sum += calculate2(position);
// 右方向
if (y + 1 < this.chessboard.length) {
position[0] = this.chessboard[x][y + 1];
} else {
position[0] = 0;
}
if (y + 2 < this.chessboard.length) {
position[1] = this.chessboard[x][y + 2];
} else {
position[1] = 0;
}
if (y + 3 < this.chessboard.length) {
position[2] = this.chessboard[x][y + 3];
} else {
position[2] = 0;
}
if (y + 4 < this.chessboard.length) {
position[3] = this.chessboard[x][y + 4];
} else {
position[3] = 0;
}
sum += calculate2(position);
// 左斜上方向
if (x - 1 >= 0 && y - 1 >= 0) {
position[0] = this.chessboard[x - 1][y - 1];
} else {
position[0] = 0;
}
if (x - 2 >= 0 && y - 2 >= 0) {
position[1] = this.chessboard[x - 2][y - 2];
} else {
position[1] = 0;
}
if (x - 3 >= 0 && y - 3 >= 0) {
position[2] = this.chessboard[x - 3][y - 3];
} else {
position[2] = 0;
}
if (x - 4 >= 0 && y - 4 >= 0) {
position[3] = this.chessboard[x - 4][y - 4];
} else {
position[3] = 0;
}
sum += calculate2(position);
// 左斜下方向
if (x + 1 < this.chessboard.length && y + 1 < this.chessboard.length) {
position[0] = this.chessboard[x + 1][y + 1];
} else {
position[0] = 0;
}
if (x + 2 < this.chessboard.length && y + 2 < this.chessboard.length) {
position[1] = this.chessboard[x + 2][y + 2];
} else {
position[1] = 0;
}
if (x + 3 < this.chessboard.length && y + 3 < this.chessboard.length) {
position[2] = this.chessboard[x + 3][y + 3];
} else {
position[2] = 0;
}
if (x + 4 < this.chessboard.length && y + 4 < this.chessboard.length) {
position[3] = this.chessboard[x + 4][y + 4];
} else {
position[3] = 0;
}
sum += calculate2(position);
// 右斜上方向
if (x - 1 >= 0 && y + 1 < this.chessboard.length) {
position[0] = this.chessboard[x - 1][y + 1];
} else {
position[0] = 0;
}
if (x - 2 >= 0 && y + 2 < this.chessboard.length) {
position[1] = this.chessboard[x - 2][y + 2];
} else {
position[1] = 0;
}
if (x - 3 >= 0 && y + 3 < this.chessboard.length) {
position[2] = this.chessboard[x - 3][y + 3];
} else {
position[2] = 0;
}
if (x - 4 >= 0 && y + 4 < this.chessboard.length) {
position[3] = this.chessboard[x - 4][y + 4];
} else {
position[3] = 0;
}
sum += calculate2(position);
// 右斜下
if (x + 1 < this.chessboard.length && y - 1 >= 0) {
position[0] = this.chessboard[x + 1][y - 1];
} else {
position[0] = 0;
}
if (x + 2 < this.chessboard.length && y - 2 >= 0) {
position[1] = this.chessboard[x + 2][y - 2];
} else {
position[1] = 0;
}
if (x + 3 < this.chessboard.length && y - 3 >= 0) {
position[2] = this.chessboard[x + 3][y - 3];
} else {
position[2] = 0;
}
if (x + 4 < this.chessboard.length && y - 4 >= 0) {
position[3] = this.chessboard[x + 4][y - 4];
} else {
position[3] = 0;
}
sum += calculate2(position);
return sum;
}
//打印棋盘
public void print() {
for (int i = 0; i < chessboard.length; i++) {
for (int j = 0; j < chessboard.length; j++) {
System.out.print(this.chessboard[i][j]+" ");
}
System.out.println();
}
}
//判断输赢
public Boolean isWin(int x,int y) {
return false;
}
//判断棋盘是否满了
public Boolean isFull() {
int judge=1;
for (int i = 0; i < chessboard.length; i++) {
for (int j = 0; j < chessboard.length; j++) {
if(this.chessboard[i][j]==0) {
judge=0;
}
}
}
return judge==1?true:false;
}
}
3、创建测试类,代码如下:
package chess;
import java.util.Scanner;
public class chessTest {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
chess ch=new chess();
System.out.println("请输入棋盘大小n(n行n列):");
int size=sc.nextInt();
ch.setGame(size,2);
ch.print();
while (true) {
System.out.println("请输入你想下的位置");
int x=sc.nextInt();
int y=sc.nextInt();
ch.getMove(x, y);
}
}
}
测试结果图:
算法大概就是这样子了,算法很简单,效果差强人意吧。
PS:后面发现chess类里面有个判断胜负的方法没写,也懒得补上了(就循环判断,很简单,就是耗点时间),希望大家不会介意。