日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > php >内容正文

php

php socket开发斗地主,基于状态机模型的斗地主游戏(NodeJsSocketIO)

發(fā)布時(shí)間:2025/3/8 php 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php socket开发斗地主,基于状态机模型的斗地主游戏(NodeJsSocketIO) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. 系統(tǒng)結(jié)構(gòu)

系統(tǒng)考慮使用Nodejs和SocketIo實(shí)現(xiàn)服務(wù)器端邏輯,前端使用HTML5。

2. 邏輯流程

1 . 主要邏輯包括用戶(hù)進(jìn)入游戲、等待對(duì)家進(jìn)入游戲、游戲過(guò)程、結(jié)束統(tǒng)計(jì)這4個(gè)過(guò)程。

2 . 游戲過(guò)程的邏輯具體如下

3 . 服務(wù)器-客戶(hù)端通訊邏輯如下

3. 客戶(hù)端界面設(shè)計(jì)

1 . 登錄界面

2 . 發(fā)牌界面

4. 數(shù)據(jù)結(jié)構(gòu)

4.1 牌型

為了便于計(jì)算,使用一維數(shù)組定義每張撲克的index,根據(jù)圖中順序,按從左到右以及從上到下遞增(即左上角的紅桃A為0,右上角的紅桃K為12,方塊A為13,以此類(lèi)推)

>

4.2 出牌規(guī)則

牌的大小順序:大王,小王,2,A,K,Q,J,10,9,8,7,6,5,4,3。

牌形分為:單張、 一對(duì)、 三張、姐妹對(duì)(兩張三張都可以連接,且連接數(shù)量無(wú)限)、順子(數(shù)量無(wú)限制)、炸彈(不能4帶1):

除了炸彈以外,普通牌形不允許對(duì)壓,相同牌形只有比它大的才能出。

炸彈任何牌形都能出,炸彈的大小為:天王炸,2,A,K,Q,J,10,9,8,7,6,5,4,3。

4.3 比較大小

根據(jù)牌型用整數(shù)定義撲克的數(shù)值大小

從3到K對(duì)應(yīng)的value為2到12

A對(duì)應(yīng)13

2對(duì)應(yīng)14

大小王對(duì)應(yīng)16與15

5. 系統(tǒng)模塊設(shè)計(jì)

5.1 出牌對(duì)象

var MODAL;

$(init);

function init() {

new modal();

//綁定頁(yè)面上的出牌按鈕,根據(jù)當(dāng)前不同的狀態(tài)運(yùn)行不同的函數(shù)

$("body").on("click","#sendCards",statusMachine);

}

function statusMachine() {}

