簡單撲克游戲總結

目錄:

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閱讀 173,285評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 這是兩個人間的簡單撲克游戲,每人27張牌,由服務器隨機分成兩組不同的數組傳遞給客戶端。 基本邏輯已實現,剩下撲克牌...
    淡就加點鹽閱讀 1,272評論 1 3
  • 兩人斗地主 一、體系結構圖 通訊模型 大功能模塊切換關系 二、邏輯流程圖 登錄login.PNG 主頁面開始界面....
    wuyumumu閱讀 524評論 0 0
  • 1、3DMAX里一般情況下模型的渦輪平滑可以刪除。這個之后呢,再添加優化修改器,就是那個啥自動減面的那個= =P開...
    羅斯基閱讀 216評論 0 0