简单扑克游戏总结

目录:

1. 体系结构

体系结构图

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

用户间通讯逻辑

客户端处理

用户点击登录按钮进入排队,并发给服务器随机 id,并进入登录状态
function doInit() {
    socket.emit("add user", id);
}
初始化socket
function socketInit() {

    // //当前只有一人登录,等待其他人加入,等待状态
    socket.on("waiting", function () {
        pkObj.data.status = WAIT;
    });

    //进入打牌界面
    socket.on("start poker", function (data) {
        if(拿到黑桃♠3) {  //转换成出牌状态
            pkObj.data.status = DISCARD;
        } else {  //等待
            pkObj.data.status = WAIT;
        }
    });

    //第三人登录显示拥挤
    socket.on("crowded", function () {
        alert("棋牌室拥挤,请稍后再试。。。");
    });

    //对方退出全部初始化,转换成重新开始状态
    socket.on("exit", function () {
        pkObj.data.status = RESTART;
    });

    //接受对方的牌,转换成出牌状态
    socket.on("receive discard", function (data) { });

    //对方放弃出牌,转换成出牌状态
    socket.on("receive abandon", function () {
        pkObj.data.status = DISCARD;
    });

    //比赛结果,装换成游戏结束状态
    socket.on("receive result", function (data) {
        pkObj.data.status = GAMEOVER;
    });
}

//游戏结束,刷新页面
function doRestart() {
    location.reload();
}

//登录状态,向服务器发送id
function doInit() {
    socket.emit("add user", id);
}

//出牌状态
function doDiscard(that) {}

//初始化扑克牌
function poker(list, n) {}

//展示对方的牌
function oppositePoker(n) {}

//根据余数排序
function sortNumber(a, b) {}

//改变特殊牌的值
function changeNum(n, m) {}

//根据序号指定精灵图的位置
function getPos(index) {}

//检查和确定出牌的类型
function checkPokerType(pokerList) {}

//将选择出的牌和对方出的牌进行比较
function compare(list1, list2) {}

//返回每张牌的面值
function getPokerFace(n) {}

服务器端处理

//从一个给定的数组arr中,随机返回num个不重复项
function getArrItems(arr, num) {}

//数组相减获得剩下的牌数组
function getArrSubtract(arr, sub) {}

//返回两组互不相同的27张牌数组
function getPoker() {}

//poker
var io = require('socket.io').listen(server);
var userCount = 0;
var user = {};
var first;  //第一位用户id
var second;  //第二位用户id

io.on('connection',function(socket) {

    //用户登录
    socket.on('add user', function(id) {
        if (userCount == 1) {
            //只有一人发送等待
        } else if (userCount == 2) {
             //两个人就发送开始打牌命令
            var data1 = { };    //包含每个人的牌组和是否还有黑桃三
            var data2 = { };
            //向每个人发送牌组
        } else if (userCount > 2) {
            //第三者不可以登录
        }
    });

    //用户退出
    socket.on('disconnect', function() {
        //其中一方退出则全部回到起始页面
    });

    //用户出牌
    socket.on('discard', function(data) {
          //向另外一个人发送牌组信息
    });

    //用户放弃
    socket.on("abandon", function(data) {
          //轮到另外一个人出牌
    });

    //用户投降
    socket.on("surrender", function(data) {
          //发送游戏结果
    });
});

3. 客户端状态转换

通讯状态转换图
说明:
  • 1.用户进入登录界面,点击登录按钮,若当前已有一个人在等待,则直接进入游戏,否则一直等待,直到第二个人登录;
  • 2.用户在游戏中随时可退出,刷新页面即视为退出,此时双方都回到登录界面,需要重新登录;
  • 3.依据哪方拿到 黑桃♠3,哪方就先出牌;
  • 4.假设A先出牌,A出牌给B,B页面区域1显示A发过来的牌,换B出牌,出现有出牌、放弃和认输三个选项。点击出牌同样发送选中牌的数据,放弃则是不刷新桌上牌列,将出牌权给A。若A认输则直接结束游戏,B获胜,重新开始。
