极大极小搜索-----不太傻的井字棋

1.说明:
这是学极大极小搜索的第二(三)天,昨天因为思路较为混乱,且对评估函数不甚了解,因此自己写出来的AI井字棋宛若ZZ,不过仔细查看了学长的PPT并且钻研了一番,总算对Minimax算法有了比较细致的理解,在参考了一个大神的博客后,我有了新的想法,于是重新打了一遍。

2.我对极大极小搜索的理解:
所谓极大极小搜索,是相对于一个对象而言、考虑两个对象的最佳招法的一种算法。
对于写的AI棋子来说,这“一个对象”就是电脑本身。
那么如何实现AI的功能呢?这里可以引进一棵树来描述:
极大极小搜索-----不太傻的井字棋_第1张图片
假设轮到电脑落子,如何判断落子的最佳位置是一个难题。我们不妨设电脑是第0层(这样好对应深度是0的判定),因为井字棋有9个格,因此最多只能走9步,那么最初深度也就是8,之后每落一子,深度逐次递减。
如果第0层是电脑,那么偶数层一定是电脑,而奇数层一定是玩家,假设第8层时电脑取胜,那么第七层的玩家一定想要尽可能缩小第八层电脑的取胜优势,而第六层的电脑又想在第七层玩家缩小自己优势的前提下,找到最大优势的落子位置…..一直递减到当前深度CurrentDepth=0为止。
关于MaxMinSearch()函数:
(1)用CountBlank(POS SaveBlank[STEP])来记录空格点,在空格点的基础之上,依次假设落点,选出估值最高的位置。
(2)因为从0开始计数,所以初始的递归深度为8,我们希望保存这一步的最佳落子坐标,因此加设全局变量MarkBestPos用来记录,通过打擂台的方法,如果当前决策值为较好者并且递归的深度与当前深度相同,也就是说到了这一次决策,那么就记录下这个坐标。
关于Evaluate()评估函数:
如果电脑输赢已定,返回INT_MAX或者 –INT_MAX,否则的话,我们将一个子放下后他所能造成的局势作为评估点,它放下后所能连成的线越多,则证明它的位置越好,那么它的值就越高,考虑行、列、对角线三个方向,将其平均值作为整体估值,同时考虑对手,如果对手下在这个点,那么他会有多大危害,因为一正一负,相加即为局势的好坏,相加越大者局势越佳。

代码如下:(Alpha-Beta剪枝就留作白天的任务了)

