chore: refactor display move WIP

This commit is contained in:
chrislicodes 2023-05-23 10:47:03 +02:00
parent dd81a4492f
commit 0eee5ed657
8 changed files with 1107 additions and 250 deletions

View file

@ -10,19 +10,17 @@ With this plugin, you can either import PGNs or simply start a fresh new game. I
## Table of contents
- [Obsidian Chess Study](#obsidian-chess-study)
- [Table of contents](#table-of-contents)
- [Motivation](#motivation)
- [Installation](#installation)
- [Usage](#usage)
- [Features](#features)
- [1.0.0](#100)
- [1.0.1](#101)
- [Settings](#settings)
- [Roadmap](#roadmap)
- [Tools Used](#tools-used)
- [Alternatives](#alternatives)
- [License](#license)
- [Motivation](#motivation)
- [Installation](#installation)
- [Usage](#usage)
- [Features](#features)
- [1.0.0](#100)
- [1.0.1](#101)
- [Settings](#settings)
- [Roadmap](#roadmap)
- [Tools Used](#tools-used)
- [Alternatives](#alternatives)
- [License](#license)
## Motivation

View file

@ -7,7 +7,6 @@ import { App, Notice } from 'obsidian';
import * as React from 'react';
import { useCallback, useMemo, useState } from 'react';
import { ChessStudyPluginSettings } from 'src/components/obsidian/SettingsTab';
import { playOtherSide, toColor, toDests } from 'src/lib/chess-logic';
import { parseUserConfig } from 'src/lib/obsidian';
import {
ChessStudyDataAdapter,
@ -15,6 +14,7 @@ import {
ChessStudyMove,
VariantMove,
} from 'src/lib/storage';
import { displayMoveInHistory, findMoveIndex } from 'src/lib/ui-state';
import { useImmerReducer } from 'use-immer';
import { ChessgroundProps, ChessgroundWrapper } from './ChessgroundWrapper';
import { CommentSection } from './CommentSection';
@ -30,48 +30,19 @@ interface AppProps {
dataAdapter: ChessStudyDataAdapter;
}
interface GameState {
export interface GameState {
currentMove: ChessStudyMove | VariantMove;
isViewOnly: boolean;
study: ChessStudyFileData;
}
interface MovePosition {
variant: { parentMoveIndex: number; variantIndex: number } | null;
moveIndex: number;
}
type GameActions =
export type GameActions =
| { type: 'ADD_MOVE_TO_HISTORY'; move: Move }
| { type: 'DISPLAY_NEXT_MOVE_IN_HISTORY' }
| { type: 'DISPLAY_PREVIOUS_MOVE_IN_HISTORY' }
| { type: 'DISPLAY_SELECTED_MOVE_IN_HISTORY'; moveId: string }
| { type: 'SYNC_CURRENT_MOVE_ID'; moveId: string }
| { type: 'SYNC_SHAPES'; shapes: DrawShape[] }
| { type: 'SYNC_COMMENT'; comment: JSONContent | null }
| { type: 'ADD_MOVE_TO_HISTORY'; move: Move };
const findMoveIndex = (
moves: ChessStudyMove[],
moveId: string
): MovePosition => {
for (const [iMainLine, move] of moves.entries()) {
if (move.moveId === moveId) return { variant: null, moveIndex: iMainLine };
for (const [iVariant, variant] of move.variants.entries()) {
const moveIndex = variant.moves.findIndex(
(move) => move.moveId === moveId
);
if (moveIndex >= 0)
return {
variant: { parentMoveIndex: iMainLine, variantIndex: iVariant },
moveIndex: moveIndex,
};
}
}
return { variant: null, moveIndex: -1 };
};
| { type: 'SYNC_COMMENT'; comment: JSONContent | null };
export const ChessStudy = ({
source,
@ -85,7 +56,11 @@ export const ChessStudy = ({
source
);
const chessLogic = useMemo(() => {
// Setup Chessground API
const [chessView, setChessView] = useState<Api | null>(null);
// Setup Chess.js API
const initialChessLogic = useMemo(() => {
const chess = new Chess();
chessStudyData.moves.forEach((move) => {
@ -98,166 +73,45 @@ export const ChessStudy = ({
return chess;
}, [chessStudyData.moves]);
// Setup Chessboard and Chess.js APIs
const [chessView, setChessView] = useState<Api | null>(null);
const [chessLogic, setChessLogic] = useState(initialChessLogic);
//? Maybe remodel all of the moves as a tree
const [gameState, dispatch] = useImmerReducer<GameState, GameActions>(
(draft, action) => {
switch (action.type) {
case 'DISPLAY_NEXT_MOVE_IN_HISTORY': {
//TODO: chess.js
const currentMoveId = draft.currentMove.moveId;
const moves = draft.study.moves;
if (!chessView) return draft;
const { variant, moveIndex } = findMoveIndex(moves, currentMoveId);
if (variant) {
const variantMoves =
moves[variant.parentMoveIndex].variants[variant.variantIndex]
.moves;
if (moveIndex < variantMoves.length - 1) {
const move = variantMoves[moveIndex + 1];
const tempChessLogic = new Chess(move.after);
chessView?.set({
fen: move.after,
check: tempChessLogic.isCheck(),
});
draft.currentMove = move;
}
} else {
if (moveIndex < moves.length - 1) {
const move = moves[moveIndex + 1];
const tempChessLogic = new Chess(move.after);
chessView?.set({
fen: move.after,
check: tempChessLogic.isCheck(),
});
draft.currentMove = move;
}
}
displayMoveInHistory(draft, chessView, setChessLogic, {
offset: 1,
selectedMoveId: null,
});
return draft;
}
case 'DISPLAY_PREVIOUS_MOVE_IN_HISTORY': {
//TODO: chess.js
const currentMoveId = draft.currentMove.moveId;
const moves = draft.study.moves;
if (!chessView) return draft;
const { variant, moveIndex } = findMoveIndex(moves, currentMoveId);
if (variant) {
const variantMoves =
moves[variant.parentMoveIndex].variants[variant.variantIndex]
.moves;
if (moveIndex > 0) {
const move = variantMoves[moveIndex - 1];
const tempChessLogic = new Chess(move.after);
chessView?.set({
fen: move.after,
check: tempChessLogic.isCheck(),
});
draft.currentMove = move;
}
} else {
if (moveIndex > 0) {
const move = moves[moveIndex - 1];
const tempChessLogic = new Chess(move.after);
chessView?.set({
fen: move.after,
check: tempChessLogic.isCheck(),
});
draft.currentMove = move;
}
}
displayMoveInHistory(draft, chessView, setChessLogic, {
offset: -1,
selectedMoveId: null,
});
return draft;
}
case 'DISPLAY_SELECTED_MOVE_IN_HISTORY': {
const currentMoveId = draft.currentMove.moveId;
const moves = draft.study.moves;
if (!chessView) return draft;
const moveId = action.moveId;
const selectedMoveId = action.moveId;
const { variant, moveIndex } = findMoveIndex(moves, moveId);
if (currentMoveId === moveId) return draft;
if (variant) {
const parent = moves[variant.parentMoveIndex];
const variantMoves = parent.variants[variant.variantIndex].moves;
const move = variantMoves[moveIndex];
const chess = new Chess(move.after);
//TODO: Make viewOnly if its not the last move in the variant
chessView?.set({
fen: move.after,
check: chess.isCheck(),
movable: {
free: false,
color: toColor(chess),
dests: toDests(chess),
events: {
//Hook up the Chessground UI changes to our App State
after: (orig, dest, _metadata) => {
const handler = playOtherSide(chessView, chess);
dispatch({
type: 'ADD_MOVE_TO_HISTORY',
move: handler(orig, dest),
});
},
},
},
turnColor: toColor(chess),
});
draft.currentMove = move;
} else {
const move = moves[moveIndex];
const chess = new Chess(move.after);
chessView?.set({
fen: move.after,
check: chess.isCheck(),
movable: {
free: false,
color: toColor(chess),
dests: toDests(chess),
events: {
//Hook up the Chessground UI changes to our App State
after: (orig, dest, _metadata) => {
const handler = playOtherSide(chessView, chess);
dispatch({
type: 'ADD_MOVE_TO_HISTORY',
move: handler(orig, dest),
});
},
},
},
turnColor: toColor(chess),
});
draft.currentMove = move;
}
displayMoveInHistory(draft, chessView, setChessLogic, {
offset: 0,
selectedMoveId: selectedMoveId,
});
return draft;
}
case 'SYNC_SHAPES': {
const currentMoveId = draft.currentMove.moveId;
const currentMoveId = draft.currentMove?.moveId;
const moves = draft.study.moves;
const { variant, moveIndex } = findMoveIndex(moves, currentMoveId);
@ -279,7 +133,7 @@ export const ChessStudy = ({
return draft;
}
case 'SYNC_COMMENT': {
const currentMoveId = draft.currentMove.moveId;
const currentMoveId = draft.currentMove?.moveId;
const moves = draft.study.moves;
const { variant, moveIndex } = findMoveIndex(moves, currentMoveId);
@ -299,13 +153,12 @@ export const ChessStudy = ({
}
return draft;
return draft;
}
case 'ADD_MOVE_TO_HISTORY': {
const newMove = action.move;
const moves = draft.study.moves;
const currentMoveId = draft.currentMove.moveId;
const currentMoveId = draft.currentMove?.moveId;
const currentMoveIndex = moves.findIndex(
(move) => move.moveId === currentMoveId
@ -358,28 +211,38 @@ export const ChessStudy = ({
} else {
const currentMove = moves[moveIndex];
//check if the next move is the same move
const nextMove = moves[moveIndex + 1];
if (nextMove.san === newMove.san) {
draft.currentMove = nextMove;
return draft;
}
//check if a variant with this first move already exists
const sameVariant = currentMove.variants.findIndex(
(variant) => variant.moves[0]?.san === newMove.san
);
if (sameVariant >= 0) {
draft.currentMove = currentMove.variants[sameVariant].moves[0];
} else {
const move = {
...newMove,
moveId: moveId,
shapes: [],
comment: null,
};
currentMove.variants.push({
parentMoveId: currentMove.moveId,
variantId: nanoid(),
moves: [move],
});
draft.currentMove = move;
return draft;
}
const move = {
...newMove,
moveId: moveId,
shapes: [],
comment: null,
};
currentMove.variants.push({
parentMoveId: currentMove.moveId,
variantId: nanoid(),
moves: [move],
});
draft.currentMove = move;
}
}
@ -449,7 +312,7 @@ export const ChessStudy = ({
</div>
<div className="CommentSection border-top">
<CommentSection
currentComment={gameState.currentMove.comment}
currentComment={gameState.currentMove?.comment}
setComments={(comment: JSONContent) =>
dispatch({ type: 'SYNC_COMMENT', comment: comment })
}

View file

@ -1,11 +1,10 @@
import { Chessground as ChessgroundApi } from 'chessground';
import * as React from 'react';
import { useEffect, useRef } from 'react';
import { Chess, Move } from 'chess.js';
import { Chessground as ChessgroundApi } from 'chessground';
import { Api } from 'chessground/api';
import { Config } from 'chessground/config';
import { DrawShape } from 'chessground/draw';
import * as React from 'react';
import { useEffect, useRef } from 'react';
import { playOtherSide, toColor, toDests } from 'src/lib/chess-logic';
export interface ChessgroundProps {
@ -45,18 +44,15 @@ export const ChessgroundWrapper = React.memo(
free: false,
color: toColor(chess),
dests: toDests(chess),
events: {
//Hook up the Chessground UI changes to our App State
after: (orig, dest, _metadata) => {
const handler = playOtherSide(chessgroundApi, chess);
addMoveToHistory(handler(orig, dest));
},
},
},
highlight: {
check: true,
},
drawable: {
onChange: (shapes) => {
setShapes(shapes);
},
},
turnColor: toColor(chess),
...config,
});
@ -65,24 +61,29 @@ export const ChessgroundWrapper = React.memo(
} else if (ref.current && api) {
api.set(config);
}
}, [addMoveToHistory, api, chess, config, setApi]);
}, [addMoveToHistory, api, chess, config, setApi, setShapes]);
//Sync Chess Logic
useEffect(() => {
api?.set({
movable: {
events: {
//Hook up the Chessground UI changes to our App State
after: (orig, dest, _metadata) => {
const handler = playOtherSide(api, chess);
addMoveToHistory(handler(orig, dest));
},
},
},
});
}, [addMoveToHistory, api, chess]);
//Sync View Only
useEffect(() => {
api?.set({ viewOnly: isViewOnly });
}, [isViewOnly, api]);
//Sync Shapes To State
useEffect(() => {
api?.set({
drawable: {
onChange: (shapes) => {
setShapes(shapes);
},
},
});
}, [api, setShapes]);
// Load Shapes
useEffect(() => {
if (shapes) {

View file

@ -78,6 +78,7 @@ export const PgnViewer = React.memo(
<div className="move-item-container">
{movePairs.map((pair, i) => {
const [wMove, bMove] = pair;
const variants = wMove.variants.concat(bMove?.variants || []);
return (
<React.Fragment key={wMove.san + bMove?.san}>
<p className="move-indicator center">{i + 1}</p>
@ -95,16 +96,15 @@ export const PgnViewer = React.memo(
)}
{/* Add Variants */}
<div
style={{
gridColumn: 'span 3',
display: 'flex',
flexDirection: 'column',
}}
>
{wMove.variants
.concat(bMove?.variants || [])
.map((variant, i) => {
{!!variants.length && (
<div
style={{
gridColumn: 'span 3',
display: 'flex',
flexDirection: 'column',
}}
>
{variants.map((variant, i) => {
return (
<div key={'sd'}>
{variant.moves.map((move) => (
@ -125,7 +125,8 @@ export const PgnViewer = React.memo(
</div>
);
})}
</div>
</div>
)}
</React.Fragment>
);
})}

95
src/lib/ui-state/index.ts Normal file
View file

@ -0,0 +1,95 @@
import { Chess } from 'chess.js';
import { Api as ChessgroundApi } from 'chessground/api';
import { Draft } from 'immer';
import { GameState } from 'src/components/react/ChessStudy';
import { toColor, toDests } from '../chess-logic';
import { ChessStudyMove, VariantMove } from '../storage';
interface MovePosition {
variant: { parentMoveIndex: number; variantIndex: number } | null;
moveIndex: number;
}
export const findMoveIndex = (
moves: ChessStudyMove[],
moveId: string
): MovePosition => {
for (const [iMainLine, move] of moves.entries()) {
if (move.moveId === moveId) return { variant: null, moveIndex: iMainLine };
for (const [iVariant, variant] of move.variants.entries()) {
const moveIndex = variant.moves.findIndex(
(move) => move.moveId === moveId
);
if (moveIndex >= 0)
return {
variant: { parentMoveIndex: iMainLine, variantIndex: iVariant },
moveIndex: moveIndex,
};
}
}
return { variant: null, moveIndex: -1 };
};
export const displayMoveInHistory = (
draft: Draft<GameState>,
chessView: ChessgroundApi,
setChessLogic: React.Dispatch<React.SetStateAction<Chess>>,
options: { offset: number; selectedMoveId: string | null } = {
offset: 0,
selectedMoveId: null,
}
): Draft<GameState> => {
let moveToDisplay: ChessStudyMove | VariantMove | null = null;
const { offset, selectedMoveId } = options;
//Figure out where we are
const currentMove = draft.currentMove;
const currentMoveId = currentMove.moveId;
const moves = draft.study.moves;
//If we pass a moveId, find out where that is and offset from there, otherwise take current moveId
const baseMoveId = selectedMoveId || currentMoveId;
const { variant, moveIndex } = findMoveIndex(moves, baseMoveId);
//Are we in a variant? Are we not? Decide which move to display
if (variant) {
const variantMoves =
moves[variant.parentMoveIndex].variants[variant.variantIndex].moves;
if (typeof variantMoves[moveIndex + offset] !== 'undefined') {
moveToDisplay = variantMoves[moveIndex + offset];
}
} else {
if (typeof moves[moveIndex + offset] !== 'undefined') {
moveToDisplay = moves[moveIndex + offset];
}
}
if (!moveToDisplay) return draft;
const chess = new Chess(moveToDisplay.after);
//TODO: Handle is view only if not the last move in variant - put this into a function also for inital setuo
chessView.set({
fen: moveToDisplay.after,
check: chess.isCheck(),
movable: {
free: false,
color: toColor(chess),
dests: toDests(chess),
},
turnColor: toColor(chess),
});
draft.currentMove = moveToDisplay;
setChessLogic(chess);
return draft;
};

View file

@ -0,0 +1,233 @@
{
"header": {
"title": null
},
"moves": [
{
"color": "w",
"piece": "p",
"from": "d2",
"to": "d4",
"san": "d4",
"flags": "b",
"lan": "d2d4",
"before": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"after": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1",
"moveId": "yAxdJXytOJhKCC9oCnc5J",
"variants": [],
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "p",
"from": "d7",
"to": "d5",
"san": "d5",
"flags": "b",
"lan": "d7d5",
"before": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1",
"after": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"moveId": "mfyQ0tk9mzDtv50N6wsqi",
"variants": [
{
"parentMoveId": "mfyQ0tk9mzDtv50N6wsqi",
"variantId": "zawmwoiab39yMQU4sYsTP",
"moves": [
{
"color": "w",
"piece": "b",
"from": "c1",
"to": "f4",
"san": "Bf4",
"flags": "n",
"lan": "c1f4",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"after": "rnbqkbnr/ppp1pppp/8/3p4/3P1B2/8/PPP1PPPP/RN1QKBNR b KQkq - 1 2",
"moveId": "0FMCs4k6WYUvvOAXNOnHZ",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "n",
"from": "b8",
"to": "c6",
"san": "Nc6",
"flags": "n",
"lan": "b8c6",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P1B2/8/PPP1PPPP/RN1QKBNR b KQkq - 1 2",
"after": "r1bqkbnr/ppp1pppp/2n5/3p4/3P1B2/8/PPP1PPPP/RN1QKBNR w KQkq - 2 3",
"moveId": "cxdXqGBcpPbGwe7mxS8QJ",
"shapes": [
{
"orig": "f4",
"dest": "e4",
"brush": "green"
},
{
"orig": "e6",
"dest": "d8",
"brush": "green"
},
{
"orig": "b5",
"dest": "d3",
"brush": "green"
},
{
"orig": "f6",
"dest": "e5",
"brush": "green"
},
{
"orig": "e3",
"dest": "e1",
"brush": "green"
},
{
"orig": "e1",
"dest": "e5",
"brush": "green"
}
],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "e2",
"to": "e3",
"san": "e3",
"flags": "n",
"lan": "e2e3",
"before": "r1bqkbnr/ppp1pppp/2n5/3p4/3P1B2/8/PPP1PPPP/RN1QKBNR w KQkq - 2 3",
"after": "r1bqkbnr/ppp1pppp/2n5/3p4/3P1B2/4P3/PPP2PPP/RN1QKBNR b KQkq - 0 3",
"moveId": "aban_oJr55di5vn_wg-yK",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "n",
"from": "g8",
"to": "f6",
"san": "Nf6",
"flags": "n",
"lan": "g8f6",
"before": "r1bqkbnr/ppp1pppp/2n5/3p4/3P1B2/4P3/PPP2PPP/RN1QKBNR b KQkq - 0 3",
"after": "r1bqkb1r/ppp1pppp/2n2n2/3p4/3P1B2/4P3/PPP2PPP/RN1QKBNR w KQkq - 1 4",
"moveId": "3LqZHTTDtdu3V0pUDWY-a",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "c2",
"to": "c3",
"san": "c3",
"flags": "n",
"lan": "c2c3",
"before": "r1bqkb1r/ppp1pppp/2n2n2/3p4/3P1B2/4P3/PPP2PPP/RN1QKBNR w KQkq - 1 4",
"after": "r1bqkb1r/ppp1pppp/2n2n2/3p4/3P1B2/2P1P3/PP3PPP/RN1QKBNR b KQkq - 0 4",
"moveId": "tNHpFahwg86SzVf7IZKBe",
"shapes": [],
"comment": null
}
]
},
{
"parentMoveId": "mfyQ0tk9mzDtv50N6wsqi",
"variantId": "F0HuRmNmRe_9VSV_anJ1B",
"moves": [
{
"color": "w",
"piece": "n",
"from": "g1",
"to": "f3",
"san": "Nf3",
"flags": "n",
"lan": "g1f3",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"after": "rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R b KQkq - 1 2",
"moveId": "IeC5BY-zuob3EmMCinr85",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "p",
"from": "c7",
"to": "c5",
"san": "c5",
"flags": "b",
"lan": "c7c5",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R b KQkq - 1 2",
"after": "rnbqkbnr/pp2pppp/8/2pp4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 0 3",
"moveId": "uxnd32VynpYL7Rmmbv_DW",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "e2",
"to": "e3",
"san": "e3",
"flags": "n",
"lan": "e2e3",
"before": "rnbqkbnr/pp2pppp/8/2pp4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 0 3",
"after": "rnbqkbnr/pp2pppp/8/2pp4/3P4/4PN2/PPP2PPP/RNBQKB1R b KQkq - 0 3",
"moveId": "mUBsfyRfvdZh9rDXtZemJ",
"shapes": [
{
"orig": "c1",
"dest": "e3",
"brush": "green"
},
{
"orig": "e3",
"brush": "green"
}
],
"comment": null
}
]
}
],
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "n",
"from": "g1",
"to": "f3",
"san": "Nf3",
"flags": "n",
"lan": "g1f3",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"after": "rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R b KQkq - 1 2",
"moveId": "VanSTXnPwijGWGmQY8CSI",
"variants": [],
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "n",
"from": "b8",
"to": "c6",
"san": "Nc6",
"flags": "n",
"lan": "b8c6",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R b KQkq - 1 2",
"after": "r1bqkbnr/ppp1pppp/2n5/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 2 3",
"moveId": "7g_GlS7valHBNT5e7-kmM",
"variants": [],
"shapes": [],
"comment": null
}
]
}

View file

@ -0,0 +1,589 @@
{
"header": {
"title": null
},
"moves": [
{
"color": "w",
"piece": "p",
"from": "d2",
"to": "d4",
"san": "d4",
"flags": "b",
"lan": "d2d4",
"before": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"after": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1",
"moveId": "K7IuFrVhWYx0tDWOgoed-",
"variants": [
{
"parentMoveId": "K7IuFrVhWYx0tDWOgoed-",
"variantId": "iYWGTJLm5C4wGN1Mvo2UH",
"moves": [
{
"color": "b",
"piece": "p",
"from": "e7",
"to": "e5",
"san": "e5",
"flags": "b",
"lan": "e7e5",
"before": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1",
"after": "rnbqkbnr/pppp1ppp/8/4p3/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"moveId": "ywV8sktmb4PuaTt4--_1x",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "d4",
"to": "e5",
"san": "dxe5",
"flags": "c",
"lan": "d4e5",
"before": "rnbqkbnr/pppp1ppp/8/4p3/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"after": "rnbqkbnr/pppp1ppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 2",
"captured": "p",
"moveId": "C7UqHuHOpCrLdbQSJs_bN",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "d8",
"to": "e7",
"san": "Qe7",
"flags": "n",
"lan": "d8e7",
"before": "rnbqkbnr/pppp1ppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 2",
"after": "rnb1kbnr/ppppqppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR w KQkq - 1 3",
"moveId": "KjRLrJAOKeThrdtQzUHXn",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "n",
"from": "g1",
"to": "f3",
"san": "Nf3",
"flags": "n",
"lan": "g1f3",
"before": "rnb1kbnr/ppppqppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR w KQkq - 1 3",
"after": "rnb1kbnr/ppppqppp/8/4P3/8/5N2/PPP1PPPP/RNBQKB1R b KQkq - 2 3",
"moveId": "n39FgkmGmQh50lr75W_n3",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "e7",
"to": "b4",
"san": "Qb4+",
"flags": "n",
"lan": "e7b4",
"before": "rnb1kbnr/ppppqppp/8/4P3/8/5N2/PPP1PPPP/RNBQKB1R b KQkq - 2 3",
"after": "rnb1kbnr/pppp1ppp/8/4P3/1q6/5N2/PPP1PPPP/RNBQKB1R w KQkq - 3 4",
"moveId": "q64HbI42DIRfeenIMKW0g",
"shapes": [
{
"orig": "f8",
"dest": "b4",
"brush": "green"
},
{
"orig": "b4",
"brush": "green"
},
{
"orig": "b4",
"dest": "d2",
"brush": "green"
},
{
"orig": "d2",
"brush": "green"
}
],
"comment": null
},
{
"color": "w",
"piece": "b",
"from": "c1",
"to": "d2",
"san": "Bd2",
"flags": "n",
"lan": "c1d2",
"before": "rnb1kbnr/pppp1ppp/8/4P3/1q6/5N2/PPP1PPPP/RNBQKB1R w KQkq - 3 4",
"after": "rnb1kbnr/pppp1ppp/8/4P3/1q6/5N2/PPPBPPPP/RN1QKB1R b KQkq - 4 4",
"moveId": "vbDAsX4SycrSNpGBxJU83",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "b4",
"to": "b2",
"san": "Qxb2",
"flags": "c",
"lan": "b4b2",
"before": "rnb1kbnr/pppp1ppp/8/4P3/1q6/5N2/PPPBPPPP/RN1QKB1R b KQkq - 4 4",
"after": "rnb1kbnr/pppp1ppp/8/4P3/8/5N2/PqPBPPPP/RN1QKB1R w KQkq - 0 5",
"captured": "p",
"moveId": "ZjWKoHRclQZy-A-7iK5_y",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "b",
"from": "d2",
"to": "c3",
"san": "Bc3",
"flags": "n",
"lan": "d2c3",
"before": "rnb1kbnr/pppp1ppp/8/4P3/8/5N2/PqPBPPPP/RN1QKB1R w KQkq - 0 5",
"after": "rnb1kbnr/pppp1ppp/8/4P3/8/2B2N2/PqP1PPPP/RN1QKB1R b KQkq - 1 5",
"moveId": "r6Hps1FP7RuVqtsxk1qbh",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "b",
"from": "f8",
"to": "b4",
"san": "Bb4",
"flags": "n",
"lan": "f8b4",
"before": "rnb1kbnr/pppp1ppp/8/4P3/8/2B2N2/PqP1PPPP/RN1QKB1R b KQkq - 1 5",
"after": "rnb1k1nr/pppp1ppp/8/4P3/1b6/2B2N2/PqP1PPPP/RN1QKB1R w KQkq - 2 6",
"moveId": "QywnHUs1JvnVyqNwUFjIn",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "q",
"from": "d1",
"to": "d4",
"san": "Qd4",
"flags": "n",
"lan": "d1d4",
"before": "rnb1k1nr/pppp1ppp/8/4P3/1b6/2B2N2/PqP1PPPP/RN1QKB1R w KQkq - 2 6",
"after": "rnb1k1nr/pppp1ppp/8/4P3/1b1Q4/2B2N2/PqP1PPPP/RN2KB1R b KQkq - 3 6",
"moveId": "Qju81DkiAaQVECVdmOD7C",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "b2",
"to": "c1",
"san": "Qc1+",
"flags": "n",
"lan": "b2c1",
"before": "rnb1k1nr/pppp1ppp/8/4P3/1b1Q4/2B2N2/PqP1PPPP/RN2KB1R b KQkq - 3 6",
"after": "rnb1k1nr/pppp1ppp/8/4P3/1b1Q4/2B2N2/P1P1PPPP/RNq1KB1R w KQkq - 4 7",
"moveId": "m4ojoMAcK45xkKp_hnnln",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "q",
"from": "d4",
"to": "d1",
"san": "Qd1",
"flags": "n",
"lan": "d4d1",
"before": "rnb1k1nr/pppp1ppp/8/4P3/1b1Q4/2B2N2/P1P1PPPP/RNq1KB1R w KQkq - 4 7",
"after": "rnb1k1nr/pppp1ppp/8/4P3/1b6/2B2N2/P1P1PPPP/RNqQKB1R b KQkq - 5 7",
"moveId": "qmdtI-fTQFJNuRST6SzGB",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "b",
"from": "b4",
"to": "c3",
"san": "Bxc3+",
"flags": "c",
"lan": "b4c3",
"before": "rnb1k1nr/pppp1ppp/8/4P3/1b6/2B2N2/P1P1PPPP/RNqQKB1R b KQkq - 5 7",
"after": "rnb1k1nr/pppp1ppp/8/4P3/8/2b2N2/P1P1PPPP/RNqQKB1R w KQkq - 0 8",
"captured": "b",
"moveId": "uZbFg5rGA_sNmp2ANJdfF",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "n",
"from": "f3",
"to": "d2",
"san": "Nfd2",
"flags": "n",
"lan": "f3d2",
"before": "rnb1k1nr/pppp1ppp/8/4P3/8/2b2N2/P1P1PPPP/RNqQKB1R w KQkq - 0 8",
"after": "rnb1k1nr/pppp1ppp/8/4P3/8/2b5/P1PNPPPP/RNqQKB1R b KQkq - 1 8",
"moveId": "UoR8Es1pB4YUa8zXnEwln",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "c1",
"to": "b2",
"san": "Qb2",
"flags": "n",
"lan": "c1b2",
"before": "rnb1k1nr/pppp1ppp/8/4P3/8/2b5/P1PNPPPP/RNqQKB1R b KQkq - 1 8",
"after": "rnb1k1nr/pppp1ppp/8/4P3/8/2b5/PqPNPPPP/RN1QKB1R w KQkq - 2 9",
"moveId": "0afi6G4oToTJFL9-WRIgr",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "n",
"from": "b1",
"to": "c3",
"san": "Nxc3",
"flags": "c",
"lan": "b1c3",
"before": "rnb1k1nr/pppp1ppp/8/4P3/8/2b5/PqPNPPPP/RN1QKB1R w KQkq - 2 9",
"after": "rnb1k1nr/pppp1ppp/8/4P3/8/2N5/PqPNPPPP/R2QKB1R b KQkq - 0 9",
"captured": "b",
"moveId": "uURMx5l1nS2UuJhbf3E1z",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "b2",
"to": "c3",
"san": "Qxc3",
"flags": "c",
"lan": "b2c3",
"before": "rnb1k1nr/pppp1ppp/8/4P3/8/2N5/PqPNPPPP/R2QKB1R b KQkq - 0 9",
"after": "rnb1k1nr/pppp1ppp/8/4P3/8/2q5/P1PNPPPP/R2QKB1R w KQkq - 0 10",
"captured": "n",
"moveId": "R8_lRLlgGr5c9ISlktSsf",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "q",
"from": "d1",
"to": "b1",
"san": "Qb1",
"flags": "n",
"lan": "d1b1",
"before": "rnb1k1nr/pppp1ppp/8/4P3/8/2q5/P1PNPPPP/R2QKB1R w KQkq - 0 10",
"after": "rnb1k1nr/pppp1ppp/8/4P3/8/2q5/P1PNPPPP/RQ2KB1R b KQkq - 1 10",
"moveId": "7Kdsg-dU5BR3ql_-dSZS2",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "n",
"from": "g8",
"to": "f6",
"san": "Nf6",
"flags": "n",
"lan": "g8f6",
"before": "rnb1k1nr/pppp1ppp/8/4P3/8/2q5/P1PNPPPP/RQ2KB1R b KQkq - 1 10",
"after": "rnb1k2r/pppp1ppp/5n2/4P3/8/2q5/P1PNPPPP/RQ2KB1R w KQkq - 2 11",
"moveId": "rDhVahgiTScPvLi8TVtpO",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "e5",
"to": "f6",
"san": "exf6",
"flags": "c",
"lan": "e5f6",
"before": "rnb1k2r/pppp1ppp/5n2/4P3/8/2q5/P1PNPPPP/RQ2KB1R w KQkq - 2 11",
"after": "rnb1k2r/pppp1ppp/5P2/8/8/2q5/P1PNPPPP/RQ2KB1R b KQkq - 0 11",
"captured": "n",
"moveId": "1RPQpfvXkUFDCsZVCDb2J",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "k",
"from": "e8",
"to": "g8",
"san": "O-O",
"flags": "k",
"lan": "e8g8",
"before": "rnb1k2r/pppp1ppp/5P2/8/8/2q5/P1PNPPPP/RQ2KB1R b KQkq - 0 11",
"after": "rnb2rk1/pppp1ppp/5P2/8/8/2q5/P1PNPPPP/RQ2KB1R w KQ - 1 12",
"moveId": "1IAGBQM6P_G5Gm1xGso5F",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "f6",
"to": "g7",
"san": "fxg7",
"flags": "c",
"lan": "f6g7",
"before": "rnb2rk1/pppp1ppp/5P2/8/8/2q5/P1PNPPPP/RQ2KB1R w KQ - 1 12",
"after": "rnb2rk1/pppp1pPp/8/8/8/2q5/P1PNPPPP/RQ2KB1R b KQ - 0 12",
"captured": "p",
"moveId": "gKLX3u-UUeiJCTxNP6h-0",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "c3",
"to": "g7",
"san": "Qxg7",
"flags": "c",
"lan": "c3g7",
"before": "rnb2rk1/pppp1pPp/8/8/8/2q5/P1PNPPPP/RQ2KB1R b KQ - 0 12",
"after": "rnb2rk1/pppp1pqp/8/8/8/8/P1PNPPPP/RQ2KB1R w KQ - 0 13",
"captured": "p",
"moveId": "k0nLjnkIAIvIiV2r8aN0i",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "e2",
"to": "e4",
"san": "e4",
"flags": "b",
"lan": "e2e4",
"before": "rnb2rk1/pppp1pqp/8/8/8/8/P1PNPPPP/RQ2KB1R w KQ - 0 13",
"after": "rnb2rk1/pppp1pqp/8/8/4P3/8/P1PN1PPP/RQ2KB1R b KQ - 0 13",
"moveId": "u2lNZ5yg8T0P9pYwo9_F7",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "p",
"from": "d7",
"to": "d5",
"san": "d5",
"flags": "b",
"lan": "d7d5",
"before": "rnb2rk1/pppp1pqp/8/8/4P3/8/P1PN1PPP/RQ2KB1R b KQ - 0 13",
"after": "rnb2rk1/ppp2pqp/8/3p4/4P3/8/P1PN1PPP/RQ2KB1R w KQ - 0 14",
"moveId": "22VZvkuhwF5ZEPMHm1VZ4",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "p",
"from": "e4",
"to": "d5",
"san": "exd5",
"flags": "c",
"lan": "e4d5",
"before": "rnb2rk1/ppp2pqp/8/3p4/4P3/8/P1PN1PPP/RQ2KB1R w KQ - 0 14",
"after": "rnb2rk1/ppp2pqp/8/3P4/8/8/P1PN1PPP/RQ2KB1R b KQ - 0 14",
"captured": "p",
"moveId": "LeIBTE8JPhv3hBiMuMX3b",
"shapes": [
{
"orig": "c3",
"dest": "c8",
"brush": "green"
},
{
"orig": "d7",
"brush": "green"
},
{
"orig": "f5",
"dest": "e4",
"brush": "green"
},
{
"orig": "e4",
"dest": "d4",
"brush": "green"
},
{
"orig": "e4",
"brush": "green"
}
],
"comment": null
},
{
"color": "b",
"piece": "r",
"from": "f8",
"to": "e8",
"san": "Re8+",
"flags": "n",
"lan": "f8e8",
"before": "rnb2rk1/ppp2pqp/8/3P4/8/8/P1PN1PPP/RQ2KB1R b KQ - 0 14",
"after": "rnb1r1k1/ppp2pqp/8/3P4/8/8/P1PN1PPP/RQ2KB1R w KQ - 1 15",
"moveId": "NB92t5ZR0vlNnN9rVIyCV",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "n",
"from": "d2",
"to": "e4",
"san": "Ne4",
"flags": "n",
"lan": "d2e4",
"before": "rnb1r1k1/ppp2pqp/8/3P4/8/8/P1PN1PPP/RQ2KB1R w KQ - 1 15",
"after": "rnb1r1k1/ppp2pqp/8/3P4/4N3/8/P1P2PPP/RQ2KB1R b KQ - 2 15",
"moveId": "LxWOfH-f8a6wjZKPxoQCY",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "r",
"from": "e8",
"to": "e4",
"san": "Rxe4+",
"flags": "c",
"lan": "e8e4",
"before": "rnb1r1k1/ppp2pqp/8/3P4/4N3/8/P1P2PPP/RQ2KB1R b KQ - 2 15",
"after": "rnb3k1/ppp2pqp/8/3P4/4r3/8/P1P2PPP/RQ2KB1R w KQ - 0 16",
"captured": "n",
"moveId": "JaI-p1_NITz88XDPgeRdb",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "k",
"from": "e1",
"to": "d2",
"san": "Kd2",
"flags": "n",
"lan": "e1d2",
"before": "rnb3k1/ppp2pqp/8/3P4/4r3/8/P1P2PPP/RQ2KB1R w KQ - 0 16",
"after": "rnb3k1/ppp2pqp/8/3P4/4r3/8/P1PK1PPP/RQ3B1R b - - 1 16",
"moveId": "IRe9hR4tsqSSiUjRJRtdX",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "q",
"from": "g7",
"to": "d4",
"san": "Qd4+",
"flags": "n",
"lan": "g7d4",
"before": "rnb3k1/ppp2pqp/8/3P4/4r3/8/P1PK1PPP/RQ3B1R b - - 1 16",
"after": "rnb3k1/ppp2p1p/8/3P4/3qr3/8/P1PK1PPP/RQ3B1R w - - 2 17",
"moveId": "AI_nM6F28Pd6_fP5E4kg8",
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "k",
"from": "d2",
"to": "c1",
"san": "Kc1",
"flags": "n",
"lan": "d2c1",
"before": "rnb3k1/ppp2p1p/8/3P4/3qr3/8/P1PK1PPP/RQ3B1R w - - 2 17",
"after": "rnb3k1/ppp2p1p/8/3P4/3qr3/8/P1P2PPP/RQK2B1R b - - 3 17",
"moveId": "i8fhacks6siwZ59vneIBt",
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "r",
"from": "e4",
"to": "e1",
"san": "Re1#",
"flags": "n",
"lan": "e4e1",
"before": "rnb3k1/ppp2p1p/8/3P4/3qr3/8/P1P2PPP/RQK2B1R b - - 3 17",
"after": "rnb3k1/ppp2p1p/8/3P4/3q4/8/P1P2PPP/RQK1rB1R w - - 4 18",
"moveId": "9RCCfYS56nKp5LGlT0pm-",
"shapes": [],
"comment": null
}
]
}
],
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "p",
"from": "d7",
"to": "d5",
"san": "d5",
"flags": "b",
"lan": "d7d5",
"before": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1",
"after": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"moveId": "caq5T9snozEXHQJo96w6l",
"variants": [],
"shapes": [],
"comment": null
},
{
"color": "w",
"piece": "n",
"from": "g1",
"to": "f3",
"san": "Nf3",
"flags": "n",
"lan": "g1f3",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",
"after": "rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R b KQkq - 1 2",
"moveId": "79E1Noq3U-uYA4t9ZDEj_",
"variants": [],
"shapes": [],
"comment": null
},
{
"color": "b",
"piece": "n",
"from": "b8",
"to": "c6",
"san": "Nc6",
"flags": "n",
"lan": "b8c6",
"before": "rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R b KQkq - 1 2",
"after": "r1bqkbnr/ppp1pppp/2n5/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 2 3",
"moveId": "EKWVgjt5Z2Cz8bqD5Ie0k",
"variants": [],
"shapes": [],
"comment": null
}
]
}

View file

@ -15,7 +15,13 @@
"after": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1",
"moveId": "EAyuEJsIKY7Nvp9v20DdD",
"variants": [],
"shapes": [],
"shapes": [
{
"orig": "d2",
"dest": "d4",
"brush": "green"
}
],
"comment": null
},
{
@ -185,7 +191,32 @@
]
}
],
"shapes": [],
"shapes": [
{
"orig": "f4",
"dest": "c1",
"brush": "green"
},
{
"orig": "e6",
"brush": "green"
},
{
"orig": "d6",
"dest": "c5",
"brush": "green"
},
{
"orig": "d3",
"dest": "b3",
"brush": "green"
},
{
"orig": "b5",
"dest": "d6",
"brush": "green"
}
],
"comment": null
},
{
@ -253,23 +284,69 @@
"variants": [],
"shapes": [
{
"orig": "f2",
"dest": "d4",
"orig": "b7",
"dest": "b4",
"brush": "green"
},
{
"orig": "c3",
"orig": "e7",
"dest": "d5",
"brush": "green"
},
{
"orig": "d6",
"dest": "c5",
"orig": "f7",
"dest": "e5",
"brush": "green"
}
],
"comment": null
},
{
"color": "w",
"piece": "n",
"from": "f3",
"to": "e5",
"san": "Ne5",
"flags": "n",
"lan": "f3e5",
"before": "r1bqkbnr/ppp1pppp/2n5/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 2 3",
"after": "r1bqkbnr/ppp1pppp/2n5/3pN3/3P4/8/PPP1PPPP/RNBQKB1R b KQkq - 3 3",
"moveId": "LqVKpTOqwUOa7ue7pbJ50",
"variants": [],
"shapes": [
{
"orig": "d3",
"dest": "e5",
"brush": "green"
},
{
"orig": "e5",
"dest": "f5",
"orig": "f7",
"dest": "d7",
"brush": "green"
},
{
"orig": "b6",
"dest": "b5",
"brush": "green"
},
{
"orig": "d4",
"dest": "c2",
"brush": "green"
},
{
"orig": "b2",
"dest": "d4",
"brush": "green"
},
{
"orig": "g6",
"dest": "f6",
"brush": "green"
},
{
"orig": "e4",
"dest": "e3",
"brush": "green"
}
],