diff --git a/.gitignore b/.gitignore index 9b1028d8..e1806d7a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,3 @@ command-leaderboard.json *.traineddata # In-Development Commands -commands/games-mp/domineering.js diff --git a/commands/games-mp/domineering.js b/commands/games-mp/domineering.js new file mode 100644 index 00000000..e3ad9de9 --- /dev/null +++ b/commands/games-mp/domineering.js @@ -0,0 +1,155 @@ +const Command = require('../../structures/Command'); +const { stripIndents } = require('common-tags'); +const { verify } = require('../../util/Util'); +const blankEmoji = '⬜'; +const nums = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟']; +const turnRegex = /^(\d+), ?(\d+)/i; + +module.exports = class DomineeringCommand extends Command { + constructor(client) { + super(client, { + name: 'domineering', + aliases: ['domineer', 'cross-cram', 'stop-gate'], + group: 'games-mp', + memberName: 'domineering', + description: 'Play a game of Domineering with another user.', + guildOnly: true, + args: [ + { + key: 'opponent', + prompt: 'What user would you like to challenge?', + type: 'user' + }, + { + key: 'size', + prompt: 'What board size do you want to use?', + type: 'integer', + min: 3, + max: 10, + default: 5 + } + ] + }); + } + + async run(msg, { opponent, size }) { + 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(size); + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + const userEmoji = '🟥'; + const oppoEmoji = '🟦'; + while (!winner) { + const user = userTurn ? msg.author : opponent; + await msg.say(stripIndents` + ${user}, at what coordinates do you want to place your block (ex. 1,1)? Type \`end\` to forefeit. + Your pieces are **${userTurn ? 'vertical' : 'horizontal'}**. + + ${this.displayBoard(board, userEmoji, oppoEmoji)} + `); + const possibleMoves = this.possibleMoves(board, userTurn); + const filter = res => { + if (res.author.id !== user.id) return false; + const pick = res.content; + if (pick.toLowerCase() === 'end') return true; + const coordPicked = pick.match(turnRegex); + if (!coordPicked) return false; + const x = Number.parseInt(coordPicked[2], 10); + const y = Number.parseInt(coordPicked[3], 10); + if (x > size || y > size || x < 1 || y < 1) return false; + if (!possibleMoves.includes(`${x - 1},${y - 1}`)) return false; + return true; + }; + const turn = await msg.channel.awaitMessages(filter, { + max: 1, + time: 60000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + if (lastTurnTimeout) { + winner = 'time'; + break; + } else { + lastTurnTimeout = true; + userTurn = !userTurn; + continue; + } + } + const choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + const matched = choice.match(turnRegex); + const x = Number.parseInt(matched[1], 10); + const y = Number.parseInt(matched[2], 10); + board[y - 1][x - 1] = userTurn ? 'U' : 'O'; + board[userTurn ? y : y - 1][userTurn ? x - 1 : x] = userTurn ? 'U' : 'O'; + userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; + const oppoPossible = this.possibleMoves(board, userTurn); + if (!oppoPossible.length) { + winner = userTurn ? opponent : msg.author; + break; + } + } + this.client.games.delete(msg.channel.id); + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(`Congrats, ${winner}! Your opponent has no possible moves left!`); + } catch (err) { + this.client.games.delete(msg.channel.id); + throw err; + } + } + + possibleMoves(board, userTurn) { + const possibleMoves = []; + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; i++) { + if (board[i][j]) continue; + if (board[userTurn ? i + 1 : i][userTurn ? j : j + 1]) continue; + possibleMoves.push(`${i},${j}`); + } + } + return possibleMoves; + } + + generateBoard(size) { + const arr = []; + for (let i = 0; i < size; i++) { + const row = []; + for (let j = 0; j < size; j++) row.push(null); + arr.push(row); + } + return arr; + } + + displayBoard(board, userColor, oppoColor) { + let str = ''; + str += '⬛'; + str += nums.slice(0, board.length).join(''); + str += '\n'; + for (let i = 0; i < board.length; i++) { + str += nums[i]; + board[i].forEach((item, j) => { + if (item === 'U') str += userColor; + else if (item === 'O') str += oppoColor; + else str += blankEmoji; + }); + str += '\n'; + } + return str; + } +}; diff --git a/commands/games-mp/imposter.js b/commands/games-mp/imposter.js index 13ea77b5..0663504e 100644 --- a/commands/games-mp/imposter.js +++ b/commands/games-mp/imposter.js @@ -152,6 +152,7 @@ module.exports = class ImposterCommand extends Command { winners.push(players.find(player => player.imposter).user.tag); break; } + if (lastTurnTimeout) lastTurnTimeout = false; } this.client.games.delete(msg.channel.id); return msg.say(`Congrats, ${list(winners)}! The kill word was **${word}**.`); diff --git a/commands/games-mp/island.js b/commands/games-mp/island.js index d8304ef8..2a0bd386 100644 --- a/commands/games-mp/island.js +++ b/commands/games-mp/island.js @@ -105,6 +105,7 @@ module.exports = class IslandCommand extends Command { `); if (playersLeft.size > 2) await delay(30000); else break; + if (lastTurnTimeout) lastTurnTimeout = false; } this.client.games.delete(msg.channel.id); const winners = players.filter(player => playersLeft.has(player.id)); diff --git a/commands/games-mp/jenga.js b/commands/games-mp/jenga.js index 121c324a..c263086e 100644 --- a/commands/games-mp/jenga.js +++ b/commands/games-mp/jenga.js @@ -100,6 +100,7 @@ module.exports = class JengaCommand extends Command { await msg.say(`${opponent.bot && !userTurn ? `I pick ${i + 1}. ` : ''}Thankfully, the tower stands.`); board.shift(); userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; } this.client.games.delete(msg.channel.id); if (winner === 'time') return msg.say('Game ended due to inactivity.'); diff --git a/commands/games-mp/nim.js b/commands/games-mp/nim.js index a3a724ef..3795eae4 100644 --- a/commands/games-mp/nim.js +++ b/commands/games-mp/nim.js @@ -137,6 +137,7 @@ module.exports = class NimCommand extends Command { break; } userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; } this.client.games.delete(msg.channel.id); if (winner === 'time') return msg.say('Game ended due to inactivity.'); diff --git a/package.json b/package.json index e69beb00..a5b431ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "126.12.2", + "version": "126.13.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": {