1.體系結(jié)構(gòu)圖

體系結(jié)構(gòu)圖.png
2.邏輯流程圖
2.1簡易流程圖

邏輯流程圖.png
2.2詳細(xì)流程圖

詳細(xì)邏輯流程圖.png
3.服務(wù)器-客戶端通訊圖

sc通訊圖.png
4.數(shù)據(jù)結(jié)構(gòu)
4.1牌型

Paste_Image.png
- 一副牌,共54張,由左至右相應(yīng)的index從0開始排開;
- 牌型:
單牌(ONE):任意一張單牌(3 < 4 <… < K< A < 2 < 小王<大王)。
對子(TWO):任意兩張點(diǎn)數(shù)相同的牌,如:66。
三頭(THREE):任意三張點(diǎn)數(shù)相同的牌,如666。
連三(THREE_2): 二個(gè)連續(xù)三張牌,如:333444
單順(STRAIGHT): 任意五張或五張以上點(diǎn)數(shù)相連的牌,如:45678或78910JQK。不包括 2和雙王。
雙順(TWO_2): 三對或更多的連續(xù)對牌
三順(THREE_3): 三對或更多連續(xù)三張牌,如:333444555
炸彈(FOUR):任意四張點(diǎn)數(shù)相同的牌,如:6666
4.2牌型比較
- 大小順序:3,4,5,6,7,8,9,10,J,Q,K,A,2,大王,小王。
- 根據(jù)相應(yīng)的index轉(zhuǎn)為對應(yīng)大小的value,3-K分別對應(yīng)3-13,A:14,2:15,小王:16 , 大王:17
- 除炸彈以外,普通牌型不允許對壓,相同牌型下比它大的能對壓。
4.3壓牌邏輯