var modal = function () {

var ptrThis;

var modalBox = {

//出牌對(duì)象的數(shù)據(jù)

default:{

//cards存儲(chǔ)服務(wù)器發(fā)送過(guò)來(lái)的撲克數(shù)組

cards:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,

38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53],

//當(dāng)前游戲的狀態(tài),有DISCARD(發(fā)牌),WATING(等待),GAMEOVER(游戲結(jié)束)三個(gè)狀態(tài)

status:"",

//myIndex為玩家所處于的座位的座位號(hào)

myIndex:0,

//leftIndex為位于進(jìn)行游戲的玩家左邊的玩家的座位號(hào)

leftIndex:0,

rightIndex:0,

//turn與座位號(hào)對(duì)應(yīng),turn表示由對(duì)應(yīng)的座位號(hào)玩家進(jìn)行操作(例如發(fā)牌,放棄)

turn:0,

//若有兩位玩家放棄出牌,則第三位玩家必須出牌,用于標(biāo)志新的出牌回合的開(kāi)始

disCardTrue:false,

//記錄前一位玩家所處的牌,用于實(shí)現(xiàn)壓牌邏輯

formercardsType:{}

},

//$goal為待插入撲克的jquery對(duì)象,cardArray為撲克數(shù)組,isDelay為true則延遲插入(隔0.3s插入一張牌)

placeCards:function ($goal,cardArray,isDelay) {},

//sort函數(shù)所用到的比較函數(shù),a,b都為撲克的index,將撲克按照value從大到小降序排列,value相同則按照花色排序

comp:function (a,b) {},

//變換當(dāng)前撲克牌的狀態(tài),未選取->選取,選取->未選取

toggleCard:function ($this) {},

//將服務(wù)器發(fā)送的無(wú)序數(shù)組按照一定規(guī)則進(jìn)行排序

cardsSort:function (cards) {},

//將已被選中并發(fā)送的撲克牌從手牌中刪除

removeCards:function () {},

//判斷從服務(wù)器發(fā)送撲克牌數(shù)組是由誰(shuí)發(fā)出的,調(diào)用placeCards函數(shù)插入撲克

//turn設(shè)置為下一位玩家,根據(jù)turn設(shè)置status

//如果撲克牌已被出完,則根據(jù)最后一位出牌人來(lái)判斷當(dāng)前玩家是勝利還是失敗

justifyWhich:function (obj) {},

//收到來(lái)自服務(wù)器轉(zhuǎn)發(fā)的某一位玩家發(fā)送的投降信息

someOneTouXiang:function (seats) {},

//清空玩家發(fā)送的撲克

clearCards:function () {},

//繪制左右兩位玩家的界面,objLeft為左邊的玩家的信息,objRight同上

drawothers:function (objLeft,objRight) {},

//繪制玩家的界面,包含手牌,obj為相關(guān)信息

drawuser:function (obj) {},

//向目標(biāo)jquery對(duì)象插入圖片,$this為目標(biāo)jquery對(duì)象,obj為相關(guān)信息(例如圖片路徑)

insertImg:function ($this,obj) {},

//移除目標(biāo)jquery對(duì)象的圖片,$this為目標(biāo)jquery對(duì)象

removeImg:function ($this) {},

//開(kāi)始游戲,seats為服務(wù)器發(fā)送過(guò)來(lái)的座位對(duì)應(yīng)著的用戶(hù)的信息,turn指定座位下標(biāo)為turn的用戶(hù)先出牌(turn由服務(wù)器的隨機(jī)數(shù)產(chǎn)生)

//存儲(chǔ)服務(wù)器發(fā)送過(guò)來(lái)的撲克牌數(shù)組,調(diào)用cardsSort,drawothers,drawuser,placeCards,initPlay

startGame:function (seats,turn) {},

//出牌前的邏輯判斷,判斷牌是否能壓過(guò)上家或者是否符合邏輯

preSend:function () {},

//在status為WATING時(shí)點(diǎn)擊出牌調(diào)用的函數(shù)

notYourTurn:function () {},

//壓牌邏輯的實(shí)現(xiàn),temp存儲(chǔ)著牌型,牌的值和牌的數(shù)量

compWhichLarger:function (temp) {},

//綁定座位點(diǎn)擊坐下事件

init:function () {},

//游戲結(jié)束正常調(diào)用end函數(shù),isWin為true則該玩家勝利

end:function (isWin) {},

//重開(kāi)一局,array為來(lái)自服務(wù)器的撲克牌數(shù)組,turn為先出牌的人

reStart:function (array,turn) {},

//切換準(zhǔn)備按鈕的狀態(tài),準(zhǔn)備->取消,取消->準(zhǔn)備

readyGame:function () {},

//游戲結(jié)束

gameover:function (isWin) {},

//放棄出牌

giveUp:function () {},

//放棄出牌得到服務(wù)器回應(yīng)

giveUpReply:function (giupCount) {},

//綁定一系列點(diǎn)擊事件

initPlay:function () {}

}

MODAL = modalBox;

return modalBox.init();

}

5.2 出牌流程

//出牌按鈕綁定狀態(tài)機(jī),根據(jù)當(dāng)前狀態(tài)運(yùn)行對(duì)應(yīng)的函數(shù),只有在處于DISCARD狀態(tài)才能正常發(fā)牌

$("body").on("click","#sendCards",statusMachine);

