超萌新级的Java项目实践——五子棋(三)

这部分内容需要一定的数据结构的基础,这一部分讲解一下五子棋的AI算法的思路和解决方案。

首先讲一下算法的概念:解题方案的准确而完整的描述简单点说,就是解决问题用的方法的描述。

例如:比较经典的背包问题,即将一堆物品装进背包,求装入背包的物品最高价值的值解决这种问题,有很多很多方法,比如把所有的方案都试试(穷举法) ,比如列转移方程使用动态规划等等,这些就是算法。

一般五子棋使用的是权值算法(有的也叫评估函数;当然,想试试强化学习和深度神经网络的就无视吧),及对于一个点给予一定的权值分析,选择一个最佳的点落子。

比方说下列情况:

超萌新级的Java项目实践——五子棋(三)_第1张图片

超萌新级的Java项目实践——五子棋(三)_第2张图片

这个情况下,白子在1位置的总权值和2位置的总权值是完全不一样的,而且很明显,1位置总权值是远远高于2位置的,原因就不详细解释了。

因此,我们只要计算出每一个可以下的位置的权值,对其进行计算,选出一个最高的就可以初步满足AI的要求了,即至少会应付对手下棋。

然后问题来了,权值怎么计算。

首先,电脑是不会下五子棋的,它不可能知道什么地方是什么情况,更不要说判断最优的点了。所以,需要人为输入棋子形态的权值。最好用字符串存棋子形态,这里我用“1”表示黑子用“0”表示白子,例如:“1”(一个黑子)设置为:20,“11”(两个黑子)设置为:100,“111”(三个黑子)设置为:2000,“1111”(四个黑子)设置为:50000之类的。根据不同的下棋方式,可以使用不同的权值,可以说,每个人的权值是独一无二的,完全看对于五子棋的理解。

然后,对于每一个可以下棋的点,通过扫描周围的棋子形态判断权值。

例如下图:

超萌新级的Java项目实践——五子棋(三)_第3张图片

超萌新级的Java项目实践——五子棋(三)_第4张图片

1位置连接了下列棋子形态:“10”,“0”,“01”,“101”,“0”。

2位置连接了下列棋子形态:“1110”,“10”。

将上述棋子形态的权值相加即为各个点的总权值。

 

顺便提一下,可以在判断的时候手动加更大的权值。例如著名的两三子的情况可以适当的额外权值来提高“AI智商”。

这里提及一个数据结构:HashMap(基于哈希表的地图接口的实现),由于棋子情况是比较多的,对于所有点的周围情况检索起来是比较麻烦的,会花很多时间,于是这里使用HashMap来储存。(也可以直接用数组等存)

首先预先将所有情况打好,创建一个接口。

 

public interface Gobang_window {
	public static final Integer onePiece = 20;
	public static final Integer twoPieces = 100;
	public static final Integer threePieces = 2000;
	public static final Integer fourPieces = 50000;
	public static final Integer Piece2 = 10;
	public static final Integer Piece12 = 10;
	public static final Integer Piece22 = 50;
	public static final Integer Piece2221 = 700;
	public static final Integer Piece222 = 1000;
	public static final Integer piecedouble3_1= 2000;
	public static final Integer piecedouble3_2= 2000;
	public static final Integer Piece22221= 10000;
	public static final Integer piececenter5or4= 20000;
	
}

 

然后,预先将数值存入哈希表。

 

	private HashMap hm = new HashMap<>();

	private void setPower() {
		hm.put("1", onePiece);
		hm.put("11", twoPieces);
		hm.put("111", threePieces);
		hm.put("1111", fourPieces);
		hm.put("2", Piece2);
		hm.put("12", Piece12);
		hm.put("22", Piece22);
		hm.put("222", Piece222);
		hm.put("22221", Piece22221);
	}

 

