QT—基于α-β 剪枝算法井字棋小游戏

(1)题目要求

采用α-β 剪枝算法实现井字棋游戏。 • 图形化界面。 • 随机选取先手后手

(2)算法思想

1. Minimax算法是一种用于决策制定的算法,它通常在博弈中使用,以帮助选择最优的行动方案。该算法基于两个概念:最小化和最大化。其中,最小化是指尽可能减少自己的损失或减轻对手的收益;最大化则是指尽可能增加自己的收益或减轻对手的损失。在Minimax算法中,我们假设当前玩家是最大化玩家,而对手是最小化玩家。我们首先考虑所有可能的下一步操作,并将其评估为对当前玩家最有利或最不利的情况。然后我们从这些可能的情况中选择一个对当前玩家最优的决策,并将其作为当前玩家的下一步操作。接着,我们模拟对手的反应,他会找到对当前玩家最不利的操作(也就是最有利的操作)。最后,我们通过递归地进行这个过程来确定最终的决策。

2.α-β剪枝算法是一种用于减少搜索空间的优化算法,常用于博弈树搜索中。它通过剪去一些不必要的搜索分支,从而减少搜索的时间复杂度。

在博弈树搜索中,α表示当前最好的最大值,β表示当前最好的最小值。算法的基本思想是,在搜索过程中,当发现某个节点的值已经超出了α和β之间的范围时,就可以停止对该节点的搜索,因为对手不会选择这个节点。具体来说,α-β剪枝算法通过递归地搜索博弈树的节点,并在搜索过程中维护α和β的值。对于MAX节点,即轮到自己走的节点,如果发现某个子节点的值大于等于β,那么就可以停止对该节点的搜索,返回当前最好的结果。对于MIN节点,即轮到对手走的节点,如果发现某个子节点的值小于等于α,那么就可以停止对该节点的搜索,返回当前最好的结果。

 通过不断更新α和β的值,α-β剪枝算法可以在搜索过程中剪去一些不必要的分支,从而大大减少搜索的时间复杂度。在最理想的情况下,α-β剪枝算法的时间复杂度可以从指数级别降低到线性级别。

(3)开发说明

用QT实现简易的井字棋,小巧简易,核心功能齐全。

(4)关键代码 

1.评估函数 evaluate

该函数采用了枚举的方式遍历整个游戏棋盘,检查每一行、每一列以及两条对角线上是否有连成 3 个的相同棋子。如果有,则判断这些棋子的归属方(computer 或 human),并将计数器 max_count 或 min_count 分别加 1,表示该方在当前局面下形成了多少个连成 3 个的棋子。最后,返回值为 max_count - min_count,即电脑方形成的连成 3 个的棋子数减去人类方形成的连成 3 个的棋子数。这个差值越大,说明当前局面对电脑越有利;反之则越有利于人类。因此,该函数用作博弈树搜索算法中的估值函数,用于评估某个游戏状态的好坏程度。

int MainWindow::evaluate() {

       int max_count = 0, min_count = 0;

       for (int i = 0; i < 3; i++) {

           if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] == computer)

               max_count++;

           if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] == human)

               min_count++;

           if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] == computer)

               max_count++;

           if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] == human)

               min_count++;}

       if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == computer)max_count++;

       if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == human)  min_count++;

       if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] == computer)  max_count++;

       if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] == human)  min_count++;

       return max_count - min_count; }//即评估函数e(s)
2. min_value
首先检查当前棋局是否已经出现胜负结果,如果已经出现,则直接返回评估值。否则,遍历当前空位,并尝试在每个空位上落子,计算出当前局面下,如果人类玩家采取最优策略,电脑可以得到的最小分数(即当前空位的极小值)。
为了计算出当前空位的极小值,需要通过调用 max_value 函数来获取电脑在当前空位落子后的最大分数。在计算完当前空位的极小值之后,将电脑在当前空位落子后的分数从小到大与 min_score 进行比较,更新 min_score。同时,需要更新 beta 的值,将其设为 min_score 和 beta 中的最小值。如果 beta <= alpha,则表明当前节点处于剪枝状态,直接返回 min_score。如果所有的空位都已经尝试完毕,依然没有找到更优的落子位置,则返回当前空位的最小值 min_score。
在这个函数中,我们假定人类会采用最优策略,即他的每一步都是为了让电脑得分最小。
int MainWindow::min_value(int alpha, int beta) {
       char result = check_win();

       if (result != ' ')  return evaluate();

       int min_score = numeric_limits<int>::max();

       for (int i = 0; i < 3; i++) {

           for (int j = 0; j < 3; j++) {

               if (board[i][j] == ' ') {

                   board[i][j] = human;

                   int current_score = max_value(alpha, beta);

                   board[i][j] = ' ';

                   min_score = min(min_score, current_score);

                   beta = min(beta, min_score);

                   if (beta <= alpha)

                       return min_score;}}}

       return min_score; }

3.max_value

实现了基于Alpha-Beta 剪枝的极小化极大搜索算法来计算计算机在游戏中采取最佳决策(下哪个棋子)的思路。首先,使用check_win()函数检查是否有一方获胜,如果有,则返回当前局面的评估值evaluate()。

如果没有一方获胜,则对每个空白位置进行遍历,将计算机下一步棋子放置在该位置,并递归调用min_value(alpha, beta)函数,得到当前局面下人类玩家最优的落子位置的评分current_score。递归结束后,将计算机落子的位置还原,并更新max_score为当前评分current_score和之前的评分max_score中的较大值。同时,更新alpha值为max_score和alpha中的较大值。最后,检查beta是否小于等于alpha, 如果是则直接返回max_score,不再继续搜索。

通过Alpha-Beta剪枝算法实现了在搜索树中减少不必要的节点,以提高搜索效率。

int MainWindow::max_value(int alpha, int beta) {

       char result = check_win();

       if (result != ' ')

           return evaluate();

       int max_score = numeric_limits<int>::min();

       for (int i = 0; i < 3; i++) {

           for (int j = 0; j < 3; j++) {

               if (board[i][j] == ' ') {

                   board[i][j] = computer;

                   int current_score = min_value(alpha, beta);

                   board[i][j] = ' ';

                   max_score = max(max_score, current_score);

                   alpha = max(alpha, max_score);

                   if (beta <= alpha)

                       return max_score;  }}}

       return max_score;}

(5)运行结果

用三角形代表"X",圆形代表"O"。

游戏目标是使三个相同的符号排成一行、一列或一条对角线,获胜者为先达成此目标者。

在初步搜索时,程序会遍历所有可能的移动并对他们进行评估。随着搜索的深入,程序将使用Alpha-Beta剪枝来减少可能移动的数量,并尽可能地削减不必要的部分。

基于Alpha-Beta剪枝的极小化极大搜索算法可以帮助计算机找到最优解,而井字棋这个游戏的状态空间相对较小,因此程序可以在有限时间内遍历所有可能的情况,并做出正确的决策。这意味着,如上面的结果所示,如果程序运行得足够快,它几乎不会犯错,从而人类几乎不可能赢得比赛,最多只能达成平局。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值