麻將通常有13張牌,在打的時候隨著吃,碰越來越少。總數應該維持在3*n + 1,n=0~4,比如1張,4張,7張或10張。胡牌時,加一張(可能是自摸或吃碰來的一張)組成n個順子或暗刻,外加一個麻將對。
這裡要研究的要點是:
1. 給出3n + 2張牌如何判斷是否已經胡牌,所有的胡牌可能組合;
2. 如果給出3n+1張牌如何判斷是否已經挺牌,挺哪些牌。
這兩個問題其實主要是第一個問題,也就是如何判斷3n +2 張牌是否胡牌的問題。後者可以簡單地通過實驗加34種麻將牌之一看是否胡牌來判斷是否挺牌,以及挺哪些牌。
如何判斷3n +2張牌是否胡牌
麻將牌包括:
1條~9條
1萬~9萬
1餅~9餅
東西南北中發白
共34種牌,34×4=136張牌。
給每張牌設一個編號
1條~9條 -------à 0 ~8
1萬~9萬 ------à 9~17
1餅~9餅 ------à 18~26
東西南北中發白 -----à 27~33
1 牌的儲存
設一個巨集,就是牌的種類
#define MAX_TILE_INDEX 34
所摸的牌就可以存在一個長度為34的陣列中
int tiles[MAX_TILE_INDEX];
陣列的每個成員最大值為4,因為每張牌的總數為4,就算摸到暗槓也不過是4. 所有陣列成員加起來應該是3n + 2
2 結果的儲存
每個胡牌必定是若干順子/暗刻,外加一個麻將對,用一個簡單的結構或類大概就是:
{
int nSequence[4][3];
int nPair[2]; //或直接nPair,不用陣列就可以表示麻將對了
}
給定的3n + 2張牌普通只胡一種情況,但特殊情況也可能有多種胡法,比如4個一萬,4個兩萬,4個3萬,2個四萬
胡牌至少可以:
i. 4個1萬,2萬和3萬的順子,外加4萬的麻將對;
ii. 1個1萬,2萬和3萬的順子,1萬暗刻,2萬暗刻,3萬暗刻,以及4萬麻將對。
這些結果都應該被存起來,胡牌應該以最大番數計算。
C++可以用一個vector模板來實現結果列表,java可以考慮用雜湊表。
3. 胡牌判斷演算法
判斷胡牌與否
判斷胡牌與否的過程:
3.1. 首先判斷總牌數是否為3n + 2,如果不是肯定不胡牌
3.2. 遍歷所有牌,找到所有一種牌數量大於2的情況,也就是ntile[index] >= 2,然後把這兩張牌(對子)去掉,記錄到結果中的nPair。然後就剩下3n張牌了,再判斷剩下的3n張牌是否能組成順子或是暗刻,如果可以全部組成,那麼這把牌就胡了,否則需要遍歷一下把其他牌當對子的可能性。
關於第二步中如何判斷3n 張牌可否組成順子或暗刻
從第一張(種)牌開始往後檢查,每張牌有5種可能, 0, 1, 2,3,4。如果是0,直接檢查下一張(種);
i. 如果是1張或兩張,要胡牌的話他(們)必須和後面兩張組成順子,如果不能組成順子,肯定不胡。如果可以組成順子,把順子牌取出,存入臨時結果,接著處理剩下的牌;
ii. 如果是3張,要胡牌有兩種可能,一是3張當作一個暗刻,還有一種可能是這3張都與後面的牌組成順子。這裡有檢查這兩種情況。比如3個一萬,3個兩萬和3個三萬。既可以以三暗刻算,也可以按三個順子算。這兩種在最後算番的時候不一樣。
iii. 如果是4張,胡牌的話必須要跟後面兩張牌組成一個順子,然後本張(種)就剩3種了,然後繼續2.2的步驟就可以了。
整個檢查過程可以用一個函式遞迴呼叫就可以了,每次處理一張,如果不能湊成順子或暗刻的話就推出返回錯誤,如果函式處理時總牌數為0,則所有牌都已處理完了,返回成功,結果也已經存在結果裡了,把結果加入結果列表。
4. 挺牌檢查
挺牌時應該是3*n + 1張牌。遍歷34種牌,加入3n +1 ,這時就是3n+2了,根據剛才的演算法算是否胡牌,如果胡牌,剛加入的那張牌就是挺牌。
5. 測試過程與結果
1. 挺牌檢查 –選用了非常複雜的九蓮寶燈,也就是3個一萬(或條,餅),3個九萬(或條餅),其他2萬到8萬(或條餅)。這種牌是挺從1萬到9萬,共9張挺牌。演算法可以正確算出。
2. 胡牌檢查 –選用了4個一萬,4個二萬,4個三萬,以及2個4萬,演算法正確列出了3種胡牌結果。