function statusMachine() {

switch(MODAL.default.status){

case "DISCARD":

//運(yùn)行至preSend函數(shù)

MODAL.preSend();

break;

case "WAITNG":

MODAL.notYourTurn();

break;

case "GAMEOVER":

MODAL.readyGame();

default:

break;

}

}

var modalBox = {

preSend:function () {

var array = new Array();

//將被選擇(用select來(lái)標(biāo)識(shí))的撲克牌的下標(biāo)取出,插入數(shù)組array中

$(".cardsLine .card").each(function () {

if($(this).hasClass("select")){

array.push($(this).attr("index"));

}

});

//compCards函數(shù)參數(shù)為排過(guò)序的array,因?yàn)橛脩?hù)手牌已經(jīng)按照一定順序排過(guò)序,所以按照一個(gè)方向取出來(lái)的牌也是具有一定是有序列的

var temp = compCards(array);

//console.log(compCards(array));

//console.log(temp);

//disCardTrue為true標(biāo)識(shí)之前已經(jīng)有兩個(gè)人放棄出牌,所以不需要考慮壓牌,只需要牌型符合一定規(guī)則即可出牌

if(MODAL.default.disCardTrue){

if(temp.type!="ERR"){

socketFun.sendCards(array);

}else{

alert("無(wú)法出牌");

}

}else{

//temp為儲(chǔ)存array牌型以及大小等數(shù)據(jù)的對(duì)象,compWhichLarger函數(shù)則是將temp與上一位玩家發(fā)的牌進(jìn)行比較,如果大于則flag為true

var flag = ptrThis.compWhichLarger(temp);

if(flag){

//將array發(fā)送至服務(wù)器,如果服務(wù)器將接受成功的消息發(fā)回,則調(diào)用 justifyWhich函數(shù)

socketFun.sendCards(array);

}else{

alert("無(wú)法出牌");

}

}

//ptrThis.sendCards();

},

justifyWhich:function (obj) {//ojb為服務(wù)器發(fā)送的消息,包含發(fā)牌人,發(fā)的牌的信息

if(obj.posterIndex!=MODAL.default.myIndex){

//如果是別人出的牌,則儲(chǔ)存該牌型

MODAL.default.formercardsType=compCards(obj.array);

}

MODAL.default.disCardTrue = false;

var $goal;//$goal為待渲染的部位

switch(obj.posterIndex){

case MODAL.default.myIndex:

ptrThis.removeCards();

$goal = $(".showCardLine");

break;

case MODAL.default.leftIndex:

$goal = $(".leftPlayer").children(".otherCards");

break;

case MODAL.default.rightIndex:

$goal = $(".rightPlayer").children(".otherCards");

break;

default:

break;

}

ptrThis.placeCards($goal,obj.array,false);

//進(jìn)入下一回合,輪次加一

MODAL.default.turn = (MODAL.default.turn+1)%3;

console.log("Now turn is"+MODAL.default.turn);

//設(shè)置下一回合該玩家是出牌還是等待

if(MODAL.default.turn==MODAL.default.myIndex){

MODAL.default.status = "DISCARD";

}else{

MODAL.default.status = "WAITNG"

}

//如果某一位玩家出完牌,則游戲結(jié)束

if(obj.sendOut){

if(obj.posterIndex==MODAL.default.myIndex){

ptrThis.end(true);

}else{

ptrThis.end(false);

}

}

}

}

5.3 客戶(hù)端SocketIO消息模型

var socket = io.connect('http://localhost:3000');

var X = window.scriptData; //截取服務(wù)器發(fā)送過(guò)來(lái)的數(shù)據(jù)

//收到服務(wù)器發(fā)送的不同的消息類(lèi)型,調(diào)用對(duì)應(yīng)的出牌模型中的函數(shù)

socket.on("connect",function () {

socket.emit("addUser",X._id); //添加用戶(hù)

})

socket.on("playerSit",function (obj) {

MODAL.insertImg($(".seat").eq(obj.index).children(),obj);

})

socket.on("leave",function (index) {

MODAL.removeImg($(".seat").eq(index).children());

})

