【C++】用程序证明井字棋必平局

受到哈佛大学CS50.3 人工智能导论课的启发,写了这个demo,用程序证明了在玩家双方都十分聪明的情况下,井字棋必平局。不过由于井字棋的机制实在太简单了,以人类算力就可以轻松得出出必平局的结论。不过这种对抗博弈的思路可以迁移到几乎任何类似的过程中,后续可能会以此为框架探究一些其他棋牌类游戏的必胜法。
原课程B站链接:哈佛大学CS50.3 人工智能导论课

#include <iostream>
#include <string>
#include <vector>
using namespace std;
 
//棋盘状态
class State
{
public:
    int data[3][3];
    State()
    {
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                this->data[i][j] = 0;
            }
        }
    }
};
//打印当前棋盘状态,调试用
void print(State state)
{
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            cout << state.data[i][j] << " ";
        }
        cout << endl;
    }
    cout << "------------------------------------" << endl;;
}
//三元组,表示一个下棋动作,三个量分别表示“落子横坐标”“落子纵坐标”“棋子值”
typedef struct Triple
{
    int data[3];
    Triple(int a, int b, int c)
    {
        data[0] = a;
        data[1] = b;
        data[2] = c;
    }
};
//判断下一步该谁走,返回玩家名字字符串
string Player(State state)
{
    int n_min = 0, n_max = 0;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (state.data[i][j] == 1)n_max++;
            if (state.data[i][j] == -1)n_min++;
        }
    }
    if (n_max <= n_min)return "Max";
    else return "Min";
}
//输入一个state和轮到谁下棋,返回所有可能操作的三元组向量
vector<Triple> Actions(State state, string player)
{
    int t = 0;
    vector<Triple> res;
    if (player == "Max")t = 1;
    else t = -1;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (state.data[i][j] == 0)
            {
                res.push_back(Triple(i, j, t));
            }
        }
    }
    return res;
}
//输入一个state和一个动作三元组,返回更新后的state
State Result(State state, Triple triple)
{
    int x = triple.data[0];
    int y = triple.data[1];
    int value = triple.data[2];
    state.data[x][y] = value;
    //print(state);
    return state;
}
//根据输入的state判断游戏是否结束,若结束返回true,否则返回false
bool Terminal(State state)
{
    int count = 0;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (state.data[i][j] != 0)count++;
        }
    }
    if (count == 9)return true;
    int a[8][3][2] =
    {
        {{0, 0}, {0, 1}, {0, 2}},
        {{1, 0}, {1, 1}, {1, 2}},
        {{2, 0}, {2, 1}, {2, 2}},
 
        {{0, 0}, {1, 0}, {2, 0}},
        {{0, 1}, {1, 1}, {2, 1}},
        {{0, 2}, {1, 2}, {2, 2}},
 
        {{0, 0}, {1, 1}, {2, 2}},
        {{0, 2}, {1, 1}, {2, 0}}
    };
    int ju1 = 0, ju2 = 0, ju3 = 0;
    for (int i = 0; i < 8; i++)
    {
        ju1 = state.data[a[i][0][0]][a[i][0][1]];
        ju2 = state.data[a[i][1][0]][a[i][1][1]];
        ju3 = state.data[a[i][2][0]][a[i][2][1]];
        if (ju1 == ju2 && ju2 == ju3)
        {
            if (ju1 != 0)return true;
        }
    }
    return false;
}
//计算当前棋盘的得分值
int Utility(State state)
{
    int a[8][3][2] =
    {
        {{0, 0}, {0, 1}, {0, 2}},
        {{1, 0}, {1, 1}, {1, 2}},
        {{2, 0}, {2, 1}, {2, 2}},
 
        {{0, 0}, {1, 0}, {2, 0}},
        {{0, 1}, {1, 1}, {2, 1}},
        {{0, 2}, {1, 2}, {2, 2}},
 
        {{0, 0}, {1, 1}, {2, 2}},
        {{0, 2}, {1, 1}, {2, 0}}
    };
    int ju1 = 0, ju2 = 0, ju3 = 0, v = 0;
    for (int i = 0; i < 8; i++)
    {
        ju1 = state.data[a[i][0][0]][a[i][0][1]];
        ju2 = state.data[a[i][1][0]][a[i][1][1]];
        ju3 = state.data[a[i][2][0]][a[i][2][1]];
        if (ju1 == ju2 && ju2 == ju3)
        {
            if (ju1 == 1)v = 1;
            else if (ju1 == -1)v = -1;
            else v = 0;
        }
    }
    return v;
}
//工具函数,作用是比较两数
int Maxx(int a, int b)
{
    if (a > b)return a;
    else return b;
}
int Minn(int a, int b)
{
    if (a < b)return a;
    else return b;
}
//极大值玩家函数
int Max_Value(State state);
//极小值玩家函数
int Min_Value(State state);
int Max_Value(State state)
{
    //递归出口,即游戏结束
    if (Terminal(state) == true)
    {
        //结算分数
        return Utility(state);
    }
    //取尽量小值作为v的初始值
    int v=-100;
    //求出当前棋盘下所有可能的操作
    vector<Triple> all_actions = Actions(state, Player(state));
    //遍历所有可能的操作,考虑Min玩家下一步应该怎么会做来更新v值
    for (int i = 0; i < all_actions.size(); i++)
    {
        v = Maxx(v, Min_Value(Result(state, all_actions[i])));
    }
    return v;
}
int Min_Value(State state)
{
    //和Max玩家同理
    if (Terminal(state) == true)
    {
        return Utility(state);
    }
    int v = 100;
    vector<Triple> all_actions = Actions(state, Player(state));
    for (int i = 0; i < all_actions.size(); i++)
    {
        v = Minn(v, Max_Value(Result(state, all_actions[i])));
    }
    return v;
}
int main()
{
    //初始棋盘
    State s;
    //Min玩家先手
    int res=Min_Value(s);
    //输出res值,意义为res=1:Max玩家必胜,res=0:必平局,res=-1:Min玩家必胜
    cout << res;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值