Minimax-Tic Tac Toe

今天学了两个名词

  • 完备信息 博弈双方完全知道对方的所有信息,从第一步到最后一步,所有的走法,整个棋盘看的清清楚楚,不存在随机性。德州扑克、麻将不在这个范围内,因为看不到对手的牌,而且下一次发出的牌是随机的。
  • 零和博弈 也就是双方的收益之和为0。玩家A优势则玩家B必然劣势,不存在双方都是优势或者双方都是劣势的情况。

Tic Tac Toe游戏比较简单,就是一个9个位置的棋盘,谁的棋子连成3个就赢了,总共只用检查8种获胜的情况。所以游戏最多只有9轮,而且满足上述两个概念,所以是练习Minimax的比较好的例子。

Minimax是啥?假设有一个方法evaluate可以根据当前棋盘的某个棋子,给出当前局势的评分。我是玩家A,手执X子;玩家B手执O字。根据X字给当前局势打一个分数,如果分数越高,说明对玩家A的X子来说越有利。反之,分数越低就说明对A越不利,则局势对玩家B的O子更好。 所以对于我要落子的局面,我希望分数越大越好,即为MAX。对于对手落子的局面,他希望分数越低越好,即为MIN

Minimax就是从当前局势开始,反复让我和对手评价局面并走一步最优的棋。我会选择得分最高的走法,对手会选择分数最低的走法。就像下图所示。
Tic Tac Toe.png
我们用DFS搜索每一步并评价,这棵树是从下往上走的,叶节点反馈评价给父节点。树的深度取决于你要预测的深度以及棋盘剩余局数。

给个Minimax的代码。

  • chessmans是棋盘,一维数组保存的。
  • chessType是当前的执子,根据当前的执子来选择要Max还是Min。
    ComputerPlayer自带一个棋子的属性,通过getChessType()得到。如果chessType跟getChessType()相同说明这句是电脑要下子的局,则应该要Max。
  • level是预测的深度
  • 方法返回值是pair,first是评价值,second是该评价值对应走的棋盘的位置。
pair<int, int> ComputerPlayer :: Minimax (Chessman* chessmans, Chessman chessType, int level){
    if(level == 0) return pair<int, int>(this->evaluate(chessmans), -1);
    Chessman otherType;
    if(chessType == X) otherType = O;
    else otherType = X;
    int bestPos = -1;
    int bestScore, score;
    bool flag = (chessType == this->getChessType());  // flag of MAX or MIN
    if(flag) bestScore = INT_MIN;// -2^31
    else bestScore = INT_MAX;// 2^31
    pair<int, int> res;
    for(int i = 0; i < 9; i++){
        if(chessmans[i] == empty){
            chessmans[i] = chessType;
            res = Minimax(chessmans, otherType, level-1);
            score = res.first;
            chessmans[i] = empty;
            if( flag == (score > bestScore) ){  //  (flag && score > bestScore) || (!flag && score < bestScore) 
                bestScore = score;
                bestPos = i;
            }
        }
    }
    if(bestPos == -1) return pair<int, int>(this->evaluate(chessmans), -1);
    return pair<int, int>(bestScore, bestPos);
}

评价函数evaluate,针对可以取胜的8条线,分下面三种情况

  • 有三个相同的棋子
  • 有两个相同的棋子+一个空格
  • 有一个相同的棋子+两个空格

以上三种情况可以计分,根据棋子的不同选择加分还是减分。分数的权值是自己调试出来的,其他权值也可以做到很好地效果。

int ComputerPlayer:: evaluate(Chessman* chessmans){
    Chessman chessType = this->getChessType();
    int score = 0;
    int condition[24] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 3, 6, 1, 4, 7, 2, 5, 8, 0, 4, 8, 2, 4 ,6};
    for(int i = 0; i < 24; i+=3){
        int emptyNum = 0, chessTypeNum = 0, otherTypeNum = 0;
        for(int j = i; j < i+3; j++){
            if(chessmans[condition[j]] == chessType) chessTypeNum++;
            else if(chessmans[condition[j]] == empty) emptyNum++;
            else otherTypeNum++;
        }
        if(chessTypeNum == 3) score += 10; // WIN!
        else if(otherTypeNum == 3) score -= 10; // LOSE!
        else if(emptyNum == 1){
            if(chessTypeNum == 2) score+=2;
            else if(otherTypeNum == 2) score-=2;
        } else if(emptyNum == 2){
            if(chessTypeNum == 1) score++;
            else if(otherTypeNum == 1) score--;
        } 
    } return score;
}

最后给个链接吧 Tic Tac Toe完整的代码

版权声明:DouMiaoO_Oo 原创文章,未经博主允许不得转载。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值