你有没有想过,一张张牌面、一串串数字,居然能被写成一段段代码?在很多人眼里,打麻将是一种讲究技巧和运气的娱乐方式;但在程序员眼里,它却是一个绝佳的算法训练场——尤其是“胡了”那一刻的瞬间快感,简直堪比程序成功运行的成就感!
我就带大家用 Python 实现一个简易版的“麻将胡了判断器”,别担心,不需要你会打麻将,只要你会看懂代码,就能感受到那种“啊!我胡了!”的兴奋。
我们先来定义什么是“胡了”——在标准中国麻将规则中,一副合法的胡牌必须由四个顺子或刻子加一个对子组成,123万 + 456筒 + 789条 + 11条(对子),这就是一手好牌。
我们的目标是:给定一组牌([1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 表示1万、1万、2万、3万、4筒、5筒、6筒、7条、8条、9条、10条、11条),判断是否能胡牌。
第一步:数据结构设计
我们将每张牌用数字表示,比如万字牌用1-9,筒子用10-18,条子用19-27,风牌和箭牌可以单独处理(本例简化不考虑),输入是一个列表,我们先统计每个数字出现的次数。
from collections import Counter
def is_valid_hu(cards):
counts = Counter(cards)
第二步:枚举所有可能的对子
我们要尝试每一种可能的对子(即两个相同的牌),然后检查剩下的牌能否分成三组顺子或刻子。
for pair in counts:
if counts[pair] < 2:
continue
# 拿掉一对
new_counts = counts.copy()
new_counts[pair] -= 2
if can_form_groups(new_counts):
return True
第三步:核心逻辑——递归判断能否分组
这是一个典型的回溯问题,我们从最小的牌开始,依次尝试:
- 如果是刻子(三个一样的):直接减去三个
- 如果是顺子(连续三个,如1,2,3):也减去这三个
- 否则,无法组成合法组合
def can_form_groups(counts):
if not any(counts.values()):
return True
# 找最小的非零牌
min_card = min(k for k in counts if counts[k] > 0)
# 尝试刻子
if counts[min_card] >= 3:
new_counts = counts.copy()
new_counts[min_card] -= 3
if can_form_groups(new_counts):
return True
# 尝试顺子(需要后续两张牌存在)
if counts[min_card] > 0 and \
counts.get(min_card + 1, 0) > 0 and \
counts.get(min_card + 2, 0) > 0:
new_counts = counts.copy()
new_counts[min_card] -= 1
new_counts[min_card + 1] -= 1
new_counts[min_card + 2] -= 1
if can_form_groups(new_counts):
return True
return False
整个函数整合如下:
def is_hu(cards):
from collections import Counter
def can_form_groups(counts):
if not any(counts.values()):
return True
min_card = min(k for k in counts if counts[k] > 0)
if counts[min_card] >= 3:
new_counts = counts.copy()
new_counts[min_card] -= 3
if can_form_groups(new_counts):
return True
if counts[min_card] > 0 and \
counts.get(min_card + 1, 0) > 0 and \
counts.get(min_card + 2, 0) > 0:
new_counts = counts.copy()
new_counts[min_card] -= 1
new_counts[min_card + 1] -= 1
new_counts[min_card + 2] -= 1
if can_form_groups(new_counts):
return True
return False
counts = Counter(cards)
for pair in counts:
if counts[pair] >= 2:
new_counts = counts.copy()
new_counts[pair] -= 2
if can_form_groups(new_counts):
return True
return False
测试一下:is_hu([1,1,2,3,4,5,6,7,8,9,10,11]) → 返回 True,恭喜你,这手牌能胡!
是不是很酷?这不是单纯的打麻将,而是把现实世界的问题抽象成数学模型,再用代码解决,这正是编程的魅力所在:让“运气游戏”变得可计算、可验证、可复用。
下次当你打麻将时,不妨想一想:你手中的牌,是不是也能被一行行代码“算”出来呢?

麻将胡了






