网页斗地主游戏

体系结构图

体系结构-nsp_k1.png

逻辑流程图

逻辑流程图.png

逻辑流程图详细

逻辑流程图详细.png

通讯图

通讯图.png

图片参考简书作者kamidox的文章使用 Sublime + PlantUML 高效地画图用代码生成图片

数据结构

var pkObj= {
    data: {
        type: 0,  //牌的类型
        from: 'Alice', //出牌方
        to: '',  //对手
        pokers: [],//出牌数组
        status: INIT,//状态
        counts: 27,//出牌数量
        success: 0
    },
    init: function() {
        ...
    }
}

牌型

poker.png

通过控制位置移动的方式得到每一张牌

.poker {
    width: 90px;
    height: 120px;
    margin-right: -50px;
    background-image: url("../images/poker.png");
}

按照3,4,5,6,7,8,9,10,J,Q,K,1,2,小王,大王的顺序显示在页面上

for(var j=0;j<obj.data.counts;j++) {
    //确定优先级
    if(obj.data.pokers[j] == 52){//大王
        index = 17;
    }else if(obj.data.pokers[j] == 53){//小王
        index = 16;
    }else if(obj.data.pokers[j]%13 == 0){//A
        index = 14;
    }else if(obj.data.pokers[j]%13 == 1){//小2
        index = 15;
    }else {
        index = obj.data.pokers[j]%13+1;
    }
    //根据数组下标,得到坐标
    var x = obj.data.pokers[j] % 13 * (-90);
    var y = parseInt(obj.data.pokers[j] / 13) * (-120);
    var left = x + "px";
    var top = y + "px";
    var pokerj = "pokerMe" + j;
    var singlePoker = $("<button class='poker' data-value=''></button>");
    singlePoker.addClass(pokerj);
    singlePoker.attr("data-value",index);
    singlePoker.css({
        "background-position": left + " " + top
    });
    $(".poker_me").append(singlePoker);
}
状态机指定出牌类型.png

生成出牌规则

  • 用状态机生成牌的类型
function typeState(type, n, m){
    switch(type){
        case ONE:
            if(n == m) {
                type = TWO;
            }else if(n+1 == m && m == 17){
               type = KING;
            } else if(n+1==m){
                type = STRAIGHT;
            } else {
                type = ERROR;
            }
            break;
        case TWO:
            if(n==m){
                type=THREE;
            }else if(n+1 == m){
                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+1 == m){
                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+1 == m){
                type = STRAIGHT;
            }else {
                type = ERROR;
            }
            break;
        default:
            break;
    }
    return type;
}
  • 得到牌的类型
function getPokerType(pokerList){
    var type = ONE;
    var n,m;
    if(pokerList.length==1){//单张牌
        n = pokerList[0];
    }else {
        for(var i=1;i<pokerList.length;i++){ //多张牌
            n = pokerList[i-1];
            m = pokerList[i];
            type = typeState(type,n,m);
        }
        if(type== TWO_2 || type == THREE_2 || type == THREE_3 || (type == STRAIGHT && pokerList.length <5)){
            type = ERROR;
        }
    }
    var result = {
        type: type,
        val: n,
        length: pokerList.length
    };
    return result;
}
  • 比较牌的大小
function comparePoker(sendPoker, receivePoker){
    var check = false;
    var dataSend = getPokerType(sendPoker);//得到发送牌的类型
    var dataReceive = getPokerType(receivePoker);//得到对方牌的类型
    console.log("发送的牌: ");
    console.log(dataSend);
    console.log("对方的牌: ");
    console.log(dataReceive);
    if(dataSend.type == dataReceive.type && (dataSend.length == dataReceive.length)
        &&(sendPoker[0]>receivePoker[0])){
        check = true;
    }
    //炸弹
    else if(dataSend.type == FOUR && dataReceive.type != FOUR){
        check = true;
    }// 王炸
    else if(dataSend.type == KING){
        check = true;
    }
    return check;
}

系统模块

  • 登陆界面
var htmlInit = "<div class='init'><button type='button' id='startGame'></button></div>";
init.png
  • 等待界面
var htmlWaiting = "<div class='waiting' id='waiting'><h2>等待另一个人加入...</h2></div>";
wait.png
  • 打牌界面
var htmlDiscard = "<div class='discard' id='discard'> <div class='friend'> <div class='image_frd'></div> <div class='poker_frd'> </div> </div> <div class='playArea'> <main class='pokerArea'> </main> </div> <div class='menu'> <button class='sendPoker' id='sendPoker'></button> <button class='anstain' id='anstain'></button><button class='abandon' id='abandon'></button> </div> <div class='me'> <div class='image_me'></div> <div class='poker_me'> </div> </div> </div>";
send poker.png

消息模型

  • 服务器端
