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分的答案。

還要說的是井字棋策略樹下,可以完全遍歷,直接使用極大極小算法,面對更復雜的棋時,還需要采用α-β剪枝等更高級的方法。

參考書籍:人工智能——一種現代方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM