基于python的AI五子棋实现(极大极小值搜索和alpha beta剪枝)

1.极大极小值搜索介绍

        人机博弈是人工智能的重要分支,人们在这一领域探索的过程中产生了大量的研究成果,而极小化极大算法(minimax)是其中最基础的算法,它由Shannon在1950年正式提出。

        Minimax算法 又名极小化极大算法,是一种找出失败的最大可能性中的最小值的算法(即最小化对手的最大得益)。通常以递归形式来实现。极大极小搜索策略一般都是使用在一些博弈类的游戏之中。这样策略本质上使用的是深度搜索策略,所以一般可以使用递归的方法来实现。在搜索过程中,对本方有利的搜索点上应该取极大值,而对本方不利的搜索点上应该取极小值。极小值和极大值都是相对而言的。在搜索过程中需要合理的控制搜索深度,搜索的深度越深,效率越低,但是一般来说,走法越好。极大极小搜索可以分开写,也可以放在一起写。

       在五子棋游戏中, AI 先获取当前所有可以下的位置(就是棋盘上的空格),然后每次在其中一个位置下子,根据棋型评估函数获取一个分数,所有位置都下过一遍后,从中获取评分最高的位置。这个就是极大值的搜索过程,我们称为 AI 的MAX层,即AI 要保证自己下棋的评分最大化。如果是轮到玩家下棋时,肯定会选取对自己最有利的位置,也可以说是对AI最不利的位置,即评分要最小化,我们称为AI的MIN层。上面是只有一层的搜索,如果要考虑多层搜索,第一层是AI下棋,第二层是玩家下棋,第三层是AI下棋,第四层是玩家下棋,依次类推。假设每一层有50个可选择的位置,每个位置看做树的一个节点,那么第一层是根节点下面的子节点,有50个节点,第二层是第一层下面的子节点,就有50×50个节点,第三层就有50×50×50个节点,依次类推,这样会形成一个巨大的博弈树。我们要做的就是搜索这棵树,找到对于AI最有利的下棋位置。假设一个两层的博弈树,如图1所示,最上面一层是树的根节点,这里MAX表示会选取下一层子节点中评分最高的。第二层的MIN表示会选取下一层子节点中评分最低的。第三层是叶子节点,只需要计算评分。注意:只有在叶子节点时才会计算评分,在树的中间层,对于AI来说暂时是无法知道哪一个节点是最有利的。

2.alpha beta剪枝介绍

       Alpha-beta剪枝是一种搜索算法,用以减少极小化极大算法(Minimax算法)搜索树的节点数。这是一种对抗性搜索算法,主要应用于机器游玩的二人游戏(如井字棋、象棋、围棋)。当算法评估出某策略的后续走法比之前策略的还差时,就会停止计算该策略的后续发展。该算法和极小化极大算法所得结论相同,但剪去了不影响最终决定的分枝。 Alpha-beta剪枝的本质就是一种基于极小化极大算法的改进方法。在人机博弈中,双方回合制地进行走棋,己方考虑当自己在所有可行的走法中作出某一特定选择后,对方可能会采取的走法,从而选择最有利于自己的走法。

        极大极小值搜索算法的缺点就是当博弈树的层数变大时,需要搜索的节点数目会指数级增长。比如上面每一层的节点为50时,六层博弈树的节点就是50的6次方,运算时间会非常漫长。
在上面的例子中,我们会计算所有叶子节点的评分,但这个不是必要的。Alpha-Beta剪枝就是用来将搜索树中不需要搜索的分支裁剪掉,以提高运算速度。基本的原理是:

        当一个 MIN 层节点的 α值 ≤ β值时 ,剪掉该节点的所有未搜索子节点
        当一个 MAX 层节点的 α值 ≥ β值时 ,剪掉该节点的所有未搜索子节点
        其中α值是该层节点当前最有利的评分,β值是父节点当前的α值,根节点因为是MAX层,所以 β值 初始化为正无穷大(+∞)。
        初始化节点的α值,如果是MAX层,初始化α值为负无穷大(-∞),这样子节点的评分肯定比这个值大。如果是MIN层,初始化α值为正无穷大(+∞),这样子节点的评分肯定比这个值小。
 上述两个步骤的核心代码实现如下:

# 负极大值搜索  alpha+beta剪枝
def negativeMax(is_ai, depth, alpha, beta):
	if is_GameOver(ai_list) or is_GameOver(me_list) or depth == 0:
		return evaluation(is_ai)
	# 未落子的位置
	blank_list = list(set(all_list).difference(set(aime_list)))
	blank_list = Rearrange(blank_list)
	for next_step in blank_list:
		global search_count
		search_count += 1
		if not has_neighbor(next_step):
			continue
		if is_ai:
			ai_list.append(next_step)
		else:
			me_list.append(next_step)
		aime_list.append(next_step)
		value = -negativeMax(not is_ai, depth-1, -beta, -alpha)
		if is_ai:
			ai_list.remove(next_step)
		else:
			me_list.remove(next_step)
		aime_list.remove(next_step)
		if value > alpha:
			if depth == DEPTH:
				next_point[0], next_point[1] = next_step[0], next_step[1]
			if value >= beta:
				global cut_count
				cut_count += 1
				return beta
			alpha = value
	return alpha

