双人扑克-总结文档.md

在线双人扑克文档说明

1. 体系结构

双人扑克-总结文档.md_第1张图片
体系结构图

2. 逻辑流程图

双人扑克-总结文档.md_第2张图片
逻辑流程图

3. 服务器和客户端的通讯交互

双人扑克-总结文档.md_第3张图片
通讯交互

4. 功能设计

数据结构

用户Object:{
    status: 用户状态,共有"WAITING"、"DISCARD"、"GAMEOVER"三种case
    leftCount:余牌计数器,当余牌数为0,即为获胜方
    cards[]: 余牌数组,存放余牌的牌面信息
}
出牌Object:{
    cardType: 出牌牌型,共有"NONE"、"ONE"、"PAIR"、"TRIPLE"、"FOUR"、"STRAIGHT"六种完整态牌型,以及"PAIRPLUS"、"TRIPLEPLUS"、"TRIPLEDOUBLE"、"ERR"四种半完成态与终止态
    startValue: 当前出牌的牌组中最小的牌面值,单张、顺子、对子、三张、炸弹的起始值
    cardCount: 对应牌型计数器。如果是对子,存放有几对;顺子,存放有几张连牌;三张成对,存放有几对
}

牌型判断

双人扑克-总结文档.md_第4张图片
牌型判断状态机

压牌原理

双人扑克-总结文档.md_第5张图片
压牌原理

分派发牌原理

  • cardBase[55]数组:
    保存54张正面牌面与1张背面牌面的base64编码; 0为背面,113为红桃AK,1426为方片AK,2739为黑桃AK,4052为草花AK,53大王,54小王。
  • cards[54]数组:
    存放54张牌的序号;从1~54。
  • cards1[27]数组:
    存放分出的第一组牌的序号,在渲染时到cardBase数组中找对应牌面的base编码
  • cards2[27]数组:
    存放分出的另一组牌的序号,在渲染时到cardBase数组中找对应牌面的base编码

服务器:
对cards数组(54张牌)进行随机排序,逐一插入元素到cards1(27张牌)和cards2(27张牌)两个数组中去。
客户端:
接收到来自服务器的cards1与cards2,进行深度拷贝并且完成页面渲染。

系统模块与实现

1. 界面绘制

初始化发牌页面

function doInit() {
    if(onlineUserCount == 2){
        $(".wait").css("display", "none");
        deal(myCards, cards);
        doCreateRivalCards(myCards, cards);
        status = "DISCARD";
    }
}

//渲染我的牌面
function deal(halfCard, cardBase) {
    halfCard.sort(compare);
    for (var i = 0; i < halfCard.length; i++) {
        var myCard = $.format(MYCARD_MODULE, cardBase[halfCard[i]], halfCard[i]);
        $('.me-cards').append(myCard);
    }
    leftCount = halfCard.length;
}

//渲染对方牌面
function doCreateRivalCards(halfCard,cardBase) {
    for (var i = 0; i < halfCard.length; i++) {
        var rivalCard = $.format(RIVALCARD_MODULE, cardBase[0], 0);
        $('.rival-cards').append(rivalCard);
    }
}

渲染出牌

