python麻将和牌算法

python麻将和牌算法

之前用vba写过,当时的思路不清楚,也没有python这样强大的工具,写了好长时间。

现在想想,真的是太太太落后了。磨刀不误砍柴工,学习还是大有裨益的。

麻将和牌规则:

胡牌的基础牌型:(1)11、123、123、123、123(2)11、123、123、123、111(1111,下同)(3)11、123、123、111、111(4)11、123、111、111、111(5)11、111、111、111、111胡牌的特殊牌型:11、11、11、11、11、11、11(七对)这里先判断7对的牌型,剩下的和牌牌型,包括基础牌型,也可以是吃碰之后的牌,所以数量可能少于14张。为保证函数solid,成为不需要维护的代码,那么就需要进行参数检查。考虑数字是否在定义的规则范围内;考虑参数的数量是否合规;考虑参数是否包含一个必备选项:对子;考虑是否是特殊和牌类型:7对;常规和牌判断;错误回顾:1,首次制作,未考虑到遍历完成,未和牌的情况,导致11,235,678 这样的未和牌的,没有返回数据。2,首次制作的时候,对于顺子的判断出现了错误。比如:112233,一个牌型可能有多张,排序完成后,123在数字上连续,但是位置上不一定相邻。所以不能直接用切片判断,应该用 a[0]+1 in a 这样的方式进行判断。3,和牌考虑是否超过了4张。没想到做一个小的项目要考虑诸多问题,如何防止此类错误的发生?1,事先把限制条件都列出来,然后在做代码的时候,考虑进去,测试时再检查一遍。2,做分枝或判断时,要确定优先次序,彻底思考每一个步骤,是否考虑了所有的情况。

#coding='utf-8'

#author='小罗QQ1271801445'

#麻将胡牌算法

#判定规则:n*(abc)+m*(ddd)+ee

#特殊牌型:7*(ee),7对。

##规则:

##胡牌的基础牌型:

##(1)11、123、123、123、123

##(2)11、123、123、123、111(1111,下同)

##(3)11、123、123、111、111

##(4)11、123、111、111、111

##(5)11、111、111、111、111

##胡牌的特殊牌型:

##11、11、11、11、11、11、11(七对)

#国士无双、十三幺九。由1筒(饼)、9筒(饼)、1条(索)、9条(索)、1万、9万、东、南、西、北、中、发、白十三种牌统称幺九牌。

#胡牌时这十三种牌某一种有两枚,而另十二种各一枚,共计十四枚,即构成十三幺。

##其中:1=单张 11=将、对子 111=刻子 1111=杠 123=顺子

##设计:

##牌型:万条饼,东西南北风,中发白。

##万:1-9

##条:11-19

##饼:21-29

##东西南北风:31,33,35,37

##中发白:41,43,45

##这样定义,方便进行连续性计算,防止 东西南风,这样凑成一组牌。

##思路,先判断7对子,然后进行常规判断。遍历和剪枝。

##先找到数量大于等于2的牌,然后去掉,那么剩下的牌,要么连续的3张,或是相同的3张。

#3张相同的,肯定是相连的3张。

#3张连续的,可能是相邻的,也可能有跳跃,比如11,2222,33,结果是22,123,123,和牌成功。如果是只做相邻检测,则结果会不对。

##为保证函数solid,成为不需要维护的代码,那么就需要进行参数检查。

##考虑数字是否在定义的规则范围内;

##考虑参数的数量是否合规;

##考虑参数是否包含一个必备选项:对子;

##考虑是否是特殊和牌类型:7对;

##常规和牌判断;

##

##错误回顾:

##1,首次制作,未考虑到遍历完成,未和牌的情况,导致11,235,678 这样的未和牌的,没有返回数据。

##2,首次制作的时候,对于顺子的判断出现了错误。比如:112233,一个牌型可能有多张,排序完成后,123在数字上连续,但是位置上不一定相邻。所以不能直接用切片判断,应该用 a[0]+1 in a 这样的方式进行判断。

##3,另外一个重大错误,举例:345,55,567,88,检查和牌项时,58都是和牌项,但是5明细已经没有牌了。所以要做数量的限制。

##知识点:通过减少print,可以大幅度减少运行时间。

import time,sys

import random #用于测试。

#公共参数,1套牌库,注意总共是4套。

pais=list(range(1,10))+list(range(11,20))+list(range(21,30))+list(range(31,38,2))+list(range(41,46,2))

def hepai(a:list):

'''Judge cards hepai. For excample:a=[1,2,3,4,4]

a=list,万:1-9,条:11-19,饼:21-29,东西南北风:31,33,35,37,中发白:41,43,45。'''

a=sorted(a)

#print(a)

#牌面检查,是否属于本函数规定的范围内。

#pais=list(range(1,10))+list(range(11,20))+list(range(21,30))+list(range(31,38,2))+list(range(41,46,2))

#print(pais)

for x in set(a):

if a.count(x)>4:#某张牌的数量超过了4,是不正确的。

