博弈算法实现三子棋

用博弈树算法实现井字棋游戏。 井字棋游戏是一种简单的棋类游戏,在3*3的棋盘上,两人轮流下子,谁的棋子先连成3颗一条直线,谁就赢了,可以横着、竖着、斜着。博弈树算法是用搜索来解决这类问题的算法,井字棋游戏步数较少,很容易用博弈树算法实现

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;

int num = 0;
int p, q;
int tmpQP[3][3]; //表示棋盘数据的临时数组,其中的元素0表示该格为空,
int cur[3][3];

const int depth = 3;  //搜索树的最大深度


void Init()
{

    for (int i = 0; i<3; i++)
    for (int j = 0; j<3; j++)
    {
        cur[i][j] = 0;
    }
}
void PrintQP()  //打印当棋盘格局的函数
{
    for (int i = 0; i<3; i++)
    {
        for (int j = 0; j<3; j++)
            cout << cur[i][j] << '\t';
        cout << endl;//换行
    }
}

int CheckWin() //有人赢了吗?返回0表示没有人赢,返回-1表示人赢了,返回1表示计算机赢了
{
    for (int i = 0; i<3; i++)
    {
        if (cur[i][0] == 1 && cur[i][1] == 1 && cur[i][2] == 1)
            return 1;
        if (cur[i][0] == -1 && cur[i][1] == -1 && cur[i][2] == -1)
            return -1;
    }
    for (int i = 0; i<3; i++)
    {
        if (cur[0][i] == 1 && cur[1][i] == 1 && cur[2][i] == 1)
            return 1;
        if (cur[0][i] == -1 && cur[1][i] == -1 && cur[2][i] == -1)
            return -1;
    }
    if ((cur[0][0] == 1 && cur[1][1] == 1 && cur[2][2] == 1) || (cur[2][0] == 1 && cur[1][1] == 1 && cur[0][2] == 1))
        return 1;
    if ((cur[0][0] == -1 && cur[1][1] == -1 && cur[2][2] == -1) || (cur[2][0] == -1 && cur[1][1] == -1 && cur[0][2] == -1))
        return -1;

    return 0;
}


int value()//评估函数
{
    p = 0;
    q = 0;
    //将棋盘中的空格填满自己的棋子,既将棋盘数组中的0变为1
    for (int i = 0; i<3; i++)
    for (int j = 0; j<3; j++)
    if (cur[i][j] == 0)
        tmpQP[i][j] = 1;
    else
        tmpQP[i][j] = cur[i][j];

    //电脑一方
    //计算每一行中有多少行的棋子连成3个的
    for (int i = 0; i<3; i++)
        p += (tmpQP[i][0] + tmpQP[i][1] + tmpQP[i][2]) / 3;
    //计算每一列中有多少列的棋子连成3个的
    for (int i = 0; i<3; i++)
        p += (tmpQP[0][i] + tmpQP[1][i] + tmpQP[2][i]) / 3;
    //斜行有没有连成3个的?
    p += (tmpQP[0][0] + tmpQP[1][1] + tmpQP[2][2]) / 3;
    p += (tmpQP[2][0] + tmpQP[1][1] + tmpQP[0][2]) / 3;

    //将棋盘中的空格填满对方的棋子,既将棋盘数组中的0变为-1
    for (int i = 0; i<3; i++)
    for (int j = 0; j<3; j++)
    if (cur[i][j] == 0)tmpQP[i][j] = -1;
    else tmpQP[i][j] = cur[i][j];

    //对方
    //计算每一行中有多少行的棋子连成3个的
    for (int i = 0; i<3; i++)
        q += (tmpQP[i][0] + tmpQP[i][1] + tmpQP[i][2]) / 3;
    //计算每一列中有多少列的棋子连成3个的
    for (int i = 0; i<3; i++)
        q += (tmpQP[0][i] + tmpQP[1][i] + tmpQP[2][i]) / 3;
    //斜行有没有连成3个的?
    q += (tmpQP[0][0] + tmpQP[1][1] + tmpQP[2][2]) / 3;
    q += (tmpQP[2][0] + tmpQP[1][1] + tmpQP[0][2]) / 3;

    return p + q;
}