壓牌邏輯.png
5.頁面展示
5.1開始游戲
開始游戲.PNG
5.2等待頁面
等待.PNG
5.3發(fā)牌頁面
發(fā)牌頁面.PNG
6代碼展示
6.1頁面渲染
//等待頁面
var htmlWait = "<h3 class='waiting'><h2>Waiting....</h2><h5>正在為你尋找小伙伴</h5></div>";
//發(fā)牌頁面
var htmlPoker="<div class='choose'><div class='oppo-usr'></div><div class='oppo-pokers'></div></div><div class='display'></div><div class='choose'><div class='user'><div class='usr'></div><button class='abandon'></button></div><div class='pokers'></div> <div class='button'> <button class='cancel'></button> <button class='no'></button> <button class='confirm'></button> </div></div>";
//登錄頁面
var htmlStart="<div class='menu'><button type='button' id='login'></button></div>";
//忙碌頁面
var htmlCrowded="<h3 class='waiting'><h2>Cowded....</h2><h5>房間好擠呀,你暫時(shí)進(jìn)不來</h5></div>";
6.2結(jié)構(gòu)定義
- 用戶對象
//狀態(tài)定義
var INIT="INIT";//初始化
var WAITING="WAITING";//等待
var DISCARD="DISCARD";//發(fā)牌
var GAMEOVER="GAMEOVER";//游戲結(jié)束
var pkObj = {
data:{
status:INIT,
count:27,
pokers:[]
},
from:id,
to: fid,
success:0,
init:function (){
if(pkObj.data.status===INIT){
$("#login").click(doInit);
}
socketInit();
}
};
- 牌對象
var ONE = "ONE";
var TWO = "TWO";
var TWO_2 = "TWO_2";
var THREE = "THREE";
var THREE_2 = "THREE_2";
var THREE_3 = "THREE_3";
var FOUR = "FOUR";
var STRAIGHT = "STRAIGHT";
var ERROR = "ERROR";
var KING = "KING";
var poker= {
type: type, //類型
val: m, //大小
length: pokerList.length //張數(shù)
};
6.3消息模型
- 服務(wù)器端
io.on('connection', function(socket){
//添加用戶
socket.on('add user', function (obj) {
count++;
//玩家一加入
if(count===1){
socket.join(obj.from);
first=obj;
first.authority=true;
}
//玩家二加入
else if(count===2){
socket.join(obj.from);
second=obj;
//洗牌
var res=shuffle();
first.data.pokers=res.poker1;
second.data.pokers=res.poker2;
first.to=second.from;
second.to=first.from;
//用戶狀態(tài)改變由WAITING到DISCARD發(fā)牌
first.data.status=DISCARD;
second.data.status=DISCARD;
//發(fā)送發(fā)牌事件
io.to(first.from).emit("start poker", first);
io.to(second.from).emit("start poker", second);
}
//玩家三登入
else{
socket.join(obj.from);
other=obj;
io.to(other.from).emit("crowded", other);
}
}
//監(jiān)聽discard事件,當(dāng)一方的牌數(shù)剩余為0,發(fā)送游戲結(jié)束信號(hào)
socket.on('discard',function(data,sendMessage){
if(data.data.count===0){
io.to(data.from).emit("game over", 1);
io.to(data.to).emit("game over",2 );
}
else{
//當(dāng)牌還有剩余時(shí),向?qū)κ职l(fā)送接收牌的信號(hào)
io.to(data.to).emit("receive poker", sendMessage,data.data.count);
}
})
//監(jiān)聽abandon事件,當(dāng)有一方放棄游戲,發(fā)送游戲結(jié)束信號(hào)
socket.on('abandon',function(data){
io.to(data.from).emit("game over", 3);
io.to(data.to).emit("game over",4);
})
})
- 客戶端
function socketInit() {
//發(fā)牌
socket.on('start poker', function (data) {
doDiscard(data);
});
//接收對方的牌
socket.on('receive poker',function (data,count) {
doReceive(data,count);
});
//擁擠
socket.on('crowded',function () {
doCrowded();
});
//游戲結(jié)束
socket.on('game over',function (data) {
//自己獲勝
if(data===1)
{
alert("you win!");
}
//對方獲勝
else if(data===2){
alert("you lose!");
}
//自己放棄
else if(data===3){
alert("你放棄啦!");
$(".wrapper").html("");
//回到初始頁面
$(".wrapper").append(htmlStart);
$("#login").click(doInit);
pkObj.data.status=INIT;
}
else{
alert("對方落荒而逃,恭喜你,不戰(zhàn)而勝!");
}
})
}
6.4函數(shù)介紹
服務(wù)端
- 洗牌函數(shù)
//洗牌
function shuffle() {
var pokers = new Array();
for(var i = 0; i < 54; i++) {
pokers[i]=i;
}
for (var i = 0; i < 54; i++) {
var rd=parseInt(Math.random()*54);
var b=pokers[i] ;
pokers[i]= pokers[rd];
pokers[rd]=b;
}
var poker1 = [];
var poker2 = [];
for(var i = 0; i < 54; i++) {
if(i<27)
poker1[i]=pokers[i];
else{
poker2[i-27]=pokers[i];
}
}
sort(poker1);
sort(poker2);
result = {
poker1: poker1,
poker2: poker2
};
return result;
}
//排序
function sort(pokers){
var i=27;
var temp;
while (i > 0) {
for (var j = 0; j < i - 1; j++) {
if (((pokers[j]+11)%13> (pokers[j + 1]+11)%13)&&(pokers[j + 1]!==52||pokers[j + 1]!==53)) {
temp = pokers[j];
pokers[j] = pokers[j + 1];
pokers[j + 1] = temp;}
//小王
if(pokers[j]===53){
temp = pokers[j];
pokers[j] = pokers[26];
pokers[26] = temp;}
//大王
if(pokers[j]===52){
temp = pokers[j];
pokers[j] =pokers[j+1];
pokers[j+1] = temp;}
}
i--;
}
}
客戶端
- 發(fā)牌
function doDiscard(data) {
pkObj=data;
$(".wrapper").html("");
$(".wrapper").append(htmlPoker);
//自己的牌
yourPoker();
//對方的牌
myPoker(pkObj.data);
$(".poker").click(function(){
if($(this).hasClass("select")){
$(this).removeClass("select");
$(this).animate({top:"+=50px"});
}
else{
$(this).addClass("select");
$(this).animate({top:"-=50px"});
}
});
//發(fā)牌
$(".confirm").click(function(){
//得到對方發(fā)來的牌
var oppo_pokerList=new Array();
if($(".display").children() !== null){
$(".display").children().each(function(){
oppo_pokerList.push(parseInt($(this).attr("data-value")));});
}
//得到自己的牌
var pokerList=new Array();
for(var i=0;i<27;i++){
var index=".choose .poker"+i;
if($(index).hasClass('select')){
$(index).html();
pokerList.push(parseInt($(index).attr("data-value")));
}
}
if(getPokerType(pokerList).type===ERROR){
console.log("error");
alert("發(fā)的牌不和標(biāo)準(zhǔn)");
doCancel();
}
//對方?jīng)]有牌即自己首發(fā)or對方放棄 自己沒有牌即表示放棄
else if(oppo_pokerList.length===0||pokerList.length===0){
doConfirm(pkObj);
}
//自己牌合乎標(biāo)準(zhǔn)
else if(comparePoker(pokerList,oppo_pokerList)) {
doConfirm(pkObj);
}
else{
alert("發(fā)的牌不和標(biāo)準(zhǔn)");
doCancel();
}
});
//取消
$(".cancel").click(function(){
console.log("cancel");
doCancel();
});
//不出
$(".no").click(function(){
$(".display .poker").remove();
var sendMessage=$(".display").html();
socket.emit('discard', pkObj, sendMessage);
doCancel();
});
//退出游戲
$(".abandon").click(function(){
socket.emit('abandon', pkObj);
})
}
- 出牌
function doConfirm(pkObj) {
var count=0;
$(".display .poker").remove();
for(var i=0;i<27;i++){
var index=".poker"+i;
if($(index).hasClass('select')){
$(index).html();
$(".display").append($(index));
count++;
}
}
var sendMessage=$(".display").html();
pkObj.data.count-=count;
socket.emit('discard', pkObj, sendMessage);
$(".confirm").hide();
$(".no").hide();
}
- 放棄出牌
function doCancel() {
for(var i=0;i<13;i++){
var index=".choose .poker"+i;
if($(index).hasClass('select')){
$(index).removeClass('select');
$(index).animate({top:"+=50px"});
}
}
}
- 牌面大小轉(zhuǎn)換
function myPoker(data) {
for(var i=0;i<27;i++){
var x=data.pokers[i]%13*(-90);
var y=parseInt(data.pokers[i]/13)*(-120);
var left=x+"px";
var top=y+"px";
var count;
//大王
if(data.pokers[i]===52)
count=17;
//小王
else if(data.pokers[i]===53)
count=16;
//A
else if((data.pokers[i]+11)%13===11){
count=14;
}
//2
else if((data.pokers[i]+11)%13===12){
count=15;
}
else {
count=(data.pokers[i])%13+1;
}
var pokeri="poker"+i;
var html = $("<button class='poker' data-value=''></button>").addClass(pokeri).attr("data-value", count);
html.css({"background-position":left+" "+top});
$(".pokers").append(html);
}
}
- 接收牌
function doReceive(data,after) {
$(".confirm").show();
$(".no").show();
var before= $(".oppo-pokers").children().length;
for(var i=before;i>after;i--){
$(".oppo-pokers").children('.oppo-poker:last').remove();
}
if($(".display").children() != null){
$(".display .poker").remove();
}
$(".display").append(data);
}
- 牌型狀態(tài)機(jī)
function typeState(type, n, m) {
switch (type) {
//單
case ONE:
if(n == m) {
type = TWO;
} else if(n == m +1 && m == 16) {
type = KING;
} else if(n == m + 1){
type = STRAIGHT;
} else {
type = ERROR;
}
break;
//對
case TWO:
if(n == m) {
type = THREE;
} else if(n == m + 1){
type = TWO_2;
} else {
type = ERROR;
}
break;
case TWO_2:
if(n == m) {
type = TWO;
} else {
type = ERROR;
}
break;
case THREE:
if(n == m) {
type = FOUR;
} else if(n == m + 1){
type = THREE_2;
} else {
type = ERROR;
}
break;
case THREE_2:
if(n == m) {
type = THREE_3;
} else {
type = ERROR;
}
break;
case THREE_3:
if(n == m) {
type = THREE;
} else {
type = ERROR;
}
break;
case STRAIGHT:
if(n == m + 1) {
type = STRAIGHT;
} else {
type = ERROR;
}
break;
default:
break
}
return type;
}
- 牌型獲取
function getPokerType(pokerList) {
var type = ONE;
var n, m;
if(pokerList.length===1){
m=pokerList[0];
}
else {
for (var i = 0; i < pokerList.length; i++) {
n = pokerList[i];
m = pokerList[i + 1];
type = typeState(type, n, m);
}
if(type == TWO_2 || type == THREE_2 || type == THREE_3 || (type == STRAIGHT && pokerList.length < 5)) {
type = ERROR;
}
}
var poker= {
type: type,
val: m,
length: pokerList.length
};
return poker;
}
- 牌型大小比較
function comparePoker(pokerList,oppo_pokerList) {
var flag= false;
var data1 = getPokerType(oppo_pokerList); //對方的牌類型
var data2 = getPokerType(pokerList); //我的牌類型
if((data1.type == data2.type && (data1.length == data2.length) && pokerList[0] > oppo_pokerList[0]) || (data1.type != FOUR && data2.type == FOUR) || (data2.type == KING)) {
flag= true;
}
//flag為true可出牌,否則不能出牌
return flag;
}