104 lines
3.1 KiB
JavaScript
104 lines
3.1 KiB
JavaScript
/**
|
|
* PGN Parser - Parses PGN files and extracts game data
|
|
*/
|
|
|
|
function parsePGN(pgnText) {
|
|
const games = [];
|
|
// Split by game boundaries - each game starts with [Event
|
|
const gameBlocks = pgnText.split(/\[\s*Event\s*"/);
|
|
|
|
for (let i = 1; i < gameBlocks.length; i++) {
|
|
const game = parseGameBlock(gameBlocks[i]);
|
|
if (game) games.push(game);
|
|
}
|
|
|
|
return games;
|
|
}
|
|
|
|
function parseGameBlock(block) {
|
|
try {
|
|
const headers = {};
|
|
const headerRegex = /^\s*\[(\w+)\s+"([^"]*)"\]/g;
|
|
let match;
|
|
|
|
// Extract all headers
|
|
let tempBlock = block;
|
|
while ((match = headerRegex.exec(tempBlock)) !== null) {
|
|
headers[match[1]] = match[2];
|
|
}
|
|
|
|
// Extract moves - everything after the last header
|
|
const lastHeaderEnd = block.lastIndexOf(']"');
|
|
let movesText = lastHeaderEnd > -1 ? block.substring(lastHeaderEnd + 2).trim() : '';
|
|
|
|
// Remove comments from moves for cleaner parsing
|
|
const moves = parseMoves(movesText);
|
|
|
|
return {
|
|
event: headers.Event || '',
|
|
site: headers.Site || '',
|
|
date: headers.Date || '',
|
|
round: headers.Round || '',
|
|
white: headers.White || '',
|
|
black: headers.Black || '',
|
|
result: headers.Result || '',
|
|
termination: headers.Termination || '',
|
|
whiteElo: headers.WhiteElo || '',
|
|
blackElo: headers.BlackElo || '',
|
|
whiteClock: headers.WhiteClock || '',
|
|
blackClock: headers.BlackClock || '',
|
|
moves: moves,
|
|
isLive: headers.Termination === 'unterminated' || headers.Result === '*'
|
|
};
|
|
} catch (e) {
|
|
console.error('Error parsing game:', e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function parseMoves(movesText) {
|
|
const moves = [];
|
|
|
|
// Remove comments in curly braces
|
|
movesText = movesText.replace(/\{[^}]*\}/g, '');
|
|
|
|
// Remove move numbers
|
|
movesText = movesText.replace(/\d+\.\s*/g, '');
|
|
|
|
// Split into individual moves
|
|
const tokens = movesText.split(/\s+/).filter(t => t.trim());
|
|
|
|
for (const token of tokens) {
|
|
if (['1-0', '0-1', '1/2-1/2', '*'].includes(token)) {
|
|
moves.push({ san: token, isResult: true });
|
|
} else if (token.length > 0) {
|
|
moves.push({ san: token, isResult: false });
|
|
}
|
|
}
|
|
|
|
return moves;
|
|
}
|
|
|
|
function filterLaraGames(games) {
|
|
return games.filter(game =>
|
|
game.white.toLowerCase().includes('kiesewetter') ||
|
|
game.black.toLowerCase().includes('kiesewetter')
|
|
);
|
|
}
|
|
|
|
function getLiveGame(laraGames) {
|
|
// Return the game that is still in progress
|
|
return laraGames.find(game => game.isLive) || null;
|
|
}
|
|
|
|
function getLatestGame(laraGames) {
|
|
if (laraGames.length === 0) return null;
|
|
// Sort by round number, return the highest round
|
|
const sorted = [...laraGames].sort((a, b) => {
|
|
const roundA = parseInt(a.round) || 0;
|
|
const roundB = parseInt(b.round) || 0;
|
|
return roundB - roundA;
|
|
});
|
|
return sorted[0];
|
|
}
|