通用麻将胡牌算法

通用麻将胡牌算法

  声明: 算法并非原创 , 但是来源已经忘记了 , 当时考虑算法的时候看了比较多的麻将胡牌算法 , 想寻找自己比较容易理解的 , 找了几篇,所以算法的出处已然忘记,不过还是感谢下原创吧 .

  算法理解之后就不难了 , 下面开始详细的阐述了.

  1. 将麻将抽象为数字

      数字 {01 ~ 09} 表示 {1 ~ 9} 筒

      数字 {11 ~ 19} 表示 {1 ~ 9} 条

      数字 {21 ~ 29} 表示 {1 ~ 9} 万

      数字 {31 33 35 37 } 表示 { 东 南 西 北 }

      数字 {41 43 45} 表示 {中 發 白}

      数字10 20 30 32 34 36 40 42 44 46 空出来不代表任何麻将牌 这样设计的好处就是使得能够形成顺子的牌在用数字表示出来的时候刚好也是连着的 , 而不能够形成顺子的牌,在用数字表示的时候并不是顺子 . 便于以后使用代码进行判断

  2. 算法的核心流程

      玩过麻将的都知道麻将玩家手上一般有两种牌 一种是手牌 一种是已经碰牌或者吃牌或者杠牌之后已经明了的牌 . 现在以玩家A为例 , 把牌分的细一点

      a. 玩家A的手牌 (数量1 + 3*n n < N , n < 4 )

      b. 其他玩家打出来的牌 (数量 1)

      c. 玩家A从牌面上取出来的牌 (数量 1)

      d. 玩家A吃碰杠的牌 (3*n + 4*m)

      能否胡牌主要是看手牌a 和b/c 的组合能否形成一对加n条顺子和m条克子 . 能则能胡 反则不能.
    这里写图片描述
      

      如上图 用数字表示为 {1,1,2,2,2,3,4,11,12,12,13,13,14,1} 前13张牌为手牌,最后一张二条为玩家A从牌面上取出的牌

OK, 现在只需要先取出一对将,然后判断剩下的牌能否全部形成顺子或者克子,现在对牌面按照相对应的数字进行从小到大的排序 . 现在从剩余的牌中最左边的牌开始 , 如果只有一张这样的牌那么这张牌A就只能当作顺子的开头 ; 如果有两张这样的牌 , 因为已经有了一对将而这两张也不能组成克子 , 所以这两张只能当作两个顺子的开头 ; 如果有三张这样的牌 , 可以组成克子 , 但是如果让他组成顺子则要求为 AAABBBCCC 与后面的三张也能组成克子 所以组成顺子或者克子本质是相同的 但是组成克子AAA的通用性要高于组成顺子AAABBBCCC 所以当有三个及以上这样牌的时候优先组成克子AAA ; 如果有四张这样的牌,要能胡牌则需要 AAAABBBBCCCC 或者 AAAABC ,对于是先组一个顺子还是一个克子都会回到上述的情况 .

(这里没有对七对等各类大胡作出判断)

步骤一:从上述数组中找到一对做”将”,并从数组中移除 , 这里共有4对牌所以要分成4种情况

  1. {1,1}(将牌) , {1,2,2,2,3,4,11,12,12,13,13,14}(余牌)

  2. {2,2}(将牌) , {1,1,1,2,3,4,11,12,12,13,13,14}(余牌)

  3. {12,12}(将牌) , {1,1,1,2,2,2,3,4,11,13,13,14}(余牌)

  4. {13,13}(将牌) , {1,1,1,2,2,2,3,4,11,12,12,14}(余牌)

  依次进行步骤二的检查 检查完最后一种情况而没有返回 “能胡牌” 则返回 不能胡牌

步骤二: 余牌数量为0 则返回 “能胡牌” 否则进入下一步 .

步骤三: 判断余牌前三张是否相同 相同-> 步骤四 ; 不同 -> 步骤五.

步骤四: 移除余牌中的前三张牌 , 返回步骤二.

步骤五: 若余牌中第一个数为N , 则判断是否有N + 1 与 N + 2 同时存在与余牌中 , 有将N , n+1 , n+2 从余牌中移除并返回 步骤二 , 否则返回 步骤一

演示如下

  1. {1,1}(将牌) , {1,2,2,2,3,4,11,12,12,13,13,14}(余牌)

    步骤二 –> 步骤三 –> 步骤五 == {2,2,4,11,12,12,13,13,14}(余牌) –>

   步骤二 –> 步骤三 –> 步骤五 –> 步骤一

  2. {2,2}(将牌) , {1,1,1,2,3,4,11,12,12,13,13,14}(余牌)

    步骤二 –> 步骤三 –> 步骤四 == {2,3,4,11,12,12,13,13,14}(余牌) –>

    步骤二 –> 步骤三 –> 步骤五 == {11,12,12,13,13,14}(余牌)–>

    步骤二 –> 步骤三 –> 步骤五 == {12,13,14}(余牌)–>

    步骤二 –> 步骤三 –> 步骤五 == {}(余牌) –>

    步骤二 “能胡牌”

