CCF201803-4棋局评估,对抗搜索,极大极小算法

先说说极大极小算法,是指给可能出现的所有状态赋予一个评估值,两个玩家通过计算不同下棋策略对应不同的评估值,来决定如何下棋。对于井字棋游戏来说,它的博弈树(各种走法组合形成的树)如下:

Alice(MAX)下X,Bob(MIN)下O,直到到达了树的终止状态即一位棋手占领一行,一列、一对角线或所有方格都被填满。Utility指效用函数,定义游戏者在状态S下的数值。在这道题中,就是指:

- 对于Alice已经获胜的局面,评估得分为(棋盘上的空格子数+1);
  - 对于Bob已经获胜的局面,评估得分为 -(棋盘上的空格子数+1);
  - 对于平局的局面,评估得分为0;

所以,在上图策略树中,无论当前局势如何,Alice(MAX)总会选择最大的评估分对应的走法,Bob(MIN)总会选择最小的评估分对应的走法。这样才能使自己尽快的赢得比赛(这一点是关键,要想清楚)。题目中只给出了策略树中叶子节点的评估分的计算方法(赢,输或平局情况的评估分计算方法),那如何计算策略树中每个非叶子节点对应的评估分值呢?

答案是采用深度优先搜索对整个策略树进行后序遍历,这样,先计算策略树中叶子节点的评估值,在一层层的往上计算非叶子节点的评估值,最终,会得到整个策略树的评估值,这样就可以确定玩家在当前情况下应该如何走棋了。

根据以上思路:

#include <bits/stdc++.h>

using namespace std;

int q[10];

int checkok(){
    int i1, i2, ok = 0;
    for(i1 = 1; i1 <= 3; i1++)
    {
        i2 = 3 * (i1 - 1);
        if ((q[i1] == q[i1 + 3])&&(q[i1 + 3] == q[i1 + 6]) && (q[i1] != 0)){
            if(q[i1] == 1) ok = 1; else ok = 2;
            break;
        }
        if ((q[i2 + 1] == q[i2 + 2]) &&(q[i2 + 2] == q[i2 + 3]) && (q[i2 + 1] != 0)){
            if(q[i2 + 1] == 1) ok = 1; else ok = 2;
            break;
        }
    }
    if( (!ok) && ((q[1] == q[5]) && (q[5] == q[9]) && (q[1] != 0)) )
        if(q[1] == 1) ok = 1; else ok = 2;

    if( (!ok) && (q[3] == q[5]) && (q[5] == q[7]) && (q[3] != 0))
        if(q[3] == 1) ok = 1; else ok = 2;

    i2 = 0;
    for(i1 = 1; i1 <= 9; i1++)
        if(q[i1] == 0) i2++;

    if(ok == 1) return (i2 + 1);
    else if(ok == 2) return -(i2 + 1);
    else if(i2 == 0) return 0;
    else return 100;
}

int dfs(int turn){
    int value = checkok();
    if(value != 100) return value;
    int i1,i2;
    if(turn == 1) i2 = -100;
    else i2 = 100;
    for(i1 = 1; i1 <= 9; i1++){
        if(q[i1] != 0) continue;
        if(turn == 1){
            q[i1] = 1;
            i2 = max(i2, dfs(0));
        }else{
            q[i1] = 2;
            i2 = min(i2, dfs(1));
        }
        q[i1] = 0;
    }
    return i2;
}

int main()
{
    //freopen("a.txt", "r", stdin);
    int T,i1,i2;
    scanf("%d", &T);
    while(T--)
    {
        for(i1 = 1; i1 <= 9; i1++){
            scanf("%d", &i2);
            q[i1] = i2;
        }
        printf("%d\n", dfs(1));
    }
    return 0;
}

可以得到100分的答案。

还要说的是井字棋策略树下,可以完全遍历,直接使用极大极小算法,面对更复杂的棋时,还需要采用α-β剪枝等更高级的方法。

参考书籍:人工智能——一种现代方法。

猜你喜欢

转载自www.cnblogs.com/chuxinbufu/p/9583044.html
今日推荐