`
xingbinice
  • 浏览: 41167 次
  • 性别: Icon_minigender_1
  • 来自: 海南
社区版块
存档分类
最新评论

包含癞子的麻将胡牌算法

阅读更多

记录一下麻将的通用胡牌算法实现,只要满足M x ABC + N x DDD + EE 即可胡牌。

在这里先分析一下最简单的胡牌思路:先找出所有可能的将牌,若除去两张将牌之外的所有牌都能成扑,则可胡牌。

将牌就是公式里唯一的对子EE、扑的意思是一套牌顺子ABC或者刻子DDD。

将牌的查找:遍历每张手牌,若有两张以上相同牌就能作将,或者用一张癞子凑也可。

接下来就只要判断一副牌是否成扑,伪码如下:

 

function isPu = (牌) { // 这里约定传入的牌是有序的、张数是3的倍数
    if (没牌) {
        return true;
    }
    if (若第一张是顺子中的一张) {
        if (isPu(去掉该顺子剩下的牌)) {
            return true;
        }
    }
    if (若第一张是刻子中的一张) {
        if (isPu(去掉该刻子剩下的牌)) {
            return true;
        }
    }
    return false;
}

 只要思路清晰了算法就很简单,为什么只考虑第一张牌?其实函数内部递归调用了每张牌都会计算到的,除非没牌了。下面给出详细代码:

 

function isPu(cards, laizi) {
    if (cards.length == 0) {
        return true;
    }
    // 若第一张是顺子中的一张
    for (var first = cards[0] - 2; first <= cards[0]; first++) {
        if(first % 10 > 7 || (laizi == 0 && first < cards[0])) { 
            // 剪枝:顺子第一张牌不会大于7点、无赖子情况下顺子第一张只能用手上的牌
            continue;
        }
        var shunCount = 0;
        for (var i = 0; i < 3; i++) {
            if (cards.indexOf(first + i) >= 0) {
                shunCount++;
            }
        }
        if (shunCount == 3 || shunCount + laizi >= 3) {
            // 找到包含第一张牌的顺子
            var puCards = cards.slice();
            var puLaizi = laizi;
            for (var i = 0; i < 3; i++) {
                var deletePos = puCards.indexOf(first + i);
                if (deletePos >= 0) {
                    puCards.splice(deletePos, 1);
                }
                else {
                    puLaizi--;
                }
            }
            if (isPu(puCards, puLaizi)) {
                // 剩下的牌成扑
                return true;
            }
        }
    }
    // 若第一张是刻子中的一张
    var keziCount = 1;
    var keziCard = cards[0];
    if (cards[1] == keziCard) {
        keziCount++;
    }
    if (cards[2] == keziCard) {
        keziCount++;
    }
    if (keziCount == 3 || keziCount + laizi >= 3) {
        var puCards = cards.slice();
        var puLaizi = laizi;
        for (var i = 0; i < 3; i++) {
            var deletePos = puCards.indexOf(keziCard);
            if (deletePos >= 0) {
                puCards.splice(deletePos, 1);
            }
            else {
                puLaizi--;
            }
        }
        if (isPu(puCards, puLaizi)) {
            return true;
        }
    }
    return false;
}

 

 下面是判断胡牌的函数:

function canHuLaizi(cards, laizi) {
    if ((cards.length + laizi + 1) % 3 != 0) {
        // 若牌张数不是2、5、8、11、14则不能胡
        return false;
    }
    // 排序方便胡牌判断
    cards.sort(function(a, b) {
        return a - b;
    })
    // 依次删除一对牌做将,其余牌全部成扑则可胡
    for (var i = 0; i < cards.length; i++) {
        if (i > 0 && cards[i] == cards[i - 1]){
            // 和上一次是同样的牌,避免重复计算
            continue; 
        }
        if ((i + 1 < cards.length && cards[i] == cards[i + 1]) || laizi > 0) {
            // 找到对子、或是用一张癞子拼出的对子
            var puCards = cards.slice();
            var puLaizi = laizi;
            puCards.splice(i, 1);
            if (puCards[i] == cards[i]) {
                puCards.splice(i, 1);
            }
            else {
                puLaizi--;
            }
            // 删去对子判断剩下的牌是否成扑
            if (isPu(puCards, puLaizi)) {
                return true;
            }
        }
    }
    if (laizi >= 2 && isPu(cards, laizi - 2)) {
        // 两个癞子做将牌
        return true;
    }
    return false;
}

 

这里做一下两个输入参数的说明:

// cards:手牌数组,不超过14张牌,每张牌由整数表示如下
// 条:1, 2, 3, 4, 5, 6, 7, 8, 9,
// 万:11, 12, 13, 14, 15, 16, 17, 18, 19,
// 筒:21, 22, 23, 24, 25, 26, 27, 28, 29,
// 东南西北中发白:31, 41, 51, 61, 71, 81, 91,
// 
// laizi:癞子数量,用整数表示

 

要做这个其实是因为维护的麻将老代码思路比较乱、代码不规范、效率比较低、维护比较难。好吧说了这么多其实就是我看不懂老代码,怕游戏上线了之后这块代码出了问题搞不定。

有关算法代码重构,这里有个小技巧:

使用随机生成的牌型数据,将新版本代码与老版本代码对比,当运算结果不一样时候可以快速的找出bug,也可以循环大量输入统计耗时比较效率。

通过这个手段解决了一些小问题,最终跑1000万次牌型输出结果一致,并且时间效率提高了好几倍,所以现在就可以安心的删掉了老代码 ^-^

分享到:
评论
2 楼 filson110 2017-07-24  
1 楼 xingbinice 2017-06-21  
记录一下有bug的版本:
majiang.isPu = function(cards, laizi)
{
    if (cards.length == 0) {
        return true;
    }
    var firstCard = cards[0];
    // 若第一张是顺子中的第一张
    var shunCount = 1;
    if (cards.indexOf(firstCard + 1) >= 0) {
        shunCount++;
    }
    if (cards.indexOf(firstCard + 2) >= 0) {
        shunCount++;
    }
    if (firstCard % 10 < 8 && (shunCount == 3 || shunCount + laizi >= 3)) {
        var puCards = cards.slice();
        var puLaizi = laizi;
        for (var i = 0; i < 3; i++) {
            var deletePos = puCards.indexOf(firstCard + i);
            if (deletePos >= 0) {
                puCards.splice(deletePos, 1);
            }
            else {
                puLaizi--;
            }
        }
        if (majiang.isPu(puCards, puLaizi)) {
            // cc.log('顺', firstCard, firstCard+1, firstCard+2);
            return true;
        }
    }
    // 若第一张是刻子中的第一张
    var keziCount = 1;
    if (cards[1] == firstCard) {
        keziCount++;
    }
    if (cards[2] == firstCard) {
        keziCount++;
    }
    if (keziCount == 3 || keziCount + laizi >= 3) {
        var puCards = cards.slice();
        var puLaizi = laizi;
        for (var i = 0; i < 3; i++) {
            var deletePos = puCards.indexOf(firstCard);
            if (deletePos >= 0) {
                puCards.splice(deletePos, 1);
            }
            else {
                puLaizi--;
            }
        }
        if (majiang.isPu(puCards, puLaizi)) {
            // cc.log('刻', firstCard, firstCard, firstCard);
            return true;
        }
    }
    return false;
}

相关推荐

    golang版超高效带癞子麻将胡牌算法

    golang版麻将胡牌算法,包含带癞子和不带癞子,将gui_index 设置为34 就是不带癞子的麻将胡牌算法,gui_index 设置为[0,34)就是带癞子的麻将胡牌算法

    麻将胡牌算法(源代码)

    自己做的一个简单的C源代码麻将胡牌算法 分享给大家学习.

    麻将癞子胡牌算法

    麻将癞子胡牌算法,效率超快。4个5个癞子都测试过,python语言写的。

    c++麻将胡牌算法,癞子胡牌算法

    麻将,癞子 胡牌 算法,基于c++代码实现,回溯算法

    麻将癞子算法

    麻将癞子算法

    麻将算法源码(任意癞子)

    分享一个麻将胡牌算法,支持多癞子,自己对麻将胡牌的理解写的一套快速识别胡牌逻辑,核心逻辑500行代码,仅对同条万进行处理,字花牌不包含在内,易理解,1M次随机胡牌牌型大概3秒左右。原创分享,我的算法也许可以...

    斗地主完整算法包含天地癞子

    斗地主算法完整版,包含二人三人四人玩法天地癞子玩法,如有修改意见,一起学习交流进步

    竞赛资料源码-基于查表法的麻将算法库,支持带癞子听牌算法,且能计算出听的牌的基础番型.zip

    【目标受众】: 本项目适合IT相关专业各种计算机技术的源代码和项目资料,如计科、人工智能、通信工程、自动化和电子信息等的在校学生、老师或者企业员工下载使用。 也适合小白学习进阶,可以用作比赛项目、可以进行...

    长牌算包算法(不含癞子)

    原创分享一个长牌算包(判定牌是否满足最低胡牌要求)算法。该算法采用树结构,通过对指定的牌进行组合所有可能,找出最优解,核心算法700行左右。测试平局0.2ms一次,最耗时的组合是6与8的组合和7组合,如果该牌很...

    癞子斗地主源码下载

    最近网上源码难找了,在这里给大家提供一份癞子斗地主源码,可用的哦[img=http://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/001/face/42.gif][/img]!!!在这里先上图给大家参考[img=...

    癞子斗地主

    癞子斗地主,其中的ai算法可以参考下。其他没什么用的

    Swift版的广东麻将完整版

    本程序实现了除广东麻将的全部功能:自动摸牌、打牌、碰、杠、听牌、胡牌(其中庄家手动打牌,具体功能: 1.系统通过骰子确定庄家,然后发牌,最开始从庄家手动打牌。 2.可以碰,杠,不能吃牌;没有癞子。只能自摸。...

    百万级麻将核心算法库

    麻将核心算法库 支持癞子万能牌 百万级 算法速度快 效率高 速度稳定 支持牌型判断功能

    JAVA麻将红中杠口口翻源码

    JAVA麻将红中杠口口翻源码和运行效果, 以及经典的牌局录像。 注意:如果打不过3大美女,你就在输入大名时,带一个等号进去,这样你的牌每局都会是大牌,哈……后门嘛。娱乐。

    计算24点(不带癞子)

    随机产生1-13四个数字,进行24点计算,其中四个数字中不包含癞子

    java斗地主的算法实现

    java斗地主的算法实现,实现了输出一幅新牌,发牌、和叫地主的功能算法

    24点(带癞子)

    随机产生1-13四个数字,进行24点计算,其中四个数字中随机指定一个为癞子,可以替换为任意数参与24点计算

    cocos2dx coverflow

    cocos2dx2.23 版本 coverflow效果

    欢乐斗地主癞子提示

    函数介绍: searchOutCard: 搜索可以出的牌 getCardsList:获取选择的牌的所有组合 getCardType:获取牌的类型

Global site tag (gtag.js) - Google Analytics