├── LICENSE ├── README.md ├── card-game-client ├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.vue │ ├── assets │ ├── dedication.png │ ├── hurt.png │ ├── logo.png │ └── strong.png │ ├── components │ └── Card.vue │ ├── main.js │ ├── pages │ ├── GameTable.vue │ └── Login.vue │ ├── router.js │ ├── store.js │ └── utils.js └── card-game-server ├── .gitignore ├── app.js ├── constants.js ├── handler.js ├── package-lock.json ├── package.json └── utils.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Xie Jingyang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # card-game 2 | 这是一个网页端的卡牌游戏 3 | 4 | [线上地址](http://cardgame.xiejingyang.com) 5 | 6 | ## 技术栈 7 | * vue全家桶 8 | * velocity.js 9 | * socketio 10 | * nodejs 11 | * express 12 | * mongodb 13 | 14 | ## 源码查看方式 15 | 如果你是看了我的博客过来的,那么你可以通过切换tag来和文章进行同步 16 | 17 | [博客地址](http://blog.xiejingyang.com) 18 | 19 | -------------------------------------------------------------------------------- /card-game-client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /card-game-client/README.md: -------------------------------------------------------------------------------- 1 | # card-game-client 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /card-game-client/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /card-game-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "card-game-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "socket.io-client": "^2.2.0", 12 | "velocity-animate": "^1.5.2", 13 | "vue": "^2.5.21", 14 | "vue-router": "^3.0.1", 15 | "vuex": "^3.0.1" 16 | }, 17 | "devDependencies": { 18 | "@vue/cli-plugin-babel": "^3.2.0", 19 | "@vue/cli-plugin-eslint": "^3.2.0", 20 | "@vue/cli-service": "^3.2.0", 21 | "babel-eslint": "^10.0.1", 22 | "eslint": "^5.8.0", 23 | "eslint-plugin-vue": "^5.0.0", 24 | "vue-template-compiler": "^2.5.21" 25 | }, 26 | "eslintConfig": { 27 | "root": true, 28 | "env": { 29 | "node": true 30 | }, 31 | "extends": [ 32 | "plugin:vue/essential", 33 | "eslint:recommended" 34 | ], 35 | "rules": {}, 36 | "parserOptions": { 37 | "parser": "babel-eslint" 38 | } 39 | }, 40 | "postcss": { 41 | "plugins": { 42 | "autoprefixer": {} 43 | } 44 | }, 45 | "browserslist": [ 46 | "> 1%", 47 | "last 2 versions", 48 | "not ie <= 8" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /card-game-client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieisabug/card-game/a97a633a453b576ab7ea90984cb44abc2bfcd31f/card-game-client/public/favicon.ico -------------------------------------------------------------------------------- /card-game-client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 程序员大战 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /card-game-client/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /card-game-client/src/assets/dedication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieisabug/card-game/a97a633a453b576ab7ea90984cb44abc2bfcd31f/card-game-client/src/assets/dedication.png -------------------------------------------------------------------------------- /card-game-client/src/assets/hurt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieisabug/card-game/a97a633a453b576ab7ea90984cb44abc2bfcd31f/card-game-client/src/assets/hurt.png -------------------------------------------------------------------------------- /card-game-client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieisabug/card-game/a97a633a453b576ab7ea90984cb44abc2bfcd31f/card-game-client/src/assets/logo.png -------------------------------------------------------------------------------- /card-game-client/src/assets/strong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieisabug/card-game/a97a633a453b576ab7ea90984cb44abc2bfcd31f/card-game-client/src/assets/strong.png -------------------------------------------------------------------------------- /card-game-client/src/components/Card.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 184 | 185 | 298 | -------------------------------------------------------------------------------- /card-game-client/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | 6 | Vue.config.productionTip = false 7 | 8 | new Vue({ 9 | router, 10 | store, 11 | render: h => h(App) 12 | }).$mount('#app') 13 | -------------------------------------------------------------------------------- /card-game-client/src/pages/GameTable.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 342 | 343 | 413 | -------------------------------------------------------------------------------- /card-game-client/src/pages/Login.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 114 | 115 | -------------------------------------------------------------------------------- /card-game-client/src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import GameTable from './pages/GameTable.vue' 4 | import Login from './pages/Login.vue' 5 | 6 | Vue.use(Router) 7 | 8 | export default new Router({ 9 | routes: [ 10 | { 11 | path: '/', 12 | name: 'gameTable', 13 | component: GameTable 14 | }, 15 | { 16 | path: '/login', 17 | name: 'login', 18 | component: Login 19 | } 20 | ] 21 | }) 22 | -------------------------------------------------------------------------------- /card-game-client/src/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | 9 | }, 10 | mutations: { 11 | 12 | }, 13 | actions: { 14 | 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /card-game-client/src/utils.js: -------------------------------------------------------------------------------- 1 | export function buildClassName(obj) { 2 | let className = []; 3 | 4 | Object.keys(obj).forEach(i => { 5 | obj[i] && className.push(i) 6 | }); 7 | 8 | return className.join(" "); 9 | } 10 | 11 | export const AttackType = { 12 | ATTACK: 1, 13 | BE_ATTACKED: 2 14 | }; -------------------------------------------------------------------------------- /card-game-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /card-game-server/app.js: -------------------------------------------------------------------------------- 1 | let http = require('http'); 2 | let express = require('express'); 3 | let cors = require('cors'); 4 | let bodyParser = require('body-parser'); 5 | let socket = require("socket.io"); 6 | 7 | let app = express(); 8 | 9 | let handleSynchronousClient = require('./handler'); 10 | 11 | app.use(cors()); 12 | app.use(bodyParser.json()); 13 | app.use(bodyParser.urlencoded({extended: false})); 14 | 15 | let server = http.createServer(app); 16 | let socketServer = socket(server); 17 | 18 | server.listen(4001, function () { 19 | console.log("listen"); 20 | }); 21 | socketServer.on('connection', function (socket) { 22 | console.log("connect one on :" + new Date().toLocaleString()); 23 | 24 | socket.on('COMMAND', function () { 25 | let args = Array.prototype.slice.call(arguments); 26 | args.push(socket, socketServer); 27 | handleSynchronousClient.apply(this, args); 28 | }); 29 | 30 | 31 | 32 | socket.on('disconnect', function () { 33 | console.log("disconnect one on :" + new Date().toLocaleString()); 34 | }); 35 | 36 | socket.on("ADD", function() { 37 | console.log(arguments); 38 | let args = Array.prototype.slice.call(arguments); 39 | let roomNumber = existUserGameRoomMap[args.userId]; 40 | memoryData[roomNumber].count += 1; 41 | memoryData[roomNumber]["one"].socket.emit("UPDATE", { 42 | count: memoryData[roomNumber].count 43 | }); 44 | memoryData[roomNumber]["two"].socket.emit("UPDATE", { 45 | count: memoryData[roomNumber].count 46 | }); 47 | }) 48 | 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /card-game-server/constants.js: -------------------------------------------------------------------------------- 1 | // { 2 | // id: 0, 3 | // name: "xxxxxx", 4 | // cardType: CardType.CHARACTER, 5 | // cost: 3, 6 | // content: `xxxxxxxxxx`, 7 | // attack: 2, 8 | // life: 1, 9 | // attackBase: 2, 10 | // lifeBase: 1, 11 | // type: [""], 12 | // isStrong: true, 13 | // isFullOfEnergy: true, 14 | // isDedication: true, 15 | // onStart: function() {}, 16 | // onOtherCardStart: function() {}, 17 | // onMyTurnStart: function() {}, 18 | // onMyTurnEnd: function() {}, 19 | // onChooseTarget: function() {}, 20 | // onEnd: function() {}, 21 | // onTableCardChange: function() {}, 22 | // } 23 | 24 | const CardType = { 25 | EFFECT: 1, // 效果牌 26 | CHARACTER: 2, // 人物牌 27 | }; 28 | 29 | const CardList = [ 30 | { 31 | id: 1, 32 | name: "励志的演说家", 33 | cardType: CardType.CHARACTER, 34 | cost: 2, 35 | content: ``, 36 | attack: 1, 37 | life: 2, 38 | attackBase: 1, 39 | lifeBase: 2, 40 | type: [""] 41 | }, 42 | { 43 | id: 5, 44 | name: "高级程序员", 45 | cardType: CardType.CHARACTER, 46 | cost: 7, 47 | content: ``, 48 | attack: 7, 49 | life: 7, 50 | attackBase: 7, 51 | lifeBase: 7, 52 | type: [""], 53 | isStrong: true 54 | }, 55 | { 56 | id: 6, 57 | name: "开发助理", 58 | cardType: CardType.CHARACTER, 59 | cost: 1, 60 | content: ``, 61 | attack: 1, 62 | life: 1, 63 | attackBase: 1, 64 | lifeBase: 1, 65 | type: [""] 66 | }, 67 | { 68 | id: 8, 69 | name: "丑陋的开发鼓励师", 70 | cardType: CardType.CHARACTER, 71 | cost: 1, 72 | content: `精力充沛`, 73 | attack: 1, 74 | life: 1, 75 | attackBase: 1, 76 | lifeBase: 1, 77 | type: [""], 78 | isFullOfEnergy: true 79 | } 80 | ]; 81 | 82 | const AttackType = { 83 | ATTACK: 1, 84 | BE_ATTACKED: 2 85 | }; 86 | 87 | const AttackAnimationType = { 88 | NORMAL: 1 89 | }; 90 | 91 | module.exports = { 92 | CardType, 93 | Cards: CardList, 94 | AttackType, 95 | AttackAnimationType 96 | } -------------------------------------------------------------------------------- /card-game-server/handler.js: -------------------------------------------------------------------------------- 1 | const { 2 | Cards, 3 | CardType, 4 | AttackType, 5 | AttackAnimationType 6 | } = require("./constants"); 7 | 8 | const {shuffle} = require("./utils"); 9 | 10 | const uuidv4 = require('uuid/v4'); 11 | const seedrandom = require("seedrandom"); 12 | 13 | const waitPairQueue = []; // 等待排序的队列 14 | const memoryData = {}; // 缓存的房间游戏数据,key => 房间号,value => 游戏数据 15 | const existUserGameRoomMap = {}; // 缓存用户的房间号, key => 用户标识,value => 房间号 16 | 17 | 18 | module.exports = function handleSynchronousClient(args, socket, socketServer) { 19 | switch (args.type) { 20 | case "CONNECT": 21 | connect(args, socket, socketServer); 22 | break; 23 | case "ATTACK_CARD": 24 | attackCard(args, socket); 25 | break; 26 | case "OUT_CARD": 27 | outCard(args, socket); 28 | break; 29 | } 30 | }; 31 | 32 | function connect(args, socket, socketServer) { 33 | const {userId} = args; 34 | if (existUserGameRoomMap[userId]) { // 如果存在已经加入的对局,则直接进入之前的对战 35 | let roomNumber = existUserGameRoomMap[userId]; 36 | let identity = memoryData[roomNumber]["one"].userId === userId ? "one" : "two"; 37 | memoryData[roomNumber][identity].socket = socket; 38 | memoryData[roomNumber][identity].socket.emit("RECONNECT", { 39 | roomNumber: roomNumber, 40 | memberId: identity 41 | }); 42 | 43 | sendCards(roomNumber, identity); // 把牌发送给玩家 44 | 45 | } else { 46 | socket.emit("WAITE"); // 不管三七二十一,先给老子等起 47 | 48 | if (waitPairQueue.length === 0) { 49 | waitPairQueue.push({ 50 | userId, socket 51 | }); 52 | 53 | socket.emit("WAITE"); 54 | } else { 55 | let waitPlayer = waitPairQueue.splice(0, 1)[0]; // 随便拉个小伙干一架 56 | let roomNumber = uuidv4(); // 生成房间号码 57 | 58 | let seed = Math.floor(Math.random() * 10000); 59 | 60 | // 初始化游戏数据 61 | waitPlayer.roomNumber = roomNumber; 62 | memoryData[roomNumber] = { 63 | "one": waitPlayer, 64 | "two": { 65 | userId, socket, roomNumber 66 | }, 67 | seed, // 随机数种子 68 | rand: seedrandom(seed), // 随机方法 69 | }; 70 | existUserGameRoomMap[userId] = roomNumber; 71 | existUserGameRoomMap[waitPlayer.userId] = roomNumber; 72 | 73 | // 进入房间 74 | socket.join(roomNumber); 75 | waitPlayer.socket.join(roomNumber); 76 | 77 | // 游戏初始化完成,发送游戏初始化数据 78 | waitPlayer.socket.emit("START", { 79 | roomNumber, 80 | memberId: "one" 81 | }); 82 | socket.emit("START", { 83 | roomNumber, 84 | memberId: "two" 85 | }); 86 | 87 | initCard(roomNumber); 88 | } 89 | } 90 | } 91 | 92 | function initCard(roomNumber) { 93 | let random = memoryData[roomNumber].rand() * 2; 94 | 95 | let first = random >= 1 ? "one" : "two"; // 判断当前是哪个玩家出牌 96 | let second = random < 1 ? "one" : "two"; 97 | 98 | memoryData[roomNumber]["one"]["remainingCards"] = shuffle(memoryData[roomNumber].rand, Cards.map((c, index) => Object.assign({k : `one-${index}`}, c))); 99 | memoryData[roomNumber]["two"]["remainingCards"] = shuffle(memoryData[roomNumber].rand, Cards.map((c, index) => Object.assign({k : `two-${index}`}, c))); 100 | 101 | let firstRemainingCards = memoryData[roomNumber][first]["remainingCards"]; 102 | let secondRemainingCards = memoryData[roomNumber][second]["remainingCards"]; 103 | 104 | Object.assign(memoryData[roomNumber][first], { 105 | tableCards:[ 106 | getNextCard(firstRemainingCards), 107 | ], 108 | cards: [ 109 | getNextCard(firstRemainingCards), 110 | getNextCard(firstRemainingCards), 111 | ], 112 | fee: 10 113 | }); 114 | 115 | Object.assign(memoryData[roomNumber][second], { 116 | tableCards:[ 117 | getNextCard(secondRemainingCards), 118 | ], 119 | cards: [ 120 | getNextCard(secondRemainingCards), 121 | ], 122 | fee: 10 123 | }); 124 | 125 | sendCards(roomNumber); 126 | 127 | } 128 | 129 | 130 | 131 | function attackCard(args, socket) { 132 | let roomNumber = args.r, myK = args.myK, attackK = args.attackK, card, attackCard; 133 | 134 | if (!memoryData[roomNumber]) { 135 | return 136 | } 137 | 138 | let belong = memoryData[roomNumber]["one"].socket.id === socket.id ? "one" : "two"; // 判断当前是哪个玩家出牌 139 | let other = memoryData[roomNumber]["one"].socket.id !== socket.id ? "one" : "two"; 140 | 141 | let index = memoryData[roomNumber][belong]["tableCards"].findIndex(c => c.k === myK); 142 | let attackIndex = memoryData[roomNumber][other]["tableCards"].findIndex(c => c.k === attackK); 143 | 144 | if (index !== -1 && attackIndex !== -1 145 | && memoryData[roomNumber][belong]["tableCards"].length > index 146 | && memoryData[roomNumber][other]["tableCards"].length > attackIndex) { 147 | card = memoryData[roomNumber][belong]["tableCards"][index]; 148 | attackCard = memoryData[roomNumber][other]["tableCards"][attackIndex]; 149 | 150 | // 奉献处理 151 | let hasDedication = memoryData[roomNumber][other]["tableCards"].some(c => c.isDedication); 152 | 153 | if (attackCard.isDedication || !hasDedication) { 154 | // 符合奉献的条件 155 | if (attackCard.isStrong) { // 强壮 156 | attackCard.isStrong = false; 157 | } else { 158 | attackCard.life -= card.attack; 159 | } 160 | 161 | if (card.isStrong) { // 强壮 162 | card.isStrong = false; 163 | } else { 164 | card.life -= attackCard.attack; 165 | } 166 | 167 | if (card.onAttack) { 168 | card.onAttack({ 169 | myGameData: memoryData[roomNumber][belong], 170 | otherGameData: memoryData[roomNumber][other], 171 | thisCard: card, 172 | beAttackCard: attackCard, 173 | }) 174 | } 175 | 176 | if (attackCard.onBeAttacked) { 177 | attackCard.onBeAttacked({ 178 | myGameData: memoryData[roomNumber][other], 179 | otherGameData: memoryData[roomNumber][belong], 180 | thisCard: attackCard, 181 | beAttackCard: card, 182 | }) 183 | } 184 | 185 | memoryData[roomNumber][belong].socket.emit("ATTACK_CARD", { 186 | index, 187 | attackIndex, 188 | attackType: AttackType.ATTACK, 189 | animationType: AttackAnimationType.NORMAL, 190 | card, 191 | attackCard 192 | }); 193 | 194 | memoryData[roomNumber][other].socket.emit("ATTACK_CARD", { 195 | index, 196 | attackIndex, 197 | attackType: AttackType.BE_ATTACKED, 198 | animationType: AttackAnimationType.NORMAL, 199 | card, 200 | attackCard 201 | }); 202 | 203 | checkCardDieEvent(roomNumber); 204 | } else { 205 | // error 您必须攻击带有奉献的单位 206 | } 207 | } 208 | 209 | } 210 | 211 | function outCard(args, socket) { 212 | let roomNumber = args.r, index = args.index, card; 213 | 214 | if (!memoryData[roomNumber]) { 215 | return 216 | } 217 | 218 | let belong = memoryData[roomNumber]["one"].socket.id === socket.id ? "one" : "two"; // 判断当前是哪个玩家出牌 219 | let other = memoryData[roomNumber]["one"].socket.id !== socket.id ? "one" : "two"; 220 | 221 | // 判断费用和位置是否已经满了 222 | if (index !== -1 && memoryData[roomNumber][belong]['cards'][index].cost <= memoryData[roomNumber][belong]["fee"]) { 223 | card = memoryData[roomNumber][belong]["cards"].splice(index, 1)[0]; 224 | if (card.cardType === CardType.CHARACTER && memoryData[roomNumber][belong]["tableCards"].length >= 10) { 225 | // error 场上怪物满了 226 | return; 227 | } 228 | 229 | memoryData[roomNumber][belong]["fee"] -= card.cost; 230 | 231 | memoryData[roomNumber][belong]["tableCards"].push(card); 232 | memoryData[roomNumber][belong].socket.emit("OUT_CARD", { 233 | index, 234 | card, 235 | isMine: true 236 | }); 237 | memoryData[roomNumber][other].socket.emit("OUT_CARD", { 238 | index, 239 | card, 240 | isMine: false 241 | }); 242 | 243 | let mySpecialMethod = getSpecialMethod(belong, roomNumber); 244 | if (card && card.onStart) { 245 | card.onStart({ 246 | myGameData: memoryData[roomNumber][belong], 247 | otherGameData: memoryData[roomNumber][other], 248 | thisCard: card, 249 | specialMethod: mySpecialMethod 250 | }) 251 | } 252 | 253 | checkCardDieEvent(roomNumber); 254 | } else { 255 | // error 费用不足 256 | } 257 | } 258 | 259 | 260 | function checkCardDieEvent(roomNumber, level, myKList, otherKList) { 261 | if (!level) { 262 | level = 1; 263 | myKList = []; 264 | otherKList = []; 265 | } 266 | 267 | if (memoryData[roomNumber]["one"]["tableCards"].some(c => c.life <= 0) || memoryData[roomNumber]["two"]["tableCards"].some(c => c.life <= 0)) { 268 | let oneSpecialMethod = getSpecialMethod("one", roomNumber), 269 | twoSpecialMethod = getSpecialMethod("two", roomNumber); 270 | 271 | for (let i = memoryData[roomNumber]["one"]["tableCards"].length - 1; i >= 0; i--) { 272 | let c = memoryData[roomNumber]["one"]["tableCards"][i]; 273 | 274 | if (c.life <= 0) { 275 | if (c.onEnd) { // 亡语 276 | c.onEnd({ 277 | myGameData: memoryData[roomNumber]["one"], 278 | otherGameData: memoryData[roomNumber]["two"], 279 | thisCard: card, 280 | specialMethod: oneSpecialMethod 281 | }) 282 | } 283 | myKList.push(c.k); 284 | memoryData[roomNumber]["one"]["tableCards"].splice(i, 1); 285 | } 286 | } 287 | 288 | for (let i = memoryData[roomNumber]["two"]["tableCards"].length - 1; i >= 0; i--) { 289 | let c = memoryData[roomNumber]["two"]["tableCards"][i]; 290 | 291 | if (c.life <= 0) { 292 | if (c.onEnd) { // 亡语 293 | c.onEnd({ 294 | myGameData: memoryData[roomNumber]["two"], 295 | otherGameData: memoryData[roomNumber]["one"], 296 | thisCard: card, 297 | specialMethod: twoSpecialMethod 298 | }) 299 | } 300 | otherKList.push(c.k); 301 | memoryData[roomNumber]["two"]["tableCards"].splice(i, 1); 302 | } 303 | } 304 | 305 | checkCardDieEvent(roomNumber, level + 1, myKList, otherKList); 306 | } 307 | 308 | if (level === 1 && (myKList.length !== 0 || otherKList.length !== 0)) { 309 | getSpecialMethod("one", roomNumber).dieCardAnimation(true, myKList, otherKList); 310 | } 311 | } 312 | 313 | function getSpecialMethod(identity, roomNumber) { 314 | let otherIdentity = identity === "one" ? "two": "one"; 315 | 316 | return { 317 | dieCardAnimation(isMine, myKList, otherKList) { 318 | memoryData[roomNumber][identity].socket.emit("DIE_CARD", { 319 | isMine, 320 | myKList, 321 | otherKList 322 | }); 323 | 324 | memoryData[roomNumber][otherIdentity].socket.emit("DIE_CARD", { 325 | isMine: !isMine, 326 | myKList, 327 | otherKList 328 | }); 329 | } 330 | } 331 | } 332 | 333 | 334 | function getNextCard(remainingCards) { 335 | if (remainingCards.length > 0) { 336 | return remainingCards.splice(0, 1)[0] 337 | } else { 338 | return null 339 | } 340 | } 341 | 342 | function sendCards(roomNumber, identity) { 343 | if (identity) { 344 | let otherIdentity = identity === "one" ? "two" : "one"; 345 | 346 | memoryData[roomNumber][identity].socket.emit("SEND_CARD", { 347 | myCard: memoryData[roomNumber][identity]["cards"], 348 | myTableCard: memoryData[roomNumber][identity]["tableCards"], 349 | otherTableCard: memoryData[roomNumber][otherIdentity]["tableCards"], 350 | }) 351 | } else { 352 | sendCards(roomNumber, "one"); 353 | sendCards(roomNumber, "two"); 354 | } 355 | } -------------------------------------------------------------------------------- /card-game-server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "card-game-server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "http://registry.npm.taobao.org/accepts/download/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "~2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "after": { 17 | "version": "0.8.2", 18 | "resolved": "http://registry.npm.taobao.org/after/download/after-0.8.2.tgz", 19 | "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" 20 | }, 21 | "array-flatten": { 22 | "version": "1.1.1", 23 | "resolved": "http://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz", 24 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 25 | }, 26 | "arraybuffer.slice": { 27 | "version": "0.0.7", 28 | "resolved": "http://registry.npm.taobao.org/arraybuffer.slice/download/arraybuffer.slice-0.0.7.tgz", 29 | "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=" 30 | }, 31 | "async-limiter": { 32 | "version": "1.0.0", 33 | "resolved": "http://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.0.tgz", 34 | "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=" 35 | }, 36 | "backo2": { 37 | "version": "1.0.2", 38 | "resolved": "http://registry.npm.taobao.org/backo2/download/backo2-1.0.2.tgz", 39 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" 40 | }, 41 | "base64-arraybuffer": { 42 | "version": "0.1.5", 43 | "resolved": "http://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.1.5.tgz", 44 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" 45 | }, 46 | "base64id": { 47 | "version": "1.0.0", 48 | "resolved": "http://registry.npm.taobao.org/base64id/download/base64id-1.0.0.tgz", 49 | "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" 50 | }, 51 | "better-assert": { 52 | "version": "1.0.2", 53 | "resolved": "http://registry.npm.taobao.org/better-assert/download/better-assert-1.0.2.tgz", 54 | "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", 55 | "requires": { 56 | "callsite": "1.0.0" 57 | } 58 | }, 59 | "blob": { 60 | "version": "0.0.5", 61 | "resolved": "http://registry.npm.taobao.org/blob/download/blob-0.0.5.tgz", 62 | "integrity": "sha1-1oDu7yX4zZGtUz9bAe7UjmTK9oM=" 63 | }, 64 | "body-parser": { 65 | "version": "1.18.3", 66 | "resolved": "http://registry.npm.taobao.org/body-parser/download/body-parser-1.18.3.tgz", 67 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 68 | "requires": { 69 | "bytes": "3.0.0", 70 | "content-type": "~1.0.4", 71 | "debug": "2.6.9", 72 | "depd": "~1.1.2", 73 | "http-errors": "~1.6.3", 74 | "iconv-lite": "0.4.23", 75 | "on-finished": "~2.3.0", 76 | "qs": "6.5.2", 77 | "raw-body": "2.3.3", 78 | "type-is": "~1.6.16" 79 | } 80 | }, 81 | "bytes": { 82 | "version": "3.0.0", 83 | "resolved": "http://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz", 84 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 85 | }, 86 | "callsite": { 87 | "version": "1.0.0", 88 | "resolved": "http://registry.npm.taobao.org/callsite/download/callsite-1.0.0.tgz", 89 | "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" 90 | }, 91 | "circular-json": { 92 | "version": "0.5.9", 93 | "resolved": "http://registry.npm.taobao.org/circular-json/download/circular-json-0.5.9.tgz", 94 | "integrity": "sha1-kydjroj0996teg0JyKUaR0OlOx0=" 95 | }, 96 | "component-bind": { 97 | "version": "1.0.0", 98 | "resolved": "http://registry.npm.taobao.org/component-bind/download/component-bind-1.0.0.tgz", 99 | "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" 100 | }, 101 | "component-emitter": { 102 | "version": "1.2.1", 103 | "resolved": "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz", 104 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" 105 | }, 106 | "component-inherit": { 107 | "version": "0.0.3", 108 | "resolved": "http://registry.npm.taobao.org/component-inherit/download/component-inherit-0.0.3.tgz", 109 | "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" 110 | }, 111 | "content-disposition": { 112 | "version": "0.5.2", 113 | "resolved": "http://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.2.tgz", 114 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 115 | }, 116 | "content-type": { 117 | "version": "1.0.4", 118 | "resolved": "http://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz", 119 | "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" 120 | }, 121 | "cookie": { 122 | "version": "0.3.1", 123 | "resolved": "http://registry.npm.taobao.org/cookie/download/cookie-0.3.1.tgz", 124 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 125 | }, 126 | "cookie-signature": { 127 | "version": "1.0.6", 128 | "resolved": "http://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz", 129 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 130 | }, 131 | "core-util-is": { 132 | "version": "1.0.2", 133 | "resolved": "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz", 134 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 135 | }, 136 | "cors": { 137 | "version": "2.8.5", 138 | "resolved": "http://registry.npm.taobao.org/cors/download/cors-2.8.5.tgz", 139 | "integrity": "sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=", 140 | "requires": { 141 | "object-assign": "^4", 142 | "vary": "^1" 143 | } 144 | }, 145 | "date-format": { 146 | "version": "1.2.0", 147 | "resolved": "http://registry.npm.taobao.org/date-format/download/date-format-1.2.0.tgz", 148 | "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=" 149 | }, 150 | "debug": { 151 | "version": "2.6.9", 152 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz", 153 | "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", 154 | "requires": { 155 | "ms": "2.0.0" 156 | } 157 | }, 158 | "depd": { 159 | "version": "1.1.2", 160 | "resolved": "http://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", 161 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 162 | }, 163 | "destroy": { 164 | "version": "1.0.4", 165 | "resolved": "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz", 166 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 167 | }, 168 | "ee-first": { 169 | "version": "1.1.1", 170 | "resolved": "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", 171 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 172 | }, 173 | "encodeurl": { 174 | "version": "1.0.2", 175 | "resolved": "http://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz", 176 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 177 | }, 178 | "engine.io": { 179 | "version": "3.3.2", 180 | "resolved": "http://registry.npm.taobao.org/engine.io/download/engine.io-3.3.2.tgz", 181 | "integrity": "sha1-GMvItvNulGHFwPgd8rgw3hYFilk=", 182 | "requires": { 183 | "accepts": "~1.3.4", 184 | "base64id": "1.0.0", 185 | "cookie": "0.3.1", 186 | "debug": "~3.1.0", 187 | "engine.io-parser": "~2.1.0", 188 | "ws": "~6.1.0" 189 | }, 190 | "dependencies": { 191 | "debug": { 192 | "version": "3.1.0", 193 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", 194 | "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", 195 | "requires": { 196 | "ms": "2.0.0" 197 | } 198 | } 199 | } 200 | }, 201 | "engine.io-client": { 202 | "version": "3.3.1", 203 | "resolved": "http://registry.npm.taobao.org/engine.io-client/download/engine.io-client-3.3.1.tgz", 204 | "integrity": "sha1-r+20oHsupItxkMMTa/6pj91PDwM=", 205 | "requires": { 206 | "component-emitter": "1.2.1", 207 | "component-inherit": "0.0.3", 208 | "debug": "~3.1.0", 209 | "engine.io-parser": "~2.1.1", 210 | "has-cors": "1.1.0", 211 | "indexof": "0.0.1", 212 | "parseqs": "0.0.5", 213 | "parseuri": "0.0.5", 214 | "ws": "~6.1.0", 215 | "xmlhttprequest-ssl": "~1.5.4", 216 | "yeast": "0.1.2" 217 | }, 218 | "dependencies": { 219 | "debug": { 220 | "version": "3.1.0", 221 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", 222 | "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", 223 | "requires": { 224 | "ms": "2.0.0" 225 | } 226 | } 227 | } 228 | }, 229 | "engine.io-parser": { 230 | "version": "2.1.3", 231 | "resolved": "http://registry.npm.taobao.org/engine.io-parser/download/engine.io-parser-2.1.3.tgz", 232 | "integrity": "sha1-dXq5cPvy37Mse3SwMyFtVznveaY=", 233 | "requires": { 234 | "after": "0.8.2", 235 | "arraybuffer.slice": "~0.0.7", 236 | "base64-arraybuffer": "0.1.5", 237 | "blob": "0.0.5", 238 | "has-binary2": "~1.0.2" 239 | } 240 | }, 241 | "escape-html": { 242 | "version": "1.0.3", 243 | "resolved": "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz", 244 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 245 | }, 246 | "etag": { 247 | "version": "1.8.1", 248 | "resolved": "http://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz", 249 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 250 | }, 251 | "express": { 252 | "version": "4.16.4", 253 | "resolved": "http://registry.npm.taobao.org/express/download/express-4.16.4.tgz", 254 | "integrity": "sha1-/d72GSYQniTFFeqX/S8b2/Yt8S4=", 255 | "requires": { 256 | "accepts": "~1.3.5", 257 | "array-flatten": "1.1.1", 258 | "body-parser": "1.18.3", 259 | "content-disposition": "0.5.2", 260 | "content-type": "~1.0.4", 261 | "cookie": "0.3.1", 262 | "cookie-signature": "1.0.6", 263 | "debug": "2.6.9", 264 | "depd": "~1.1.2", 265 | "encodeurl": "~1.0.2", 266 | "escape-html": "~1.0.3", 267 | "etag": "~1.8.1", 268 | "finalhandler": "1.1.1", 269 | "fresh": "0.5.2", 270 | "merge-descriptors": "1.0.1", 271 | "methods": "~1.1.2", 272 | "on-finished": "~2.3.0", 273 | "parseurl": "~1.3.2", 274 | "path-to-regexp": "0.1.7", 275 | "proxy-addr": "~2.0.4", 276 | "qs": "6.5.2", 277 | "range-parser": "~1.2.0", 278 | "safe-buffer": "5.1.2", 279 | "send": "0.16.2", 280 | "serve-static": "1.13.2", 281 | "setprototypeof": "1.1.0", 282 | "statuses": "~1.4.0", 283 | "type-is": "~1.6.16", 284 | "utils-merge": "1.0.1", 285 | "vary": "~1.1.2" 286 | }, 287 | "dependencies": { 288 | "statuses": { 289 | "version": "1.4.0", 290 | "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.4.0.tgz", 291 | "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic=" 292 | } 293 | } 294 | }, 295 | "finalhandler": { 296 | "version": "1.1.1", 297 | "resolved": "http://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.1.tgz", 298 | "integrity": "sha1-7r9O2EAHnIP0JJA4ydcDAIMBsQU=", 299 | "requires": { 300 | "debug": "2.6.9", 301 | "encodeurl": "~1.0.2", 302 | "escape-html": "~1.0.3", 303 | "on-finished": "~2.3.0", 304 | "parseurl": "~1.3.2", 305 | "statuses": "~1.4.0", 306 | "unpipe": "~1.0.0" 307 | }, 308 | "dependencies": { 309 | "statuses": { 310 | "version": "1.4.0", 311 | "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.4.0.tgz", 312 | "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic=" 313 | } 314 | } 315 | }, 316 | "forwarded": { 317 | "version": "0.1.2", 318 | "resolved": "http://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz", 319 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 320 | }, 321 | "fresh": { 322 | "version": "0.5.2", 323 | "resolved": "http://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz", 324 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 325 | }, 326 | "has-binary2": { 327 | "version": "1.0.3", 328 | "resolved": "http://registry.npm.taobao.org/has-binary2/download/has-binary2-1.0.3.tgz", 329 | "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", 330 | "requires": { 331 | "isarray": "2.0.1" 332 | }, 333 | "dependencies": { 334 | "isarray": { 335 | "version": "2.0.1", 336 | "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-2.0.1.tgz", 337 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" 338 | } 339 | } 340 | }, 341 | "has-cors": { 342 | "version": "1.1.0", 343 | "resolved": "http://registry.npm.taobao.org/has-cors/download/has-cors-1.1.0.tgz", 344 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" 345 | }, 346 | "http-errors": { 347 | "version": "1.6.3", 348 | "resolved": "http://registry.npm.taobao.org/http-errors/download/http-errors-1.6.3.tgz", 349 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 350 | "requires": { 351 | "depd": "~1.1.2", 352 | "inherits": "2.0.3", 353 | "setprototypeof": "1.1.0", 354 | "statuses": ">= 1.4.0 < 2" 355 | } 356 | }, 357 | "iconv-lite": { 358 | "version": "0.4.23", 359 | "resolved": "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.23.tgz", 360 | "integrity": "sha1-KXhx9jvlB63Pv8pxXQzQ7thOmmM=", 361 | "requires": { 362 | "safer-buffer": ">= 2.1.2 < 3" 363 | } 364 | }, 365 | "indexof": { 366 | "version": "0.0.1", 367 | "resolved": "http://registry.npm.taobao.org/indexof/download/indexof-0.0.1.tgz", 368 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" 369 | }, 370 | "inherits": { 371 | "version": "2.0.3", 372 | "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", 373 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 374 | }, 375 | "ipaddr.js": { 376 | "version": "1.8.0", 377 | "resolved": "http://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.8.0.tgz", 378 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 379 | }, 380 | "isarray": { 381 | "version": "1.0.0", 382 | "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz", 383 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 384 | }, 385 | "log4js": { 386 | "version": "3.0.6", 387 | "resolved": "http://registry.npm.taobao.org/log4js/download/log4js-3.0.6.tgz", 388 | "integrity": "sha1-5srO2Uln7uuc45n5+GgqSysoyP8=", 389 | "requires": { 390 | "circular-json": "^0.5.5", 391 | "date-format": "^1.2.0", 392 | "debug": "^3.1.0", 393 | "rfdc": "^1.1.2", 394 | "streamroller": "0.7.0" 395 | }, 396 | "dependencies": { 397 | "debug": { 398 | "version": "3.2.6", 399 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz", 400 | "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", 401 | "requires": { 402 | "ms": "^2.1.1" 403 | } 404 | }, 405 | "ms": { 406 | "version": "2.1.1", 407 | "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz", 408 | "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" 409 | } 410 | } 411 | }, 412 | "media-typer": { 413 | "version": "0.3.0", 414 | "resolved": "http://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz", 415 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 416 | }, 417 | "merge-descriptors": { 418 | "version": "1.0.1", 419 | "resolved": "http://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz", 420 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 421 | }, 422 | "methods": { 423 | "version": "1.1.2", 424 | "resolved": "http://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz", 425 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 426 | }, 427 | "mime": { 428 | "version": "1.4.1", 429 | "resolved": "http://registry.npm.taobao.org/mime/download/mime-1.4.1.tgz", 430 | "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=" 431 | }, 432 | "mime-db": { 433 | "version": "1.37.0", 434 | "resolved": "http://registry.npm.taobao.org/mime-db/download/mime-db-1.37.0.tgz", 435 | "integrity": "sha1-C2oM5v2+lXbiXx8tL96IMNwK0Ng=" 436 | }, 437 | "mime-types": { 438 | "version": "2.1.21", 439 | "resolved": "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.21.tgz", 440 | "integrity": "sha1-KJlaoey3cHQv5q5+WPkYHHRLP5Y=", 441 | "requires": { 442 | "mime-db": "~1.37.0" 443 | } 444 | }, 445 | "minimist": { 446 | "version": "0.0.8", 447 | "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz", 448 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 449 | }, 450 | "mkdirp": { 451 | "version": "0.5.1", 452 | "resolved": "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz", 453 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 454 | "requires": { 455 | "minimist": "0.0.8" 456 | } 457 | }, 458 | "ms": { 459 | "version": "2.0.0", 460 | "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", 461 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 462 | }, 463 | "negotiator": { 464 | "version": "0.6.1", 465 | "resolved": "http://registry.npm.taobao.org/negotiator/download/negotiator-0.6.1.tgz", 466 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 467 | }, 468 | "object-assign": { 469 | "version": "4.1.1", 470 | "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", 471 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 472 | }, 473 | "object-component": { 474 | "version": "0.0.3", 475 | "resolved": "http://registry.npm.taobao.org/object-component/download/object-component-0.0.3.tgz", 476 | "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" 477 | }, 478 | "on-finished": { 479 | "version": "2.3.0", 480 | "resolved": "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz", 481 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 482 | "requires": { 483 | "ee-first": "1.1.1" 484 | } 485 | }, 486 | "parseqs": { 487 | "version": "0.0.5", 488 | "resolved": "http://registry.npm.taobao.org/parseqs/download/parseqs-0.0.5.tgz", 489 | "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", 490 | "requires": { 491 | "better-assert": "~1.0.0" 492 | } 493 | }, 494 | "parseuri": { 495 | "version": "0.0.5", 496 | "resolved": "http://registry.npm.taobao.org/parseuri/download/parseuri-0.0.5.tgz", 497 | "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", 498 | "requires": { 499 | "better-assert": "~1.0.0" 500 | } 501 | }, 502 | "parseurl": { 503 | "version": "1.3.2", 504 | "resolved": "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.2.tgz", 505 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 506 | }, 507 | "path-to-regexp": { 508 | "version": "0.1.7", 509 | "resolved": "http://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz", 510 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 511 | }, 512 | "process-nextick-args": { 513 | "version": "2.0.0", 514 | "resolved": "http://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.0.tgz", 515 | "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=" 516 | }, 517 | "proxy-addr": { 518 | "version": "2.0.4", 519 | "resolved": "http://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.4.tgz", 520 | "integrity": "sha1-7PxzO/Iv+Mb0B/onUye5q2fki5M=", 521 | "requires": { 522 | "forwarded": "~0.1.2", 523 | "ipaddr.js": "1.8.0" 524 | } 525 | }, 526 | "qs": { 527 | "version": "6.5.2", 528 | "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz", 529 | "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" 530 | }, 531 | "range-parser": { 532 | "version": "1.2.0", 533 | "resolved": "http://registry.npm.taobao.org/range-parser/download/range-parser-1.2.0.tgz", 534 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 535 | }, 536 | "raw-body": { 537 | "version": "2.3.3", 538 | "resolved": "http://registry.npm.taobao.org/raw-body/download/raw-body-2.3.3.tgz", 539 | "integrity": "sha1-GzJOzmtXBuFThVvBFIxlu39uoMM=", 540 | "requires": { 541 | "bytes": "3.0.0", 542 | "http-errors": "1.6.3", 543 | "iconv-lite": "0.4.23", 544 | "unpipe": "1.0.0" 545 | } 546 | }, 547 | "readable-stream": { 548 | "version": "2.3.6", 549 | "resolved": "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.6.tgz", 550 | "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", 551 | "requires": { 552 | "core-util-is": "~1.0.0", 553 | "inherits": "~2.0.3", 554 | "isarray": "~1.0.0", 555 | "process-nextick-args": "~2.0.0", 556 | "safe-buffer": "~5.1.1", 557 | "string_decoder": "~1.1.1", 558 | "util-deprecate": "~1.0.1" 559 | } 560 | }, 561 | "rfdc": { 562 | "version": "1.1.2", 563 | "resolved": "http://registry.npm.taobao.org/rfdc/download/rfdc-1.1.2.tgz", 564 | "integrity": "sha1-5uctdPXcOd6PU49l4Aw2wYAY40k=" 565 | }, 566 | "safe-buffer": { 567 | "version": "5.1.2", 568 | "resolved": "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", 569 | "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" 570 | }, 571 | "safer-buffer": { 572 | "version": "2.1.2", 573 | "resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", 574 | "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" 575 | }, 576 | "send": { 577 | "version": "0.16.2", 578 | "resolved": "http://registry.npm.taobao.org/send/download/send-0.16.2.tgz", 579 | "integrity": "sha1-bsyh4PjBVtFBWXVZhI32RzCmu8E=", 580 | "requires": { 581 | "debug": "2.6.9", 582 | "depd": "~1.1.2", 583 | "destroy": "~1.0.4", 584 | "encodeurl": "~1.0.2", 585 | "escape-html": "~1.0.3", 586 | "etag": "~1.8.1", 587 | "fresh": "0.5.2", 588 | "http-errors": "~1.6.2", 589 | "mime": "1.4.1", 590 | "ms": "2.0.0", 591 | "on-finished": "~2.3.0", 592 | "range-parser": "~1.2.0", 593 | "statuses": "~1.4.0" 594 | }, 595 | "dependencies": { 596 | "statuses": { 597 | "version": "1.4.0", 598 | "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.4.0.tgz", 599 | "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic=" 600 | } 601 | } 602 | }, 603 | "serve-static": { 604 | "version": "1.13.2", 605 | "resolved": "http://registry.npm.taobao.org/serve-static/download/serve-static-1.13.2.tgz", 606 | "integrity": "sha1-CV6Ecv1bRiN9tQzkhqQ/S4bGzsE=", 607 | "requires": { 608 | "encodeurl": "~1.0.2", 609 | "escape-html": "~1.0.3", 610 | "parseurl": "~1.3.2", 611 | "send": "0.16.2" 612 | } 613 | }, 614 | "setprototypeof": { 615 | "version": "1.1.0", 616 | "resolved": "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.0.tgz", 617 | "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" 618 | }, 619 | "socket.io": { 620 | "version": "2.2.0", 621 | "resolved": "http://registry.npm.taobao.org/socket.io/download/socket.io-2.2.0.tgz", 622 | "integrity": "sha1-8PYzFh72cSyXKzB1mOzQjJsbTVs=", 623 | "requires": { 624 | "debug": "~4.1.0", 625 | "engine.io": "~3.3.1", 626 | "has-binary2": "~1.0.2", 627 | "socket.io-adapter": "~1.1.0", 628 | "socket.io-client": "2.2.0", 629 | "socket.io-parser": "~3.3.0" 630 | }, 631 | "dependencies": { 632 | "debug": { 633 | "version": "4.1.1", 634 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz", 635 | "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", 636 | "requires": { 637 | "ms": "^2.1.1" 638 | } 639 | }, 640 | "ms": { 641 | "version": "2.1.1", 642 | "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz", 643 | "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" 644 | } 645 | } 646 | }, 647 | "socket.io-adapter": { 648 | "version": "1.1.1", 649 | "resolved": "http://registry.npm.taobao.org/socket.io-adapter/download/socket.io-adapter-1.1.1.tgz", 650 | "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" 651 | }, 652 | "socket.io-client": { 653 | "version": "2.2.0", 654 | "resolved": "http://registry.npm.taobao.org/socket.io-client/download/socket.io-client-2.2.0.tgz", 655 | "integrity": "sha1-hOc+48Q9UCDMwaJY+u65rsJyOvc=", 656 | "requires": { 657 | "backo2": "1.0.2", 658 | "base64-arraybuffer": "0.1.5", 659 | "component-bind": "1.0.0", 660 | "component-emitter": "1.2.1", 661 | "debug": "~3.1.0", 662 | "engine.io-client": "~3.3.1", 663 | "has-binary2": "~1.0.2", 664 | "has-cors": "1.1.0", 665 | "indexof": "0.0.1", 666 | "object-component": "0.0.3", 667 | "parseqs": "0.0.5", 668 | "parseuri": "0.0.5", 669 | "socket.io-parser": "~3.3.0", 670 | "to-array": "0.1.4" 671 | }, 672 | "dependencies": { 673 | "debug": { 674 | "version": "3.1.0", 675 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", 676 | "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", 677 | "requires": { 678 | "ms": "2.0.0" 679 | } 680 | } 681 | } 682 | }, 683 | "socket.io-parser": { 684 | "version": "3.3.0", 685 | "resolved": "http://registry.npm.taobao.org/socket.io-parser/download/socket.io-parser-3.3.0.tgz", 686 | "integrity": "sha1-K1KpalCf3zFEC6QP7WCUx9TxJi8=", 687 | "requires": { 688 | "component-emitter": "1.2.1", 689 | "debug": "~3.1.0", 690 | "isarray": "2.0.1" 691 | }, 692 | "dependencies": { 693 | "debug": { 694 | "version": "3.1.0", 695 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", 696 | "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", 697 | "requires": { 698 | "ms": "2.0.0" 699 | } 700 | }, 701 | "isarray": { 702 | "version": "2.0.1", 703 | "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-2.0.1.tgz", 704 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" 705 | } 706 | } 707 | }, 708 | "statuses": { 709 | "version": "1.5.0", 710 | "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz", 711 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 712 | }, 713 | "streamroller": { 714 | "version": "0.7.0", 715 | "resolved": "http://registry.npm.taobao.org/streamroller/download/streamroller-0.7.0.tgz", 716 | "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=", 717 | "requires": { 718 | "date-format": "^1.2.0", 719 | "debug": "^3.1.0", 720 | "mkdirp": "^0.5.1", 721 | "readable-stream": "^2.3.0" 722 | }, 723 | "dependencies": { 724 | "debug": { 725 | "version": "3.2.6", 726 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz", 727 | "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", 728 | "requires": { 729 | "ms": "^2.1.1" 730 | } 731 | }, 732 | "ms": { 733 | "version": "2.1.1", 734 | "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz", 735 | "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" 736 | } 737 | } 738 | }, 739 | "string_decoder": { 740 | "version": "1.1.1", 741 | "resolved": "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz", 742 | "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", 743 | "requires": { 744 | "safe-buffer": "~5.1.0" 745 | } 746 | }, 747 | "to-array": { 748 | "version": "0.1.4", 749 | "resolved": "http://registry.npm.taobao.org/to-array/download/to-array-0.1.4.tgz", 750 | "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" 751 | }, 752 | "type-is": { 753 | "version": "1.6.16", 754 | "resolved": "http://registry.npm.taobao.org/type-is/download/type-is-1.6.16.tgz", 755 | "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", 756 | "requires": { 757 | "media-typer": "0.3.0", 758 | "mime-types": "~2.1.18" 759 | } 760 | }, 761 | "unpipe": { 762 | "version": "1.0.0", 763 | "resolved": "http://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz", 764 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 765 | }, 766 | "util-deprecate": { 767 | "version": "1.0.2", 768 | "resolved": "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz", 769 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 770 | }, 771 | "utils-merge": { 772 | "version": "1.0.1", 773 | "resolved": "http://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz", 774 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 775 | }, 776 | "uuid": { 777 | "version": "3.3.2", 778 | "resolved": "http://registry.npm.taobao.org/uuid/download/uuid-3.3.2.tgz", 779 | "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" 780 | }, 781 | "vary": { 782 | "version": "1.1.2", 783 | "resolved": "http://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz", 784 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 785 | }, 786 | "ws": { 787 | "version": "6.1.2", 788 | "resolved": "http://registry.npm.taobao.org/ws/download/ws-6.1.2.tgz", 789 | "integrity": "sha1-PMdGLph5LwrGeUJBSJA97TucOtg=", 790 | "requires": { 791 | "async-limiter": "~1.0.0" 792 | } 793 | }, 794 | "xmlhttprequest-ssl": { 795 | "version": "1.5.5", 796 | "resolved": "http://registry.npm.taobao.org/xmlhttprequest-ssl/download/xmlhttprequest-ssl-1.5.5.tgz", 797 | "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" 798 | }, 799 | "yeast": { 800 | "version": "0.1.2", 801 | "resolved": "http://registry.npm.taobao.org/yeast/download/yeast-0.1.2.tgz", 802 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" 803 | } 804 | } 805 | } 806 | -------------------------------------------------------------------------------- /card-game-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "card-game-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.18.3", 13 | "cors": "^2.8.5", 14 | "express": "^4.16.4", 15 | "log4js": "^3.0.6", 16 | "seedrandom": "^3.0.5", 17 | "socket.io": "^2.2.0", 18 | "uuid": "^3.3.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /card-game-server/utils.js: -------------------------------------------------------------------------------- 1 | function shuffle(rand, a) { 2 | for (let i = a.length - 1; i > 0; i--) { 3 | const j = Math.floor(rand() * (i + 1)); 4 | [a[i], a[j]] = [a[j], a[i]] 5 | } 6 | 7 | return a; 8 | } 9 | 10 | module.exports = { 11 | shuffle 12 | } --------------------------------------------------------------------------------