//出牌
function doOutCards() {
    if(outCards.length != 0) {
        outCards.sort(compare);
        //复制outCards数组,避免污染outCards原数组
        var copy = [];
        for(var i = 0;i> rival
        // 判断函数结束
        if(outObj.cardType != "ERR" && comp == true) {
            // 清空前一轮的出牌,渲染新的出牌
            $(".out-cards").empty();
            for (var i = 0; i < outCards.length; i++) {
                $("img[name=" + outCards[i] + "]").remove();
                var outcard = $.format(OUTCARD_MODULE, cards[outCards[i]], outCards[i]);
                $(".out-cards").append(outcard);
            }
            //更新我方计数器,查看余牌是否为0,能否获胜
            leftCount -= outCards.length;
            //把我方此轮的出牌,发送给服务器
            socket.emit('nextTurn', _username, outCards, outObj);
            console.log(leftCount);
            outCards.splice(0, outCards.length);
            $("#play-options").css("display", "none");
            //判定余牌数之后的操作之后,还要修改status
            if (leftCount == 0) {
                //给服务器发送我方获胜信息,由服务器完成二次分发
                socket.emit("win", _username);
                status = "GAMEOVER";
            }
        }
    }
}

2. 通讯机制

通讯命令定义

服务器端:

io.on('connection',function (socket) {
  var uname;

  //接收到js初始化base64成功信号后,打乱分牌成两堆
  socket.on('cardDone',function (_cards) {
    if (cards.length == 0){
      cards = _cards;
      console.log("我收到牌了");
      //发牌
      cardNumber.sort(function(){ return 0.5 - Math.random() });
      for(var i = 0; i < 27; i++){
        card1.push(cardNumber.pop());
      }
      for(var i = 0; i < 27; i++){
        card2.push(cardNumber.pop());
      }
      card1.sort(compare);
      card2.sort(compare);
      console.dir(card1);
      console.dir(card2);
    }
  })
  socket.on('login', function (_username) {
    console.dir(pkList);
    var username = _username;
    pkList.push(username);
    clientSockets[username] = socket;
    console.log("新用户");
    console.dir(pkList);
    //如果在线两人,开始pk
    if(pkList.length == 2) {
      clientSockets[pkList[0]].emit('gameInit', card1 ,cards);
      clientSockets[pkList[0]].emit('firstOut');
      clientSockets[pkList[1]].emit('gameInit', card2, cards);
    }
  });
  socket.on('nextTurn',function (player , outCards ,outObj) {
    //移交出牌权, 给对手渲染我方出的牌,还要把余牌数量相应减少
    console.log("这是XX打的牌:");
    console.log(player);
    if (player == pkList[0]){
      console.log('给第二个人传牌');
      console.dir(outCards);
      clientSockets[pkList[1]].emit('checkRound', outCards, cards ,outObj);
    } else {
      console.log('给第一个人传牌');
      console.dir(outCards);
      clientSockets[pkList[0]].emit('checkRound', outCards, cards ,outObj);
    }
  })
  socket.on('win',function (winnerName) {
    //在线人数改为0 , 需要按下restart才能+1 
    clientSockets[pkList[0]].emit('gameOver', winnerName);
    clientSockets[pkList[1]].emit('gameOver', winnerName);
  })
  //非正常下线
  socket.on('disconnect',function (username) {
    onlineCount -=1;
    //删除该用户对应的键值对,即socket包
    delete clientSockets[username];
    socket.leave(username);
    // 移除pk列表对应元素
    pkList.splice(pkList.indexOf(username),1);
    console.log(username + 'offline....');
  })
  socket.on('leave',function () {
    socket.emit('disconnect');
  })
})

客户端:

socket.on('firstOut',function () {
        // alert("没错,我先出牌");
        //显示function bar
        $("#play-options").css("display","block");
    })
    //接收到对方的出牌,渲染来牌与减少对方张数,并且获得出牌权
    socket.on('checkRound', function (outCard, cardBase,obj) {
        $("#play-options").css("display","block");
        $(".out-cards").empty();
        console.log("hello 我收到牌了");
        console.dir(outCard);
        for (var i = 0; i < outCard.length; i++) {
            console.log("出牌了  yizhang");
            var outcard = $.format(OUTCARD_MODULE, cardBase[outCard[i]], outCard[i]);
            $('.rival-card')[0].remove();
            $(".out-cards").append(outcard);
        }
        // 把对方的来牌obj接受,并且复制到rival对象中去,留到后续比较时候用
        for(key in obj){
            rival[key] = obj[key];
        }
        console.dir(rival);
    })
    //显示某一个方的获胜信息,游戏结束
    socket.on('gameOver', function (winnerName) {
        $('.wait').css("display","block");
        $(".wait-info")[0].innerHTML =  "游戏结束," + winnerName + "获胜!";
        status = "GAMEOVER";
        //还要渲染一个选择框,是否重新开始,或者退出
    })

##### 实际触发调用




3. 牌型判断函数

输入:牌1值,牌2值
输出:牌型、出牌起始值、对应牌型计数器
牌型判断状态机,修改CARDTYPE、STARTVALUE、CARDCOUNT等参数

function cardTypeMachine(card1,card2) {
    switch(CARDTYPE){
        case "ONE":
            checkPairOrStraight(card1,card2);
            break;
        case "PAIR":
            checkTripleOrPairplus(card1,card2);
            break;
        case "TRIPLE":
            checkFourOrTripleplus(card1,card2);
            break;
        case "STRAIGHT":
            checkStraight(card1,card2);
            break;
        case "PAIRPLUS":
            checkPair(card1,card2);
            break;
        case "TRIPLEPLUS":
            checkTriple(card1,card2);
            break;
        case "FOUR":
            checkFourMore(card1,card2);
            break;
        case "TRIPLEDOUBLE":
            checkTriple(card1,card2);
            break;
    }
}

//1 顺子还是对子
function checkPairOrStraight(card1, card2) {
    if(card1 == card2){
        CARDTYPE = "PAIR";
    }else if (card1 + 1 == card2){
        CARDTYPE = "STRAIGHT";
        CARDCOUNT++;
    }else {
        CARDTYPE = "ERR";
    }
}
//2 三张还是对子加一
function checkTripleOrPairplus(card1, card2) {
    if(card1 == card2){
        CARDTYPE = "TRIPLE";
    }else if (card1 + 1 == card2){
        CARDTYPE = "PAIRPLUS";
    }else {
        CARDTYPE = "ERR";
    }
}
//3 四张还是三带一(此处是三张连队的中间态)
function checkFourOrTripleplus(card1, card2) {
    if(card1 == card2){
        CARDTYPE = "FOUR";
    }else if (card1 +1 == card2){
        CARDTYPE = "TRIPLEPLUS";
    }else {
        CARDTYPE = "ERR";
    }
}
//4 是否是顺子加一
function checkStraight(card1, card2) {
    if(card1 +1 == card2){
        CARDTYPE = "STRAIGHT";
    }else {
        CARDTYPE = "ERR";
    }
}
//5 是否是连对
function checkPair(card1, card2) {
    if(card1 == card2){
        CARDTYPE = "PAIR"
        CARDCOUNT++;
    }else {
        CARDTYPE = "ERR";
    }
}
//6 是否是三带二(此处是三张连队的中间态)
function checkTripledouble(card1, card2) {
    if(card1 == card2){
        CARDTYPE = "TRIPLEDOUBLE";
    }else {
        CARDTYPE = "ERR";
    }
}
//7 判断四张加一
function checkFourMore(card1, card2) {
    CARDTYPE = "ERR";
}
//8 判断判断三张连对
function checkTriple(card1, card2) {
    if(card1 == card2){
        CARDTYPE = "TRIPLE";
        CARDCOUNT++;
    }else if (card1 +1 == card2){
        CARDTYPE = "ERR";
    }
}

出牌牌组实际调用牌型判断函数,并且把CARDTYPE、STARTVALUE、CARDCOUNT等参数封装成一个出牌对象

for(var i = 0;i

4. 压牌比较函数

输入:我方压牌对象,对方出牌对象
输出:boolean值,true表示可出牌压住对方,false表示不可出牌

function cardCompare(myObj,rivalObj) {
    if (rivalObj.cardType == "FOUR") {
        if (myObj.cardType == "FOUR") {     //我方是炸弹,对方也是炸弹
            return valueCompare(myObj.startValue, rivalObj.startValue);
        } else {                           //我方不是炸弹,对方是炸弹
            return false;
        }
    }else if(rivalObj.cardType == "NONE"){
        return true;
    }else {
        if(myObj.cardType == "FOUR"){    //我方是炸弹,对方不是炸弹
            return valueCompare(myObj.startValue,rivalObj.startValue);
        }else {                      //我方不是炸弹,对方也不是炸弹
            //牌型相同,张数相同才能比较大小
            if(myObj.cardType == rivalObj.cardType && myObj.cardCount == rivalObj.cardCount){
                return valueCompare(myObj.startValue,rivalObj.startValue);
            }else{
                return false;
            }
        }
    }
}

function valueCompare(value1,value2) {
    if (value1 > value2){
        return true;
    }else{
        return false;
    }
}

你可能感兴趣的:(双人扑克-总结文档.md)