#include
#include
typedef struct position {
    int x;
    int y;
}POS;   //坐标结构体
#define N 3
#define STEP 9
#define COMPUTER 1
#define MANAGER -1
int CurrentDepth;
int player;
int chess[N][N];
int TempChess[N][N];    //虚拟的chess
int isEnd();
int Evaluate();
int CountBlank(POS SaveBlank[STEP]);
int MaxMinSearch(int depth);
void SetChess(POS MarkPos);
void RemoveChess(POS MarkPos);
void InitBoard();   //初始化边界
void WhoPlayFirst();
void ManPlay();
void ComPlay();
void DrawBoard();
POS BestPosMark;
int main()
{
    int step = 0;
    CurrentDepth = STEP-1;
    InitBoard();
    WhoPlayFirst();
    if (player == MANAGER)
    {
        for (step = 1;step <= STEP;)
        {
            ManPlay();
            DrawBoard();
            if (isEnd()==MANAGER)
            {
                printf("\n恭喜您战胜电脑!\n");
                system("pause");
                return 0;
            }
            step++;
            CurrentDepth--;
            if (CurrentDepth == 0)
            {
                printf("\n平局了!\n");
                system("pause");
                return 0;
            }
            player = (player == COMPUTER) ? MANAGER : COMPUTER;
            ComPlay();
            DrawBoard();
            if(isEnd()==COMPUTER)
            {
                printf("\n很遗憾,电脑战胜了您!\n");
                system("pause");
                return 0;
            }
            step++;
            CurrentDepth--;
            if (CurrentDepth == 0)
            {
                printf("\n平局了!\n");
                system("pause");
                return 0;
            }
            player = (player == COMPUTER) ? MANAGER : COMPUTER;
        }
    }
    if (player == COMPUTER)
    {
        for (step = 1;step <= STEP;)
        {
            ComPlay();
            DrawBoard();
            if (isEnd()==COMPUTER)
            {
                printf("\n很遗憾,电脑战胜了您!\n");
                system("pause");
                return 0;
            }
            step++;
            CurrentDepth--;
            if (CurrentDepth == 0)
            {
                printf("\n平局了!\n");
                system("pause");
                return 0;
            }
            player = (player == COMPUTER) ? MANAGER : COMPUTER;
            ManPlay();
            DrawBoard();
            if(isEnd()==MANAGER)
            {
                printf("\n恭喜您战胜电脑!\n");
                system("pause");
                return 0;
            }
            step++;
            CurrentDepth--;
            if (CurrentDepth == 0)
            {
                printf("\n平局了!\n");
                return 0;
            }
            player = (player == COMPUTER) ? MANAGER : COMPUTER;
        }
    }
    return 0;
}
void DrawBoard()
{
    int i, j;
    for (i = 0;i < N;i++)
    {
        printf("-------------\n");
        for (j = 0;j < N;j++)
        {
            if (chess[i][j] == COMPUTER)
                printf("| X ");
            else if (chess[i][j] == MANAGER)
                printf("| O ");
            else
                printf("|   ");
        }
        printf("|\n");
    }
    printf("-------------\n");
}
int isEnd()
{
    int i, j;
    int count = 0;
    for (i = 0;i < N;i++)   //行
    {
        count = 0;
        for (j = 0;j < N;j++)
            count += chess[i][j];
        if (count == 3 || count == -3)
            return count / 3;
    }
    for (j = 0;j < N;j++)   //列
    {
        count = 0;
        for (i = 0;i < N;i++)
            count += chess[i][j];
        if (count == 3 || count == -3)
            return count / 3;
    }
    count = 0;
    count = chess[0][0] + chess[1][1] + chess[2][2];
    if (count == 3 || count == -3)
        return count / 3;
    count = chess[0][2] + chess[1][1] + chess[2][0];
    if (count == 3 || count == -3)
        return count / 3;
    return 0;
}
int CountBlank(POS SaveBlank[STEP])
{
    int i, j;
    int count = 0;
    for (i = 0;i < N;i++)
    {
        for (j = 0;j < N;j++)
        {
            if (chess[i][j] == 0)   //若未被占
            {
                SaveBlank[count].x = i;
                SaveBlank[count].y = j;
                count++;
            }
        }
    }
    return count;
}
int Evaluate()
{
    int flag = 1;
    int i, j;
    int count = 0;
    if (isEnd() == COMPUTER)    //将自己的优势设置为无限大
        return INT_MAX;
    if (isEnd() == MANAGER)     //将自己的劣势设置为无限大
        return -INT_MAX;
    for (i = 0;i < N;i++)
    {
        for (j = 0;j < N;j++)
        {
            if (chess[i][j] == 0)
                TempChess[i][j] = COMPUTER; //将剩余的地方全放上电脑棋子
            else
                TempChess[i][j] = chess[i][j];
        }
    }
    //以下为电脑,记录若放满棋子后连成三个棋子的数量,越多则代表位置越重要
    for (i = 0;i < N;i++)
    {
        for (j = 0;j < N;j++)
            count += chess[i][j];
        count /= 3;
    }
    for (j = 0;j < N;j++)
    {
        for (i = 0;i < N;i++)
            count += chess[i][j];
        count /= 3;
    }
    count += (TempChess[0][0] + TempChess[1][1] + TempChess[2][2]) / 3;
    count += (TempChess[0][2] + TempChess[1][1] + TempChess[2][0]) / 3;

    //以下为玩家
    for (i = 0;i < N;i++)
    {
        for (j = 0;j < N;j++)
        {
            if (chess[i][j] == 0)
                TempChess[i][j] = MANAGER;  //将剩余的地方全放上电脑棋子
            else
                TempChess[i][j] = chess[i][j];
        }
    }
    for (i = 0;i < N;i++)
    {
        for (j = 0;j < N;j++)
            count += chess[i][j];
        count /= 3;
    }
    for (j = 0;j < N;j++)
    {
        for (i = 0;i < N;i++)
            count += chess[i][j];
        count /= 3;
    }
    count += (TempChess[0][0] + TempChess[1][1] + TempChess[2][2]) / 3;
    count += (TempChess[0][2] + TempChess[1][1] + TempChess[2][0]) / 3;

    return count;   //count是根据对电脑的优势和对玩家的优势相减综合算出的
}
void RemoveChess(POS MarkPos)
{
    chess[MarkPos.x][MarkPos.y] = 0;
    player = (player == COMPUTER) ? MANAGER : COMPUTER;
}
void SetChess(POS MarkPos)
{
    chess[MarkPos.x][MarkPos.y] = player;
    player = (player == COMPUTER) ? MANAGER : COMPUTER;
}
int MaxMinSearch(int depth)
{
    int BestValue = 0;
    int Value = 0;
    int i, count = 0;
    POS SaveBlank[STEP];
    if (COMPUTER == isEnd() || MANAGER == isEnd())
        return Evaluate();
    if (depth == 0)
        return Evaluate();
    if (player == COMPUTER)
        BestValue = -INT_MAX;
    if (player == MANAGER)
        BestValue = INT_MAX;
    count = CountBlank(SaveBlank);
    for (i = 0;i < count;i++)
    {
        POS MarkPos = SaveBlank[i];
        SetChess(MarkPos);
        Value = MaxMinSearch(depth - 1);
        RemoveChess(MarkPos);
        if (player == MANAGER)
        {
            if (Value < BestValue)
            {
                BestValue = Value;
                if (depth == CurrentDepth)
                {
                    BestPosMark = MarkPos;
                }
            }
        }
        else if (player == COMPUTER)
        {
            if (Value > BestValue)
            {
                BestValue = Value;
                if (depth == CurrentDepth)
                {
                    BestPosMark = MarkPos;
                }
            }
        }
    }
    return BestValue;
}
void ComPlay()
{
    MaxMinSearch(CurrentDepth);
    printf("\n电脑落子的位置为:(%d,%d)\n", BestPosMark.x + 1, BestPosMark.y + 1);
    chess[BestPosMark.x][BestPosMark.y] = COMPUTER;
}
void ManPlay()
{
    POS man;
    printf("\n请输入您的落子位置:\n");
    scanf("%d %d", &man.x, &man.y);
    man.x--;
    man.y--;
    chess[man.x][man.y] = MANAGER;
}
void InitBoard()
{
    int i, j;
    for (i = 0;i < N;i++)
        for (j = 0;j < N;j++)
            TempChess[i][j] = chess[i][j] = 0;
}
void WhoPlayFirst()
{
    char ch;
    printf("欢迎试玩AI井字棋,请选择您的落子先后:1---先手 2---后手\n");
    ch = getchar();
    player = (ch == '1') ? MANAGER : COMPUTER;
}

你可能感兴趣的:(MaxMinSearch)