热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于状态机模子的斗地主游戏(NodeJs&SocketIO)

1.体系构造体系斟酌运用Nodejs和SocketIo完成服务器端逻辑,前端运用HTML5。2.逻辑流程1.重要逻辑包括用户进入游戏、守候对家进入游戏、游戏历程、完毕统计这4个历程

1. 体系构造

体系斟酌运用Nodejs和SocketIo完成服务器端逻辑,前端运用HTML5。
《基于状态机模子的斗地主游戏(NodeJs&SocketIO)》

4. 数据构造

4.1 牌型

为了便于盘算,运用一维数组定义每张扑克的index,依据图中递次,按从左到右以及从上到下递增(即左上角的红桃A为0,右上角的红桃K为12,方块A为13,以此类推)

《基于状态机模子的斗地主游戏(NodeJs&SocketIO)》>

4.2 出牌划定规矩

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

  • 牌形分为:单张、 一对、 三张、姐妹对(两张三张都可以衔接,且衔接数目无穷)、顺子(数目无穷制)、炸弹(不能4带1):

  • 除了炸弹之外,一般牌形不允许对压,雷同牌形只要比它大的才出。

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

4.3 比较大小

依据牌型用整数定义扑克的数值大小

  • 从3到K对应的value为2到12

  • A对应13

  • 2对应14

  • 大小王对应16与15

5. 体系模块设想

5.1 出牌对象

var MODAL;
$(init);
function init() {
new modal();
//绑定页面上的出牌按钮,依据当前差别的状况运转差别的函数
$("body").on("click","#sendCards",statusMachine);
}
function statusMachine() {}
var modal = function () {
var ptrThis;
var modalBox = {
//出牌对象的数据
default:{
//cards存储服务器发送过来的扑克数组
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],
//当前游戏的状况,有DISCARD(发牌),WATING(守候),GAMEOVER(游戏完毕)三个状况
status:"",
//myIndex为玩家所处于的坐位的坐位号
myIndex:0,
//leftIndex为位于举行游戏的玩家左侧的玩家的坐位号
leftIndex:0,
rightIndex:0,
//turn与坐位号对应,turn示意由对应的坐位号玩家举行操纵(比方发牌,摒弃)
turn:0,
//如有两位玩家摒弃出牌,则第三位玩家必需出牌,用于标志新的出牌回合的最先
disCardTrue:false,
//纪录前一名玩家所处的牌,用于完成压牌逻辑
formercardsType:{}
},
//$goal为待插进去扑克的jquery对象,cardArray为扑克数组,isDelay为true则耽误插进去(隔0.3s插进去一张牌)
placeCards:function ($goal,cardArray,isDelay) {},
//sort函数所用到的比较函数,a,b都为扑克的index,将扑克根据value从大到小降序分列,value雷同则根据花样排序
comp:function (a,b) {},
//变更当前扑克牌的状况,未拔取->拔取,拔取->未拔取
toggleCard:function ($this) {},
//将服务器发送的无序数组根据肯定划定规矩举行排序
cardsSort:function (cards) {},
//将已被选中并发送的扑克牌从手牌中删除
removeCards:function () {},
//推断从服务器发送扑克牌数组是由谁发出的,挪用placeCards函数插进去扑克
//turn设置为下一名玩家,依据turn设置status
//假如扑克牌已被出完,则依据末了一名出牌人来推断当前玩家是胜利照样失利
justifyWhich:function (obj) {},
//收到来自服务器转发的某一名玩家发送的投诚信息
someOneTouXiang:function (seats) {},
//清空玩家发送的扑克
clearCards:function () {},
//绘制摆布两位玩家的界面,objLeft为左侧的玩家的信息,objRight同上
drawothers:function (objLeft,objRight) {},
//绘制玩家的界面,包括手牌,obj为相干信息
drawuser:function (obj) {},
//向目的jquery对象插进去图片,$this为目的jquery对象,obj为相干信息(比方图片途径)
insertImg:function ($this,obj) {},
//移除目的jquery对象的图片,$this为目的jquery对象
removeImg:function ($this) {},
//最先游戏,seats为服务器发送过来的坐位对应着的用户的信息,turn指定坐位下标为turn的用户先出牌(turn由服务器的随机数发生)
//存储服务器发送过来的扑克牌数组,挪用cardsSort,drawothers,drawuser,placeCards,initPlay
startGame:function (seats,turn) {},
//出牌前的逻辑推断,推断牌是不是能压过上家或许是不是相符逻辑
preSend:function () {},
//在status为WATING时点击出牌挪用的函数
notYourTurn:function () {},
//压牌逻辑的完成,temp存储着牌型,牌的值和牌的数目
compWhichLarger:function (temp) {},
//绑定坐位点击坐下事宜
init:function () {},
//游戏完毕一般挪用end函数,isWin为true则该玩家胜利
end:function (isWin) {},
//重开一局,array为来自服务器的扑克牌数组,turn为先出牌的人
reStart:function (array,turn) {},
//切换预备按钮的状况,预备->作废,作废->预备
readyGame:function () {},
//游戏完毕
gameover:function (isWin) {},
//摒弃出牌
giveUp:function () {},
//摒弃出牌获得服务器回应
giveUpReply:function (giupCount) {},
//绑定一系列点击事宜
initPlay:function () {}
}
MODAL = modalBox;
return modalBox.init();
}

5.2 出牌流程

