基于蒙特卡洛猜牌-极大极小搜索-alpha-beta剪枝-AI斗地主

蒙特卡洛树搜索
全称 Monte Carlo Tree Search(MCTS),是一种人工智能问题中做出最优决策的方法,一般是在组合博弈中的行动(move)规划形式。它结合了随机模拟的一般性和树搜索的准确性。MCTS 受到快速关注主要是由计算机围棋程序的成功以及其潜在的在众多难题上的应用所致。超越博弈游戏本身,MCTS 理论上可以被用在以 {状态 state,行动 action} 对定义和用模拟进行预测输出结果的任何领域。

原文地址

一、程序设计

MCTS通过很多次模拟真实对局,当模拟的次数足够多,(利用大数定律)模拟后获得的最佳收益的节点,就接近于理论上真实的最佳收益节点。那么这个节点所包含的Action(行动)就是当前state(状态)下最佳的选择。以斗地主来为例,MCTS就是在当前各家手牌的情况下,在符合斗地主规则和出牌常规的情况下框定可选的行动,进行n次模拟对局,每次对局都执行不完全相同的行动,记录和更新每种行动带来的收益,最终选择收益最佳的行动(出牌、跟牌或不出)。

一般情况下MCTS适用于二人零和完全信息博弈游戏。然而斗地主是3人游戏,并且是非完全信息博弈游戏,MCTS能适用吗?
我们先来尝试简化这个问题。首先,如果斗地主是明牌的,在规则允许的情况下,每个玩家的可选行动对其他玩家都是透明的,那就变成了3人完全信息博弈游戏。其次,斗地主中3个人分为两派,可以将2个农民和1地主之间的博弈,看成2人零和游戏。这样来考虑之后,斗地主就变成了MCTS擅长的“二人零和完全信息博弈游戏”了,理论上应该是可以实现的。

MCTS循环
每一次MCTS树搜索分为4个步骤:
1.选择(selection):从非叶子节点中选择未完全扩展的节点进行扩展;如果叶子节点都已扩展,选择UCT最高的节点。
2.扩展(extension):
选择第一个未尝试的行动。
用这个行动创建一个新的MCTS节点。其中父节点为当前节点,游戏状态为执行行动之后的游戏状态,此行动作为节点引发的行动。
将扩展出来的新节点加入MCTS树上。
返回新节点。
3.模拟(simulation):推演游戏过程,最终返回游戏结果信息(一般包括游戏得分,胜方等)。
4.反馈(backpropagation):反向传播游戏结果,更新各节点的q和n。
重复进行以上4个步骤足够多的次数,就可以通过选择UCT最高的节点作为下一步行动。

二、效果实现

AI 对局

请添加图片描述

其他效果省略

三、核心算法

本次毕设系统在设计中,主要采用Nodejs html javascript css结合的方式实现前端扑克牌对局,其中Nodejs主要实现对局算法的实现以及接收界面端用户操作的数据,实现用户线上对局的总体功能点。

核心算法实现


class AI{
    constructor(param) {
        param = param || {};
        this.player = param.player;
        this.game = param.game;
    }
    classify(pokerList){
        pokerList.sort(this.sortFunction);
        if(pokerList.length===0){
            return {1: [],2: [],3: [],4: []};
        }
        let lastPoker = pokerList[0];
        let curList = [lastPoker];
        let lists = [];
        for(let i=1; i<pokerList.length; i++){
            if(pokerList[i].number !== lastPoker.number){
                lists.push(curList);
                curList = [pokerList[i]];
            }else{
                curList.push(pokerList[i]);
            }
            lastPoker = pokerList[i];
        }
        lists.push(curList);
        let Count1List = [];let Count2List = [];
        let Count3List = [];let Count4List = [];
        for(let i=0; i<lists.length; i++){
            if(lists[i].length === 3){
                Count3List.push(lists[i]);
            }else if(lists[i].length === 2){
                Count2List.push(lists[i]);
            }else if(lists[i].length === 1){
                Count1List.push(lists[i]);
            }else if(lists[i].length === 4){
                Count4List.push(lists[i]);
            }
        }
        return {
            1: Count1List,
            2: Count2List,
            3: Count3List,
            4: Count4List,
        };
    }
    getClassifyObj(pokerList0){
        let poker15 = [];
        let poker16 = [];
        let poker17 = [];
        let pokerList = pokerList0.slice(0);
        for(let i=0; i<pokerList.length; i++){
            if(pokerList[i].number === 15){
                let poker = pokerList.splice(i,1);
                i--;
                poker15.push(poker[0]);
            }else if(pokerList[i].number === 16){
                let poker = pokerList.splice(i,1);
                i--;
                poker16.push(poker[0]);
            }else if(pokerList[i].number === 17){
                let poker = pokerList.splice(i,1);
                i--;
                poker17.push(poker[0]);
            }
        }
        let obj = this.classify(pokerList);
        let Count1List = obj[1];
        let Count2List = obj[2];
        let Count3List = obj[3];
        let Count4List = obj[4];
        let four = Count4List;
        let three = [];
        let threeList = [];
        let two = [];
        let twoList = [];
        let one = [];
        let oneList = [];
    //接牌1 最小接 不拆 炸
    getByObj1(lastObj){
        let obj;
        obj = this.getSmallestObjByObj(lastObj);
        if(!obj){
            obj = this.getByBoom(lastObj);
        }
        return obj;
    }

    //接牌2 最小接 炸 拆
    getByObj2(lastObj){
        let obj;
        obj = this.getByObj1(lastObj);
        if(!obj){
            obj = this.getBySplit(lastObj);
        }
        return obj;
    }

    //接牌3 最小接 不拆 不炸 不出王、2、AAA
    getByObj3(lastObj){
        let obj;
        obj = this.getSmallestObjNoBig(lastObj);

        return obj;
    }

    //接牌4
    getByObj4(lastObj){
        let obj;
        if(lastObj.type==='one'){
            obj = this.getByObj5(lastObj);
        }else{
            obj = this.getByObj2(lastObj);
        }
        return obj;
    }
}

获取源码

计算机博弈理论的研究希望计算机能够像人一样、思维、判断和推理,并能够做出理性的决策。棋类博弈由于规则明确、竞技性高,且人类选手往往胜于计算机等原因,在计算机博弈理论的研究过程中一直受到重要关注和深入的探讨,并促进了计算机博弈理论的发展。传统的基于博弈树搜索和静态评估的博弈方法在国际象棋、中国象棋等棋类项目中获得了明显的成功,该类项目的盘面估计与博弈树搜索过程相对独立,棋子在盘面中的作用相对明确,且棋局中的专家规则相对较为容易概括和总结。 然而传统的博弈理论在计算机围棋博弈中遇到了明显的困难:围棋具有巨大的搜索空间;盘面评估与博弈树搜索紧密相关,只能通过对将来落子的可能性进行分析才能准确地确定棋子之间的关系;与此同时,高层次的围棋知识也很难归纳,归纳之后常有例外,并且在手工构建围棋知识和规则的过程中常会出现矛盾而导致不一致性。这些独特的因素为围棋及拥有类似性质的计算机博弈问题研究带来了新的挑战。 从2006年开始,计算机围棋博弈的相关研究有了跨越式的发展,基于蒙特卡罗模拟的博弈树搜索算法获得了重要的成功,并开始逐步引领计算机博弈理论研究的方向。在本章,我们将介绍蒙特卡罗博弈理论及其在围棋等棋类博弈中的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值