Skip to content

Commit b1612b1

Browse files
Fix stockfish move color ordering
1 parent 8cf595e commit b1612b1

3 files changed

Lines changed: 92 additions & 58 deletions

File tree

src/hooks/useAnalysisController/useAnalysisController.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,12 @@ export const useAnalysisController = (
179179
}
180180

181181
if (moveEvaluation?.stockfish) {
182-
const bestMove = Object.entries(moveEvaluation.stockfish.cp_vec)[0]
182+
const bestMove = moveEvaluation.stockfish.model_move
183183
if (bestMove) {
184184
arrows.push({
185185
brush: 'blue',
186-
orig: bestMove[0].slice(0, 2) as Key,
187-
dest: bestMove[0].slice(2, 4) as Key,
186+
orig: bestMove.slice(0, 2) as Key,
187+
dest: bestMove.slice(2, 4) as Key,
188188
modifiers: { lineWidth: 8 },
189189
} as DrawShape)
190190
}

src/hooks/useAnalysisController/useMoveRecommendations.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useMemo } from 'react'
22
import { Chess } from 'chess.ts'
33
import { MAIA_MODELS } from 'src/constants/common'
44
import { GameNode, MaiaEvaluation, StockfishEvaluation } from 'src/types'
5+
import { sortStockfishMoves } from './utils'
56

