麻将牌的构成
麻将牌是由下面的牌组成:
1.万字牌:1-9 共 9 张(每个数字各一张)比如 1 万 2 万 3 万等
2.条子牌:1-9 共 9 张(每个数字各一张) 比如 1 条,2 条,3 条等
3.筒子牌:1-9 共 9 张(每个数字各一张) 比如 1 筒,2 筒,3 筒等
4.字牌:东南西北风各 4 张,红中、发财、白板各 4 张。
由基本牌组合而成的 万,条,筒的牌的数目为 4* 9 * 3 = 108。 由字牌组成的牌的数量为 4 * 7 = 28 。
所以整副牌就由 136 张牌组成。花牌(春夏秋冬)等不计算。
麻将的碰,杠,吃,听,胡
碰、杠、吃是麻将游戏中常用的三种操作,以便于玩家将手中的牌进行组合,以尽可能地增加胡牌的机会。具体意义如下:
碰:当玩家手中有两张相同的牌,而桌上已有一张相同的牌时,玩家可以选择碰此牌,即将自己手中的两张相同的牌与桌上的一张相同的牌组成一个刻子。
杠:当玩家手中有一张牌,而桌上已有三张相同的牌时,玩家可以选择杠此牌,即将自己手中的一张牌与桌上的三张相同的牌组成一个杠。这里面杠有多种,在算法中一定要注意处理。 具体分类如下:
弯杠:当自己手中有两张牌,别人打出一张牌自己进行了碰操作,然后自己又摸到了一张牌,形成了杠
明杠:自己手中有 3 张牌,别人打出一张牌,形成的杠
暗杠: 自己摸到了 4 张一样的牌为暗杠
吃:当桌上有玩家打出一张牌后,其他玩家如果手中有两张数字连着的牌,就可以选择吃此牌,即将桌上的牌和手中的牌组合成一个顺子。
听:听牌是麻将游戏中的一个术语,指的是在某一时刻,手牌中的牌差一张即可和牌,当前的手牌一定是 3n+1 张。
胡:在麻将游戏中,“胡”指的是牌手通过组成某些牌型,使结束时所剩余的牌组满足胡牌规则,从而赢得游戏的过程。
麻将胡牌条件
麻将想要胡牌,必须得满足一个条件就是 3n + 2
3 指的是由 3 张牌组成的面子( 顺子或者是刻子)
n 是指由 n 个(大于等于 0)组成的面子
2 指的就是由 2 张牌组成的将,这个将是必不可少的,面子可以不存在,但是将必须得存在,也就是说 n 可以为 0,但是 2 必须存在。如下图所示:

举个例子,当手牌为 14 张时 正好满足 3n +2 3x4 + 2 = 14 。表示由 4 组面子和一对将组成的手牌。再举个例子当手牌为 1 万 1 万 1 万 5 万 5 万 也是满足 3n+2,也可以满足胡牌的条件。
胡牌算法简介
胡牌算法实现起来有多种多样,但是大致分为两种,一种是使用回溯算法,一种是查表法。
查表法是利用事先生成好的所有的胡牌牌型,然后将这些牌型加载到内存中,直接在内存中对比即可,效率非常快,缺点只不灵活,需要事先按规则生成好表。
我们现在介绍的这种算法也是用到了回溯算法,我们称之为 “选将拆分法” 。
我们先把手牌中所有可能做将的牌先找出来,然后去掉这组将牌,看剩下的牌是否能满足 3n 条件,如果可以满足则可以胡牌,如果不能满足则不能胡牌。
这种算法中间利用适当的“剪枝” ,执行起来效率非常的快,同时这种算法对于处理任意赖子的效率也是很强悍,不比查表法慢。
我后面会逐步的分析如何这种算法,让任何没有基础的人都可以彻底掌握这种算法。我们当前介绍的是普通情况下的胡牌,不计算赖子的情况下。赖子的算法我们放在下一篇来讲,只要掌握了这种算法的原理,对于赖子的牌型算法也会很容易理解。
选将拆分法
选将拆分法就是将手牌中所有可能做将的牌,也可以说任意的牌,只要它的数量大于 2,那么它都可以是将。
我们先把这些可以做将的牌单独的列举出来。然后利用回溯法开始遍历这些将,首先将此将牌从手牌中移除,然后判断剩余的牌是否能满足 3n。如果可以满足那么就可以胡牌。当所有的将牌都遍历完了却没有满足 3n,则这副手牌不能胡牌。
算法数据结构
构建数据结构
在前面我们己经讲述过了,牌一共为136 张,万,条,筒各 9 种类型合计共 27 种类型,字牌共 7 种类型,所以牌的类型总体加起来就是 34 种类型。
因此我们可以使用一个数组来保存整副牌的数据结构。数组的下标用来表示当前牌的类型,数组的值用来表示当前类型的个数。因此我们就可以得出如下的数据结构:
int[] cards = {
2,0,0,0,0,0,0,0,0, //-- 1-9 万
0,0,0,0,0,0,0,0,0, //-- 1-9 条
0,0,0,0,0,0,0,0,0, //-- 1-9 筒
0,0,0,0,0,0,0 //- 东南西北中发白
};
我来举个简单的例子来帮助大家理解下这个数据结构,当前数据结构中数组的下标 0的值为 2,这就是表示 1 万这张牌的个数是 2。
数据结构使用
我们为什么使用这种数据结构而不使用其它的数据结构来构造整副牌呢?主要原因有下面几个,我会逐一为大家