From ab36cd240e969d57b1edfa9b696deca0f25e4d28 Mon Sep 17 00:00:00 2001 From: Dragon Fire Date: Sun, 7 Feb 2021 10:15:35 -0500 Subject: [PATCH] Add fifty rule move and SAN notation --- commands/games-mp/chess.js | 43 +++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/commands/games-mp/chess.js b/commands/games-mp/chess.js index 6e77f062..4ee99478 100644 --- a/commands/games-mp/chess.js +++ b/commands/games-mp/chess.js @@ -5,9 +5,9 @@ const moment = require('moment'); const { stripIndents } = require('common-tags'); const path = require('path'); const { verify, reactIfAble } = require('../../util/Util'); -const { drawImageWithTint, centerImagePart } = require('../../util/Canvas'); +const { centerImagePart } = require('../../util/Canvas'); 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 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 whitePlayer = msg.author; let blackPlayer = opponent; + let fiftyRuleMove = 0; if (resumeGame) { await msg.reply(stripIndents` 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; whitePlayer = data.color === 'white' ? msg.author : opponent; blackPlayer = data.color === 'black' ? msg.author : opponent; + fiftyRuleMove = data.fiftyRuleMove; await this.client.redis.del(`chess-${msg.author.id}`); } else { game = new jsChess.Game(); @@ -93,7 +95,7 @@ module.exports = class ChessCommand extends Command { } let prevPieces = null; let saved = false; - while (!game.exportJson().checkMate) { + while (!game.exportJson().checkMate && fiftyRuleMove <= 50) { const gameState = game.exportJson(); const user = gameState.turn === 'black' ? blackPlayer : whitePlayer; 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; const move = choice.match(turnRegex); 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, '❌'); return false; } @@ -156,7 +159,13 @@ module.exports = class ChessCommand extends Command { if (gameState.turn === 'white') whiteTime -= new Date() - now; await this.client.redis.set( `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; break; @@ -166,6 +175,11 @@ module.exports = class ChessCommand extends Command { if (gameState.turn === 'white') whiteTime -= timeTaken - 5000; const choice = turn.first().content.toUpperCase().match(turnRegex); game.move(choice[1], choice[2]); + if (gameState.pieces[choice[1]].toUpperCase() === 'P') { + fiftyRuleMove = 0; + } else { + fiftyRuleMove++; + } } } 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 (fiftyRuleMove > 50) return msg.say('Due to the fifty move rule, this game is a draw.'); const gameState = game.exportJson(); if (!gameState.checkMate) return msg.say('Game ended due to forfeit.'); 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) { const canvas = createCanvas(this.images.board.width, this.images.board.height); const ctx = canvas.getContext('2d'); @@ -299,12 +327,13 @@ module.exports = class ChessCommand extends Command { return { name, color }; } - exportGame(game, blackTime, whiteTime, playerColor) { + exportGame(game, blackTime, whiteTime, playerColor, fiftyRuleMove) { return JSON.stringify({ fen: game.exportFEN(), blackTime: blackTime === Infinity ? -1 : blackTime, whiteTime: whiteTime === Infinity ? -1 : whiteTime, - color: playerColor + color: playerColor, + fiftyRuleMove }); } };