这个算法主要是帮助计算胡的什么牌跟给一些策略,给出几个测试样例自己体会一下就好了,能够比较快的计算出怎么胡牌,如何快速胡牌,无聊写着玩的。
# 使用1-9表示筒子,11-19表示条子,21-29表示万子,31表示红中,32表示发财,33表示白板,41-44表示东南西北
#样例1:
hand = [6,6,7,7,7,8,8,8]
#样例2:
hand = [6,7,7,7,8,8,8,2]
#样例3:
hand = [2,3,12,13]
#样例3:
hand = [2,2,12,13]
直接给出源代码,自己替换一下手牌就好了:
from collections import Counter
# 数字对应的文字映射表,用于将数字转换成麻将牌的名称
tile_names = {
**{i: f"{i}筒" for i in range(1, 10)}, # 1-9 -> 筒子(饼子)
**{i: f"{i - 10}条" for i in range(11, 20)}, # 11-19 -> 条子(索子)
**{i: f"{i - 20}万" for i in range(21, 30)}, # 21-29 -> 万子
31: "红中", # 31 -> 红中
32: "发财", # 32 -> 发财
33: "白板", # 33 -> 白板
41: "东风", # 41 -> 东风
42: "南风", # 42 -> 南风
43: "西风", # 43 -> 西风
44: "北风" # 44 -> 北风
}
# 判断当前手牌是否可以胡牌
def can_win(tiles):
if len(tiles) % 3 != 2: # 检查手牌是否符合胡牌条件 (除3余2)
return False
count = Counter(tiles) # 统计每种牌出现的次数
for tile, cnt in count.items(): # 遍历所有牌
if cnt >= 2: # 如果某张牌出现至少两次(将牌)
remaining_tiles = list(tiles) # 复制手牌
remaining_tiles.remove(tile) # 移除一对将牌
remaining_tiles.remove(tile)
if can_form_melds(remaining_tiles): # 如果剩下的牌可以形成面子(顺子或刻子),则可以胡牌
return True
return False # 没有符合条件的将牌和面子组合,不能胡牌
# 判断手中的牌是否可以组成面子(刻子或顺子)
def can_form_melds(tiles):
if len(tiles) == 0: # 如果没有剩余的牌,说明所有牌都成功组成面子
return True
count = Counter(tiles) # 统计每种牌的数量
# 首先检查是否有刻子(3张相同的牌)
for tile, cnt in count.items():
if cnt >= 3: # 如果有3张相同的牌
remaining_tiles = list(tiles) # 复制剩余的牌
remaining_tiles.remove(tile) # 移除三张相同的牌(刻子)
remaining_tiles.remove(tile)
remaining_tiles.remove(tile)
if can_form_melds(remaining_tiles): # 递归检查剩下的牌是否还能组成面子
return True
# 检查是否有顺子(3张连续的牌),但中发白和风牌不能组成顺子
for tile in tiles:
if tile in {31, 32, 33, 41, 42, 43, 44}: # 中发白和风牌不能组成顺子
continue
if tile + 1 in tiles and tile + 2 in tiles: # 检查当前牌和它后面的两张是否可以组成顺子
remaining_tiles = list(tiles) # 复制剩余的牌
remaining_tiles.remove(tile) # 移除组成顺子的3张牌
remaining_tiles.remove(tile + 1)
remaining_tiles.remove(tile + 2)
if can_form_melds(remaining_tiles): # 递归检查剩下的牌是否还能组成面子
return True
return False # 如果没有找到刻子或顺子,不能组成面子
# 判断手牌是否处于听牌状态
def check_tingpai(hand):
possible_draws = [i for i in range(1, 10)] + [i for i in range(11, 20)] + [i for i in range(21, 30)] + [31, 32, 33] + [41, 42, 43, 44]
ting_pai_list = [] # 用来存储可以听的牌
for draw in possible_draws: # 遍历每张可能的抓牌
new_hand = sorted(hand + [draw]) # 将抓到的牌加入手牌并排序
if can_win(new_hand): # 如果加入这张牌后可以胡牌
ting_pai_list.append(draw) # 将这张牌加入听牌列表
return ting_pai_list # 返回所有可能听的牌
# 将数字牌转换为文字表示(如1筒,2条,3万等)
def tile_to_text(tiles):
return [tile_names[tile] for tile in tiles] # 根据映射表将牌转换为文字表示
# 查找打出什么牌后可以听什么牌的函数
def find_draw_and_discard_options(hand):
possible_draws = [i for i in range(1, 10)] + [i for i in range(11, 20)] + [i for i in range(21, 30)] + [31, 32, 33] + [41, 42, 43, 44]
seen_combinations = set() # 记录已经处理过的组合,避免重复
draw_possibilities = [] # 用来存储摸牌后的打牌策略
# 遍历每张可能的摸牌
for draw in possible_draws:
new_hand = sorted(hand + [draw]) # 摸牌后新的手牌
for discard in hand: # 尝试打出每一张手牌
new_hand_with_discard = list(new_hand) # 复制手牌
new_hand_with_discard.remove(discard) # 移除当前打出的牌
# 如果打牌后能进入听牌阶段
ting_pai_list = check_tingpai(new_hand_with_discard)
if ting_pai_list:
draw_text = tile_names[draw] # 摸的牌转换为文字表示
discard_text = tile_names[discard] # 打出的牌转换为文字表示
ting_pai_text = tile_to_text(ting_pai_list) # 将听牌列表转换为文字表示
# 打印摸牌后的打牌策略
if (draw, discard, tuple(sorted(ting_pai_list))) not in seen_combinations:
seen_combinations.add((draw, discard, tuple(sorted(ting_pai_list)))) # 记录组合
draw_possibilities.append((draw_text, discard_text, ting_pai_text)) # 记录摸牌策略
# 找出最多听牌方案数量
if draw_possibilities:
max_ting_pai_count = max(len(ting_pai_text) for _, _, ting_pai_text in draw_possibilities) # 找到最多听牌的数量
# 找出所有听牌数量最多的方案
max_draw_possibilities = [
(draw_text, discard_text, ting_pai_text)
for draw_text, discard_text, ting_pai_text in draw_possibilities
if len(ting_pai_text) == max_ting_pai_count
]
# 打印所有最多听牌方案的策略
print(f"# 目前处于一进一叫阶段,摸牌后打牌策略如下所示:")
for draw_text, discard_text, ting_pai_text in max_draw_possibilities:
print(f"\n摸上 {draw_text} 后,共有 {len(ting_pai_text)} 种打牌方案,分别为:")
for discard in ting_pai_text: # 直接打印集合中的打牌方案
print(f" - {discard}")
print() # 换行
# 打印每一张听的牌的剩余数量和总的剩余张数
def print_remaining_tiles(tingpai_list, hand):
hand_counter = Counter(hand) # 统计当前手牌
sum_remaining = 0 # 用来统计总剩余数量
for tingpai_number in tingpai_list: # 遍历每张听牌(这里使用数字形式的牌)
# 获取听牌对应的文字(如2筒 -> "2筒")
tingpai = tile_names[tingpai_number]
# 计算剩余数量,4是每张牌的总数量,减去手牌中的数量
remaining_count = 4 - hand_counter.get(tingpai_number, 0)
# 打印出每张听牌的剩余数量
print(f"听{tingpai},除去手牌,还剩 {remaining_count} 张")
# 累加到总剩余数量
sum_remaining += remaining_count
# 打印总剩余数量
print(f"\n总共听牌的数量为:{sum_remaining} 张牌")
# 打印所有的打牌方案
def find_discard_and_draw_options(hand):
discard_tingpai_dict = {} # 用来保存每次打出后的听牌方案,避免重复
# 检查当前是否符合打牌前的听牌条件
if len(hand) % 3 == 2: # 只有在手牌数是13的情况下才进行计算
for tile in hand:
new_hand = hand.copy()
new_hand.remove(tile) # 移除这张牌,模拟打出这张牌
tingpai_list = check_tingpai(new_hand)
# 如果有听牌,则记录,并去重
if tingpai_list:
discard_tingpai_dict[tile_names[tile]] = tingpai_list
# 打印去重后的听牌方案
for discard, tingpai_list in discard_tingpai_dict.items():
print()
print("# 目前处于打牌——>听牌阶段,听牌方案如下所示:↓-----------↓-----------↓-----------↓-----------↓-----------↓-----------↓-----------↓")
print()
print(f"打出 {discard} 后,你可以听到以下牌:")
# 打印每个听牌的剩余数量和总的剩余数量
print_remaining_tiles(tingpai_list, hand)
print("↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑------↑")
print()
# 示例手牌
hand = [6,7,7,7,8,8,8,6] # 手牌
# 调用函数,展示一进一叫阶段的摸牌后打牌策略
if len(hand) % 3 == 1:
if check_tingpai(hand) != []:
print()
print(
"# 目前处于听牌阶段,听牌方案如下所示:↓-----------↓-----------↓-----------↓-----------↓-----------↓-----------↓-----------↓")
print_remaining_tiles(check_tingpai(hand), hand)
print(
"↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑-----------↑------↑")
print()
else:
find_draw_and_discard_options(hand)
if len(hand) % 3 == 2:
if can_win(hand):
print("可以胡牌!!!!!")
else:
find_discard_and_draw_options(hand)