67
export const useMoveRecommendations = (
78
currentNode: GameNode | null,
@@ -23,6 +24,7 @@ export const useMoveRecommendations = (
2324
cp: number
2425
winrate?: number
2526
winrate_loss?: number
27+
cp_relative?: number
2628
}[]
2729
isBlackTurn?: boolean
2830
} = {
@@ -44,10 +46,14 @@ export const useMoveRecommendations = (
4446
const cp_relative_vec = moveEvaluation.stockfish.cp_relative_vec || {}
4547
const winrate_vec = moveEvaluation.stockfish.winrate_vec || {}
4648
const winrate_loss_vec = moveEvaluation.stockfish.winrate_loss_vec || {}
49+
const sortedMoves = sortStockfishMoves(
50+
moveEvaluation.stockfish,
51+
Object.keys(cp_vec),
52+
)
4753

48-
const stockfish = Object.entries(cp_vec).map(([move, cp]) => ({
54+
const stockfish = sortedMoves.map((move) => ({
4955
move,
50-
cp,
56+
cp: cp_vec[move] || 0,
5157
winrate: winrate_vec[move] || 0,
5258
winrate_loss: winrate_loss_vec[move] || 0,
5359
cp_relative: cp_relative_vec[move] || 0,
@@ -80,7 +86,10 @@ export const useMoveRecommendations = (
8086

8187
// Get top 3 Stockfish moves
8288
if (stockfish) {
83-
for (const move of Object.keys(stockfish.cp_vec).slice(0, 3)) {
89+
for (const move of sortStockfishMoves(
90+
stockfish,
91+
Object.keys(stockfish.cp_vec),
92+
).slice(0, 3)) {
8493
if (candidates.find((c) => c[0] === move)) continue
8594
candidates.push([move, move])
8695
}

src/hooks/useAnalysisController/utils.ts

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,51 @@ type ColorSanMappingResult = {
1414
}
1515
}
1616

17+
const getStockfishMoveOrderingScore = (
18+
stockfish: StockfishEvaluation,
19+
move: string,
20+
): number => {
21+
const winrateLoss = stockfish.winrate_loss_vec?.[move]
22+
if (winrateLoss !== undefined) {
23+
return winrateLoss
24+
}
25+
26+
const relativeEval = stockfish.cp_relative_vec?.[move]
27+
if (relativeEval !== undefined) {
28+
return relativeEval
29+
}
30+
31+
const cp = stockfish.cp_vec?.[move]
32+
if (cp !== undefined) {
33+
return cp
34+
}
35+
36+
return Number.NEGATIVE_INFINITY
37+
}
38+
39+
export const sortStockfishMoves = (
40+
stockfish: StockfishEvaluation,
41+
moves: string[],
42+
): string[] =>
43+
[...moves].sort((a, b) => {
44+
const scoreDiff =
45+
getStockfishMoveOrderingScore(stockfish, b) -
46+
getStockfishMoveOrderingScore(stockfish, a)
47+
48+
if (scoreDiff !== 0) {
49+
return scoreDiff
50+
}
51+
52+
const cpDiff =
53+
(stockfish.cp_vec?.[b] ?? Number.NEGATIVE_INFINITY) -
54+
(stockfish.cp_vec?.[a] ?? Number.NEGATIVE_INFINITY)
55+
if (cpDiff !== 0) {
56+
return cpDiff
57+
}
58+
59+
return a.localeCompare(b)
60+
})
61+
1762
// Unified function to calculate color for a single move
1863
export const calculateMoveColor = (
1964
stockfish: StockfishEvaluation | undefined,
@@ -56,6 +101,7 @@ export const generateColorSanMapping = (
56101

57102
const chess = new Chess(fen)
58103
const moves = chess.moves({ verbose: true })
104+
const moveKeys = moves.map((m) => `${m.from}${m.to}${m.promotion || ''}`)
59105
moves.forEach((m) => {
60106
const moveKey = `${m.from}${m.to}${m.promotion || ''}`
61107
mapping[moveKey] = {
@@ -81,51 +127,37 @@ export const generateColorSanMapping = (
81127
stockfish.winrate_loss_vec &&
82128
Object.keys(stockfish.winrate_loss_vec).length > 0
83129
) {
84-
const goodMoves = moves
85-
.map((m) => `${m.from}${m.to}${m.promotion || ''}`)
86-
.filter((move) => {
130+
const goodMoves = sortStockfishMoves(
131+
stockfish,
132+
moveKeys.filter((move) => {
87133
const winrateLoss = stockfish.winrate_loss_vec?.[move]
88134
return (
89135
winrateLoss !== undefined &&
90136
winrateLoss >= -MOVE_CLASSIFICATION_THRESHOLDS.INACCURACY_THRESHOLD
91137
)
92-
})
93-
.sort((a, b) => {
94-
const aLoss = stockfish.winrate_loss_vec?.[a] || 0
95-
const bLoss = stockfish.winrate_loss_vec?.[b] || 0
96-
return bLoss - aLoss
97-
})
98-
99-
const okMoves = moves
100-
.map((m) => `${m.from}${m.to}${m.promotion || ''}`)
101-
.filter((move) => {
138+
}),
139+
)
140+
const okMoves = sortStockfishMoves(
141+
stockfish,
142+
moveKeys.filter((move) => {
102143
const winrateLoss = stockfish.winrate_loss_vec?.[move]
103144
return (
104145
winrateLoss !== undefined &&
105146
winrateLoss >= -MOVE_CLASSIFICATION_THRESHOLDS.BLUNDER_THRESHOLD &&
106147
winrateLoss < -MOVE_CLASSIFICATION_THRESHOLDS.INACCURACY_THRESHOLD
107148
)
108-
})
109-
.sort((a, b) => {
110-
const aLoss = stockfish.winrate_loss_vec?.[a] || 0
111-
const bLoss = stockfish.winrate_loss_vec?.[b] || 0
112-
return bLoss - aLoss
113-
})
114-
115-
const blunderMoves = moves
116-
.map((m) => `${m.from}${m.to}${m.promotion || ''}`)
117-
.filter((move) => {
149+
}),
150+
)
151+
const blunderMoves = sortStockfishMoves(
152+
stockfish,
153+
moveKeys.filter((move) => {
118154
const winrateLoss = stockfish.winrate_loss_vec?.[move]
119155
return (
120156
winrateLoss !== undefined &&
121157
winrateLoss < -MOVE_CLASSIFICATION_THRESHOLDS.BLUNDER_THRESHOLD
122158
)
123-
})
124-
.sort((a, b) => {
125-
const aLoss = stockfish.winrate_loss_vec?.[a] || 0
126-
const bLoss = stockfish.winrate_loss_vec?.[b] || 0
127-
return bLoss - aLoss
128-
})
159+
}),
160+
)
129161

130162
goodMoves.forEach((move, i) => {
131163
mapping[move].color = COLORS.good[Math.min(i, COLORS.good.length - 1)]
@@ -140,30 +172,23 @@ export const generateColorSanMapping = (
140172
COLORS.blunder[Math.min(i, COLORS.blunder.length - 1)]
141173
})
142174
} else {
143-
const goodMoves = moves
144-
.map((m) => `${m.from}${m.to}${m.promotion || ''}`)
145-
.filter((move) => stockfish.cp_relative_vec[move] >= -50)
146-
.sort(
147-
(a, b) => stockfish.cp_relative_vec[b] - stockfish.cp_relative_vec[a],
148-
)
149-
150-
const okMoves = moves
151-
.map((m) => `${m.from}${m.to}${m.promotion || ''}`)
152-
.filter(
153-
(move) =>
175+
const goodMoves = sortStockfishMoves(
176+
stockfish,
177+
moveKeys.filter((move) => stockfish.cp_relative_vec[move] >= -50),
178+
)
179+
const okMoves = sortStockfishMoves(
180+
stockfish,
181+
moveKeys.filter((move) => {
182+
return (
154183
stockfish.cp_relative_vec[move] >= -150 &&
155-
stockfish.cp_relative_vec[move] < -50,
156-
)
157-
.sort(
158-
(a, b) => stockfish.cp_relative_vec[b] - stockfish.cp_relative_vec[a],
159-
)
160-
161-
const blunderMoves = moves
162-
.map((m) => `${m.from}${m.to}${m.promotion || ''}`)
163-
.filter((move) => stockfish.cp_relative_vec[move] < -150)
164-
.sort(
165-
(a, b) => stockfish.cp_relative_vec[b] - stockfish.cp_relative_vec[a],
166-
)
184+
stockfish.cp_relative_vec[move] < -50
185+
)
186+
}),
187+
)
188+
const blunderMoves = sortStockfishMoves(
189+
stockfish,
190+
moveKeys.filter((move) => stockfish.cp_relative_vec[move] < -150),
191+
)
167192

168193
goodMoves.forEach((move, i) => {
169194
mapping[move].color = COLORS.good[Math.min(i, COLORS.good.length - 1)]

0 commit comments

Comments
 (0)