more gaming

master
Jan 2021-03-07 00:04:30 +01:00
parent 93f34d1681
commit f6fe3ca2e9
2 changed files with 259 additions and 0 deletions

249
commands/connect4.js Normal file
View File

@ -0,0 +1,249 @@
const Revolt = require('revolt.js');
const { client, config } = require('..');
const getUser = require('../util/get_user');
/**
* @type {Map<String, { createdBy: String, opponent: String, messageID: String }>}
*/
let sessions = new Map();
const gameEndEmitter = new (require('events'));
let playerSymbol = 'ඞ';
let invisColor = '242424';
// Note to myself: https://katex.org/docs/supported.html
module.exports.meta = {
name: 'connect4',
aliases: [ 'connect-4' ],
description: 'Play connect 4.'
}
/**
*
* @param { Revolt.Message } message
* @param { string[] } args
*/
module.exports.run = async (message, args) => new Promise(async (resolve, reject) => {
if ((await client.channels.fetch(message.channel)).channel_type !== 'Group')
return client.channels.sendMessage(message.channel, ':x: You can\'t play here.');
if (args[0]?.toLowerCase() === 'stop' || args[0]?.toLowerCase() === 'end') {
if (!sessions.get(message.channel)?.messageID)
return client.channels.sendMessage(message.channel, `:x: No ongoing match found.`);
sessions.delete(message.channel);
gameEndEmitter.emit(message.channel);
await client.channels.sendMessage(message.channel, `:white_check_mark: Game stopped`);
return;
}
if (sessions.get(message.channel))
return client.channels.sendMessage(message.channel, `:x: There's an ongoing match ` +
`[here](https://app.revolt.chat/channel/${message.channel}/${sessions.get(message.channel).messageID}), ` +
`please wait for it to finish or use \`${config.prefix}connect4 stop\`.`);
if (!args[0])
return client.channels.sendMessage(message.channel, ':x: Please specify the ID or username of your opponent.');
let opponent = await getUser(args.join(' '));
if (!opponent)
return client.channels.sendMessage(message.channel, 'I can\'t find that user.');
if (opponent._id === client.user._id)
return client.channels.sendMessage(message.channel, ':x: You can\'t play against me. (yet)');
if (opponent._id === message.author)
return client.channels.sendMessage(message.channel, ':x: You can\'t play against yourself, dumbus');
if (client.channels.get(message.channel).recipients?.indexOf(opponent._id) === -1)
return client.channels.sendMessage(message.channel, ':x: That user is not in this group.');
let field = [
[' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' '],
];
let red = await client.users.fetch(message.author);
let blue = opponent;
let currentPlayer = 'R';
let winner = null;
if (Math.random() > 0.5) [red, blue] = [blue, red];
if (Math.random() > 0.5) currentPlayer = 'B';
let text;
let msg;
let resendTip = true;
const updateMsg = async () => {
text = `Blue: <@${blue._id}> \u200b Red: <@${red._id}>\n`
+ `${winner === null ? `**${currentPlayer === 'R' ? 'Red' : 'Blue'}'s turn**` : ''}\n\u200b\n`
+ '| 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n'
+ '|:-:|:-:|:-:|:-:|:-:|:-:|:-:|\n';
field.forEach(row => {
row.forEach(p => {
let color
if (p !== ' ')
color = p === 'B' ? 'blue' : 'red';
else
color = invisColor;
text += `| $\\color{${color}}\\huge\\text{\u200b ${playerSymbol} \u200b}$`;
});
text += '|\n';
});
if (winner === false) {
text += '\n\u200b\n# No winner'
} else if (winner !== null) {
if (Math.random() > 0.8) {
text += `\n\u200b\n# $\\color{${winner === 'B' ? 'red' : 'blue'}}\\huge\\text{𓀐}$ ` +
`$\\color{${winner === 'B' ? 'blue' : 'red'}}\\huge\\text{𓂺ඞ}$\n` +
`# <@${(winner === 'R' ? blue : red)._id}> got fucked by <@${(winner === 'R' ? red : blue)._id}>!`;
} else {
text += `\n\u200b\n# <@${(winner === 'R' ? red : blue)._id}> wins`;
}
} else if (resendTip) {
text += `\n\u200b\n##### Tip: Use \`${config.prefix}resend\` to move this message to the bottom`;
}
if (!msg) {
msg = await client.channels.sendMessage(message.channel, text);
sessions.set(message.channel, { messageID: msg._id, createdBy: message.author, opponent: opponent._id });
} else
await client.channels.editMessage(message.channel, msg._id, { content: text });
}
await updateMsg();
const end = awaitMessages(message.channel, mesg => {
if (mesg.content?.toLowerCase() === `${config.prefix}resend` && (mesg.author === red._id || mesg.author === blue._id)) {
let oldID = msg?._id;
msg = null;
resendTip = false;
updateMsg();
client.channels.deleteMessage(message.channel, oldID)
.catch(console.warn);
return;
}
let [num, bruh] = mesg.content.split(' ');
num = Number(num) - 1;
if (bruh || isNaN(num) || num > 6 || num < 0) return;
if (mesg.author !== red._id && mesg.author !== blue._id)
return client.channels.sendMessage(message.channel, `You are not part of this game, <@${mesg.author}>`);
if ((currentPlayer === 'R' ? red : blue)._id !== mesg.author)
return client.channels.sendMessage(message.channel, `It's not your turn, <@${mesg.author}>`);
if (field[0][num] !== ' ')
return client.channels.sendMessage(message.channel, 'That row is occupied.');
let i;
for (i = 5; field[i][num] !== ' '; i--) {}
field[i][num] = currentPlayer;
currentPlayer = currentPlayer === 'R' ? 'B' : 'R';
let w = getWinner(field);
if (w === false) {
winner = false;
client.channels.sendMessage(message.channel, 'Draw: Nobody wins');
end();
}
if (w) {
winner = w;
client.channels.sendMessage(message.channel, `<@${w === 'R' ? red._id : blue._id}> wins`);
end();
}
updateMsg();
});
});
/**
*
* @param {String} channel
* @param {function} callback
*/
const emitter = new (require('events'));
client.on('message', message => {
emitter.emit('message', message);
});
function awaitMessages(channel, callback) {
let ended = false;
const listener = emitter.on('message', message => {
if (message.channel === channel && !ended) {
callback(message);
}
});
gameEndEmitter.on(channel, () => ended = true);
return () => {
// somehow remove the listener idk how
gameEndEmitter.emit(channel);
}
}
/**
* @param {('R'|'B'|' ')[][]} field
* @returns {null|'R'|'B'|boolean}
*/
const getWinner = field => {
// Horizontal
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 4; j++) {
if (field[i][j] !== ' ' &&
field[i][j + 1] === field[i][j] &&
field[i][j + 2] === field[i][j] &&
field[i][j + 3] === field[i][j]
) return field[i][j];
}
}
// Vertical
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 7; j++) {
if (field[i][j] !== ' ' &&
field[i + 1][j] === field[i][j] &&
field[i + 2][j] === field[i][j] &&
field[i + 3][j] === field[i][j]
) return field[i][j];
}
}
// Diagonal (Top left - bottom right)
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 7; j++) {
if (field[i][j] !== ' ' &&
field[i + 1]?.[j + 1] === field[i][j] &&
field[i + 2]?.[j + 2] === field[i][j] &&
field[i + 3]?.[j + 3] === field[i][j]
) return field[i][j];
}
}
// Diagonal (Top right - bottom left)
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 7; j++) {
if (field[i][j] !== ' ' &&
field[i - 1]?.[j + 1] === field[i][j] &&
field[i - 2]?.[j + 2] === field[i][j] &&
field[i - 3]?.[j + 3] === field[i][j]
) return field[i][j];
}
}
// Draw
let draw = true;
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 7; j++) {
if (field[i][j] === ' ') draw = false;
}
}
if (draw) return false;
return null;
}

View File

@ -1,3 +1,4 @@
const Revolt = require('revolt.js');
const { client, config } = require('..');
const getUser = require('../util/get_user');
@ -5,6 +6,7 @@ const getUser = require('../util/get_user');
* @type {Map<String, { createdBy: String, opponent: String, messageID: String }>}
*/
let sessions = new Map();
const gameEndEmitter = new (require('events'));
module.exports.meta = {
name: 'tictactoe',
@ -26,6 +28,7 @@ module.exports.run = async (message, args) => new Promise(async (resolve, reject
return client.channels.sendMessage(message.channel, `:x: No ongoing match found.`);
sessions.delete(message.channel);
gameEndEmitter.emit(message.channel);
await client.channels.sendMessage(message.channel, `:white_check_mark: Game stopped`);
return;
}
@ -142,6 +145,13 @@ function awaitMessages(channel, callback) {
callback(message);
}
});
gameEndEmitter.on(channel, () => ended = true);
return () => {
// somehow remove the listener idk how
gameEndEmitter.emit(channel);
}
return () => {
// somehow remove the listener idk how
ended = true;