麻將胡牌算法

這一段時間棋牌類非常火,這裏簡單的實現一下麻將的胡牌算法,算法很簡單,核心代碼就小几十行。由於本人不太會玩麻將,所以有些專用名詞不會叫,知道意思就行。由於每個地方的麻將玩法都不太一樣,我們這裏先就用一些通用的胡牌規則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;
				}
			}
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章