//极大结点下界为A,极小结点上界为B
int cut(int &val, int dep, bool max)  //主算法部分,实现A-B剪枝的算法,val为上一层的评价值,dep为搜索深度,max记录上一层是否为极大层
{
    if (dep == depth || dep + num == 9)     //如果搜索深度达到最大深度,或者深度加上当前棋子数已经达到9,就直接调用评价函数
    {
        return value();
    }
    int i, j, flag, temp;

    bool out = false;                //out记录是否剪枝,初始为false

    if (CheckWin() == 1)            //如果用户玩家输了,就置上一层的评价值为无穷(用很大的值代表无穷)
    {
        val = 10000;
        return 0;
    }

    if (max)                    //如果上一层是极大层,本层则需要是极小层,记录flag为无穷大;反之,则为记录为负无穷大
        flag = 10000;            //flag记录本层节点的极值
    else
        flag = -10000;
    for (i = 0; i<3 && !out; i++)      //两重循环,遍历棋盘所有位置
    {
        for (j = 0; j<3 && !out; j++)
        {
            if (cur[i][j] == 0)     //如果该位置上没有棋子
            {
                if (max)                //并且为上一层为极大层,即本层为极小层,轮到用户玩家走了。
                {
                    cur[i][j] = -1;     //该位置填上用户玩家棋子

                    if (CheckWin() == -1)    //如果用户玩家赢了
                        temp = -10000;    //置棋盘评价值为负无穷
                    else
                        temp = cut(flag, dep + 1, !max);  //否则继续调用ab剪枝函数
                    if (temp<flag)              //如果下一步棋盘的评价值小于本层节点的极值,则置本层极值为更小者,即后辈结点极小值<=祖先节点极大值
                        flag = temp;
                    if (flag <= val)           //如果本层的极值已经小于上一层的评价值,则不需要搜索下去,剪枝   A剪枝
                        out = true;
                }
                else                //如果上一层为极小层,算法与上面刚好相反
                {
                    cur[i][j] = 1;//该位置填上电脑玩家棋子

                    if (CheckWin() == 1)//如果电脑玩家赢了,置棋盘评价值为正无穷
                        temp = 10000;
                    else
                        temp = cut(flag, dep + 1, !max); // 否则继续调用ab剪枝函数
                    if (temp>flag)     //如果下一步棋盘的评价值大于本层节点的极值,则置本层极值为更小者,即后辈结点极大值>=祖先节点极小值
                        flag = temp;
                    if (flag >= val)     //如果本层的极值已经大于上一层的评价值,则不需要搜索下去,剪枝 B剪枝
                        out = true;
                }
                cur[i][j] = 0;   //把模拟下的一步棋还原,回溯
            }
        }
    }
    if (max)      //根据上一层是否为极大层,用本层的极值修改上一层的评价值
    {
        if (flag>val)
            val = flag;
    }
    else
    {
        if (flag<val)
            val = flag;
    }


    return flag;   //函数返回的是本层的极值
}


//用户通过此函数来输入落子的位置,
//比如,用户输入31,则表示用户在第3行第1列落子。
void UserInput()
{
    int pos, x, y;
L1: cout << "Please input your qizi (xy):\n ";
    cin >> pos;
    x = pos / 10, y = pos % 10;
    if (x>0 && x<4 && y>0 && y<4 && cur[x - 1][y - 1] == 0)
    {
        cur[x - 1][y - 1] = -1;
    }
    else
    {
        cout << "Input Error!\n";
        goto L1;
    }
}


//主程序
int main()
{
    int m = -10000, val = -10000, dep = 1;                //m 用来存放最大的val
    int x_pos, y_pos;                                 //记录最佳走步的坐标
    Init();
    cout << "Qipan: " << endl;
    PrintQP();
    char IsFirst;
    cout << "Do you want do first?(y/n)";
    cin >> IsFirst;
    while (IsFirst != 'y'&&IsFirst != 'n')
    {
        cout << "ERROR!" << "Do you want do first?(y/n)";
        cin >> IsFirst;
    }

    if (IsFirst == 'y')
    {

    L4:                                                      // 人先走
        UserInput();
        PrintQP();
        cout << endl;
        num++;
        value();
        if (q == 0)
        {
            cout << "DOWN GAME!" << endl;
            system("pause");
            return 0;
        }
        if (CheckWin() == -1)
        {
            cout << "You Win! GAME OVER." << endl;
            system("pause");
            return 0;
        }

        for (int x = 0; x<3; x++)
        for (int y = 0; y<3; y++){
            if (cur[x][y] == 0){
                cur[x][y] = 1;
                cut(val, dep, 1);
                if (CheckWin() == 1)  {
                    cout << "The computer put the qizi at:" << x + 1 << y + 1 << endl;
                    PrintQP();
                    cout << "The computer WIN! GAME OVER." << endl;
                    system("pause");
                    return 0;
                }

                if (val>m) {
                    m = val;
                    x_pos = x; y_pos = y;
                }
                val = -10000;
                cur[x][y] = 0;
            }
        }
        cur[x_pos][y_pos] = 1;

        val = -10000; m = -10000; dep = 1;
        cout << "The computer put the qizi at:" << x_pos + 1 << y_pos + 1 << endl;
        PrintQP();
        cout << endl;
        num++;
        value();
        if (q == 0)
        {
            cout << "DOWN GAME!" << endl;
            system("pause");
            return 0;
        }

        goto L4;
    }


    else

    {                                                    // 计算机先走
    L5:
        for (int x = 0; x<3; x++)
        for (int y = 0; y<3; y++){
            if (cur[x][y] == 0){
                cur[x][y] = 1;
                cut(val, dep, 1);
                if (CheckWin() == 1)  {
                    cout << "The computer put the qizi at:" << x + 1 << y + 1 << endl;
                    PrintQP();
                    cout << "The computer WIN! GAME OVER." << endl;
                    system("pause");
                    return 0;
                }
                if (val>m) {
                    m = val;
                    x_pos = x; y_pos = y;
                }
                val = -10000;
                cur[x][y] = 0;
            }
        }
        cur[x_pos][y_pos] = 1;

        val = -10000; m = -10000; dep = 1;
        cout << "The computer put the qizi at:" << x_pos + 1 << y_pos + 1 << endl;
        PrintQP();
        cout << endl;
        num++;
        value();
        if (p == 0)
        {
            cout << "DOWN GAME!" << endl;
            system("pause");
            return 0;
        }


        UserInput();
        PrintQP();
        cout << endl;
        num++;
        value();
        if (p == 0)
        {
            cout << "DOWN GAME!" << endl;
            system("pause");
            return 0;
        }
        if (CheckWin() == -1)
        {
            cout << "Conguatulations! You Win! GAME OVER." << endl;
            system("pause");
            return 0;
        }

        goto L5;

    }

    system("pause");
    return 0;
}

膜拜一下大佬(http://www.51hei.com/bbs/dpj-30442-1.html)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值