<!doctype html><html lang="zh"><head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>麻将</title>
<style type="text/css">
body{ background-color: #123456; margin: 0mm }
.desk{ width: 100vmin; height: 100vmin; background-color: #fff; display: flex; flex-direction: column }
.card{ font: 5.5vmin/7vmin arial }
.droped{ flex: 1 }
.player1{ background-color: #abcdef; text-align: center; padding: 1.5vmin 0mm }
.player1 .card{ cursor: pointer }
.player1 .card:hover{ transform: translateY(-0.5vmin); display: inline-block }
.hule{ border: 2vmin ridge #abcdef; padding: 2vmin 3vmin; text-align: center; background-color: green; text-align: center }
.hule .card{ box-shadow: 0mm 0mm 7vmin yellow }
.hule .b{ font: bold 5.5vmin/7vmin arial; color: red; text-shadow: 0mm 0mm 5vmin yellow; }
</style></head><body>
<div class="desk" popover="manual">
<div class="droped"></div>
<div class="player1"></div>
</div>
<div popover="manual" class="hule">
<div class="b">和了</div>
<p class="card"></p>
<div>
<input type="button" value="接着打" onclick="majiang.buhu()" />
<input type="button" value="再来一局" onclick="majiang.newGame()" />
</div>
</div>
</body><script type="text/javascript">
var majiang = new function() {
this.newGame = function() {
droped.innerHTML = "";
// 洗牌
this.xipai();
// 发牌
this.player1.fapai();
win.hidePopover();
};
// 洗牌
this.xipai = function() {
this.cards = new Array;
for(var i = 0; i < cards.length; i++) {
cards[i].rand = Math.random();
this.cards.push(cards[i]);
}
this.cards.sort(function(a, b) { return a.rand - b.rand; });
};
// 发牌
this.fapai = function() {
var card = this.cards.pop();
if(!card) return alert("牌已发完");
return card;
};
// 和了
this.hule = function() {
hule.innerHTML = hupai.ables.map(function(x) {
return x.map(function(y){ return y[1]; }).join(" ");
}).join("<br />");
win.showPopover();
};
// 不和
this.buhu = function() {
this.player1.canDrop = true;
win.hidePopover();
};
// 丢牌
this.drop = function(card) { droped.appendChild(card.tag); };
var droped = document.querySelector(".droped");
var cards = initCards();
this.player1 = new Player(".player1");
var win = document.querySelector(".hule");
var hule = win.querySelector(".card");
};
// 玩家对象
function Player(css) {
// 摸1张牌
this.mopai = function(num, sort) {
var card = majiang.fapai();
if(!card) return; // 没摸到
card.inPlayerId = this.cards.length;
card.player = this;
this.cards.push(card);
div.appendChild(card.tag);
if(!--num) return this.hupai(sort);
setTimeout(function() { me.mopai(num, sort); }, 100);
};
this.hupai = function(sort) {
// 开局发牌后重新排序
if(sort) return setTimeout(function() { me.sort(); }, 500);
// 摸到牌后判断是否和牌
if(!hupai.check(this)) return;
majiang.hule(); console.log(hupai.ables);
setTimeout(function() { me.canDrop = false; }, 1);
};
// 重新排序
this.sort = function() {
this.cards.sort(function(a, b) { return a.id - b.id; });
div.innerHTML = "";
for(var i = 0; i < this.cards.length; i++) {
var card = this.cards[i];
card.inPlayerId = i;
div.appendChild(this.cards[i].tag);
}
div.appendChild(mo);
this.canDrop = false;
};
// 重新发牌
this.fapai = function() {
div.innerHTML = "";
this.cards.length = 0;
this.mopai(13, 1);
};
// 丢牌
this.drop = function(id) {
if(!this.canDrop) return;
var card = this.cards[id];
if(!card) return;
majiang.drop(card);
this.cards.splice(id, 1);
this.sort();
delete card.player;
};
this.cards = new Array;
var div = document.querySelector(css);
var mo = document.createElement("span");
mo.innerHTML = "\ud83c\udc2b";
mo.className = "card";
mo.style.userSelect = "none";
mo.title = "摸牌";
mo.onclick = function() {
div.removeChild(mo);
me.mopai(1);
me.canDrop = true;
};
var me = this;
}
// 和牌检测
var hupai = new function() {
var ables = this.ables = new Array;
this.check = function(player) {
ables.length = 0;
var cards = new Array; hasJiang = false;
player.cards.forEach(function(x) { cards.push({ num: x.num, kind: x.kind, id: x.id, text: x.tag.innerHTML }); });
cards.sort(function(a, b){ return a.id - b.id; });
check(cards, new Array);
return ables.length > 0;
};
// 递归检测
function check(cards, logs) {
var card = cards.shift();
// “刻、顺、将”三种可能性,看哪组 logs 能递归走到最后
ke(card, cards.slice(0), logs.slice(0));
shun(card, cards.slice(0), logs.slice(0));
jiang(card, cards.slice(0), logs.slice(0));
}
// 检测刻子
function ke(card, cards, logs) {
if(cards.length < 2) return false;
if(card.num != cards[0].num) return false;
if(card.num != cards[1].num) return false;
logs.push([ "刻子", card.text + cards[0].text + cards[1].text ]);
if(cards.length < 3) return ables.push(logs); // 检测完成
check(cards.slice(2), logs);
}
// 检测顺子
function shun(card, cards, logs) {
if(cards.length < 2) return false;
var next = card.num + 2, arr = new Array;
for(var i = cards.length - 1; i >= 0; i--) {
var x = cards[i];
if(x.kind != card.kind) continue; // 不同色
if(cards[i].num != next) continue;
cards.splice(i, 1); next--;
arr.unshift(x);
if(arr.length > 1) break;
}
if(arr.length < 2) return false;
logs.push([ "顺子", card.text + arr[0].text + arr[1].text ]);
return !cards.length ? ables.push(logs) : check(cards, logs);
}
// 检测将牌
function jiang(card, cards, logs) {
if(logs.some(function(x) { return x[0] == "将牌"; })) return false;
if(card.num != cards[0].num) return false;
logs.push([ "将牌", card.text + cards[0].text ]);
if(cards.length < 2) return ables.push(logs);
check(cards.slice(1), logs);
}
var me = this;
};
// 初始化卡牌
function initCards() {
var cards = new Array, snum = 56327, pre = "\ud83c", idx = 0;
var newCard = function(i) {
var card = { id: ++idx, num: i };
card.kind = Math.floor(i / 9); // 万,条,筒
var tag = document.createElement("span");
tag.innerHTML = pre + String.fromCharCode(snum + i);
tag.className = "card"; card.tag = tag;
card.tag.onclick = function() {
if(!card.player) return;
card.player.drop(card.inPlayerId);
};
cards.push(card);
};
for(var i = 0; i < 27; i++) {
newCard(i); newCard(i); newCard(i); newCard(i);
}
return cards;
}
majiang.newGame();
document.querySelector(".desk").showPopover();
</script></html>