Files
sharechess/src/game/PGNHelpers.ts
2022-04-25 20:31:48 -07:00

101 lines
2.2 KiB
TypeScript

import { Chess } from "chess.js";
const PGN_KEYS_TO_SHORT = {
Event: "E",
Site: "S",
Round: "N",
White: "W",
Black: "B",
Date: "D",
Result: "R",
FEN: "F",
SetUp: "SU",
WhiteElo: "WE",
BlackElo: "BE",
};
const PGN_KEYS = Object.keys(PGN_KEYS_TO_SHORT);
const PGN_KEYS_TO_LONG = Object.fromEntries(
Object.entries(PGN_KEYS_TO_SHORT).map(([long, short]) => [short, long])
);
const cleanPGN = (pgn: string) => {
const game = new Chess();
game.load_pgn(pgn);
game.delete_comments();
if (!game.header().Result) {
const result = (pgn.trim().match(/(1-0)|(0-1)|(1\/2-1\/2)$/) || [])[0];
if (result) {
game.header("Result", result);
}
}
const headerEntries = Object.entries(game.header());
const [_, moves] =
headerEntries.length > 0
? game.pgn().split("\n\n")
: [undefined, game.pgn()];
const header = headerEntries
.filter(([key, val]) => PGN_KEYS.includes(key) && val !== "?")
.map(([key, val]) => `[${key} "${val}"]`)
.sort()
.join("\n");
return [header, moves].join("\n\n");
};
const compressPGN = (pgn: string) => {
const game = new Chess();
game.load_pgn(pgn);
const moves = game.history().join(" ");
const header = Object.entries(game.header())
.filter(([key]) => PGN_KEYS.includes(key))
.map(
([key, val]) =>
`${PGN_KEYS_TO_SHORT[key as keyof typeof PGN_KEYS_TO_SHORT]} ${val}`
)
.sort()
.join("\n");
return btoa([header, moves].join("\n\n"));
};
const decompressPGN = (compressedPGN: string) => {
const [headerRaw, movesRaw] = atob(compressedPGN).split("\n\n");
let result;
const header = headerRaw
.split("\n")
.map((entry) => {
const [shortKey, ...data] = entry.split(" ");
const value = data.join(" ");
if (shortKey === "R") {
result = value;
}
return `[${PGN_KEYS_TO_LONG[shortKey]} "${value}"]`;
})
.join("\n");
const moves =
movesRaw
.split(" ")
.map((move, index) =>
index % 2 === 0 ? `${index / 2 + 1}. ${move}` : move
)
.join(" ") + ` ${result}`;
return [header, moves].join("\n\n");
};
export { cleanPGN, compressPGN, decompressPGN };