From 76ad33403bc901c1689988885542ba8bbe617fa1 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Mon, 10 Jun 2019 11:45:32 -0700 Subject: [PATCH 01/33] logic coppied over from spike --- .eslintignore | 5 ++ .eslintrc.json | 25 ++++++++ .gitignore | 145 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- app.js | 19 ++++++ events.js | 7 +++ package.json | 26 +++++++++ server.js | 154 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 app.js create mode 100644 events.js create mode 100644 package.json create mode 100644 server.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..05b1cf3 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +**/node_modules/* +**/vendor/* +**/*.min.js +**/coverage/* +**/build/* diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..7018e1b --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "env": { + "browser": true, + "node": true, + "commonjs": true, + "jest": true, + "es6": true + }, + "globals": { + "fail":true + }, + "extends": "eslint:recommended", + "parserOptions": { + "sourceType": "module", + "ecmaVersion": 2018 + }, + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": ["error", "single", { "allowTemplateLiterals": true }], + "comma-dangle": ["error", "always-multiline"], + "semi": [ "error", "always" ], + "no-unused-vars": [1, {"vars": "local", "args": "none"}] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9497282 --- /dev/null +++ b/.gitignore @@ -0,0 +1,145 @@ +# 401 JS +db +.env +temp +build + +# Created by https://www.gitignore.io/api/vim,osx,node,linux,windows +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/vim,osx,node,linux,windows diff --git a/README.md b/README.md index 6bb5639..b83b85d 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# socket_server \ No newline at end of file +# spike \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..f67d78a --- /dev/null +++ b/app.js @@ -0,0 +1,19 @@ +'use strict'; + +const client = require('socket.io-client'); +const prompt = require('prompt'); + +const socket = client.connect('http://localhost:3000'); + +socket.emit('move'); + +socket.on('moved', (payload) => { + console.log('It\'s your turn'); + + prompt.start(); + + prompt.get(['move'], (err, data) => { + socket.emit('move', {move: data.move}); + console.log('You selected', data.move); + }); +}) \ No newline at end of file diff --git a/events.js b/events.js new file mode 100644 index 0000000..64edd37 --- /dev/null +++ b/events.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = { + move: 'move', + turn: 'turn', +}; + diff --git a/package.json b/package.json new file mode 100644 index 0000000..273d483 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "socket_server", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/401-advanced-javascript-nimble/socket_server.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/401-advanced-javascript-nimble/socket_server/issues" + }, + "homepage": "https://github.com/401-advanced-javascript-nimble/socket_server#readme", + "dependencies": { + "dotenv": "^8.0.0", + "eslint": "^5.16.0", + "socket.io": "^2.2.0" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..5ebe170 --- /dev/null +++ b/server.js @@ -0,0 +1,154 @@ +'use strict'; + +require('dotenv').config(); +const prompt = require('prompt'); + +const events = require('./events.js'); + +const io = require('socket.io')(process.env.PORT); + +const players = []; + +io.on('connect', (socket) => { + console.log(`Socket ${socket.id} connected`); + players.push(socket.id); + + if(players.length === 2) { + // Chris - This gets the party started!!! + getInput(); + } + + socket.on(events.move, payload => { + console.log(payload); + + let playerWhoMoved = players.indexOf(socket.id); + if(playerWhoMoved === 0) { + io.to(`${players[1]}`).emit(events.turn, 'Your Turn'); + } + if(playerWhoMoved === 1) { + io.to(`${players[0]}`).emit(events.turn, 'Your turn'); + } + }); +}); + + + + +// Chris - this controls the prompt, and calls the gameCycle with the inputs. +const getInput = () => { + console.table(stacks); + prompt.start(); + prompt.get(['stack', 'number'], (err, data) => { + if(err) { + throw new Error(err); + } + let stackNumber = data.stack; + let numberToTake = data.number; + gameCycle(stackNumber, numberToTake); + }); +}; + +// Chris - Here is the gameplay loop. This continues until totalRemainingItems is 0, which changes isGameOver to true. +const gameCycle = (stackNumber, numberToTake) => { + if(checkChoices(stackNumber, numberToTake) === true) { + takeItemsFromStack(stackNumber, numberToTake); + } + checkForGameOver(); + if(isGameOver === false) { + console.log({totalItemsRemaining}); + getInput(); + } + else { + console.log('GAME OVER!!!!!!!'); + } +}; + + + +//============================================= + +//Morgana - generates a random amount between 5 and 25 +function generateRandomAmount() { + return Math.floor(Math.random() * 20) + 5; +} + +// Chris - Global variable to control if the game is still ongoing or if it has ended. +let isGameOver = false; + +// Chris - Here's a basic setup for stacks to use during the game. Might not be best as an array, but it was the easiest for me to get started with. +// Refactoring into a different data type shouldn't be too tough if needed. +//Morgana - switched it over to dynamic ammounts +const stacks = [ + { + name: 'A', + numberOfItems: generateRandomAmount(), + }, + { + name: 'B', + numberOfItems: generateRandomAmount(), + }, + { + name: 'C', + numberOfItems: generateRandomAmount(), + }, +]; + +// Chris - a tally to keep track of how many items are left game-wide accross all stacks. +// When this is zero, game is over. +let totalItemsRemaining; + +// Chris - this function tallies a new total for totalItemsRemaining after a player makes a valid move. +// would change if we go to something other than an array to store the stacks. +const _tallyTotalItemsRemaining = () => { + totalItemsRemaining = stacks.reduce((acc, cur) => { + return acc + cur.numberOfItems; + }, 0); +}; + +// Chris - sets initial state for totalItemsRemaining. +// Typically this only runs inside the takeItemsFromStack function to ensure it is consistently updated during gameplay. +_tallyTotalItemsRemaining(); + +// Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. +// I'm subtracting 1 from the stackNumber to keep indexing simpler from a player's perpective (I doubt they'd choose to take from stack 0) +// would change if we go to something other than an array to store the stacks. +const takeItemsFromStack = (stackNumber, numberToTake) => { + stacks[stackNumber - 1].numberOfItems = stacks[stackNumber - 1].numberOfItems - numberToTake; + _tallyTotalItemsRemaining(); +}; + +// Chris - checks and validates the players' inputs. +const checkChoices = (stackNumber, numberToTake) => { + // checks and validation for stackNumber + if(typeof stackNumber !== 'number') { + stackNumber = parseInt(stackNumber); + } + if(isNaN(stackNumber) || stackNumber > stacks.length) { + console.log('Invalid stack choice'); + return false; + } + + // checks and validation for numberToTake + if(typeof numberToTake !== 'number') { + numberToTake = parseInt(numberToTake); + } + if(isNaN(numberToTake) || numberToTake < 1 || numberToTake > stacks[stackNumber - 1].numberOfItems) { + console.log('Invalid numberToTake choice'); + return false; + } + + // Chris - if all the above is good, return true to continue gameplay loop + return true; +}; + +// Chris - used to end the game when totalRemainingItems is empty +const checkForGameOver = () => { + if(totalItemsRemaining === 0) { + isGameOver = true; + } + else { + return; + } +}; + + From 4bef4f58b0f9827eb52f1d4c316b92850e99ef78 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Mon, 10 Jun 2019 13:22:30 -0700 Subject: [PATCH 02/33] most of the game logic integrated --- package.json | 4 ++- server.js | 92 +++++++++++++++++++------------------------------- test_client.js | 28 +++++++++++++++ 3 files changed, 65 insertions(+), 59 deletions(-) create mode 100644 test_client.js diff --git a/package.json b/package.json index 273d483..f8fbd0c 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "dependencies": { "dotenv": "^8.0.0", "eslint": "^5.16.0", - "socket.io": "^2.2.0" + "prompt": "^1.0.0", + "socket.io": "^2.2.0", + "socket.io-client": "^2.2.0" } } diff --git a/server.js b/server.js index 5ebe170..8d65b26 100644 --- a/server.js +++ b/server.js @@ -7,6 +7,18 @@ const events = require('./events.js'); const io = require('socket.io')(process.env.PORT); +// Chris - Global variable to control if the game is still ongoing or if it has ended. +let isGameOver = false; + +// Chris - Here's a basic setup for stacks to use during the game. Might not be best as an array, but it was the easiest for me to get started with. +// Refactoring into a different data type shouldn't be too tough if needed. +//Morgana - switched it over to dynamic ammounts +const stacks = {}; + +stacks.a = generateRandomAmount(); +stacks.b = generateRandomAmount(); +stacks.c = generateRandomAmount(); + const players = []; io.on('connect', (socket) => { @@ -14,8 +26,7 @@ io.on('connect', (socket) => { players.push(socket.id); if(players.length === 2) { - // Chris - This gets the party started!!! - getInput(); + io.to(`${players[0]}`).emit(events.turn, 'You Start'); } socket.on(events.move, payload => { @@ -23,40 +34,26 @@ io.on('connect', (socket) => { let playerWhoMoved = players.indexOf(socket.id); if(playerWhoMoved === 0) { - io.to(`${players[1]}`).emit(events.turn, 'Your Turn'); + gameCycle(payload[0], payload[1]); + io.to(`${players[1]}`).emit(events.turn, stacks); } if(playerWhoMoved === 1) { - io.to(`${players[0]}`).emit(events.turn, 'Your turn'); + io.to(`${players[0]}`).emit(events.turn, stacks); + gameCycle(payload[0], payload[1]); } }); }); - - -// Chris - this controls the prompt, and calls the gameCycle with the inputs. -const getInput = () => { - console.table(stacks); - prompt.start(); - prompt.get(['stack', 'number'], (err, data) => { - if(err) { - throw new Error(err); - } - let stackNumber = data.stack; - let numberToTake = data.number; - gameCycle(stackNumber, numberToTake); - }); -}; - // Chris - Here is the gameplay loop. This continues until totalRemainingItems is 0, which changes isGameOver to true. -const gameCycle = (stackNumber, numberToTake) => { - if(checkChoices(stackNumber, numberToTake) === true) { - takeItemsFromStack(stackNumber, numberToTake); +const gameCycle = (stackChoice, numberToTake) => { + if(checkChoices(stackChoice, numberToTake) === true) { + takeItemsFromStack(stackChoice, numberToTake); } checkForGameOver(); if(isGameOver === false) { console.log({totalItemsRemaining}); - getInput(); + // getInput(); } else { console.log('GAME OVER!!!!!!!'); @@ -72,27 +69,6 @@ function generateRandomAmount() { return Math.floor(Math.random() * 20) + 5; } -// Chris - Global variable to control if the game is still ongoing or if it has ended. -let isGameOver = false; - -// Chris - Here's a basic setup for stacks to use during the game. Might not be best as an array, but it was the easiest for me to get started with. -// Refactoring into a different data type shouldn't be too tough if needed. -//Morgana - switched it over to dynamic ammounts -const stacks = [ - { - name: 'A', - numberOfItems: generateRandomAmount(), - }, - { - name: 'B', - numberOfItems: generateRandomAmount(), - }, - { - name: 'C', - numberOfItems: generateRandomAmount(), - }, -]; - // Chris - a tally to keep track of how many items are left game-wide accross all stacks. // When this is zero, game is over. let totalItemsRemaining; @@ -100,9 +76,11 @@ let totalItemsRemaining; // Chris - this function tallies a new total for totalItemsRemaining after a player makes a valid move. // would change if we go to something other than an array to store the stacks. const _tallyTotalItemsRemaining = () => { - totalItemsRemaining = stacks.reduce((acc, cur) => { - return acc + cur.numberOfItems; - }, 0); + let total = 0; + for(let i in stacks) { + total += stacks[i]; + } + return total; }; // Chris - sets initial state for totalItemsRemaining. @@ -110,20 +88,18 @@ const _tallyTotalItemsRemaining = () => { _tallyTotalItemsRemaining(); // Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. -// I'm subtracting 1 from the stackNumber to keep indexing simpler from a player's perpective (I doubt they'd choose to take from stack 0) +// I'm subtracting 1 from the stackChoice to keep indexing simpler from a player's perpective (I doubt they'd choose to take from stack 0) // would change if we go to something other than an array to store the stacks. -const takeItemsFromStack = (stackNumber, numberToTake) => { - stacks[stackNumber - 1].numberOfItems = stacks[stackNumber - 1].numberOfItems - numberToTake; +const takeItemsFromStack = (stackChoice, numberToTake) => { + stacks[stackChoice] = stacks[stackChoice] - numberToTake; _tallyTotalItemsRemaining(); }; // Chris - checks and validates the players' inputs. -const checkChoices = (stackNumber, numberToTake) => { - // checks and validation for stackNumber - if(typeof stackNumber !== 'number') { - stackNumber = parseInt(stackNumber); - } - if(isNaN(stackNumber) || stackNumber > stacks.length) { +const checkChoices = (stackChoice, numberToTake) => { + // checks and validation for stackChoice + stackChoice.toLowerCase(); + if(!Object.keys(stacks).includes(stackChoice)) { console.log('Invalid stack choice'); return false; } @@ -132,7 +108,7 @@ const checkChoices = (stackNumber, numberToTake) => { if(typeof numberToTake !== 'number') { numberToTake = parseInt(numberToTake); } - if(isNaN(numberToTake) || numberToTake < 1 || numberToTake > stacks[stackNumber - 1].numberOfItems) { + if(isNaN(numberToTake) || numberToTake < 1 || numberToTake > stacks[stackChoice]) { console.log('Invalid numberToTake choice'); return false; } diff --git a/test_client.js b/test_client.js new file mode 100644 index 0000000..d050466 --- /dev/null +++ b/test_client.js @@ -0,0 +1,28 @@ +'use strict'; + +const client = require('socket.io-client'); +const prompt = require('prompt'); + +const socket = client.connect('http://localhost:3000'); + +// socket.emit('move'); + +socket.on('turn', (payload) => { + console.log('It\'s your turn'); + console.log(payload); + + prompt.start(); + + + prompt.get(['stack', 'amount'], (err, data) => { + if(err) { + throw new Error(err); + } + let stack = data.stack; + let amount = data.amount; + + socket.emit('move', [stack, amount]); + + // gameCycle(stackNumber, numberToTake); + }); +}); \ No newline at end of file From 9c112b169d47a2514679b6127a95196b2df74ca3 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Mon, 10 Jun 2019 13:42:17 -0700 Subject: [PATCH 03/33] removal and total working --- server.js | 76 +++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/server.js b/server.js index 8d65b26..36c74c2 100644 --- a/server.js +++ b/server.js @@ -7,26 +7,35 @@ const events = require('./events.js'); const io = require('socket.io')(process.env.PORT); -// Chris - Global variable to control if the game is still ongoing or if it has ended. -let isGameOver = false; +//=========================================== +//Global Variables +//=========================================== + +// Chris - a tally to keep track of how many items are left game-wide accross all stacks. +// When this is zero, game is over. +let totalItemsRemaining; // Chris - Here's a basic setup for stacks to use during the game. Might not be best as an array, but it was the easiest for me to get started with. // Refactoring into a different data type shouldn't be too tough if needed. -//Morgana - switched it over to dynamic ammounts -const stacks = {}; - -stacks.a = generateRandomAmount(); -stacks.b = generateRandomAmount(); -stacks.c = generateRandomAmount(); +//Morgana - switched it over to being an object and using dynamic amounts. +const stacks = { + a: generateRandomAmount(), + b: generateRandomAmount(), + c: generateRandomAmount(), +}; const players = []; +//=========================================== +//Socket +//=========================================== + io.on('connect', (socket) => { console.log(`Socket ${socket.id} connected`); players.push(socket.id); if(players.length === 2) { - io.to(`${players[0]}`).emit(events.turn, 'You Start'); + io.to(`${players[0]}`).emit(events.turn, stacks); } socket.on(events.move, payload => { @@ -35,44 +44,48 @@ io.on('connect', (socket) => { let playerWhoMoved = players.indexOf(socket.id); if(playerWhoMoved === 0) { gameCycle(payload[0], payload[1]); - io.to(`${players[1]}`).emit(events.turn, stacks); + + if(totalItemsRemaining === 0) { + io.to(`${players[1]}`).emit(events.turn, 'Game Over!'); + } else { + io.to(`${players[1]}`).emit(events.turn, stacks); + } } if(playerWhoMoved === 1) { - io.to(`${players[0]}`).emit(events.turn, stacks); gameCycle(payload[0], payload[1]); + + if(totalItemsRemaining === 0) { + io.to(`${players[0]}`).emit(events.turn, 'Game Over!'); + } else { + io.to(`${players[0]}`).emit(events.turn, stacks); + } } }); }); +//=========================================== +//Server Side Game Logic +//=========================================== + // Chris - Here is the gameplay loop. This continues until totalRemainingItems is 0, which changes isGameOver to true. const gameCycle = (stackChoice, numberToTake) => { if(checkChoices(stackChoice, numberToTake) === true) { takeItemsFromStack(stackChoice, numberToTake); } - checkForGameOver(); - if(isGameOver === false) { + if(totalItemsRemaining !== 0) { console.log({totalItemsRemaining}); - // getInput(); } else { console.log('GAME OVER!!!!!!!'); } }; - - -//============================================= - //Morgana - generates a random amount between 5 and 25 function generateRandomAmount() { return Math.floor(Math.random() * 20) + 5; } -// Chris - a tally to keep track of how many items are left game-wide accross all stacks. -// When this is zero, game is over. -let totalItemsRemaining; - // Chris - this function tallies a new total for totalItemsRemaining after a player makes a valid move. // would change if we go to something other than an array to store the stacks. const _tallyTotalItemsRemaining = () => { @@ -80,13 +93,9 @@ const _tallyTotalItemsRemaining = () => { for(let i in stacks) { total += stacks[i]; } - return total; + totalItemsRemaining = total; }; -// Chris - sets initial state for totalItemsRemaining. -// Typically this only runs inside the takeItemsFromStack function to ensure it is consistently updated during gameplay. -_tallyTotalItemsRemaining(); - // Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. // I'm subtracting 1 from the stackChoice to keep indexing simpler from a player's perpective (I doubt they'd choose to take from stack 0) // would change if we go to something other than an array to store the stacks. @@ -96,6 +105,7 @@ const takeItemsFromStack = (stackChoice, numberToTake) => { }; // Chris - checks and validates the players' inputs. +// Morgana - Changed to reflect new stack structure const checkChoices = (stackChoice, numberToTake) => { // checks and validation for stackChoice stackChoice.toLowerCase(); @@ -109,7 +119,7 @@ const checkChoices = (stackChoice, numberToTake) => { numberToTake = parseInt(numberToTake); } if(isNaN(numberToTake) || numberToTake < 1 || numberToTake > stacks[stackChoice]) { - console.log('Invalid numberToTake choice'); + console.log('Invalid amount'); return false; } @@ -117,14 +127,4 @@ const checkChoices = (stackChoice, numberToTake) => { return true; }; -// Chris - used to end the game when totalRemainingItems is empty -const checkForGameOver = () => { - if(totalItemsRemaining === 0) { - isGameOver = true; - } - else { - return; - } -}; - From 501bb2af3c5b2a5f67b16e1063b86b2a85f0462b Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Mon, 10 Jun 2019 14:00:14 -0700 Subject: [PATCH 04/33] game endsat the same time for both players --- events.js | 1 + server.js | 28 +++++++++++++--------------- test_client.js | 7 +++++-- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/events.js b/events.js index 64edd37..0023950 100644 --- a/events.js +++ b/events.js @@ -3,5 +3,6 @@ module.exports = { move: 'move', turn: 'turn', + gameOver: 'game over', }; diff --git a/server.js b/server.js index 36c74c2..6192168 100644 --- a/server.js +++ b/server.js @@ -39,26 +39,15 @@ io.on('connect', (socket) => { } socket.on(events.move, payload => { - console.log(payload); - let playerWhoMoved = players.indexOf(socket.id); + if(playerWhoMoved === 0) { gameCycle(payload[0], payload[1]); - - if(totalItemsRemaining === 0) { - io.to(`${players[1]}`).emit(events.turn, 'Game Over!'); - } else { - io.to(`${players[1]}`).emit(events.turn, stacks); - } + io.to(`${players[1]}`).emit(events.turn, stacks); } - if(playerWhoMoved === 1) { + else if(playerWhoMoved === 1) { gameCycle(payload[0], payload[1]); - - if(totalItemsRemaining === 0) { - io.to(`${players[0]}`).emit(events.turn, 'Game Over!'); - } else { - io.to(`${players[0]}`).emit(events.turn, stacks); - } + io.to(`${players[0]}`).emit(events.turn, stacks); } }); }); @@ -68,6 +57,13 @@ io.on('connect', (socket) => { //Server Side Game Logic //=========================================== +function checkAndHandleGameOver() { + if(totalItemsRemaining === 0) { + io.to(`${players[0]}`).emit(events.gameOver, 'Game Over!'); + io.to(`${players[1]}`).emit(events.gameOver, 'Game Over!'); + } +} + // Chris - Here is the gameplay loop. This continues until totalRemainingItems is 0, which changes isGameOver to true. const gameCycle = (stackChoice, numberToTake) => { if(checkChoices(stackChoice, numberToTake) === true) { @@ -78,6 +74,8 @@ const gameCycle = (stackChoice, numberToTake) => { } else { console.log('GAME OVER!!!!!!!'); + io.to(`${players[0]}`).emit(events.gameOver, 'Game Over!'); + io.to(`${players[1]}`).emit(events.gameOver, 'Game Over!'); } }; diff --git a/test_client.js b/test_client.js index d050466..3d38e7d 100644 --- a/test_client.js +++ b/test_client.js @@ -13,7 +13,6 @@ socket.on('turn', (payload) => { prompt.start(); - prompt.get(['stack', 'amount'], (err, data) => { if(err) { throw new Error(err); @@ -22,7 +21,11 @@ socket.on('turn', (payload) => { let amount = data.amount; socket.emit('move', [stack, amount]); - // gameCycle(stackNumber, numberToTake); }); +}); + +socket.on('game over', (payload) => { + console.log('Game Over!'); + socket.close(); }); \ No newline at end of file From 018b5b102d9b291b032e5e960496e1342223268d Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Mon, 10 Jun 2019 14:07:10 -0700 Subject: [PATCH 05/33] comments update --- server.js | 19 +++++-------------- test_client.js | 1 - 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/server.js b/server.js index 6192168..3b84814 100644 --- a/server.js +++ b/server.js @@ -15,8 +15,7 @@ const io = require('socket.io')(process.env.PORT); // When this is zero, game is over. let totalItemsRemaining; -// Chris - Here's a basic setup for stacks to use during the game. Might not be best as an array, but it was the easiest for me to get started with. -// Refactoring into a different data type shouldn't be too tough if needed. +// Chris - Here's a basic setup for stacks to use during the game. //Morgana - switched it over to being an object and using dynamic amounts. const stacks = { a: generateRandomAmount(), @@ -57,14 +56,7 @@ io.on('connect', (socket) => { //Server Side Game Logic //=========================================== -function checkAndHandleGameOver() { - if(totalItemsRemaining === 0) { - io.to(`${players[0]}`).emit(events.gameOver, 'Game Over!'); - io.to(`${players[1]}`).emit(events.gameOver, 'Game Over!'); - } -} - -// Chris - Here is the gameplay loop. This continues until totalRemainingItems is 0, which changes isGameOver to true. +//Morgana - refactored to handle new stack structure and gameover emits const gameCycle = (stackChoice, numberToTake) => { if(checkChoices(stackChoice, numberToTake) === true) { takeItemsFromStack(stackChoice, numberToTake); @@ -85,7 +77,7 @@ function generateRandomAmount() { } // Chris - this function tallies a new total for totalItemsRemaining after a player makes a valid move. -// would change if we go to something other than an array to store the stacks. +// Morgana - adjusted for new stack structure const _tallyTotalItemsRemaining = () => { let total = 0; for(let i in stacks) { @@ -95,8 +87,7 @@ const _tallyTotalItemsRemaining = () => { }; // Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. -// I'm subtracting 1 from the stackChoice to keep indexing simpler from a player's perpective (I doubt they'd choose to take from stack 0) -// would change if we go to something other than an array to store the stacks. +// Morgana - adjusted for new stack structure const takeItemsFromStack = (stackChoice, numberToTake) => { stacks[stackChoice] = stacks[stackChoice] - numberToTake; _tallyTotalItemsRemaining(); @@ -121,7 +112,7 @@ const checkChoices = (stackChoice, numberToTake) => { return false; } - // Chris - if all the above is good, return true to continue gameplay loop + // Chris - if all the above is good continue return true; }; diff --git a/test_client.js b/test_client.js index 3d38e7d..28d1fca 100644 --- a/test_client.js +++ b/test_client.js @@ -21,7 +21,6 @@ socket.on('turn', (payload) => { let amount = data.amount; socket.emit('move', [stack, amount]); - // gameCycle(stackNumber, numberToTake); }); }); From a6d05ba90abdcc7bead3a1fef2fd67da150ed06d Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Mon, 10 Jun 2019 15:13:58 -0700 Subject: [PATCH 06/33] can handle invalid input (client side) --- server.js | 30 +++------------------ test_client.js | 71 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 38 deletions(-) diff --git a/server.js b/server.js index 3b84814..9420c5e 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,6 @@ 'use strict'; require('dotenv').config(); -const prompt = require('prompt'); const events = require('./events.js'); @@ -26,7 +25,7 @@ const stacks = { const players = []; //=========================================== -//Socket +//Sockets //=========================================== io.on('connect', (socket) => { @@ -58,9 +57,9 @@ io.on('connect', (socket) => { //Morgana - refactored to handle new stack structure and gameover emits const gameCycle = (stackChoice, numberToTake) => { - if(checkChoices(stackChoice, numberToTake) === true) { + // if(checkChoices(stackChoice, numberToTake) === true) { takeItemsFromStack(stackChoice, numberToTake); - } + // } if(totalItemsRemaining !== 0) { console.log({totalItemsRemaining}); } @@ -93,27 +92,4 @@ const takeItemsFromStack = (stackChoice, numberToTake) => { _tallyTotalItemsRemaining(); }; -// Chris - checks and validates the players' inputs. -// Morgana - Changed to reflect new stack structure -const checkChoices = (stackChoice, numberToTake) => { - // checks and validation for stackChoice - stackChoice.toLowerCase(); - if(!Object.keys(stacks).includes(stackChoice)) { - console.log('Invalid stack choice'); - return false; - } - - // checks and validation for numberToTake - if(typeof numberToTake !== 'number') { - numberToTake = parseInt(numberToTake); - } - if(isNaN(numberToTake) || numberToTake < 1 || numberToTake > stacks[stackChoice]) { - console.log('Invalid amount'); - return false; - } - - // Chris - if all the above is good continue - return true; -}; - diff --git a/test_client.js b/test_client.js index 28d1fca..5c6a96a 100644 --- a/test_client.js +++ b/test_client.js @@ -3,28 +3,77 @@ const client = require('socket.io-client'); const prompt = require('prompt'); +//Morgana - refactor to use deployed link const socket = client.connect('http://localhost:3000'); -// socket.emit('move'); +//=================================== +// Client Socket +//=================================== + +prompt.message = 'Your move'; socket.on('turn', (payload) => { - console.log('It\'s your turn'); + showPrompt(payload); +}); + +socket.on('game over', (payload) => { + console.log('Game Over!'); + socket.close(); +}); + + +//=================================== +// Prompt Functionality +//=================================== + +//Morgana - reusable prompt function +//calls itself again if an invalid selection is made +function showPrompt(payload) { console.log(payload); prompt.start(); prompt.get(['stack', 'amount'], (err, data) => { - if(err) { - throw new Error(err); - } + // if(err) { + // throw new Error(err); + // } let stack = data.stack; let amount = data.amount; - socket.emit('move', [stack, amount]); + if(checkChoices(stack, amount, payload)) { + socket.emit('move', [stack, amount]); + } + else { + showPrompt(payload); + } }); -}); +} + +//=================================== +// Client Side Game Logic +//=================================== + + +// Chris - checks and validates the players' inputs. +// Morgana - Changed to reflect new stack structure +const checkChoices = (stackChoice, numberToTake, stacks) => { + // checks and validation for stackChoice + stackChoice.toLowerCase(); + if(!Object.keys(stacks).includes(stackChoice)) { + console.log('Invalid stack, please pick again.'); + return false; + } + + // checks and validation for numberToTake + if(typeof numberToTake !== 'number') { + numberToTake = parseInt(numberToTake); + } + if(isNaN(numberToTake) || numberToTake < 1 || numberToTake > stacks[stackChoice]) { + console.log('Invalid amount, please pick again'); + return false; + } + + // Chris - if all the above is good continue + return true; +}; -socket.on('game over', (payload) => { - console.log('Game Over!'); - socket.close(); -}); \ No newline at end of file From fa4a810821f3401f88bd3fba0d832d5d20481600 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Mon, 10 Jun 2019 16:06:38 -0700 Subject: [PATCH 07/33] removed test client --- app.js | 2 +- server.js | 4 +-- test_client.js | 79 -------------------------------------------------- 3 files changed, 3 insertions(+), 82 deletions(-) delete mode 100644 test_client.js diff --git a/app.js b/app.js index f67d78a..d04ad04 100644 --- a/app.js +++ b/app.js @@ -16,4 +16,4 @@ socket.on('moved', (payload) => { socket.emit('move', {move: data.move}); console.log('You selected', data.move); }); -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/server.js b/server.js index 9420c5e..ef50c20 100644 --- a/server.js +++ b/server.js @@ -58,7 +58,7 @@ io.on('connect', (socket) => { //Morgana - refactored to handle new stack structure and gameover emits const gameCycle = (stackChoice, numberToTake) => { // if(checkChoices(stackChoice, numberToTake) === true) { - takeItemsFromStack(stackChoice, numberToTake); + takeItemsFromStack(stackChoice, numberToTake); // } if(totalItemsRemaining !== 0) { console.log({totalItemsRemaining}); @@ -82,7 +82,7 @@ const _tallyTotalItemsRemaining = () => { for(let i in stacks) { total += stacks[i]; } - totalItemsRemaining = total; + totalItemsRemaining = total; }; // Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. diff --git a/test_client.js b/test_client.js deleted file mode 100644 index 5c6a96a..0000000 --- a/test_client.js +++ /dev/null @@ -1,79 +0,0 @@ -'use strict'; - -const client = require('socket.io-client'); -const prompt = require('prompt'); - -//Morgana - refactor to use deployed link -const socket = client.connect('http://localhost:3000'); - -//=================================== -// Client Socket -//=================================== - -prompt.message = 'Your move'; - -socket.on('turn', (payload) => { - showPrompt(payload); -}); - -socket.on('game over', (payload) => { - console.log('Game Over!'); - socket.close(); -}); - - -//=================================== -// Prompt Functionality -//=================================== - -//Morgana - reusable prompt function -//calls itself again if an invalid selection is made -function showPrompt(payload) { - console.log(payload); - - prompt.start(); - - prompt.get(['stack', 'amount'], (err, data) => { - // if(err) { - // throw new Error(err); - // } - let stack = data.stack; - let amount = data.amount; - - if(checkChoices(stack, amount, payload)) { - socket.emit('move', [stack, amount]); - } - else { - showPrompt(payload); - } - }); -} - -//=================================== -// Client Side Game Logic -//=================================== - - -// Chris - checks and validates the players' inputs. -// Morgana - Changed to reflect new stack structure -const checkChoices = (stackChoice, numberToTake, stacks) => { - // checks and validation for stackChoice - stackChoice.toLowerCase(); - if(!Object.keys(stacks).includes(stackChoice)) { - console.log('Invalid stack, please pick again.'); - return false; - } - - // checks and validation for numberToTake - if(typeof numberToTake !== 'number') { - numberToTake = parseInt(numberToTake); - } - if(isNaN(numberToTake) || numberToTake < 1 || numberToTake > stacks[stackChoice]) { - console.log('Invalid amount, please pick again'); - return false; - } - - // Chris - if all the above is good continue - return true; -}; - From 8b6ac702999d15cca7bb98089d6f91a48b00d471 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Tue, 11 Jun 2019 09:49:16 -0700 Subject: [PATCH 08/33] game flow updates --- events.js | 1 + server.js | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/events.js b/events.js index 0023950..d4fd71c 100644 --- a/events.js +++ b/events.js @@ -4,5 +4,6 @@ module.exports = { move: 'move', turn: 'turn', gameOver: 'game over', + message: 'message-from-server', }; diff --git a/server.js b/server.js index ef50c20..606a042 100644 --- a/server.js +++ b/server.js @@ -16,13 +16,13 @@ let totalItemsRemaining; // Chris - Here's a basic setup for stacks to use during the game. //Morgana - switched it over to being an object and using dynamic amounts. -const stacks = { +let stacks = { a: generateRandomAmount(), b: generateRandomAmount(), c: generateRandomAmount(), }; -const players = []; +let players = []; //=========================================== //Sockets @@ -32,6 +32,10 @@ io.on('connect', (socket) => { console.log(`Socket ${socket.id} connected`); players.push(socket.id); + if(players.length === 1) { + io.to(`${players[0]}`).emit(events.message, 'Waiting for second player...'); + } + if(players.length === 2) { io.to(`${players[0]}`).emit(events.turn, stacks); } @@ -48,6 +52,11 @@ io.on('connect', (socket) => { io.to(`${players[0]}`).emit(events.turn, stacks); } }); + + socket.on('disconnect', () => { + console.log(`Socket ${socket.id} disconnected`); + players.splice(players.indexOf[socket.id], 1); + }); }); @@ -67,6 +76,12 @@ const gameCycle = (stackChoice, numberToTake) => { console.log('GAME OVER!!!!!!!'); io.to(`${players[0]}`).emit(events.gameOver, 'Game Over!'); io.to(`${players[1]}`).emit(events.gameOver, 'Game Over!'); + players = []; + stacks = { + a: generateRandomAmount(), + b: generateRandomAmount(), + c: generateRandomAmount(), + }; } }; From 98f9f59fe42e202e71d141ebe31b500c8e095828 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Tue, 11 Jun 2019 10:00:46 -0700 Subject: [PATCH 09/33] messages --- server.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server.js b/server.js index 606a042..cb9c71d 100644 --- a/server.js +++ b/server.js @@ -37,6 +37,8 @@ io.on('connect', (socket) => { } if(players.length === 2) { + io.to(`${players[0]}`).emit(events.message, 'Starting Game'); + io.to(`${players[1]}`).emit(events.message, 'Starting Game'); io.to(`${players[0]}`).emit(events.turn, stacks); } @@ -46,10 +48,13 @@ io.on('connect', (socket) => { if(playerWhoMoved === 0) { gameCycle(payload[0], payload[1]); io.to(`${players[1]}`).emit(events.turn, stacks); + io.to(`${players[0]}`).emit(events.message, 'Waiting for other player to move'); + } else if(playerWhoMoved === 1) { gameCycle(payload[0], payload[1]); io.to(`${players[0]}`).emit(events.turn, stacks); + io.to(`${players[1]}`).emit(events.message, 'Waiting for other player to move'); } }); From 9f50f6b5167d8ecbc5cafda4b60544effd82bc08 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Tue, 11 Jun 2019 10:01:08 -0700 Subject: [PATCH 10/33] messages --- server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server.js b/server.js index cb9c71d..5c463c5 100644 --- a/server.js +++ b/server.js @@ -39,6 +39,7 @@ io.on('connect', (socket) => { if(players.length === 2) { io.to(`${players[0]}`).emit(events.message, 'Starting Game'); io.to(`${players[1]}`).emit(events.message, 'Starting Game'); + io.to(`${players[1]}`).emit(events.message, 'Waiting for other player to move'); io.to(`${players[0]}`).emit(events.turn, stacks); } From 0d12ff5b58b522d3428052dade35501dc0766a09 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Tue, 11 Jun 2019 10:35:07 -0700 Subject: [PATCH 11/33] ends game when a player disconnects --- server.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server.js b/server.js index 5c463c5..d8e73d5 100644 --- a/server.js +++ b/server.js @@ -62,6 +62,10 @@ io.on('connect', (socket) => { socket.on('disconnect', () => { console.log(`Socket ${socket.id} disconnected`); players.splice(players.indexOf[socket.id], 1); + if(players.length === 1) { + io.to(`${players[0]}`).emit(events.message, 'Other player disconnected, ending game'); + io.to(`${players[0]}`).emit(events.gameOver, 'Game Over!'); + } }); }); From e96b573dd4ef4474246b1fa36ac1e98f798b4003 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Tue, 11 Jun 2019 12:08:38 -0700 Subject: [PATCH 12/33] reafactored to allow multiple games at once --- app.js | 19 ----------- game.js | 42 ++++++++++++++++++++++++ server.js | 98 +++++++++++++++++++++++-------------------------------- 3 files changed, 83 insertions(+), 76 deletions(-) delete mode 100644 app.js create mode 100644 game.js diff --git a/app.js b/app.js deleted file mode 100644 index d04ad04..0000000 --- a/app.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -const client = require('socket.io-client'); -const prompt = require('prompt'); - -const socket = client.connect('http://localhost:3000'); - -socket.emit('move'); - -socket.on('moved', (payload) => { - console.log('It\'s your turn'); - - prompt.start(); - - prompt.get(['move'], (err, data) => { - socket.emit('move', {move: data.move}); - console.log('You selected', data.move); - }); -}); \ No newline at end of file diff --git a/game.js b/game.js new file mode 100644 index 0000000..c424e17 --- /dev/null +++ b/game.js @@ -0,0 +1,42 @@ +'use strict'; + +class Game { + constructor(id) { + this.id = id; + // Chris - Here's a basic setup for stacks to use during the game. + //Morgana - switched it over to being an object and using dynamic amounts. + this.stacks = { + a: this.generateRandomAmount(), + b: this.generateRandomAmount(), + c: this.generateRandomAmount(), + }; + // Chris - a tally to keep track of how many items are left game-wide accross all stacks. + // When this is zero, game is over. + this.totalItemsRemaining; + } + + //Morgana - generates a random amount between 5 and 25 + generateRandomAmount() { + return Math.floor(Math.random() * 20) + 5; + } + + // Chris - this function tallies a new total for totalItemsRemaining after a player makes a valid move. + // Morgana - adjusted for new stack structure + _tallyTotalItemsRemaining() { + let total = 0; + for(let i in this.stacks) { + total += this.stacks[i]; + } + this.totalItemsRemaining = total; + } + + // Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. + // Morgana - adjusted for new stack structure + takeItemsFromStack (stackChoice, numberToTake) { + this.stacks[stackChoice] = this.stacks[stackChoice] - numberToTake; + this._tallyTotalItemsRemaining(); + } + +} + +module.exports = Game; \ No newline at end of file diff --git a/server.js b/server.js index d8e73d5..4f32dcc 100644 --- a/server.js +++ b/server.js @@ -3,6 +3,7 @@ require('dotenv').config(); const events = require('./events.js'); +const Game = require('./game.js'); const io = require('socket.io')(process.env.PORT); @@ -10,17 +11,7 @@ const io = require('socket.io')(process.env.PORT); //Global Variables //=========================================== -// Chris - a tally to keep track of how many items are left game-wide accross all stacks. -// When this is zero, game is over. -let totalItemsRemaining; - -// Chris - Here's a basic setup for stacks to use during the game. -//Morgana - switched it over to being an object and using dynamic amounts. -let stacks = { - a: generateRandomAmount(), - b: generateRandomAmount(), - c: generateRandomAmount(), -}; +let games = {}; let players = []; @@ -30,6 +21,7 @@ let players = []; io.on('connect', (socket) => { console.log(`Socket ${socket.id} connected`); + players.push(socket.id); if(players.length === 1) { @@ -37,29 +29,48 @@ io.on('connect', (socket) => { } if(players.length === 2) { - io.to(`${players[0]}`).emit(events.message, 'Starting Game'); - io.to(`${players[1]}`).emit(events.message, 'Starting Game'); - io.to(`${players[1]}`).emit(events.message, 'Waiting for other player to move'); - io.to(`${players[0]}`).emit(events.turn, stacks); + //Morgana - When there are two players, create a new game instance with an id + let gameInstance = new Game(Object.keys(games).length); + //Morgana - Set the players, and save the game to the games object + //Morgana - spread operator creates a shallow copy, avoiding reference issues + gameInstance.players = [...players]; + console.log(gameInstance); + games[gameInstance.id] = gameInstance; + console.log(games); + + //Morgana - send the initial game-start messages + io.to(`${gameInstance.players[0]}`).emit(events.message, 'Starting Game'); + io.to(`${gameInstance.players[1]}`).emit(events.message, 'Starting Game'); + io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); + io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); + + //Morgana - empty the players array + players = []; } socket.on(events.move, payload => { - let playerWhoMoved = players.indexOf(socket.id); + //retrive game id + const gameID = payload[0]; + const stackChoice = payload[1]; + const numberToTake = payload[2]; + const currentGame = games[gameID]; + let playerWhoMoved = currentGame.players.indexOf(socket.id); if(playerWhoMoved === 0) { - gameCycle(payload[0], payload[1]); - io.to(`${players[1]}`).emit(events.turn, stacks); - io.to(`${players[0]}`).emit(events.message, 'Waiting for other player to move'); + gameCycle(currentGame, stackChoice, numberToTake); + io.to(`${currentGame.players[1]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); + io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); } else if(playerWhoMoved === 1) { - gameCycle(payload[0], payload[1]); - io.to(`${players[0]}`).emit(events.turn, stacks); - io.to(`${players[1]}`).emit(events.message, 'Waiting for other player to move'); + gameCycle(currentGame, stackChoice, numberToTake); + io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); + io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); } }); socket.on('disconnect', () => { + //retrieve game id? console.log(`Socket ${socket.id} disconnected`); players.splice(players.indexOf[socket.id], 1); if(players.length === 1) { @@ -75,46 +86,19 @@ io.on('connect', (socket) => { //=========================================== //Morgana - refactored to handle new stack structure and gameover emits -const gameCycle = (stackChoice, numberToTake) => { - // if(checkChoices(stackChoice, numberToTake) === true) { - takeItemsFromStack(stackChoice, numberToTake); - // } - if(totalItemsRemaining !== 0) { - console.log({totalItemsRemaining}); +const gameCycle = (currentGame, stackChoice, numberToTake) => { + + currentGame.takeItemsFromStack(stackChoice, numberToTake); + + if(currentGame.totalItemsRemaining !== 0) { + console.log('Total remaining', currentGame.totalItemsRemaining); } else { console.log('GAME OVER!!!!!!!'); - io.to(`${players[0]}`).emit(events.gameOver, 'Game Over!'); - io.to(`${players[1]}`).emit(events.gameOver, 'Game Over!'); - players = []; - stacks = { - a: generateRandomAmount(), - b: generateRandomAmount(), - c: generateRandomAmount(), - }; - } -}; - -//Morgana - generates a random amount between 5 and 25 -function generateRandomAmount() { - return Math.floor(Math.random() * 20) + 5; -} - -// Chris - this function tallies a new total for totalItemsRemaining after a player makes a valid move. -// Morgana - adjusted for new stack structure -const _tallyTotalItemsRemaining = () => { - let total = 0; - for(let i in stacks) { - total += stacks[i]; + io.to(`${currentGame.players[0]}`).emit(events.gameOver, 'Game Over!'); + io.to(`${currentGame.players[1]}`).emit(events.gameOver, 'Game Over!'); } - totalItemsRemaining = total; }; -// Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. -// Morgana - adjusted for new stack structure -const takeItemsFromStack = (stackChoice, numberToTake) => { - stacks[stackChoice] = stacks[stackChoice] - numberToTake; - _tallyTotalItemsRemaining(); -}; From fa4bfe1e2deb0a8f438121c73a12ac76b22f1cb9 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Tue, 11 Jun 2019 15:28:36 -0700 Subject: [PATCH 13/33] mid-game disconnect handled --- server.js | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/server.js b/server.js index 4f32dcc..94e216f 100644 --- a/server.js +++ b/server.js @@ -21,11 +21,12 @@ let players = []; io.on('connect', (socket) => { console.log(`Socket ${socket.id} connected`); + socket.gameID = 'none yet'; - players.push(socket.id); + players.push(socket); if(players.length === 1) { - io.to(`${players[0]}`).emit(events.message, 'Waiting for second player...'); + io.to(`${players[0].id}`).emit(events.message, 'Waiting for second player...'); } if(players.length === 2) { @@ -33,23 +34,23 @@ io.on('connect', (socket) => { let gameInstance = new Game(Object.keys(games).length); //Morgana - Set the players, and save the game to the games object //Morgana - spread operator creates a shallow copy, avoiding reference issues - gameInstance.players = [...players]; - console.log(gameInstance); + gameInstance.players = [players[0].id, players[1].id]; games[gameInstance.id] = gameInstance; - console.log(games); - + players[0].gameID = gameInstance.id; + players[1].gameID = gameInstance.id; + //Morgana - send the initial game-start messages io.to(`${gameInstance.players[0]}`).emit(events.message, 'Starting Game'); io.to(`${gameInstance.players[1]}`).emit(events.message, 'Starting Game'); io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); - //Morgana - empty the players array + //Morgana - empty the players array once a game is initialized players = []; } socket.on(events.move, payload => { - //retrive game id + const gameID = payload[0]; const stackChoice = payload[1]; const numberToTake = payload[2]; @@ -70,13 +71,19 @@ io.on('connect', (socket) => { }); socket.on('disconnect', () => { - //retrieve game id? console.log(`Socket ${socket.id} disconnected`); - players.splice(players.indexOf[socket.id], 1); - if(players.length === 1) { - io.to(`${players[0]}`).emit(events.message, 'Other player disconnected, ending game'); - io.to(`${players[0]}`).emit(events.gameOver, 'Game Over!'); + + console.log('on disconnect', socket.gameID); + const gameToEnd = games[socket.gameID]; + + if(gameToEnd) { + io.to(`${gameToEnd.players[0]}`).emit(events.message, 'Player disconnected, ending game'); + io.to(`${gameToEnd.players[0]}`).emit(events.gameOver, 'Game Over!'); + io.to(`${gameToEnd.players[1]}`).emit(events.message, 'Player disconnected, ending game'); + io.to(`${gameToEnd.players[1]}`).emit(events.gameOver, 'Game Over!'); } + + if(players.includes(socket)) players.splice(players.indexOf(socket.id), 1); }); }); From 9f25d986e31e53af4b4962e9a90b1084651edf44 Mon Sep 17 00:00:00 2001 From: Chris Kozlowski Date: Tue, 11 Jun 2019 16:57:36 -0700 Subject: [PATCH 14/33] working on countdown timer, need refactor --- events.js | 1 + game.js | 22 + package-lock.json | 1281 +++++++++++++++++++++++++++++++++++++++++++++ server.js | 12 +- 4 files changed, 1314 insertions(+), 2 deletions(-) create mode 100644 package-lock.json diff --git a/events.js b/events.js index d4fd71c..3b6bf1b 100644 --- a/events.js +++ b/events.js @@ -5,5 +5,6 @@ module.exports = { turn: 'turn', gameOver: 'game over', message: 'message-from-server', + countdown: 'countdown', }; diff --git a/game.js b/game.js index c424e17..2080a27 100644 --- a/game.js +++ b/game.js @@ -10,9 +10,31 @@ class Game { b: this.generateRandomAmount(), c: this.generateRandomAmount(), }; + this.players = []; + this.timeLeft = 20; + this.countdown; // Chris - a tally to keep track of how many items are left game-wide accross all stacks. // When this is zero, game is over. this.totalItemsRemaining; + this.decrement = function(player) { + this.timeLeft--; + io.to(`${player}`).emit(events.message, this.timeLeft); + if (this.timeLeft === -1) { + console.log('Ran out of time!!'); + io.emit(events.message, 'Time\'s up!!'); + io.to(`${this.players[0]}`).emit(events.gameOver, 'Game Over!'); + io.to(`${this.players[1]}`).emit(events.gameOver, 'Game Over!'); + this.players = []; + this.stacks = { + a: this.generateRandomAmount(), + b: this.generateRandomAmount(), + c: this.generateRandomAmount(), + }; + clearInterval(this.countdown); + this.countdown = null; + this.timeLeft = 20; + } + } } //Morgana - generates a random amount between 5 and 25 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1929f12 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1281 @@ +{ + "name": "socket_server", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==" + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colors": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==" + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "deep-equal": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", + "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dotenv": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz", + "integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "engine.io": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz", + "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==", + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~6.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "engine.io-client": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz", + "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==", + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==" + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "requires": { + "flat-cache": "^2.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "i": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", + "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "import-fresh": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", + "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inquirer": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz", + "integrity": "sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "ncp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "pkginfo": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", + "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "prompt": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz", + "integrity": "sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4=", + "requires": { + "colors": "^1.1.2", + "pkginfo": "0.x.x", + "read": "1.0.x", + "revalidator": "0.1.x", + "utile": "0.3.x", + "winston": "2.1.x" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "~0.0.4" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "revalidator": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "socket.io": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz", + "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==", + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.3.1", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.2.0", + "socket.io-parser": "~3.3.0" + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" + }, + "socket.io-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz", + "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.3.1", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "socket.io-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", + "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.0.tgz", + "integrity": "sha512-nHFDrxmbrkU7JAFKqKbDJXfzrX2UBsWmrieXFTGxiI5e4ncg3VqsZeI4EzNmX0ncp4XNGVeoxIWJXfCIXwrsvw==", + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "utile": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/utile/-/utile-0.3.0.tgz", + "integrity": "sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo=", + "requires": { + "async": "~0.9.0", + "deep-equal": "~0.2.1", + "i": "0.3.x", + "mkdirp": "0.x.x", + "ncp": "1.0.x", + "rimraf": "2.x.x" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "winston": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", + "requires": { + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" + } + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "requires": { + "mkdirp": "^0.5.1" + } + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + } + } +} diff --git a/server.js b/server.js index 94e216f..230eb2f 100644 --- a/server.js +++ b/server.js @@ -44,28 +44,32 @@ io.on('connect', (socket) => { io.to(`${gameInstance.players[1]}`).emit(events.message, 'Starting Game'); io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); - + gameInstance.countdown = setInterval(gameInstance.decrement, 1000, gameInstance.players[0]); + console.log(gameInstance.countdown) //Morgana - empty the players array once a game is initialized players = []; } socket.on(events.move, payload => { - const gameID = payload[0]; const stackChoice = payload[1]; const numberToTake = payload[2]; const currentGame = games[gameID]; + clearInterval(currentGame.countdown); + currentGame.timeLeft = 20; let playerWhoMoved = currentGame.players.indexOf(socket.id); if(playerWhoMoved === 0) { gameCycle(currentGame, stackChoice, numberToTake); io.to(`${currentGame.players[1]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); + currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[1]); io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); } else if(playerWhoMoved === 1) { gameCycle(currentGame, stackChoice, numberToTake); io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); + currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[1]); io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); } }); @@ -81,6 +85,8 @@ io.on('connect', (socket) => { io.to(`${gameToEnd.players[0]}`).emit(events.gameOver, 'Game Over!'); io.to(`${gameToEnd.players[1]}`).emit(events.message, 'Player disconnected, ending game'); io.to(`${gameToEnd.players[1]}`).emit(events.gameOver, 'Game Over!'); + clearInterval(gameToEnd.countdown); + gameToEnd.timeLeft = 20; } if(players.includes(socket)) players.splice(players.indexOf(socket.id), 1); @@ -104,6 +110,8 @@ const gameCycle = (currentGame, stackChoice, numberToTake) => { console.log('GAME OVER!!!!!!!'); io.to(`${currentGame.players[0]}`).emit(events.gameOver, 'Game Over!'); io.to(`${currentGame.players[1]}`).emit(events.gameOver, 'Game Over!'); + clearInterval(currentGame.countdown); + currentGame.timeLeft = 20; } }; From c6747094252fd9c30fcbdcd5cb087873afe37567 Mon Sep 17 00:00:00 2001 From: Chris Kozlowski Date: Tue, 11 Jun 2019 17:57:48 -0700 Subject: [PATCH 15/33] got countdown timer functional --- game.js | 46 ++++++++++++++++++++++++++-------------------- server.js | 3 +-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/game.js b/game.js index 2080a27..0630cf9 100644 --- a/game.js +++ b/game.js @@ -1,7 +1,10 @@ 'use strict'; +const events = require('./events.js'); + class Game { - constructor(id) { + constructor(id, io) { + this.io = io; this.id = id; // Chris - Here's a basic setup for stacks to use during the game. //Morgana - switched it over to being an object and using dynamic amounts. @@ -16,27 +19,30 @@ class Game { // Chris - a tally to keep track of how many items are left game-wide accross all stacks. // When this is zero, game is over. this.totalItemsRemaining; - this.decrement = function(player) { - this.timeLeft--; - io.to(`${player}`).emit(events.message, this.timeLeft); - if (this.timeLeft === -1) { - console.log('Ran out of time!!'); - io.emit(events.message, 'Time\'s up!!'); - io.to(`${this.players[0]}`).emit(events.gameOver, 'Game Over!'); - io.to(`${this.players[1]}`).emit(events.gameOver, 'Game Over!'); - this.players = []; - this.stacks = { - a: this.generateRandomAmount(), - b: this.generateRandomAmount(), - c: this.generateRandomAmount(), - }; - clearInterval(this.countdown); - this.countdown = null; - this.timeLeft = 20; - } + this.decrement = this.decrement.bind(this); + } + + decrement(player) { + console.log(this); + this.timeLeft = this.timeLeft - 1; + console.log(this.timeLeft); + this.io.to(`${player}`).emit(events.message, this.timeLeft); + if (this.timeLeft === -1) { + console.log('Ran out of time!!'); + this.io.emit(events.message, 'Time\'s up!!'); + this.io.to(`${this.players[0]}`).emit(events.gameOver, 'Game Over!'); + this.io.to(`${this.players[1]}`).emit(events.gameOver, 'Game Over!'); + this.players = []; + this.stacks = { + a: this.generateRandomAmount(), + b: this.generateRandomAmount(), + c: this.generateRandomAmount(), + }; + clearInterval(this.countdown); + this.countdown = null; + this.timeLeft = 20; } } - //Morgana - generates a random amount between 5 and 25 generateRandomAmount() { return Math.floor(Math.random() * 20) + 5; diff --git a/server.js b/server.js index 230eb2f..168121f 100644 --- a/server.js +++ b/server.js @@ -31,7 +31,7 @@ io.on('connect', (socket) => { if(players.length === 2) { //Morgana - When there are two players, create a new game instance with an id - let gameInstance = new Game(Object.keys(games).length); + let gameInstance = new Game(Object.keys(games).length, io); //Morgana - Set the players, and save the game to the games object //Morgana - spread operator creates a shallow copy, avoiding reference issues gameInstance.players = [players[0].id, players[1].id]; @@ -45,7 +45,6 @@ io.on('connect', (socket) => { io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); gameInstance.countdown = setInterval(gameInstance.decrement, 1000, gameInstance.players[0]); - console.log(gameInstance.countdown) //Morgana - empty the players array once a game is initialized players = []; } From a5c558258b1878c66f7bda1ba0df69288133ad8a Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Wed, 12 Jun 2019 08:03:05 -0700 Subject: [PATCH 16/33] sends a win event --- events.js | 1 + server.js | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/events.js b/events.js index d4fd71c..5aac1de 100644 --- a/events.js +++ b/events.js @@ -5,5 +5,6 @@ module.exports = { turn: 'turn', gameOver: 'game over', message: 'message-from-server', + win: 'win', }; diff --git a/server.js b/server.js index 94e216f..0c72b7f 100644 --- a/server.js +++ b/server.js @@ -58,13 +58,13 @@ io.on('connect', (socket) => { let playerWhoMoved = currentGame.players.indexOf(socket.id); if(playerWhoMoved === 0) { - gameCycle(currentGame, stackChoice, numberToTake); + gameCycle(currentGame, stackChoice, numberToTake, socket.id); io.to(`${currentGame.players[1]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); } else if(playerWhoMoved === 1) { - gameCycle(currentGame, stackChoice, numberToTake); + gameCycle(currentGame, stackChoice, numberToTake, socket.id); io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); } @@ -93,7 +93,7 @@ io.on('connect', (socket) => { //=========================================== //Morgana - refactored to handle new stack structure and gameover emits -const gameCycle = (currentGame, stackChoice, numberToTake) => { +const gameCycle = (currentGame, stackChoice, numberToTake, socketID) => { currentGame.takeItemsFromStack(stackChoice, numberToTake); @@ -102,6 +102,8 @@ const gameCycle = (currentGame, stackChoice, numberToTake) => { } else { console.log('GAME OVER!!!!!!!'); + //Morgana - pass win back to client + io.to(`${socketID}`).emit(events.win); io.to(`${currentGame.players[0]}`).emit(events.gameOver, 'Game Over!'); io.to(`${currentGame.players[1]}`).emit(events.gameOver, 'Game Over!'); } From c431c5c495f309fa3952badaee6647ee3d222af3 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Wed, 12 Jun 2019 10:02:21 -0700 Subject: [PATCH 17/33] sending win to correct person --- server.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 0c72b7f..fa0928e 100644 --- a/server.js +++ b/server.js @@ -58,13 +58,15 @@ io.on('connect', (socket) => { let playerWhoMoved = currentGame.players.indexOf(socket.id); if(playerWhoMoved === 0) { - gameCycle(currentGame, stackChoice, numberToTake, socket.id); + const otherPlayer = currentGame.players[1]; + gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); io.to(`${currentGame.players[1]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); } else if(playerWhoMoved === 1) { - gameCycle(currentGame, stackChoice, numberToTake, socket.id); + const otherPlayer = currentGame.players[0]; + gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); } From 4ac4ced6bda58ce35d11868df1c10ebe7bf78fd2 Mon Sep 17 00:00:00 2001 From: Chris Kozlowski Date: Wed, 12 Jun 2019 12:59:59 -0700 Subject: [PATCH 18/33] refactor for new countdown timer --- game.js | 2 +- server.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/game.js b/game.js index 0630cf9..f13bd32 100644 --- a/game.js +++ b/game.js @@ -26,7 +26,7 @@ class Game { console.log(this); this.timeLeft = this.timeLeft - 1; console.log(this.timeLeft); - this.io.to(`${player}`).emit(events.message, this.timeLeft); + this.io.to(`${player}`).emit(events.countdown, this.timeLeft); if (this.timeLeft === -1) { console.log('Ran out of time!!'); this.io.emit(events.message, 'Time\'s up!!'); diff --git a/server.js b/server.js index 168121f..facdde3 100644 --- a/server.js +++ b/server.js @@ -44,6 +44,7 @@ io.on('connect', (socket) => { io.to(`${gameInstance.players[1]}`).emit(events.message, 'Starting Game'); io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); + io.to(`${gameInstance.players[1]}`).emit(events.message, gameInstance.stacks); gameInstance.countdown = setInterval(gameInstance.decrement, 1000, gameInstance.players[0]); //Morgana - empty the players array once a game is initialized players = []; @@ -68,7 +69,7 @@ io.on('connect', (socket) => { else if(playerWhoMoved === 1) { gameCycle(currentGame, stackChoice, numberToTake); io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); - currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[1]); + currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[0]); io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); } }); From de69fc34ae20fe5f0ee3fc3e9415263bd00dc0df Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Wed, 12 Jun 2019 13:38:15 -0700 Subject: [PATCH 19/33] fixed server side timer --- server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server.js b/server.js index 78d15ad..7fe4ab9 100644 --- a/server.js +++ b/server.js @@ -44,7 +44,6 @@ io.on('connect', (socket) => { io.to(`${gameInstance.players[1]}`).emit(events.message, 'Starting Game'); io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); - io.to(`${gameInstance.players[1]}`).emit(events.message, gameInstance.stacks); gameInstance.countdown = setInterval(gameInstance.decrement, 1000, gameInstance.players[0]); //Morgana - empty the players array once a game is initialized players = []; From 97b1688d3f16251997c56e16831689595bd0be05 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Wed, 12 Jun 2019 15:05:08 -0700 Subject: [PATCH 20/33] game class tests --- __tests__/game.test.js | 88 ++++++++++++++++++++++++++++++++++++++++ __tests__/server.test.js | 7 ++++ game.js | 14 ++----- package.json | 6 ++- 4 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 __tests__/game.test.js create mode 100644 __tests__/server.test.js diff --git a/__tests__/game.test.js b/__tests__/game.test.js new file mode 100644 index 0000000..4598420 --- /dev/null +++ b/__tests__/game.test.js @@ -0,0 +1,88 @@ +'use strict'; + +const Game = require('../game.js'); + +describe( 'Game Class', () => { + + describe('instantiating', () => { + it('can instantiate with an id', () => { + const gameWithId = new Game(7); + expect(gameWithId.id).toBe(7); + }); + + it('if no id passed in, id equals null', () => { + const emptyGame = new Game(); + expect(emptyGame.id).toBeNull(); + }); + + }); + + describe('starting properties', () => { + const testGame = new Game(42); + + it('should have stacks', () => { + expect(testGame.stacks).toBeDefined(); + expect(testGame.stacks.a).toBeDefined(); + expect(Object.keys(testGame.stacks)).toEqual(expect.arrayContaining(['a', 'b', 'c'])); + expect(typeof testGame.stacks.a).toBe('number'); + }); + + it('can add players', () => { + expect(testGame.players).toBeDefined(); + + testGame.players.push('fred'); + testGame.players.push('george'); + + expect(testGame.players[0]).toBe('fred'); + expect(testGame.players[1]).toBe('george'); + }); + + it('has a turn time limit', () => { + expect(testGame.timeLeft).toBeDefined(); + expect(typeof testGame.timeLeft).toBe('number'); + expect(testGame.timeLeft > 0).toBeTruthy(); + expect(testGame.countdown).toBeDefined(); + }); + + it('has a property to store the total number of things in the stacks', () => { + expect(testGame.totalItemsRemaining).toBeDefined(); + expect(typeof testGame.totalItemsRemaining).toBe('number'); + }); + + it('has a property to store reference to the server socket', () => { + expect(testGame.io).toBeDefined(); + }); + }); + + describe('Methods', () => { + const testGame = new Game(12); + + it('can randomly generate amounts for the stacks', () => { + expect(typeof testGame.generateRandomAmount()).toBe('number'); + expect(testGame.generateRandomAmount() >= 0).toBeTruthy(); + }); + + it('can tally the total items in the stacks', () => { + const a = testGame.stacks.a; + const b = testGame.stacks.b; + const c = testGame.stacks.c; + const total = a + b + c; + + testGame._tallyTotalItemsRemaining(); + expect(testGame.totalItemsRemaining).toEqual(total); + }); + + it('can remove a given amount from a given stack', () => { + const startingValue = testGame.stacks.a; + testGame.takeItemsFromStack('a', 4); + expect(testGame.stacks.a).toEqual(startingValue - 4); + }); + + // it('can decrement the time left', () => { + // const startingTime = testGame.timeLeft; + // testGame.players.push('mock player'); + // testGame.decrement('mock player'); + // expect(testGame.timeLeft).toEqual(startingTime - 1); + // }); + }); +}); \ No newline at end of file diff --git a/__tests__/server.test.js b/__tests__/server.test.js new file mode 100644 index 0000000..bd27cf9 --- /dev/null +++ b/__tests__/server.test.js @@ -0,0 +1,7 @@ +'use strict'; + +describe( 'Socket Server', () => { + it( '', () => { + + }); +}); \ No newline at end of file diff --git a/game.js b/game.js index f13bd32..3ed1515 100644 --- a/game.js +++ b/game.js @@ -4,8 +4,8 @@ const events = require('./events.js'); class Game { constructor(id, io) { - this.io = io; - this.id = id; + this.io = io || null; + this.id = id || null; // Chris - Here's a basic setup for stacks to use during the game. //Morgana - switched it over to being an object and using dynamic amounts. this.stacks = { @@ -15,10 +15,10 @@ class Game { }; this.players = []; this.timeLeft = 20; - this.countdown; + this.countdown = null; // Chris - a tally to keep track of how many items are left game-wide accross all stacks. // When this is zero, game is over. - this.totalItemsRemaining; + this.totalItemsRemaining = 0; this.decrement = this.decrement.bind(this); } @@ -32,12 +32,6 @@ class Game { this.io.emit(events.message, 'Time\'s up!!'); this.io.to(`${this.players[0]}`).emit(events.gameOver, 'Game Over!'); this.io.to(`${this.players[1]}`).emit(events.gameOver, 'Game Over!'); - this.players = []; - this.stacks = { - a: this.generateRandomAmount(), - b: this.generateRandomAmount(), - c: this.generateRandomAmount(), - }; clearInterval(this.countdown); this.countdown = null; this.timeLeft = 20; diff --git a/package.json b/package.json index f8fbd0c..4d868d2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "server.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "jest --verbose --coverage", "start": "node server.js" }, "repository": { @@ -21,8 +21,10 @@ "dependencies": { "dotenv": "^8.0.0", "eslint": "^5.16.0", + "jest": "^24.8.0", "prompt": "^1.0.0", "socket.io": "^2.2.0", - "socket.io-client": "^2.2.0" + "socket.io-client": "^2.2.0", + "socket.io-mock": "^1.2.3" } } From bbb9472e6876d3b483ac35feaba582d4eb4af155 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Wed, 12 Jun 2019 17:42:27 -0700 Subject: [PATCH 21/33] mid-way through testing --- __tests__/server.test.js | 13 ++++++-- game.js | 67 ++++++++++++++++++++++++++++++++++------ lib/emit_wrapper.js | 7 +++++ package.json | 3 +- server.js | 20 +++++++----- 5 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 lib/emit_wrapper.js diff --git a/__tests__/server.test.js b/__tests__/server.test.js index bd27cf9..33c3cb4 100644 --- a/__tests__/server.test.js +++ b/__tests__/server.test.js @@ -1,7 +1,14 @@ 'use strict'; -describe( 'Socket Server', () => { - it( '', () => { - +const emitWrapper = require('../lib/emit_wrapper.js'); + +describe( 'Emit Wrapper', () => { + it( 'When passed an event, a payload, and a callback, it runs the callback with the event and payload', () => { + const event = 12; + const payload = 3; + function callback (event, payload) { + return event + payload; + } + expect(emitWrapper(event, payload, callback)).toEqual(15); }); }); \ No newline at end of file diff --git a/game.js b/game.js index 3ed1515..02dddef 100644 --- a/game.js +++ b/game.js @@ -1,8 +1,25 @@ 'use strict'; -const events = require('./events.js'); +/** + * Game Class Module + * @module game + * @exports Game - Game class + */ + +const events = require('./events.js'); +const emitWrapper = require('./lib/emit_wrapper.js'); +/** + * Game Class + * @class Game + */ class Game { + /** + * Constructor + * @constructor + * @param {*} id - Game id + * @param {*} io - Socket server instance + */ constructor(id, io) { this.io = io || null; this.id = id || null; @@ -22,28 +39,52 @@ class Game { this.decrement = this.decrement.bind(this); } + /** + * Decrement + * @method decrement + * @param {*} player - Player socket id + * Decrements the timeLeft variable and emits an event to the player + * Emits game over events if the time funs out + * Triggered at some interval in the socket server + */ decrement(player) { - console.log(this); this.timeLeft = this.timeLeft - 1; console.log(this.timeLeft); - this.io.to(`${player}`).emit(events.countdown, this.timeLeft); + + emitWrapper(events.countdown, this.timeLeft, this.io.to(`${player}`).emit); + // this.io.to(`${player}`).emit(events.countdown, this.timeLeft); if (this.timeLeft === -1) { console.log('Ran out of time!!'); - this.io.emit(events.message, 'Time\'s up!!'); - this.io.to(`${this.players[0]}`).emit(events.gameOver, 'Game Over!'); - this.io.to(`${this.players[1]}`).emit(events.gameOver, 'Game Over!'); + + emitWrapper(events.message, 'Time\'s up!!', this.io.emit); + // this.io.emit(events.message, 'Time\'s up!!'); + + emitWrapper(events.gameOver, 'Game Over!', this.io.to(`${this.players[0]}`).emit); + // this.io.to(`${this.players[0]}`).emit(events.gameOver, 'Game Over!'); + + emitWrapper(events.gameOver, 'Game Over!', this.io.to(`${this.players[1]}`).emit); + // this.io.to(`${this.players[1]}`).emit(events.gameOver, 'Game Over!'); clearInterval(this.countdown); this.countdown = null; this.timeLeft = 20; } } - //Morgana - generates a random amount between 5 and 25 + + /** + * Generate Random Ammount + * @method generateRandomAmount + * Generates a random number between 5 and 25 + */ generateRandomAmount() { return Math.floor(Math.random() * 20) + 5; } - // Chris - this function tallies a new total for totalItemsRemaining after a player makes a valid move. - // Morgana - adjusted for new stack structure + /** + * Tally Remaining + * @method _tallyTotalItemsRemaining + * Tallies the total amount in all stacks + * Updates the totalItemsRemaining property + */ _tallyTotalItemsRemaining() { let total = 0; for(let i in this.stacks) { @@ -53,7 +94,13 @@ class Game { } // Chris - This applies the player's move to the stack they selected, and updates totalRemainingItems. - // Morgana - adjusted for new stack structure + /** + * Take from Stack + * @method takeItemsFromStack + * @param {String} stackChoice - Stack to remove items from + * @param {Number} numberToTake - Amount to remove + * Given a stack name and an amount, removes the amount from the stack and runs the _tallyTotalRemaining method + */ takeItemsFromStack (stackChoice, numberToTake) { this.stacks[stackChoice] = this.stacks[stackChoice] - numberToTake; this._tallyTotalItemsRemaining(); diff --git a/lib/emit_wrapper.js b/lib/emit_wrapper.js new file mode 100644 index 0000000..2c70e53 --- /dev/null +++ b/lib/emit_wrapper.js @@ -0,0 +1,7 @@ +'use strict'; + +function emitWrapper(event, payload, callback) { + return callback(event, payload); +} + +module.exports = emitWrapper; \ No newline at end of file diff --git a/package.json b/package.json index 4d868d2..04c5abc 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "jest": "^24.8.0", "prompt": "^1.0.0", "socket.io": "^2.2.0", - "socket.io-client": "^2.2.0", - "socket.io-mock": "^1.2.3" + "socket.io-client": "^2.2.0" } } diff --git a/server.js b/server.js index 7fe4ab9..006aec1 100644 --- a/server.js +++ b/server.js @@ -5,6 +5,8 @@ require('dotenv').config(); const events = require('./events.js'); const Game = require('./game.js'); +const emitWrapper = require('./lib/emit_wrapper.js'); + const io = require('socket.io')(process.env.PORT); //=========================================== @@ -12,21 +14,23 @@ const io = require('socket.io')(process.env.PORT); //=========================================== let games = {}; - let players = []; //=========================================== -//Sockets +//Socket Functionality //=========================================== -io.on('connect', (socket) => { +io.on('connect', playerConnect); + +function playerConnect(socket) { + console.log(this); console.log(`Socket ${socket.id} connected`); socket.gameID = 'none yet'; players.push(socket); if(players.length === 1) { - io.to(`${players[0].id}`).emit(events.message, 'Waiting for second player...'); + emitWrapper(events.message, 'Waiting for second player...', io.to(`${players[0].id}`).emit.bind(this)); } if(players.length === 2) { @@ -41,6 +45,9 @@ io.on('connect', (socket) => { //Morgana - send the initial game-start messages io.to(`${gameInstance.players[0]}`).emit(events.message, 'Starting Game'); + + + io.to(`${gameInstance.players[1]}`).emit(events.message, 'Starting Game'); io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); @@ -92,7 +99,7 @@ io.on('connect', (socket) => { if(players.includes(socket)) players.splice(players.indexOf(socket.id), 1); }); -}); +} //=========================================== @@ -117,6 +124,3 @@ const gameCycle = (currentGame, stackChoice, numberToTake, socketID) => { currentGame.timeLeft = 20; } }; - - - From 6731cfc0c2712d09d872002e090df243affa65e4 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Thu, 13 Jun 2019 08:45:53 -0700 Subject: [PATCH 22/33] complete refactor, now modular --- game.js | 19 ++----- lib/connection_wrapper.js | 48 ++++++++++++++++ lib/disconnect_wrapper.js | 24 ++++++++ lib/emit_wrapper.js | 9 ++- lib/game_cycle.js | 24 ++++++++ lib/move_wrapper.js | 35 ++++++++++++ server.js | 117 ++++---------------------------------- 7 files changed, 156 insertions(+), 120 deletions(-) create mode 100644 lib/connection_wrapper.js create mode 100644 lib/disconnect_wrapper.js create mode 100644 lib/game_cycle.js create mode 100644 lib/move_wrapper.js diff --git a/game.js b/game.js index 02dddef..e571c38 100644 --- a/game.js +++ b/game.js @@ -21,8 +21,8 @@ class Game { * @param {*} io - Socket server instance */ constructor(id, io) { - this.io = io || null; - this.id = id || null; + this.io = io; + this.id = id; // Chris - Here's a basic setup for stacks to use during the game. //Morgana - switched it over to being an object and using dynamic amounts. this.stacks = { @@ -51,19 +51,12 @@ class Game { this.timeLeft = this.timeLeft - 1; console.log(this.timeLeft); - emitWrapper(events.countdown, this.timeLeft, this.io.to(`${player}`).emit); - // this.io.to(`${player}`).emit(events.countdown, this.timeLeft); + emitWrapper(this.io, events.countdown, this.timeLeft, `${player}`); if (this.timeLeft === -1) { console.log('Ran out of time!!'); - - emitWrapper(events.message, 'Time\'s up!!', this.io.emit); - // this.io.emit(events.message, 'Time\'s up!!'); - - emitWrapper(events.gameOver, 'Game Over!', this.io.to(`${this.players[0]}`).emit); - // this.io.to(`${this.players[0]}`).emit(events.gameOver, 'Game Over!'); - - emitWrapper(events.gameOver, 'Game Over!', this.io.to(`${this.players[1]}`).emit); - // this.io.to(`${this.players[1]}`).emit(events.gameOver, 'Game Over!'); + emitWrapper(this.io, events.message, 'Time\'s up!!'); + emitWrapper(this.io, events.gameOver, 'Game Over!', `${this.players[0]}`); + emitWrapper(this.io, events.gameOver, 'Game Over!', `${this.players[1]}`); clearInterval(this.countdown); this.countdown = null; this.timeLeft = 20; diff --git a/lib/connection_wrapper.js b/lib/connection_wrapper.js new file mode 100644 index 0000000..bf0f4f0 --- /dev/null +++ b/lib/connection_wrapper.js @@ -0,0 +1,48 @@ +'use strict'; + +const events = require('../events.js'); +const Game = require('../game.js'); +const emitWrapper = require('./emit_wrapper.js'); + +let players = []; +const games = {}; + +function curryIo(io) { + return function connectionWrapper(socket) { + + socket.gameID = 'none yet'; + + players.push(socket); + + if(players.length === 1) { + emitWrapper(io, events.message, 'Waiting for second player...', `${players[0].id}`); + } + + if(players.length === 2) { + emitWrapper(io, events.message, 'In too', `${players[1].id}`); + + const gameID = Object.keys(games, io).length; + players[0].gameID = gameID; + players[1].gameID = gameID; + + let gameInstance = new Game(gameID, io); + gameInstance.players = [players[0].id, players[1].id]; + + //Morgana - send the initial game-start messages + emitWrapper(io, events.message, 'Starting Game', `${gameInstance.players[0]}`); + emitWrapper(io, events.turn, [gameInstance.id, gameInstance.stacks], `${gameInstance.players[0]}`); + + emitWrapper(io, events.message, 'Starting Game', `${gameInstance.players[1]}`); + emitWrapper(io, events.message, 'Waiting for other player to move', `${gameInstance.players[1]}`); + + gameInstance.countdown = setInterval(gameInstance.decrement, 1000, gameInstance.players[0]); + + games[gameID] = gameInstance; + players = []; + } + + return games; + }; +} + +module.exports = curryIo; \ No newline at end of file diff --git a/lib/disconnect_wrapper.js b/lib/disconnect_wrapper.js new file mode 100644 index 0000000..f62c7c8 --- /dev/null +++ b/lib/disconnect_wrapper.js @@ -0,0 +1,24 @@ +'use strict'; + +const emitWrapper = require('./emit_wrapper.js'); +const events = require('../events.js'); + +function curryIo(io) { + return function disconnectWrapper(socket, games) { + console.log(`Socket ${socket.id} disconnected`); + + console.log('on disconnect', socket.gameID); + const gameToEnd = games[socket.gameID]; + + if(gameToEnd) { + emitWrapper(io, events.message, 'Player disconnected, ending game', `${gameToEnd.players[0]}`); + emitWrapper(io, events.gameOver, 'Game Over!', `${gameToEnd.players[0]}`); + emitWrapper(io, events.message, 'Player disconnected, ending game', `${gameToEnd.players[1]}`); + emitWrapper(io, events.gameOver, 'Game Over!', `${gameToEnd.players[1]}`); + clearInterval(gameToEnd.countdown); + gameToEnd.timeLeft = 20; + } + }; +} + +module.exports = curryIo; \ No newline at end of file diff --git a/lib/emit_wrapper.js b/lib/emit_wrapper.js index 2c70e53..8177f39 100644 --- a/lib/emit_wrapper.js +++ b/lib/emit_wrapper.js @@ -1,7 +1,12 @@ 'use strict'; -function emitWrapper(event, payload, callback) { - return callback(event, payload); +function emitWrapper(io, event, payload, target){ + if(!target) { + return io.emit(event, payload); + } + else { + return io.to(target).emit(event, payload); + } } module.exports = emitWrapper; \ No newline at end of file diff --git a/lib/game_cycle.js b/lib/game_cycle.js new file mode 100644 index 0000000..4685299 --- /dev/null +++ b/lib/game_cycle.js @@ -0,0 +1,24 @@ +'use strict'; + +const emitWrapper = require('./emit_wrapper.js'); +const events = require('../events.js'); + +function gameCycle (io, currentGame, stackChoice, numberToTake, socketID) { + + currentGame.takeItemsFromStack(stackChoice, numberToTake); + + if(currentGame.totalItemsRemaining !== 0) { + console.log('Total remaining', currentGame.totalItemsRemaining); + } + else { + console.log('GAME OVER!!!!!!!'); + emitWrapper(io, events.win, '', `${socketID}`); + emitWrapper(io, events.gameOver, 'Game Over!', `${currentGame.players[0]}`); + emitWrapper(io, events.gameOver, 'Game Over!', `${currentGame.players[1]}`); + + clearInterval(currentGame.countdown); + currentGame.timeLeft = 20; + } +} + +module.exports = gameCycle; \ No newline at end of file diff --git a/lib/move_wrapper.js b/lib/move_wrapper.js new file mode 100644 index 0000000..f2d3234 --- /dev/null +++ b/lib/move_wrapper.js @@ -0,0 +1,35 @@ +'use strict'; + +const emitWrapper = require('./emit_wrapper.js'); +const events = require('../events.js'); +const gameCycle = require('./game_cycle.js'); + +function curryIo(io) { + return function moveWrapper(socket, games, payload) { + + const gameID = payload[0]; + const stackChoice = payload[1]; + const numberToTake = payload[2]; + const currentGame = games[gameID]; + clearInterval(currentGame.countdown); + currentGame.timeLeft = 20; + let playerWhoMoved = currentGame.players.indexOf(socket.id); + + if(playerWhoMoved === 0) { + const otherPlayer = currentGame.players[1]; + gameCycle(io, currentGame, stackChoice, numberToTake, otherPlayer); + emitWrapper(io, events.turn, [currentGame.id, currentGame.stacks], `${currentGame.players[1]}`); + currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[1]); + emitWrapper(io, events.message, 'Waiting for other player to move', `${currentGame.players[0]}`); + } + else if(playerWhoMoved === 1) { + const otherPlayer = currentGame.players[0]; + gameCycle(io, currentGame, stackChoice, numberToTake, otherPlayer); + emitWrapper(io, events.turn, [currentGame.id, currentGame.stacks], `${currentGame.players[0]}`); + currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[0]); + emitWrapper(io, events.message, 'Waiting for other player to move', `${currentGame.players[1]}`); + } + }; +} + +module.exports = curryIo; \ No newline at end of file diff --git a/server.js b/server.js index 006aec1..cae7a70 100644 --- a/server.js +++ b/server.js @@ -2,125 +2,32 @@ require('dotenv').config(); -const events = require('./events.js'); -const Game = require('./game.js'); +const io = require('socket.io')(process.env.PORT); -const emitWrapper = require('./lib/emit_wrapper.js'); +const connectionWrapper = require('./lib/connection_wrapper.js')(io); +const moveWrapper = require('./lib/move_wrapper.js')(io); +const disconnectWrapper = require('./lib/disconnect_wrapper.js')(io); -const io = require('socket.io')(process.env.PORT); +const events = require('./events.js'); -//=========================================== -//Global Variables -//=========================================== +// const emitWrapper = require('./lib/emit_wrapper.js'); let games = {}; -let players = []; -//=========================================== -//Socket Functionality -//=========================================== +io.on('connect', connectionContainer); -io.on('connect', playerConnect); -function playerConnect(socket) { - console.log(this); +function connectionContainer(socket) { console.log(`Socket ${socket.id} connected`); - socket.gameID = 'none yet'; - - players.push(socket); - - if(players.length === 1) { - emitWrapper(events.message, 'Waiting for second player...', io.to(`${players[0].id}`).emit.bind(this)); - } - - if(players.length === 2) { - //Morgana - When there are two players, create a new game instance with an id - let gameInstance = new Game(Object.keys(games).length, io); - //Morgana - Set the players, and save the game to the games object - //Morgana - spread operator creates a shallow copy, avoiding reference issues - gameInstance.players = [players[0].id, players[1].id]; - games[gameInstance.id] = gameInstance; - players[0].gameID = gameInstance.id; - players[1].gameID = gameInstance.id; - - //Morgana - send the initial game-start messages - io.to(`${gameInstance.players[0]}`).emit(events.message, 'Starting Game'); - - - io.to(`${gameInstance.players[1]}`).emit(events.message, 'Starting Game'); - io.to(`${gameInstance.players[1]}`).emit(events.message, 'Waiting for other player to move'); - io.to(`${gameInstance.players[0]}`).emit(events.turn, [gameInstance.id, gameInstance.stacks]); - gameInstance.countdown = setInterval(gameInstance.decrement, 1000, gameInstance.players[0]); - //Morgana - empty the players array once a game is initialized - players = []; - } + games = connectionWrapper(socket); socket.on(events.move, payload => { - const gameID = payload[0]; - const stackChoice = payload[1]; - const numberToTake = payload[2]; - const currentGame = games[gameID]; - clearInterval(currentGame.countdown); - currentGame.timeLeft = 20; - let playerWhoMoved = currentGame.players.indexOf(socket.id); - - if(playerWhoMoved === 0) { - const otherPlayer = currentGame.players[1]; - gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); - io.to(`${currentGame.players[1]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); - currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[1]); - io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); - - } - else if(playerWhoMoved === 1) { - const otherPlayer = currentGame.players[0]; - gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); - io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); - currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[0]); - io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); - } + moveWrapper(socket, games, payload); }); socket.on('disconnect', () => { - console.log(`Socket ${socket.id} disconnected`); - - console.log('on disconnect', socket.gameID); - const gameToEnd = games[socket.gameID]; - - if(gameToEnd) { - io.to(`${gameToEnd.players[0]}`).emit(events.message, 'Player disconnected, ending game'); - io.to(`${gameToEnd.players[0]}`).emit(events.gameOver, 'Game Over!'); - io.to(`${gameToEnd.players[1]}`).emit(events.message, 'Player disconnected, ending game'); - io.to(`${gameToEnd.players[1]}`).emit(events.gameOver, 'Game Over!'); - clearInterval(gameToEnd.countdown); - gameToEnd.timeLeft = 20; - } - - if(players.includes(socket)) players.splice(players.indexOf(socket.id), 1); + disconnectWrapper(socket, games); }); -} - - -//=========================================== -//Server Side Game Logic -//=========================================== - -//Morgana - refactored to handle new stack structure and gameover emits -const gameCycle = (currentGame, stackChoice, numberToTake, socketID) => { - - currentGame.takeItemsFromStack(stackChoice, numberToTake); - if(currentGame.totalItemsRemaining !== 0) { - console.log('Total remaining', currentGame.totalItemsRemaining); - } - else { - console.log('GAME OVER!!!!!!!'); - //Morgana - pass win back to client - io.to(`${socketID}`).emit(events.win); - io.to(`${currentGame.players[0]}`).emit(events.gameOver, 'Game Over!'); - io.to(`${currentGame.players[1]}`).emit(events.gameOver, 'Game Over!'); - clearInterval(currentGame.countdown); - currentGame.timeLeft = 20; - } -}; +} \ No newline at end of file From 33f328fb4f72a227a05dca0a969793c27ede0b53 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Thu, 13 Jun 2019 10:36:04 -0700 Subject: [PATCH 23/33] tests complete --- __tests__/game.test.js | 54 +++++++++++++------- __tests__/server.test.js | 101 +++++++++++++++++++++++++++++++++++--- game.js | 2 +- lib/connection_wrapper.js | 5 +- lib/disconnect_wrapper.js | 1 + server.js | 5 +- 6 files changed, 137 insertions(+), 31 deletions(-) diff --git a/__tests__/game.test.js b/__tests__/game.test.js index 4598420..08cfb2a 100644 --- a/__tests__/game.test.js +++ b/__tests__/game.test.js @@ -2,19 +2,37 @@ const Game = require('../game.js'); +class MockIo { + constructor() { + + } + to(target) { + this.target = target; + return this; + } + emit(event, payload) { + this.event = event; + this.payload = payload; + return this; + } +} + +const testIo = new MockIo; + describe( 'Game Class', () => { describe('instantiating', () => { - it('can instantiate with an id', () => { - const gameWithId = new Game(7); + it('can instantiate with an id and io', () => { + const gameWithId = new Game(7, testIo); expect(gameWithId.id).toBe(7); + expect(gameWithId.io).toBeDefined; }); - - it('if no id passed in, id equals null', () => { + + it('instantiating without id and io results in undefineds', () => { const emptyGame = new Game(); - expect(emptyGame.id).toBeNull(); + expect(emptyGame.id).not.toBeDefined(); + expect(emptyGame.io).not.toBeDefined(); }); - }); describe('starting properties', () => { @@ -48,14 +66,10 @@ describe( 'Game Class', () => { expect(testGame.totalItemsRemaining).toBeDefined(); expect(typeof testGame.totalItemsRemaining).toBe('number'); }); - - it('has a property to store reference to the server socket', () => { - expect(testGame.io).toBeDefined(); - }); }); describe('Methods', () => { - const testGame = new Game(12); + const testGame = new Game(12, testIo); it('can randomly generate amounts for the stacks', () => { expect(typeof testGame.generateRandomAmount()).toBe('number'); @@ -78,11 +92,17 @@ describe( 'Game Class', () => { expect(testGame.stacks.a).toEqual(startingValue - 4); }); - // it('can decrement the time left', () => { - // const startingTime = testGame.timeLeft; - // testGame.players.push('mock player'); - // testGame.decrement('mock player'); - // expect(testGame.timeLeft).toEqual(startingTime - 1); - // }); + it('can decrement the time left', () => { + const startingTime = testGame.timeLeft; + testGame.players.push('mock player'); + testGame.decrement('mock player'); + expect(testGame.timeLeft).toEqual(startingTime - 1); + }); + + it('if time runs out, it ends the game', () => { + testGame.timeLeft = 0; + testGame.decrement('mock player'); + expect(testGame.io.payload).toBe('Game Over!'); + }); }); }); \ No newline at end of file diff --git a/__tests__/server.test.js b/__tests__/server.test.js index 33c3cb4..ee38e7b 100644 --- a/__tests__/server.test.js +++ b/__tests__/server.test.js @@ -1,14 +1,99 @@ 'use strict'; const emitWrapper = require('../lib/emit_wrapper.js'); +const Game = require('../game.js'); -describe( 'Emit Wrapper', () => { - it( 'When passed an event, a payload, and a callback, it runs the callback with the event and payload', () => { - const event = 12; - const payload = 3; - function callback (event, payload) { - return event + payload; - } - expect(emitWrapper(event, payload, callback)).toEqual(15); +class MockIo { + constructor() { + + } + to(target) { + this.target = target; + return this; + } + emit(event, payload) { + this.event = event; + this.payload = payload; + return this; + } +} + +const mockSocket1 = { + id: 'Alice', + gameID: 0, +}; + +const mockSocket2 = { + id: 'Ted', + gameID: 0, +}; + +const testIo = new MockIo; + +let gameHolder = {}; + +describe( 'Event Emitter', () => { + const eventTestIo = new MockIo; + it( 'Wraps the emit event', () => { + emitWrapper(eventTestIo, 'move', [1,3], 'fred'); + }); + it('Does not require a target', () => { + emitWrapper(eventTestIo, 'move', [4,5]); + }); +}); + +describe('Game creation', () => { + const connectionWrapper = require('../lib/connection_wrapper.js')(testIo); + + it('When a single socket connects, it stores the socket and sends a wait message', () => { + const {games, players} = connectionWrapper(mockSocket1); + expect(players.length).toBe(1); + expect(players[0].id).toBe('Alice'); + expect(typeof testIo.payload).toBe('string'); + }); + + it('When a second socket connects, it creates a game, sets the countdown, and clears the players array (waitingroom)', () => { + const {games, players} = connectionWrapper(mockSocket2); + gameHolder = games; + + expect(Object.keys(games).length).toBe(1); + expect(games[0]).toBeDefined(); + expect(games[0]).toBeInstanceOf(Game); + expect(games[0].countdown).toBeDefined(); + expect(players).toStrictEqual([]); + }); +}); + +describe('On move', () => { + const moveWrapper = require('../lib/move_wrapper.js')(testIo); + it('Can correctly target the player/socket that \'moved\'', () => { + + moveWrapper(mockSocket1, gameHolder, [mockSocket1.gameID,'a',3]); + expect(testIo.target).toBe('Alice'); + expect(typeof testIo.payload).toBe('string'); + + moveWrapper(mockSocket2, gameHolder, [mockSocket1.gameID,'b',3]); + expect(testIo.target).toBe('Ted'); + expect(typeof testIo.payload).toBe('string'); + }); +}); + +describe('Game Cycle', () => { + const gameCycle = require('../lib/game_cycle.js'); + it('if the stacks are empty, the game ends', () => { + gameHolder[0].stacks['a'] = 1; + gameHolder[0].stacks['b'] = 0; + gameHolder[0].stacks['c'] = 0; + gameCycle(testIo, gameHolder[0], 'a', 1, mockSocket1.id); + expect(testIo.payload).toBe('Game Over!'); + }); +}); + +describe('On Disconnect', () => { + it('if a player disconnects mid-game, it ends the game', () => { + const disconnectWrapper = require('../lib/disconnect_wrapper.js')(testIo); + const ended = disconnectWrapper(mockSocket1, gameHolder); + expect(testIo.payload).toBe('Game Over!'); + expect(ended).toStrictEqual(gameHolder[0]); }); }); \ No newline at end of file diff --git a/game.js b/game.js index e571c38..4b68e54 100644 --- a/game.js +++ b/game.js @@ -50,8 +50,8 @@ class Game { decrement(player) { this.timeLeft = this.timeLeft - 1; console.log(this.timeLeft); - emitWrapper(this.io, events.countdown, this.timeLeft, `${player}`); + if (this.timeLeft === -1) { console.log('Ran out of time!!'); emitWrapper(this.io, events.message, 'Time\'s up!!'); diff --git a/lib/connection_wrapper.js b/lib/connection_wrapper.js index bf0f4f0..e01c29d 100644 --- a/lib/connection_wrapper.js +++ b/lib/connection_wrapper.js @@ -28,7 +28,6 @@ function curryIo(io) { let gameInstance = new Game(gameID, io); gameInstance.players = [players[0].id, players[1].id]; - //Morgana - send the initial game-start messages emitWrapper(io, events.message, 'Starting Game', `${gameInstance.players[0]}`); emitWrapper(io, events.turn, [gameInstance.id, gameInstance.stacks], `${gameInstance.players[0]}`); @@ -39,9 +38,9 @@ function curryIo(io) { games[gameID] = gameInstance; players = []; - } + } - return games; + return {games, players}; }; } diff --git a/lib/disconnect_wrapper.js b/lib/disconnect_wrapper.js index f62c7c8..9c345af 100644 --- a/lib/disconnect_wrapper.js +++ b/lib/disconnect_wrapper.js @@ -18,6 +18,7 @@ function curryIo(io) { clearInterval(gameToEnd.countdown); gameToEnd.timeLeft = 20; } + return gameToEnd; }; } diff --git a/server.js b/server.js index cae7a70..d72a761 100644 --- a/server.js +++ b/server.js @@ -20,8 +20,9 @@ io.on('connect', connectionContainer); function connectionContainer(socket) { console.log(`Socket ${socket.id} connected`); - games = connectionWrapper(socket); - + const {gamesObj, players} = connectionWrapper(socket); + games = gamesObj; + socket.on(events.move, payload => { moveWrapper(socket, games, payload); }); From 3d2d6c31262023b7cbef996962e26a1aed537307 Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Thu, 13 Jun 2019 10:54:49 -0700 Subject: [PATCH 24/33] small change and one more test --- __tests__/server.test.js | 15 +++++++++++++++ lib/connection_wrapper.js | 2 -- lib/game_cycle.js | 9 ++++++++- server.js | 8 ++++---- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/__tests__/server.test.js b/__tests__/server.test.js index ee38e7b..aa94f5b 100644 --- a/__tests__/server.test.js +++ b/__tests__/server.test.js @@ -87,6 +87,21 @@ describe('Game Cycle', () => { gameCycle(testIo, gameHolder[0], 'a', 1, mockSocket1.id); expect(testIo.payload).toBe('Game Over!'); }); + it('targets the correct player for win/loss', () => { + gameHolder[0].stacks['a'] = 1; + gameHolder[0].stacks['b'] = 0; + gameHolder[0].stacks['c'] = 0; + let results = gameCycle(testIo, gameHolder[0], 'a', 1, mockSocket1.id); + expect(results.winner).toBe('Alice'); + expect(results.loser).toBe('Ted'); + + gameHolder[0].stacks['a'] = 1; + gameHolder[0].stacks['b'] = 0; + gameHolder[0].stacks['c'] = 0; + results = gameCycle(testIo, gameHolder[0], 'a', 1, mockSocket2.id); + expect(results.winner).toBe('Ted'); + expect(results.loser).toBe('Alice'); + }); }); describe('On Disconnect', () => { diff --git a/lib/connection_wrapper.js b/lib/connection_wrapper.js index e01c29d..dfbcf61 100644 --- a/lib/connection_wrapper.js +++ b/lib/connection_wrapper.js @@ -19,8 +19,6 @@ function curryIo(io) { } if(players.length === 2) { - emitWrapper(io, events.message, 'In too', `${players[1].id}`); - const gameID = Object.keys(games, io).length; players[0].gameID = gameID; players[1].gameID = gameID; diff --git a/lib/game_cycle.js b/lib/game_cycle.js index 4685299..4bc81b8 100644 --- a/lib/game_cycle.js +++ b/lib/game_cycle.js @@ -11,13 +11,20 @@ function gameCycle (io, currentGame, stackChoice, numberToTake, socketID) { console.log('Total remaining', currentGame.totalItemsRemaining); } else { + const winner = socketID; + let loser; + if(currentGame.players[0] === socketID) loser = currentGame.players[1]; + if(currentGame.players[1] === socketID) loser = currentGame.players[0]; + console.log('GAME OVER!!!!!!!'); - emitWrapper(io, events.win, '', `${socketID}`); + emitWrapper(io, events.win, '', `${winner}`); + emitWrapper(io, events.message, 'You\'ve lost :(', `${loser}`); emitWrapper(io, events.gameOver, 'Game Over!', `${currentGame.players[0]}`); emitWrapper(io, events.gameOver, 'Game Over!', `${currentGame.players[1]}`); clearInterval(currentGame.countdown); currentGame.timeLeft = 20; + return {winner, loser}; } } diff --git a/server.js b/server.js index d72a761..d6106c2 100644 --- a/server.js +++ b/server.js @@ -12,7 +12,7 @@ const events = require('./events.js'); // const emitWrapper = require('./lib/emit_wrapper.js'); -let games = {}; +let gamesHolder = {}; io.on('connect', connectionContainer); @@ -20,9 +20,9 @@ io.on('connect', connectionContainer); function connectionContainer(socket) { console.log(`Socket ${socket.id} connected`); - const {gamesObj, players} = connectionWrapper(socket); - games = gamesObj; - + const {games, players} = connectionWrapper(socket); + gamesHolder = games; + socket.on(events.move, payload => { moveWrapper(socket, games, payload); }); From a2e76591af53254f1489f40616ed8ffa6b8b6b8b Mon Sep 17 00:00:00 2001 From: Morgana Spake Date: Thu, 13 Jun 2019 11:14:15 -0700 Subject: [PATCH 25/33] small bugfix --- server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index d6106c2..046dd46 100644 --- a/server.js +++ b/server.js @@ -24,11 +24,11 @@ function connectionContainer(socket) { gamesHolder = games; socket.on(events.move, payload => { - moveWrapper(socket, games, payload); + moveWrapper(socket, gamesHolder, payload); }); socket.on('disconnect', () => { - disconnectWrapper(socket, games); + disconnectWrapper(socket, gamesHolder); }); } \ No newline at end of file From bcf8332d6904c4a19f5d9457a150c7260d3e06eb Mon Sep 17 00:00:00 2001 From: rebecca-pete Date: Thu, 13 Jun 2019 12:16:49 -0700 Subject: [PATCH 26/33] updated readme --- README.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b83b85d..d2bae15 100644 --- a/README.md +++ b/README.md @@ -1 +1,74 @@ -# spike \ No newline at end of file +# 401 Midterm - Nimble + +## Socket Server + +#### Authors: Becky, Chris, Joé, Morgana + +## Links & Resources +* [GitHub Repo](https://github.com/401-advanced-javascript-nimble/client) +* [Heroku Deployment](https://nimble-api-server.herokuapp.com/) +* [Microsoft Azure Deploylment](https://401-advanced-javascript-nimble-socket-server.azurewebsites.net) +* [Travis]( --- ) + +### Documentation + +### Modules +* `server.js` -- Requires **dotenv** and **socket.io** and creates a socket server instance. Defines a function called connectionContainer that includes logic to record game and players, reacts to player moves, and reacts when a user opts to disconnect. +* `game.js` -- Module that defines the Game class and related methods. Specifically, the starting amount in each stack, the **takeItemsRemaining** method to remove items from a stack, **tallyTotalItemsRemaining** to keep track of overall remaining items, and **decrement** to show the time remaining for each turn. +* `events.js` -- Module that exports an object with events the socket server will be listening for, namely, move, turn, gameOver, message, countdown, and win. +* `lib/connection_wrapper.js` -- Module that uses the curryIo function. It includes logic that keeps track of how many players are available for a game. If only one player has joined, they receive the message, 'Waiting for second player...'. If a second player joins, a new game is created. The message event 'Starting game' is sent to player one, followed by a turn event. The message event 'Starting game' is sent to player two, followed by the message event 'Waiting for player to move'. + +* `lib/disconnect_wrapper.js` -- Module that uses the curryIo function. + + + + + +* `lib/emit_wrapper.js` -- +* `lib/game_cycle.js` -- +* `lib/move_wrapper.js` -- Module that uses the curryIo function. + + +* `src/commands/handle_leaderboard.js` -- Module with async function to render the leaderboard. Specifically, it requires **superagent** for AJAX requests to our server for user stats, **cli-table** to build the leaderboard table in the command line, and **figlet** to create ASCII Art from text. +* `src/commands/handle_play.js` -- Module with async function to validate user token and start new game. +* `src/commands/handle_signin.js` -- Module that requires **prompts** to create a more colorful CLI interface, **clear** to clear the terminal screen, and **figlet** to create ASCII Art from text. Specifically, it includes an async function that prompts user for username and password for **signin**. + +* `src/commands/handle_signout.js` -- Module with logic to sign user out. + +* `src/commands/handle_signup.js` -- Module that requires **prompts** to create a more colorful CLI interface, **clear** to clear the terminal screen, and **figlet** to create ASCII Art from text. Specifically, it includes an async function that prompts user for username and password for **signup**. + +* `src/lib/User.js` -- Module that requires **superagent** for AJAX requests to our server, and **configstore** as a means to store user data. A User class is defined with methods to create, hold, and delete a user authentication token. This is used to communicate the user's win/loss status of the current game to the API Server. + +* `src/lib/game.js` -- Module that requires **socket.io-client**, and **prompts**. A Game class is defined and includes logic for the **client socket** to react to events such as a message from the server, a new turn, turn countdown, clear line after timeout, game over, and game win. It also includes logic for **prompt functionality** such as 'Which stack?' and 'How much?', and **client-side game logic** to validate stack and amount provided by user. + + + +### Setup +#### `.env` Requirements +* SECRET -- token generation +* MONGODB_URI -- database location +* TOKEN_EXPIRATION_TIME -- token lifetime +* PORT -- port to use (when running locally) +* API_SERVER_URI=https://nimble-api-server.herokuapp.com +* SOCKET_SERVER_URL=https://401-advanced-javascript-nimble-socket-server.azurewebsites.net + +### Running the App +* `POST /signup` -- Add a user to the database. If the user's role is a superuser, they will be provided a key, otherwise the user will receive a token. Requires: username, password, email +Optional: role +* `POST /signin` -- Passes through auth middleware to verify username and password, and provides a new token if successful. +Requires: username, password +* `GET /leaderboard` +* `PUT /socket` -- Used for the socket server/client to send updates after a game to be saved to the database. +Requires: stats: {wins} +* `GET /admin` + +### Operating Instructions +* The RESTful API server runs live at https://nimble-api-server.herokuapp.com/. Perform the above requests to interact with the API. + +#### Tests +* How do you run tests? +* What assertions were made? +* What assertions need to be / should be made? + +#### UML +![UML](./assets/Nimble_UML.jpg) \ No newline at end of file From 73439754b3bad270eadfb583552b29cc90a78d84 Mon Sep 17 00:00:00 2001 From: rebecca-pete Date: Thu, 13 Jun 2019 14:31:54 -0700 Subject: [PATCH 27/33] updated readme --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d2bae15..b0d726b 100644 --- a/README.md +++ b/README.md @@ -17,18 +17,15 @@ * `game.js` -- Module that defines the Game class and related methods. Specifically, the starting amount in each stack, the **takeItemsRemaining** method to remove items from a stack, **tallyTotalItemsRemaining** to keep track of overall remaining items, and **decrement** to show the time remaining for each turn. * `events.js` -- Module that exports an object with events the socket server will be listening for, namely, move, turn, gameOver, message, countdown, and win. * `lib/connection_wrapper.js` -- Module that uses the curryIo function. It includes logic that keeps track of how many players are available for a game. If only one player has joined, they receive the message, 'Waiting for second player...'. If a second player joins, a new game is created. The message event 'Starting game' is sent to player one, followed by a turn event. The message event 'Starting game' is sent to player two, followed by the message event 'Waiting for player to move'. +* `lib/disconnect_wrapper.js` -- Module that uses the curryIo function. It includes logic that emits the message event, 'Player disconnected, ending game', when either player ends the game AND logic that emits the gameOver event with message, 'Game Over!', when either player ends a game. +* `lib/emit_wrapper.js` -- Module with logic for the emitWrapper function that takes in four parameters: **io** (the socket server instance), **event**, (the event to listen for), **payload** (usually data associated with an event), and **target** (the client id for a specific player). If a target exists, the emitted event is sent to that specific player. Otherwise the emitted event is sent to all listening parties. +* `lib/game_cycle.js` -- Module that defines and exports the gameCycle function which takes in five parameters: **io** (the socket server instance), **currentGame** (the current game instance), **stackChoice** (the stack from which the current player chose to remove items), **numberToTake** (the amount the player chose to remove), and **socketID** (can represent either player). Includes logic that states the winner based on the number of remaining items. Emits events to the winner and loser regarding their game status. +* +* `lib/move_wrapper.js` -- Module that uses the curryIo function. It includes logic for the moveWrapper function that takes in three parameters: **socket** (can represent either player), **games** (an object with a property containing current game id), and **payload** (an array of data containing the user's selections for their turn). -* `lib/disconnect_wrapper.js` -- Module that uses the curryIo function. - - -* `lib/emit_wrapper.js` -- -* `lib/game_cycle.js` -- -* `lib/move_wrapper.js` -- Module that uses the curryIo function. - - * `src/commands/handle_leaderboard.js` -- Module with async function to render the leaderboard. Specifically, it requires **superagent** for AJAX requests to our server for user stats, **cli-table** to build the leaderboard table in the command line, and **figlet** to create ASCII Art from text. * `src/commands/handle_play.js` -- Module with async function to validate user token and start new game. * `src/commands/handle_signin.js` -- Module that requires **prompts** to create a more colorful CLI interface, **clear** to clear the terminal screen, and **figlet** to create ASCII Art from text. Specifically, it includes an async function that prompts user for username and password for **signin**. From c95c4fce403572eb7f490a4a2d2ed7bbcd8237d5 Mon Sep 17 00:00:00 2001 From: rebecca-pete Date: Thu, 13 Jun 2019 14:45:50 -0700 Subject: [PATCH 28/33] updated readme --- README.md | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index b0d726b..c988891 100644 --- a/README.md +++ b/README.md @@ -20,25 +20,8 @@ * `lib/disconnect_wrapper.js` -- Module that uses the curryIo function. It includes logic that emits the message event, 'Player disconnected, ending game', when either player ends the game AND logic that emits the gameOver event with message, 'Game Over!', when either player ends a game. * `lib/emit_wrapper.js` -- Module with logic for the emitWrapper function that takes in four parameters: **io** (the socket server instance), **event**, (the event to listen for), **payload** (usually data associated with an event), and **target** (the client id for a specific player). If a target exists, the emitted event is sent to that specific player. Otherwise the emitted event is sent to all listening parties. * `lib/game_cycle.js` -- Module that defines and exports the gameCycle function which takes in five parameters: **io** (the socket server instance), **currentGame** (the current game instance), **stackChoice** (the stack from which the current player chose to remove items), **numberToTake** (the amount the player chose to remove), and **socketID** (can represent either player). Includes logic that states the winner based on the number of remaining items. Emits events to the winner and loser regarding their game status. -* -* `lib/move_wrapper.js` -- Module that uses the curryIo function. It includes logic for the moveWrapper function that takes in three parameters: **socket** (can represent either player), **games** (an object with a property containing current game id), and **payload** (an array of data containing the user's selections for their turn). - - - - -* `src/commands/handle_leaderboard.js` -- Module with async function to render the leaderboard. Specifically, it requires **superagent** for AJAX requests to our server for user stats, **cli-table** to build the leaderboard table in the command line, and **figlet** to create ASCII Art from text. -* `src/commands/handle_play.js` -- Module with async function to validate user token and start new game. -* `src/commands/handle_signin.js` -- Module that requires **prompts** to create a more colorful CLI interface, **clear** to clear the terminal screen, and **figlet** to create ASCII Art from text. Specifically, it includes an async function that prompts user for username and password for **signin**. - -* `src/commands/handle_signout.js` -- Module with logic to sign user out. - -* `src/commands/handle_signup.js` -- Module that requires **prompts** to create a more colorful CLI interface, **clear** to clear the terminal screen, and **figlet** to create ASCII Art from text. Specifically, it includes an async function that prompts user for username and password for **signup**. - -* `src/lib/User.js` -- Module that requires **superagent** for AJAX requests to our server, and **configstore** as a means to store user data. A User class is defined with methods to create, hold, and delete a user authentication token. This is used to communicate the user's win/loss status of the current game to the API Server. - -* `src/lib/game.js` -- Module that requires **socket.io-client**, and **prompts**. A Game class is defined and includes logic for the **client socket** to react to events such as a message from the server, a new turn, turn countdown, clear line after timeout, game over, and game win. It also includes logic for **prompt functionality** such as 'Which stack?' and 'How much?', and **client-side game logic** to validate stack and amount provided by user. - +* `lib/move_wrapper.js` -- Module that uses the curryIo function. It includes logic for the moveWrapper function that takes in three parameters: **socket** (can represent either player), **games** (an object with a property containing current game id), and **payload** (an array of data containing the user's selections for their turn). ### Setup #### `.env` Requirements @@ -63,9 +46,8 @@ Requires: stats: {wins} * The RESTful API server runs live at https://nimble-api-server.herokuapp.com/. Perform the above requests to interact with the API. #### Tests -* How do you run tests? -* What assertions were made? -* What assertions need to be / should be made? +* **How do you run tests?** We refactored much of the code, wrapping functions in other functions in order to more easily test our own code. +* **What assertions were made?** Only two players per game. #### UML ![UML](./assets/Nimble_UML.jpg) \ No newline at end of file From 4905ab646f7bd14ad01d11aea81f9de57fd99c43 Mon Sep 17 00:00:00 2001 From: rebecca-pete Date: Thu, 13 Jun 2019 15:45:43 -0700 Subject: [PATCH 29/33] updated readme --- README.md | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c988891..5456b58 100644 --- a/README.md +++ b/README.md @@ -5,45 +5,35 @@ #### Authors: Becky, Chris, Joé, Morgana ## Links & Resources -* [GitHub Repo](https://github.com/401-advanced-javascript-nimble/client) -* [Heroku Deployment](https://nimble-api-server.herokuapp.com/) -* [Microsoft Azure Deploylment](https://401-advanced-javascript-nimble-socket-server.azurewebsites.net) -* [Travis]( --- ) +* [GitHub Repo](https://github.com/401-advanced-javascript-nimble/socket_server) +* [Socket Server Deploylment](https://401-advanced-javascript-nimble-socket-server.azurewebsites.net) ### Documentation ### Modules -* `server.js` -- Requires **dotenv** and **socket.io** and creates a socket server instance. Defines a function called connectionContainer that includes logic to record game and players, reacts to player moves, and reacts when a user opts to disconnect. +* `server.js` -- Requires **dotenv** and **socket.io** and creates a socket server instance. Reacts to a socket connection, reacts to player moves, and reacts when a user opts to disconnect. * `game.js` -- Module that defines the Game class and related methods. Specifically, the starting amount in each stack, the **takeItemsRemaining** method to remove items from a stack, **tallyTotalItemsRemaining** to keep track of overall remaining items, and **decrement** to show the time remaining for each turn. * `events.js` -- Module that exports an object with events the socket server will be listening for, namely, move, turn, gameOver, message, countdown, and win. -* `lib/connection_wrapper.js` -- Module that uses the curryIo function. It includes logic that keeps track of how many players are available for a game. If only one player has joined, they receive the message, 'Waiting for second player...'. If a second player joins, a new game is created. The message event 'Starting game' is sent to player one, followed by a turn event. The message event 'Starting game' is sent to player two, followed by the message event 'Waiting for player to move'. -* `lib/disconnect_wrapper.js` -- Module that uses the curryIo function. It includes logic that emits the message event, 'Player disconnected, ending game', when either player ends the game AND logic that emits the gameOver event with message, 'Game Over!', when either player ends a game. -* `lib/emit_wrapper.js` -- Module with logic for the emitWrapper function that takes in four parameters: **io** (the socket server instance), **event**, (the event to listen for), **payload** (usually data associated with an event), and **target** (the client id for a specific player). If a target exists, the emitted event is sent to that specific player. Otherwise the emitted event is sent to all listening parties. -* `lib/game_cycle.js` -- Module that defines and exports the gameCycle function which takes in five parameters: **io** (the socket server instance), **currentGame** (the current game instance), **stackChoice** (the stack from which the current player chose to remove items), **numberToTake** (the amount the player chose to remove), and **socketID** (can represent either player). Includes logic that states the winner based on the number of remaining items. Emits events to the winner and loser regarding their game status. +* `lib/connection_wrapper.js` -- Module that contains a curried 'start of game' function. It includes logic that keeps track of how many players are available for a game. If only one player has joined, they receive the message, 'Waiting for second player...'. If a second player joins, a new game is created. The message event 'Starting game' is sent to player one, followed by a turn event. The message event 'Starting game' is sent to player two, followed by the message event 'Waiting for player to move'. +* `lib/disconnect_wrapper.js` -- Module that contains a curried 'disconnect' function. When a player disconnects, emits a 'Player disconnected, ending game' and 'Game Over!' message to players. +* `lib/emit_wrapper.js` -- Module with logic for the emitWrapper function that takes in four parameters: **io** (the socket server instance), **event**, (the event to listen for), **payload** (usually data associated with an event), and **target** (the socket id for a specific player). If a target exists, the emitted event is sent to that specific player. Otherwise the emitted event is sent to all listening parties. +* `lib/game_cycle.js` -- Module that defines and exports the gameCycle function which takes in five parameters: **io** (the socket server instance), **currentGame** (the current game instance), **stackChoice** (the stack from which the current player chose to remove items), **numberToTake** (the amount the player chose to remove), and **socketID** (can represent either player). When items in all stacks equal zero, emits events to the winner and loser. -* `lib/move_wrapper.js` -- Module that uses the curryIo function. It includes logic for the moveWrapper function that takes in three parameters: **socket** (can represent either player), **games** (an object with a property containing current game id), and **payload** (an array of data containing the user's selections for their turn). +* `lib/move_wrapper.js` -- Module that contains a curried 'on move' function. It includes logic for the moveWrapper function that takes in three parameters: **socket** (can represent either player), **games** (an object containing instances), and **payload** (an array of data containing the game id and user's selections for their turn). ### Setup #### `.env` Requirements -* SECRET -- token generation -* MONGODB_URI -- database location -* TOKEN_EXPIRATION_TIME -- token lifetime * PORT -- port to use (when running locally) -* API_SERVER_URI=https://nimble-api-server.herokuapp.com -* SOCKET_SERVER_URL=https://401-advanced-javascript-nimble-socket-server.azurewebsites.net ### Running the App -* `POST /signup` -- Add a user to the database. If the user's role is a superuser, they will be provided a key, otherwise the user will receive a token. Requires: username, password, email -Optional: role -* `POST /signin` -- Passes through auth middleware to verify username and password, and provides a new token if successful. -Requires: username, password -* `GET /leaderboard` -* `PUT /socket` -- Used for the socket server/client to send updates after a game to be saved to the database. -Requires: stats: {wins} -* `GET /admin` +* Clone or download repo +* Run `npm i` +* Run `node server.js` ### Operating Instructions -* The RESTful API server runs live at https://nimble-api-server.herokuapp.com/. Perform the above requests to interact with the API. +* Part of a three part system. Please see these repos: +* [API Server Repo](https://github.com/401-advanced-javascript-nimble/API_server) +* [Client Repo](https://github.com/401-advanced-javascript-nimble/client) #### Tests * **How do you run tests?** We refactored much of the code, wrapping functions in other functions in order to more easily test our own code. From 912c8b0b2bce235772ff32d8ed66714a2137c5ee Mon Sep 17 00:00:00 2001 From: rebecca-pete Date: Thu, 13 Jun 2019 15:53:29 -0700 Subject: [PATCH 30/33] updated readme --- README.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5456b58..6a90c5e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ * PORT -- port to use (when running locally) ### Running the App -* Clone or download repo +* Clone or download the repo at (https://github.com/401-advanced-javascript-nimble/socket_server) * Run `npm i` * Run `node server.js` @@ -36,8 +36,38 @@ * [Client Repo](https://github.com/401-advanced-javascript-nimble/client) #### Tests -* **How do you run tests?** We refactored much of the code, wrapping functions in other functions in order to more easily test our own code. -* **What assertions were made?** Only two players per game. +* **How do you run tests?** `npm test` +* **What assertions were made?** +Server + Event Emitter + Wraps the emit event + Does not require a target + Game creation + When a single socket connects, it stores the socket and sends a wait message + When a second socket connects, it creates a game, sets the countdown, and clears the players array ingroom) + On move + Can correctly target the player/socket that 'moved' + Game Cycle + if the stacks are empty, the game ends + targets the correct player for win/loss + On Disconnect + if a player disconnects mid-game, it ends the game + +Game Class + instantiating + can instantiate with an id and io + instantiating without id and io results in undefineds + starting properties + should have stacks + can add players + has a turn time limit + has a property to store the total number of things in the stacks + Methods + can randomly generate amounts for the stacks + can tally the total items in the stacks + can remove a given amount from a given stack + can decrement the time left + if time runs out, it ends the game #### UML ![UML](./assets/Nimble_UML.jpg) \ No newline at end of file From b86c8dad741693d534d416b20df142526ee568ce Mon Sep 17 00:00:00 2001 From: Chris Kozlowski Date: Thu, 13 Jun 2019 16:51:13 -0700 Subject: [PATCH 31/33] refactor timer to run independantly client-side --- game.js | 3 --- server.js | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/game.js b/game.js index f13bd32..a6d4fed 100644 --- a/game.js +++ b/game.js @@ -23,10 +23,7 @@ class Game { } decrement(player) { - console.log(this); this.timeLeft = this.timeLeft - 1; - console.log(this.timeLeft); - this.io.to(`${player}`).emit(events.countdown, this.timeLeft); if (this.timeLeft === -1) { console.log('Ran out of time!!'); this.io.emit(events.message, 'Time\'s up!!'); diff --git a/server.js b/server.js index 7fe4ab9..f8d29c8 100644 --- a/server.js +++ b/server.js @@ -63,14 +63,13 @@ io.on('connect', (socket) => { gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); io.to(`${currentGame.players[1]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[1]); - io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); - + io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); } else if(playerWhoMoved === 1) { const otherPlayer = currentGame.players[0]; gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); - io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[0]); + io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); } }); From 8b744743aa8211c8457e9b8c90eb9e6b712f9fa4 Mon Sep 17 00:00:00 2001 From: Morgana Spake <40220209+MSpake@users.noreply.github.com> Date: Thu, 13 Jun 2019 17:34:05 -0700 Subject: [PATCH 32/33] Update server.js --- server.js | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/server.js b/server.js index 76f4bad..9d9ef90 100644 --- a/server.js +++ b/server.js @@ -24,29 +24,6 @@ function connectionContainer(socket) { gamesHolder = games; socket.on(events.move, payload => { -// const gameID = payload[0]; -// const stackChoice = payload[1]; -// const numberToTake = payload[2]; -// const currentGame = games[gameID]; -// clearInterval(currentGame.countdown); -// currentGame.timeLeft = 20; -// let playerWhoMoved = currentGame.players.indexOf(socket.id); - -// if(playerWhoMoved === 0) { -// const otherPlayer = currentGame.players[1]; -// gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); -// io.to(`${currentGame.players[1]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); -// currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[1]); -// io.to(`${currentGame.players[0]}`).emit(events.message, 'Waiting for other player to move'); -// } -// else if(playerWhoMoved === 1) { -// const otherPlayer = currentGame.players[0]; -// gameCycle(currentGame, stackChoice, numberToTake, otherPlayer); -// currentGame.countdown = setInterval(currentGame.decrement, 1000, currentGame.players[0]); -// io.to(`${currentGame.players[0]}`).emit(events.turn, [currentGame.id, currentGame.stacks]); -// io.to(`${currentGame.players[1]}`).emit(events.message, 'Waiting for other player to move'); -// } - moveWrapper(socket, gamesHolder, payload); }); @@ -54,4 +31,4 @@ function connectionContainer(socket) { disconnectWrapper(socket, gamesHolder); }); -} \ No newline at end of file +} From dffebcb60ffa88e4dcd3ec38372d3cb61d207323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=CC=81=20Jemmely?= Date: Fri, 14 Jun 2019 09:10:29 -0700 Subject: [PATCH 33/33] Update you lost message --- lib/game_cycle.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/game_cycle.js b/lib/game_cycle.js index 4bc81b8..7dbf525 100644 --- a/lib/game_cycle.js +++ b/lib/game_cycle.js @@ -3,29 +3,27 @@ const emitWrapper = require('./emit_wrapper.js'); const events = require('../events.js'); -function gameCycle (io, currentGame, stackChoice, numberToTake, socketID) { - +function gameCycle(io, currentGame, stackChoice, numberToTake, socketID) { currentGame.takeItemsFromStack(stackChoice, numberToTake); - if(currentGame.totalItemsRemaining !== 0) { + if (currentGame.totalItemsRemaining !== 0) { console.log('Total remaining', currentGame.totalItemsRemaining); - } - else { + } else { const winner = socketID; let loser; - if(currentGame.players[0] === socketID) loser = currentGame.players[1]; - if(currentGame.players[1] === socketID) loser = currentGame.players[0]; + if (currentGame.players[0] === socketID) loser = currentGame.players[1]; + if (currentGame.players[1] === socketID) loser = currentGame.players[0]; console.log('GAME OVER!!!!!!!'); emitWrapper(io, events.win, '', `${winner}`); - emitWrapper(io, events.message, 'You\'ve lost :(', `${loser}`); + emitWrapper(io, events.message, '😔 You\'ve lost', `${loser}`); emitWrapper(io, events.gameOver, 'Game Over!', `${currentGame.players[0]}`); emitWrapper(io, events.gameOver, 'Game Over!', `${currentGame.players[1]}`); clearInterval(currentGame.countdown); currentGame.timeLeft = 20; - return {winner, loser}; + return { winner, loser }; } } -module.exports = gameCycle; \ No newline at end of file +module.exports = gameCycle;