io.on('connection', function(socket){
   var user;
   //用户上线
   socket.on('startGame', function(obj){
      user = obj.data.from;
      console.log(user+"joined....");
      socket.join(obj.data.from);//加入房间
      if(onlineList.indexOf(user) == -1){
         onlineList.push(user);//添加到在线用户数组
      }
      count++;
      if(count == 1){
         meObj = obj;
         meObj.data.status = config.site.WAITING;//状态机置为waiting状态
         io.to(meObj.data.from).emit("waiting");
      }else if(count == 2){
          frdObj=obj;
          meObj.data.to = frdObj.data.from;
          frdObj.data.to = meObj.data.from;
          meObj.data.status = config.site.DISCARD;
          frdObj.data.status = config.site.DISCARD;
         //洗牌
         var pokers = shuffle();
         var pokerCounts=26;
         var poker_me = new Array(pokerCounts);//我的牌
         var poker_frd = new Array(pokerCounts);//朋友的牌
         for(var k=0, m=0;k<54;k++){
            if(k<=pokerCounts){
                poker_me[k]=pokers[k];
            }else{
                poker_frd[m]=pokers[k];
                m++;
            }
         }
         //排序
         sort(poker_me);
         sort(poker_frd);
         //对象
         meObj.data.pokers = poker_me;
         frdObj.data.pokers = poker_frd;
         //预先发牌
         io.to(meObj.data.from).emit("discard", meObj, frdObj.data.counts);
         io.to(frdObj.data.from).emit("discard", frdObj, meObj.data.counts);
      }else if(count > 2){//第三方加入,显示拥挤
         var index = onlineList.indexOf(user);//查找用户所在的位置
         if(index !== -1){
            onlineList.splice(index,1);//删除用户
         }
         count = 2;
         socket.emit("crowded");
      }
   });
    //发牌
    socket.on('send poker', function(obj, count, sendPoker){
      if(obj.data.counts == 0){
          obj.data.status = config.site.GAMEOVER;
          obj.data.success = 1;//成功
          io.to(obj.data.from).emit("game over", "你成功了");
          io.to(obj.data.to).emit("game over", "你失败了");
      }else{
          //收到牌
          io.to(obj.data.to).emit("receive poker", count, sendPoker);
      }
    });
    //放弃
    socket.on('send abandon', function(obj){
      obj.data.status = config.site.WAITING;
      io.to(obj.data.to).emit("receive abandon");
      var index = onlineList.indexOf(obj.data.from);
      if(index !== -1){
         onlineList.splice(index,1);//删除用户
         socket.leave(obj.data.from);//离开房间
      }
      console.log(obj.data.from + 'Offline....');
    });
   //连接错误
   socket.on('disconnect', function(){
     var index = onlineList.indexOf(user);//查找用户所在的位置
     if(index !== -1){
       onlineList.splice(index,1);//删除用户
       socket.leave(user);//离开房间
     }
     console.log(user + 'Offline....');
   });
   //用户离开
   socket.on('leave', function(){
     socket.emit('disconnect');
   });
});
  • 客户端client
//初始化
socket.on('init', function(obj){
    doInit();
});
//等待
socket.on('waiting', function(obj){
    doWaiting();
});
//发牌
socket.on("discard", function(objMe, countsFrd){
    doDiscard();
    showPoker(objMe);//显示我的牌
    showPokerFrd(countsFrd);//显示对方的牌
    sendPoker(objMe); //发送牌
});
//收到牌
socket.on('receive poker', function(countFrd,sendPoker){
    recePoker(countFrd, sendPoker);
});
//游戏结束
socket.on('game over', function(message){
    alert(message);
});
//一方放弃
socket.on('receive abandon', function(){
    alert("对方放弃了....");
});
//第三方进入,显示拥挤
socket.on('crowded', function(){
    alert("人满了...");
});

出牌函数

  • 洗牌函数(返回洗牌数组)
function shuffle() {
    var temp, count = 54;
    var pokers = new Array(count);//牌
    for (var i = 0; i < count; i++) {
        pokers[i] = i;
    }
    //打乱牌
    for (var j = 0; j < count; j++) {
        var rd = parseInt(Math.random() * count);
        temp = pokers[j];
        pokers[j] = pokers[rd];
        pokers[rd] = temp;
    }
    return pokers;
}
  • 给牌排序(输入:牌的数组)
function sort(pokers){
   var i=27,temp;
   while (i > 0) {
       for (var j = 0; j < i-1; j++) {
         if ((pokers[j]+11)%13 > (pokers[j + 1]+11)%13) {
                temp = pokers[j];
                pokers[j] = pokers[j + 1];
                pokers[j + 1] = temp;
          }
         if(pokers[j]===52){//大王一直往后移
               temp = pokers[j];
               pokers[j] = pokers[j + 1];
               pokers[j + 1] = temp;
         }
         if(pokers[j]===53 && pokers[j+1]!==52){//小王一直往后移,直到遇到大王
              temp = pokers[j];
              pokers[j] = pokers[j + 1];
              pokers[j + 1] = temp;
         }
       }
       i--;
   }
}
  • 显示牌
