@@ -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).
173173let 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.
186186let writeCrossEngineFiles ( outputFolder : string ) ( dateStr : string ) ( allScores : Score seq ) =
0 commit comments