From 050acb5707ff7ae1ec9fd451ad261c0dab9f6c63 Mon Sep 17 00:00:00 2001 From: Francisco Salgueiro Date: Fri, 11 Oct 2024 16:54:10 +0100 Subject: [PATCH] fix castling rights (#346) --- src/components/boards/Board.tsx | 23 +++--- src/components/panels/info/FenInput.tsx | 93 +++++++++++++++++++------ 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/src/components/boards/Board.tsx b/src/components/boards/Board.tsx index d74e66a4..eac438a8 100644 --- a/src/components/boards/Board.tsx +++ b/src/components/boards/Board.tsx @@ -73,7 +73,7 @@ import { chessgroundDests, chessgroundMove } from "chessops/compat"; import { makeSan } from "chessops/san"; import domtoimage from "dom-to-image"; import { useAtom, useAtomValue } from "jotai"; -import { memo, useContext, useMemo, useState } from "react"; +import { memo, useCallback, useContext, useMemo, useState } from "react"; import { Helmet } from "react-helmet"; import { useHotkeys } from "react-hotkeys-hook"; import { useTranslation } from "react-i18next"; @@ -558,16 +558,19 @@ function Board({ const [enableBoardScroll] = useAtom(enableBoardScrollAtom); const [snapArrows] = useAtom(snapArrowsAtom); - const setBoardFen = (fen: string) => { - if (!fen || !editingMode) { - return; - } - const newFen = `${fen} ${currentNode.fen.split(" ").slice(1).join(" ")}`; + const setBoardFen = useCallback( + (fen: string) => { + if (!fen || !editingMode) { + return; + } + const newFen = `${fen} ${currentNode.fen.split(" ").slice(1).join(" ")}`; - if (newFen !== currentNode.fen) { - setFen(newFen); - } - }; + if (newFen !== currentNode.fen) { + setFen(newFen); + } + }, + [editingMode, currentNode, setFen], + ); useHotkeys(keyMap.TOGGLE_EVAL_BAR.keys, () => setEvalOpen((e) => !e)); diff --git a/src/components/panels/info/FenInput.tsx b/src/components/panels/info/FenInput.tsx index e7008cfc..1fb61114 100644 --- a/src/components/panels/info/FenInput.tsx +++ b/src/components/panels/info/FenInput.tsx @@ -2,23 +2,17 @@ import { TreeStateContext } from "@/components/common/TreeStateContext"; import { getCastlingSquare, swapMove } from "@/utils/chessops"; import { Button, Checkbox, Group, Select, Stack, Text } from "@mantine/core"; import { EMPTY_FEN, INITIAL_FEN, makeFen, parseFen } from "chessops/fen"; -import { memo, useContext } from "react"; +import { memo, useCallback, useContext, useEffect, useMemo } from "react"; import { useStore } from "zustand"; import FenSearch from "./FenSearch"; +import { SquareSet, type Setup } from "chessops"; type Castlingrights = { k: boolean; q: boolean; }; -function FenInput({ currentFen }: { currentFen: string }) { - const store = useContext(TreeStateContext)!; - const setFen = useStore(store, (s) => s.setFen); - - const [setup, error] = parseFen(currentFen).unwrap( - (v) => [v, null], - (e) => [null, e], - ); +function getCastlingRights(setup: Setup) { let whiteCastling: Castlingrights = { k: false, q: false }; let blackCastling: Castlingrights = { k: false, q: false }; @@ -49,19 +43,78 @@ function FenInput({ currentFen }: { currentFen: string }) { }; } - function setCastlingRights( - color: "w" | "b", - side: "q" | "k", - value: boolean, - ) { - if (setup) { - const castlingSquare = getCastlingSquare(setup, color, side); - if (castlingSquare !== undefined) { - setup.castlingRights = setup.castlingRights.set(castlingSquare, value); - setFen(makeFen(setup)); + return { + whiteCastling, + blackCastling, + }; +} + +function FenInput({ currentFen }: { currentFen: string }) { + const store = useContext(TreeStateContext)!; + const setFen = useStore(store, (s) => s.setFen); + + const [setup, error] = useMemo( + () => + parseFen(currentFen).unwrap( + (v) => [v, null], + (e) => [null, e], + ), + [currentFen], + ); + + if (!setup) { + return {error.message}; + } + + const { whiteCastling, blackCastling } = useMemo( + () => getCastlingRights(setup), + [setup], + ); + + const setCastlingRights = useCallback( + (color: "w" | "b", side: "q" | "k", value: boolean) => { + if (setup) { + const castlingSquare = getCastlingSquare(setup, color, side); + if (castlingSquare !== undefined) { + setup.castlingRights = setup.castlingRights.set( + castlingSquare, + value, + ); + setFen(makeFen(setup)); + } } + }, + [setup, setFen], + ); + + useEffect(() => { + let newCastlingRights = SquareSet.empty(); + if (whiteCastling.q) { + newCastlingRights = newCastlingRights.set( + getCastlingSquare(setup, "w", "q")!, + true, + ); } - } + if (blackCastling.q) { + newCastlingRights = newCastlingRights.set( + getCastlingSquare(setup, "b", "q")!, + true, + ); + } + if (whiteCastling.k) { + newCastlingRights = newCastlingRights.set( + getCastlingSquare(setup, "w", "k")!, + true, + ); + } + if (blackCastling.k) { + newCastlingRights = newCastlingRights.set( + getCastlingSquare(setup, "b", "k")!, + true, + ); + } + setFen(makeFen({ ...setup, castlingRights: newCastlingRights })); + }, [blackCastling, setCastlingRights, setup, whiteCastling, setFen]); return (