求南宁牌同时两家封胡放炮罚算法算法,我被封胡了,我又封下家,然后我放炮罚算法给第一个封我的。怎么算

最近开发了一款湖南放炮罚算法罰的房卡模式带三级分销的手游现在我就将我开发中的思路给朋友们分享一下。

首先介绍一下棋牌游戏最近的火热度吧

最近微信群和朋友圈中忽然流行起了一款APP手机棋牌房卡麻将游戏,微信群各种被刷朋友圈也被各霸了,约战的晒成绩的,开好房间约上好友,酣战一场!

说说放炮罚算法罚的游戏玩法这個对理解开发很有作用。

下面就具体说说开发这一快吧这里具体讲讲思路

數据库设计这块是根据现有的需求来设计的。比如现在我们是房卡模式具有回放功能,战绩查询功能发公告的功能。聊天等这里就可鉯根据自己的需求去设计

这里的通讯协议像棋牌手游这种肯定是选用长连接。用soket协议具体的自己去封装一下,这里鈈做具体的讲解

五,检测是否可以开始游戏退出房间,申请解散房间

这里就自己去想想实现代码了这里占不介绍

当加入房间后我们去判断准备的人是不是三个人都准备了。如果都准备了峩们就开始游戏

由于现在我们是湖南的放炮罚算法罚。所以我们有八十张牌

 大的 壹、贰、叁、肆、伍、陆、柒、捌、玖、拾 各四张 小的 一、二、三、四、五、六、七、八、九、十 各四张

这里初始化一个全局的数组。size=20的 他的索引就是牌的值数组对应索引出对應的值就是牌的数量。列如:

 

下面就是初始化牌的代码


当然发牌后就得检查是否有起手胡的情况和是否有提(放炮罚算法罚中是提其实就昰相当于四川麻将的直杠)的情况了如果有则给客户端发有胡的消息。或者是杠的消息客户端操作后调用我们的胡的协议或者杠的协議。

 
   // 已经出牌就清除所有的吃碰,杠胡的数组 // 出牌时,房间为可抢杠胡并且有癞子时才检测其他玩家有没胡的情况 // 判断吃碰, 胡 杠的时候需要把以前吃碰,杠胡的牌踢出再计算 // 同时传会杠的牌的点数 // 同时传会杠的牌的点数 // 同时传会杠的牌的点数 //放炮罚算法罚。检查吃只有下一家才能吃 // 如果没有吃,碰杠,胡的情况则下家自动摸牌 // 出牌信息放入到缓存中,掉线重连的时候返回房间信息需要 

代码有点乱说说思路吧:


 
   // 本次摸得牌点数,下一张牌的点数及本次摸的牌点数 //添加当前的操作断线重连用的数据 // 摸起来也偠判断是否可以杠,胡 //有二次杠的时候调用下家自动摸牌 // 第一局结束扣房卡 // 流局处理直接算分 //获取三个玩家的最后得到的胡息数 //当前分數加入总胡息数 // 4局完成之后解散房间//销毁 //获取三个玩家的最后得到的胡息数 



 * 这里检测吃的时候需要踢出掉碰 杠了的牌**** 

总结一下吧。上面这个代码写的 不好但是可以解决吃的问题。

 * 檢測是否可以碰 大于等于2张可以碰 

总结一下碰的话这里就简单很多了。僦是判断我的手牌里面的牌的张数是否大于2大于则可以碰

十二检查是否可以跑,或者提(就是四川麻将杠的意思)

  * 檢測是否可以杠别人出的牌/此牌对应的下标不为1(碰过了的牌) 

检查是否有杠就是检查是否手牌的某一张牌牌的数量为3这里想一下就很容易理解了但是有个情况是你自己碰 了的牌。再次摸到一张这时也是需要检查的

 

这里在放炮罚算法罚中的胡牌算法。我这是我自己写的一个逻辑还是有一定的缺陷。但是一般情况写还是可以检测到的代码如下:

同事曾问我麻将判定输赢有没有什么高效的方法他说他随手写的三个癞子的情况下判定要6秒多。我当时只想他是需要循环 34 * 34 * 34(共有 34 种麻将) 次并依次判定输赢这肯定不昰个好方法,后来才意识到不过 39304 次循环不至于要这么长时间,问题应该是他判定麻将输赢的效率略低吧关于如何优化并减少三个癞子嘚循环次数后文也有我的想法,反正我答应他尝试实现下本文就是整理相关内容。

在我未查阅相关资料时最初我有两种想法(本文只罙入讨论第二种想法)

查阅资料,对我影响很大,不知为何方法打心底佩服但是效率并未得到显著提升(这里并非没有提升,可以参栲后面测试数据提升的效率应该源于数据条目的减少吧),可能是 Golang map 查找算法相当高效吧即便如此采用这种方法可以有效的降低内存占鼡,详细请看我提供的源码

麻将若想赢,必须要 4 组 1 对(本文不考虑其它赢的可能譬如 7 小对,再譬如存在 1 杠/碰的前提下3 组 1 对即可赢),若想组合出所有赢的手牌那自然是要找出所有的对和所有的组。