socket.on("seatsInfo",function (obj) {

console.log("seatsInfo"+obj);

for(var key in obj){

console.log(key);

MODAL.insertImg($(".seat").eq(obj[key].index).children(),obj[key]);

}

})

socket.on("gameStart",function (obj,turn) {//服務(wù)器通知玩家游戲開(kāi)始

MODAL.startGame(obj,turn);

})

socket.on("postCards",function (obj) {//服務(wù)器返回出牌人以及出牌信息

MODAL.justifyWhich(obj);

})

socket.on("reStart",function (array,turn) {//服務(wù)器返回重新開(kāi)始游戲的信息

MODAL.reStart(array,turn);

})

socket.on("giveup",function (giupCount) {//服務(wù)器返回放棄信息

MODAL.giveUpReply(giupCount);

})

socket.on("renshu",function (seats) {

MODAL.someOneTouXiang(seats);

})

var socketFun = {

//出牌對(duì)象通過(guò)socketFun調(diào)用相關(guān)函數(shù)與服務(wù)器通信

sit:function ($this) {

var obj = {

id:X._id,

index:$this.parent().index()

}

socket.emit("sitSeat",obj);

},

sendCards:function (array) {

var sendOut;

if(($(".cardsLine .cards").children().length-array.length)==0){

sendOut = true;

}else{

sendOut = false;

}

var obj = {

array:array,

posterIndex:MODAL.default.myIndex,

sendOut:sendOut

}

socket.emit("postCards",obj);

},

readyMsg:function (obj) {//告知服務(wù)器該玩家準(zhǔn)備

socket.emit("readyMsg",obj);

},

giveUp:function () {//告知服務(wù)器放棄出牌

socket.emit("giveup");

},

touxiang:function (index) {//告知服務(wù)器該玩家投降

socket.emit("touxiang",index)

}

}

5.4 壓牌邏輯

根據(jù)牌型數(shù)組判斷牌型的邏輯使用狀態(tài)機(jī)實(shí)現(xiàn),其狀態(tài)遷移圖如下:

