体系结构图
逻辑流程图
逻辑流程图详细
通讯图
图片参考简书作者kamidox的文章使用 Sublime + PlantUML 高效地画图用代码生成图片
数据结构
var pkObj= {
data: {
type: 0, //牌的类型
from: 'Alice', //出牌方
to: '', //对手
pokers: [],//出牌数组
status: INIT,//状态
counts: 27,//出牌数量
success: 0
},
init: function() {
...
}
}
牌型
通过控制位置移动的方式得到每一张牌
.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);
}
生成出牌规则
- 用状态机生成牌的类型
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>";
- 等待界面
var htmlWaiting = "<div class='waiting' id='waiting'><h2>等待另一个人加入...</h2></div>";
- 打牌界面
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>";
消息模型
- 服务器端
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);//收到牌
}