UNPKG

lichess-pgn-viewer

Version:

PGN viewer widget, designed to be embedded in content pages.

127 lines 5.05 kB
import { makeUci } from 'chessops'; import { scalachessCharPair } from 'chessops/compat'; import { makeFen } from 'chessops/fen'; import { parsePgn, parseComment, startingPosition, transform } from 'chessops/pgn'; import { makeSanAndPlay, parseSan } from 'chessops/san'; import { Game } from './game'; import { Path } from './path'; class State { constructor(pos, path, clocks) { this.pos = pos; this.path = path; this.clocks = clocks; this.clone = () => new State(this.pos.clone(), this.path, { ...this.clocks }); } } export const parseComments = (strings) => { const comments = strings.map(parseComment); const reduceTimes = (times) => times.reduce((last, time) => (typeof time == undefined ? last : time), undefined); return { texts: comments.map(c => c.text).filter(t => !!t), shapes: comments.flatMap(c => c.shapes), clock: reduceTimes(comments.map(c => c.clock)), emt: reduceTimes(comments.map(c => c.emt)), }; }; export const makeGame = (pgn, lichess = false) => { var _a, _b; const game = parsePgn(pgn)[0] || parsePgn('*')[0]; const start = startingPosition(game.headers).unwrap(); const fen = makeFen(start.toSetup()); const comments = parseComments(game.comments || []); const headers = new Map(Array.from(game.headers, ([key, value]) => [key.toLowerCase(), value])); const metadata = makeMetadata(headers, lichess); const initial = { fen, turn: start.turn, check: start.isCheck(), pos: start.clone(), comments: comments.texts, shapes: comments.shapes, clocks: { white: ((_a = metadata.timeControl) === null || _a === void 0 ? void 0 : _a.initial) || comments.clock, black: ((_b = metadata.timeControl) === null || _b === void 0 ? void 0 : _b.initial) || comments.clock, }, }; const moves = makeMoves(start, game.moves, metadata); const players = makePlayers(headers, metadata); return new Game(initial, moves, players, metadata); }; const makeMoves = (start, moves, metadata) => transform(moves, new State(start, Path.root, {}), (state, node, _index) => { const move = parseSan(state.pos, node.san); if (!move) return undefined; const moveId = scalachessCharPair(move); const path = state.path.append(moveId); const san = makeSanAndPlay(state.pos, move); state.path = path; const setup = state.pos.toSetup(); const comments = parseComments(node.comments || []); const startingComments = parseComments(node.startingComments || []); const shapes = [...comments.shapes, ...startingComments.shapes]; const ply = (setup.fullmoves - 1) * 2 + (state.pos.turn === 'white' ? 0 : 1); let clocks = (state.clocks = makeClocks(state.clocks, state.pos.turn, comments.clock)); if (ply < 2 && metadata.timeControl) clocks = { white: metadata.timeControl.initial, black: metadata.timeControl.initial, ...clocks, }; const moveNode = { path, ply, move, san, uci: makeUci(move), fen: makeFen(state.pos.toSetup()), turn: state.pos.turn, check: state.pos.isCheck(), comments: comments.texts, startingComments: startingComments.texts, nags: node.nags || [], shapes, clocks, emt: comments.emt, }; return moveNode; }); const makeClocks = (prev, turn, clk) => turn == 'white' ? { ...prev, black: clk } : { ...prev, white: clk }; function makePlayers(headers, metadata) { const get = (color, field) => { const raw = headers.get(`${color}${field}`); return raw == '?' || raw == '' ? undefined : raw; }; const makePlayer = (color) => { const name = get(color, ''); return { name, title: get(color, 'title'), rating: parseInt(get(color, 'elo') || '') || undefined, isLichessUser: metadata.isLichess && !!(name === null || name === void 0 ? void 0 : name.match(/^[a-z0-9][a-z0-9_-]{0,28}[a-z0-9]$/i)), }; }; return { white: makePlayer('white'), black: makePlayer('black'), }; } function makeMetadata(headers, lichess) { var _a; const site = headers.get('source') || headers.get('site'); const tcs = (_a = headers .get('timecontrol')) === null || _a === void 0 ? void 0 : _a.split('+').map(x => parseInt(x)); const timeControl = tcs && tcs[0] ? { initial: tcs[0], increment: tcs[1] || 0, } : undefined; const orientation = headers.get('orientation'); return { externalLink: site && site.match(/^https?:\/\//) ? site : undefined, isLichess: !!(lichess && (site === null || site === void 0 ? void 0 : site.startsWith(lichess))), timeControl, orientation: orientation === 'white' || orientation === 'black' ? orientation : undefined, }; } //# sourceMappingURL=pgn.js.map