From 537de46ed4859502dc3958a385258f21d806ba47 Mon Sep 17 00:00:00 2001 From: Dragon Fire Date: Thu, 9 Apr 2020 17:24:36 -0400 Subject: [PATCH] Dots and Boxes Command --- .gitignore | 2 - README.md | 3 +- commands/games-mp/dots-and-boxes.js | 162 ++++++++++++++++++++++++++++ package.json | 2 +- 4 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 commands/games-mp/dots-and-boxes.js diff --git a/.gitignore b/.gitignore index 1c666f5b..595778e4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,3 @@ package-lock.json logs/ *.log .env - -commands/games-mp/dots-and-boxes.js diff --git a/README.md b/README.md index 2f0342a4..c7b61ab3 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ in the appropriate channel's topic to use it. ## Commands -Total: 382 +Total: 383 ### Utility: @@ -388,6 +388,7 @@ Total: 382 * **balloon-pop:** Don't let yourself be the last one to pump the balloon before it pops! * **battle:** Engage in a turn-based battle against another user or the AI. * **connect-four:** Play a game of Connect Four with another user. +* **dots-and-boxes:** Play a game of Dots and Boxes with another user. * **emoji-emoji-revolution:** Can you type arrow emoji faster than anyone else has ever typed them before? * **gunfight:** Engage in a western gunfight against another user. High noon. * **pick-a-number:** Two players pick a number between 1 and 10. Whoever's closer wins. diff --git a/commands/games-mp/dots-and-boxes.js b/commands/games-mp/dots-and-boxes.js new file mode 100644 index 00000000..156c1693 --- /dev/null +++ b/commands/games-mp/dots-and-boxes.js @@ -0,0 +1,162 @@ +const Command = require('../../structures/Command'); +const { stripIndents } = require('common-tags'); +const { verify } = require('../../util/Util'); +const squareIDs = [5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18, 20, 21, 22, 23]; + +module.exports = class DotsAndBoxesCommand extends Command { + constructor(client) { + super(client, { + name: 'dots-and-boxes', + aliases: ['dots-boxes', 'dot-box', 'dot-and-box', 'territory-capture'], + group: 'games-mp', + memberName: 'dots-and-boxes', + description: 'Play a game of Dots and Boxes with another user.', + guildOnly: true, + args: [ + { + key: 'opponent', + prompt: 'What user would you like to challenge?', + type: 'user' + } + ] + }); + } + + async run(msg, { opponent }) { + if (opponent.bot) return msg.reply('Bots may not be played against.'); + if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); + const current = this.client.games.get(msg.channel.id); + if (current) return msg.reply(`Please wait until the current game of \`${current.name}\` is finished.`); + this.client.games.set(msg.channel.id, { name: this.name }); + try { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) { + this.client.games.delete(msg.channel.id); + return msg.say('Looks like they declined...'); + } + const board = this.generateBoard(); + const taken = []; + const owned = {}; + let userTurn = true; + let winner = null; + while (taken.length < 24) { + const user = userTurn ? msg.author : opponent; + await msg.say(stripIndents` + ${user}, which connection do you pick? Type \`end\` to forefeit. + _Format like \`1-2\` or \`0-5\`. Any two spaces bordering **vertical or horizontal**._ + + P1: ${msg.author.tag} | P2: ${opponent.tag} + ${this.displayBoard(board, taken, owned)} + `); + const filter = res => { + if (res.author.id !== user.id) return false; + const choice = res.content; + if (choice.toLowerCase() === 'end') return true; + const matched = choice.match(/([0-9]+)\-([0-9]+)/); + if (!match) return false; + let first = Number.parseInt(matched[1], 10); + let second = Number.parseInt(matched[2], 10); + if (first === second) return false; + if (second > first) { + const temp = first; + first = second; + second = temp; + } + if (first > 24 || second > 24 || first < 0 || second < 0) return false; + const column1 = first % 5; + const column2 = second % 5; + if (second !== first + 1 && column1 !== column2) { + const row1 = Math.floor(first / 5); + const row2 = Math.floor(second / 5); + if (row2 !== row1 - 1) return false; + return !taken.includes(`${first}-${second}`); + } + return !taken.includes(`${first}-${second}`); + }; + const turn = await msg.channel.awaitMessages(filter, { + max: 1, + time: 60000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + userTurn = !userTurn; + continue; + } + const choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + const matched = choice.match(/([0-9]+)\-([0-9]+)/); + let first = Number.parseInt(matched[1], 10); + let second = Number.parseInt(matched[2], 10); + if (second > first) { + const temp = first; + first = second; + second = temp; + } + taken.push(`${first}-${second}`); + const newSquare = this.calcNewSquare(taken, owned); + if (newSquare) { + owned[newSquare] = userTurn ? 'P1' : 'P2'; + await msg.say(`${user}, great job! Keep going until you can\'t make any more!`); + } else { + userTurn = !userTurn; + } + } + this.client.games.delete(msg.channel.id); + return msg.say(winner ? `Congrats, ${winner}!` : 'Looks like it\'s a draw...'); + } catch (err) { + this.client.games.delete(msg.channel.id); + throw err; + } + } + + calcSquare(num, taken) { + return taken.includes(`${num}-${num + 1}`) + && taken.includes(`${num}-${num + 5}`) + && taken.includes(`${num + 1}-${num + 1 + 5}`) + && taken.includes(`${num + 5}-${num + 1 + 5}`); + } + + calcNewSquare(taken, owned) { + for (const square of squareIDs) { + if (owned[square]) continue; + if (this.calcSquare(square, taken)) return square; + } + return null; + } + + generateBoard() { + const arr = []; + for (let i = 0; i < 5; i++) { + const row = []; + for (let j = 0 + (i * 5); j < 5 + (i * 5); j++) row.push(j); + arr.push(row); + } + return arr; + } + + displayBoard(board, taken, owned) { + const displayed = []; + board.map((values, row) => { + if (row !== 0) { + let takenMids = ''; + for (let i = 0 + (row * 5); i < 5 + (row * 5); i++) { + if (taken.includes(`${i - 5}-${i}`)) takenMids += '||'; + else takenMids += ' '; + takenMids += owned[i] || ' '; + } + displayed.push(takenMids); + } + displayed.push(values.map(slot => { + let val = slot.toString().padStart(2, '0'); + if (taken.includes(`${slot}-${slot + 1}`)) val += '=='; + else val += ' '; + return val; + }).join('')); + }); + return displayed.join('\n'); + } +}; diff --git a/package.json b/package.json index 8c497a51..5c28e663 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "112.19.15", + "version": "112.20.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": {