WIP
This commit is contained in:
3986
package-lock.json
generated
3986
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,17 +7,22 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/hammerjs": "^2.0.41",
|
||||
"@types/node": "^17.0.8",
|
||||
"@types/webfontloader": "^1.6.34",
|
||||
"typescript": "^4.4.4",
|
||||
"vite": "^2.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@arrows/array": "^1.4.1",
|
||||
"@types/chess.js": "^0.11.2",
|
||||
"@types/gif.js": "^0.2.2",
|
||||
"chess.js": "^0.12.0",
|
||||
"gif.js": "^0.2.0",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"hammerjs": "^2.0.8",
|
||||
"i": "^0.3.7",
|
||||
"npm": "^8.4.0",
|
||||
"webfontloader": "^1.6.28",
|
||||
"webm-writer": "^1.0.0"
|
||||
}
|
||||
|
||||
23
src/board/layers/drawCircle.ts
Normal file
23
src/board/layers/drawCircle.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
const drawCircle = async (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
radius: number,
|
||||
x: number,
|
||||
y: number,
|
||||
thickness: number,
|
||||
color: string,
|
||||
fill: boolean = false
|
||||
) => {
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = thickness;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x + radius, y + radius, radius - thickness / 2, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
||||
if (fill) {
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
}
|
||||
};
|
||||
|
||||
export default drawCircle;
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Solid } from "./../../types";
|
||||
import { Move } from "chess.js";
|
||||
import { Style, SquareStyle } from "../../types";
|
||||
import drawRectangle from "./drawRectangle";
|
||||
import { changeHSL } from "../../utils/colors";
|
||||
|
||||
const FILES = "abcdefghijklmnopqrstuwvxyz";
|
||||
|
||||
@@ -32,9 +34,26 @@ const drawMoveIndicators = async (
|
||||
let toStyle;
|
||||
|
||||
if (moveIndicator.type === "hueShift") {
|
||||
ctx.filter = `hue-rotate(${moveIndicator.data}deg)`;
|
||||
fromStyle = (x0 + y0) % 2 === 0 ? light : dark;
|
||||
toStyle = (x1 + y1) % 2 === 0 ? light : dark;
|
||||
const newLight: Solid = {
|
||||
type: "solid",
|
||||
data: {
|
||||
color: light.data.color
|
||||
? changeHSL(light.data.color, moveIndicator.data)
|
||||
: "#00ff0055",
|
||||
},
|
||||
};
|
||||
|
||||
const newDark: Solid = {
|
||||
type: "solid",
|
||||
data: {
|
||||
color: dark.data.color
|
||||
? changeHSL(dark.data.color, moveIndicator.data)
|
||||
: "#00ff0055",
|
||||
},
|
||||
};
|
||||
|
||||
fromStyle = (x0 + y0) % 2 === 0 ? newLight : newDark;
|
||||
toStyle = (x1 + y1) % 2 === 0 ? newLight : newDark;
|
||||
} else {
|
||||
fromStyle = {
|
||||
type: "solid",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BoardData, PieceType, PieceColor, PiecesStyle } from "../../types";
|
||||
import ImagesCache from "../loaders/PiecesCache";
|
||||
// import drawCircle from "./drawCircle";
|
||||
|
||||
const drawPieces = async (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
@@ -35,12 +36,23 @@ const drawPieces = async (
|
||||
);
|
||||
}
|
||||
|
||||
if (color === check && type === "k") {
|
||||
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") {
|
||||
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px #ff002f)`);
|
||||
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px #ff002f)`);
|
||||
// if ((color === check || color === mate) && type === "k") {
|
||||
// const hex = check ? "#ffa600" : "#ff002f";
|
||||
// drawCircle(
|
||||
// ctx,
|
||||
// squareSize / 2,
|
||||
// borderWidth + file * squareSize,
|
||||
// borderWidth + rank * squareSize + margin,
|
||||
// 0,
|
||||
// hex,
|
||||
// true
|
||||
// );
|
||||
// }
|
||||
|
||||
if ((color === check || color === mate) && type === "k") {
|
||||
const hex = check ? "#ffa600" : "#ff002f";
|
||||
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px ${hex})`);
|
||||
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px ${hex})`);
|
||||
}
|
||||
|
||||
ctx.filter = filters.length > 0 ? filters.join(" ") : "none";
|
||||
|
||||
21
src/board/layers/drawRectangleStroke.ts
Normal file
21
src/board/layers/drawRectangleStroke.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
const drawRectangleStroke = async (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
width: number,
|
||||
height: number,
|
||||
x: number,
|
||||
y: number,
|
||||
thickness: number,
|
||||
color: string
|
||||
) => {
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = thickness;
|
||||
ctx.strokeRect(
|
||||
x + thickness / 2,
|
||||
y + thickness / 2,
|
||||
width - thickness,
|
||||
height - thickness
|
||||
);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
export default drawRectangleStroke;
|
||||
@@ -23,7 +23,7 @@ const style: Style = {
|
||||
},
|
||||
moveIndicator: {
|
||||
type: "hueShift",
|
||||
data: 40,
|
||||
data: -40,
|
||||
},
|
||||
border: {
|
||||
type: "solid",
|
||||
|
||||
@@ -23,7 +23,7 @@ const style: Style = {
|
||||
},
|
||||
moveIndicator: {
|
||||
type: "hueShift",
|
||||
data: 180,
|
||||
data: -120,
|
||||
},
|
||||
border: {
|
||||
type: "solid",
|
||||
|
||||
@@ -23,7 +23,7 @@ const style: Style = {
|
||||
},
|
||||
moveIndicator: {
|
||||
type: "hueShift",
|
||||
data: 330,
|
||||
data: 30,
|
||||
},
|
||||
border: {
|
||||
type: "solid",
|
||||
|
||||
@@ -12,7 +12,7 @@ const style: Style = {
|
||||
dark: {
|
||||
type: "solid",
|
||||
data: {
|
||||
color: "#5d3e7d",
|
||||
color: "#7b4fa8",
|
||||
},
|
||||
},
|
||||
light: {
|
||||
@@ -23,7 +23,7 @@ const style: Style = {
|
||||
},
|
||||
moveIndicator: {
|
||||
type: "hueShift",
|
||||
data: 60,
|
||||
data: -60,
|
||||
},
|
||||
border: {
|
||||
type: "solid",
|
||||
|
||||
@@ -29,7 +29,9 @@ class GIF {
|
||||
|
||||
frames: number
|
||||
) {
|
||||
this.gif.addFrame(frame, { delay: frames * this.frameTime });
|
||||
while (frames--) {
|
||||
this.gif.addFrame(frame, { delay: this.frameTime });
|
||||
}
|
||||
}
|
||||
|
||||
render(): Promise<File> {
|
||||
|
||||
@@ -33,7 +33,7 @@ const createAnimation = async (
|
||||
board.render();
|
||||
|
||||
// @ts-ignore
|
||||
await encoder.add(getData(board, encoder), 5);
|
||||
await encoder.add(getData(board, encoder), 4);
|
||||
|
||||
await board.frame(game.getBoardData(), header, null, game.materialInfo());
|
||||
board.render();
|
||||
|
||||
@@ -28,6 +28,8 @@ class Game {
|
||||
this.game.load_pgn(cleanPGN(pgn));
|
||||
this.game.delete_comments();
|
||||
|
||||
console.log(this.game.pgn());
|
||||
|
||||
const moves = this.game.history({ verbose: true });
|
||||
|
||||
this.moves = moves.map((item, i) => ({
|
||||
@@ -53,14 +55,15 @@ class Game {
|
||||
}
|
||||
|
||||
next() {
|
||||
this.currentPly++;
|
||||
const move = this.moves[this.currentPly - 1];
|
||||
const move = this.moves[this.currentPly];
|
||||
|
||||
if (!move) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.currentPly++;
|
||||
this.replay.move(move);
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
@@ -73,13 +76,7 @@ class Game {
|
||||
return null;
|
||||
}
|
||||
|
||||
const move = this.moves[this.currentPly];
|
||||
|
||||
// if (!move) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
console.log("Prev!");
|
||||
const move = this.moves[this.currentPly - 1];
|
||||
|
||||
return move;
|
||||
}
|
||||
@@ -103,11 +100,11 @@ class Game {
|
||||
ply > this.currentPly ? this.next() : this.prev();
|
||||
}
|
||||
|
||||
return this.moves[ply];
|
||||
return this.moves[ply - 1];
|
||||
}
|
||||
|
||||
materialInfo() {
|
||||
const pieces = this.getBoardData().flat().filter(Boolean);
|
||||
const pieces = this.replay.board().flat().filter(Boolean);
|
||||
|
||||
const sum = { w: 0, b: 0 };
|
||||
const imbalance = {
|
||||
@@ -138,6 +135,7 @@ class Game {
|
||||
}
|
||||
|
||||
getBoardData() {
|
||||
// console.log(this.replay.ascii());
|
||||
return this.replay.board();
|
||||
}
|
||||
|
||||
|
||||
186
src/main.ts
186
src/main.ts
@@ -7,14 +7,14 @@ import pgns from "./test-data/pgns";
|
||||
import createAnimation from "./encoders/createAnimation";
|
||||
// import { decompressPGN } from "./game/PGNHelpers";
|
||||
import WebFont from "webfontloader";
|
||||
import Player from "./player/Player";
|
||||
import * as Hammer from "hammerjs";
|
||||
|
||||
const $app = document.querySelector<HTMLImageElement>("#app");
|
||||
|
||||
// const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const boardConfig: BoardConfig = {
|
||||
size: 720,
|
||||
boardStyle: styles.violet,
|
||||
boardStyle: styles.sunset,
|
||||
piecesStyle: "tatiana",
|
||||
showBorder: true,
|
||||
showExtraInfo: true,
|
||||
@@ -32,120 +32,6 @@ const gameConfig: GameConfig = {
|
||||
loop: true,
|
||||
};
|
||||
|
||||
class Player {
|
||||
// private interval = 1000;
|
||||
private game: Game = new Game();
|
||||
private header: { [key: string]: string | undefined } = {};
|
||||
public playing: boolean = false;
|
||||
|
||||
constructor(private board: Board, private config: GameConfig) {
|
||||
this.board
|
||||
.frame(this.game.getBoardData(), {}, null)
|
||||
.then((_) => this.board.render());
|
||||
}
|
||||
|
||||
updateConfig(config: Partial<GameConfig>) {
|
||||
this.config = { ...this.config, ...config };
|
||||
}
|
||||
|
||||
load(game: Game) {
|
||||
this.game = game;
|
||||
this.header = game.getHeader();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async play() {
|
||||
this.playing = true;
|
||||
|
||||
while (this.playing) {}
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.playing = false;
|
||||
}
|
||||
|
||||
async prev() {
|
||||
const move = this.game.prev();
|
||||
|
||||
console.log({ ply: move?.ply, end: move?.end });
|
||||
|
||||
if (!move) {
|
||||
await this.board.titleFrame(this.header);
|
||||
this.board.render();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.board.frame(
|
||||
this.game.getBoardData(),
|
||||
this.header,
|
||||
move,
|
||||
this.game.materialInfo()
|
||||
);
|
||||
this.board.render();
|
||||
}
|
||||
|
||||
async next() {
|
||||
const move = this.game.next();
|
||||
|
||||
console.log({ ply: move?.ply, end: move?.end });
|
||||
|
||||
if (!move) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.board.frame(
|
||||
this.game.getBoardData(),
|
||||
this.header,
|
||||
move,
|
||||
this.game.materialInfo()
|
||||
);
|
||||
this.board.render();
|
||||
}
|
||||
|
||||
first() {}
|
||||
|
||||
last() {}
|
||||
}
|
||||
|
||||
// const play = async (
|
||||
// board: Board,
|
||||
// config: GameConfig,
|
||||
// pgn: string | null,
|
||||
// interval: number = 1000
|
||||
// ) => {
|
||||
// const game = new Game();
|
||||
|
||||
// if (pgn) {
|
||||
// game.loadPGN(pgn);
|
||||
// }
|
||||
|
||||
// console.log(game.pgn());
|
||||
|
||||
// const header = game.getHeader();
|
||||
|
||||
// await board.titleFrame(header);
|
||||
// board.render();
|
||||
// await board.frame(game.getBoardData(), header, null, game.materialInfo());
|
||||
// await delay(interval * 1);
|
||||
// board.render();
|
||||
|
||||
// while (true) {
|
||||
// const move = game.next();
|
||||
|
||||
// if (!move) {
|
||||
// break;
|
||||
// }
|
||||
|
||||
// await board.frame(game.getBoardData(), header, move, game.materialInfo());
|
||||
// await delay(interval);
|
||||
// board.render();
|
||||
// }
|
||||
|
||||
// await delay(interval * 5);
|
||||
// // play(board, config, pgn, interval);
|
||||
// };
|
||||
|
||||
const createDownloadLink = async (pgn: string, boardConfig: BoardConfig) => {
|
||||
const file = await createAnimation(pgn, boardConfig, "GIF");
|
||||
const link = document.createElement("a");
|
||||
@@ -172,14 +58,21 @@ const main = async () => {
|
||||
// const interval = 1000;
|
||||
// play(board, gameConfig, pgn, interval);
|
||||
|
||||
const player = new Player(board, gameConfig).load(new Game().loadPGN(pgn));
|
||||
const player = new Player(board, gameConfig);
|
||||
|
||||
await player.load(new Game().loadPGN(pgn));
|
||||
|
||||
// @ts-ignore
|
||||
window.player = player;
|
||||
|
||||
document.addEventListener("click", () => {
|
||||
player.next();
|
||||
});
|
||||
// @ts-ignore
|
||||
window.load = async (pgn: string) => {
|
||||
await player.load(new Game().loadPGN(pgn));
|
||||
};
|
||||
|
||||
// document.addEventListener("click", () => {
|
||||
// player.next();
|
||||
// });
|
||||
|
||||
document.addEventListener(
|
||||
"contextmenu",
|
||||
@@ -192,18 +85,21 @@ const main = async () => {
|
||||
);
|
||||
|
||||
document.addEventListener("keydown", ({ key }) => {
|
||||
console.log(key);
|
||||
switch (key) {
|
||||
case "ArrowLeft":
|
||||
player.pause();
|
||||
player.prev();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
player.pause();
|
||||
player.next();
|
||||
break;
|
||||
case "ArrowUp":
|
||||
player.pause();
|
||||
player.first();
|
||||
break;
|
||||
case "ArrowDown":
|
||||
player.pause();
|
||||
player.last();
|
||||
break;
|
||||
case " ":
|
||||
@@ -221,9 +117,51 @@ const main = async () => {
|
||||
}
|
||||
});
|
||||
|
||||
createDownloadLink(pgn, boardConfig).then((link) => {
|
||||
document.body.appendChild(link);
|
||||
const hammer = new Hammer.Manager(board.canvas);
|
||||
hammer.add(new Hammer.Swipe());
|
||||
hammer.add(new Hammer.Pinch());
|
||||
hammer.add(new Hammer.Press({ time: 500 }));
|
||||
hammer.add(new Hammer.Tap({ taps: 2 }));
|
||||
|
||||
hammer.on("swiperight", () => {
|
||||
player.pause();
|
||||
player.prev();
|
||||
});
|
||||
|
||||
hammer.on("swipeleft", () => {
|
||||
player.pause();
|
||||
player.next();
|
||||
});
|
||||
|
||||
hammer.on("swipeup", () => {
|
||||
player.pause();
|
||||
player.first();
|
||||
});
|
||||
|
||||
hammer.on("swipedown", () => {
|
||||
player.pause();
|
||||
player.last();
|
||||
});
|
||||
|
||||
hammer.on("pinchin", () => {
|
||||
board.showBorder();
|
||||
});
|
||||
|
||||
hammer.on("pinchout", () => {
|
||||
board.hideBorder();
|
||||
});
|
||||
|
||||
hammer.on("tap", () => {
|
||||
player.playing ? player.pause() : player.play();
|
||||
});
|
||||
|
||||
hammer.on("press", () => {
|
||||
board.flip();
|
||||
});
|
||||
|
||||
// createDownloadLink(pgn, boardConfig).then((link) => {
|
||||
// document.body.appendChild(link);
|
||||
// });
|
||||
};
|
||||
|
||||
WebFont.load({
|
||||
|
||||
155
src/player/Player.ts
Normal file
155
src/player/Player.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { GameConfig } from "../types";
|
||||
import Board from "../board/Board";
|
||||
import Game from "../game/Game";
|
||||
|
||||
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
class Player {
|
||||
private interval = 1000;
|
||||
private game: Game = new Game();
|
||||
private header: { [key: string]: string | undefined } = {};
|
||||
private start: boolean = true;
|
||||
private end: boolean = false;
|
||||
public playing: boolean = false;
|
||||
private firstRender: Promise<void>;
|
||||
|
||||
constructor(private board: Board, private config: GameConfig) {
|
||||
this.firstRender = this.board
|
||||
.frame(this.game.getBoardData(), {}, null)
|
||||
.then((_) => this.board.render());
|
||||
}
|
||||
|
||||
updateConfig(config: Partial<GameConfig>) {
|
||||
this.config = { ...this.config, ...config };
|
||||
}
|
||||
|
||||
async load(game: Game) {
|
||||
this.pause();
|
||||
await this.firstRender;
|
||||
|
||||
this.game = game;
|
||||
this.header = game.getHeader();
|
||||
|
||||
await this.board.titleFrame(this.game.getHeader());
|
||||
this.board.render();
|
||||
}
|
||||
|
||||
async play() {
|
||||
this.playing = true;
|
||||
|
||||
while (true) {
|
||||
await delay(this.interval);
|
||||
if (!this.playing) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.next();
|
||||
}
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.playing = false;
|
||||
}
|
||||
|
||||
async prev() {
|
||||
if (this.start) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.end = false;
|
||||
|
||||
const move = this.game.prev();
|
||||
|
||||
if (!move) {
|
||||
await this.board.titleFrame(this.header);
|
||||
this.board.render();
|
||||
this.start = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.board.frame(
|
||||
this.game.getBoardData(),
|
||||
this.header,
|
||||
move,
|
||||
this.game.materialInfo()
|
||||
);
|
||||
this.board.render();
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (this.end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.start) {
|
||||
await this.board.frame(
|
||||
this.game.getBoardData(),
|
||||
this.header,
|
||||
null,
|
||||
this.game.materialInfo()
|
||||
);
|
||||
this.board.render();
|
||||
this.start = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const move = this.game.next();
|
||||
|
||||
this.end = move?.end === 0;
|
||||
|
||||
if (!move) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.board.frame(
|
||||
this.game.getBoardData(),
|
||||
this.header,
|
||||
move,
|
||||
this.game.materialInfo()
|
||||
);
|
||||
this.board.render();
|
||||
}
|
||||
|
||||
async first() {
|
||||
this.game.first();
|
||||
|
||||
await this.board.titleFrame(this.header);
|
||||
this.board.render();
|
||||
|
||||
this.end = false;
|
||||
this.start = true;
|
||||
}
|
||||
|
||||
async last() {
|
||||
const move = this.game.last();
|
||||
|
||||
await this.board.frame(
|
||||
this.game.getBoardData(),
|
||||
this.header,
|
||||
move,
|
||||
this.game.materialInfo()
|
||||
);
|
||||
this.board.render();
|
||||
|
||||
this.end = true;
|
||||
this.start = false;
|
||||
}
|
||||
|
||||
async goto(ply: number) {
|
||||
const move = this.game.goto(ply);
|
||||
|
||||
await this.board.frame(
|
||||
this.game.getBoardData(),
|
||||
this.header,
|
||||
move,
|
||||
this.game.materialInfo()
|
||||
);
|
||||
this.board.render();
|
||||
|
||||
this.start = false;
|
||||
this.end = move?.end === 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default Player;
|
||||
@@ -10,8 +10,8 @@
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #111;
|
||||
background-image: url(background.png);
|
||||
background-color: #191d24;
|
||||
/* background-image: url(background.png); */
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
@@ -1,47 +1,4 @@
|
||||
const pgns = [
|
||||
`[Event "Paris Opera"]
|
||||
[Site "Paris FRA"]
|
||||
[Date "1858.??.??"]
|
||||
[EventDate "?"]
|
||||
[Round "?"]
|
||||
[Result "1-0"]
|
||||
[White "Paul Morphy"]
|
||||
[Black "Duke Karl / Count Isouard"]
|
||||
[ECO "C41"]
|
||||
[WhiteElo "?"]
|
||||
[BlackElo "?"]
|
||||
[PlyCount "33"]
|
||||
|
||||
1.e4 e5 2.Nf3 d6 3.d4 Bg4 {This is a weak move
|
||||
already.--Fischer} 4.dxe5 Bxf3 5.Qxf3 dxe5 6.Bc4 Nf6 7.Qb3 Qe7
|
||||
8.Nc3 c6 9.Bg5 {Black is in what's like a zugzwang position
|
||||
here. He can't develop the [Queen's] knight because the pawn
|
||||
is hanging, the bishop is blocked because of the
|
||||
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`,
|
||||
|
||||
`[Event "Karpov - Kasparov World Championship Match"]
|
||||
[Site "Moscow URS"]
|
||||
[Date "1985.10.15"]
|
||||
[EventDate "?"]
|
||||
[Round "16"]
|
||||
[Result "0-1"]
|
||||
[White "Anatoly Karpov"]
|
||||
[Black "Garry Kasparov"]
|
||||
[ECO "B44"]
|
||||
[WhiteElo "?"]
|
||||
[BlackElo "?"]
|
||||
[PlyCount "80"]
|
||||
|
||||
1.e4 c5 2.Nf3 e6 3.d4 cxd4 4.Nxd4 Nc6 5.Nb5 d6 6.c4 Nf6 7.N1c3
|
||||
a6 8.Na3 d5 9.cxd5 exd5 10.exd5 Nb4 11.Be2 Bc5 12.O-O O-O
|
||||
13.Bf3 Bf5 14.Bg5 Re8 15.Qd2 b5 16.Rad1 Nd3 17.Nab1 h6 18.Bh4
|
||||
b4 19.Na4 Bd6 20.Bg3 Rc8 21.b3 g5 22.Bxd6 Qxd6 23.g3 Nd7
|
||||
24.Bg2 Qf6 25.a3 a5 26.axb4 axb4 27.Qa2 Bg6 28.d6 g4 29.Qd2
|
||||
Kg7 30.f3 Qxd6 31.fxg4 Qd4+ 32.Kh1 Nf6 33.Rf4 Ne4 34.Qxd3 Nf2+
|
||||
35.Rxf2 Bxd3 36.Rfd2 Qe3 37.Rxd3 Rc1 38.Nb2 Qf2 39.Nd2 Rxd1+
|
||||
40.Nxd1 Re1+ 0-1`,
|
||||
|
||||
`[Event "Hoogovens Group A"]
|
||||
[Site "Wijk aan Zee NED"]
|
||||
[Date "1999.01.20"]
|
||||
@@ -65,85 +22,28 @@ Qc4 31. Qxf6 Kxa3 32. Qxa6+ Kxb4 33. c3+ Kxc3 34. Qa1+ Kd2
|
||||
35. Qb2+ Kd1 36. Bf1 Rd2 37. Rd7 Rxd7 38. Bxc4 bxc4 39. Qxh8
|
||||
Rd3 40. Qa8 c3 41. Qa4+ Ke1 42. f4 f5 43. Kc1 Rd2 44. Qa7 1-0`,
|
||||
|
||||
`[Event "Rated Bullet game"]
|
||||
[Site "https://lichess.org/cGLWSWdH"]
|
||||
[Date "2022.01.26"]
|
||||
[White "caderek"]
|
||||
[Black "Gacioreks"]
|
||||
[Result "1-0"]
|
||||
[UTCDate "2022.01.26"]
|
||||
[UTCTime "13:44:13"]
|
||||
[WhiteElo "1350"]
|
||||
[BlackElo "1385"]
|
||||
[WhiteRatingDiff "+6"]
|
||||
[BlackRatingDiff "-6"]
|
||||
[Variant "Standard"]
|
||||
[TimeControl "60+0"]
|
||||
[ECO "A00"]
|
||||
[Opening "Polish Opening: Wolferts Gambit"]
|
||||
[Termination "Normal"]
|
||||
[Annotator "lichess.org"]
|
||||
|
||||
1. b4 e5 2. Bb2 c5 { A00 Polish Opening: Wolferts Gambit } 3. b5 d6 4. e3 c4 5. Bxc4 Be6 6. Bxe6 fxe6 7. Nf3 d5 8. Nxe5 Bd6 9. Nf3 Qf6 10. Bxf6 gxf6 11. O-O e5 12. d3 e4 13. dxe4 dxe4 14. Nd4 Be5 15. c3 a6 16. a4 axb5 17. Nxb5 Nc6 18. Qc2 Nb4 19. Qd2 Nh6 20. Rd1 O-O 21. Qe2 b6 22. Nd2 Nd3 23. Nxe4 Nxf2 24. Qxf2 Nf5 25. Qxf5 Bxc3 26. Qe6+ Rf7 27. Nbxc3 Rc8 28. Qxc8+ Kg7 29. Qe6 Re7 30. Qxf6+ Kg8 31. Ng5 Rg7 32. Rd8# { White wins by checkmate. } 1-0`,
|
||||
|
||||
`[Event "Casual Correspondence game"]
|
||||
[Site "https://lichess.org/3torGyiS"]
|
||||
[Date "2022.01.27"]
|
||||
[White "lichess AI level 1"]
|
||||
[Black "caderek"]
|
||||
[Result "0-1"]
|
||||
[UTCDate "2022.01.27"]
|
||||
[UTCTime "22:35:13"]
|
||||
[WhiteElo "?"]
|
||||
[BlackElo "1500"]
|
||||
[Variant "Standard"]
|
||||
[TimeControl "-"]
|
||||
[ECO "A10"]
|
||||
[Opening "English Opening: Anglo-Lithuanian Variation"]
|
||||
[Termination "Normal"]
|
||||
[Annotator "lichess.org"]
|
||||
|
||||
1. d4 d6 2. Qd3 g6 3. Qxg6 Bg7 4. Qxg7 h6 5. Qxh8 f6 6. Qxg8+ Kd7 7. Qxd8+ Ke6 8. Qxc8+ Kf7 9. Qxb8 Kg6 10. Qxa8 Kh7 11. Qxa7 Kg6 12. Qxb7 Kh5 13. Qxc7 Kg6 14. Qxe7 Kh5 15. Qxd6 Kg4 16. Qxf6 Kh5 17. Qxh6+ Kg4 18. a4 Kf5 19. a5 Ke4 20. a6 Kd5 21. a7 Kc4 22. a8=Q Kb4 23. d5 Kb5 24. d6 Kb6 25. d7+ Kb5 26. d8=Q Kb4 27. c3+ Kb3 28. g4 Kc2 29. g5 Kb3 30. g6 Kc2 31. g7 Kb3 32. g8=Q+ Kc2 33. Qgh8 Kb3 34. f4 Kc2 35. f5 Kb3 36. f6 Kc2 37. e4 Kb3 38. e5 Kc2 39. e6 Kb3 40. f7 Kc2 41. f8=Q Kb3 42. e7 Kc2 43. e8=Q Kb3 44. h4 Kc2 45. h5 Kb3 46. Qhg5 Kc2 47. h6 Kb3 48. h7 Kc2 49. Qhg7 Kb3 50. h8=Q Kc2 51. b4 Kb3 52. b5 Kc2 53. b6 Kb3 54. b7 Kc2 55. Bb5 Kb3 56. b8=Q Kc2 57. c4 Kb3 58. c5 Kc2 59. c6 Kb3 60. c7 Kc2 61. Qba7 Kb3 62. Q7f6 Kc2 63. c8=Q+ Kb3 64. Qf8f7+ Kb4 65. Qdd6#`,
|
||||
|
||||
`[Event "Casual Bullet game"]
|
||||
[Site "https://lichess.org/R3wUJdwo"]
|
||||
[Date "2022.01.28"]
|
||||
[White "caderek"]
|
||||
[Black "thrillhouse742"]
|
||||
[Result "0-1"]
|
||||
[UTCDate "2022.01.28"]
|
||||
[UTCTime "20:53:46"]
|
||||
[WhiteElo "1328"]
|
||||
[BlackElo "1819"]
|
||||
[Variant "Standard"]
|
||||
[TimeControl "60+0"]
|
||||
[ECO "A00"]
|
||||
[Opening "Polish Opening"]
|
||||
[Termination "Time forfeit"]
|
||||
[Annotator "lichess.org"]
|
||||
|
||||
1. b4 { A00 Polish Opening } d5 2. Bb2 Nf6 3. e3 Bg4 4. Be2 Bxe2 5. Qxe2 e6 6. Nf3 Nbd7 7. O-O?? { (0.00 → -1.69) Blunder. b5 was best. } (7. b5 a6 8. a4 axb5 9. axb5 Rxa1 10. Bxa1 Bd6 11. O-O e5) 7... c6? { (-1.69 → -0.17) Mistake. Bxb4 was best. } (7... Bxb4 8. d3 O-O 9. Nbd2 Re8 10. Rab1 a5 11. Ba1 Qc8 12. c4 c6 13. Ne5 Nxe5 14. Bxe5) 8. a3 Bd6 9. d3 Rc8? { (-0.08 → 0.95) Mistake. O-O was best. } (9... O-O 10. c4) 10. Nbd2?! { (0.95 → 0.14) Inaccuracy. e4 was best. } (10. e4) 10... Bb8?! { (0.14 → 0.86) Inaccuracy. O-O was best. } (10... O-O 11. c4 e5 12. Rfc1 Re8 13. cxd5 cxd5 14. Nb3 Nf8 15. Rxc8 Qxc8 16. Rc1 Qd7 17. Nc5) 11. c4?! { (0.86 → 0.21) Inaccuracy. e4 was best. } (11. e4 O-O 12. e5 Ng4 13. c4 dxc4 14. Nxc4 Nh6 15. Rac1 Nb6 16. Na5 Qd7 17. Nb3 Na4) 11... dxc4?! { (0.21 → 1.06) Inaccuracy. O-O was best. } (11... O-O 12. e4) 12. dxc4 O-O 13. Qd1?! { (1.18 → 0.63) Inaccuracy. c5 was best. } (13. c5 b6 14. cxb6 Qxb6 15. Nc4 Qa6 16. Bxf6 Nxf6 17. Nd4 Qb7 18. Nb3 Nd7 19. Nca5 Qb5) 13... Qc7 14. g3 Ng4?! { (0.77 → 1.38) Inaccuracy. b6 was best. } (14... b6) 15. h3?? { (1.38 → -0.17) Blunder. c5 was best. } (15. c5) 15... Ngf6? { (-0.17 → 1.03) Mistake. Nxe3 was best. } (15... Nxe3 16. fxe3) 16. Kg2 Nh5 17. Ne4 Ndf6?! { (1.05 → 1.65) Inaccuracy. c5 was best. } (17... c5 18. b5 Rcd8 19. Qe2 h6 20. Nh4 Ndf6 21. Nxf6+ Nxf6 22. Bxf6 gxf6 23. a4 Rd7 24. Nf3) 18. Nxf6+ Nxf6 19. Qd4?! { (1.47 → 0.68) Inaccuracy. Bxf6 was best. } (19. Bxf6) 19... Rfd8 20. Qc3 Kf8?! { (0.68 → 1.36) Inaccuracy. Qe7 was best. } (20... Qe7 21. Qc2 e5 22. e4 Ne8 23. c5 f6 24. Bc1 Nc7 25. Be3 Ne6 26. a4 Qf7 27. Nh4) 21. Rad1?! { (1.36 → 0.77) Inaccuracy. Qc2 was best. } (21. Qc2 Kg8) 21... Rxd1 22. Rxd1 Rd8 23. Rxd8+ Qxd8 24. Ng5 h6? { (0.64 → 1.97) Mistake. Kg8 was best. } (24... Kg8 25. Ne4) 25. Nh7+ Nxh7?? { (2.04 → 6.18) Blunder. Kg8 was best. } (25... Kg8 26. Nxf6+) 26. Qxg7+ Ke7 27. Qxh7 Qd7 28. Qxh6 Kd8 29. Qg5+ Kc7 30. Qf4+ Qd6 31. Qxd6+ Kxd6 32. f4 Kd7 33. Be5 Bxe5 34. fxe5 a6 35. g4 Ke7 36. h4 f6 37. h5 fxe5 38. Kg3 Kf6 39. h6 Kg6 40. h7 Kxh7 { Black wins on time. } 0-1`,
|
||||
|
||||
`[Event "Rated Bullet game"]
|
||||
[Site "https://lichess.org/tfEplTYl"]
|
||||
[Date "2022.01.28"]
|
||||
[White "PHOS747"]
|
||||
[Black "caderek"]
|
||||
[Result "0-1"]
|
||||
[UTCDate "2022.01.28"]
|
||||
[UTCTime "22:35:37"]
|
||||
[WhiteElo "1335"]
|
||||
`[Black "caderek"]
|
||||
[BlackElo "1325"]
|
||||
[WhiteRatingDiff "-6"]
|
||||
[BlackRatingDiff "+6"]
|
||||
[Variant "Standard"]
|
||||
[TimeControl "60+0"]
|
||||
[ECO "B20"]
|
||||
[Opening "Sicilian Defense: Bowdler Attack"]
|
||||
[Termination "Normal"]
|
||||
[Annotator "lichess.org"]
|
||||
[Date "2022.01.28"]
|
||||
[Event "Rated Bullet game"]
|
||||
[Result "0-1"]
|
||||
[Site "https://lichess.org/tfEplTYl"]
|
||||
[White "PHOS747"]
|
||||
[WhiteElo "1335"]
|
||||
|
||||
1. e4 c5 2. Bc4 { B20 Sicilian Defense: Bowdler Attack } Nc6 3. Qf3 Ne5 4. Qc3 Nxc4 5. Qxc4 b6 6. Nh3 e6 7. Ng5 Qxg5 8. d4 Qxc1+ 9. Ke2 Qxh1 10. f4 Qxg2+ 11. Ke3 Qxh2 12. Nd2 Qh3+ 13. Nf3 Nf6 14. e5 Ng4+ 15. Ke4 Bb7+ 16. Kd3 Qxf3+ 17. Kd2 Qe3+ 18. Kd1 Nf2# { Black wins by checkmate. } 0-1`,
|
||||
1. e4 c5 2. Bc4 Nc6 3. Qf3 Ne5 4. Qc3 Nxc4 5. Qxc4 b6 6. Nh3 e6 7. Ng5 Qxg5 8. d4 Qxc1+ 9. Ke2 Qxh1 10. f4 Qxg2+ 11. Ke3 Qxh2 12. Nd2 Qh3+ 13. Nf3 Nf6 14. e5 Ng4+ 15. Ke4 Bb7+ 16. Kd3 Qxf3+ 17. Kd2 Qe3+ 18. Kd1 Nf2# 0-1`,
|
||||
|
||||
`[Black "Carlsen, Magnus"]
|
||||
[BlackElo "2865"]
|
||||
[Date "2022.01.29"]
|
||||
[Event "Tata Steel Chess Masters 2022"]
|
||||
[Result "0-1"]
|
||||
[Round "12.1"]
|
||||
[Site "Wijk aan Zee, Netherlands"]
|
||||
[White "Caruana, Fabiano"]
|
||||
[WhiteElo "2792"]
|
||||
|
||||
1. e4 c5 2. Nf3 Nc6 3. Bb5 g6 4. O-O Bg7 5. c3 Nf6 6. Re1 O-O 7. d4 d5 8. e5 Ne4 9. Be3 cxd4 10. cxd4 Qb6 11. Qe2 Bd7 12. Ba4 Rac8 13. Nc3 Nxc3 14. bxc3 Qd8 15. Bb3 Na5 16. Rac1 Nxb3 17. axb3 Qb6 18. Qa2 a5 19. Qa3 Rfe8 20. c4 dxc4 21. bxc4 Qa6 22. c5 Bc6 23. Rb1 a4 24. Rec1 Rcd8 25. Nd2 Qe2 26. f3 Rxd4 27. Bxd4 Qxd2 28. Rd1 Qf4 29. Qb4 e6 30. Bc3 Qxb4 31. Bxb4 Bxe5 32. Ba3 Bf6 33. Kf2 Be7 34. Rb6 Rc8 35. Rd2 f6 36. f4 e5 37. fxe5 fxe5 38. Re2 Rf8+ 39. Ke1 Rf5 40. Rb1 e4 41. Rc1 Bh4+ 42. g3 Bg5 43. Rb1 Rf3 44. Bc1 Bf6 45. Rb6 Rf5 46. Ba3 Kf7 47. Rf2 Rf3 48. Rxf3 exf3 49. Kf1 Bd4 0-1`,
|
||||
];
|
||||
|
||||
export default pgns;
|
||||
|
||||
@@ -8,6 +8,7 @@ export type GradientDir =
|
||||
export type GradientData = {
|
||||
dir: GradientDir;
|
||||
colors: string[];
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export type Gradient = {
|
||||
@@ -17,6 +18,7 @@ export type Gradient = {
|
||||
|
||||
export type SolidData = {
|
||||
color: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export type Solid = {
|
||||
@@ -26,6 +28,7 @@ export type Solid = {
|
||||
|
||||
export type ImageData = {
|
||||
src: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export type Image = {
|
||||
|
||||
95
src/utils/colors.ts
Normal file
95
src/utils/colors.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import chunk from "@arrows/array/chunk_";
|
||||
|
||||
/**
|
||||
* Converts color in RGB to HSL
|
||||
*
|
||||
* @param r - in [0,255]
|
||||
* @param g - in [0,255]
|
||||
* @param b - in [0,255]
|
||||
* @returns [h, l, s] - h in [0,360) and s, l in [0,1]
|
||||
*/
|
||||
const rgb2hsl = (r: number, g: number, b: number) => {
|
||||
r = r / 255;
|
||||
g = g / 255;
|
||||
b = b / 255;
|
||||
|
||||
let v = Math.max(r, g, b),
|
||||
c = v - Math.min(r, g, b),
|
||||
f = 1 - Math.abs(v + v - c - 1);
|
||||
let h =
|
||||
c && (v == r ? (g - b) / c : v == g ? 2 + (b - r) / c : 4 + (r - g) / c);
|
||||
|
||||
return [60 * (h < 0 ? h + 6 : h), f ? c / f : 0, (v + v - c) / 2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts color in HSL to RGB
|
||||
*
|
||||
* @param h - in [0,360]
|
||||
* @param s - in [0,1]
|
||||
* @param l - in [0,1]
|
||||
* @returns [r, g, b] - r, g, b in [0,255]
|
||||
*/
|
||||
let hsl2rgb = (h: number, s: number, l: number) => {
|
||||
const a = s * Math.min(l, 1 - l);
|
||||
const f = (n: number, k = (n + h / 30) % 12) =>
|
||||
l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||
|
||||
return (
|
||||
"#" +
|
||||
[Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)]
|
||||
.map((x) => x.toString(16))
|
||||
.join("")
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes HLS of the provided color
|
||||
*
|
||||
* @param r - in [0,255]
|
||||
* @param g - in [0,255]
|
||||
* @param b - in [0,255]
|
||||
* @param deltaH - in [-360, 360] (-) towards yellow (+) towards blue
|
||||
* @param deltaS - in [-1, 1]
|
||||
* @param deltaL - in [-1, 1]
|
||||
* @returns [r, g, b] - r, g, b in [0,255]
|
||||
*/
|
||||
const changeHSL = (
|
||||
colorHex: string,
|
||||
deltaH: number,
|
||||
deltaS: number = 0,
|
||||
deltaL: number = 0
|
||||
) => {
|
||||
const [r, g, b] = chunk(
|
||||
colorHex.length === 4 ? 1 : 2,
|
||||
colorHex.slice(1).split("")
|
||||
).map((x) => parseInt(x.join(""), 16));
|
||||
|
||||
let [h, s, l] = rgb2hsl(r, g, b);
|
||||
const absDelta = Math.abs(deltaH);
|
||||
|
||||
if (h >= 60 && h < 240) {
|
||||
h += deltaH;
|
||||
if (h < 60 && h > 60 - absDelta) {
|
||||
h = 60;
|
||||
} else if (h > 240 && h < 240 + absDelta) {
|
||||
h = 240;
|
||||
}
|
||||
} else {
|
||||
h -= deltaH;
|
||||
if (h > 60 && h < 60 + absDelta) {
|
||||
h = 60;
|
||||
} else if (h < 240 && h > 240 - absDelta) {
|
||||
h = 240;
|
||||
}
|
||||
}
|
||||
|
||||
h = h > 360 ? h - 360 : h > 0 ? h + 360 : h;
|
||||
|
||||
s = Math.min(1, Math.max(0, s + deltaS));
|
||||
l = Math.min(1, Math.max(0, l + deltaL));
|
||||
|
||||
return hsl2rgb(h, s, l);
|
||||
};
|
||||
|
||||
export { changeHSL };
|
||||
Reference in New Issue
Block a user