return False

if x not in pais:

print('参数错误:输入的牌型{}不在范围内。\n万:1-9,条:11-19,饼:21-29,东西南北风:31,33,35,37,中发白:41,43,45。'.format(x))

return False

#牌数检查。

if len(a)%3!=2:

print('和牌失败:牌数不正确。')

return False

#是否有对子检查。

double=[]

for x in set(a):

if a.count(x)>=2:

double.append(x)

#print(double)

if len(double)==0:

#print('和牌失败:无对子')

return False

#7对子检查(由于不常见,可以放到后面进行判断)

#对子的检查,特征1:必须是14张;特征2:一个牌型,有2张,或4张。特别注意有4张的情况。

if len(a)==14:

for x in set(a):

if a.count(x) not in [2,4]:

break

else:

## print('和牌:7对子。',a)

return True

#十三幺检查。

if len(a)==14:

gtws=[1, 9, 11, 19, 21, 29, 31, 33, 35, 37, 41, 43, 45] #[1,9,11,19,21,29]+list(range(31,38,2))+list(range(41,46,2)) #用固定的表示方法,计算速度回加快。

#print(gtws)

for x in gtws:

if 1<=a.count(x)<=2:

pass

else:

break

else:

print('和牌:国土无双,十三幺!')

return True

#常规和牌检测。

a1=a.copy()

a2=[] #a2用来存放和牌后分组的结果。

for x in double:

#print('double',x)

#print(a1[0] in a1 and (a1[0]+1) in a1 and (a1[0]+2) in a1)

a1.remove(x)

a1.remove(x)

a2.append((x,x))

for i in range(int(len(a1)/3)):

#print('i-',i)

if a1.count(a1[0])==3:

#列表移除,可以使用remove,pop,和切片,这里切片更加实用。

a2.append((a1[0],)*3)

a1=a1[3:]

#print(a1)

elif a1[0] in a1 and a1[0]+1 in a1 and a1[0]+2 in a1:#这里注意,11,2222,33,和牌结果22,123,123,则连续的3个可能不是相邻的。

a2.append((a1[0],a1[0]+1,a1[0]+2))

a1.remove(a1[0]+2)

a1.remove(a1[0]+1)

a1.remove(a1[0])

#print(a1)

else:

a1=a.copy()

a2=[]

#print('重置')

break

else:

#print('和牌成功,结果:',a2)

return True

#如果上述没有返回和牌成功,这里需要返回和牌失败。

else:

#print('和牌失败:遍历完成。')

return False

#单元测试:

#assert hepai([1,1,2,2,3,3,4,4,5,5,6,6,7,7])==True,'7对和牌'

try:

print('单元测试开始:',

hepai([1,1,2,2,3,3,4,4,5,5,6,6,7,7])==True,#7对和牌。

hepai([1,1,1,1,13,13,4,4,5,5,6,6,17,17]),#含4个一样牌的7对。

hepai([1,9,11,19,21,29]+list(range(31,38,2))+list(range(41,46,2))+[29,])==True, #十三幺测试。

hepai([1,1,2,2,2,2,3,3])==True,#不连续和牌。首次写的时候,这里给忽略掉了。

hepai([1,1,1,2,2,2,3,3,3,4,5,17,18,19])==True, #正常和牌。

hepai([18,18,31,33,35,31,31,33,33,35,35])==True, #东西南北风

hepai([33,34,35,36,36])==False, #参数错误。

hepai([1,2,3,4])==False, #数量不正确。

hepai([1,2,3,4,5])==False, #无对子。

hepai([1,1,2,3,5,6,7,8])==False) #遍历完成,不和牌。

print('单元测试结束,如果有False,请检查。')

print('*'*50)

except:

print('运行测试未通过,请检查。')

运行结果:

和牌成功,结果: [(1, 1), (2, 3, 4), (2, 3, 4), (5, 6, 7), (5, 6, 7)]

和牌成功,结果: [(2, 2), (1, 2, 3), (1, 2, 3)]

和牌成功,结果: [(3, 3), (1, 1, 1), (2, 2, 2), (3, 4, 5), (17, 18, 19)]

和牌成功,结果: [(18, 18), (31, 31, 31), (33, 33, 33), (35, 35, 35)]

参数错误:输入的牌型不在范围内。

万:1-9,条:11-19,饼:21-29,东西南北风:31,33,35,37,中发白:41,43,45。

和牌失败:牌数不正确

和牌失败:无对子

和牌失败:遍历完成。

单元测试开始: True True True True True True True True

单元测试结束,如果有False,请检查。

相关推荐

iis 怎麼看版本:深度解析與實用指南,輕鬆掌握您的IIS伺服器資訊
山楂泡水喝的功效和作用
o365邮箱

山楂泡水喝的功效和作用

📅 01-14 👁️ 2691
创建了Cisco账号,Cisco 软件却无法登录
o365邮箱

创建了Cisco账号,Cisco 软件却无法登录

📅 08-06 👁️ 714