From 22870f4c027a2e5032213acab94f578257025b02 Mon Sep 17 00:00:00 2001 From: Dragon Fire Date: Tue, 17 Mar 2020 16:55:24 -0400 Subject: [PATCH] Connect Four Command --- README.md | 4 +- commands/mp-games/connect-four.js | 137 ++++++++++++++++++++++++++++++ package.json | 4 +- 3 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 commands/mp-games/connect-four.js diff --git a/README.md b/README.md index a2424058..c16ca1bd 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ don't grant that permission. ## Commands -Total: 368 +Total: 369 ### Utility: @@ -363,6 +363,7 @@ Total: 368 * **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. * **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. * **quiz-duel:** Answer a series of quiz questions against an opponent. @@ -718,6 +719,7 @@ here. - [Hasbro](https://shop.hasbro.com/en-us) * bro-hoof ([Original "My Little Pony: Friendship is Magic" Show](https://mylittlepony.hasbro.com/en-us)) * brony-speak ([Original "My Little Pony: Friendship is Magic" Show](https://mylittlepony.hasbro.com/en-us)) + * connect-four (Original "Connect Four" Game) * scrabble-score ([Original Scrabble Game](https://scrabble.hasbro.com/en-us)) - [Hastebin](https://hastebin.com/about.md) * generate-commands (API) diff --git a/commands/mp-games/connect-four.js b/commands/mp-games/connect-four.js new file mode 100644 index 00000000..7b6fa88f --- /dev/null +++ b/commands/mp-games/connect-four.js @@ -0,0 +1,137 @@ +const Command = require('../../structures/Command'); +const { stripIndents } = require('common-tags'); +const { verify } = require('../../util/Util'); +const blankEmoji = '⚪️'; +const playerOneEmoji = '🔴'; +const playerTwoEmoji = '🟡'; +const nums = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣']; + +module.exports = class ConnectFourCommand extends Command { + constructor(client) { + super(client, { + name: 'connect-four', + aliases: ['connect-4'], + group: 'mp-games', + memberName: 'connect-four', + description: 'Play a game of Connect Four with another user.', + guildOnly: true, + credit: [ + { + name: 'Hasbro', + url: 'https://shop.hasbro.com/en-us', + reason: 'Original "Connect Four" Game' + } + ], + 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(); + let userTurn = true; + let winner = null; + while (!winner && board.some(row => row.includes(null))) { + const user = userTurn ? msg.author : opponent; + const sign = userTurn ? 'user' : 'oppo'; + await msg.say(stripIndents` + ${user}, which row do you pick? Type \`end\` to forefeit. + + ${this.displayBoard(board)} + ${nums.join('')} + `); + const filter = res => { + const choice = res.content; + if (choice.toLowerCase() === 'end') return true; + if (!board[Number.parseInt(choice, 10) - 1].includes(null)) return false; + return res.author.id === user.id; + }; + const turn = await msg.channel.awaitMessages(filter, { + max: 1, + time: 30000 + }); + 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 i = Number.parseInt(choice, 10) - 1; + board[i][board[i][6]] = sign; + board[i][6] += 1; + if (this.verifyWin(board)) winner = userTurn ? msg.author : opponent; + 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; + } + } + + checkLine(a, b, c, d) { + return (a !== null) && (a === b) && (a === c) && (a === d); + } + + verifyWin(bd) { + for (let r = 0; r < 3; r++) { + for (let c = 0; c < 7; c++) { + if (this.checkLine(bd[r][c], bd[r + 1][c], bd[r + 2][c], bd[r + 3][c])) return bd[r][c]; + } + } + for (let r = 0; r < 6; r++) { + for (let c = 0; c < 4; c++) { + if (this.checkLine(bd[r][c], bd[r][c + 1], bd[r][c + 2], bd[r][c + 3])) return bd[r][c]; + } + } + for (let r = 0; r < 3; r++) { + for (let c = 0; c < 4; c++) { + if (this.checkLine(bd[r][c], bd[r + 1][c + 1], bd[r + 2][c + 2], bd[r + 3][c + 3])) return bd[r][c]; + } + } + for (let r = 3; r < 6; r++) { + for (let c = 0; c < 4; c++) { + if (this.checkLine(bd[r][c], bd[r - 1][c + 1], bd[r - 2][c + 2], bd[r - 3][c + 3])) return bd[r][c]; + } + } + return null; + } + + generateBoard() { + const arr = []; + for (let i = 0; i < 7; i++) { + arr.push([null, null, null, null, null, null, 0]); + } + return arr; + } + + displayBoard(board) { + return board.map(row => row.map(piece => { + if (piece === 'user') return playerOneEmoji; + if (piece === 'oppo') return playerTwoEmoji; + return blankEmoji; + }).join(' ')).join('\n'); + } +}; diff --git a/package.json b/package.json index 9c7a6cdb..faa5f5d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "112.8.0", + "version": "112.9.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { @@ -59,7 +59,7 @@ "devDependencies": { "eslint": "^6.8.0", "eslint-config-amber": "^2.0.2", - "eslint-plugin-json": "^2.1.0" + "eslint-plugin-json": "^2.1.1" }, "eslintConfig": { "extends": "amber",