貌似去年去面试一家公司,问了麻将的算法。虽然之前做过广东麻将,但是胡牌算法在服务端,就没有在意。
现在在网上搜了一些算法试了试 = =!
麻将普通的胡牌就是刻子+顺子+将。癞子可以充当任意一张牌。
参考:
https://xingbinice.iteye.com/blog/2380673
网上搜罗的算法,先取将牌,然后递归判断剩下的牌是否能组成刻子或顺子。
public canHuLaizi(cards, laizi) { if ((cards.length + laizi + 1) % 3 != 0) { // 若牌张数不是2、5、8、11、14则不能胡 return false; } // 排序方便胡牌判断 cards.sort(function(a, b) { return a - b; }) // 依次删除一对牌做将,其余牌全部成扑则可胡 for (var i = 0; i < cards.length; i++) { if (i > 0 && cards[i] == cards[i - 1]){ // 和上一次是同样的牌,避免重复计算 continue; } if ((i + 1 < cards.length && cards[i] == cards[i + 1]) || laizi > 0) { // 找到对子、或是用一张癞子拼出的对子 var puCards = cards.slice(); var puLaizi = laizi; puCards.splice(i, 1); if (puCards[i] == cards[i]) { puCards.splice(i, 1); } else { puLaizi--; } // 删去对子判断剩下的牌是否成扑 if (this.isPu(puCards, puLaizi)) { return true; } } } if (laizi >= 2 && this.isPu(cards, laizi - 2)) { // 两个癞子做将牌 return true; } return false; }
递归判断数组cards是否能组成顺子或刻子
public isPu(cards, laizi) { if (cards.length == 0) { return true; } // 若第一张是顺子中的一张 for (var first = cards[0] - 2; first <= cards[0]; first++) { if(first % 10 > 7 || (laizi == 0 && first < cards[0])) { // 剪枝:顺子第一张牌不会大于7点、无赖子情况下顺子第一张只能用手上的牌 continue; } var shunCount = 0; for (var i = 0; i < 3; i++) { if (cards.indexOf(first + i) >= 0) { shunCount++; } } if (shunCount == 3 || shunCount + laizi >= 3) { // 找到包含第一张牌的顺子 var puCards = cards.slice(); var puLaizi = laizi; for (var i = 0; i < 3; i++) { var deletePos = puCards.indexOf(first + i); if (deletePos >= 0) { puCards.splice(deletePos, 1); } else { puLaizi--; } } if (this.isPu(puCards, puLaizi)) { // 剩下的牌成扑 return true; } } } // 若第一张是刻子中的一张 var keziCount = 1; var keziCard = cards[0]; if (cards[1] == keziCard) { keziCount++; } if (cards[2] == keziCard) { keziCount++; } if (keziCount == 3 || keziCount + laizi >= 3) { var puCards = cards.slice(); var puLaizi = laizi; for (var i = 0; i < 3; i++) { var deletePos = puCards.indexOf(keziCard); if (deletePos >= 0) { puCards.splice(deletePos, 1); } else { puLaizi--; } } if (this.isPu(puCards, puLaizi)) { return true; } } return false; }
测试1万次,含4癞子是否能胡牌。大概花了50ms。
// cards:手牌数组,不超过14张牌,每张牌由整数表示如下 // 条:1, 2, 3, 4, 5, 6, 7, 8, 9, // 万:11, 12, 13, 14, 15, 16, 17, 18, 19, // 筒:21, 22, 23, 24, 25, 26, 27, 28, 29, // 东南西北中发白:31, 41, 51, 61, 71, 81, 91, // // laizi:癞子数量,用整数表示 let cardList = [1,1,2,3,4,23,24,27,28,5]; let laizi = 4; let bHuPai; let startTime = egret.getTimer(); for(let i=0;i<10000;i++){ bHuPai = this.canHuLaizi(cardList, laizi); } console.log(egret.getTimer() - startTime, bHuPai); //51 true
判断听牌
判断听牌,就是将条,万,索,字共34张麻将牌遍历一遍加入手牌,看是否能胡,如果能胡,则表示听此张牌。