麻将开金算法java代_通用麻将胡牌算法

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

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

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条克子 . 能则能胡 反则不能.

9d0101bc9775a99498e3c7ac36fbf9ff.png

如上图 用数字表示为 {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 mah, int ID)

{

List pais = new List(mah);

pais.Add(ID);

//只有两张牌

if (pais.Count == 2)

{

return pais[0] == pais[1];

}

//先排序

pais.Sort();

//依据牌的顺序从左到右依次分出将牌

for (int i = 0; i < pais.Count; i++)

{

List paiT = new List(pais);

List 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 mahs)

{

if (mahs.Count == 0)

{

return true;

}

List 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;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
麻将胡牌算法涉及到很多细节和规则,下面是一个基本的JAVA实现: 1. 首先,我们需要一个表示牌的数据结构,可以使用一个数组或者列表来存储玩家手牌的牌型。 ```java public class Mahjong { public static final int MAX_COUNT = 34; // 麻将牌的总数 public static final int MAX_WEAVE = 4; // 最大组合数 public static final int COLOR_WAN = 0; // 万 public static final int COLOR_TIAO = 1; // 条 public static final int COLOR_TONG = 2; // 筒 public static final int COLOR_FENG = 3; // 风 public static final int COLOR_JIAN = 4; // 箭 public static final int CARD_FENG_EAST = 27; // 东 public static final int CARD_FENG_SOUTH = 28; // 南 public static final int CARD_FENG_WEST = 29; // 西 public static final int CARD_FENG_NORTH = 30; // 北 public static final int CARD_JIAN_ZHONG = 31; // 中 public static final int CARD_JIAN_FA = 32; // 发 public static final int CARD_JIAN_BAI = 33; // 白 private int[] cards = new int[MAX_COUNT]; private int count = 0; // 添加一张牌 public void addCard(int card) { cards[card]++; count++; } // 移除一张牌 public void removeCard(int card) { if (cards[card] > 0) { cards[card]--; count--; } } // 获取指定牌的数量 public int getCardCount(int card) { return cards[card]; } // 清除手牌 public void clear() { for (int i = 0; i < MAX_COUNT; i++) { cards[i] = 0; } count = 0; } // 获取所有牌的数量 public int getCount() { return count; } } ``` 2. 接下来,我们需要实现一个判断是否能够胡牌的函数。这个函数需要考虑到麻将的基本规则,如顺子、刻子、将牌等。 ```java public class MahjongUtils { public static final int MAX_HU_COUNT = 14; // 最大胡牌数 // 判断是否能够胡牌 public static boolean checkHu(Mahjong mahjong) { int[] cards = mahjong.getCards(); int count = mahjong.getCount(); // 如果牌的数量不是3n+2,则不能胡牌 if ((count % 3) != 2) { return false; } // 拆分牌型,分别判断顺子、刻子、将牌 int[] cardsCopy = Arrays.copyOf(cards, cards.length); int[] huCards = new int[MAX_HU_COUNT]; int huCount = 0; for (int i = 0; i < Mahjong.MAX_COUNT; i++) { if (cardsCopy[i] > 0) { // 判断顺子 if (i < Mahjong.CARD_FENG_EAST) { if (i % 9 <= 6 && cardsCopy[i + 1] > 0 && cardsCopy[i + 2] > 0) { cardsCopy[i]--; cardsCopy[i + 1]--; cardsCopy[i + 2]--; } } // 判断刻子 if (cardsCopy[i] >= 3) { cardsCopy[i] -= 3; } // 判断将牌 if (cardsCopy[i] == 2) { huCards[huCount++] = i; cardsCopy[i] -= 2; } } } // 如果剩余的牌型能够组成完整的顺子和刻子,则说明可以胡牌 for (int i = 0; i < Mahjong.MAX_COUNT; i++) { if (cardsCopy[i] != 0) { return false; } } return true; } } ``` 3. 最后,我们可以编写一个测试函数来验证算法的正确性。 ```java public class MahjongTest { public static void main(String[] args) { Mahjong mahjong = new Mahjong(); mahjong.addCard(Mahjong.CARD_WAN_1); mahjong.addCard(Mahjong.CARD_WAN_1); mahjong.addCard(Mahjong.CARD_WAN_1); mahjong.addCard(Mahjong.CARD_WAN_2); mahjong.addCard(Mahjong.CARD_WAN_3); mahjong.addCard(Mahjong.CARD_WAN_4); mahjong.addCard(Mahjong.CARD_WAN_5); mahjong.addCard(Mahjong.CARD_WAN_6); mahjong.addCard(Mahjong.CARD_WAN_7); mahjong.addCard(Mahjong.CARD_WAN_8); mahjong.addCard(Mahjong.CARD_WAN_9); mahjong.addCard(Mahjong.CARD_TONG_1); mahjong.addCard(Mahjong.CARD_TONG_1); boolean isHu = MahjongUtils.checkHu(mahjong); System.out.println("是否胡牌:" + isHu); } } ``` 这样,我们就实现了一个简单的麻将胡牌算法。当然,实际上麻将的规则非常复杂,这个算法只是一个基本的实现,还需要根据具体的麻将规则进行进一步的优化和调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值