From 966ce13f67fe334a3b62a1d8168bd9254023d502 Mon Sep 17 00:00:00 2001 From: Kenrick Date: Wed, 3 Oct 2018 22:15:57 +0800 Subject: [PATCH 1/5] Update dependencies --- app.js | 336 +++++++++---------- game.js | 441 ------------------------- package.json | 7 +- yarn.lock | 910 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1084 insertions(+), 610 deletions(-) delete mode 100644 game.js create mode 100644 yarn.lock diff --git a/app.js b/app.js index 5b6d9f9..d577991 100644 --- a/app.js +++ b/app.js @@ -1,167 +1,171 @@ -var restify = require('restify'); -var builder = require('botbuilder'); -var c4game = require('./game'); - -require('dotenv').config({silent: true}); - -//========================================================= -// Bot Setup -//========================================================= - -// Setup Restify Server -var server = restify.createServer(); -var port = process.env.PORT || 3978; -server.listen(port, function () { - console.log('%s listening to %s', server.name, server.url); -}); - -// Create chat bot -var connector = new builder.ChatConnector({ - appId: process.env.MICROSOFT_APP_ID, - appPassword: process.env.MICROSOFT_APP_PASSWORD -}); -server.post('/c4bot/api/messages', connector.listen()); - -var bot = new builder.UniversalBot(connector); -var intents = new builder.IntentDialog(); - -bot.dialog('/', intents); - -intents.matches(/^(new|play|start)/i, [ - function (session) { - session.beginDialog("/new"); - } -]); -intents.matches(/^(help|how|guide)/i, [ - function (session) { - session.beginDialog("/help"); - } -]); -intents.onDefault([ - function (session) { - session.send("Hi there, I am c4bot, a bot that plays Connect Four game with you. Click here https://bots.botframework.com/bot?id=c4bot to find out more about me and my policies."); - builder.Prompts.choice(session, "What would you like to do?", "New game|Help", { - retryPrompt: "Sorry, I didn't understand that. \"New game\" or \"help\"?" - }); - }, - function (session, results) { - if (results.response && results.response.entity.toLowerCase() != 'help') { - session.beginDialog('/new'); - } else { - session.beginDialog('/help'); - } - } -]); - - -bot.dialog('/help', [ - function (session) { - session.send("I will play a Connect Four game with you."); - var msg = new builder.Message(session) - .text("A GIF worth a thousand words.") - .attachments([{ - contentType: "image/gif", - contentUrl: "https://upload.wikimedia.org/wikipedia/commons/a/ad/Connect_Four.gif" - }]); - session.send(msg); - session.send("If you encounter any issue, please report it to my GitHub repo: https://github.com/kenrick95/c4bot/issues"); - session.endDialog(); - } -]); -bot.dialog('/new', [ - function (session) { - session.send("Game started..."); - var game = new c4game.Game(); - session.userData.gameState = game.gameState; - session.send(canvasToMessage(session, game.canvas, 0)); - session.beginDialog("/move"); - }, - function (session) { - var wonPlayer = session.userData.gameState.wonPlayer; - var gameEnded = session.userData.gameState.won; - - var msg = ""; - if (gameEnded) { - msg = "It's a draw"; - if (wonPlayer > 0) { - msg = "You won!"; - } else if (wonPlayer < 0) { - msg = "Computer won!"; - } - msg += "\n"; - } - - session.send(); - session.endDialog(msg + "Game over. Thank you for playing.\nSay 'hi' again to restart the game."); - } -]); -bot.dialog('/invalid', [ - function (session) { - session.send("Invalid move, please choose another column"); - session.replaceDialog("/move"); - } -]); - -bot.dialog('/move', [ - function (session) { - builder.Prompts.text(session, "Please choose a column (1-7)"); - }, - function (session, results) { - if (/^(end|restart)/i.test(results.response)) { - session.endDialog(); - return; - } - - var choice = builder.EntityRecognizer.parseNumber(results.response) - 1; - if (isNaN(choice) || choice < 0 || choice > 6) { - session.replaceDialog("/invalid"); - return; - } - - var game = new c4game.Game(); - // restore state - game.gameState = session.userData.gameState; - - // resume game - game.gameState.paused = false; - - // do action - var valid = game.action(choice, function () { - // print state (after user move) - session.send(canvasToMessage(session, game.canvas, 1)); - session.sendBatch(); - - if (!game.gameState.won) { - this.ai.bind(this)(-1); - - // print state (after AI move) - session.send(canvasToMessage(session, game.canvas, -1)); - } - - }.bind(game)); - - if (valid < 1) { - session.replaceDialog("/invalid"); - return; - } - - // save state - session.userData.gameState = game.gameState; - - // next move - if (!session.userData.gameState.won) { - session.replaceDialog("/move"); - } else { - session.endDialog(); - } - } -]); - -function canvasToMessage(session, canvas, player) { - return new builder.Message(session) - .text((player != 0) ? ((player > 0) ? "You moved 🔴" : "Computer moved 🔵") : "Waiting for your move") - .attachments([{ - contentType: "image/png", - contentUrl: canvas.toDataURL() - }]); +var restify = require('restify'); +var builder = require('botbuilder'); +var c4game = require('@kenrick95/c4'); + +require('dotenv').config(); + +//========================================================= +// Bot Setup +//========================================================= + +// Setup Restify Server +var server = restify.createServer(); +var port = process.env.PORT || 3978; +server.listen(port, function () { + console.log('%s listening to %s', server.name, server.url); +}); + +// Create chat bot +var connector = new builder.ChatConnector({ + appId: process.env.MICROSOFT_APP_ID, + appPassword: process.env.MICROSOFT_APP_PASSWORD +}); +server.post('/c4bot/api/messages', connector.listen()); + +var bot = new builder.UniversalBot(connector); +var intents = new builder.IntentDialog(); + +bot.dialog('/', intents); + +intents.matches(/^(new|play|start)/i, [ + function (session) { + session.beginDialog("/new"); + } +]); +intents.matches(/^(help|how|guide)/i, [ + function (session) { + session.beginDialog("/help"); + } +]); +intents.onDefault([ + function (session) { + session.send("Hi there, I am c4bot, a bot that plays Connect Four game with you. Click here https://bots.botframework.com/bot?id=c4bot to find out more about me and my policies."); + builder.Prompts.choice(session, "What would you like to do?", "New game|Help", { + retryPrompt: "Sorry, I didn't understand that. \"New game\" or \"help\"?" + }); + }, + function (session, results) { + if (results.response && results.response.entity.toLowerCase() != 'help') { + session.beginDialog('/new'); + } else { + session.beginDialog('/help'); + } + } +]); + + +bot.dialog('/help', [ + function (session) { + session.send("I will play a Connect Four game with you."); + var msg = new builder.Message(session) + .text("A GIF worth a thousand words.") + .attachments([{ + contentType: "image/gif", + contentUrl: "https://upload.wikimedia.org/wikipedia/commons/a/ad/Connect_Four.gif" + }]); + session.send(msg); + session.send("If you encounter any issue, please report it to my GitHub repo: https://github.com/kenrick95/c4bot/issues"); + session.endDialog(); + } +]); +bot.dialog('/new', [ + function (session) { + session.send("Game started..."); + // const board = new Board(canvas) + // board.render() + // Game.initGameLocalAi() + + var game = new c4game.Game(); + session.userData.gameState = game.gameState; + session.send(canvasToMessage(session, game.canvas, 0)); + session.beginDialog("/move"); + }, + function (session) { + var wonPlayer = session.userData.gameState.wonPlayer; + var gameEnded = session.userData.gameState.won; + + var msg = ""; + if (gameEnded) { + msg = "It's a draw"; + if (wonPlayer > 0) { + msg = "You won!"; + } else if (wonPlayer < 0) { + msg = "Computer won!"; + } + msg += "\n"; + } + + session.send(); + session.endDialog(msg + "Game over. Thank you for playing.\nSay 'hi' again to restart the game."); + } +]); +bot.dialog('/invalid', [ + function (session) { + session.send("Invalid move, please choose another column"); + session.replaceDialog("/move"); + } +]); + +bot.dialog('/move', [ + function (session) { + builder.Prompts.text(session, "Please choose a column (1-7)"); + }, + function (session, results) { + if (/^(end|restart)/i.test(results.response)) { + session.endDialog(); + return; + } + + var choice = builder.EntityRecognizer.parseNumber(results.response) - 1; + if (isNaN(choice) || choice < 0 || choice > 6) { + session.replaceDialog("/invalid"); + return; + } + + var game = new c4game.Game(); + // restore state + game.gameState = session.userData.gameState; + + // resume game + game.gameState.paused = false; + + // do action + var valid = game.action(choice, function () { + // print state (after user move) + session.send(canvasToMessage(session, game.canvas, 1)); + session.sendBatch(); + + if (!game.gameState.won) { + this.ai.bind(this)(-1); + + // print state (after AI move) + session.send(canvasToMessage(session, game.canvas, -1)); + } + + }.bind(game)); + + if (valid < 1) { + session.replaceDialog("/invalid"); + return; + } + + // save state + session.userData.gameState = game.gameState; + + // next move + if (!session.userData.gameState.won) { + session.replaceDialog("/move"); + } else { + session.endDialog(); + } + } +]); + +function canvasToMessage(session, canvas, player) { + return new builder.Message(session) + .text((player != 0) ? ((player > 0) ? "You moved 🔴" : "Computer moved 🔵") : "Waiting for your move") + .attachments([{ + contentType: "image/png", + contentUrl: canvas.toDataURL() + }]); } \ No newline at end of file diff --git a/game.js b/game.js deleted file mode 100644 index 2ab2152..0000000 --- a/game.js +++ /dev/null @@ -1,441 +0,0 @@ -"use strict"; -var Canvas = require('canvas'); - -module.exports = { - Game: function() { - this.gameState = { - map: [], - paused: false, - won: false, - rejectAction: false, - move: 0, - initOnceDone: false, - wonPlayer: 0 - } - - /** - * Only initalize once for these functions, can prevent race condition - */ - this.initOnce = function () { - if (this.gameState.initOnceDone) { - return false; - } - - this.canvas = new Canvas(640, 480); - this.context = this.canvas.getContext('2d'); - this.gameState.initOnceDone = true; - }; - - this.init = function () { - this.gameState.map = []; - this.gameState.paused = false; - this.gameState.won = false; - this.gameState.rejectAction = false; - this.gameState.move = 0; - this.initOnce(); - - var i, j; - for (i = 0; i <= 6; i++) { - this.gameState.map[i] = []; - for (j = 0; j <= 7; j++) { - this.gameState.map[i][j] = 0; - } - } - this.clearMap(); - this.drawMask(); - // this.print(); - }; - - this.playerMove = function () { - if (this.gameState.move % 2 === 0) { - return 1; - } - return -1; - }; - - this.print = function () { - var i, j, msg; - msg = "\n"; - msg += "Move: " + this.gameState.move; - msg += "\n"; - for (i = 0; i < 6; i++) { - for (j = 0; j < 7; j++) { - msg += " " + this.gameState.map[i][j]; - } - msg += "\n"; - } - console.log(msg); - }; - - this.win = function (player) { - if (this.gameState.won) { - return false; - } - this.gameState.wonPlayer = player; - this.gameState.paused = true; - this.gameState.won = true; - this.gameState.rejectAction = false; - var msg = null; - if (player > 0) { - msg = "You win"; - } else if (player < 0) { - msg = "Computer wins"; - } else { - msg = "It's a draw"; - } - msg += " - Thanks for playing"; - this.context.save(); - this.context.font = '14pt sans-serif'; - this.context.fillStyle = "#111"; - this.context.fillText(msg, 200, 20); - this.context.restore(); - console.info(msg); - }; - this.fillMap = function (state, column, value) { - var tempMap = state.clone(); - if (tempMap[0][column] !== 0 || column < 0 || column > 6) { - return -1; - } - - var done = false, - row = 0, - i; - for (i = 0; i < 5; i++) { - if (tempMap[i + 1][column] !== 0) { - done = true; - row = i; - break; - } - } - if (!done) { - row = 5; - } - tempMap[row][column] = value; - return tempMap; - - }; - - this.action = function (column, callback) { - if (this.gameState.paused || this.gameState.won) { - return 0; - } - if (this.gameState.map[0][column] !== 0 || column < 0 || column > 6) { - return -1; - } - - var done = false; - var row = 0, i; - for (i = 0; i < 5; i++) { - if (this.gameState.map[i + 1][column] !== 0) { - done = true; - row = i; - break; - } - } - if (!done) { - row = 5; - } - this.gameState.map[row][column] = this.playerMove(this.gameState.move); - this.gameState.move++; - this.drawMap(); - this.check(); - // this.print(); - callback(); - this.gameState.paused = true; - return 1; - }; - - this.check = function () { - var i, j, k; - var temp_r = 0, temp_b = 0, temp_br = 0, temp_tr = 0; - for (i = 0; i < 6; i++) { - for (j = 0; j < 7; j++) { - temp_r = 0; - temp_b = 0; - temp_br = 0; - temp_tr = 0; - for (k = 0; k <= 3; k++) { - //from (i,j) to right - if (j + k < 7) { - temp_r += this.gameState.map[i][j + k]; - } - //from (i,j) to bottom - if (i + k < 6) { - temp_b += this.gameState.map[i + k][j]; - } - - //from (i,j) to bottom-right - if (i + k < 6 && j + k < 7) { - temp_br += this.gameState.map[i + k][j + k]; - } - - //from (i,j) to top-right - if (i - k >= 0 && j + k < 7) { - temp_tr += this.gameState.map[i - k][j + k]; - } - } - if (Math.abs(temp_r) === 4) { - this.win(temp_r); - } else if (Math.abs(temp_b) === 4) { - this.win(temp_b); - } else if (Math.abs(temp_br) === 4) { - this.win(temp_br); - } else if (Math.abs(temp_tr) === 4) { - this.win(temp_tr); - } - - } - } - // check if draw - if ((this.gameState.move === 42) && (!this.gameState.won)) { - this.win(0); - } - }; - - this.drawCircle = function (x, y, r, fill, stroke) { - this.context.save(); - this.context.fillStyle = fill; - this.context.strokeStyle = stroke; - this.context.beginPath(); - this.context.arc(x, y, r, 0, 2 * Math.PI, false); - //this.context.stroke(); - this.context.fill(); - this.context.restore(); - }; - this.drawMask = function () { - // draw the mask - // http://stackoverflow.com/questions/6271419/how-to-fill-the-opposite-shape-on-canvas - // --> http://stackoverflow.com/a/11770000/917957 - - this.context.save(); - this.context.fillStyle = "#ddd"; - this.context.beginPath(); - var x, y; - for (y = 0; y < 6; y++) { - for (x = 0; x < 7; x++) { - this.context.arc(75 * x + 100, 75 * y + 50, 25, 0, 2 * Math.PI); - this.context.rect(75 * x + 150, 75 * y, -100, 100); - } - } - this.context.fill(); - this.context.restore(); - }; - - this.drawMap = function () { - var x, y; - var fg_color; - for (y = 0; y < 6; y++) { - for (x = 0; x < 7; x++) { - fg_color = "transparent"; - if (this.gameState.map[y][x] >= 1) { - fg_color = "#ff4136"; - } else if (this.gameState.map[y][x] <= -1) { - fg_color = "#0074d9"; - } - this.drawCircle(75 * x + 100, 75 * y + 50, 25, fg_color, "black"); - } - } - }; - this.clearMap = function () { - this.context.save(); - this.context.fillStyle = "#fff"; - this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); - this.context.restore(); - }; - - this.ai = function (aiMoveValue) { - var choice = null; - - var state = this.gameState.map.clone(); - function checkState(state) { - - var winVal = 0; - var chainVal = 0; - var i, j, k; - var temp_r = 0, temp_b = 0, temp_br = 0, temp_tr = 0; - for (i = 0; i < 6; i++) { - for (j = 0; j < 7; j++) { - temp_r = 0; - temp_b = 0; - temp_br = 0; - temp_tr = 0; - for (k = 0; k <= 3; k++) { - //from (i,j) to right - if (j + k < 7) { - temp_r += state[i][j + k]; - } - - //from (i,j) to bottom - if (i + k < 6) { - temp_b += state[i + k][j]; - } - - //from (i,j) to bottom-right - if (i + k < 6 && j + k < 7) { - temp_br += state[i + k][j + k]; - } - - //from (i,j) to top-right - if (i - k >= 0 && j + k < 7) { - temp_tr += state[i - k][j + k]; - } - } - chainVal += temp_r * temp_r * temp_r; - chainVal += temp_b * temp_b * temp_b; - chainVal += temp_br * temp_br * temp_br; - chainVal += temp_tr * temp_tr * temp_tr; - - if (Math.abs(temp_r) === 4) { - winVal = temp_r; - } else if (Math.abs(temp_b) === 4) { - winVal = temp_b; - } else if (Math.abs(temp_br) === 4) { - winVal = temp_br; - } else if (Math.abs(temp_tr) === 4) { - winVal = temp_tr; - } - - } - } - return [winVal, chainVal]; - } - function value(state, depth, alpha, beta) { - var val = checkState(state); - if (depth >= 4) { // if slow (or memory consumption is high), lower the value - - // calculate value - var retValue = 0; - - // if win, value = +inf - var winVal = val[0]; - var chainVal = val[1] * aiMoveValue; - retValue = chainVal; - - // If it lead to winning, then do it - if (winVal === 4 * aiMoveValue) { // AI win, AI wants to win of course - retValue = 999999; - } else if (winVal === 4 * aiMoveValue * -1) { // AI lose, AI hates losing - retValue = 999999 * -1; - } - retValue -= depth * depth; - - return [retValue, -1]; - } - - var win = val[0]; - // if already won, then return the value right away - if (win === 4 * aiMoveValue) { // AI win, AI wants to win of course - return [999999 - depth * depth, -1]; - } - if (win === 4 * aiMoveValue * -1) { // AI lose, AI hates losing - return [999999 * -1 - depth * depth, -1]; - } - - if (depth % 2 === 0) { - return minState.bind(this)(state, depth + 1, alpha, beta); - } - return maxState.bind(this)(state, depth + 1, alpha, beta); - - } - function choose(choice) { - return choice[Math.floor(Math.random() * choice.length)]; - } - function maxState(state, depth, alpha, beta) { - var v = -100000000007; - var move = -1; - var tempVal = null; - var tempState = null; - var moveQueue = []; - var j; - for (j = 0; j < 7; j++) { - tempState = this.fillMap(state, j, aiMoveValue); - if (tempState !== -1) { - - tempVal = value.bind(this)(tempState, depth, alpha, beta); - if (tempVal[0] > v) { - v = tempVal[0]; - move = j; - moveQueue = []; - moveQueue.push(j); - } else if (tempVal[0] === v) { - moveQueue.push(j); - } - - // alpha-beta pruning - if (v > beta) { - move = choose(moveQueue); - return [v, move]; - } - alpha = Math.max(alpha, v); - } - } - move = choose(moveQueue); - - return [v, move]; - } - function minState(state, depth, alpha, beta) { - var v = 100000000007; - var move = -1; - var tempVal = null; - var tempState = null; - var moveQueue = []; - var j; - - for (j = 0; j < 7; j++) { - tempState = this.fillMap(state, j, aiMoveValue * -1); - if (tempState !== -1) { - - tempVal = value.bind(this)(tempState, depth, alpha, beta); - if (tempVal[0] < v) { - v = tempVal[0]; - move = j; - moveQueue = []; - moveQueue.push(j); - } else if (tempVal[0] === v) { - moveQueue.push(j); - } - - // alpha-beta pruning - if (v < alpha) { - move = choose(moveQueue); - return [v, move]; - } - beta = Math.min(beta, v); - } - } - move = choose(moveQueue); - - return [v, move]; - } - var choice_val = maxState.bind(this)(state, 0, -100000000007, 100000000007); - choice = choice_val[1]; - var val = choice_val[0]; - console.info("AI " + aiMoveValue + " choose column: " + choice + " (value: " + val + ")"); - - this.gameState.paused = false; - var done = this.action(choice, function () { - this.gameState.rejectAction = false; - }.bind(this)); - - // if fail, then random - while (done < 0) { - console.error("Falling back to random agent"); - choice = Math.floor(Math.random() * 7); - done = this.action(choice, function () { - this.gameState.rejectAction = false; - }.bind(this)); - } - - }; - this.init(); - } -}; - -// http://stackoverflow.com/questions/13756482/create-copy-of-multi-dimensional-array-not-reference-javascript -Array.prototype.clone = function () { - var arr = [], i; - for (i = 0; i < this.length; i++) { - arr[i] = this[i].slice(); - } - return arr; -}; diff --git a/package.json b/package.json index af6cc07..dd6dd54 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,11 @@ "description": "Connect Four bot", "main": "app.js", "dependencies": { - "botbuilder": "^3.6.0", - "canvas": "^1.5.0", + "@kenrick95/c4": "^3.0.2", + "botbuilder": "^3.15.0", "dotenv": "^2.0.0", - "restify": "^4.1.1" + "pureimage": "^0.1.6", + "restify": "^7.2.1" }, "devDependencies": {}, "scripts": { diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..dd2fb32 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,910 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@kenrick95/c4@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@kenrick95/c4/-/c4-3.0.2.tgz#6f02fa5cc048f8ed4a975b051186735a2ff099df" + dependencies: + es6-promise "^4.2.5" + +"@types/async@^2.0.48": + version "2.0.49" + resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.49.tgz#92e33d13f74c895cb9a7f38ba97db8431ed14bc0" + +"@types/body-parser@*": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/caseless@*": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.1.tgz#9794c69c8385d0192acc471a540d1f8e0d16218a" + +"@types/connect@*": + version "3.4.32" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" + dependencies: + "@types/node" "*" + +"@types/events@*": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" + +"@types/express-serve-static-core@*": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz#fdfe777594ddc1fe8eb8eccce52e261b496e43e7" + dependencies: + "@types/events" "*" + "@types/node" "*" + "@types/range-parser" "*" + +"@types/express@^4.11.1": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.0.tgz#6d8bc42ccaa6f35cf29a2b7c3333cb47b5a32a19" + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/serve-static" "*" + +"@types/form-data@*", "@types/form-data@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" + dependencies: + "@types/node" "*" + +"@types/jsonwebtoken@^7.2.6": + version "7.2.8" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz#8d199dab4ddb5bba3234f8311b804d2027af2b3a" + dependencies: + "@types/node" "*" + +"@types/mime@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" + +"@types/node@*": + version "10.11.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.11.3.tgz#c055536ac8a5e871701aa01914be5731539d01ee" + +"@types/node@^9.6.1": + version "9.6.32" + resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.32.tgz#1b64134f630b30c9cda4810aa4a94fc2d4141dbd" + +"@types/range-parser@*": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.2.tgz#fa8e1ad1d474688a757140c91de6dace6f4abc8d" + +"@types/request@^2.47.0": + version "2.47.1" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.47.1.tgz#25410d3afbdac04c91a94ad9efc9824100735824" + dependencies: + "@types/caseless" "*" + "@types/form-data" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + +"@types/serve-static@*": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + +"@types/sprintf-js@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/sprintf-js/-/sprintf-js-1.1.0.tgz#6850a98382b517c4a4d0401c63f2b0be76cee092" + +"@types/tough-cookie@*": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9" + +"@types/url-join@^0.8.1": + version "0.8.2" + resolved "https://registry.yarnpkg.com/@types/url-join/-/url-join-0.8.2.tgz#1181ecbe1d97b7034e0ea1e35e62e86cc26b422d" + +ajv@^5.3.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +async@^1.5.2: + version "1.5.2" + resolved "http://registry.npmjs.org/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + dependencies: + tweetnacl "^0.14.3" + +botbuilder@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/botbuilder/-/botbuilder-3.15.0.tgz#394e7abd35a0781c0365df62ee953fcedfa6d50c" + dependencies: + "@types/async" "^2.0.48" + "@types/express" "^4.11.1" + "@types/form-data" "^2.2.1" + "@types/jsonwebtoken" "^7.2.6" + "@types/node" "^9.6.1" + "@types/request" "^2.47.0" + "@types/sprintf-js" "^1.1.0" + "@types/url-join" "^0.8.1" + async "^1.5.2" + base64url "^2.0.0" + chrono-node "^1.1.3" + jsonwebtoken "^7.0.1" + promise "^7.1.1" + request "^2.69.0" + rsa-pem-from-mod-exp "^0.8.4" + sprintf-js "^1.0.3" + url-join "^1.1.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + +bunyan@^1.8.12: + version "1.8.12" + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797" + optionalDependencies: + dtrace-provider "~0.8" + moment "^2.10.6" + mv "~2" + safe-json-stringify "~1" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +chrono-node@^1.1.3: + version "1.3.5" + resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-1.3.5.tgz#a2495298a32da82bcc01ad9be7d77efa5e244122" + dependencies: + moment "^2.10.3" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +combined-stream@1.0.6: + version "1.0.6" + resolved "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + dependencies: + delayed-stream "~1.0.0" + +combined-stream@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + dependencies: + delayed-stream "~1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +csv-generate@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-1.1.2.tgz#ec6b00edaed6e59ad9c20582f4c364e28b146240" + +csv-parse@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-1.3.3.tgz#d1cfd8743c2f849a0abb2fd544db56695d19a490" + +csv-stringify@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-1.1.2.tgz#77a41526581bce3380f12b00d7c5bbac70c82b58" + dependencies: + lodash.get "~4.4.2" + +csv@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/csv/-/csv-1.2.1.tgz#5231edfc1c7152512ec45781076a7a97ff525c0c" + dependencies: + csv-generate "^1.1.2" + csv-parse "^1.3.3" + csv-stringify "^1.1.2" + stream-transform "^0.2.2" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@^2.6.8: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +detect-node@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + +dotenv@^2.0.0: + version "2.0.0" + resolved "http://registry.npmjs.org/dotenv/-/dotenv-2.0.0.tgz#bd759c357aaa70365e01c96b7b0bec08a6e0d949" + +dtrace-provider@^0.8.1, dtrace-provider@~0.8: + version "0.8.7" + resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.7.tgz#dc939b4d3e0620cfe0c1cd803d0d2d7ed04ffd04" + dependencies: + nan "^2.10.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3" + dependencies: + safe-buffer "^5.0.1" + +es6-promise@^4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054" + +escape-regexp-component@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz#9c63b6d0b25ff2a88c3adbd18c5b61acc3b9faa2" + +ewma@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ewma/-/ewma-2.0.1.tgz#9876c1c491ac5733c8666001a3961a04c97cf1e8" + dependencies: + assert-plus "^1.0.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +extsprintf@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-decode-uri-component@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.0.tgz#7ce10336aa4b26286fee93d71e6785ff0f596a33" + +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +find-my-way@^1.13.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/find-my-way/-/find-my-way-1.15.3.tgz#fca6271fea10387d7f19f0c9ff3ea517d34f6fde" + dependencies: + fast-decode-uri-component "^1.0.0" + safe-regex "^1.1.0" + semver-store "^0.3.0" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + dependencies: + asynckit "^0.4.0" + combined-stream "1.0.6" + mime-types "^2.1.12" + +formidable@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +handle-thing@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29" + dependencies: + ajv "^5.3.0" + har-schema "^2.0.0" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + +http-signature@^1.2.0, http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isemail@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/isemail/-/isemail-1.2.0.tgz#be03df8cc3e29de4d2c5df6501263f1fa4595e9a" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +joi@^6.10.1: + version "6.10.1" + resolved "http://registry.npmjs.org/joi/-/joi-6.10.1.tgz#4d50c318079122000fe5f16af1ff8e1917b77e06" + dependencies: + hoek "2.x.x" + isemail "1.x.x" + moment "2.x.x" + topo "1.x.x" + +jpeg-js@^0.3.3: + version "0.3.4" + resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.4.tgz#dc2ba501ee3d58b7bb893c5d1fab47294917e7e7" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +jsonwebtoken@^7.0.1: + version "7.4.3" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz#77f5021de058b605a1783fa1283e99812e645638" + dependencies: + joi "^6.10.1" + jws "^3.1.4" + lodash.once "^4.0.0" + ms "^2.0.0" + xtend "^4.0.1" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jwa@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.6.tgz#87240e76c9808dbde18783cf2264ef4929ee50e6" + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.10" + safe-buffer "^5.0.1" + +jws@^3.1.4: + version "3.1.5" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.5.tgz#80d12d05b293d1e841e7cb8b4e69e561adcf834f" + dependencies: + jwa "^1.1.5" + safe-buffer "^5.0.1" + +lodash.get@~4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + +lodash@^4.17.10, lodash@^4.2.1: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + +lru-cache@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +mime-db@~1.36.0: + version "1.36.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.20" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" + dependencies: + mime-db "~1.36.0" + +mime@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +"minimatch@2 || 3": + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +mkdirp@~0.5.1: + version "0.5.1" + resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +moment@2.x.x, moment@^2.10.3, moment@^2.10.6: + version "2.22.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +ms@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + +mv@~2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" + dependencies: + mkdirp "~0.5.1" + ncp "~2.0.0" + rimraf "~2.4.0" + +nan@^2.10.0: + version "2.11.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.1.tgz#90e22bccb8ca57ea4cd37cc83d3819b52eea6766" + +ncp@~2.0.0: + version "2.0.0" + resolved "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + +negotiator@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + +obuf@^1.0.0, obuf@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + +once@^1.3.0, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +opentype.js@^0.4.3: + version "0.4.11" + resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-0.4.11.tgz#281a2390639cc15931c955d8d63c14a7c7772b41" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pidusage@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-1.2.0.tgz#65ee96ace4e08a4cd3f9240996c85b367171ee92" + +pngjs@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +psl@^1.1.24: + version "1.1.29" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +pureimage@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/pureimage/-/pureimage-0.1.6.tgz#32b4de248e1ed660bddf744523375b6fa7be3821" + dependencies: + jpeg-js "^0.3.3" + opentype.js "^0.4.3" + pngjs "^3.3.1" + +qs@^6.5.2, qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + +readable-stream@^2.0.1, readable-stream@^2.2.9: + version "2.3.6" + resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +request@^2.69.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +restify-errors@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/restify-errors/-/restify-errors-5.0.0.tgz#668717e100683eec6ce0d515f89ff1dbec254a8d" + dependencies: + assert-plus "^1.0.0" + lodash "^4.2.1" + verror "^1.8.1" + optionalDependencies: + safe-json-stringify "^1.0.3" + +restify@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/restify/-/restify-7.2.1.tgz#fac4d149224cbcfb3ed06585df08433569025dab" + dependencies: + assert-plus "^1.0.0" + bunyan "^1.8.12" + csv "^1.1.1" + escape-regexp-component "^1.0.2" + ewma "^2.0.1" + find-my-way "^1.13.0" + formidable "^1.2.1" + http-signature "^1.2.0" + lodash "^4.17.10" + lru-cache "^4.1.3" + mime "^1.5.0" + negotiator "^0.6.1" + once "^1.4.0" + pidusage "^1.2.0" + qs "^6.5.2" + restify-errors "^5.0.0" + semver "^5.4.1" + spdy "^3.4.7" + uuid "^3.1.0" + vasync "^1.6.4" + verror "^1.10.0" + optionalDependencies: + dtrace-provider "^0.8.1" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rimraf@~2.4.0: + version "2.4.5" + resolved "http://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + dependencies: + glob "^6.0.1" + +rsa-pem-from-mod-exp@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.4.tgz#362a42c6d304056d493b3f12bceabb2c6576a6d4" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-json-stringify@^1.0.3, safe-json-stringify@~1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + +semver-store@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/semver-store/-/semver-store-0.3.0.tgz#ce602ff07df37080ec9f4fb40b29576547befbe9" + +semver@^5.4.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" + +spdy-transport@^2.0.18: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.1.0.tgz#4bbb15aaffed0beefdd56ad61dbdc8ba3e2cb7a1" + dependencies: + debug "^2.6.8" + detect-node "^2.0.3" + hpack.js "^2.1.6" + obuf "^1.1.1" + readable-stream "^2.2.9" + safe-buffer "^5.0.1" + wbuf "^1.7.2" + +spdy@^3.4.7: + version "3.4.7" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.7.tgz#42ff41ece5cc0f99a3a6c28aabb73f5c3b03acbc" + dependencies: + debug "^2.6.8" + handle-thing "^1.2.5" + http-deceiver "^1.2.7" + safe-buffer "^5.0.1" + select-hose "^2.0.0" + spdy-transport "^2.0.18" + +sprintf-js@^1.0.3: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" + +sshpk@^1.7.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + safer-buffer "^2.0.2" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stream-transform@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-0.2.2.tgz#75867487f49528f8bf1d82499658753d02df7838" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +topo@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/topo/-/topo-1.1.0.tgz#e9d751615d1bb87dc865db182fa1ca0a5ef536d5" + dependencies: + hoek "2.x.x" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +url-join@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^3.1.0, uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + +vasync@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.6.4.tgz#dfe93616ad0e7ae801b332a9d88bfc5cdc8e1d1f" + dependencies: + verror "1.6.0" + +verror@1.10.0, verror@^1.10.0, verror@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +verror@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.6.0.tgz#7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5" + dependencies: + extsprintf "1.2.0" + +wbuf@^1.1.0, wbuf@^1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + dependencies: + minimalistic-assert "^1.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +xtend@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" From 189cb8851b7bebd271c46f9b14174ba38d973892 Mon Sep 17 00:00:00 2001 From: Kenrick Date: Thu, 4 Oct 2018 13:52:45 +0800 Subject: [PATCH 2/5] Bump deps, add test file --- package.json | 2 +- test.js | 38 ++++++++++++++++++++++++++++++++++++++ yarn.lock | 6 +++--- 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 test.js diff --git a/package.json b/package.json index dd6dd54..a699005 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Connect Four bot", "main": "app.js", "dependencies": { - "@kenrick95/c4": "^3.0.2", + "@kenrick95/c4": "^3.0.4", "botbuilder": "^3.15.0", "dotenv": "^2.0.0", "pureimage": "^0.1.6", diff --git a/test.js b/test.js new file mode 100644 index 0000000..83995a9 --- /dev/null +++ b/test.js @@ -0,0 +1,38 @@ +var PImage = require('pureimage'); +var canvas = PImage.make(640, 480).getContext('2d'); +var c4game = require('@kenrick95/c4'); + +const { GameBase, PlayerAi, Player, BoardPiece } = c4game; + +class Game extends GameBase { + constructor(players, canvas) { + super(players, canvas) + } +} + +class PlayerHuman extends Player { + constructor(boardPiece, canvas) { + super(boardPiece, canvas) + this.clickPromiseResolver = null + } + + doAction(column) { + if (this.clickPromiseResolver && 0 <= column && column < Board.COLUMNS) { + this.clickPromiseResolver(column) + } + } + + async getAction(board) { + return new Promise(r => (this.clickPromiseResolver = r)) + } +} + + +const game = new Game( + [ + new PlayerHuman(BoardPiece.PLAYER_1, canvas), + new PlayerAi(BoardPiece.PLAYER_2, canvas), + ], + canvas +); +game.start(); diff --git a/yarn.lock b/yarn.lock index dd2fb32..6093920 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@kenrick95/c4@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@kenrick95/c4/-/c4-3.0.2.tgz#6f02fa5cc048f8ed4a975b051186735a2ff099df" +"@kenrick95/c4@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@kenrick95/c4/-/c4-3.0.4.tgz#828092e051623884459498aff44001cc1e9f8e40" dependencies: es6-promise "^4.2.5" From 914ebe43a3777cb3047a7299d181955171b30011 Mon Sep 17 00:00:00 2001 From: Kenrick Date: Tue, 6 Nov 2018 10:18:59 +0800 Subject: [PATCH 3/5] Update dependency --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index dd6dd54..fc0b6e6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Connect Four bot", "main": "app.js", "dependencies": { - "@kenrick95/c4": "^3.0.2", + "@kenrick95/c4": "^3.0.5", "botbuilder": "^3.15.0", "dotenv": "^2.0.0", "pureimage": "^0.1.6", diff --git a/yarn.lock b/yarn.lock index dd2fb32..6be450a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@kenrick95/c4@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@kenrick95/c4/-/c4-3.0.2.tgz#6f02fa5cc048f8ed4a975b051186735a2ff099df" +"@kenrick95/c4@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@kenrick95/c4/-/c4-3.0.5.tgz#09eaa4760df4d4017c7455843355f1575a31d5f0" dependencies: es6-promise "^4.2.5" From 681430cc664779bc0989c75da42161bc6bbd2114 Mon Sep 17 00:00:00 2001 From: Kenrick Date: Tue, 6 Nov 2018 10:40:23 +0800 Subject: [PATCH 4/5] It works! --- test.js | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/test.js b/test.js index 83995a9..bf714e6 100644 --- a/test.js +++ b/test.js @@ -1,38 +1,27 @@ +// Note: getBoardScale reads from "window.innerWidth" +global.window = { + innerWidth: 1024 +}; var PImage = require('pureimage'); var canvas = PImage.make(640, 480).getContext('2d'); var c4game = require('@kenrick95/c4'); -const { GameBase, PlayerAi, Player, BoardPiece } = c4game; +const { GameBase, PlayerAi, PlayerHuman, BoardPiece, BoardBase } = c4game; class Game extends GameBase { - constructor(players, canvas) { - super(players, canvas) + constructor(players, board) { + super(players, board); } -} - -class PlayerHuman extends Player { - constructor(boardPiece, canvas) { - super(boardPiece, canvas) - this.clickPromiseResolver = null - } - - doAction(column) { - if (this.clickPromiseResolver && 0 <= column && column < Board.COLUMNS) { - this.clickPromiseResolver(column) - } - } - - async getAction(board) { - return new Promise(r => (this.clickPromiseResolver = r)) + afterMove() { + // no-op } } - +const board = new BoardBase(canvas); +const playerHuman = new PlayerHuman(BoardPiece.PLAYER_1); const game = new Game( - [ - new PlayerHuman(BoardPiece.PLAYER_1, canvas), - new PlayerAi(BoardPiece.PLAYER_2, canvas), - ], - canvas + [playerHuman, new PlayerAi(BoardPiece.PLAYER_2)], + board ); game.start(); +playerHuman.doAction(4); From 4313a040221ed8a459740f26e0d596374442a755 Mon Sep 17 00:00:00 2001 From: Kenrick Date: Tue, 6 Nov 2018 22:10:07 +0800 Subject: [PATCH 5/5] tmp --- app.js | 303 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 181 insertions(+), 122 deletions(-) diff --git a/app.js b/app.js index d577991..bd298d0 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,20 @@ var restify = require('restify'); var builder = require('botbuilder'); var c4game = require('@kenrick95/c4'); +var PImage = require('pureimage'); +const image = PImage.make(640, 480); +var canvas = image.getContext('2d'); + +const { GameBase, PlayerAi, PlayerHuman, BoardPiece, BoardBase } = c4game; + +class Game extends GameBase { + constructor(players, board) { + super(players, board); + } + afterMove() { + // no-op + } +} require('dotenv').config(); @@ -11,14 +25,14 @@ require('dotenv').config(); // Setup Restify Server var server = restify.createServer(); var port = process.env.PORT || 3978; -server.listen(port, function () { - console.log('%s listening to %s', server.name, server.url); +server.listen(port, function() { + console.log('%s listening to %s', server.name, server.url); }); - + // Create chat bot var connector = new builder.ChatConnector({ - appId: process.env.MICROSOFT_APP_ID, - appPassword: process.env.MICROSOFT_APP_PASSWORD + appId: process.env.MICROSOFT_APP_ID, + appPassword: process.env.MICROSOFT_APP_PASSWORD }); server.post('/c4bot/api/messages', connector.listen()); @@ -28,144 +42,189 @@ var intents = new builder.IntentDialog(); bot.dialog('/', intents); intents.matches(/^(new|play|start)/i, [ - function (session) { - session.beginDialog("/new"); - } + function(session) { + session.beginDialog('/new'); + } ]); intents.matches(/^(help|how|guide)/i, [ - function (session) { - session.beginDialog("/help"); - } + function(session) { + session.beginDialog('/help'); + } ]); intents.onDefault([ - function (session) { - session.send("Hi there, I am c4bot, a bot that plays Connect Four game with you. Click here https://bots.botframework.com/bot?id=c4bot to find out more about me and my policies."); - builder.Prompts.choice(session, "What would you like to do?", "New game|Help", { - retryPrompt: "Sorry, I didn't understand that. \"New game\" or \"help\"?" - }); - }, - function (session, results) { - if (results.response && results.response.entity.toLowerCase() != 'help') { - session.beginDialog('/new'); - } else { - session.beginDialog('/help'); - } + function(session) { + session.send( + 'Hi there, I am c4bot, a bot that plays Connect Four game with you. Click here https://bots.botframework.com/bot?id=c4bot to find out more about me and my policies.' + ); + builder.Prompts.choice( + session, + 'What would you like to do?', + 'New game|Help', + { + retryPrompt: 'Sorry, I didn\'t understand that. "New game" or "help"?' + } + ); + }, + function(session, results) { + if (results.response && results.response.entity.toLowerCase() != 'help') { + session.beginDialog('/new'); + } else { + session.beginDialog('/help'); } + } ]); - bot.dialog('/help', [ - function (session) { - session.send("I will play a Connect Four game with you."); - var msg = new builder.Message(session) - .text("A GIF worth a thousand words.") - .attachments([{ - contentType: "image/gif", - contentUrl: "https://upload.wikimedia.org/wikipedia/commons/a/ad/Connect_Four.gif" - }]); - session.send(msg); - session.send("If you encounter any issue, please report it to my GitHub repo: https://github.com/kenrick95/c4bot/issues"); - session.endDialog(); - } + function(session) { + session.send('I will play a Connect Four game with you.'); + var msg = new builder.Message(session) + .text('A GIF worth a thousand words.') + .attachments([ + { + contentType: 'image/gif', + contentUrl: + 'https://upload.wikimedia.org/wikipedia/commons/a/ad/Connect_Four.gif' + } + ]); + session.send(msg); + session.send( + 'If you encounter any issue, please report it to my GitHub repo: https://github.com/kenrick95/c4bot/issues' + ); + session.endDialog(); + } ]); bot.dialog('/new', [ - function (session) { - session.send("Game started..."); - // const board = new Board(canvas) - // board.render() - // Game.initGameLocalAi() - - var game = new c4game.Game(); - session.userData.gameState = game.gameState; - session.send(canvasToMessage(session, game.canvas, 0)); - session.beginDialog("/move"); - }, - function (session) { - var wonPlayer = session.userData.gameState.wonPlayer; - var gameEnded = session.userData.gameState.won; - - var msg = ""; - if (gameEnded) { - msg = "It's a draw"; - if (wonPlayer > 0) { - msg = "You won!"; - } else if (wonPlayer < 0) { - msg = "Computer won!"; - } - msg += "\n"; - } - - session.send(); - session.endDialog(msg + "Game over. Thank you for playing.\nSay 'hi' again to restart the game."); + function(session) { + session.send('Game started...'); + + const board = new BoardBase(canvas); + const game = new Game( + [new PlayerHuman(BoardPiece.PLAYER_1), new PlayerAi(BoardPiece.PLAYER_2)], + board + ); + game.start(); + session.userData.gameState = { + board: game.board, + players: game.players, + currentPlayerId: game.currentPlayerId, + isMoveAllowed: game.isMoveAllowed, + isGameWon: game.isGameWon + }; + session.send(canvasToMessage(session, board, 0)); + session.beginDialog('/move'); + }, + function(session) { + var wonPlayer = session.userData.gameState.currentPlayerId; + var gameEnded = session.userData.gameState.isGameWon; + + var msg = ''; + if (gameEnded) { + msg = "It's a draw"; + if (wonPlayer > 0) { + msg = 'You won!'; + } else if (wonPlayer < 0) { + msg = 'Computer won!'; + } + msg += '\n'; } + + session.send(); + session.endDialog( + msg + + "Game over. Thank you for playing.\nSay 'hi' again to restart the game." + ); + } ]); bot.dialog('/invalid', [ - function (session) { - session.send("Invalid move, please choose another column"); - session.replaceDialog("/move"); - } + function(session) { + session.send('Invalid move, please choose another column'); + session.replaceDialog('/move'); + } ]); bot.dialog('/move', [ - function (session) { - builder.Prompts.text(session, "Please choose a column (1-7)"); - }, - function (session, results) { - if (/^(end|restart)/i.test(results.response)) { - session.endDialog(); - return; - } - - var choice = builder.EntityRecognizer.parseNumber(results.response) - 1; - if (isNaN(choice) || choice < 0 || choice > 6) { - session.replaceDialog("/invalid"); - return; - } - - var game = new c4game.Game(); - // restore state - game.gameState = session.userData.gameState; - - // resume game - game.gameState.paused = false; + function(session) { + builder.Prompts.text(session, 'Please choose a column (1-7)'); + }, + function(session, results) { + if (/^(end|restart)/i.test(results.response)) { + session.endDialog(); + return; + } - // do action - var valid = game.action(choice, function () { - // print state (after user move) - session.send(canvasToMessage(session, game.canvas, 1)); - session.sendBatch(); + var choice = builder.EntityRecognizer.parseNumber(results.response) - 1; + if (isNaN(choice) || choice < 0 || choice > 6) { + session.replaceDialog('/invalid'); + return; + } - if (!game.gameState.won) { - this.ai.bind(this)(-1); + const board = new BoardBase(canvas); + const game = new Game( + [new PlayerHuman(BoardPiece.PLAYER_1), new PlayerAi(BoardPiece.PLAYER_2)], + board + ); + // restore state + game.board = session.userData.board; + game.players = session.userData.players; + game.currentPlayerId = session.userData.currentPlayerId; + game.isMoveAllowed = session.userData.board; + game.isGameWon = session.userData.isGameWon; + + // do action + // TODO: Idk what this piece of code is about ... + var valid = game.action( + choice, + function() { + // print state (after user move) + session.send(canvasToMessage(session, game.canvas, 1)); + session.sendBatch(); + + if (!game.gameState.won) { + this.ai.bind(this)(-1); + + // print state (after AI move) + session.send(canvasToMessage(session, game.canvas, -1)); + } + }.bind(game) + ); - // print state (after AI move) - session.send(canvasToMessage(session, game.canvas, -1)); - } - - }.bind(game)); + if (valid < 1) { + session.replaceDialog('/invalid'); + return; + } - if (valid < 1) { - session.replaceDialog("/invalid"); - return; - } - - // save state - session.userData.gameState = game.gameState; - - // next move - if (!session.userData.gameState.won) { - session.replaceDialog("/move"); - } else { - session.endDialog(); - } + // save state + session.userData.gameState = { + board: game.board, + players: game.players, + currentPlayerId: game.currentPlayerId, + isMoveAllowed: game.isMoveAllowed, + isGameWon: game.isGameWon + }; + + // next move + if (!session.userData.gameState.won) { + session.replaceDialog('/move'); + } else { + session.endDialog(); } + } ]); function canvasToMessage(session, canvas, player) { - return new builder.Message(session) - .text((player != 0) ? ((player > 0) ? "You moved 🔴" : "Computer moved 🔵") : "Waiting for your move") - .attachments([{ - contentType: "image/png", - contentUrl: canvas.toDataURL() - }]); -} \ No newline at end of file + return new builder.Message(session) + .text( + player != 0 + ? player > 0 + ? 'You moved 🔴' + : 'Computer moved 🔵' + : 'Waiting for your move' + ) + .attachments([ + { + contentType: 'image/png', + // TODO: Convert this "stream" to "base64" + contentUrl: PImage.encodePNGToStream(image) + } + ]); +}