智能胡牌判断算法 · 麻将AI核心引擎
老胡头算法(Lao Hu Tou Algorithm)是麻将AI领域最经典的胡牌判断算法之一。它通过递归回溯的方式,将手牌逐一拆解为顺子、刻子和对子,从而高效判断当前手牌是否满足胡牌条件。
该算法起源于国内早期的麻将游戏开发社区,因其代码简洁、逻辑清晰、性能出色而广泛流传,至今仍是许多麻将AI系统的核心基础模块。
麻将胡牌的本质,是将所有手牌(胡牌时共14张)分解为面子与将牌的组合。
其中面子可以是顺子(同花色连续三张)或刻子(三张同牌),将牌为一对(两张相同)。
老胡头算法的核心思路:先挑出一对将,把剩余牌做递归拆解,只要找到一种全部分解的方案,即可判定胡牌。
将34种牌(万、条、筒、字牌)映射为数组索引,统计每种牌的张数。
遍历所有牌型,轮流尝试每种牌拿出2张作为将牌(即对子)。
将剩余牌递归拆为顺子或刻子,若全部拆完则判定为胡牌。
跳过数量为0的牌型,大量减少无效递归,提升运算速度。
以下是老胡头算法的核心代码,采用经典递归回溯框架,并加入了剪枝优化处理:
// 麻将老胡头算法 - 胡牌判断核心
function isHu(pai) {
// 初始化34种牌的计数数组
let count = new Array(34).fill(0);
for (let p of pai) count[p]++;
// 总牌数必须是 3的倍数+2
if (pai.length % 3 !== 2) return false;
// 依次尝试每种牌作为将牌
for (let king = 0; king < 34; king++) {
if (count[king] >= 2) {
count[king] -= 2; // 拿出将牌
if (canSplit(count)) {
count[king] += 2;
return true;
}
count[king] += 2;
}
}
return false;
}
// 递归拆解剩余牌
function canSplit(count) {
// 找到第一张非零牌
let i = 0;
while (i < 34 && count[i] === 0) i++;
if (i === 34) return true; // 全部拆完了
// 尝试刻子(三张相同)
if (count[i] >= 3) {
count[i] -= 3;
if (canSplit(count)) {
count[i] += 3;
return true;
}
count[i] += 3;
}
// 尝试顺子(仅限万条筒)
if (i < 27 && count[i] > 0 &&
count[i+1] > 0 && count[i+2] > 0 &&
Math.floor(i/9) === Math.floor((i+2)/9)) {
count[i]--; count[i+1]--; count[i+2]--;
if (canSplit(count)) {
count[i]++; count[i+1]++; count[i+2]++;
return true;
}
count[i]++; count[i+1]++; count[i+2]++;
}
return false;
}
以上代码可直接复制运行,通过传入手牌编号数组即可判断是否胡牌。