页面布局
出牌界面框架
2.1 PokerObj 对象定义
//pkObj
var pkObj = {
    data: {
        type: ERROR,    //出牌类型
        discardList: 0, //出牌数组
        remain: 27,    //我方牌的剩余量
        from: 0,    //我方id
        to: 0,  //对方id
        before: false,  //先出牌或对方放弃
        status: INIT    //状态:INIT初始化, DISCARD出牌,GAMEOVER游戏结束
    },
    init: function () {
        //初始化socket
        socketInit();
        //加载登录界面
        $(wrapper).append(htmlLogin);
    }
};
2.2 定义各种状态
var INIT = "INIT";  //开始
var WAIT = "WAIT";  //等待2
var DISCARD = "DISCARD" ;  //出牌
var GAMEOVER = "GAMEOVER";  //结束
var RESTART = "RESTART"; // 重新开始
2.3 根据状态绑定各种点击事件
function init() {
    //绑定点击事件
    $("body").on("click", clickEvent);
    //初始化socket和登录界面
    pkObj.init();
}
//点击事件
function clickEvent(e) {
    var target = e.target;
    var that = $(target);

    switch (pkObj.data.status) {
        case INIT:  //未登录状态
            doInit();
            break;
        case DISCARD:   //出牌状态
            doDiscard(that);
            break;
        case GAMEOVER:  //游戏结束状态
            doRestart();
            break;
        default:
            break;
    }
}

相关函数

//登录状态
function doInit() {
    //获得唯一的id
    window.id = new Date().getTime()+""+Math.floor(Math.random()*899+100);
    pkObj.data.from = id;
    socket.emit("add user", id);
}
//出牌状态
function doDiscard(that) {
    var element = that.attr("id");
    var temp = element.replace(/[p]([0-9]+)/g, "p");
    switch (temp) {
        //p表示点击了选择的牌
        case "p":
            break;
        //点击出牌按钮
        case "discard":
            break;
        //点击放弃按钮
        case "abandon":
            $(".click-up").each(function () {
                $(this).removeClass("click-up");
            });
            socket.emit("abandon", pkObj.data.to);
            $(".button-turn").addClass("hide-block");
            pkObj.data.status = WAIT;
            break;
        //点击认输按钮
        case "surrender":
            var data = {
                to: pkObj.data.to,
                from: pkObj.data.from
            };
            socket.emit("surrender", data);
            pkObj.data.status = GAMEOVER;
            break;
        default:
            break;
    }
}
2.5 出牌状态点击 "#box-below" 的牌,选中后上移显示
if (that.parent().attr("id") == "box-below") {
    if (that.hasClass("click-up")) {
        that.removeClass("click-up");
    } else {
        that.addClass("click-up");
    }
}
2.6 点击出牌按钮将选中的牌显示在 “#preview-below” 里

利用.prop("outerHTML")获取选中的牌的整个div代码

$(".click-up").each(function() {
    list[i] = $(this).prop("outerHTML");  //获取选中的牌的整个div代码
    $(this).removeClass("click-up");
    $("#preview-below").append(list[i]);
    $(this).remove();
    i++;
});
2.7 将出牌的数组,对方id,己方id,出牌后剩余量 放在data对象里一起发给服务器,并将状态设置为WAIT
pkObj.data.remain -= list.length;
var data = {
    discard: list,
    from: pkObj.data.from,
    to: pkObj.data.to,
    num: list.length,
    remain: pkObj.data.remain
};
pkObj.data.before = false;
$("#abandon").removeAttr("disabled");
socket.emit("discard", data);
$(".button-turn").addClass("hide-block");
pkObj.data.status = WAIT;
2.8 接收对方的牌,利用append() 放入 “#preview-above” 里
//接受对方的牌
socket.on("receive discard", function(data) {
    var num = data.num;
    var length = $(".opposite").length;
    $("#preview-above").html("");
    $("#preview-below").html("");
    for (var index in data.discard) {
        $("#preview-above").append(data.discard[index]);
    }
    $(".button-turn").removeClass("hide-block");
    // $(".opposite:eq(" + i + ")").remove();
    $(".box-upper").html("");
    oppositePoker(length - num);
    pkObj.data.status = DISCARD;
});

3. 判断牌组类型

简单扑克游戏规则.gif

关牌规则

1.牌数

一副牌,保留了所以牌,共54张;

