Add fifty rule move and SAN notation

This commit is contained in:
Dragon Fire
2021-02-07 10:15:35 -05:00
parent 00176dee69
commit ab36cd240e
+36 -7
View File
@@ -5,9 +5,9 @@ const moment = require('moment');
const { stripIndents } = require('common-tags'); const { stripIndents } = require('common-tags');
const path = require('path'); const path = require('path');
const { verify, reactIfAble } = require('../../util/Util'); const { verify, reactIfAble } = require('../../util/Util');
const { drawImageWithTint, centerImagePart } = require('../../util/Canvas'); const { centerImagePart } = require('../../util/Canvas');
const { FAILURE_EMOJI_ID } = process.env; const { FAILURE_EMOJI_ID } = process.env;
const turnRegex = /^([A-H][1-8])(?: |, ?|-?>?)?([A-H][1-8])$/; const turnRegex = /^((?:[A-H][1-8])|(?:[PKRQBN]))?([A-H])?(?: |, ?|-?>?)?([A-H][1-8])$/;
const pieces = ['pawn', 'rook', 'knight', 'king', 'queen', 'bishop']; const pieces = ['pawn', 'rook', 'knight', 'king', 'queen', 'bishop'];
const cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']; const cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
@@ -71,6 +71,7 @@ module.exports = class ChessCommand extends Command {
let blackTime = time === 0 ? Infinity : time * 60000; let blackTime = time === 0 ? Infinity : time * 60000;
let whitePlayer = msg.author; let whitePlayer = msg.author;
let blackPlayer = opponent; let blackPlayer = opponent;
let fiftyRuleMove = 0;
if (resumeGame) { if (resumeGame) {
await msg.reply(stripIndents` await msg.reply(stripIndents`
You have a saved game, do you want to resume it? You have a saved game, do you want to resume it?
@@ -84,6 +85,7 @@ module.exports = class ChessCommand extends Command {
blackTime = data.blackTime === -1 ? Infinity : data.blackTime; blackTime = data.blackTime === -1 ? Infinity : data.blackTime;
whitePlayer = data.color === 'white' ? msg.author : opponent; whitePlayer = data.color === 'white' ? msg.author : opponent;
blackPlayer = data.color === 'black' ? msg.author : opponent; blackPlayer = data.color === 'black' ? msg.author : opponent;
fiftyRuleMove = data.fiftyRuleMove;
await this.client.redis.del(`chess-${msg.author.id}`); await this.client.redis.del(`chess-${msg.author.id}`);
} else { } else {
game = new jsChess.Game(); game = new jsChess.Game();
@@ -93,7 +95,7 @@ module.exports = class ChessCommand extends Command {
} }
let prevPieces = null; let prevPieces = null;
let saved = false; let saved = false;
while (!game.exportJson().checkMate) { while (!game.exportJson().checkMate && fiftyRuleMove <= 50) {
const gameState = game.exportJson(); const gameState = game.exportJson();
const user = gameState.turn === 'black' ? blackPlayer : whitePlayer; const user = gameState.turn === 'black' ? blackPlayer : whitePlayer;
const userTime = gameState.turn === 'black' ? blackTime : whiteTime; const userTime = gameState.turn === 'black' ? blackTime : whiteTime;
@@ -123,7 +125,8 @@ module.exports = class ChessCommand extends Command {
if (res.author.id !== user.id) return false; if (res.author.id !== user.id) return false;
const move = choice.match(turnRegex); const move = choice.match(turnRegex);
if (!move) return false; if (!move) return false;
if (!moves[move[1]] || !moves[move[1]].includes(move[2])) { const parsed = this.parseSAN(gameState, moves, move);
if (!parsed || !moves[parsed[0]] || !moves[parsed[0]].includes(parsed[1])) {
reactIfAble(res, res.author, FAILURE_EMOJI_ID, '❌'); reactIfAble(res, res.author, FAILURE_EMOJI_ID, '❌');
return false; return false;
} }
@@ -156,7 +159,13 @@ module.exports = class ChessCommand extends Command {
if (gameState.turn === 'white') whiteTime -= new Date() - now; if (gameState.turn === 'white') whiteTime -= new Date() - now;
await this.client.redis.set( await this.client.redis.set(
`chess-${author.id}`, `chess-${author.id}`,
this.exportGame(game, blackTime, whiteTime, whitePlayer.id === author.id ? 'white' : 'black') this.exportGame(
game,
blackTime,
whiteTime,
whitePlayer.id === author.id ? 'white' : 'black',
fiftyRuleMove
)
); );
saved = true; saved = true;
break; break;
@@ -166,6 +175,11 @@ module.exports = class ChessCommand extends Command {
if (gameState.turn === 'white') whiteTime -= timeTaken - 5000; if (gameState.turn === 'white') whiteTime -= timeTaken - 5000;
const choice = turn.first().content.toUpperCase().match(turnRegex); const choice = turn.first().content.toUpperCase().match(turnRegex);
game.move(choice[1], choice[2]); game.move(choice[1], choice[2]);
if (gameState.pieces[choice[1]].toUpperCase() === 'P') {
fiftyRuleMove = 0;
} else {
fiftyRuleMove++;
}
} }
} }
this.client.games.delete(msg.channel.id); this.client.games.delete(msg.channel.id);
@@ -176,6 +190,7 @@ module.exports = class ChessCommand extends Command {
If you want to delete your saved game, use ${this.client.registry.commands.get('chess-delete').usage()}. If you want to delete your saved game, use ${this.client.registry.commands.get('chess-delete').usage()}.
`); `);
} }
if (fiftyRuleMove > 50) return msg.say('Due to the fifty move rule, this game is a draw.');
const gameState = game.exportJson(); const gameState = game.exportJson();
if (!gameState.checkMate) return msg.say('Game ended due to forfeit.'); if (!gameState.checkMate) return msg.say('Game ended due to forfeit.');
const winner = gameState.turn === 'black' ? whitePlayer : blackPlayer; const winner = gameState.turn === 'black' ? whitePlayer : blackPlayer;
@@ -188,6 +203,19 @@ module.exports = class ChessCommand extends Command {
} }
} }
parseSAN(gameState, moves, move) {
if (!move || !move[3]) return null;
const initial = move[1] || 'P';
if (gameState.pieces[initial]) return [initial, move[3]];
const possiblePieces = Object.keys(gameState.pieces).filter(piece => {
if (gameState.pieces[piece] !== initial) return false;
if (move[2] && !piece.startsWith(move[2])) return false;
return moves[piece].includes(move[3]);
});
if (possiblePieces.length === 1) return [possiblePieces[0], move[3]];
return null;
}
displayBoard(gameState, prevPieces) { displayBoard(gameState, prevPieces) {
const canvas = createCanvas(this.images.board.width, this.images.board.height); const canvas = createCanvas(this.images.board.width, this.images.board.height);
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
@@ -299,12 +327,13 @@ module.exports = class ChessCommand extends Command {
return { name, color }; return { name, color };
} }
exportGame(game, blackTime, whiteTime, playerColor) { exportGame(game, blackTime, whiteTime, playerColor, fiftyRuleMove) {
return JSON.stringify({ return JSON.stringify({
fen: game.exportFEN(), fen: game.exportFEN(),
blackTime: blackTime === Infinity ? -1 : blackTime, blackTime: blackTime === Infinity ? -1 : blackTime,
whiteTime: whiteTime === Infinity ? -1 : whiteTime, whiteTime: whiteTime === Infinity ? -1 : whiteTime,
color: playerColor color: playerColor,
fiftyRuleMove
}); });
} }
}; };