这一段时间棋牌类非常火,这里简单的实现一下麻将的胡牌算法,算法很简单,核心代码就小几十行。由于本人不太会玩麻将,所以有些专用名词不会叫,知道意思就行。由于每个地方的麻将玩法都不太一样,我们这里先就用一些通用的胡牌规则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;
}
}
}
}