function showPoker(obj){
    var index;
    for(var j=0;j<obj.data.counts;j++) {
        //确定优先级
        if(obj.data.pokers[j] == 52){//大王
            index = 17;
        }else if(obj.data.pokers[j] == 53){//小王
            index = 16;
        }else if(obj.data.pokers[j]%13 == 0){//A
            index = 14;
        }else if(obj.data.pokers[j]%13 == 1){//小2
            index = 15;
        }else {
            index = obj.data.pokers[j]%13+1;
        }
        var x = obj.data.pokers[j] % 13 * (-90);
        var y = parseInt(obj.data.pokers[j] / 13) * (-120);
        var left = x + "px";
        var top = y + "px";
        var pokerj = "pokerMe" + j;
        var singlePoker = $("<button class='poker' data-value=''></button>");
        singlePoker.addClass(pokerj);
        singlePoker.attr("data-value",index);
        singlePoker.css({
            "background-position": left + " " + top
        });
        $(".poker_me").append(singlePoker);
    }

    $(".poker_me .poker").click(function(){
        if($(this).hasClass('selected')){//已经被选择
            $(this).animate({
                "margin-top":"0px"
            });
            $(this).removeClass("selected");//移除属性
        }else {
            $(this).animate({//未选择
                "margin-top":"-20px"
            });
            $(this).addClass("selected");//添加属性,牌选中
        }
    });
}
  • 打牌--没有牌表示成功
function sendPoker(obj) {
    //发牌
    $("#sendPoker").click(function(){
        var count = 0;//每次发的牌的个数
        var sendPokerList = new Array();//发出去的牌
        var receivePokerList = new Array();//收到的牌
        //如果展示区域有牌,那么先清空牌
        if($(".pokerArea").children('.poker') !== null){  //展示区域有牌
            $(".pokerArea").children('.poker').each(function(){
                receivePokerList.push(parseInt($(this).attr("data-value")));//获取当前元素
            });
        }
        console.log("对方的牌");
        console.log(receivePokerList);

        for(var k=0;k<27;k++) {
            var index = ".me .pokerMe" + k;//我的牌
            if ($(index).hasClass('selected')) {
                sendPokerList[count] = parseInt($(index).attr("data-value"));
                obj.data.counts--;
                count++;
            }
        }

        console.log("发送的牌");
        console.log(sendPokerList);

        //开始发牌
        if(receivePokerList.length===0){
            $('.pokerArea').html("");//清空
            for(var k=0;k<27;k++) {
                var index = ".pokerMe" + k;
                if ($(index).hasClass('selected')) {
                    $(".pokerArea").append($(index));
                }
            }
            var sendPoker = $(".pokerArea").html();
            var type = getPokerType(sendPokerList).type;
            console.log(type);
            if(type=="ONE" || type == "TWO" || type == "TWO_2"
              || type == "THREE" || type == "THREE_2" || type == "THREE_3"
              || type == "FOUR" || type == "STRAIGHT"){
                socket.emit('send poker', obj, count, sendPoker);//发送牌到对方界面
            }else if(type=="ERROR"){
               alert("发送的牌不符合要求....")
            }
            $(".sendPoker").hide();//发送之后,给按钮移除disabled的属性
            $(".anstain").hide();
        }
        //发牌
        else if(comparePoker(sendPokerList, receivePokerList)){
            $('.pokerArea').html("");//清空
            for(var k=0;k<27;k++) {
                var index = ".pokerMe" + k;
                if ($(index).hasClass('selected')) {
                    $(".pokerArea").append($(index));
                }
            }
            var sendPoker = $(".pokerArea").html();
            socket.emit('send poker', obj, count, sendPoker);//发送牌到对方界面
            $(".sendPoker").hide();//发送之后,给按钮移除disabled的属性
            $(".anstain").hide();
        }else{//不能发牌
            alert("不能发送,请重新选择");
        }

    });
    //不出,弃权
    $("#anstain").click(function(){
        console.log("不出");
        socket.emit('send poker', obj, 0, "");//发送牌到数据库
        $(".sendPoker").hide();//发送之后,给按钮移除disabled的属性
        $(".anstain").hide();
    });
    $("#abandon").click(function(){
        obj.data.success = 0;
        obj.data.status = INIT;
        socket.emit('send abandon',obj);
        doInit();
    });
}
  • 收到牌
function recePoker(countFrd,sendPoker){
    $(".sendPoker").show();//发送之后,给按钮移除disabled的属性
    $(".anstain").show();
    $('.pokerArea').html("");//清空
    for(var i=0;i<countFrd;i++){
        $(".poker_frd .poker:first").remove();
    }
    $(".pokerArea").append(sendPoker);//收到牌
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 两人斗地主 一、体系结构图 通讯模型 大功能模块切换关系 二、逻辑流程图 登录login.PNG 主页面开始界面....
    wuyumumu阅读 497评论 0 0
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,770评论 0 38
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,728评论 2 17
  • 微课时代的来临是广大培训和教育界的福音。 一方面,成本低。另一方面,没有地域限制。 于是,微课就爆发了。 微课的内...
    格局林状元阅读 272评论 1 0
  • webbuilder里头怎么配置登陆接口啊,求大神指点
    十年後吧阅读 318评论 0 0