@@ -155,13 +155,30 @@ export const useAnalysisController = (game: AnalyzedGame) => {
155
155
const moves = chess . moves ( { verbose : true } )
156
156
const stockfish = controller . currentNode . analysis . stockfish
157
157
158
+ // Define thresholds for winrate loss
159
+ const GOOD_THRESHOLD = - INACCURACY_THRESHOLD // -0.05 (less than 5% winrate loss)
160
+ const OK_THRESHOLD = - BLUNDER_THRESHOLD // -0.1 (between 5% and 10% winrate loss)
161
+ // Anything worse than -0.1 is a blunder
162
+
158
163
moves . forEach ( ( m ) => {
159
164
const moveKey = `${ m . from } ${ m . to } `
165
+ // Use winrate_loss_vec if available, otherwise fall back to cp_relative_vec
166
+ const winrateLoss = stockfish ?. winrate_loss_vec ?. [ moveKey ]
160
167
const relativeEval = stockfish ?. cp_relative_vec [ moveKey ]
161
168
162
169
let color = '#FFF'
163
170
164
- if ( relativeEval !== undefined ) {
171
+ if ( winrateLoss !== undefined ) {
172
+ // Use winrate loss for coloring
173
+ if ( winrateLoss >= GOOD_THRESHOLD ) {
174
+ color = COLORS . good [ 0 ]
175
+ } else if ( winrateLoss >= OK_THRESHOLD ) {
176
+ color = COLORS . ok [ 0 ]
177
+ } else {
178
+ color = COLORS . blunder [ 0 ]
179
+ }
180
+ } else if ( relativeEval !== undefined ) {
181
+ // Fall back to CP-based coloring if winrate is not available
165
182
if ( relativeEval >= - 50 ) {
166
183
color = COLORS . good [ 0 ]
167
184
} else if ( relativeEval >= - 150 ) {
@@ -178,43 +195,105 @@ export const useAnalysisController = (game: AnalyzedGame) => {
178
195
} )
179
196
180
197
if ( stockfish ) {
181
- const goodMoves = moves
182
- . map ( ( m ) => `${ m . from } ${ m . to } ` )
183
- . filter ( ( move ) => stockfish . cp_relative_vec [ move ] >= - 50 )
184
- . sort (
185
- ( a , b ) => stockfish . cp_relative_vec [ b ] - stockfish . cp_relative_vec [ a ] ,
186
- )
187
-
188
- const okMoves = moves
189
- . map ( ( m ) => `${ m . from } ${ m . to } ` )
190
- . filter (
191
- ( move ) =>
192
- stockfish . cp_relative_vec [ move ] >= - 150 &&
193
- stockfish . cp_relative_vec [ move ] < - 50 ,
194
- )
195
- . sort (
196
- ( a , b ) => stockfish . cp_relative_vec [ b ] - stockfish . cp_relative_vec [ a ] ,
197
- )
198
-
199
- const blunderMoves = moves
200
- . map ( ( m ) => `${ m . from } ${ m . to } ` )
201
- . filter ( ( move ) => stockfish . cp_relative_vec [ move ] < - 150 )
202
- . sort (
203
- ( a , b ) => stockfish . cp_relative_vec [ b ] - stockfish . cp_relative_vec [ a ] ,
204
- )
205
-
206
- goodMoves . forEach ( ( move , i ) => {
207
- mapping [ move ] . color = COLORS . good [ Math . min ( i , COLORS . good . length - 1 ) ]
208
- } )
209
-
210
- okMoves . forEach ( ( move , i ) => {
211
- mapping [ move ] . color = COLORS . ok [ Math . min ( i , COLORS . ok . length - 1 ) ]
212
- } )
213
-
214
- blunderMoves . forEach ( ( move , i ) => {
215
- mapping [ move ] . color =
216
- COLORS . blunder [ Math . min ( i , COLORS . blunder . length - 1 ) ]
217
- } )
198
+ if (
199
+ stockfish . winrate_loss_vec &&
200
+ Object . keys ( stockfish . winrate_loss_vec ) . length > 0
201
+ ) {
202
+ const goodMoves = moves
203
+ . map ( ( m ) => `${ m . from } ${ m . to } ${ m . promotion || '' } ` )
204
+ . filter ( ( move ) => {
205
+ const winrateLoss = stockfish . winrate_loss_vec ?. [ move ]
206
+ return winrateLoss !== undefined && winrateLoss >= GOOD_THRESHOLD
207
+ } )
208
+ . sort ( ( a , b ) => {
209
+ const aLoss = stockfish . winrate_loss_vec ?. [ a ] || 0
210
+ const bLoss = stockfish . winrate_loss_vec ?. [ b ] || 0
211
+ return bLoss - aLoss
212
+ } )
213
+
214
+ const okMoves = moves
215
+ . map ( ( m ) => `${ m . from } ${ m . to } ${ m . promotion || '' } ` )
216
+ . filter ( ( move ) => {
217
+ const winrateLoss = stockfish . winrate_loss_vec ?. [ move ]
218
+ return (
219
+ winrateLoss !== undefined &&
220
+ winrateLoss >= OK_THRESHOLD &&
221
+ winrateLoss < GOOD_THRESHOLD
222
+ )
223
+ } )
224
+ . sort ( ( a , b ) => {
225
+ const aLoss = stockfish . winrate_loss_vec ?. [ a ] || 0
226
+ const bLoss = stockfish . winrate_loss_vec ?. [ b ] || 0
227
+ return bLoss - aLoss
228
+ } )
229
+
230
+ const blunderMoves = moves
231
+ . map ( ( m ) => `${ m . from } ${ m . to } ${ m . promotion || '' } ` )
232
+ . filter ( ( move ) => {
233
+ const winrateLoss = stockfish . winrate_loss_vec ?. [ move ]
234
+ return winrateLoss !== undefined && winrateLoss < OK_THRESHOLD
235
+ } )
236
+ . sort ( ( a , b ) => {
237
+ const aLoss = stockfish . winrate_loss_vec ?. [ a ] || 0
238
+ const bLoss = stockfish . winrate_loss_vec ?. [ b ] || 0
239
+ return bLoss - aLoss
240
+ } )
241
+
242
+ goodMoves . forEach ( ( move , i ) => {
243
+ mapping [ move ] . color = COLORS . good [ Math . min ( i , COLORS . good . length - 1 ) ]
244
+ } )
245
+
246
+ okMoves . forEach ( ( move , i ) => {
247
+ mapping [ move ] . color = COLORS . ok [ Math . min ( i , COLORS . ok . length - 1 ) ]
248
+ } )
249
+
250
+ blunderMoves . forEach ( ( move , i ) => {
251
+ mapping [ move ] . color =
252
+ COLORS . blunder [ Math . min ( i , COLORS . blunder . length - 1 ) ]
253
+ } )
254
+ } else {
255
+ // Fall back to CP-based coloring if winrate is not available
256
+ const goodMoves = moves
257
+ . map ( ( m ) => `${ m . from } ${ m . to } ${ m . promotion || '' } ` )
258
+ . filter ( ( move ) => stockfish . cp_relative_vec [ move ] >= - 50 )
259
+ . sort (
260
+ ( a , b ) =>
261
+ stockfish . cp_relative_vec [ b ] - stockfish . cp_relative_vec [ a ] ,
262
+ )
263
+
264
+ const okMoves = moves
265
+ . map ( ( m ) => `${ m . from } ${ m . to } ${ m . promotion || '' } ` )
266
+ . filter (
267
+ ( move ) =>
268
+ stockfish . cp_relative_vec [ move ] >= - 150 &&
269
+ stockfish . cp_relative_vec [ move ] < - 50 ,
270
+ )
271
+ . sort (
272
+ ( a , b ) =>
273
+ stockfish . cp_relative_vec [ b ] - stockfish . cp_relative_vec [ a ] ,
274
+ )
275
+
276
+ const blunderMoves = moves
277
+ . map ( ( m ) => `${ m . from } ${ m . to } ${ m . promotion || '' } ` )
278
+ . filter ( ( move ) => stockfish . cp_relative_vec [ move ] < - 150 )
279
+ . sort (
280
+ ( a , b ) =>
281
+ stockfish . cp_relative_vec [ b ] - stockfish . cp_relative_vec [ a ] ,
282
+ )
283
+
284
+ goodMoves . forEach ( ( move , i ) => {
285
+ mapping [ move ] . color = COLORS . good [ Math . min ( i , COLORS . good . length - 1 ) ]
286
+ } )
287
+
288
+ okMoves . forEach ( ( move , i ) => {
289
+ mapping [ move ] . color = COLORS . ok [ Math . min ( i , COLORS . ok . length - 1 ) ]
290
+ } )
291
+
292
+ blunderMoves . forEach ( ( move , i ) => {
293
+ mapping [ move ] . color =
294
+ COLORS . blunder [ Math . min ( i , COLORS . blunder . length - 1 ) ]
295
+ } )
296
+ }
218
297
}
219
298
220
299
return mapping
0 commit comments