diff --git a/packages/chess/src/app/App.tsx b/packages/chess/src/app/App.tsx index 14decb3..d9a2dc7 100644 --- a/packages/chess/src/app/App.tsx +++ b/packages/chess/src/app/App.tsx @@ -1,32 +1,51 @@ -import { Routes, Route } from 'react-router-dom' +import { Routes, Route, useNavigate } from 'react-router-dom' import { GameView } from '../ui/GameView' import { RulesView } from '../ui/RulesView' +import { SavePanel } from '../ui/SavePanel' +import { useChessEngine } from '../hooks/useChessEngine' +import { ChessEngine } from '../engine' +import type { AttrKey, FactValue } from '@paratype/rete' export function App() { + const chessState = useChessEngine() + return (
} /> - } /> + } /> } /> - } /> + } />
) } +function SaveWrapper({ chessState }: { chessState: ReturnType }) { + const navigate = useNavigate() + return ( + { + const newEngine = new ChessEngine() + const oldFacts = newEngine.session.allFacts(); + for (const f of oldFacts) { + newEngine.session.retract(f.id, f.attr as AttrKey); + } + for (const fact of loadedFacts) { + newEngine.session.insert(fact.id, fact.attr as AttrKey, fact.value as FactValue) + } + chessState.loadEngine(newEngine) + navigate('/game') + }} + /> + ) +} + function Home() { return (

Home

) -} - -function Save() { - return ( -
-

Save

-
- ) -} +} \ No newline at end of file diff --git a/packages/chess/src/hooks/useChessEngine.ts b/packages/chess/src/hooks/useChessEngine.ts index 5f37dde..056f3b1 100644 --- a/packages/chess/src/hooks/useChessEngine.ts +++ b/packages/chess/src/hooks/useChessEngine.ts @@ -4,6 +4,7 @@ import type { PieceType } from '../schema'; export function useChessEngine() { const engineRef = useRef(null); + const moveHistoryRef = useRef>([]); // Force re-render state const [tick, setTick] = useState(0); @@ -15,6 +16,12 @@ export function useChessEngine() { const engine = engineRef.current; + const loadEngine = useCallback((newEngine: ChessEngine) => { + engineRef.current = newEngine; + moveHistoryRef.current = []; + setTick(t => t + 1); + }, []); + const getTurn = useCallback(() => { return engine.getCurrentTurn(); }, [engine, tick]); @@ -34,6 +41,7 @@ export function useChessEngine() { const applyMove = useCallback((from: number, to: number, promoteTo: PieceType = 'queen'): GameResult | null => { const move = engine.findMove(from, to, promoteTo); if (move) { + moveHistoryRef.current.push({ from, to, promoteTo }); const result = engine.applyMove(move, promoteTo); setTick(t => t + 1); // trigger re-render return result; @@ -41,6 +49,21 @@ export function useChessEngine() { return null; }, [engine]); + const undo = useCallback(() => { + if (moveHistoryRef.current.length > 0) { + moveHistoryRef.current.pop(); + const newEngine = new ChessEngine(); + for (const historyMove of moveHistoryRef.current) { + const move = newEngine.findMove(historyMove.from, historyMove.to, historyMove.promoteTo); + if (move) { + newEngine.applyMove(move, historyMove.promoteTo); + } + } + engineRef.current = newEngine; + setTick(t => t + 1); + } + }, []); + return { engine, turn: getTurn(), @@ -48,5 +71,8 @@ export function useChessEngine() { legalMoves: getLegalMoves(), result: getResult(), applyMove, + undo, + canUndo: moveHistoryRef.current.length > 0, + loadEngine, }; } diff --git a/packages/chess/src/ui/GameView.tsx b/packages/chess/src/ui/GameView.tsx index 3771401..21657fa 100644 --- a/packages/chess/src/ui/GameView.tsx +++ b/packages/chess/src/ui/GameView.tsx @@ -1,14 +1,17 @@ -import { useChessEngine } from '../hooks/useChessEngine'; import { Board } from './Board'; - +import { useChessEngine } from '../hooks/useChessEngine'; import type { ChessFact, ChessAttrMap } from '../schema'; interface GameViewProps { - onUndo?: () => void; + engineState?: ReturnType; } -export function GameView({ onUndo }: GameViewProps) { - const { facts, legalMoves, turn, result, applyMove } = useChessEngine(); +export function GameView({ engineState }: GameViewProps) { + // Use passed in engine state or create local state if none provided + const localChessState = useChessEngine(); + const state = engineState || localChessState; + + const { facts, legalMoves, turn, result, applyMove, undo, canUndo } = state; const handleMove = (from: number, to: number) => { applyMove(from, to, 'queen'); @@ -39,8 +42,9 @@ export function GameView({ onUndo }: GameViewProps) { + + {!currentFacts && ( +

Start a game to enable saving.

+ )} + + +
+ {slots.length === 0 ? ( +
+ No saved games found. +
+ ) : ( +
    + {slots.map((slot) => ( +
  • +
    + {slot.name} + + {new Date(slot.timestamp).toLocaleString()} + +
    +
    + + +
    +
  • + ))} +
+ )} +
+ + + + ); +}