public static bool IsCanHU(List<int> mah, int ID)
{
    List<int> pais = new List<int>(mah);

    pais.Add(ID);
    //只有两张牌
    if (pais.Count == 2)
    {
        return pais[0] == pais[1];
    }

    //先排序
    pais.Sort();

    //依据牌的顺序从左到右依次分出将牌
    for (int i = 0; i < pais.Count; i++)
    {
        List<int> paiT = new List<int>(pais);
        List<int> ds = pais.FindAll(delegate (int d)
        {
            return pais[i] == d;
        });

        //判断是否能做将牌
        if (ds.Count >= 2)
        {
            //移除两张将牌
            paiT.Remove(pais[i]);
            paiT.Remove(pais[i]);

            //避免重复运算 将光标移到其他牌上
            i += ds.Count;

            if (HuPaiPanDin(paiT))
            {
                return true;
            }
        }
    }
    return false;
}

private static bool HuPaiPanDin(List<int> mahs)
{
    if (mahs.Count == 0)
    {
        return true;
    }

    List<int> fs = mahs.FindAll(delegate (int a)
    {
        return mahs[0] == a;
    });

    //组成克子
    if (fs.Count == 3)
    {
        mahs.Remove(mahs[0]);
        mahs.Remove(mahs[0]);
        mahs.Remove(mahs[0]);

        return HuPaiPanDin(mahs);
    }
    else
    { //组成顺子
        if (mahs.Contains(mahs[0] + 1) && mahs.Contains(mahs[0] + 2))
        {
            mahs.Remove(mahs[0] + 2);
            mahs.Remove(mahs[0] + 1);
            mahs.Remove(mahs[0]);

            return HuPaiPanDin(mahs);
        }
        return false;
    }
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
麻将算法是一个比较复杂的问题,需要考虑到很多因素,比如型、风、的顺序、听、打法等等。以下是一个简单的 Python 麻将算法: 1. 初始化库 首先,我们需要定义一副,并随机洗。假设我们使用的是国标麻将,一副包括 136 ,分为万、条、饼、东西南北中发白七种花色。每种花色有 1 到 9 ,风和箭各有 4 。我们可以使用一个数组来表示一副完整的: ```python pai = ['1万', '2万', ..., '9饼', '东', '南', '西', '北', '中', '发', '白', '1万', '2万', ..., '9饼', '东', '南', '西', '北', '中', '发', '白', '1万', '2万', ..., '9饼', '东', '南', '西', '北', '中', '发', '白', '1万', '2万', ..., '9饼', '东', '南', '西', '北', '中', '发', '白'] ``` 然后,我们使用 Python 的 random 库来进行洗: ```python import random random.shuffle(pai) ``` 2. 定义玩家手牌 我们可以定义一个列表来表示玩家的手牌,根据麻将规则,每个玩家初始有 13 : ```python player_hand = pai[0:13] ``` 3. 判断是否可以和是指玩家手牌中满足和条件的情况,包括听和自摸两种情况。听是指玩家手牌中只差一就可以和的情况,而自摸是指玩家摸到了一可以和。 判断是否可以和需要考虑很多因素,包括和方式、型、风、打法等等。这里只给出一个简单的判断方式: ```python def can_hu(player_hand): """ 判断玩家手牌是否可以和。 """ # 首先判断是否可以自摸 for pai in pai_list: if check_hu(player_hand + [pai]): return True # 如果不能自摸,则判断是否可以听 for pai in player_hand: tmp_hand = player_hand.copy() tmp_hand.remove(pai) for pai2 in pai_list: if check_hu(tmp_hand + [pai2]): return True return False ``` 其中,check_hu 函数用于判断一副是否可以和,这里不再赘述。 4. 判断是否可以杠是指玩家手牌中有四相同的或者三相同的加上一指定的。判断是否可以杠也需要考虑很多因素,包括明杠和暗杠、杠后摸和杠后不摸等等。这里只给出一个简单的判断方式: ```python def can_gang(player_hand, pai): """ 判断玩家手牌中是否可以杠。 """ if player_hand.count(pai) == 3: return 'an_gang' # 暗杠 elif player_hand.count(pai) == 4: return 'ming_gang' # 明杠 else: return False ``` 其中,pai 是要杠的。 5. 判断是否可以 是指玩家手牌中有两相同的,再加上一由其他玩家打出的指定。判断是否可以也比较简单: ```python def can_peng(player_hand, pai): """ 判断玩家手牌中是否可以。 """ if player_hand.count(pai) >= 2: return True else: return False ``` 其中,pai 是由其他玩家打出的。 以上只是一个简单的麻将算法,实际应用中需要考虑更多的因素。如果您需要更详细的算法实现,可以参考一些开源的麻将游戏项目,比如 MahjongAI 和 Mjolnir。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值