做一个微信欢乐斗地主之残局解答器!

今年过年的时候,在玩微信小程序之欢乐斗地主。发现里面还含有一个小游戏叫做“残局闯关”,如下图。

 


这里面的题,如果不熟悉其中的套路,有个别几道还真的不好做(下文有例子)。于是,我便萌发了设计并实现一个残局解答器的想法。从过年期间就开始利用业余时间进行coding,到今天晚上,用了大约3周左右的业余时间,终于实现出了一个基本的残局解答器。目前UI也已经全部完成。可以轻松解题了!
那么,如何做一个残局解答器呢?它是由哪几个部分构成的呢?算法又是怎样的呢?下面一个一个来介绍。

一、 残局解答器的组成部分
本残局解答器由以下这几部分组成:
1. 局面表示法
2. 招法生成器
3. 招法分类器
4. 招法过滤器
5. 招法应对器
6. 计算引擎
7. UI界面(未全部完成)
8. 测试程序

 

 

 

以下一一介绍:

1. 局面表示法
   局面表示分为2种,一种是用户输入输出局面表示法,第二种是引擎计算时使用的局面表示法。
   对于用户输入输出,那就和自然表示一样,J就是'J', Q就是'Q',小王是'Y',大王是'Z', 2是2或'2'都可以。
   对于引擎计算时的局面表示,那就全部是数据了,如J、Q、K、A、2分别是11,12,13,14,18. 小王是20,大王是30. 
   
2. 招法生成器(MoveGener)
   给定一个list,即一手牌,能够计算出这手牌所有的可出的招法,包括pass。
   本程序中,除pass外,共分为14种招法,分别为:单牌、对子、三个、炸弹、王炸、3+1、3+2、连续单牌(至少5连张)、连对(至少3连对)、飞机(连续2个或多个相同的3张牌)、连续3+1、连续3+2、4+2、4+双对。
   
3. 招法分类器(MoveClassifier)
   给定一个招法(即一个list),能够返回该招法的类型。若是连续型招法(如连对),还能指出是几连。
   
4. 招法过滤器(MoveFilter)
   给定一系列招法,再给定一个对手招法,在这一系列招法中,找出能够克制对手招法的招法。
   比如,[[3,3], [4,4], [5,5]], 对手招法是[4,4],那么经过MoveFilter的计算,得到的返回应该是 [[5,5]]
   
5. 招法应对器(get_resp_moves)
   给定一手牌,以及一个对手招法,给出这手牌中所有能够应对该对手招法的招法。
   可见,招法应对器就是对MoveGener、MoveClassifier、和MoveFilter的一个综合使用。
   第一步,利用MoveClassifier,判断对手招法是14种招法类型的哪一种;
   第二步,利用MoveGener,生成这种类型的这手牌的所有招法;
   第三步,利用MoveFilter,过滤第二步中产生的招法,得到可以应对对手招法的招法。
   
6. 计算引擎
   这是本程序的核心部分。
   本程序一共实现了2个引擎 - 蒙特卡洛搜索引擎(分为单进程版和多进程版) 和 Min-Max搜索引擎。
   详情见下一章。
   
7. UI界面
   因为未完全实现,所以这里从略。不过即使没有UI界面,本程序也是可以用来解答残局的。
   
8. 测试程序
   上述各个组成部分都有自己对应的测试程序。保证无bug. 
   

二、计算引擎

Min-Max纯暴力搜索加剪枝
Min-Max算法是国际象棋和中国象棋的人工智能程序的基石算法。虽然象棋AI种类繁多,优化方法也是千变万化,但是所有象棋AI归根结底,都是建立于Min-Max这个基石之上的。
那么,Min-Max算法是什么意思呢?
其实,意思很直观。先这么看,假设地主先出,地主一手牌就出完了,那么这种情况就是地主必胜。给地主先出之前的局面打分,就是满分100分。假设地主先出,但牌没有出完,农民接着出。不论地主出什么牌,农民手上只有2张牌,王炸,好了,那么农民必胜。这个时候,给地主出完牌而农民待出牌的局面打分,就是0分,这是农民必胜局面。再往上一层,给地主待出牌的局面打分,发现无论地主出什么牌,比如10种出法,10种返回都是0分,那么地主待出牌的这个局面自然也就是0分。
换句话说,在地主待出牌的局面下,一旦找到一种出法的返回值是100分,那么选这种打法就地主必胜啦;反之,若所有招法的局面都返回0分,那这就是地主必败局面,如果要往上一层返回,就是返回0分。
总结一下:
地主待出牌局面,一旦找到一个100分的招法,剩下招法都不用试了,直接返回100分,这就是剪枝;
同理,农民待出牌局面,一旦找到一个0分的招法,剩下的招法都不用试了,直接返回0分,这就是剪枝。

利用Min-Max算法,以及上面介绍的剪枝算法,我发现程序运行的速度有了极大的提升。其实原因就是剪枝了。

三、运行效果

最后,来看一下运行效果吧。
聪明的读者,请看一下2018年3月残局闯关的第12关吧,您能过去吗?

首先,编辑solve_python.py文件,只需修改其中2行表示地主牌和农民牌的,如下:

 

 

然后,运行程序:

没错,唯一的一招必杀是先出 10 !所有其他招法都是必败招。这就是Min-Max的威力了!

其实,一般的题用不了这么长的时间,几秒到几十秒就做出来了,但这一道题确实有点难度,所以时间也耗得久一些。地主出牌后,用户可以根据手机上农民的出牌,再把农民出的牌手动输入到以上人机交互界面里,然后电脑会继续计算地主的应招,这一次计算就很快了,基本是秒级的。

最后,公布一下源代码地址:

https://github.com/FinixLei/WeChat_LandLords

(完)

关于本残局解答器最新的故事(2019-05),请参见《C++大战Python - 以C++11重写欢乐斗地主残局解答器》


   
   

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值