麻将胡牌算法

这篇博客介绍了麻将胡牌算法的实现,包括数据结构的设计和胡牌规则的判断。作者通过创建一个int[34]数组表示牌型,然后遍历查找2张以上牌型,接着检查剩余牌是否符合3n+2的胡牌规则。文章提供了一个简化的Java代码示例,并提到实际麻将规则的复杂性可能需要进一步调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一段时间棋牌类非常火,这里简单的实现一下麻将的胡牌算法,算法很简单,核心代码就小几十行。由于本人不太会玩麻将,所以有些专用名词不会叫,知道意思就行。由于每个地方的麻将玩法都不太一样,我们这里先就用一些通用的胡牌规则3n+2的形式,还有最后只剩下一个对子也算胡牌。下面我们简单的介绍一下算法,然后贴一个java实现的代码。

麻将的大概:

麻将一共有34种牌,东,南,西,北,红中,白板,发财,一万--九万,一条--九条,一筒--九筒,总共136张牌。

数据结构:

我们把34种牌定义成一个byte[34]或int[34]的数组,我们这边就用int[34]。每个玩家身上都放一个int[34]的数组,牌数最多是14张,下标表示牌的类型,内容表示牌的数量,类似下图:(东风写成东方了,懒的改了)



 

算法描述:

1、找到所有数量是2张或2张以上的牌,放到一个列表里面,这里的操作不影响玩家手中的牌,不要删掉对子。

2、对上面的列表进行遍历,这时把玩家手中的牌去掉一个相应的对子,如果剩下的牌是3n类型的,也就是说要么3张一样,要么都能结合成1+2+3这种牌型的,那么就算胡牌。

3、接上面,如何判断是3n类型的?首先遍历玩家手上的int[34]数组,每个牌种有5种数量,就是说可以有0到4张牌。

a、在下标小于7时,就是上面的“东,南,西,北,红中,白板,发财”,因为它们不能组成1+2+3这种模式,所以只要它们不等于3就说明不是胡牌。

b、下标7--16(不含),16--25(不含),25-34(不含),这三段的处理方式一致。

*当该牌的数量是0时,则跳过,执行下一种牌;

*当为1时,一定要和后面的2张牌构成一个“顺子”,如果构不成,那就不是胡牌。能构成,则把相应的牌数量减去1,然后执行下一种牌;

*当为2时,方法和1一样,但是执行的不是下一种牌,而是还是当前的牌;

*当为3时,因为可以构成一个“杠”,所以直接跳过执行下一种牌,(这里会涉及到另一种情况,比如3*3*3,每种连续的牌都有三张,可能因为结算的倍数不一致而导致算法不一样,这个具体看麻将规则)。

*当为4时,先执行为1时的步骤,然后执行为3时的步骤。

4、上方的遍历时可以使用递归的调用。

java代码:由于时间花的不多,难免有差错。个人网站:http://www.pengmj.com/396.html

 

public class Mahjong {
	final static String[] paicn = {"东","南","西","北","红","财","白","一万","二万","三万","四万","五万","六万","七万","八万","九万","一条","二条","三条","四条","五条","六条","七条","八条","九条","一筒","二筒","三筒","四筒","五筒","六筒","七筒","八筒","九筒"};
	final static int MJMAX = 136;
	/**
	 * 手上的牌数
	 */
	final static int PLAYINITCOUNT = 14;
	/**
	 * 麻将的种类
	 */
	public final static int MAX_SIZE = 34;
	public static void main(String[] args) {
		long l = System.currentTimeMillis();
		int[] pai = new int[MAX_SIZE];
		//这里是简单的随机发牌
		int[] all = new int[MJMAX];
		for(int n=0;n<4;n++){
			for(int j=0;j<MAX_SIZE;j++){
				all[n*MAX_SIZE+j] = j;
			}
		}
		Set fapai = new HashSet();
		int c = 0;
		while(true){
			Random ran = new Random();
			int t = (int)(ran.nextDouble()*MJMAX);
			if(fapai.contains(t))continue;
			fapai.add(t);
			c++;
			if(c==PLAYINITCOUNT)
				break;
		}
		for(Iterator it=fapai.iterator();it.hasNext();){
			pai[all[it.next().intValue()]]++;
		}
		//~~~~~
		//int[] pai = {0,3,0,2,0,0,0,0,0,0,0,2,2,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
		for(int i=0;i<MAX_SIZE;i++){//打印一下手中牌的中文
			if(pai[i]==0)continue;
			int j = pai[i];
			for(int n=0;n<j;n++){
				System.out.print(paicn[i]);
			}
		}
		System.out.println();
		List yidui = new ArrayList();
		for(int m=0;m<MAX_SIZE;m++){//把所有对拉出来 			if(pai[m]>=2){
				yidui.add(m);
			}
		}
		boolean hu = false;
		for(Integer yd : yidui){
			int[] paitemp = new int[34];
			System.arraycopy(pai, 0, paitemp, 0, MAX_SIZE);//把手中的牌拷贝一份进行操作,可能占用的内存比较大,这个还没仔细考虑过
			paitemp[yd] = paitemp[yd]-2;
			boolean sign = handlerSign(paitemp,0,7);
			if(!sign)continue;
			boolean sun1 = handlerShun(paitemp,7,16);
			if(!sun1)continue;
			boolean sun2 = handlerShun(paitemp,16,25);
			if(!sun2)continue;
			boolean sun3 = handlerShun(paitemp,25,34);
			if(!sun3)continue;
			hu = true;
			break;
		}
		if(hu)
			System.out.println("HU");//胡
		else
			System.out.println("BU HU");//不胡  哈哈
		System.out.println("time:"+(System.currentTimeMillis()-l));
	}
	/**
	 * 检测单牌的
	 */
	public static boolean handlerSign(int[] pai,int start,int end){
		for(int i=start;i<end;i++){
			if(pai[i]!=3)return false;
		}
		return true;
	}
	/**
	 * 检测顺子
	 */
	public static boolean handlerShun(int[] pai,int start,int end){
			int i = start;
			if(i==end-1){
				if(pai[i]==0||pai[i]==3)return true;
				return false;
			}
			if(pai[i]==0){
				return handlerShun(pai,++i,end);
			}
			if(pai[i]<3){
				if(i+2<end&&pai[i+1]>0&&pai[i+2]>0){//后续是否有茬
					pai[i]=pai[i]-1;
					pai[i+1] = pai[i+1]-1;
					pai[i+2] = pai[i+2]-1;
					return handlerShun(pai,i,end);
				}
				else{
					return false;
				}
			}
			else if(pai[i]==3){
				return handlerShun(pai,++i,end);
			}
			else {
				if(i+2<end&&pai[i+1]>0&&pai[i+2]>0){//后续是否有茬
					pai[i]=pai[i]-1;
					pai[i+1] = pai[i+1]-1;
					pai[i+2] = pai[i+2]-1;
					return handlerShun(pai,i,end);
				}
				else {
					return false;
				}
			}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值