正常的麻将胡牌方式为满足N * ABC + M *DDD +EE 的形式,及存在一个对子(EE),剩余牌均能组成顺子(ABC)或者刻子(DDD)。
很容易发现必须满足size%3 == 2的形式才可以去计算胡牌。
数据结构的选取:
麻将有万、饼、条各九种,另外还有东西南北中,春夏秋冬。
种类不是很多,一个字节表示就可以了,前四位代表类型,后四位代表值,东西南北中,春夏秋冬可以集中到一种类型中去。
普通麻将的计算方式:
- 1.首先找出所有包含一对的情形,移除对子(注意去重),记下剩余牌的所有集合为Tn;
- 2.针对每个Tn中的数组尝试移除一个顺子,成功转到2,失败到3。
- 3.针对每个Tn中的数组尝试移除一个刻子(DDD),成功转到2。
- 4.若当前的数组的数量变为0,则表示,当前的方案可以胡牌。
2,3,4可以作为一个check_3n(检测是否满足N * ABC + M *DDD)的函数,递归调用即可。
针对有癞子的麻将(百搭):
最简单的办法是尝试将癞子牌变为所有派来进行尝试,不过如果手中有多张癞子牌的话计算量就相当大了,比如3张,则需要计算牌的种类的3次方次,虽然中途可以通过剪枝减少部分计算量,但还是太慢了。
针对这种情况我们可以在计算出癞子的数量,如果出现找出顺子或刻子失败,我们则可以用癞子去补,如果失败了,那么当前的方案就不通过。
- 1.同样找出所有包含一对的情形,移除对子,移除的时候需要注意更新癞子的数量这里需要注意的是对子是怎么产生的:
- 原有的对子
- 一个癞子和一普通的组成的对子
- 一对癞子
- 2.针对每个数组尝试移除一个顺子,成功转到2,如果失败尝试用癞子去补,癞子也不够,转到3。
- 3.针对每个数组尝试移除一个刻子(DDD),成功转到2,如果失败尝试用癞子去补,癞子也不够,当前的方案就不通过。
- 4.若当前的数组的数量变为0,则表示,当前的方案可以胡牌。
有些人好像还不很了解,补充一个lua版无癞子的算法吧(很多复制都可以优化掉):
function table_copy_table(ori_tab)
if (type(ori_tab) ~= "table") then
return nil
end
local new_tab = {}
for i,v in pairs(ori_tab) do
local vtyp = type(v)
if (vtyp == "table") then
new_tab[i] = table_copy_table(v)
elseif (vtyp == "thread") then
new_tab[i] = v
elseif (vtyp == "userdata") then
new_tab[i] = v
else
new_tab[i] = v
end
end
return new_tab
end
function sort_card(t)
table.sort(t, function(a,b)
local sa,ra = a&0xf,a&0xf0
local sb,rb = b&0xf,b&0xf0
if ra == rb then
return sa < sb
else
return ra < rb
end
end)
end
function remove_three_same(t)
assert(#t%3 == 0 and #t > 0)
local found = false
local begin
for i=1,#t - 2 do
if t[i] == t[i + 1] and t[i] == t[i + 2] then
found = true
begin = i
break
end
end
if found