最近打麻将有点频繁(有点罪恶),结合得发一篇博客的情况,因此这里小水一篇文章,用回溯的方式实现一下胡牌的判定。
基础胡牌:三副刻子或者顺子加上一对子,即可胡牌。
特殊牌面:
- 十三幺:由三种序数牌的1、9牌,东西南北中发白各一种,以及再加上这里面任意一张牌组成对子
- 七小对:即七个对子组成
对于十三幺和七小对比较容易判断,本质上计数即可:
用数组来表示各种类型的牌,即下标0-33分为代表1-9万,1-9筒,1-9条,以及东西南北中发白。用value表示所持牌的数量。
首先判断七小对:即判断是否有七个对子。
//判断七小对
public boolean isSevenPairs(int[] pai) {
int pairCount = 0;
for (int i = 0; i < 34; ++i) {
if (pai[i] == 2 || pai[i] == 4) {
pairCount += pai[i] / 2;
} else if (pai[i] == 3 || pai[i] == 1) {
return false;
}
}
return (pairCount == 7);
}
判断是否是十三幺:首先列出十三幺所有牌型的下标,然后同样判断是否具有这个牌型即可。
public boolean isThirteenYao(int[] pai) {
int[] wonders = new int[]{
0, 8, 9, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33};
for (int i : wonders) {
if (pai[i] < 1)
return false;
}
int pairCount = 0;
for (int i : wonders) {
if (pai[i] == 2) {
pairCount++;
}
}
return (pairCount == 1);
}
基础胡牌:市面上的麻将相关游戏应该有更加高效的判断算法,这里实现一种比较容易理解,回溯的方式来依次遍历所有可能性,以此看看有没有符合胡牌牌型。
在回溯过程中,每遍历一张牌,会先尝试将其作为对子,如果不符合,再尝试作为刻子,或者顺子。
public boolean isWinn(int[] pai, int index, int pairs, int melds) {
if (index >= pai.length) {
return pairs == 7 || (pairs == 1 && melds == 4);
}
//尝试形成对子
if (pai[index] >= 2) {
pai[index] -= 2;
if (isWinn(pai, index, pairs + 1, melds)) {
return true;
}
pai[index] += 2;
}
//尝试形成顺子
if (index < 27 && !isEightAndNight(index) && pai[index] > 0 && pai[index + 1] > 0 && pai[index + 2] > 0) {
pai[index]--;
pai[index + 1]--;
pai[index + 2]--;
if (isWinn(pai, index, pairs, melds + 1)) {
return true;
}
pai[index]++;
pai[index + 1]++;
pai[index + 2]++;
}
// 尝试形成刻子
if (pai[index] >= 3) {
pai[index] -= 3;
if (isWinn(pai, index, pairs, melds + 1)) {
return true;
}
pai[index] += 3;
}
if (pai[index] == 0 && isWinn(pai, index + 1, pairs, melds)) {
return true;
}
return false;
}
//因为8 和9的牌后续是没有了,无法形成顺子,因此需要排除
public isEightAndNight(int index) {
if(index == 7 || index == 8 || index == 16 || index == 17 || index == 25 || index == 26 ){
return true;
}
return false;
}