然后在玩家下一个棋子的时候,对棋盘的可用点进行权值估算,并确定下棋的位置,然后根据位置绘制棋子。

 

		int s = 0, d = 0;
		String code;//用于保存棋子的形态

		for (int i = 0; i < RC; i++)//初始化权值表
			for (int j = 0; j < RC; j++)
				valueTable[i][j] = 0;
		
		for (int i = 0; i < RC; i++)
			for (int j = 0; j < RC; j++)
				//对于每个位置进行各个方向的判断
				if (piece[i][j] == 0) {

					code = "";//棋子的形态重置
					c = 0;//相同的棋子判断标志变量
					for (int k = i + 1; k < RC; k++)
						if (piece[k][j] == 0)
							break;
						else if (c == 0) {
							c = piece[k][j];
							code += piece[k][j];
						} else if (piece[k][j] == c)
							code += piece[k][j];
						else {
							code += piece[k][j];
							break;
						}
					if (hm.get(code) != null) {//特殊情况要额外计数
						valueTable[i][j] += hm.get(code);
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}
					code = "";
					c = 0;

					for (int k = j + 1; k < RC; k++)
						if (piece[i][k] == 0)
							break;
						else if (c == 0) {
							c = piece[i][k];
							code += piece[i][k];
						} else if (piece[i][k] == c)
							code += piece[i][k];
						else {
							code += piece[i][k];
							break;
						}
					if (hm.get(code) != null) {
						valueTable[i][j] += hm.get(code);// ��
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}
					code = "";
					c = 0;

					for (int k = i - 1; k > 0; k--)
						if (piece[k][j] == 0)
							break;
						else if (c == 0) {
							c = piece[k][j];
							code += piece[k][j];
						} else if (piece[k][j] == c)
							code += piece[k][j];
						else {
							code += piece[k][j];
							break;
						}
					if (hm.get(code) != null) {
						valueTable[i][j] += hm.get(code);
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}

					code = "";
					c = 0;

					for (int k = j - 1; k > 0; k--)
						if (piece[i][k] == 0)
							break;
						else if (c == 0) {
							c = piece[i][k];
							code += piece[i][k];
						} else if (piece[i][k] == c)
							code += piece[i][k];
						else {
							code += piece[i][k];
							break;
						}
					if (hm.get(code) != null) {
						valueTable[i][j] += hm.get(code);
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}

					code = "";
					c = 0;

					for (int k = 1; (i + k < RC) && (j + k < RC); k++)
						if (piece[i + k][j + k] == 0)
							break;
						else if (c == 0) {
							c = piece[i + k][j + k];
							code += piece[i + k][j + k];
						} else if (piece[i + k][j + k] == c)
							code += piece[i + k][j + k];
						else {
							code += piece[i + k][j + k];
							break;
						}
					if (hm.get(code) != null) {
						valueTable[i][j] += hm.get(code);
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}

					code = "";
					c = 0;

					for (int k = 1; (i - k > 0) && (j - k > 0); k++)
						if (piece[i - k][j - k] == 0)
							break;
						else if (c == 0) {
							c = piece[i - k][j - k];
							code += piece[i - k][j - k];
						} else if (piece[i - k][j - k] == c)
							code += piece[i - k][j - k];
						else {
							code += piece[i - k][j - k];
							break;
						}
					if (hm.get(code) != null) {
						valueTable[i][j] += hm.get(code);
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}

					code = "";
					c = 0;

					for (int k = 1; (i + k < RC) && (j - k > 0); k++)
						if (piece[i + k][j - k] == 0)
							break;
						else if (c == 0) {
							c = piece[i + k][j - k];
							code += piece[i + k][j - k];
						} else if (piece[i + k][j - k] == c)
							code += piece[i + k][j - k];
						else {
							code += piece[i + k][j - k];
							break;
						}
					if (hm.get(code) != null) {
						valueTable[i][j] += hm.get(code);
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}

					code = "";
					c = 0;

					for (int k = 1; (i - k > 0) && (j + k < RC); k++)
						if (piece[i - k][j + k] == 0)
							break;
						else if (c == 0) {
							c = piece[i - k][j + k];
							code += piece[i - k][j + k];
						} else if (piece[i - k][j + k] == c)
							code += piece[i - k][j + k];
						else {
							code += piece[i - k][j + k];
							break;
						}
					if (hm.get(code) != null) {
						valueTable[i][j] += hm.get(code);
						if (code.equals("11") || (code.equals("111")) || code.equals("1111"))
							s++;
						if (code.equals("22") || (code.equals("222")) || code.equals("2222"))
							d++;
					}
					//对特殊棋子形态进行判断
					if (s >= 2)
						valueTable[i][j] += piecedouble3_1;
					if (d >= 2)
						valueTable[i][j] += piecedouble3_2;
					s = 0;
					d = 0;
					piece[i][j] = 1;
					if ((Checkborad(5))||(Checkborad(4)))
						valueTable[i][j] += piececenter5or4;
					piece[i][j] = 0;
					
					
				}

		code = "";
		c = 0;

		for (int i = 0; i < RC; i++)
			for (int j = 0; j < RC; j++)
				if (max < valueTable[i][j]) {
					max = valueTable[i][j];
					x = i;
					y = j;
				}
		max = -1;

 

这样就初步完成五子棋的AI。但是这样的AI是比较“傻”的,判断能力有限。如果需要更加“聪明”的AI的话,可以使用博弈树等更好的(当然原理也不简单),这里就不会再说明了。

致此,五子棋的项目实现的差不多了,虽然大部分的内容是在讲理论和实现方法,代码稍微少了点,但是,跟着这些思路自己手动一步一步写的话会大大提升编程能力。

附上博弈树的源码和五子棋接口

源代码:https://github.com/IamA1536/GobangTeamWork.git

简易接口:https://github.com/IamA1536/GobangWork.git

你可能感兴趣的:(简单项目)