斗地主游戏文档

1.体系结构图

体系结构图.png

2.逻辑流程图

2.1简易流程图

逻辑流程图.png

2.2详细流程图

详细逻辑流程图.png

3.服务器-客户端通讯图

sc通讯图.png

4.数据结构

4.1牌型

Paste_Image.png
  • 一副牌,共54张,由左至右相应的index从0开始排开;
  • 牌型:
    单牌(ONE):任意一张单牌(3 < 4 <… < K< A < 2 < 小王<大王)。
    对子(TWO):任意两张点数相同的牌,如:66。
    三头(THREE):任意三张点数相同的牌,如666。
    连三(THREE_2): 二个连续三张牌,如:333444
    单顺(STRAIGHT): 任意五张或五张以上点数相连的牌,如:45678或78910JQK。不包括 2和双王。
    双顺(TWO_2): 三对或更多的连续对牌
    三顺(THREE_3): 三对或更多连续三张牌,如:333444555
    炸弹(FOUR):任意四张点数相同的牌,如:6666

4.2牌型比较

  • 大小顺序:3,4,5,6,7,8,9,10,J,Q,K,A,2,大王,小王。
  • 根据相应的index转为对应大小的value,3-K分别对应3-13,A:14,2:15,小王:16 , 大王:17
  • 除炸弹以外,普通牌型不允许对压,相同牌型下比它大的能对压。

4.3压牌逻辑

压牌逻辑.png

5.页面展示

5.1开始游戏

开始游戏.PNG

5.2等待页面

等待.PNG

5.3发牌页面

发牌页面.PNG

6代码展示

6.1页面渲染

//等待页面
var htmlWait = "<h3 class='waiting'><h2>Waiting....</h2><h5>正在为你寻找小伙伴</h5></div>";
//发牌页面
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>房间好挤呀,你暂时进不来</h5></div>";

6.2结构定义

  • 用户对象
//状态定义
var INIT="INIT";//初始化
var WAITING="WAITING";//等待
var DISCARD="DISCARD";//发牌
var GAMEOVER="GAMEOVER";//游戏结束
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  //张数
    };

6.3消息模型

  • 服务器端
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;
        //用户状态改变由WAITING到DISCARD发牌
        first.data.status=DISCARD;
        second.data.status=DISCARD;
        //发送发牌事件
        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);
        }
  }
//监听discard事件,当一方的牌数剩余为0,发送游戏结束信号
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{
      //当牌还有剩余时,向对手发送接收牌的信号
        io.to(data.to).emit("receive poker", sendMessage,data.data.count);
    }
  })
  //监听abandon事件,当有一方放弃游戏,发送游戏结束信号
  socket.on('abandon',function(data){
      io.to(data.from).emit("game over", 3);
      io.to(data.to).emit("game over",4);
    })
  })
  • 客户端
function socketInit() {
    //发牌
    socket.on('start poker', function (data) {
        doDiscard(data);
    });
  //接收对方的牌
    socket.on('receive poker',function (data,count) {
        doReceive(data,count);
    });
  //拥挤
    socket.on('crowded',function () {
        doCrowded();
    });
  //游戏结束
    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("对方落荒而逃,恭喜你,不战而胜!");
        }
    })
}  
                            

6.4函数介绍

服务端
  • 洗牌函数
//洗牌
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--;
         }
}
客户端
  • 发牌
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"});
        }
    });
    //发牌
    $(".confirm").click(function(){
        //得到对方发来的牌
        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("发的牌不和标准");
            doCancel();
        }
        //对方没有牌即自己首发or对方放弃 自己没有牌即表示放弃
        else if(oppo_pokerList.length===0||pokerList.length===0){
            doConfirm(pkObj);
        }
         //自己牌合乎标准
         else if(comparePoker(pokerList,oppo_pokerList)) {
            doConfirm(pkObj);
        }
        else{
            alert("发的牌不和标准");
            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"});
        }
    }
}
  • 牌面大小转换
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);
}
  • 牌型状态机
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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 体系结构图 逻辑流程图 逻辑流程图详细 通讯图 图片参考简书作者kamidox的文章使用 Sublime + Pl...
    ofelia_why阅读 936评论 0 3
  • 两人斗地主 一、体系结构图 通讯模型 大功能模块切换关系 二、逻辑流程图 登录login.PNG 主页面开始界面....
    wuyumumu阅读 484评论 0 0
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,674评论 0 38
  • 斗地主游戏文档 1.体系结构 2.逻辑流程图 3.服务器-客户端通讯图 4.数据结构 4.1牌型 4.2比较大小 ...
    折柳画马阅读 1,071评论 0 2
  • Nginx的代理功能与负载均衡功能是最常被用到的,关于nginx的基本语法常识与配置已在上篇文章中有说明,这篇就开...
    R_X阅读 1,019评论 0 0