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;
}
nodejs 癞子麻将的更多相关文章
- 服务器编程入门(3)TCP协议详解
问题聚焦: 本节从如下四个方面讨论TCP协议: TCP头部信息:指定通信的源端端口号.目的端端口号.管理TCP连接,控制两个方向的数据流 TCP状态转移过程:TCP连接的任意一 ...
- 使用JS开发桌面端应用程序NW.js-3-开发问题小记
前言 因为我们的项目是2C的,而XP系统是最大的用户量占比,所以只能使用nw开发而不能用Electron,本文谨记开发nw过程中遇到的各种问题以及解决方案. nw.Window.open打开新窗口不能 ...
- 从.Net到Java学习第十二篇——SpringBoot+JPA提供跨域接口
从.Net到Java学习系列目录 最近又撸了半个月的前端代码,做app离线存储,然后又花了一周去将过去的wcf项目转webapi,java又被落下了,总感觉我特么像斗地主中的癞子牌,变来变去..... ...
- 宋宝华:Docker 最初的2小时(Docker从入门到入门)【转】
最初的2小时,你会爱上Docker,对原理和使用流程有个最基本的理解,避免满世界无头苍蝇式找资料.本人反对暴风骤雨式多管齐下狂轰滥炸的学习方式,提倡迭代学习法,就是先知道怎么玩,有个感性认识,再深入学 ...
- [bilibili]弹幕屏蔽列表
<filters> <item enabled="true">t=定单身</item> <item enabled="true& ...
- 剑指offer编程题66道题 36-66
36.两个链表的第一个公共节点 题目描述 输入两个链表,找出它们的第一个公共结点. 1.具有重合节点的两个链表是一个Y字性,用两个堆栈放这两个链表,从尾部开始遍历,直到遍历到最后一个重合节点. 这种算 ...
- 剑指Offer - 九度1355 - 扑克牌顺子
剑指Offer - 九度1355 - 扑克牌顺子2014-01-30 23:19 题目描述: LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^). ...
- Android掌中游斗地主游戏源码完整版
源码大放送-掌中游斗地主(完整版),集合了单机斗地主.网络斗地主.癞子斗地主等,有史以来最有参考价值的源码,虽然运行慢了一点但是功能正常,用的是纯java写的. 项目详细说明:http://andro ...
- 【剑指Offer】45、扑克牌顺子
题目描述: LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到 ...
随机推荐
- SQLSERVER数据库调优
1.锁表查询 --查看锁住的表 select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName ...
- 交换上的FLAPPING事件 (zhuan)
今天在学校的一个三层交换上看到持续的日志信息: Host 00:E0:FC:09:BC:F9is flapping between fa0/x and fa0/y. 思科对此官方的解释是: Error ...
- tensorflow冻结变量方法(tensorflow freeze variable)
最近由于项目需要,要对tensorflow构造的模型中部分变量冻结,然后继续训练,因此研究了一下tf中冻结变量的方法,目前找到三种,各有优缺点,记录如下: 1.名词解释 冻结变量,指的是在训练模型时, ...
- 【ORACLE】SQL查询出每个组中的第一条记录
CREATE TABLE [TestTable] ( ) NOT NULL , ) NOT NULL , ) ))) GO ALTER TABLE [TestTable] ADD PRIMARY KE ...
- Thread类的join()方法
public class Demo { /** * Thread类的join()方法 * -------------------------------- * 1)join() * 2)join(lo ...
- VS每次F5都重新编译代码,即使没有任何修改
遇到一个奇怪现象,不知道怎么设置了,突然工程的Release模式下,F5每次都要重新编译代码,而且是全部代码都重新编译 而Debug模式没问题 重启VS,重启机器,清理工程重新生成工程都无法解决 最后 ...
- Rafy环境配置
如果现在项目已经创建好啦,要使用Rafy框架进行存储,这里我简单的总结下配置的环境步骤: 一.添加引用Rafy的SDK,如下几个; 二.新建文件夹Entities 在此文件夹下创建rafy实体以及仓库 ...
- poj 2074
哎怎么说,感觉现在处理平面上点线的题已经比较熟练了. 这题就离散化然后搞个前缀和就没了. 准备开始进一步的自闭了. 下面是disguss的一些样例... 其实是我自己写错了个地方,本来能1A的. #i ...
- oracle按时间创建分区表
首先明确分区表和表分区的区别:表分区是一种思想,分区表示一种技术实现.当表的大小过G的时候可以考虑进行表分区,提高查询效率,均衡IO.oracle分区表是oracle数据库提供的一种表分区的实现形式. ...
- TCP、UDP详解与抓包工具使用
参考:https://www.cnblogs.com/HPAHPA/p/7737641.html TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间 ...