nodejs 癞子麻将
'use strict'; var _ = require('lodash'); var quick = require('quick-pomelo'); var P = quick.Promise; var cor = P.coroutine; var _ = require('lodash'); var uuid = require('node-uuid'); var Logic = require('./logic'); var User = require('./user'); var Score = require('./score'); var conf = require('../config/games').lbmajiang || {}; var M = require('../../share/message'); var C = require('../../share/constant'); var logger = quick.logger.getLogger('table', __filename); // 所有牌 const CARDS = [ 11, 12, 13, 14, 15, 16, 17, 18, 19, // 筒 21, 22, 23, 24, 25, 26, 27, 28, 29, // 万 31, 32, 33, 34, 35, 36, 37, 38, 39, // 条 41, 42, 43, 44, // 风 51, 52, 53, // 字 11, 12, 13, 14, 15, 16, 17, 18, 19, // 筒 21, 22, 23, 24, 25, 26, 27, 28, 29, // 万 31, 32, 33, 34, 35, 36, 37, 38, 39, // 条 41, 42, 43, 44, // 风 51, 52, 53, // 字 11, 12, 13, 14, 15, 16, 17, 18, 19, // 筒 21, 22, 23, 24, 25, 26, 27, 28, 29, // 万 31, 32, 33, 34, 35, 36, 37, 38, 39, // 条 41, 42, 43, 44, // 风 51, 52, 53, // 字 11, 12, 13, 14, 15, 16, 17, 18, 19, // 筒 21, 22, 23, 24, 25, 26, 27, 28, 29, // 万 31, 32, 33, 34, 35, 36, 37, 38, 39, // 条 41, 42, 43, 44, // 风 51, 52, 53, // 字 61, 62, 63, 64, 65, 66, 67, 68 // 花 ]; // 块类型 const STYLE = { NULL: 0, // 无效 CHI: 1, // 吃牌 SUN: 2, // 顺序 PENG: 3, // 碰子 KE: 4, // 刻子 GANG: 5, // 杠子 ANGANG: 6, // 暗杠 ZMGANG: 7 // 自摸明杠 }; // 椅子数 const CHAIR_COUNT = 4; const CARDS_COUNT = 13; // const LAIZI = 60; // 构造方法 var Logic = function (type, lingbi) { // 类型 this.type = type; // 七对 this.has7d = true; //this.has7d = !!(type & 1); // 全牌 var cards = CARDS; //不带花 if (!(lingbi & 4)) cards = cards.filter(card => card < 60); // 去字 // if (type & 2) cards = cards.filter(card => card < 40); this.jiaZhanghu = !!(lingbi & 1); this.castCount = 0; this.disCount = 0; this.lastCount = cards.length; this.cardsPool = cards; this.dahua = lingbi & 4; this.id = 0; }; // 导出常量 Logic.CARDS = CARDS; Logic.STYLE = STYLE; Logic.CHAIR_COUNT = CHAIR_COUNT; Logic.CARDS_COUNT = CARDS_COUNT; // 导出类 module.exports = Logic; // 原型对象 var proto = Logic.prototype; // 重新洗牌 proto.shuffle = function () { this.castCount = 0; this.disCount = 0; this.lastCount = this.cardsPool.length; this.cardsPool = _.shuffle(this.cardsPool); }; // 填充数组 proto.fillDeep = function (array, o, isMore) { for (let i = 0; i < array.length; ++i) { if (!isMore) { array[i] = _.clone(o); } else { array[i] = _.cloneDeep(o); } } return array; }; // 获取类型 proto.getType = function (card) { return Math.floor(card / 10); }; // 获取牌值 proto.getValue = function (card) { return card % 10; }; //获取癞子个数 proto.getLaiziCount = function (Arr, laizi) { var laiziCount = 0; for (var i = 0; i < Arr.length; ++i) { if (Arr[i] == laizi) { laiziCount++;//癞子数量 } } return laiziCount; } // 手牌排序 proto.sort = function (handCards) { return _.sortBy(handCards); }; // 删除手牌 proto.remove = function (handCards, cards) { this.sort(handCards); if (typeof (cards) == 'number') { if (cards == handCards[handCards.length - 1]) { return (handCards.pop(), true); } else { let pos = handCards.indexOf(cards); if (pos == -1) { return false; } return (handCards.splice(pos, 1), true); } } var length = cards.length; if (length > 1 && cards[0] == cards[length - 1]) { if (cards[0] == handCards[handCards.length - 1]) { handCards.pop(); length -= 1; } let pos = handCards.indexOf(cards[0]); if (pos == -1) return false; handCards.splice(pos, length); } else { for (let i = 0; i < length; ++i) { let pos = handCards.indexOf(cards[i]); if (pos == -1) return false; handCards.splice(pos, 1); } } return true; }; // 调整手牌 proto.adjust = function (handCards) { var length = handCards.length; if (length < 2) return false; if (handCards[length - 1] < handCards[length - 2]) { let moCard = handCards.pop(); let pos = _.findIndex(handCards, (i) => (i >= moCard)); if (pos == -1) handCards.push(moCard); else handCards.splice(pos, 0, moCard); } return true; }; // 投骰子 proto.dice = function () { return _.random(1, 6); }; //随机出癞子 proto.laizi = function () { var card = CARDS[_.random(0, 135)]; if (this.getType(card) == 5) this.laizi(); return card; } //判断花牌 proto.isHua = function (card) { return ((card > 60 && card < 69) || (card < 54 && card > 50));//中白发算作花 } // 余牌数 proto.leaveCount = function (disCount) { if (disCount >= 0) { this.lastCount = this.lastCount + this.disCount - disCount; this.disCount = disCount; if (this.lastCount < 0) this.lastCount = 0; } return this.lastCount; }; // 初始手牌 proto.handCards = function (chBanker, tableId) { this.shuffle(); var result = []; this.id = tableId; var castCount = 0; for (let i = 0; i < CHAIR_COUNT; ++i) { let cardCount = CARDS_COUNT; if (i == chBanker) cardCount += 1; let cards = this.cardsPool.slice(castCount, castCount + cardCount); castCount += cardCount; result.push({ cards: this.sort(cards) }); } this.castCount += castCount; this.lastCount -= castCount; //!!!测试牌型============================= // var handCards = []; // handCards[chBanker] = [13, 13, 16, 16, 19, 19, 44, 44, 44, 53, 53, 53, 19,27]; // handCards[(chBanker + 1) % 4] = [13, 13, 16, 16, 19, 19, 44, 44, 44, 53, 53, 53, 51]; // handCards[(chBanker + 2) % 4] = [21, 22, 23, 24, 25, 26, 27, 28, 25, 27, 27, 25, 53]; // handCards[(chBanker + 3) % 4] = [23, 29, 13, 14, 15, 16, 17, 18, 19, 19, 19, 11, 11]; // for (var i = 0; i < 4; ++i) { // result.push({ cards: this.sort(handCards[i]) }); // } return result; }; // 是否无牌 proto.isNoCards = function () { return this.lastCount <= 0; }; // 摸牌 proto.moCard = function () { var card = 0; // logger.info("桌子id ==" + this.id + ":剩下的牌数",this.lastCount); if (this.lastCount > 0) { card = this.cardsPool[this.castCount++]; this.lastCount -= 1; } // if(card>60) this.moCard(); return { card: card }; }; // 是否将牌 proto.isJang = function (lcard, rcard) { return lcard == rcard; }; // 可否碰牌 proto.isPeng = function (handCards, card) { for (let i = 0; i < handCards.length - 1; ++i) { if (handCards[i] == card && handCards[i + 1] == card) { return true; } } return false; }; // 碰牌 proto.peng = function (handCards, card) { if (this.isPeng(handCards, card)) { return { style: STYLE.PENG, cards: [card, card, card], disc: [card] }; } }; // 吃牌,1-@**左吃, 2-*@*中吃, 3-@**右吃 proto.chi = function (handCards, card, type) { var chis = this.chiAnalyze(handCards, card, type); if (chis[0]) { let res = { style: STYLE.CHI, type: type, disc: [card] }; if (type == 1) { res.cards = [card, card + 1, card + 2]; if (this.getValue(card) < 7) { res.disc.push(card + 3); } } else if (type == 2) { res.cards = [card - 1, card, card + 1]; } else { res.cards = [card - 2, card - 1, card]; if (this.getValue(card) > 3) { res.disc.push(card - 3); } } return res; } }; // 杠牌,1-普通杠, 2-暗杠, 3-自摸明杠 proto.gang = function (handCards, card, type) { // 结果 var res = null; // 明杠 if (type == 1) { let gang = this.gangAnalyze(handCards, card); if (gang) { res = { style: STYLE.GANG, cards: [card, card, card] }; } } // 暗杠 else if (type == 2) { let anGang = this.anGangAnalyze(handCards, card); if (anGang[0]) { res = { style: STYLE.ANGANG, cards: [card, card, card] }; } } return res; }; // 自摸明杠,1-普通杠, 2-暗杠, 3-自摸明杠 proto.zmGang = function (handCards, huCards, card) { var zmGang = this.zmGangAnalyze(handCards, huCards, card); if (zmGang[0]) { return { style: STYLE.ZMGANG, cards: [card, card, card] }; } }; // 吃牌分析,1-@**左吃, 2-*@*中吃, 3-@**右吃, type可以不传 proto.chiAnalyze = function (handCards, card, type) { // 结果集合 var result = []; // 排除风字 if (handCards.length < 2 || card > 40) return result; // 查找句子 var length = handCards.length; for (let i = 0; i < length; ++i) { // 牌面值 let value = this.getValue(card); // @**左吃,吃类型(可以不指定) if (!type || type == 1) { if (value < 8 && i < length - 1) { if (handCards[i] == card + 1 && handCards[i + 1] == card + 2) { result.push({ type: 1, card: card }); } } } // *@*中吃,吃类型(可以不指定) if (!type || type == 2) { if (value > 1 && value < 9 && i < length - 1) { if (handCards[i] == card - 1 && handCards[i + 1] != card - 1) { for (let j = i + 1; j < i + 5 && j < length; ++j) { if (handCards[j] == card + 1) { result.push({ type: 2, card: card }); break; } } } } } // **@右吃,吃类型(可以不指定) if (!type || type == 3) { if (value > 2 && i < length - 1) { if (handCards[i] == card - 2 && handCards[i + 1] == card - 1) { result.push({ type: 3, card: card }); } } } } return result; }; // 普通杠分析,1-普通杠, 2-暗杠, 3-自摸明杠 proto.gangAnalyze = function (handCards, card) { // 结果对象 var result = null; var length = handCards.length; if (this.dahua) { if (length < 3 || card > 50) return result; } else { if (length < 3) return result; } // 查找同牌 for (let i = 0; i < handCards.length - 2; ++i) { if (handCards[i] == card && handCards[i + 1] == card && handCards[i + 2] == card) { result = { type: 1, card: card }; // logger.info("桌子id ==" + this.id + ":gang 普通杠分析 card = %d ",card); break; } } return result; }; // 暗杠分析,1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传 proto.anGangAnalyze = function (handCards, card) { // 结果对象 var result = []; var length = handCards.length; if (length < 4) return result; //带花,中发白不能 杠 if (this.dahua && card > 50) return result; // 查找同牌 var moCard = handCards[length - 1]; for (let i = 0; i < length - 3; ++i) { // 指定杠牌,可以不指定 if (card && handCards[i] != card) continue; // 判定是否刻子 if (handCards[i] == handCards[i + 1] && handCards[i] == handCards[i + 2]) { // 手上有杠牌 if (handCards[i] == handCards[i + 3]) { result.push({ type: 2, card: handCards[i] }); } // 刚摸到杠牌 else if (handCards[i] == moCard) { result.push({ type: 2, card: moCard }); } } } return result; }; // 自摸杠分析,1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传 proto.zmGangAnalyze = function (handCards, huCards, card) { // 返回结果 var result = []; // 遍历句子 for (let i = 0; i < huCards.length; ++i) { if (huCards[i].style != STYLE.PENG) { continue; } if (this.dahua && card > 50) return result; let _card = huCards[i].cards[0]; if (card && _card != card) continue; let pos = handCards.indexOf(_card); if (pos != -1) { result.push({ type: 3, card: _card }); if (card) break; } } return result; }; // 分析牌型 proto.parseBlock = function (card1, card2, card3) { // 刻子 if (card1 && card1 == card2 && card1 == card3) { return { style: STYLE.KE, cards: [card1, card2, card3] }; } // 顺子(排除风字) var cards = this.sort([card1, card2, card3]); if (cards[2] < 40) { if (cards[2] == cards[1] + 1 && cards[1] == cards[0] + 1) { return { style: STYLE.SUN, cards: cards }; } } }; // 提取成句 proto._dumpFixed = function (cards, huCards) { for (let i = 0; i < cards.length - 2; ++i) { let card = cards[i]; if (card <= 0) continue; if (card == cards[i + 1] && card == cards[i + 2]) { huCards.push({ style: STYLE.KE, cards: [card, card, card] }); cards[i] = 0; cards[i + 1] = 0; cards[i + 2] = 0; i += 2; continue; } if (card > 40 || this.getValue(card) > 7) continue; let second = -1, third = -1; for (let j = i + 1; j < cards.length; ++j) { if (cards[j] == card + 1) second = j; if (cards[j] == card + 2) third = j; if (cards[j] > card + 2) break; if (second != -1 && third != -1) break; } if (second != -1 && third != -1) { huCards.push({ style: STYLE.SUN, cards: [card, cards[second], cards[third]] }); cards[i] = 0; cards[second] = 0; cards[third] = 0; } } }; //听牌分析--不带癞子 proto.isTingpai = function (handcards, huCards) { var tingCards = []; for (var i = 0; i < handcards.length; ++i) { var card = handcards[i]; var ting = []; for (var j = 0; j < 31; ++j) { //将手牌中的牌依次替换,判断是否能胡牌 handcards[i] = CARDS[j]; if (this.jiaZhanghu) { if (this.jiaZhangHu(handcards, handcards[i], huCards, true)) ting.push(CARDS[j]); } else { if (this.huAnalyze(handcards, handcards[i], huCards, true) || this.isSevenDouble(handcards)) { ting.push(CARDS[j]); } } } if (this.jiaZhanghu) { if (ting.length == 1) tingCards.push({ chu: card, ting: ting }); } else { if (ting.length > 0) tingCards.push({ chu: card, ting: ting }); } handcards[i] = card; } return tingCards; } // 胡牌分析--不带赖子 proto.huAnalyze = function (handCards, card, huCards, isTouch) { // 全部手牌 if (!isTouch) handCards = handCards.concat(card); handCards = this.sort(handCards); var huRes = { card: card, jiang: 0, huCards: [] }; // 重设将牌 var resetHuRes = (jiang) => { huRes.jiang = jiang; huRes.huCards = []; for (let huCard of huCards) huRes.huCards.push(huCard); }; // 手牌不全 var length = handCards.length; if (handCards.length % 3 != 2 || handCards.length > 14) return; // 是否七对 if (this.has7d && this.isSevenDouble(handCards)) return (resetHuRes(-1), huRes); // 确定将牌 for (let i = 0; i < handCards.length - 1; ++i) { if (!this.isJang(handCards[i], handCards[i + 1])) continue; resetHuRes(handCards[i]); let cards = handCards.slice(0, i);//拼接除开将外的牌 cards = cards.concat(handCards.slice(i + 2)); this._dumpFixed(cards, huRes.huCards); if (huRes.huCards.length >= 4) return huRes; } }; proto.jiaZhangHu = function (handCards, card, huCards, isTouch) { if (this.pengHu(handCards, huCards)) return false; if (this.isSevenDouble(handCards)) return false; var huRes = this.huAnalyze(handCards, card, huCards, isTouch); if (huRes) { if (huRes.jiang == card) { return true; } if (card < 40) { for (let i = 0; i < huRes.huCards.length; ++i) { if (huRes.huCards[i].style == STYLE.SUN) { if (huRes.huCards[i].cards[1] == card) return true; if (this.getValue(card) == 3 && huRes.huCards[i].cards[2] == card) return true; if (this.getValue(card) == 7 && huRes.huCards[i].cards[0] == card) return true; } } } } return false; } //判断碰碰胡 proto.pengHu = function (handcards, huCards, laizi) { // logger.info("桌子id ==" + this.id + ":碰碰胡hucard = [],handcards = []",huCards,handcards); handcards = this.sort(handcards);//排序 var pengCount = 0; if (handcards.length != 5) return false; for (var i = 0; i < huCards.length; ++i) { // logger.info("桌子id ==" + this.id + ":huCards[i].style=%d",huCards[i].style); if (huCards[i].style == STYLE.PENG || huCards[i].style == STYLE.GANG || huCards[i].style == STYLE.ANGANG || huCards[i].style == STYLE.ZMGANG) pengCount++; } if (pengCount == 3) { if (laizi < 0) { if ((handcards[0] == handcards[1] && handcards[2] == handcards[4]) || (handcards[0] == handcards[2] && handcards[3] == handcards[4])) return true; } else { var laizicount = this.getLaiziCount(handcards, laizi); if (laizicount == 0) { if ((handcards[0] == handcards[1] && handcards[2] == handcards[4]) || (handcards[0] == handcards[2] && handcards[3] == handcards[4])) return true; } else if (laizicount >= 3) return true; else { for (var i = 0; i < 5; ++i) { if (i < 3 && handcards[i] == handcards[i + 1] && handcards[i] == handcards[i + 2]) return true; if (i < 2 && handcards[i] == handcards[i + 1] && handcards[i + 2] == handcards[i + 3]) return true; } if (handcards[0] == handcards[1] && handcards[3] == handcards[4]) return true; } } } return false; } // 是否七对--不带癞子 proto.isSevenDouble = function (handCards) { if (handCards.length <= CARDS_COUNT) return false; var dcount = 0; var counts = {}; for (let i = 0; i < handCards.length; ++i) { let card = handCards[i]; counts[card] = (counts[card] || 0) + 1; if (counts[card] >= 2) { dcount += 1; counts[card] = 0; } } return (dcount >= 7); }; proto.is7dadui = function (handcards, laizi) { var gangCount = 0; if (laizi <= 0) { if (this.isSevenDouble(handcards)) { for (let i = 0; i < 11; ++i) { if (handcards[i] == handcards[i + 3]) { gangCount++; } } } } else { var laizicount = this.getLaiziCount(handcards, laizi); if (this.is_7_pair(handcards, laizi, laizicount)) { for (let i = 0; i < 11; ++i) { if (handcards[i] == laizi) continue; if (handcards[i] == handcards[i + 3]) gangCount++; else if (handcards[i] == handcards[i + 2]) { gangCount++; laizicount--; } } } } return gangCount; } //获取手牌中牌的数量 proto.getCountByCardId = function (huHandcard, cardId) { var count = 0; for (let i = 0; i < huHandcard.length; i++) { if (cardId == huHandcard[i]) { count++; } } return count; }; //胡牌时玩家手牌中只要有三张风牌 或者 幺 九 花牌要+1 proto.getFengYJ = function (huHandcards, huCardId, laizi) { let flowerCount = 0; let cardId = []; logger.info("桌子id ==" + this.id + ":胡牌时的手牌getFengYJ = []", huHandcards); for (var i = 0; i < huHandcards.length; ++i) { let card = huHandcards[i]; let count = this.getCountByCardId(huHandcards, card); //癞子不计算在内 if (card == laizi) continue; // //可能是2对倒 胡的牌 是幺九 // if(card ==huCardId&&count>=2) // count++; //已经检测过的就跳过吧 let bAdded = false; for (let j = 0; j < cardId.length; j++) { if (card == cardId[j]) bAdded = true; } if (bAdded) continue; if (card == laizi) continue; if ((card < 45 && card > 40) && count >= 3) { flowerCount++; } if ((card % 10 == 1 || card % 10 == 9) && count >= 3) { flowerCount++; } if (count >= 3) { cardId.push(card); } } return flowerCount; } //带癞子是否是7对 proto.is_7_pair = function (handCards, laizi, laiziCount) { // logger.info("桌子id ==" + this.id + ":is_7_pair>>>laiziCount= %d >> handCards = []",laiziCount,handCards); if (handCards.length <= CARDS_COUNT) return false;//手牌小于14张不能胡7小对 handCards = this.sort(handCards);//排序 var dcount = 0; var nTmp = handCards.length; for (var i = 0; i < nTmp; ++i) { if (handCards[i] == laizi) continue;//遇到癞子不判断 if (i < nTmp - 1 && handCards[i] == handCards[i + 1]) { dcount++; i++; } else { if (laiziCount == 0) return false; laiziCount--; dcount++; } } if (laiziCount == 2) dcount++; // logger.info("桌子id ==" + this.id + ":is_7_pair>>> dcount = %d",dcount); return (dcount == 7); }; //判断将牌 proto.EnumerateJong = function (handCards/*除过癞子*/, laiziArr) { var arrJiang = []; var nJiang = 0; for (var i = 0; i < handCards.length - 1; ++i) { let jiang = []; if (handCards[i] == handCards[i + 1]) { jiang.push(handCards[i]); jiang.push(handCards[i + 1]); nJiang++; } else { if (laiziArr.length > 0) { jiang.push(handCards[i]); jiang.push(laiziArr[0]); nJiang++; } } if (jiang.length > 0) { arrJiang.push(jiang); } } if (laiziArr.length > 0) { let jang = []; jang.push(handCards[handCards.length - 1]); jang.push(laiziArr[0]); nJiang++; arrJiang.push(jang); } if (laiziArr.length > 1) { let jang = []; jang.push(laiziArr[0]); jang.push(laiziArr[0]); nJiang++; arrJiang.push(jang); } return { nJiang: nJiang, arrJiang: arrJiang }; } /* 检测胡牌 cnNormalStone->除过癞子的所有牌的张数 cnGrouped->吃碰杠的数量 sHuResult->胡牌的类型 */ proto.CheckNormal = function (hand, laizi, state) { var laiziArr = []; var handcards = []; for (let i = 0; i < hand.length; ++i) { //癞子和普通牌分开 if (hand[i] == laizi) { laiziArr.push(hand[i]); } else { handcards.push(hand[i]); } } var laizicount = this.getLaiziCount(hand, laizi); if (state) { laiziArr = laiziArr.splice(0, 1); handcards.push(laiziArr[0]); laizicount--; } if (this.is_7_pair(hand, laizi, laizicount)) { return true; } handcards = this.sort(handcards); var anJongIndex = this.EnumerateJong(handcards, laiziArr); // logger.info("桌子id ==" + this.id + ":CheckNormal >>>> anJongIndex >>>> ",anJongIndex); if (anJongIndex.nJiang == 0) { return false; } var cnLeftStone = 0;//剩下的牌数 var cnLeftHun = 0;//剩下的癞子数 for (let i = 0; i < anJongIndex.nJiang; ++i) { let asStoneTmp = handcards.slice(0, handcards.length); if (anJongIndex.arrJiang[i][1] == laiziArr[0]) { //将的第一张牌是癞子 if (anJongIndex.arrJiang[i][0] == laiziArr[0]) { //将的第二张是癞子 cnLeftStone = asStoneTmp.length; cnLeftHun = laiziArr.length - 2; } else { cnLeftStone = asStoneTmp.length - 1; cnLeftHun = laiziArr.length - 1; this.removeByValue(asStoneTmp, anJongIndex.arrJiang[i][0]); } } else { //两张都不是癞子 cnLeftStone = asStoneTmp.length - 2; cnLeftHun = laiziArr.length; this.removeByValue(asStoneTmp, anJongIndex.arrJiang[i][0]); this.removeByValue(asStoneTmp, anJongIndex.arrJiang[i][1]); } if (this.CreateTree(asStoneTmp, cnLeftStone, cnLeftHun)) { return true;; } } return false; } /* 创建四叉树,用来判胡 handcards->除过将和癞子以外的所有手牌 cnNormalStone->除过将和癞子后的手牌数量 cnHun->癞子数量 */ proto.CreateTree = function (handcards, cnNormalStone, cnHun) { // logger.info("桌子id ==" + this.id + ":CreateTree>>>>cnNormalStone = %d,cnHun = %d",cnNormalStone,cnHun); if (cnNormalStone + cnHun > 14) { return false; } if (cnNormalStone + cnHun == 0) { //单钓 return true; } if (cnNormalStone == 0) { if (cnHun % 3 != 0) { return false; } //除过将后剩下的都是癞子 return true; } var res = this.CreateLeftChild(0, 0, handcards, cnNormalStone, cnHun); res |= this.CreateLeftChild(0, 1, handcards, cnNormalStone, cnHun); res |= this.CreateLeftChild(0, 2, handcards, cnNormalStone, cnHun); res |= this.CreateRightChild(0, handcards, cnNormalStone, cnHun); return res; } /* 构造左边的三棵子树( 顺 ),nChild指明构造第几棵子树 第n棵子树取第一个牌作为顺的第n张 nParentIndex->上一层结点下标 */ proto.CreateLeftChild = function (nParentIndex, nChild, asStone, cnNormalStone, cnHun) { var asStoneTmp = asStone.slice(0, asStone.length); if (cnNormalStone == 0 || (cnNormalStone + cnHun) % 3 != 0) { // logger.info("桌子id ==" + this.id + ":cnNormalStone == 0 || ( cnNormalStone + cnHun ) % 3 != 0,构造左边的三棵子树( 顺 ) 失败"); return false; } if (this.getType(asStone[0]) > 3) { //风 字不能做顺子 // logger.info("桌子id ==" + this.id + ":字不能做顺子,构造左边的三棵子树( 顺 ) 失败"); return false; } // 当前结点下标 var nIndex = (nParentIndex << 2) + nChild + 1; var asSpareStone = [];//用来存剩余的牌 var asNode = [];//存组好的牌 var res = this.CreateShun(asStone, cnNormalStone, cnHun, nChild, asSpareStone); if (!res) { return false; } cnNormalStone = res.cnNormalStone; cnHun = res.cnHun; asSpareStone = res.asSpareStone; //这层节点创建成功 if ((cnNormalStone + cnHun) % 3 != 0) { return false; } if (cnNormalStone + cnHun >= 3) { if (cnNormalStone == 0) { if (cnHun % 3 != 0) { return false; } return true; } var bSuccess = this.CreateLeftChild(nIndex, 0, asSpareStone, cnNormalStone, cnHun); bSuccess |= this.CreateLeftChild(nIndex, 1, asSpareStone, cnNormalStone, cnHun); bSuccess |= this.CreateLeftChild(nIndex, 2, asSpareStone, cnNormalStone, cnHun); bSuccess |= this.CreateRightChild(nIndex, asSpareStone, cnNormalStone, cnHun); // logger.info("桌子id ==" + this.id + ":CreateLeftChild >>>> bSuccess = ",bSuccess); return bSuccess; } return true; } /* 构造右子树(刻) */ proto.CreateRightChild = function (nParentIndex, asStone, cnNormalStone, cnHun) { var asStoneTmp = asStone.slice(0, asStone.length); if (cnNormalStone == 0 || (cnNormalStone + cnHun) % 3 != 0) { return false; } var nIndex = (nParentIndex << 2) + 4; // logger.info("桌子id ==" + this.id + ":CreateRightChild >>> nIndex = %d,",nIndex); var cnSameStone = 0; var sGroup = []; for (let i = 0; i < cnNormalStone; ++i) { //查找刻子 if (asStone[0] != asStone[i]) { break; } cnSameStone++; sGroup.push(asStone[i]); this.removeByValue(asStoneTmp, asStone[i]); if (cnSameStone == 3) { break; } } var res = this.PerfectGroup(sGroup, cnHun); if (!res) { //检测数组是否完整(3*n)有癞子用癞子补缺 return false; } cnHun = res.cnHun; // logger.info("桌子id ==" + this.id + ":CreateRightChild >>>cnHun = %d,",cnHun); //本层节点创建成功 cnNormalStone -= cnSameStone; if ((cnNormalStone + cnHun) % 3 != 0) { return false; } if (cnNormalStone + cnHun >= 3) { if (cnNormalStone == 0) { if (cnHun != 3) { return false; } return true; } var bSuccess = this.CreateLeftChild(nIndex, 0, asStoneTmp, cnNormalStone, cnHun); bSuccess |= this.CreateLeftChild(nIndex, 1, asStoneTmp, cnNormalStone, cnHun); bSuccess |= this.CreateLeftChild(nIndex, 2, asStoneTmp, cnNormalStone, cnHun); bSuccess |= this.CreateRightChild(nIndex, asStoneTmp, cnNormalStone, cnHun); // logger.info("桌子id ==" + this.id + ":CreateRightChild >>>> bSuccess = ",bSuccess); return bSuccess; } return true; } /* // 给定一个牌数组,以这个牌数组的第一张牌为基础,构造一个顺牌分组,nBaseIndex指明第一张牌 // 在这个分组里要放置的位置 */ proto.CreateShun = function (asStone, cnNormalStone, cnHun, nBaseIndex, asSpareStone) { if (cnNormalStone <= 0 || nBaseIndex < 0 || nBaseIndex >= 3) { // logger.info("桌子id ==" + this.id + ":CreateShun >>>> cnNormalStone <= 0 || nBaseIndex < 0 || nBaseIndex >= 3"); return false; } if (nBaseIndex > cnHun) { // logger.info("桌子id ==" + this.id + ":CreateShun >>>> nBaseIndex > cnHun"); // nBaseIndex == 2 则要求 cnHun >= 2,nBaseIndex == 1 则要求 cnHun >= 1 return false; } if (this.getValue(asStone[0]) == 8 && nBaseIndex == 0 // 第一张牌是8,不能作顺的第一张 || this.getValue(asStone[0]) == 9 && nBaseIndex < 2// 第一张牌是9,只能作顺的第三张 || this.getValue(asStone[0]) == 1 && nBaseIndex > 0// 第一张牌是1,只能作顺的第一张 || this.getValue(asStone[0]) == 2 && nBaseIndex == 2)// 第一张牌是2,不能作顺的第三张 { // logger.info("桌子id ==" + this.id + ":CreateShun >>>> 第一张牌是8,不能作顺的第一张......"); return false; } asSpareStone = asStone.slice(0, asStone.length); // logger.info("桌子id ==" + this.id + ":CreateShun >> asSpareStone",asSpareStone); var sGroup = [];//存一组顺子 if (nBaseIndex != 2) { for (let i = 0; i < cnNormalStone; ++i) { var nDiff = asStone[i] - asStone[0]; if (nDiff <= 2) { //找顺子 if (sGroup.length == 0) { //加入第一张牌 sGroup.push(asStone[i]); this.removeByValue(asSpareStone, asStone[i]); } else { //顺子中没有这张牌,则把这张牌加入顺子数组 if (!this.FindValue(sGroup, asStone[i])) { sGroup.push(asStone[i]); this.removeByValue(asSpareStone, asStone[i]); } } } else { break; } } } else { //如果这张牌作为顺子的第三张牌,直接用癞子补缺 sGroup.push(asStone[0]); this.removeByValue(asSpareStone, asStone[0]); } var cnScrapHun = cnHun; // 剩下的混数 /// logger.info("桌子id ==" + this.id + ":CreateShun >> sGroup",sGroup); var res = this.PerfectGroup(sGroup, cnScrapHun); if (!res) { // 失败,这个分组无法填充完整 return false; } cnNormalStone = cnNormalStone - 3 + cnHun - res.cnHun; cnHun = res.cnHun; return { cnHun: cnHun, cnNormalStone: cnNormalStone, asSpareStone: asSpareStone }; } /* 是否一个完备的分组,如果不完备,在需要的地方补充混 asHun->癞子数组 */ proto.PerfectGroup = function (sGroup, cnHun) { // logger.info("桌子id ==" + this.id + ":PerfectGroup >> cnHun = %d , sGroup=[]",cnHun,sGroup); if (sGroup.length == 0) { return false; } else if (sGroup.length == 1) { if (cnHun < 2) return false; else cnHun -= 2; } else if (sGroup.length == 2) { if (cnHun < 1) return false; else cnHun -= 1; } return { cnHun: cnHun }; } //删除手牌中指定牌 proto.removeByValue = function (arr, val) { for (var i = 0; i < arr.length; i++) { if (arr[i] == val) { arr.splice(i, 1); break; } } } //查找手牌中指定的牌 proto.FindValue = function (arr, val) { for (let i = 0; i < arr.length; ++i) { if (arr[i] == val) { return true; } } return false; }