WIP
This commit is contained in:
@@ -6,8 +6,12 @@
|
|||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<div id="app"></div>
|
<div class="layout">
|
||||||
|
<div id="setup" class="setup-box"></div>
|
||||||
|
<div id="board" class="board-box"></div>
|
||||||
|
<div id="moves" class="moves-box"></div>
|
||||||
|
</div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
27
package-lock.json
generated
27
package-lock.json
generated
@@ -12,6 +12,7 @@
|
|||||||
"@types/chess.js": "^0.11.2",
|
"@types/chess.js": "^0.11.2",
|
||||||
"@types/gif.js": "^0.2.2",
|
"@types/gif.js": "^0.2.2",
|
||||||
"chess.js": "^0.12.0",
|
"chess.js": "^0.12.0",
|
||||||
|
"common-tags": "^1.8.2",
|
||||||
"gif.js": "^0.2.0",
|
"gif.js": "^0.2.0",
|
||||||
"h264-mp4-encoder": "^1.0.12",
|
"h264-mp4-encoder": "^1.0.12",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
"webm-writer": "^1.0.0"
|
"webm-writer": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/common-tags": "^1.8.1",
|
||||||
"@types/hammerjs": "^2.0.41",
|
"@types/hammerjs": "^2.0.41",
|
||||||
"@types/node": "^17.0.8",
|
"@types/node": "^17.0.8",
|
||||||
"@types/webfontloader": "^1.6.34",
|
"@types/webfontloader": "^1.6.34",
|
||||||
@@ -46,6 +48,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/chess.js/-/chess.js-0.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chess.js/-/chess.js-0.11.2.tgz",
|
||||||
"integrity": "sha512-ZgT0f5Dm1x+kjA/RhCQpBM2NoUGkdk9JFdwncW7/GdqzHCOkLsOTq4k/EKRiRTCFLXR4fTKk8j7KNOmzzHQVgg=="
|
"integrity": "sha512-ZgT0f5Dm1x+kjA/RhCQpBM2NoUGkdk9JFdwncW7/GdqzHCOkLsOTq4k/EKRiRTCFLXR4fTKk8j7KNOmzzHQVgg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/common-tags": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-20R/mDpKSPWdJs5TOpz3e7zqbeCNuMCPhV7Yndk9KU2Rbij2r5W4RzwDPkzC+2lzUqXYu9rFzTktCBnDjHuNQg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/gif.js": {
|
"node_modules/@types/gif.js": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.2.tgz",
|
||||||
@@ -74,6 +82,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-0.12.0.tgz",
|
||||||
"integrity": "sha512-eF1xf4j88r/AwIRxqV1FXZbVgMt4yUx05xrL+ZMqUrY+snFrUxGVHs0VgEd3AvngujDE9th7XZqqCwEKEQ7mWQ=="
|
"integrity": "sha512-eF1xf4j88r/AwIRxqV1FXZbVgMt4yUx05xrL+ZMqUrY+snFrUxGVHs0VgEd3AvngujDE9th7XZqqCwEKEQ7mWQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/common-tags": {
|
||||||
|
"version": "1.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
|
||||||
|
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.13.15",
|
"version": "0.13.15",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
|
||||||
@@ -2889,6 +2905,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/chess.js/-/chess.js-0.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chess.js/-/chess.js-0.11.2.tgz",
|
||||||
"integrity": "sha512-ZgT0f5Dm1x+kjA/RhCQpBM2NoUGkdk9JFdwncW7/GdqzHCOkLsOTq4k/EKRiRTCFLXR4fTKk8j7KNOmzzHQVgg=="
|
"integrity": "sha512-ZgT0f5Dm1x+kjA/RhCQpBM2NoUGkdk9JFdwncW7/GdqzHCOkLsOTq4k/EKRiRTCFLXR4fTKk8j7KNOmzzHQVgg=="
|
||||||
},
|
},
|
||||||
|
"@types/common-tags": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-20R/mDpKSPWdJs5TOpz3e7zqbeCNuMCPhV7Yndk9KU2Rbij2r5W4RzwDPkzC+2lzUqXYu9rFzTktCBnDjHuNQg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/gif.js": {
|
"@types/gif.js": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.2.tgz",
|
||||||
@@ -2917,6 +2939,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-0.12.0.tgz",
|
||||||
"integrity": "sha512-eF1xf4j88r/AwIRxqV1FXZbVgMt4yUx05xrL+ZMqUrY+snFrUxGVHs0VgEd3AvngujDE9th7XZqqCwEKEQ7mWQ=="
|
"integrity": "sha512-eF1xf4j88r/AwIRxqV1FXZbVgMt4yUx05xrL+ZMqUrY+snFrUxGVHs0VgEd3AvngujDE9th7XZqqCwEKEQ7mWQ=="
|
||||||
},
|
},
|
||||||
|
"common-tags": {
|
||||||
|
"version": "1.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
|
||||||
|
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="
|
||||||
|
},
|
||||||
"esbuild": {
|
"esbuild": {
|
||||||
"version": "0.13.15",
|
"version": "0.13.15",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/common-tags": "^1.8.1",
|
||||||
"@types/hammerjs": "^2.0.41",
|
"@types/hammerjs": "^2.0.41",
|
||||||
"@types/node": "^17.0.8",
|
"@types/node": "^17.0.8",
|
||||||
"@types/webfontloader": "^1.6.34",
|
"@types/webfontloader": "^1.6.34",
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
"@types/chess.js": "^0.11.2",
|
"@types/chess.js": "^0.11.2",
|
||||||
"@types/gif.js": "^0.2.2",
|
"@types/gif.js": "^0.2.2",
|
||||||
"chess.js": "^0.12.0",
|
"chess.js": "^0.12.0",
|
||||||
|
"common-tags": "^1.8.2",
|
||||||
"gif.js": "^0.2.0",
|
"gif.js": "^0.2.0",
|
||||||
"h264-mp4-encoder": "^1.0.12",
|
"h264-mp4-encoder": "^1.0.12",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { BoardConfig, PiecesStyle } from "./../types";
|
import { BoardConfig, PiecesStyle, Position } from "./../types";
|
||||||
import { Material, MoveWithDetails } from "../game/Game_x";
|
import { Style } from "../types";
|
||||||
import { Style, BoardData } from "../types";
|
|
||||||
import drawRectangle from "./layers/drawRectangle";
|
import drawRectangle from "./layers/drawRectangle";
|
||||||
import drawCoords from "./layers/drawCoords";
|
import drawCoords from "./layers/drawCoords";
|
||||||
import drawMoveIndicators from "./layers/drawMoveIndicators";
|
import drawMoveIndicators from "./layers/drawMoveIndicators";
|
||||||
@@ -37,14 +36,10 @@ class Board {
|
|||||||
private style: Style = boards.standard;
|
private style: Style = boards.standard;
|
||||||
private flipped: boolean = false;
|
private flipped: boolean = false;
|
||||||
private header: { [key: string]: string | undefined } = {};
|
private header: { [key: string]: string | undefined } = {};
|
||||||
private boardData: BoardData | null = null;
|
// private boardData: BoardData | null = null;
|
||||||
private ctx: CanvasRenderingContext2D;
|
|
||||||
private tempCtx: CanvasRenderingContext2D;
|
|
||||||
private borderVisible: boolean = true;
|
private borderVisible: boolean = true;
|
||||||
private lastMove: MoveWithDetails | null = null;
|
private lastPosition: Position | null = null;
|
||||||
private lastMaterial: Material | undefined = undefined;
|
// private lastMaterial: Material | undefined = undefined;
|
||||||
public canvas: HTMLCanvasElement = document.createElement("canvas");
|
|
||||||
private tempCanvas: HTMLCanvasElement = document.createElement("canvas");
|
|
||||||
private background: HTMLCanvasElement | null = null;
|
private background: HTMLCanvasElement | null = null;
|
||||||
private extraInfo: boolean = true;
|
private extraInfo: boolean = true;
|
||||||
private piecesStyle: PiecesStyle = "tatiana";
|
private piecesStyle: PiecesStyle = "tatiana";
|
||||||
@@ -54,6 +49,12 @@ class Board {
|
|||||||
private showChecks: boolean = true;
|
private showChecks: boolean = true;
|
||||||
private currentScreen: "title" | "move" = "move";
|
private currentScreen: "title" | "move" = "move";
|
||||||
|
|
||||||
|
private ctx: CanvasRenderingContext2D;
|
||||||
|
private tempCtx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
|
private tempCanvas: HTMLCanvasElement = document.createElement("canvas");
|
||||||
|
public canvas: HTMLCanvasElement = document.createElement("canvas");
|
||||||
|
|
||||||
constructor(config: Partial<BoardConfig> = {}) {
|
constructor(config: Partial<BoardConfig> = {}) {
|
||||||
const ctx = this.canvas.getContext("2d");
|
const ctx = this.canvas.getContext("2d");
|
||||||
const tempCtx = this.tempCanvas.getContext("2d");
|
const tempCtx = this.tempCanvas.getContext("2d");
|
||||||
@@ -96,13 +97,8 @@ class Board {
|
|||||||
|
|
||||||
if (this.currentScreen === "title") {
|
if (this.currentScreen === "title") {
|
||||||
await this.titleFrame(this.header);
|
await this.titleFrame(this.header);
|
||||||
} else {
|
} else if (this.lastPosition !== null) {
|
||||||
await this.frame(
|
await this.frame(this.lastPosition, this.header);
|
||||||
this.boardData,
|
|
||||||
this.header,
|
|
||||||
this.lastMove,
|
|
||||||
this.lastMaterial
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
@@ -174,30 +170,6 @@ class Board {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
isCheck(move: MoveWithDetails | null) {
|
|
||||||
if (!move) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return move.san.includes("+");
|
|
||||||
}
|
|
||||||
|
|
||||||
isMate(move: MoveWithDetails | null) {
|
|
||||||
if (!move) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return move.san.includes("#");
|
|
||||||
}
|
|
||||||
|
|
||||||
getOppositeColor(color?: "w" | "b") {
|
|
||||||
if (!color) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return color === "w" ? "b" : "w";
|
|
||||||
}
|
|
||||||
|
|
||||||
async titleFrame(header: { [key: string]: string | undefined }) {
|
async titleFrame(header: { [key: string]: string | undefined }) {
|
||||||
this.currentScreen = "title";
|
this.currentScreen = "title";
|
||||||
this.header = header;
|
this.header = header;
|
||||||
@@ -265,22 +237,12 @@ class Board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async frame(
|
async frame(
|
||||||
boardData: BoardData | null,
|
position: Position | null,
|
||||||
header: { [key: string]: string | undefined },
|
header: { [key: string]: string | undefined }
|
||||||
move: MoveWithDetails | null = null,
|
|
||||||
material?: Material
|
|
||||||
) {
|
) {
|
||||||
this.currentScreen = "move";
|
this.currentScreen = "move";
|
||||||
this.lastMove = move;
|
this.lastPosition = position;
|
||||||
this.boardData = boardData;
|
this.header = header;
|
||||||
this.lastMaterial = material;
|
|
||||||
|
|
||||||
const check = this.isCheck(move)
|
|
||||||
? this.getOppositeColor(move?.color)
|
|
||||||
: undefined;
|
|
||||||
const mate = this.isMate(move)
|
|
||||||
? this.getOppositeColor(move?.color)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
this.tempCtx.clearRect(0, 0, this.size, this.size);
|
this.tempCtx.clearRect(0, 0, this.size, this.size);
|
||||||
|
|
||||||
@@ -290,11 +252,10 @@ class Board {
|
|||||||
|
|
||||||
this.tempCtx.drawImage((await this.background) as HTMLCanvasElement, 0, 0);
|
this.tempCtx.drawImage((await this.background) as HTMLCanvasElement, 0, 0);
|
||||||
|
|
||||||
if (boardData !== null) {
|
if (this.lastPosition?.move && this.showMoveIndicator) {
|
||||||
if (this.lastMove && this.showMoveIndicator) {
|
|
||||||
await drawMoveIndicators(
|
await drawMoveIndicators(
|
||||||
this.tempCtx,
|
this.tempCtx,
|
||||||
this.lastMove,
|
this.lastPosition.move,
|
||||||
this.squareSize,
|
this.squareSize,
|
||||||
this.style,
|
this.style,
|
||||||
this.borderWidth,
|
this.borderWidth,
|
||||||
@@ -318,18 +279,16 @@ class Board {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const piecesShadow = false;
|
if (!this.lastPosition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await drawPieces(
|
await drawPieces(
|
||||||
this.tempCtx,
|
this.tempCtx,
|
||||||
boardData,
|
this.lastPosition,
|
||||||
this.squareSize,
|
this.squareSize,
|
||||||
this.borderWidth,
|
this.borderWidth,
|
||||||
this.tiles,
|
|
||||||
this.flipped,
|
this.flipped,
|
||||||
check && this.showChecks ? check : undefined,
|
|
||||||
mate && this.showChecks ? mate : undefined,
|
|
||||||
piecesShadow,
|
|
||||||
this.margin,
|
this.margin,
|
||||||
this.piecesStyle
|
this.piecesStyle
|
||||||
);
|
);
|
||||||
@@ -342,14 +301,12 @@ class Board {
|
|||||||
this.scale,
|
this.scale,
|
||||||
this.margin,
|
this.margin,
|
||||||
this.style,
|
this.style,
|
||||||
header,
|
this.header,
|
||||||
this.flipped,
|
this.flipped,
|
||||||
move?.end === 0,
|
this.lastPosition
|
||||||
material && this.showMaterial ? material : undefined
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.ctx.clearRect(0, 0, this.size, this.size);
|
this.ctx.clearRect(0, 0, this.size, this.size);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Material } from "../../game/Game_x";
|
import { Style, Position } from "./../../types";
|
||||||
import { Style } from "./../../types";
|
|
||||||
import drawText from "./drawText";
|
import drawText from "./drawText";
|
||||||
|
|
||||||
const chessFontMapping: { [key: string]: string } = {
|
const chessFontMapping: { [key: string]: string } = {
|
||||||
@@ -20,8 +19,7 @@ const drawExtraInfo = async (
|
|||||||
style: Style,
|
style: Style,
|
||||||
data: { [key: string]: string | undefined },
|
data: { [key: string]: string | undefined },
|
||||||
flipped: boolean,
|
flipped: boolean,
|
||||||
lastMove: boolean,
|
position: Position
|
||||||
material?: Material
|
|
||||||
) => {
|
) => {
|
||||||
const fontSize = Math.round(20 * scale);
|
const fontSize = Math.round(20 * scale);
|
||||||
let offsetX = (margin - fontSize) / 2;
|
let offsetX = (margin - fontSize) / 2;
|
||||||
@@ -88,7 +86,7 @@ const drawExtraInfo = async (
|
|||||||
let rightMarginWhite = 0;
|
let rightMarginWhite = 0;
|
||||||
let rightMarginBlack = 0;
|
let rightMarginBlack = 0;
|
||||||
|
|
||||||
if (lastMove && data.Result) {
|
if (position.last && data.Result) {
|
||||||
const [resultWhite, resultBlack] = data.Result.split("-");
|
const [resultWhite, resultBlack] = data.Result.split("-");
|
||||||
|
|
||||||
const textWhite =
|
const textWhite =
|
||||||
@@ -125,8 +123,9 @@ const drawExtraInfo = async (
|
|||||||
rightMarginBlack = w + 20 * scale;
|
rightMarginBlack = w + 20 * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (material) {
|
const { diff, imbalance } = position.material;
|
||||||
const textWhite = material.diff > 0 ? `+${Math.abs(material.diff)}` : "";
|
|
||||||
|
const textWhite = diff > 0 ? `+${Math.abs(diff)}` : "";
|
||||||
|
|
||||||
rightMarginWhite += drawText(
|
rightMarginWhite += drawText(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -139,7 +138,7 @@ const drawExtraInfo = async (
|
|||||||
"right"
|
"right"
|
||||||
);
|
);
|
||||||
|
|
||||||
const textBlack = material.diff < 0 ? `+${Math.abs(material.diff)}` : "";
|
const textBlack = diff < 0 ? `+${Math.abs(diff)}` : "";
|
||||||
|
|
||||||
rightMarginBlack += drawText(
|
rightMarginBlack += drawText(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -152,7 +151,7 @@ const drawExtraInfo = async (
|
|||||||
"right"
|
"right"
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const [piece, count] of Object.entries(material.imbalance.w)) {
|
for (const [piece, count] of Object.entries(imbalance.w)) {
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const textWidth = drawText(
|
const textWidth = drawText(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -174,7 +173,7 @@ const drawExtraInfo = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [piece, count] of Object.entries(material.imbalance.b)) {
|
for (const [piece, count] of Object.entries(imbalance.b)) {
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const textWidth = drawText(
|
const textWidth = drawText(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -193,8 +192,6 @@ const drawExtraInfo = async (
|
|||||||
: piece === "p"
|
: piece === "p"
|
||||||
? textWidth * 0.4
|
? textWidth * 0.4
|
||||||
: textWidth * 0.6;
|
: textWidth * 0.6;
|
||||||
// i === count - 1 || piece !== "p" ? textWidth * 0.8 : textWidth * 0.4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,41 +1,25 @@
|
|||||||
import { BoardData, PieceType, PieceColor, PiecesStyle } from "../../types";
|
import { Position, PieceType, PieceColor, PiecesStyle } from "../../types";
|
||||||
import ImagesCache from "../loaders/PiecesCache";
|
import ImagesCache from "../loaders/PiecesCache";
|
||||||
// import drawCircle from "./drawCircle";
|
// import drawCircle from "./drawCircle";
|
||||||
|
|
||||||
const drawPieces = async (
|
const drawPieces = async (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
board: BoardData,
|
position: Position,
|
||||||
squareSize: number,
|
squareSize: number,
|
||||||
borderWidth: number,
|
borderWidth: number,
|
||||||
tiles: number,
|
|
||||||
flipped: boolean,
|
flipped: boolean,
|
||||||
check: "b" | "w" | undefined,
|
|
||||||
mate: "b" | "w" | undefined,
|
|
||||||
shadow: boolean,
|
|
||||||
margin: number,
|
margin: number,
|
||||||
piecesStyle: PiecesStyle
|
piecesStyle: PiecesStyle
|
||||||
) => {
|
) => {
|
||||||
for (let y = 0; y < 8; y++) {
|
const { placement, check, mate, turn } = position;
|
||||||
for (let x = 0; x < 8; x++) {
|
|
||||||
if (board[y][x] !== null) {
|
for (const { x, y, type, color } of placement) {
|
||||||
const { type, color } = board[y][x] as {
|
|
||||||
type: PieceType;
|
|
||||||
color: PieceColor;
|
|
||||||
};
|
|
||||||
const img = await ImagesCache.get(piecesStyle, type, color);
|
const img = await ImagesCache.get(piecesStyle, type, color);
|
||||||
const rank = flipped ? tiles - 1 - y : y;
|
const rank = flipped ? 8 - 1 - y : y;
|
||||||
const file = flipped ? tiles - 1 - x : x;
|
const file = flipped ? 8 - 1 - x : x;
|
||||||
|
|
||||||
const filters = [];
|
const filters = [];
|
||||||
|
|
||||||
if (shadow) {
|
|
||||||
filters.push(
|
|
||||||
`drop-shadow(${squareSize * 0.05}px ${squareSize * 0.05}px ${
|
|
||||||
squareSize * 0.05
|
|
||||||
}px rgba(0, 0, 0, 0.6))`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if ((color === check || color === mate) && type === "k") {
|
// if ((color === check || color === mate) && type === "k") {
|
||||||
// const hex = check ? "#ffa600" : "#ff002f";
|
// const hex = check ? "#ffa600" : "#ff002f";
|
||||||
// drawCircle(
|
// drawCircle(
|
||||||
@@ -49,13 +33,13 @@ const drawPieces = async (
|
|||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if ((color === check || color === mate) && type === "k") {
|
if ((check || mate) && type === "k" && color === turn) {
|
||||||
const hex = check ? "#ffa600" : "#ff002f";
|
const hex = mate ? "#ff002f" : "#ffa600";
|
||||||
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px ${hex})`);
|
filters.push(`drop-shadow(0 0 ${squareSize * 0.07}px ${hex})`);
|
||||||
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";
|
ctx.filter = filters.join(" ");
|
||||||
|
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
img,
|
img,
|
||||||
@@ -64,11 +48,9 @@ const drawPieces = async (
|
|||||||
squareSize,
|
squareSize,
|
||||||
squareSize
|
squareSize
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.filter = "none";
|
ctx.filter = "none";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default drawPieces;
|
export default drawPieces;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const style: Style = {
|
|||||||
border: {
|
border: {
|
||||||
type: "solid",
|
type: "solid",
|
||||||
data: {
|
data: {
|
||||||
color: "#40522f",
|
color: "#312e2b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
coords: {
|
coords: {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const createAnimation = async (
|
|||||||
? new MP4(board.width, board.height)
|
? new MP4(board.width, board.height)
|
||||||
: new WebM();
|
: new WebM();
|
||||||
|
|
||||||
const header = game.getHeader();
|
const header = game.header;
|
||||||
|
|
||||||
await board.titleFrame(header);
|
await board.titleFrame(header);
|
||||||
board.render();
|
board.render();
|
||||||
@@ -35,25 +35,12 @@ const createAnimation = async (
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await encoder.add(getData(board, encoder), 4);
|
await encoder.add(getData(board, encoder), 4);
|
||||||
|
|
||||||
await board.frame(game.getBoardData(), header, null, game.materialInfo());
|
for (let ply = 0; ply < game.length; ply++) {
|
||||||
|
const position = game.getPosition(ply);
|
||||||
|
await board.frame(position, header);
|
||||||
board.render();
|
board.render();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await encoder.add(getData(board, encoder), 1);
|
await encoder.add(getData(board, encoder), position.end === 0 ? 5 : 1);
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const move = game.next();
|
|
||||||
|
|
||||||
// console.log(move);
|
|
||||||
|
|
||||||
if (!move) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
await board.frame(game.getBoardData(), header, move, game.materialInfo());
|
|
||||||
board.render();
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
await encoder.add(getData(board, encoder), move.end === 0 ? 5 : 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await encoder.render();
|
return await encoder.render();
|
||||||
|
|||||||
171
src/game/Game.ts
171
src/game/Game.ts
@@ -1,9 +1,7 @@
|
|||||||
import { PieceType } from "../types";
|
import { PieceType, PieceColor, BoardData, Position } from "../types";
|
||||||
import { Chess, ChessInstance, Move } from "chess.js";
|
import { Chess, ChessInstance } from "chess.js";
|
||||||
import { cleanPGN } from "./PGNHelpers";
|
import { cleanPGN } from "./PGNHelpers";
|
||||||
|
|
||||||
export type MoveWithDetails = Move & { ply: number; end: number; fen: string };
|
|
||||||
|
|
||||||
const MATERIAL_VALUE: Map<PieceType, number> = new Map([
|
const MATERIAL_VALUE: Map<PieceType, number> = new Map([
|
||||||
["q", 9],
|
["q", 9],
|
||||||
["r", 5],
|
["r", 5],
|
||||||
@@ -13,110 +11,96 @@ const MATERIAL_VALUE: Map<PieceType, number> = new Map([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
class Game {
|
class Game {
|
||||||
private game: ChessInstance;
|
private positions: Position[] = [];
|
||||||
private replay: ChessInstance;
|
private game: ChessInstance = new Chess();
|
||||||
private moves: MoveWithDetails[];
|
|
||||||
private currentPly: number = 0;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {}
|
||||||
this.game = new Chess();
|
|
||||||
this.replay = new Chess();
|
|
||||||
this.moves = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
getMoves() {
|
|
||||||
return this.moves.map((move) => move.san);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFEN() {
|
|
||||||
return this.replay.fen();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadPGN(pgn: string) {
|
loadPGN(pgn: string) {
|
||||||
this.game.load_pgn(cleanPGN(pgn));
|
const game = new Chess();
|
||||||
this.game.delete_comments();
|
const replay = new Chess();
|
||||||
|
|
||||||
const moves = this.game.history({ verbose: true });
|
game.load_pgn(cleanPGN(pgn));
|
||||||
const tempGame = new Chess();
|
game.delete_comments();
|
||||||
const fen = this.game.header().FEN;
|
|
||||||
|
const moves = game.history({ verbose: true });
|
||||||
|
const fen = game.header().FEN;
|
||||||
|
|
||||||
if (fen) {
|
if (fen) {
|
||||||
tempGame.load(fen);
|
replay.load(fen);
|
||||||
this.replay.load(fen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.moves = moves.map((item, i) => {
|
this.positions = [
|
||||||
tempGame.move(item);
|
{
|
||||||
|
ply: 0,
|
||||||
|
move: null,
|
||||||
|
end: moves.length,
|
||||||
|
fen: replay.fen(),
|
||||||
|
check: replay.in_check(),
|
||||||
|
mate: replay.in_checkmate(),
|
||||||
|
turn: replay.turn(),
|
||||||
|
material: this.materialInfo(replay.board()),
|
||||||
|
placement: this.getPlacement(replay.fen()),
|
||||||
|
last: moves.length === 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return {
|
moves.forEach((item, i) => {
|
||||||
...item,
|
replay.move(item);
|
||||||
|
|
||||||
|
const currentFEN = replay.fen();
|
||||||
|
|
||||||
|
this.positions.push({
|
||||||
ply: i + 1,
|
ply: i + 1,
|
||||||
|
move: item,
|
||||||
end: moves.length - 1 - i,
|
end: moves.length - 1 - i,
|
||||||
fen: tempGame.fen(),
|
fen: currentFEN,
|
||||||
};
|
check: replay.in_check(),
|
||||||
|
mate: replay.in_checkmate(),
|
||||||
|
turn: replay.turn(),
|
||||||
|
material: this.materialInfo(replay.board()),
|
||||||
|
placement: this.getPlacement(currentFEN),
|
||||||
|
last: i === moves.length - 1,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.currentPly = 0;
|
this.game = game;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFEN(fen: string) {
|
loadFEN(fen: string) {
|
||||||
this.game.load(fen);
|
this.game = new Chess(fen);
|
||||||
return this;
|
this.positions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
next() {
|
private getPlacement(fen: string) {
|
||||||
const move = this.moves[this.currentPly];
|
const board = new Chess(fen).board();
|
||||||
|
|
||||||
if (!move) {
|
const placement: {
|
||||||
return null;
|
x: number;
|
||||||
|
y: number;
|
||||||
|
type: PieceType;
|
||||||
|
color: PieceColor;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
|
for (let y = 0; y < 8; y++) {
|
||||||
|
for (let x = 0; x < 8; x++) {
|
||||||
|
if (board[y][x] !== null) {
|
||||||
|
const { type, color } = board[y][x] as {
|
||||||
|
type: PieceType;
|
||||||
|
color: PieceColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
placement.push({ x, y, type, color });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentPly++;
|
return placement;
|
||||||
this.replay.move(move);
|
|
||||||
|
|
||||||
return move;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prev() {
|
private materialInfo(boardData: BoardData) {
|
||||||
const undo = Boolean(this.replay.undo());
|
const pieces = boardData.flat().filter(Boolean);
|
||||||
|
|
||||||
if (undo) {
|
|
||||||
this.currentPly--;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const move = this.moves[this.currentPly - 1];
|
|
||||||
|
|
||||||
return move;
|
|
||||||
}
|
|
||||||
|
|
||||||
last() {
|
|
||||||
while (this.next()) {}
|
|
||||||
return this.moves[this.moves.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
first() {
|
|
||||||
while (this.prev()) {}
|
|
||||||
return this.moves[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
goto(ply: number) {
|
|
||||||
if (ply === this.currentPly || ply < 0 || ply > this.moves.length - 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (this.currentPly !== ply) {
|
|
||||||
ply > this.currentPly ? this.next() : this.prev();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.moves[ply - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
materialInfo() {
|
|
||||||
const pieces = this.replay.board().flat().filter(Boolean);
|
|
||||||
|
|
||||||
const sum = { w: 0, b: 0 };
|
const sum = { w: 0, b: 0 };
|
||||||
const imbalance = {
|
const imbalance = {
|
||||||
@@ -146,20 +130,23 @@ class Game {
|
|||||||
return { sum, imbalance, count, diff: sum.w - sum.b };
|
return { sum, imbalance, count, diff: sum.w - sum.b };
|
||||||
}
|
}
|
||||||
|
|
||||||
getBoardData() {
|
get length() {
|
||||||
// console.log(this.replay.ascii());
|
return this.positions.length;
|
||||||
return this.replay.board();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeader() {
|
get header() {
|
||||||
return this.game.header();
|
return this.game.header();
|
||||||
}
|
}
|
||||||
|
|
||||||
pgn() {
|
getPosition(ply: number) {
|
||||||
return this.game.pgn();
|
const position = this.positions[ply];
|
||||||
}
|
|
||||||
|
return position ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Material = ReturnType<Game["materialInfo"]>;
|
getMoves() {
|
||||||
|
return this.positions.slice(1).map(({ move }) => move?.san) as string[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default Game;
|
export default Game;
|
||||||
|
|||||||
@@ -1,150 +0,0 @@
|
|||||||
// import { PieceType, BoardData } from "../types";
|
|
||||||
// import { Chess, ChessInstance, Move } from "chess.js";
|
|
||||||
// import { cleanPGN } from "./PGNHelpers";
|
|
||||||
|
|
||||||
// export type MoveWithDetails = Move & { ply: number; end: number; fen: string };
|
|
||||||
|
|
||||||
// const MATERIAL_VALUE: Map<PieceType, number> = new Map([
|
|
||||||
// ["q", 9],
|
|
||||||
// ["r", 5],
|
|
||||||
// ["b", 3],
|
|
||||||
// ["n", 3],
|
|
||||||
// ["p", 1],
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// class Game {
|
|
||||||
// private moves: MoveWithDetails[];
|
|
||||||
// private currentPly: number = 0;
|
|
||||||
|
|
||||||
// constructor() {
|
|
||||||
// this.moves = [];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// getMoves() {
|
|
||||||
// return this.moves.map((move) => move.san);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// loadPGN(pgn: string) {
|
|
||||||
// const game = new Chess();
|
|
||||||
// const replay = new Chess();
|
|
||||||
|
|
||||||
// game.load_pgn(cleanPGN(pgn));
|
|
||||||
// game.delete_comments();
|
|
||||||
|
|
||||||
// const moves = game.history({ verbose: true });
|
|
||||||
// const fen = game.header().FEN;
|
|
||||||
|
|
||||||
// if (fen) {
|
|
||||||
// replay.load(fen);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.moves = moves.map((item, i) => {
|
|
||||||
// replay.move(item);
|
|
||||||
|
|
||||||
// const currentFEN = replay.fen();
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// ...item,
|
|
||||||
// ply: i + 1,
|
|
||||||
// end: moves.length - 1 - i,
|
|
||||||
// fen: currentFEN,
|
|
||||||
// material: this.materialInfo(replay.board()),
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
|
|
||||||
// this.currentPly = 0;
|
|
||||||
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// next() {
|
|
||||||
// // const move = this.moves[this.currentPly];
|
|
||||||
// // if (!move) {
|
|
||||||
// // return null;
|
|
||||||
// // }
|
|
||||||
// // this.currentPly++;
|
|
||||||
// // this.replay.move(move);
|
|
||||||
// // return move;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// prev() {
|
|
||||||
// // const undo = Boolean(this.replay.undo());
|
|
||||||
// // if (undo) {
|
|
||||||
// // this.currentPly--;
|
|
||||||
// // } else {
|
|
||||||
// // return null;
|
|
||||||
// // }
|
|
||||||
// // const move = this.moves[this.currentPly - 1];
|
|
||||||
// // return move;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// last() {
|
|
||||||
// while (this.next()) {}
|
|
||||||
// return this.moves[this.moves.length - 1];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// first() {
|
|
||||||
// while (this.prev()) {}
|
|
||||||
// return this.moves[0];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// goto(ply: number) {
|
|
||||||
// if (ply === this.currentPly || ply < 0 || ply > this.moves.length - 1) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// while (this.currentPly !== ply) {
|
|
||||||
// ply > this.currentPly ? this.next() : this.prev();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return this.moves[ply - 1];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// materialInfo(boardData: BoardData) {
|
|
||||||
// const pieces = boardData.flat().filter(Boolean);
|
|
||||||
|
|
||||||
// const sum = { w: 0, b: 0 };
|
|
||||||
// const imbalance = {
|
|
||||||
// w: { p: 0, n: 0, b: 0, r: 0, q: 0 },
|
|
||||||
// b: { p: 0, n: 0, b: 0, r: 0, q: 0 },
|
|
||||||
// };
|
|
||||||
// const count = {
|
|
||||||
// w: { p: 0, n: 0, b: 0, r: 0, q: 0 },
|
|
||||||
// b: { p: 0, n: 0, b: 0, r: 0, q: 0 },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// for (const piece of pieces) {
|
|
||||||
// if (piece !== null && piece.type !== "k") {
|
|
||||||
// sum[piece.color] += MATERIAL_VALUE.get(piece.type) ?? 0;
|
|
||||||
// count[piece.color][piece.type] += 1;
|
|
||||||
|
|
||||||
// const oppositeColor = piece.color === "b" ? "w" : "b";
|
|
||||||
|
|
||||||
// if (imbalance[oppositeColor][piece.type] === 0) {
|
|
||||||
// imbalance[piece.color][piece.type] += 1;
|
|
||||||
// } else {
|
|
||||||
// imbalance[oppositeColor][piece.type] -= 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return { sum, imbalance, count, diff: sum.w - sum.b };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// getBoardData() {
|
|
||||||
// // return this.replay.board();
|
|
||||||
// return new Chess().board();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// getHeader() {
|
|
||||||
// return this.game.header();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pgn() {
|
|
||||||
// return this.game.pgn();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export type Material = ReturnType<Game["materialInfo"]>;
|
|
||||||
|
|
||||||
// export default Game;
|
|
||||||
19
src/main.ts
19
src/main.ts
@@ -1,20 +1,22 @@
|
|||||||
import { BoardConfig, GameConfig } from "./types";
|
import { BoardConfig, GameConfig } from "./types";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
import Board from "./board/Board";
|
import Board from "./board/Board";
|
||||||
import styles from "./board/styles-board";
|
|
||||||
import Game from "./game/Game";
|
import Game from "./game/Game";
|
||||||
|
import styles from "./board/styles-board";
|
||||||
import pgns from "./test-data/pgns";
|
import pgns from "./test-data/pgns";
|
||||||
import createAnimation from "./encoders/createAnimation";
|
import createAnimation from "./encoders/createAnimation";
|
||||||
// import { decompressPGN } from "./game/PGNHelpers";
|
// import { decompressPGN } from "./game/PGNHelpers";
|
||||||
import WebFont from "webfontloader";
|
import WebFont from "webfontloader";
|
||||||
import Player from "./player/Player";
|
import Player from "./player/Player";
|
||||||
import * as Hammer from "hammerjs";
|
import * as Hammer from "hammerjs";
|
||||||
|
import Moves from "./ui/moves/Moves";
|
||||||
|
|
||||||
const $app = document.querySelector<HTMLImageElement>("#app");
|
const $board = document.querySelector<HTMLImageElement>("#board");
|
||||||
|
const $moves = document.querySelector<HTMLImageElement>("#moves");
|
||||||
|
|
||||||
const boardConfig: BoardConfig = {
|
const boardConfig: BoardConfig = {
|
||||||
size: 1024,
|
size: 1024,
|
||||||
boardStyle: styles.chesscom,
|
boardStyle: styles.lila,
|
||||||
piecesStyle: "tatiana",
|
piecesStyle: "tatiana",
|
||||||
showBorder: true,
|
showBorder: true,
|
||||||
showExtraInfo: true,
|
showExtraInfo: true,
|
||||||
@@ -33,7 +35,7 @@ const gameConfig: GameConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createDownloadLink = async (pgn: string, boardConfig: BoardConfig) => {
|
const createDownloadLink = async (pgn: string, boardConfig: BoardConfig) => {
|
||||||
const file = await createAnimation(pgn, boardConfig, "GIF");
|
const file = await createAnimation(pgn, { ...boardConfig, size: 720 }, "GIF");
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.innerText = "DOWNLOAD";
|
link.innerText = "DOWNLOAD";
|
||||||
link.setAttribute("href", URL.createObjectURL(file));
|
link.setAttribute("href", URL.createObjectURL(file));
|
||||||
@@ -53,7 +55,7 @@ const main = async () => {
|
|||||||
// const pgn = pgns[2];
|
// const pgn = pgns[2];
|
||||||
const board = new Board(boardConfig);
|
const board = new Board(boardConfig);
|
||||||
|
|
||||||
$app?.appendChild(board.canvas);
|
$board?.appendChild(board.canvas);
|
||||||
|
|
||||||
// const interval = 1000;
|
// const interval = 1000;
|
||||||
// play(board, gameConfig, pgn, interval);
|
// play(board, gameConfig, pgn, interval);
|
||||||
@@ -61,12 +63,15 @@ const main = async () => {
|
|||||||
const player = new Player(board, gameConfig);
|
const player = new Player(board, gameConfig);
|
||||||
const game = new Game().loadPGN(pgn);
|
const game = new Game().loadPGN(pgn);
|
||||||
|
|
||||||
|
const moves = new Moves($moves as HTMLElement, player).load(game.getMoves());
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.game = game;
|
||||||
|
|
||||||
await player.load(game);
|
await player.load(game);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.player = player;
|
window.player = player;
|
||||||
// @ts-ignore
|
|
||||||
window.game = game;
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.load = async (pgn: string) => {
|
window.load = async (pgn: string) => {
|
||||||
|
|||||||
BIN
src/pattern-light.png
Normal file
BIN
src/pattern-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
src/pattern-light.png~
Normal file
BIN
src/pattern-light.png~
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -7,15 +7,14 @@ const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|||||||
class Player {
|
class Player {
|
||||||
private interval = 1000;
|
private interval = 1000;
|
||||||
private game: Game = new Game();
|
private game: Game = new Game();
|
||||||
private header: { [key: string]: string | undefined } = {};
|
private ply: number = 0;
|
||||||
private start: boolean = true;
|
|
||||||
private end: boolean = false;
|
|
||||||
public playing: boolean = false;
|
public playing: boolean = false;
|
||||||
|
|
||||||
private firstRender: Promise<void>;
|
private firstRender: Promise<void>;
|
||||||
|
|
||||||
constructor(private board: Board, private config: GameConfig) {
|
constructor(private board: Board, private config: GameConfig) {
|
||||||
this.firstRender = this.board
|
this.firstRender = this.board
|
||||||
.frame(this.game.getBoardData(), {}, null)
|
.frame(this.game.getPosition(0), this.game.header)
|
||||||
.then((_) => this.board.render());
|
.then((_) => this.board.render());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,9 +27,9 @@ class Player {
|
|||||||
await this.firstRender;
|
await this.firstRender;
|
||||||
|
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.header = game.getHeader();
|
this.ply = -1;
|
||||||
|
|
||||||
await this.board.titleFrame(this.game.getHeader());
|
await this.board.titleFrame(this.game.header);
|
||||||
this.board.render();
|
this.board.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,103 +51,56 @@ class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async prev() {
|
async prev() {
|
||||||
if (this.start) {
|
const ply = this.ply - 1;
|
||||||
|
|
||||||
|
if (ply < -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.end = false;
|
this.ply = ply;
|
||||||
|
|
||||||
const move = this.game.prev();
|
if (ply === -1) {
|
||||||
|
await this.board.titleFrame(this.game.header);
|
||||||
if (!move) {
|
|
||||||
await this.board.titleFrame(this.header);
|
|
||||||
this.board.render();
|
this.board.render();
|
||||||
this.start = true;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.board.frame(
|
await this.board.frame(this.game.getPosition(ply), this.game.header);
|
||||||
this.game.getBoardData(),
|
|
||||||
this.header,
|
|
||||||
move,
|
|
||||||
this.game.materialInfo()
|
|
||||||
);
|
|
||||||
this.board.render();
|
this.board.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
async next() {
|
async next() {
|
||||||
if (this.end) {
|
const ply = this.ply + 1;
|
||||||
|
|
||||||
|
if (ply >= this.game.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.start) {
|
this.ply = ply;
|
||||||
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();
|
await this.board.frame(this.game.getPosition(ply), this.game.header);
|
||||||
|
|
||||||
this.end = move?.end === 0;
|
|
||||||
|
|
||||||
if (!move) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.board.frame(
|
|
||||||
this.game.getBoardData(),
|
|
||||||
this.header,
|
|
||||||
move,
|
|
||||||
this.game.materialInfo()
|
|
||||||
);
|
|
||||||
this.board.render();
|
this.board.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
async first() {
|
async first() {
|
||||||
this.game.first();
|
this.ply = 0;
|
||||||
|
|
||||||
await this.board.titleFrame(this.header);
|
await this.board.frame(this.game.getPosition(this.ply), this.game.header);
|
||||||
this.board.render();
|
this.board.render();
|
||||||
|
|
||||||
this.end = false;
|
|
||||||
this.start = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async last() {
|
async last() {
|
||||||
const move = this.game.last();
|
this.ply = this.game.length - 1;
|
||||||
|
|
||||||
await this.board.frame(
|
await this.board.frame(this.game.getPosition(this.ply), this.game.header);
|
||||||
this.game.getBoardData(),
|
|
||||||
this.header,
|
|
||||||
move,
|
|
||||||
this.game.materialInfo()
|
|
||||||
);
|
|
||||||
this.board.render();
|
this.board.render();
|
||||||
|
|
||||||
this.end = true;
|
|
||||||
this.start = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async goto(ply: number) {
|
async goto(ply: number) {
|
||||||
const move = this.game.goto(ply);
|
this.ply = ply;
|
||||||
|
|
||||||
await this.board.frame(
|
await this.board.frame(this.game.getPosition(this.ply), this.game.header);
|
||||||
this.game.getBoardData(),
|
|
||||||
this.header,
|
|
||||||
move,
|
|
||||||
this.game.materialInfo()
|
|
||||||
);
|
|
||||||
this.board.render();
|
this.board.render();
|
||||||
|
|
||||||
this.start = false;
|
|
||||||
this.end = move?.end === 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #191d24;
|
|
||||||
background-image: url(pattern.png);
|
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 2.5vh;
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
background-color: #191d24;
|
||||||
|
background-image: url(pattern.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.light {
|
||||||
|
background-color: #cfcfcf;
|
||||||
|
background-image: url(pattern-light.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
.board {
|
.board {
|
||||||
@@ -30,3 +37,23 @@ body {
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 95vh;
|
max-height: 95vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moves-box {
|
||||||
|
background: rgba(255, 192, 203, 0.5);
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-box {
|
||||||
|
background: rgba(255, 166, 0, 0.5);
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-box {
|
||||||
|
background: rgba(135, 207, 235, 0.5);
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|||||||
49
src/types.ts
49
src/types.ts
@@ -1,3 +1,5 @@
|
|||||||
|
import { Move } from "chess.js";
|
||||||
|
|
||||||
export type GradientDir =
|
export type GradientDir =
|
||||||
| "horizontal"
|
| "horizontal"
|
||||||
| "vertical"
|
| "vertical"
|
||||||
@@ -112,3 +114,50 @@ export type GameConfig = {
|
|||||||
toPly: number | null;
|
toPly: number | null;
|
||||||
loop: boolean;
|
loop: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MaterialCount = {
|
||||||
|
w: {
|
||||||
|
p: number;
|
||||||
|
n: number;
|
||||||
|
b: number;
|
||||||
|
r: number;
|
||||||
|
q: number;
|
||||||
|
};
|
||||||
|
b: {
|
||||||
|
p: number;
|
||||||
|
n: number;
|
||||||
|
b: number;
|
||||||
|
r: number;
|
||||||
|
q: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Material = {
|
||||||
|
sum: {
|
||||||
|
w: number;
|
||||||
|
b: number;
|
||||||
|
};
|
||||||
|
imbalance: MaterialCount;
|
||||||
|
count: MaterialCount;
|
||||||
|
diff: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Placement = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
type: PieceType;
|
||||||
|
color: PieceColor;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
export type Position = {
|
||||||
|
move: Move | null;
|
||||||
|
ply: number;
|
||||||
|
end: number;
|
||||||
|
fen: string;
|
||||||
|
check: boolean;
|
||||||
|
mate: boolean;
|
||||||
|
turn: PieceColor;
|
||||||
|
material: Material;
|
||||||
|
placement: Placement;
|
||||||
|
last: boolean;
|
||||||
|
};
|
||||||
|
|||||||
36
src/ui/moves/Moves.ts
Normal file
36
src/ui/moves/Moves.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { html } from "common-tags";
|
||||||
|
import Player from "../../player/Player";
|
||||||
|
import chunk_ from "@arrows/array/chunk_";
|
||||||
|
|
||||||
|
class Moves {
|
||||||
|
constructor(private element: HTMLElement, private player: Player) {
|
||||||
|
this.element.addEventListener("click", (e) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
|
||||||
|
if (target?.dataset?.type === "ply") {
|
||||||
|
const ply = Number(target?.dataset?.ply);
|
||||||
|
this.player.goto(ply);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
load(moves: string[]) {
|
||||||
|
const items = chunk_(2, moves).map(
|
||||||
|
([white, black], i) =>
|
||||||
|
html`<p>
|
||||||
|
${i + 1}. <span data-type="ply" data-ply=${i * 2 + 1}>${white}</span>
|
||||||
|
<span data-type="ply" data-ply=${i * 2 + 2}>${black}</span>
|
||||||
|
</p>`
|
||||||
|
);
|
||||||
|
|
||||||
|
const content = html`<div class="moves">
|
||||||
|
${items.join("\n")}
|
||||||
|
</ul> `;
|
||||||
|
|
||||||
|
this.element.innerHTML = content;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Moves;
|
||||||
Reference in New Issue
Block a user