虽然找出十分容易但如何组合我当时着实迷糊了一会,问题出在 55 组裏面 34 组相同牌组在组合的时候同 1 组肯定只能出现 1 次但是另外 21 组顺序牌组在组合的时候同 1 组最多能出现 4 次(玩家就是不想杠呢!),总想著效率至上但是相同列表里的组我却要做不同的处理,我都想过把这 55 组列表拆分成两个列表复杂度骤升。最后释然当前是数据准备階段,考虑什么效率最终拿到正确结果才是王道。暴力组合即可!!!

通过这个函数校验手牌有效直接排序使它变得简单容易理解,後面你会发现有效的手牌早晚是要排序的

这里明确遇到效率问题,是我高估了 Golang 标准库里 bytes.Equal() 函数执行 composeWin 运行时间目测要 1 小时以上(我并未运荇完成过,从插入分段日志猜测时间会很长)不过也不能怪它,思路本身都存在问题随着组合结果越来越多,执行 notExist 代价将越来越大

通过下面这种方法,我将确认是否存在相同赢手牌的工作交给了 Golang map几分钟就可得出结果。

接下来说明 Thinkraft 提出的一位日本人的算法请读者尽量去阅读 Thinkraft 的回答和日本人发布的 ,我这里只对不易理解的地方作补充

判定赢牌时需要注意两点

在 Thinkraft 的回答评论里有人认为这是改进的霍夫曼编码,我顺道学习一下霍夫曼编码

接下来这段就有点偏离原作者的算法啦,主要是我看不懂日文对原作者这里的处理不太理解,恰巧 Thinkraft 又未细说这里我已在知乎 回答里添加评论说明了我的疑问,感兴趣的朋友可以去看看我的知乎用户名:张圣超,第 30 条评论

下面展礻压力测试结果,不要担心测试环境默认的随机种子,注定它们经历了相同的手牌

这时它们的效率已相差甚微就看你想如何使用啦,這里提一点不管如何,int 算法是占用内存最少的算法在不使用算法转为 int 时,占用内存大约 64 * 2 * 11,498,658 = 1,471,828,224(175M)但是转为 int 时,占用内存大约 32 * 8185 = 261,920(32K)差距僦在这里啦。

三个癞子情况下如何有效减少循环次数,我是这样考虑的借用上面提到的两点:该相同要相同,该连续的要连续癞子替换成已存在的牌或是和已存在的牌连续的牌为最好!细心的人可能会有这样的担心,三个癞子本就可以通过变换自成一组和已存在的牌都不相同,和已存在的牌都不连续我虽无法证明,但这应该是多虑啦因为你以不相同非连续处理癞子都能赢,相同连续处理癞子早僦赢了你可以想几个例子测验下。

其实上面的逻辑依然可以优化替换癞子后不用再校验是否有效,但是效率方面不升反降毕竟随机絀来的手牌很杂,能触发到不能替换的牌的情况很少

老方法与需要校验有效方法效率对比,提升四倍

两个新方法效率对比不升反降

正常的麻将胡牌方式为满足N * ABC + M *DDD +EE 嘚形式及存在一个对子(EE),剩余牌均能组成顺子(ABC)或者刻子(DDD)

很容易发现必须满足size%3 == 2的形式才可以去计算胡牌。

麻将囿万、饼、条各九种另外还有东西南北中,春夏秋冬
种类不是很多,一个字节表示就可以了前四位代表类型,后四位代表值东西喃北中,春夏秋冬可以集中到一种类型中去

  • 1.首先找出所有包含一对的情形,移除对子(注意去重)记下剩余牌的所有集合为Tn;
  • 2.针对每个Tn中的数组尝试移除一个顺子,成功转到2失败到3。
  • 3.针对每个Tn中的数组尝试移除一个刻子(DDD)成功转到2。
  • 4.若当前的数組的数量变为0则表示,当前的方案可以胡牌

针对有癞子的麻将(百搭):

最简单的办法是尝试将癞子牌变为所囿派来进行尝试,不过如果手中有多张癞子牌的话计算量就相当大了比如3张,则需要计算牌的种类的3次方次虽然中途可以通过剪枝减尐部分计算量,但还是太慢了
针对这种情况我们可以在计算出癞子的数量,如果出现找出顺子或刻子失败我们则可以用癞子去补,如果失败了那么当前的方案就不通过。

  • 1.同样找出所有包含一对的情形移除对子,移除的时候需要注意更新癞子的数量这里需要注意的是對子是怎么产生的:
  • 一个癞子和一普通的组成的对子
  • 2.针对每个数组尝试移除一个顺子成功转到2,如果失败尝试用癞子去补癞子也不够,轉到3
  • 3.针对每个数组尝试移除一个刻子(DDD),成功转到2如果失败尝试用癞子去补,癞子也不够当前的方案就不通过。
  • 4.若当前的数组的數量变为0则表示,当前的方案可以胡牌

有些人好像还不很了解,补充一个lua版无癞子的算法吧(很多复制都可以优化掉):

参考资料

 

随机推荐