@@ -9,6 +9,25 @@ import { useSound } from 'src/hooks/useSound'
99import { safeUpdateRating } from 'src/lib/ratingUtils'
1010import { MaiaEngineContext } from 'src/contexts'
1111
12+ const MAIA_VALUE_HEAD_DEBUG_KEY = 'maia.valueHeadDebug'
13+
14+ const isTruthy = ( value : string | null | undefined ) : boolean => {
15+ if ( ! value ) return false
16+ return [ '1' , 'true' , 'yes' , 'on' ] . includes ( value . toLowerCase ( ) )
17+ }
18+
19+ const isMaiaValueHeadDebugEnabled = ( ) : boolean => {
20+ if ( typeof window === 'undefined' ) {
21+ return false
22+ }
23+
24+ try {
25+ return isTruthy ( window . localStorage . getItem ( MAIA_VALUE_HEAD_DEBUG_KEY ) )
26+ } catch {
27+ return false
28+ }
29+ }
30+
1231const playStatsLoader = async ( ) => {
1332 const stats = await fetchPlayPlayerStats ( )
1433 return {
@@ -35,13 +54,22 @@ export const useVsMaiaPlayController = (
3554 playGameConfig . maiaMoveSelectionMode === 'value_head'
3655 ? maiaEngine . status
3756 : null
57+ const playerRatingForValueHead = stats . rating ?? 1500
58+ const valueHeadDebugEnabled = isMaiaValueHeadDebugEnabled ( )
3859
3960 const selectValueHeadMove = useCallback ( async ( ) => {
4061 if (
4162 ! controller . currentNode ||
4263 valueHeadStatus !== 'ready' ||
4364 ! valueHeadModel
4465 ) {
66+ if ( valueHeadDebugEnabled ) {
67+ console . log ( '[Maia value-head debug] selector unavailable' , {
68+ hasCurrentNode : ! ! controller . currentNode ,
69+ valueHeadStatus,
70+ hasModel : ! ! valueHeadModel ,
71+ } )
72+ }
4573 return null
4674 }
4775
@@ -71,9 +99,13 @@ export const useVsMaiaPlayController = (
7199 10 ,
72100 )
73101 const modelElo = Number . isNaN ( maiaRating ) ? 1500 : maiaRating
102+
103+ // After Maia makes a candidate move, it is the human's turn.
104+ // Maia's value head conditions on the side to move as elo_self,
105+ // so the resulting boards must use the human as elo_self and Maia as elo_oppo.
74106 const { result } = await valueHeadModel . batchEvaluate (
75107 candidateBoards ,
76- Array ( candidateBoards . length ) . fill ( modelElo ) ,
108+ Array ( candidateBoards . length ) . fill ( playerRatingForValueHead ) ,
77109 Array ( candidateBoards . length ) . fill ( modelElo ) ,
78110 )
79111
@@ -91,6 +123,30 @@ export const useVsMaiaPlayController = (
91123 }
92124 }
93125
126+ if ( valueHeadDebugEnabled ) {
127+ const candidateSummaries = candidateMoves
128+ . map ( ( moveUci , index ) => {
129+ const moveResult = result [ index ]
130+ const whiteWinProb = moveResult . value
131+ const maiaWinProb = maiaIsWhite ? whiteWinProb : 1 - whiteWinProb
132+ const board = new Chess ( currentFen )
133+ const moveObj = board . move ( moveUci , { sloppy : true } )
134+
135+ return {
136+ move : moveUci ,
137+ san : moveObj ?. san ?? moveUci ,
138+ maiaWinProb : Number ( maiaWinProb . toFixed ( 4 ) ) ,
139+ }
140+ } )
141+ . sort ( ( a , b ) => b . maiaWinProb - a . maiaWinProb )
142+
143+ console . groupCollapsed (
144+ `[Maia value-head debug] ${ playGameConfig . maiaVersion } selected ${ bestMove } ` ,
145+ )
146+ console . table ( candidateSummaries )
147+ console . groupEnd ( )
148+ }
149+
94150 const estimatedDelaySeconds = Math . min (
95151 3 ,
96152 0.35 + legalMoves . length * 0.04 + Math . random ( ) * 0.25 ,
@@ -104,8 +160,10 @@ export const useVsMaiaPlayController = (
104160 controller . currentNode ,
105161 controller . player ,
106162 playGameConfig . maiaVersion ,
163+ playerRatingForValueHead ,
107164 valueHeadModel ,
108165 valueHeadStatus ,
166+ valueHeadDebugEnabled ,
109167 ] )
110168
111169 const makePlayerMove = async ( moveUci : string ) => {
0 commit comments