react-chess-replay-trainer 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -28,6 +28,10 @@ export interface ReplayTrainerState {
|
|
|
28
28
|
/** Revealed/expected move at the current ply (set after a miss or reveal). */
|
|
29
29
|
expectedSan: string | null;
|
|
30
30
|
expectedUci: string | null;
|
|
31
|
+
/** Destination square of the last correct guess (green check overlay). */
|
|
32
|
+
correctMoveSquare: string | null;
|
|
33
|
+
/** FEN shown on the board (includes a pending correct-move ply). */
|
|
34
|
+
displayFen: string;
|
|
31
35
|
canPrev: boolean;
|
|
32
36
|
canNext: boolean;
|
|
33
37
|
goFirst: () => void;
|
package/dist/index.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
3
|
-
import { createExpectedMoveDropHandler, ThemeProvider, ChessboardDnDProvider, HighlightChessboard, PlyNavigation, AnalysisErrorBoundary, AnalysisBoard } from 'react-chess-core';
|
|
3
|
+
import { useCorrectMoveFeedback, createExpectedMoveDropHandler, fenAfterUci, ThemeProvider, ChessboardDnDProvider, HighlightChessboard, PlyNavigation, AnalysisErrorBoundary, AnalysisBoard } from 'react-chess-core';
|
|
4
4
|
export { AnalysisBoard, AnalysisBoardCore, AnalysisErrorBoundary, DEFAULT_ANALYSIS_LAYOUT, DefaultPlyNavigation, PlyNavigation, MISS_MOVE_ANIMATION_MS as REPLAY_MISS_MOVE_ANIMATION_MS, MISS_REFUTATION_MAX_WAIT_MS as REPLAY_MISS_REFUTATION_MAX_WAIT_MS, MISS_REFUTATION_PAUSE_MS as REPLAY_MISS_REFUTATION_PAUSE_MS, MISS_WRONG_PAUSE_MS as REPLAY_MISS_WRONG_PAUSE_MS, REFUTATION_EVAL_GAP_CP as REPLAY_REFUTATION_EVAL_GAP_CP, REFUTATION_EVAL_GAP_PAWNS as REPLAY_REFUTATION_EVAL_GAP_PAWNS, defaultRenderPlyNavigation, fenAfterUci, getMissDisplay as getReplayMissDisplay, refutationEvalGapCp, refutationFromEvaluation, refutationEngineOptions as replayRefutationEngineOptions, uciFromDrop, useMissBoard as useReplayMissBoard, useMissSequence as useReplayMissSequence, useMissRefutation as useReplayRefutation } from 'react-chess-core';
|
|
5
5
|
import { Chess } from 'chess.js';
|
|
6
6
|
|
|
@@ -78,6 +78,8 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
78
78
|
const [expectedSan, setExpectedSan] = useState(null);
|
|
79
79
|
const [autoplayActive, setAutoplayActive] = useState(false);
|
|
80
80
|
const [expectedUci, setExpectedUci] = useState(null);
|
|
81
|
+
const [feedbackFen, setFeedbackFen] = useState(null);
|
|
82
|
+
const { correctMoveSquare, showCorrectMove, clearCorrectMoveFeedback, isShowingCorrectMove, } = useCorrectMoveFeedback();
|
|
81
83
|
const fetchGameRef = useRef(fetchGame);
|
|
82
84
|
fetchGameRef.current = fetchGame;
|
|
83
85
|
const onMissRef = useRef(onMiss);
|
|
@@ -88,6 +90,8 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
88
90
|
const completedFiredRef = useRef(false);
|
|
89
91
|
const modeRef = useRef('browse');
|
|
90
92
|
const trainColorRef = useRef('both');
|
|
93
|
+
const showingCorrectMoveRef = useRef(false);
|
|
94
|
+
showingCorrectMoveRef.current = isShowingCorrectMove;
|
|
91
95
|
useEffect(() => {
|
|
92
96
|
let cancelled = false;
|
|
93
97
|
setLoading(true);
|
|
@@ -131,6 +135,7 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
131
135
|
const movesUci = useMemo(() => { var _a; return (_a = game === null || game === void 0 ? void 0 : game.movesUci) !== null && _a !== void 0 ? _a : []; }, [game]);
|
|
132
136
|
const totalPly = movesUci.length;
|
|
133
137
|
const fen = useMemo(() => fenAtPly(movesUci, plyIndex), [movesUci, plyIndex]);
|
|
138
|
+
const displayFen = feedbackFen !== null && feedbackFen !== void 0 ? feedbackFen : fen;
|
|
134
139
|
const complete = plyIndex >= totalPly && totalPly > 0;
|
|
135
140
|
const sideToMove$1 = sideToMove(fen);
|
|
136
141
|
const isUserTurn = isTrainSideToMove(trainColor, sideToMove$1);
|
|
@@ -140,7 +145,9 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
140
145
|
setFeedback(null);
|
|
141
146
|
setExpectedSan(null);
|
|
142
147
|
setExpectedUci(null);
|
|
143
|
-
|
|
148
|
+
setFeedbackFen(null);
|
|
149
|
+
clearCorrectMoveFeedback();
|
|
150
|
+
}, [clearCorrectMoveFeedback]);
|
|
144
151
|
const stopAutoplay = useCallback(() => {
|
|
145
152
|
setAutoplayActive(false);
|
|
146
153
|
}, []);
|
|
@@ -220,12 +227,21 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
220
227
|
expectedUci: movesUci[plyIndex],
|
|
221
228
|
enabled: modeRef.current === 'train' &&
|
|
222
229
|
!complete &&
|
|
230
|
+
!showingCorrectMoveRef.current &&
|
|
223
231
|
isTrainSideToMove(trainColorRef.current, sideToMove$1),
|
|
224
|
-
onCorrect: () => {
|
|
232
|
+
onCorrect: (uci) => {
|
|
225
233
|
setFeedback('correct');
|
|
226
234
|
setExpectedSan(null);
|
|
227
235
|
setExpectedUci(null);
|
|
228
|
-
|
|
236
|
+
const nextFen = fenAfterUci(fen, uci);
|
|
237
|
+
if (nextFen) {
|
|
238
|
+
setFeedbackFen(nextFen);
|
|
239
|
+
}
|
|
240
|
+
showCorrectMove(uci.slice(2, 4), () => {
|
|
241
|
+
setFeedbackFen(null);
|
|
242
|
+
setFeedback(null);
|
|
243
|
+
setPlyIndex((p) => p + 1);
|
|
244
|
+
});
|
|
229
245
|
},
|
|
230
246
|
onIncorrect: () => {
|
|
231
247
|
var _a, _b;
|
|
@@ -238,7 +254,7 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
238
254
|
setExpectedUci(expectedUci);
|
|
239
255
|
recordMiss(plyIndex);
|
|
240
256
|
},
|
|
241
|
-
})(source, target, piece), [complete, movesUci, plyIndex,
|
|
257
|
+
})(source, target, piece), [complete, fen, movesUci, plyIndex, game, recordMiss, sideToMove$1, showCorrectMove]);
|
|
242
258
|
useEffect(() => {
|
|
243
259
|
var _a;
|
|
244
260
|
if (mode === 'train' && complete && !completedFiredRef.current) {
|
|
@@ -267,12 +283,15 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
267
283
|
if (mode !== 'train' || complete || trainColor === 'both' || isUserTurn) {
|
|
268
284
|
return;
|
|
269
285
|
}
|
|
286
|
+
if (isShowingCorrectMove) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
270
289
|
const id = setTimeout(() => {
|
|
271
290
|
setPlyIndex((p) => (p < totalPly ? p + 1 : p));
|
|
272
291
|
clearTransient();
|
|
273
292
|
}, OPPONENT_MOVE_DELAY_MS);
|
|
274
293
|
return () => clearTimeout(id);
|
|
275
|
-
}, [mode, complete, trainColor, isUserTurn, plyIndex, totalPly, clearTransient]);
|
|
294
|
+
}, [mode, complete, trainColor, isUserTurn, plyIndex, totalPly, clearTransient, isShowingCorrectMove]);
|
|
276
295
|
return {
|
|
277
296
|
game,
|
|
278
297
|
loading,
|
|
@@ -288,6 +307,8 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
288
307
|
feedback,
|
|
289
308
|
expectedSan,
|
|
290
309
|
expectedUci,
|
|
310
|
+
correctMoveSquare,
|
|
311
|
+
displayFen,
|
|
291
312
|
canPrev: plyIndex > 0,
|
|
292
313
|
canNext: plyIndex < totalPly,
|
|
293
314
|
goFirst,
|
|
@@ -437,7 +458,7 @@ const ReplayTrainer = ({ gameId, fetchGame, startFen, onMiss, onComplete, onExit
|
|
|
437
458
|
]
|
|
438
459
|
: [];
|
|
439
460
|
const draggable = training && !state.complete;
|
|
440
|
-
return (jsxs(ThemeProvider, { theme: theme, boardTheme: boardTheme, children: [jsxs("div", { style: mainContainerStyle(boardWidth, colors), children: [jsxs("div", { style: headerStyle, children: [jsxs("span", { style: playerNameStyle, children: [((_b = game.white) !== null && _b !== void 0 ? _b : 'White'), " vs ", ((_c = game.black) !== null && _c !== void 0 ? _c : 'Black')] }), game.result && jsx("span", { style: subtleTextStyle(colors), children: game.result })] }), jsx(ChessboardDnDProvider, { children: jsx(HighlightChessboard, { boardWidth: boardWidth, checkSquare: "", hintSquare: null, incorrectMoveSquare: null, position: state.
|
|
461
|
+
return (jsxs(ThemeProvider, { theme: theme, boardTheme: boardTheme, children: [jsxs("div", { style: mainContainerStyle(boardWidth, colors), children: [jsxs("div", { style: headerStyle, children: [jsxs("span", { style: playerNameStyle, children: [((_b = game.white) !== null && _b !== void 0 ? _b : 'White'), " vs ", ((_c = game.black) !== null && _c !== void 0 ? _c : 'Black')] }), game.result && jsx("span", { style: subtleTextStyle(colors), children: game.result })] }), jsx(ChessboardDnDProvider, { children: jsx(HighlightChessboard, { boardWidth: boardWidth, checkSquare: "", hintSquare: null, incorrectMoveSquare: null, correctMoveSquare: state.correctMoveSquare, position: state.displayFen, boardOrientation: boardOrientation, arePiecesDraggable: draggable && !state.correctMoveSquare, isDraggablePiece: ({ piece }) => {
|
|
441
462
|
if (state.trainColor === 'white')
|
|
442
463
|
return piece[0] === 'w';
|
|
443
464
|
if (state.trainColor === 'black')
|
package/dist/index.js
CHANGED
|
@@ -79,6 +79,8 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
79
79
|
const [expectedSan, setExpectedSan] = react.useState(null);
|
|
80
80
|
const [autoplayActive, setAutoplayActive] = react.useState(false);
|
|
81
81
|
const [expectedUci, setExpectedUci] = react.useState(null);
|
|
82
|
+
const [feedbackFen, setFeedbackFen] = react.useState(null);
|
|
83
|
+
const { correctMoveSquare, showCorrectMove, clearCorrectMoveFeedback, isShowingCorrectMove, } = reactChessCore.useCorrectMoveFeedback();
|
|
82
84
|
const fetchGameRef = react.useRef(fetchGame);
|
|
83
85
|
fetchGameRef.current = fetchGame;
|
|
84
86
|
const onMissRef = react.useRef(onMiss);
|
|
@@ -89,6 +91,8 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
89
91
|
const completedFiredRef = react.useRef(false);
|
|
90
92
|
const modeRef = react.useRef('browse');
|
|
91
93
|
const trainColorRef = react.useRef('both');
|
|
94
|
+
const showingCorrectMoveRef = react.useRef(false);
|
|
95
|
+
showingCorrectMoveRef.current = isShowingCorrectMove;
|
|
92
96
|
react.useEffect(() => {
|
|
93
97
|
let cancelled = false;
|
|
94
98
|
setLoading(true);
|
|
@@ -132,6 +136,7 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
132
136
|
const movesUci = react.useMemo(() => { var _a; return (_a = game === null || game === void 0 ? void 0 : game.movesUci) !== null && _a !== void 0 ? _a : []; }, [game]);
|
|
133
137
|
const totalPly = movesUci.length;
|
|
134
138
|
const fen = react.useMemo(() => fenAtPly(movesUci, plyIndex), [movesUci, plyIndex]);
|
|
139
|
+
const displayFen = feedbackFen !== null && feedbackFen !== void 0 ? feedbackFen : fen;
|
|
135
140
|
const complete = plyIndex >= totalPly && totalPly > 0;
|
|
136
141
|
const sideToMove$1 = sideToMove(fen);
|
|
137
142
|
const isUserTurn = isTrainSideToMove(trainColor, sideToMove$1);
|
|
@@ -141,7 +146,9 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
141
146
|
setFeedback(null);
|
|
142
147
|
setExpectedSan(null);
|
|
143
148
|
setExpectedUci(null);
|
|
144
|
-
|
|
149
|
+
setFeedbackFen(null);
|
|
150
|
+
clearCorrectMoveFeedback();
|
|
151
|
+
}, [clearCorrectMoveFeedback]);
|
|
145
152
|
const stopAutoplay = react.useCallback(() => {
|
|
146
153
|
setAutoplayActive(false);
|
|
147
154
|
}, []);
|
|
@@ -221,12 +228,21 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
221
228
|
expectedUci: movesUci[plyIndex],
|
|
222
229
|
enabled: modeRef.current === 'train' &&
|
|
223
230
|
!complete &&
|
|
231
|
+
!showingCorrectMoveRef.current &&
|
|
224
232
|
isTrainSideToMove(trainColorRef.current, sideToMove$1),
|
|
225
|
-
onCorrect: () => {
|
|
233
|
+
onCorrect: (uci) => {
|
|
226
234
|
setFeedback('correct');
|
|
227
235
|
setExpectedSan(null);
|
|
228
236
|
setExpectedUci(null);
|
|
229
|
-
|
|
237
|
+
const nextFen = reactChessCore.fenAfterUci(fen, uci);
|
|
238
|
+
if (nextFen) {
|
|
239
|
+
setFeedbackFen(nextFen);
|
|
240
|
+
}
|
|
241
|
+
showCorrectMove(uci.slice(2, 4), () => {
|
|
242
|
+
setFeedbackFen(null);
|
|
243
|
+
setFeedback(null);
|
|
244
|
+
setPlyIndex((p) => p + 1);
|
|
245
|
+
});
|
|
230
246
|
},
|
|
231
247
|
onIncorrect: () => {
|
|
232
248
|
var _a, _b;
|
|
@@ -239,7 +255,7 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
239
255
|
setExpectedUci(expectedUci);
|
|
240
256
|
recordMiss(plyIndex);
|
|
241
257
|
},
|
|
242
|
-
})(source, target, piece), [complete, movesUci, plyIndex,
|
|
258
|
+
})(source, target, piece), [complete, fen, movesUci, plyIndex, game, recordMiss, sideToMove$1, showCorrectMove]);
|
|
243
259
|
react.useEffect(() => {
|
|
244
260
|
var _a;
|
|
245
261
|
if (mode === 'train' && complete && !completedFiredRef.current) {
|
|
@@ -268,12 +284,15 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
268
284
|
if (mode !== 'train' || complete || trainColor === 'both' || isUserTurn) {
|
|
269
285
|
return;
|
|
270
286
|
}
|
|
287
|
+
if (isShowingCorrectMove) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
271
290
|
const id = setTimeout(() => {
|
|
272
291
|
setPlyIndex((p) => (p < totalPly ? p + 1 : p));
|
|
273
292
|
clearTransient();
|
|
274
293
|
}, OPPONENT_MOVE_DELAY_MS);
|
|
275
294
|
return () => clearTimeout(id);
|
|
276
|
-
}, [mode, complete, trainColor, isUserTurn, plyIndex, totalPly, clearTransient]);
|
|
295
|
+
}, [mode, complete, trainColor, isUserTurn, plyIndex, totalPly, clearTransient, isShowingCorrectMove]);
|
|
277
296
|
return {
|
|
278
297
|
game,
|
|
279
298
|
loading,
|
|
@@ -289,6 +308,8 @@ function useReplayTrainer({ gameId, startFen, fetchGame, onMiss, onComplete, })
|
|
|
289
308
|
feedback,
|
|
290
309
|
expectedSan,
|
|
291
310
|
expectedUci,
|
|
311
|
+
correctMoveSquare,
|
|
312
|
+
displayFen,
|
|
292
313
|
canPrev: plyIndex > 0,
|
|
293
314
|
canNext: plyIndex < totalPly,
|
|
294
315
|
goFirst,
|
|
@@ -438,7 +459,7 @@ const ReplayTrainer = ({ gameId, fetchGame, startFen, onMiss, onComplete, onExit
|
|
|
438
459
|
]
|
|
439
460
|
: [];
|
|
440
461
|
const draggable = training && !state.complete;
|
|
441
|
-
return (jsxRuntime.jsxs(reactChessCore.ThemeProvider, { theme: theme, boardTheme: boardTheme, children: [jsxRuntime.jsxs("div", { style: mainContainerStyle(boardWidth, colors), children: [jsxRuntime.jsxs("div", { style: headerStyle, children: [jsxRuntime.jsxs("span", { style: playerNameStyle, children: [((_b = game.white) !== null && _b !== void 0 ? _b : 'White'), " vs ", ((_c = game.black) !== null && _c !== void 0 ? _c : 'Black')] }), game.result && jsxRuntime.jsx("span", { style: subtleTextStyle(colors), children: game.result })] }), jsxRuntime.jsx(reactChessCore.ChessboardDnDProvider, { children: jsxRuntime.jsx(reactChessCore.HighlightChessboard, { boardWidth: boardWidth, checkSquare: "", hintSquare: null, incorrectMoveSquare: null, position: state.
|
|
462
|
+
return (jsxRuntime.jsxs(reactChessCore.ThemeProvider, { theme: theme, boardTheme: boardTheme, children: [jsxRuntime.jsxs("div", { style: mainContainerStyle(boardWidth, colors), children: [jsxRuntime.jsxs("div", { style: headerStyle, children: [jsxRuntime.jsxs("span", { style: playerNameStyle, children: [((_b = game.white) !== null && _b !== void 0 ? _b : 'White'), " vs ", ((_c = game.black) !== null && _c !== void 0 ? _c : 'Black')] }), game.result && jsxRuntime.jsx("span", { style: subtleTextStyle(colors), children: game.result })] }), jsxRuntime.jsx(reactChessCore.ChessboardDnDProvider, { children: jsxRuntime.jsx(reactChessCore.HighlightChessboard, { boardWidth: boardWidth, checkSquare: "", hintSquare: null, incorrectMoveSquare: null, correctMoveSquare: state.correctMoveSquare, position: state.displayFen, boardOrientation: boardOrientation, arePiecesDraggable: draggable && !state.correctMoveSquare, isDraggablePiece: ({ piece }) => {
|
|
442
463
|
if (state.trainColor === 'white')
|
|
443
464
|
return piece[0] === 'w';
|
|
444
465
|
if (state.trainColor === 'black')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-chess-replay-trainer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "React component for browsing a chess game and drilling its moves, reporting misses (uses react-chess-core for board and analysis)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Robert Blackwell",
|