//出牌按钮绑定状况机,依据当前状况运转对应的函数,只要在处于DISCARD状况才一般发牌
$("body").on("click","#sendCards",statusMachine);
function statusMachine() {
switch(MODAL.default.status){
case "DISCARD":
//运转至preSend函数
MODAL.preSend();
break;
case "WAITNG":
MODAL.notYourTurn();
break;
case "GAMEOVER":
MODAL.readyGame();
default:
break;
}
}
var modalBox = {
preSend:function () {
var array = new Array();
//将被挑选(用select来标识)的扑克牌的下标掏出,插进去数组array中
$(".cardsLine .card").each(function () {
if($(this).hasClass("select")){
array.push($(this).attr("index"));
}
});
//compCards函数参数为排过序的array,由于用户手牌已根据肯定递次排过序,所以根据一个方向掏出来的牌也是具有肯定是有序列的
var temp = compCards(array);
//console.log(compCards(array));
//console.log(temp);
//disCardTrue为true标识之前已有两个人摒弃出牌,所以不需要斟酌压牌,只需要牌型相符肯定划定规矩即可出牌
if(MODAL.default.disCardTrue){
if(temp.type!="ERR"){
socketFun.sendCards(array);
}else{
alert("没法出牌");
}
}else{
//temp为贮存array牌型以及大小等数据的对象,compWhichLarger函数则是将temp与上一名玩家发的牌举行比较,假如大于则flag为true
var flag = ptrThis.compWhichLarger(temp);
if(flag){
//将array发送至服务器,假如服务器将接收胜利的音讯发还,则挪用 justifyWhich函数
socketFun.sendCards(array);
}else{
alert("没法出牌");
}
}
//ptrThis.sendCards();
},
justifyWhich:function (obj) {//ojb为服务器发送的音讯,包括发牌人,发的牌的信息
if(obj.posterIndex!=MODAL.default.myIndex){
//假如是他人出的牌,则贮存该牌型
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);
//进入下一回合,轮次加一
MODAL.default.turn = (MODAL.default.turn+1)%3;
console.log("Now turn is"+MODAL.default.turn);
//设置下一回合该玩家是出牌照样守候
if(MODAL.default.turn==MODAL.default.myIndex){
MODAL.default.status = "DISCARD";
}else{
MODAL.default.status = "WAITNG"
}
//假如某一名玩家出完牌,则游戏完毕
if(obj.sendOut){
if(obj.posterIndex==MODAL.default.myIndex){
ptrThis.end(true);
}else{
ptrThis.end(false);
}
}
}
}

5.3 客户端SocketIO音讯模子

var socket = io.connect('http://localhost:3000');
var X = window.scriptData; //截取服务器发送过来的数据
//收到服务器发送的差别的音讯范例,挪用对应的出牌模子中的函数
socket.on("connect",function () {
socket.emit("addUser",X._id); //增加用户
})
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) {//服务器关照玩家游戏最先
MODAL.startGame(obj,turn);
})
socket.on("postCards",function (obj) {//服务器返回出牌人以及出牌信息
MODAL.justifyWhich(obj);
})
socket.on("reStart",function (array,turn) {//服务器返回重新最先游戏的信息
MODAL.reStart(array,turn);
})
socket.on("giveup",function (giupCount) {//服务器返回摒弃信息
MODAL.giveUpReply(giupCount);
})
socket.on("renshu",function (seats) {
MODAL.someOneTouXiang(seats);
})
var socketFun = {
//出牌对象经由过程socketFun挪用相干函数与服务器通讯
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) {//示知服务器该玩家预备
socket.emit("readyMsg",obj);
},
giveUp:function () {//示知服务器摒弃出牌
socket.emit("giveup");
},
touxiang:function (index) {//示知服务器该玩家投诚
socket.emit("touxiang",index)
}
}

5.4 压牌逻辑
依据牌型数组推断牌型的逻辑运用状况机完成,其状况迁徙图以下:

《基于状态机模子的斗地主游戏(NodeJs&SocketIO)》

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的下标
var ptr;
//end标志状况机是不是完毕
var end = false;
//data存储着每一张扑克的value,防止屡次运算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){ //假如第一个和第二个数字雷同
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 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处于中间状况,完毕则失足
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 console.log("END:"+end);
console.log(box.cardsType);
if(end){
break;
}
switch(box.cardsType.type){
//ONE示意单张牌,这个ONE状况完毕有用
case "ONE":
box.statusOne();
break;
//TWO示意一对,完毕有用
case "TWO":
box.statusTwo();
break;
//THREE示意三张一样的牌,完毕有用
case "THREE":
box.statusThree();
break;
//STRAIGHT示意顺子,依据array长度推断是不是有用
case "STRAIGHT":
box.statusStraight();
break;
//TWO-ONE示意形如xx(x+1)(x+1)(x+2)的牌型,完毕无效,返回范例ERR
case "TWO-ONE":
box.statusTwoOne();
break;
case "TWO-TWO":
//TWO-TWO示意形如xx(x+1)(x+1)(x+2)(x+2)的牌型,完毕有用
box.statusTwoTwo();
break;
//THREE-ONE示意形如xxx(x+1)(x+1)(x+1)(x+2)的牌型,完毕无效,返回范例ERR
case "THREE-ONE":
box.statusThreeOne();
break;
//THREE-TWO示意形如xxx(x+1)(x+1)(x+1)(x+2)(x+2)的牌型,完毕无效,返回范例ERR
case "THREE-TWO":
box.statusThreeTwo();
break;
//THREE-THREE示意形如xxx(x+1)(x+1)(x+1)(x+2)(x+2)(x+2)的牌型,完毕有用
case "THREE-THREE":
box.statusThreeThree();
break;
//BOMB示意炸弹,返回有用
case "BOMB":
box.statusBomb();
break;
//ERR示意牌型不合逻辑,无效
case "ERR":
box.ERR();
break;
}
}
return box.cardsType;
}

细致代码见GITHUB的pokepoke项目


推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
author-avatar
喵喵心碎
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有