Skip to content

Commit fd722e6

Browse files
committed
Modern chessboard UI, move list layouts, and UX overhaul
Introduced ModernChessboard component with right/below move list layouts and responsive CSS. Refactored PlayVsComputer to use new board, added player bar, icons, clocks, and review game flow. Enhanced move/variation handling, move action menus, and PGN save/review. Added UCI script loader to EnginePanel. Improved file browser, analysis UI, and terminology. Refactored F# backend for more robust puzzle/engine analysis and board state management. Numerous UI/UX and styling improvements throughout.
1 parent b2e3270 commit fd722e6

13 files changed

Lines changed: 1458 additions & 205 deletions

File tree

ChessLibrary/Analysis/PuzzleCrossEngine.fs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ let analyzeCrossEngine (scores: Score seq) : CrossEngineResult list =
4949
e.FailedPuzzles |> Seq.map (fun (p, _) -> p.PuzzleId) |> Set.ofSeq)
5050
|> Map.ofList
5151

52-
// Build puzzle lookup by PuzzleId
52+
// Puzzle lookup for solved puzzles (any engine's data works since MovePlayed isn't needed)
5353
let puzzleLookup =
5454
engines
5555
|> Seq.collect (fun e ->
@@ -59,11 +59,11 @@ let analyzeCrossEngine (scores: Score seq) : CrossEngineResult list =
5959
|> Seq.distinctBy fst
6060
|> Map.ofSeq
6161

62-
// Failed puzzle move lookup: engine -> puzzleId -> movePlayed
63-
let failedMoveLookup =
62+
// Per-engine failed puzzle lookup: uses each engine's own data (with MovePlayed set)
63+
let failedPuzzleLookup =
6464
engines |> List.map (fun e ->
6565
e.Engine,
66-
e.FailedPuzzles |> Seq.map (fun (p, m) -> p.PuzzleId, m) |> Map.ofSeq)
66+
e.FailedPuzzles |> Seq.map (fun (p, m) -> p.PuzzleId, (p, m)) |> Map.ofSeq)
6767
|> Map.ofList
6868

6969
// Uniquely solved: only engine X solved, all others failed
@@ -96,9 +96,7 @@ let analyzeCrossEngine (scores: Score seq) : CrossEngineResult list =
9696
| sets -> sets |> List.fold Set.intersect myFailed
9797
let puzzlesWithMoves =
9898
unique |> Set.toList |> List.choose (fun id ->
99-
match Map.tryFind id puzzleLookup, Map.tryFind id (failedMoveLookup.[eng]) with
100-
| Some p, Some m -> Some (p, m)
101-
| _ -> None)
99+
Map.tryFind id failedPuzzleLookup.[eng])
102100
eng, puzzlesWithMoves)
103101
|> Map.ofList
104102

@@ -111,13 +109,15 @@ let analyzeCrossEngine (scores: Score seq) : CrossEngineResult list =
111109
| first :: rest -> rest |> List.fold Set.intersect first
112110
let failedByAll =
113111
allFailedIds |> Set.toList |> List.choose (fun id ->
114-
match Map.tryFind id puzzleLookup with
112+
// Use any engine's failed puzzle data (all engines failed, so all have MovePlayed set)
113+
let puzzleData =
114+
engineNames |> List.tryPick (fun eng ->
115+
failedPuzzleLookup.[eng] |> Map.tryFind id |> Option.map fst)
116+
match puzzleData with
115117
| Some p ->
116118
let moves =
117119
engineNames |> List.choose (fun eng ->
118-
match Map.tryFind id (failedMoveLookup.[eng]) with
119-
| Some m -> Some (eng, m)
120-
| None -> None)
120+
failedPuzzleLookup.[eng] |> Map.tryFind id |> Option.map (fun (_, m) -> eng, m))
121121
|> Map.ofList
122122
Some (p, moves)
123123
| None -> None)
@@ -169,18 +169,18 @@ let private getFailedPuzzleEpd (puzzle: CsvPuzzleData) (policyStr: string) =
169169
None)
170170

171171
/// Get FEN and best-move SAN for a solved puzzle command.
172-
/// Returns (fen, bmSan, uciCorrectMove).
172+
/// Returns (fen, bmSan, uciCorrectMove). Uses the last command (decisive move).
173173
let private getSolvedPuzzleEpd (puzzle: CsvPuzzleData) =
174174
let board = Board()
175-
puzzle.Commands |> Seq.tryPick (fun cmd ->
175+
let mutable result : (string * string * string) option = None
176+
for cmd in puzzle.Commands do
176177
if not (String.IsNullOrWhiteSpace(cmd.CorrectMove)) && cmd.CorrectMove.Length >= 4 then
177178
board.PlayCommands(cmd.Command)
178179
let fen = board.FEN()
179180
board.PlayUciMove(cmd.CorrectMove)
180181
let bm = board.SanMovesPlayed |> Seq.tryLast |> Option.defaultValue ""
181-
Some (fen, bm, cmd.CorrectMove)
182-
else
183-
None)
182+
result <- Some (fen, bm, cmd.CorrectMove)
183+
result
184184

185185
/// Write cross-engine analysis files. Only creates files that have content. Does nothing for single-engine runs.
186186
let writeCrossEngineFiles (outputFolder: string) (dateStr: string) (allScores: Score seq) =

ChessLibrary/Analysis/PuzzleEngineAgent.fs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ let startValueEngineAgent (engineCfg:EngineConfig) =
3232
| Ok reply ->
3333
reply.Reply(true)
3434
| NewGame reply ->
35+
engine.UciNewGame()
36+
engine.WaitForReadyOk() |> ignore
3537
reply.Reply()
3638
| BestMove (cmd, reply) ->
3739
let mv = bestQPuzzleValueOnly engine cmd
@@ -158,9 +160,8 @@ let startPolicyEngineAgent (engineCfg:EngineConfig) nodes =
158160

159161
//Per-puzzle async workflow
160162
let runPuzzleViaAgent (agent:MailboxProcessor<EngineMsg>) (valueHead : bool) (puzzle:CsvPuzzleData) = async {
161-
// Reset engine state between puzzles (policy/search only)
162-
if not valueHead then
163-
do! agent.PostAndAsyncReply(fun ch -> NewGame ch)
163+
// Reset engine state between puzzles
164+
do! agent.PostAndAsyncReply(fun ch -> NewGame ch)
164165

165166
// 3b) Fresh board per puzzle
166167
let board = Board()

ChessLibrary/Chess/Board.fs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ type Board() =
498498
| _ -> ()
499499

500500
// Graph-aware callers expect a path command
501-
member this.PositionWithMovesFromGraph() =
501+
member this.PositionWithMovesFromGraph() =
502502
updatePathFromCurrent()
503503
this.PositionWithMoves()
504504

@@ -1555,9 +1555,8 @@ type Board() =
15551555
|_ -> ()
15561556

15571557
member this.PlayFenWithMoves (fenMoves : string) =
1558-
this.ResetBoardState()
15591558
let (fen, moves) = FEN.parseFENandMoves fenMoves
1560-
this.LoadFen fen
1559+
this.ResetBoardStateFromFen fen
15611560
let normalizedFen = this.FEN()
15621561
this.CurrentFEN <- normalizedFen
15631562
this.StartPosition <- normalizedFen

0 commit comments

Comments
 (0)