斗地主残局破解算法

前言

斗地主残局,双方都是明牌,玩家作为地主方先出,只要优先出完所有手牌即表示战胜,比较考验玩家的斗地主水平和智商。

很多斗地主的应用里面都会有残局闯关模式,例如:欢乐斗地主,途游斗地主等等。记得小时候庙会,就会有斗地主残局的摆摊,5块钱一局,我只围观过并没有玩过,如今想来有点遗憾。到了现如今,随着网络的迅速发展,直播平台也出现了斗地主残局的身影,前段时间我就在抖音上刷到过。

那么问题来了,有的斗地主残局可能是套路局,也就是必输局,我们怎么快速的计算判断残局是否有解呢?其实,因为完全信息,有限的步骤(手牌数有限,打完结束),我们完全可以模拟出所有的出牌组合,来暴力计算结果。

示例

可以先看下残局破解的效果:

残局破解示例

很容易看出,该残局我方要先出2保证能必胜。为什么不出其他的呢?

  1. 我出3行不行?如果出3,对面过7然后压大王,就输了。
  2. 我先出对子行不行?显然不行,由于我方的的两个单牌都会被对方的大王压制,必定会输。
  3. 所以我方应该出2保证只留一个单牌,同时我方的对J会压制对面的对10,才是必胜的关键。

这里只是简单的分析一下,具体算法往下看。

核心逻辑

我们可以利用递归的思路,计算所有可能性组合。

我们定义一个函数handOut模拟出牌过程,函数有三个参数:我的手牌mePokers,对方的手牌enemyPokers,以及对方上次的出牌lastHand。函数的返回值是一个bool类型,表示我方能否战胜对方。

function handout(mePokers, enemyPokers, lastHand);

既然是递归,那就会有函数的终止条件:一方出完牌游戏终止;当我方出完牌表示胜利,对方出完牌表示失败。

  // 我全部出完牌,直接获胜
  if (mePokers.length == 0) {
    return true;
  }

  // 对手全部出完牌,我失败
  if (enemyPokers.length == 0) {
    return false;
  }

模拟出牌就需要知道可以出哪些牌型,这个算法很简单这里就不在阐述了。举个栗子,例如:手牌为A Q 10 10,可出牌的组合就是:(A)、(Q)、(10)、(10 10)、(不出),一共五种可出牌型。

  1. 遍历这五种组合,模拟出牌;
  2. 根据我方出牌,模拟对手出牌,然后递归调用handOut把双方互换(站在对方角度);
  // 获取我当前可以出的所有手牌组合,包括过牌
  var allHands = getAllHands(mePokers);
  // 遍历我的所有出牌组合,进行模拟出牌
  for (var hand of allHands) {
    // 如果上一轮对手出了牌,则这一轮我必须要出比对手更大的牌
    // 或者 对手上一轮选择过牌,那么我只需出任意牌,但是不能过牌
    // 或者 如果上一轮对手出了牌,但我这一轮选择过牌
    if ((lastHand.type != PASS && canComb2BeatComb1(lastHand, hand) ||
        (lastHand.type == PASS && hand.type != PASS) ||
        (lastHand.type != PASS && hand.type == PASS))) {
      // 模拟对手出牌,如果对手不能取胜,则我必胜
      if (!handout(enemyPokers, previewMakeHand(mePokers, hand), hand)) {
        return true;
      }
    }
  }

其他

1. 关于牌值的定义

将 3,4,5,6,7,8,9,10,J,Q,K,A,2,小王,大王
用 3,4,5,6,7,8,9,10,11,12,13,14,16,18,19 表示
目的是更方便判断顺子和大小关系。

2. 出牌大小比较

除了炸弹,只有相同牌型才能比较。比较主牌的大小,例如(5 5 5 K K)和(Q Q Q 3 3),第一个牌型的主牌是5,第二个是Q也就是12,显然(Q Q Q 3 3)大于(5 5 5 K K)。

/**
 * comb1 先出,问后出的 comb2 是否能打过 comb1
 * 1. 同种牌型比较 main 值, main 值大的胜
 * 2. 炸弹大过其他牌型
 * 3. 牌型不同, 后出为负
 * 
 * @param {Array} comb1 牌1
 * @param {Array} comb2 牌2
 * @returns comb2是否能打过comb1
 */
function canComb2BeatComb1(comb1, comb2) {
  if (comb2.type == PASS) {
    return false;
  }
  if (comb1.type == PASS) {
    return true;
  }
  if (comb1.type == comb2.type) {
    return comb2.main > comb1.main;
  }
  return comb2.type == BOMB;
}

优化

1. 缓存优化

已经计算的结果,缓存起来,下次直接取来用,不用重复计算。
这里使用我方手牌、对方手牌、上次对方出牌为键,能否必胜为值,存入缓存集合。

const key = mePokers.toString() + enemyPokers.toString() + JSON.stringify(lastHand);

2. 多线程优化

将初始的所有出牌n种可能,每一个分发给一个线程(服务器),并发的执行,最后再汇总结果。可以提升n倍的效率。

最后

通过斗地主残局破解算法的研究,我明白了一个道理:人与人的区别主要在于思维方式,一个人的思维方式,决定了他看待世界的角度,一个人的思维方式,决定了一个人的人生高度。

如有不当之处,欢迎指正。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容