def AI():
	global cut_count
	global search_count
	# 剪枝次数
	cut_count = 0
	# 搜索次数
	search_count = 0
	negativeMax(True, DEPTH, -99999999, 99999999)
	print('[Cut_Count]: %d, [Search_Count]: %d' % (cut_count, search_count))
	return next_point[0], next_point[1]

 3.展示结果

      运行环境:python3.6.5

4.参考资料

1. ​​​​​​Python 五子棋AI实现(3):极大极小值搜索和alpha beta剪枝_marble_xu的博客-CSDN博客_极大极小值算法实现五子棋

2.​​​​​​AI五子棋第二篇-运用极大极小值算法书写AI三子棋,可拓展到五子棋(建议收藏)_Ja_King_ZH的博客-CSDN博客_五子棋极大极小值

5. 全部代码下载

基于python的AI五子棋实现(极大极小值搜索和alphabeta剪枝)-机器学习文档类资源-CSDN下载

  • 9
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是基于alpha-beta剪枝算法实现的C++五子棋代码,供您参考: ```c++ #include <iostream> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int N = 15; int board[N][N]; // 棋盘 int score[N][N]; // 每个位置的得分 int dx[] = {-1, -1, 0, 1, 1, 1, 0, -1}; int dy[] = {0, 1, 1, 1, 0, -1, -1, -1}; int max_depth = 3; // 最大搜索深度 int best_x, best_y; // 记录最优落子位置 // 初始化棋盘 void init() { memset(board, 0, sizeof board); memset(score, 0, sizeof score); best_x = best_y = N / 2; board[N/2][N/2] = 1; // 中心落子 } // 检查(x, y)是否越界 bool check(int x, int y) { return x >= 0 && x < N && y >= 0 && y < N; } // 判断是否有五子连珠 bool win(int x, int y) { for (int i = 0; i < 8; i++) { int cnt = 1; for (int j = 1; j <= 4; j++) { int nx = x + j * dx[i], ny = y + j * dy[i]; if (check(nx, ny) && board[nx][ny] == board[x][y]) cnt ++; else break; } for (int j = 1; j <= 4; j++) { int nx = x - j * dx[i], ny = y - j * dy[i]; if (check(nx, ny) && board[nx][ny] == board[x][y]) cnt ++; else break; } if (cnt >= 5) return true; } return false; } // 计算(x, y)的得分 int calc(int x, int y) { int res = 0; for (int i = 0; i < 8; i++) { int cnt = 0; bool flag1 = true, flag2 = true; for (int j = 1; j <= 4; j++) { int nx = x + j * dx[i], ny = y + j * dy[i]; if (check(nx, ny)) { if (board[nx][ny] == 1) cnt ++; else if (board[nx][ny] == 2) {flag1 = false; break;} } else {flag1 = false; break;} } if (flag1) res += cnt * cnt; cnt = 0; for (int j = 1; j <= 4; j++) { int nx = x - j * dx[i], ny = y - j * dy[i]; if (check(nx, ny)) { if (board[nx][ny] == 1) cnt ++; else if (board[nx][ny] == 2) {flag2 = false; break;} } else {flag2 = false; break;} } if (flag2) res += cnt * cnt; } return res; } // 计算每个位置的得分 void get_score() { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (board[i][j] == 0) { score[i][j] = calc(i, j); } } } } // alpha-beta剪枝搜索 int dfs(int depth, int alpha, int beta) { if (depth == max_depth) return calc(best_x, best_y); vector<pair<int, int>> v; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (board[i][j] == 0) { v.push_back({i, j}); } } } sort(v.begin(), v.end(), [](pair<int, int> a, pair<int, int> b) { return score[a.first][a.second] > score[b.first][b.second]; }); int res = -1e9; for (auto [x, y] : v) { board[x][y] = depth % 2 + 1; if (win(x, y)) { // 有必胜局面,直接返回 board[x][y] = 0; return depth % 2 == 0 ? 1e9 : -1e9; } get_score(); int t = dfs(depth + 1, alpha, beta); board[x][y] = 0; if (depth % 2 == 0) { // MAX节点 if (t > res) { res = t; if (depth == 0) { best_x = x; best_y = y; } } alpha = max(alpha, t); } else { // MIN节点 if (t < res) { res = t; } beta = min(beta, t); } if (alpha >= beta) break; // alpha剪枝beta剪枝 } return res; } int main() { init(); get_score(); dfs(0, -1e9, 1e9); cout << best_x << " " << best_y << endl; return 0; } ``` 在以上代码中,我们使用了alpha-beta剪枝算法进行搜索搜索过程中,我们先计算每个位置的得分,然后按照得分从高到低对所有空位进行排序。在搜索时,我们依次枚举每个空位,对于每个空位,我们先尝试在该位置落子,然后计算该位置落子后的得分,然后进入下一层搜索。如果搜索到达了最大深度,我们就返回该位置的得分。如果该位置可以使当前玩家获胜,我们直接返回极大值或极小值。如果该位置不是必胜局面,我们继续搜索下一层。在搜索下一层时,我们需要将当前玩家交换为对手玩家,并更新alphabeta。如果当前节点是MAX节点,则更新alpha,并进行alpha剪枝。如果当前节点是MIN节点,则更新beta,并进行beta剪枝。最后,我们返回当前节点的值。 由于五子棋搜索空间非常庞大,因此我们需要设置一个最大搜索深度,以避免搜索时间过长。在以上代码中,我们设置了最大搜索深度为3。如果您的电脑性能较好,您可以适当增加最大搜索深度,以提高搜索质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

就是求关注

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值