function compCards(array) {

if(array.length==2&&data[array[0]].value==16&&data[array[1]].value==15){//天王炸

var cardsType={

count:array.length,

type:"KINGBOMB",

value:data[array[0]].value

};

return cardsType;

}

//ptr指向array的下標(biāo)

var ptr;

//end標(biāo)志狀態(tài)機(jī)是否結(jié)束

var end = false;

//data存儲(chǔ)著每一張撲克的value,避免多次運(yùn)算value

var box = {

cardsType:{

count:array.length,

type:"ONE",

value:data[array[0]].value

},

setType:function (type) {

this.cardsType.type = type;

},

statusOne:function () {

if(this.cardsType.count==1){

end = true;

return ;

}

if(data[array[0]].value==data[array[1]].value){ //如果第一個(gè)和第二個(gè)數(shù)字相同

this.setType("TWO");

return ;

}

if(data[array[0]].value==data[array[1]].value+1){

this.setType("STRAIGHT");

}else{

this.setType("ERR");

}

return ;

},

statusTwo:function () {

if(this.cardsType.count==2){

end = true;

return ;

}

if(data[array[1]].value==data[array[2]].value){

this.setType("THREE");

return ;

}

if(data[array[1]].value==data[array[2]].value+1){

this.setType("TWO-ONE");

}else{

this.setType("ERR");

}

},

statusThree:function () {

if(this.cardsType.count==3){

end = true;

return ;

}

if(data[array[2]].value==data[array[3]].value){

this.setType("BOMB");

return ;

}

if(data[array[2]].value==data[array[3]].value+1){

this.setType("THREE-ONE");

}else{

this.setType("ERR");

}

return ;

},

statusStraight:function () {

if(this.cardsType.count< 5){

this.setType("ERR");

end = true;

return ;

}

if(ptr< this.cardsType.count-1){

if(data[array[ptr]].value!=data[array[ptr+1]].value+1){

this.setType("ERR");

end = true;

return ;

}

}else{

end = true;

return ;

}

},

statusTwoOne:function () {

if(ptr==this.cardsType.count-1){ //TwoOne處于中間狀態(tài),結(jié)束則出錯(cuò)

this.setType("ERR");

return ;

}

if(data[array[ptr]].value==data[array[ptr+1]].value){

this.setType("TWO-TWO");

}else{

this.setType("ERR");

}

return ;

},

statusTwoTwo:function () {

if(ptr==this.cardsType.count-1){

end = true;

return ;

}

if(data[array[ptr]].value==data[array[ptr]].value+1){

this.setType("TWO-ONE");

}else{

this.setType("ERR");

}

return ;

},

statusThreeOne:function () {

if(ptr==this.cardsType.count-1){

this.setType("ERR");

return ;

}

if(data[array[ptr]].value==data[array[ptr+1]].value){

this.setType("THREE-TWO");

}else{

this.setType("ERR");

}

return ;

},

statusThreeTwo:function () {

if(ptr==this.cardsType.count-1){

this.setType("ERR");

return ;

}

if(data[array[ptr]].value==data[array[ptr+1]].value){

this.setType("THREE-THREE");

}else{

this.setType("ERR");

}

return ;

},

statusThreeThree:function () {

if(ptr==this.cardsType.count-1){

end = true;

return ;

}

if(data[array[ptr]].value==data[array[ptr+1]].value+1){

this.setType("THREE-ONE");

}else{

this.setType("ERR");

}

return ;

},

statusBomb:function () {

if(ptr==this.cardsType.count-1){

end = true;

return ;

}

if(data[array[ptr]].value!=data[array[ptr+1]].value){

this.setType("ERR");

}

},

ERR:function () {

end = true;

return ;

}

};

for(ptr = 0;ptr< box.cardsType.count;++ptr){

console.log("END:"+end);

console.log(box.cardsType);

if(end){

break;

}

switch(box.cardsType.type){

//ONE表示單張牌,這個(gè)ONE狀態(tài)結(jié)束有效

case "ONE":

box.statusOne();

break;

//TWO表示一對(duì),結(jié)束有效

case "TWO":

box.statusTwo();

break;

//THREE表示三張一樣的牌,結(jié)束有效

case "THREE":

box.statusThree();

break;

//STRAIGHT表示順子,根據(jù)array長(zhǎng)度判斷是否有效

case "STRAIGHT":

box.statusStraight();

break;

//TWO-ONE表示形如xx(x+1)(x+1)(x+2)的牌型,結(jié)束無(wú)效,返回類(lèi)型ERR

case "TWO-ONE":

box.statusTwoOne();

break;

case "TWO-TWO":

//TWO-TWO表示形如xx(x+1)(x+1)(x+2)(x+2)的牌型,結(jié)束有效

box.statusTwoTwo();

break;

//THREE-ONE表示形如xxx(x+1)(x+1)(x+1)(x+2)的牌型,結(jié)束無(wú)效,返回類(lèi)型ERR

case "THREE-ONE":

box.statusThreeOne();

break;

//THREE-TWO表示形如xxx(x+1)(x+1)(x+1)(x+2)(x+2)的牌型,結(jié)束無(wú)效,返回類(lèi)型ERR

case "THREE-TWO":

box.statusThreeTwo();

break;

//THREE-THREE表示形如xxx(x+1)(x+1)(x+1)(x+2)(x+2)(x+2)的牌型,結(jié)束有效

case "THREE-THREE":

box.statusThreeThree();

break;

//BOMB表示炸彈,返回有效

case "BOMB":

box.statusBomb();

break;

//ERR表示牌型不合邏輯,無(wú)效

case "ERR":

box.ERR();

break;

}

}

return box.cardsType;

}

詳細(xì)代碼見(jiàn)GITHUB的pokepoke項(xiàng)目

總結(jié)

以上是生活随笔為你收集整理的php socket开发斗地主,基于状态机模型的斗地主游戏(NodeJsSocketIO)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。