一个简单的麻将算法

这个算法主要是帮助计算胡的什么牌跟给一些策略,给出几个测试样例自己体会一下就好了,能够比较快的计算出怎么胡牌,如何快速胡牌,无聊写着玩的。

# 使用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)

 

 

你可能感兴趣的:(算法,python,windows)