WIP
This commit is contained in:
@@ -19,7 +19,7 @@ const drawPieces = async (
|
|||||||
type: PieceType;
|
type: PieceType;
|
||||||
color: PieceColor;
|
color: PieceColor;
|
||||||
};
|
};
|
||||||
const img = await ImagesCache.get("tatiana", type, color);
|
const img = await ImagesCache.get("cburnett", type, color);
|
||||||
const rank = flipped ? tiles - 1 - y : y;
|
const rank = flipped ? tiles - 1 - y : y;
|
||||||
const file = flipped ? tiles - 1 - x : x;
|
const file = flipped ? tiles - 1 - x : x;
|
||||||
|
|
||||||
@@ -34,9 +34,11 @@ const drawPieces = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (color === check && type === "k") {
|
if (color === check && type === "k") {
|
||||||
filters.push(`drop-shadow(0 0 ${squareSize * 0.1}px orange)`);
|
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px #ffa600)`);
|
||||||
|
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px #ffa600)`);
|
||||||
} else if (color === mate && type === "k") {
|
} else if (color === mate && type === "k") {
|
||||||
filters.push(`drop-shadow(0 0 ${squareSize * 0.1}px red)`);
|
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px #ff002f)`);
|
||||||
|
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px #ff002f)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.filter = filters.length > 0 ? filters.join(" ") : "none";
|
ctx.filter = filters.length > 0 ? filters.join(" ") : "none";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import calm from "./calm";
|
|||||||
import standard from "./standard";
|
import standard from "./standard";
|
||||||
import glass from "./glass";
|
import glass from "./glass";
|
||||||
// import kittens from "./kittens";
|
// import kittens from "./kittens";
|
||||||
|
import lichess from "./lichess";
|
||||||
import lila from "./lila";
|
import lila from "./lila";
|
||||||
import mono from "./mono";
|
import mono from "./mono";
|
||||||
import peach from "./peach";
|
import peach from "./peach";
|
||||||
@@ -12,6 +13,7 @@ export default {
|
|||||||
calm,
|
calm,
|
||||||
glass,
|
glass,
|
||||||
// kittens,
|
// kittens,
|
||||||
|
lichess,
|
||||||
lila,
|
lila,
|
||||||
mono,
|
mono,
|
||||||
peach,
|
peach,
|
||||||
|
|||||||
42
src/board/styles-board/lichess.ts
Normal file
42
src/board/styles-board/lichess.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Style } from "../../types";
|
||||||
|
|
||||||
|
const style: Style = {
|
||||||
|
name: "Lichess",
|
||||||
|
background: {
|
||||||
|
type: "solid",
|
||||||
|
data: {
|
||||||
|
color: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
type: "solid",
|
||||||
|
data: {
|
||||||
|
color: "#b58863",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
type: "solid",
|
||||||
|
data: {
|
||||||
|
color: "#f0d9b5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
moveIndicator: {
|
||||||
|
type: "solid",
|
||||||
|
data: {
|
||||||
|
color: "rgba(155,199,0,0.41)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
border: null,
|
||||||
|
// border: {
|
||||||
|
// type: "solid",
|
||||||
|
// data: {
|
||||||
|
// color: "#896d56",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
coords: {
|
||||||
|
lightColor: "#f0d9b5",
|
||||||
|
darkColor: "#b58863",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default style;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Chess, ChessInstance, Move } from "chess.js";
|
import { Chess, ChessInstance, Move } from "chess.js";
|
||||||
|
import { cleanPGN } from "./PGNHelpers";
|
||||||
|
|
||||||
class Game {
|
class Game {
|
||||||
private game: ChessInstance;
|
private game: ChessInstance;
|
||||||
@@ -12,33 +13,8 @@ class Game {
|
|||||||
this.moves = [];
|
this.moves = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private cleanPGN(pgn: string) {
|
|
||||||
const game = new Chess();
|
|
||||||
game.load_pgn(pgn);
|
|
||||||
game.delete_comments();
|
|
||||||
const [_, moves] = game.pgn().split("\n\n");
|
|
||||||
|
|
||||||
const header = Object.entries(game.header())
|
|
||||||
.filter(([key]) =>
|
|
||||||
[
|
|
||||||
"event",
|
|
||||||
"site",
|
|
||||||
"white",
|
|
||||||
"black",
|
|
||||||
"date",
|
|
||||||
"result",
|
|
||||||
"opening",
|
|
||||||
].includes(key.toLowerCase())
|
|
||||||
)
|
|
||||||
.map(([key, val]) => `[${key} "${val}"]`)
|
|
||||||
.sort()
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
return [header, moves].join("\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
loadPGN(pgn: string) {
|
loadPGN(pgn: string) {
|
||||||
this.game.load_pgn(this.cleanPGN(pgn));
|
this.game.load_pgn(cleanPGN(pgn));
|
||||||
this.game.delete_comments();
|
this.game.delete_comments();
|
||||||
this.moves = this.game.history({ verbose: true });
|
this.moves = this.game.history({ verbose: true });
|
||||||
this.currentPly = 0;
|
this.currentPly = 0;
|
||||||
|
|||||||
79
src/game/PGNHelpers.ts
Normal file
79
src/game/PGNHelpers.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { Chess } from "chess.js";
|
||||||
|
|
||||||
|
const PGN_KEYS_TO_SHORT = {
|
||||||
|
Event: "E",
|
||||||
|
Site: "S",
|
||||||
|
White: "W",
|
||||||
|
Black: "B",
|
||||||
|
Date: "D",
|
||||||
|
Result: "R",
|
||||||
|
FEN: "F",
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
const [_, moves] = game.pgn().split("\n\n");
|
||||||
|
|
||||||
|
const header = Object.entries(game.header())
|
||||||
|
.filter(([key]) => PGN_KEYS.includes(key))
|
||||||
|
.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]) => `${key[0].toUpperCase()} ${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 };
|
||||||
28
src/main.ts
28
src/main.ts
@@ -5,18 +5,22 @@ import styles from "./board/styles-board";
|
|||||||
import Game from "./game/Game";
|
import Game from "./game/Game";
|
||||||
import pgns from "./test-data/pgns";
|
import pgns from "./test-data/pgns";
|
||||||
import createSimpleGIF from "./gif/createSimpleGIF";
|
import createSimpleGIF from "./gif/createSimpleGIF";
|
||||||
|
import { compressPGN, decompressPGN } from "./game/PGNHelpers";
|
||||||
|
|
||||||
const $app = document.querySelector<HTMLImageElement>("#app");
|
const $app = document.querySelector<HTMLImageElement>("#app");
|
||||||
|
|
||||||
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
const play = async (board: Board, pgn: string, interval: number) => {
|
const play = async (board: Board, pgn: string | null, interval: number) => {
|
||||||
const game = new Game().loadPGN(pgn);
|
const game = new Game();
|
||||||
|
|
||||||
|
if (pgn) {
|
||||||
|
game.loadPGN(pgn);
|
||||||
|
}
|
||||||
|
|
||||||
// game.goto(28);
|
// game.goto(28);
|
||||||
await board.render(game.getBoardData());
|
await board.render(game.getBoardData());
|
||||||
|
|
||||||
console.log(game.pgn());
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const move = game.next();
|
const move = game.next();
|
||||||
|
|
||||||
@@ -28,7 +32,7 @@ const play = async (board: Board, pgn: string, interval: number) => {
|
|||||||
await board.render(game.getBoardData(), move);
|
await board.render(game.getBoardData(), move);
|
||||||
}
|
}
|
||||||
|
|
||||||
await delay(interval);
|
await delay(interval * 3);
|
||||||
play(board, pgn, interval);
|
play(board, pgn, interval);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,17 +46,21 @@ const createDownloadLink = async (pgn: string, style: Style) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
const style = styles.calm;
|
const style = styles.avocado;
|
||||||
// Object.values(styles).forEach((style, i) => {
|
|
||||||
|
const hash = window.location.hash;
|
||||||
|
const pgn = hash === "" ? null : decompressPGN(hash.slice(1));
|
||||||
const board = new Board(8).setStyle(style).setSize(720).showBorder();
|
const board = new Board(8).setStyle(style).setSize(720).showBorder();
|
||||||
|
|
||||||
|
window.location.hash = "#alala";
|
||||||
|
|
||||||
$app?.appendChild(board.canvas);
|
$app?.appendChild(board.canvas);
|
||||||
|
|
||||||
play(board, pgns[0], 1000);
|
play(board, pgn, 1000);
|
||||||
// });
|
|
||||||
|
|
||||||
// const link = await createDownloadLink(pgns[0], styles.peach);
|
// createDownloadLink(pgns[0], style).then((link) => {
|
||||||
// document.body.appendChild(link);
|
// document.body.appendChild(link);
|
||||||
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|||||||
@@ -20,6 +20,25 @@ is hanging, the bishop is blocked because of the
|
|||||||
Queen.--Fischer} b5 10.Nxb5 cxb5 11.Bxb5+ Nbd7 12.O-O-O Rd8
|
Queen.--Fischer} b5 10.Nxb5 cxb5 11.Bxb5+ Nbd7 12.O-O-O Rd8
|
||||||
13.Rxd7 Rxd7 14.Rd1 Qe6 15.Bxd7+ Nxd7 16.Qb8+ Nxb8 17.Rd8# 1-0`,
|
13.Rxd7 Rxd7 14.Rd1 Qe6 15.Bxd7+ Nxd7 16.Qb8+ Nxb8 17.Rd8# 1-0`,
|
||||||
|
|
||||||
|
`[Event "Casual Bullet game"]
|
||||||
|
[Site "https://lichess.org/9PndkM3J"]
|
||||||
|
[Date "2022.01.15"]
|
||||||
|
[White "Anonymous"]
|
||||||
|
[Black "Anonymous"]
|
||||||
|
[Result "1-0"]
|
||||||
|
[UTCDate "2022.01.15"]
|
||||||
|
[UTCTime "19:04:57"]
|
||||||
|
[WhiteElo "?"]
|
||||||
|
[BlackElo "?"]
|
||||||
|
[Variant "Standard"]
|
||||||
|
[TimeControl "60+0"]
|
||||||
|
[ECO "A00"]
|
||||||
|
[Opening "Polish Opening"]
|
||||||
|
[Termination "Normal"]
|
||||||
|
[Annotator "lichess.org"]
|
||||||
|
|
||||||
|
1. b4 { A00 Polish Opening } e5 2. Bb2 Nc6 3. b5 Nd4 4. e3 Nxb5 5. Bxb5 c6 6. Be2 Nf6 7. Bxe5 d6 8. Bb2 Be7 9. Nf3 O-O 10. O-O Bg4 11. d3 d5 12. Nbd2 Rc8 13. Qc1 Be6 14. c4 d4 15. Bxd4 Bg4 16. Bb2 Qc7 17. Qc3 Bxf3 18. Bxf3 Rcd8 19. Ne4 a5 20. a3 b5 21. Rad1 b4 22. axb4 Bxb4 23. Nxf6+ gxf6 24. Qxf6 Rd6 25. Qg7# { White wins by checkmate. } 1-0`,
|
||||||
|
|
||||||
`[Event "Hoogovens Group A"]
|
`[Event "Hoogovens Group A"]
|
||||||
[Site "Wijk aan Zee NED"]
|
[Site "Wijk aan Zee NED"]
|
||||||
[Date "1999.01.20"]
|
[Date "1999.01.20"]
|
||||||
|
|||||||
Reference in New Issue
Block a user