2.发牌

由系统随机分发2家的牌,每家27张,不重复

3.出牌
  • 第一次为黑桃3先出
  • 牌的大小顺序:大王,小王,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。

思路

1. 客户端向服务器发送的是出牌的扑克块状代码数组
//利用$(selector).each 将每一个选中(上移)的扑克牌的html放进数组
var i = 0;
$(".click-up").each(function() {
    list[i] = $(this).prop("outerHTML"); //获取选中的牌的整个html代码
    $(this).removeClass("click-up");
    $("#preview-below").append(list[i]);
    $(this).remove();
    i++;
});
2. 利用socket.io发送到对方客户端,如下获取
//利用append动态添加到指定id为“preview-above”的div里
for (var index in data.discard) {
    $("#preview-above").append(data.discard[index]);
}

把id为preview-above元素里的扑克.poker利用attr("id")取出id值并进行正则操作获取值

$("#preview-above").find(".poker").each(function() {
    var pokerId = $(this).attr("id"); //atte取出id值
    pokerId = pokerId.replace(/[p]([0-9]+)/g, "$1"); //获取选中的牌的编号(删掉id值首字母p)
    aboveList[j] = getPokerFace(pokerId);
    j++;
});
3. 判断牌的类型

3.1 由于扑克牌的序号是从1到54,但这并不是扑克牌的面值。转换操作:

//返回每张牌的面值
function getPokerFace(n) {
    var temp = n % 13;
    var result;
    if(n == 53) {   //大王
        result = 17;
    } else if(n == 54) {  //小王
        result = 16;
    }
    if(temp >= 3 && temp <= 12) {  //3到Q
        result = temp;
    } else if(temp == 0) {  //老K
        result = 13;
    } else if(temp == 1) {  //A
        result = 14;
    } else if(temp == 2) {  //2
        result = 15;
    }
    return result;
}

3.2 获取到扑克牌面值,进行确定扑克牌数组的类型操作

//出牌类型:单,对,连对,炸
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";
简陋的状态转化
//牌型状态机
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;
    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: m,
        length: pokerList.length
    };
    return result;
}

3.3 获取我自己选中的牌

$("#box-below").find(".click-up").each(function() {
    var pokerId = $(this).attr("id");
    pokerId = pokerId.replace(/[p]([0-9]+)/g, "$1"); //获取选中的牌的id(删掉首字母p)
    belowList[k] = getPokerFace(pokerId);
    k++;
});

将两个扑克牌数组的类型进行比较并确定大小

//pkObj.data.before为true表示谁拿到♠3 或者 对方放弃我可以出任意牌,同时我出的牌必须符合规则,即type != ERROR
var myPoker = getPokerType(belowList);

//compare(aboveList, belowList)用于比较相同类型的牌的大小
if(compare(aboveList, belowList) || (pkObj.data.before && myPoker.type != ERROR))

//将选择出的牌和对方出的牌进行比较
function compare(list1, list2) {
    var check = false;
    var data1 = getPokerType(list1); //对方的牌类型
    var data2 = getPokerType(list2);  //我的牌类型

    //1.类型相同的情况下再比较数组第一个元素大小;
    //2.4表示炸,对方不是炸,我出炸则check为true
    //3.天王炸
    if((data1.type == data2.type && (data1.length == data2.length) && list2[0] > list1[0]) || (data1.type != FOUR && data2.type == FOUR) || (data2.type == KING)) {
        check = true;
    }
    //check为true可出牌,否则不能出牌
    return check;
}

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 158,563评论 24 689
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 121,995评论 17 134
  • 这是两个人间的简单扑克游戏,每人27张牌,由服务器随机分成两组不同的数组传递给客户端。 基本逻辑已实现,剩下扑克牌...
    淡就加点盐阅读 551评论 1 3
  • 两人斗地主 一、体系结构图 通讯模型 大功能模块切换关系 二、逻辑流程图 登录login.PNG 主页面开始界面....
    wuyumumu阅读 216评论 0 0
  • 1、3DMAX里一般情况下模型的涡轮平滑可以删除。这个之后呢,再添加优化修改器,就是那个啥自动减面的那个= =P开...
    罗斯基阅读 68评论 0 0