Files
2024-05-21 21:02:12 -04:00

152 lines
4.7 KiB
JavaScript

const Command = require('../../framework/Command');
const { Collection } = require('@discordjs/collection');
const { stripIndents } = require('common-tags');
const { awaitPlayers, reactIfAble } = require('../../util/Util');
const nums = require('../../assets/json/bingo');
const { SUCCESS_EMOJI_ID } = process.env;
const rows = Object.keys(nums);
const callNums = Array.from({ length: 75 }, (v, i) => i + 1);
module.exports = class BingoCommand extends Command {
constructor(client) {
super(client, {
name: 'bingo',
group: 'games-mp',
description: 'Play bingo with up to 99 other users.',
guildOnly: true,
game: true,
args: [
{
key: 'playersCount',
type: 'integer',
min: 1,
max: 100
}
]
});
}
async run(msg, { playersCount }) {
const awaitedPlayers = await awaitPlayers(msg, playersCount, 1, this.client.blacklist.user);
if (!awaitedPlayers) return msg.say('Game could not be started...');
const players = new Collection();
for (const player of awaitedPlayers) {
players.set(player, {
board: this.generateBoard(),
id: player,
user: await this.client.users.fetch(player)
});
}
let winner = null;
const called = ['FR'];
while (!winner) {
const validNums = callNums.filter(num => !called.includes(num));
if (!validNums.length) break;
const picked = validNums[Math.floor(Math.random() * validNums.length)];
called.push(picked);
for (const player of players.values()) {
try {
await player.user.send(stripIndents`
**${this.findRowValue(picked)} ${picked}** was called in ${msg.channel}.
${this.generateBoardDisplay(player.board, called)}
`);
} catch {
await msg.say(`${player.user}, I couldn't send your board! Turn on DMs!`);
}
}
await msg.say(stripIndents`
**${this.findRowValue(picked)} ${picked}**!
Check your DMs for your board. If you have bingo, type \`bingo\`!
If you wish to drop out, type \`leave game\`.
_Next number will be called in 20 seconds. ${validNums.length - 1} numbers left._
`);
const filter = res => {
if (!players.has(res.author.id)) return false;
if (res.content.toLowerCase() === 'leave game') {
players.delete(res.author.id);
reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅');
if (!players.size) return true;
return false;
}
if (res.content.toLowerCase() !== 'bingo') return false;
if (!this.checkBingo(players.get(res.author.id).board, called)) {
msg.say(`${res.author}, you don't have bingo, liar.`).catch(() => null);
return false;
}
return true;
};
const bingo = await msg.channel.awaitMessages({ filter, max: 1, time: 20000 });
if (!players.size) {
winner = 0;
break;
}
if (!bingo.size) continue;
winner = bingo.first().author;
}
if (winner === 0) return msg.say('Everyone dropped out...');
if (!winner) return msg.say('I called the entire board, but no one said bingo...');
return msg.say(`Congrats, ${winner}!`);
}
generateBoard() {
const result = [];
for (const [rowID, values] of Object.entries(nums)) {
const picked = [];
for (let i = 0; i < 5; i++) {
const valid = values.filter(value => !picked.includes(value));
picked.push(valid[Math.floor(Math.random() * valid.length)]);
}
const sorted = picked.sort((a, b) => a - b);
if (rowID === 'N') sorted[2] = 'FR';
result.push(sorted);
}
return result;
}
generateBoardDisplay(board, called) {
const mapped = board.map((values, i) => {
const row = rows[i];
const mapVal = values.map(value => {
if (called.includes(value) || value === 'FR') return 'XX';
return value.toString().padStart(2, '0');
}).join(' | ');
return `${row} | ${mapVal}`;
}).join('\n--------------------------\n');
return stripIndents`
\`\`\`
${mapped}
\`\`\`
`;
}
findRowValue(num) {
if (nums.B.includes(num)) return 'B';
if (nums.I.includes(num)) return 'I';
if (nums.N.includes(num)) return 'N';
if (nums.G.includes(num)) return 'G';
if (nums.O.includes(num)) return 'O';
return null;
}
checkLine(called, a, b, c, d, e) {
return called.includes(a)
&& called.includes(b)
&& called.includes(c)
&& called.includes(d)
&& called.includes(e);
}
checkBingo(bd, ca) {
for (let r = 0; r < rows.length; r++) {
if (this.checkLine(ca, bd[r][0], bd[r][1], bd[r][2], bd[r][3], bd[r][4])) return true;
}
for (let c = 0; c < rows.length; c++) {
if (this.checkLine(ca, bd[0][c], bd[1][c], bd[2][c], bd[3][c], bd[4][c])) return true;
}
if (this.checkLine(ca, bd[0][0], bd[1][1], bd[2][2], bd[3][3], bd[4][4])) return true;
if (this.checkLine(ca, bd[4][0], bd[3][1], bd[2][2], bd[1][3], bd[0][4])) return true;
return false;
}
};