1- import { useCallback , useContext , useEffect } from 'react'
1+ import { useEffect } from 'react'
22import { Chess } from 'chess.ts'
33import { PlayGameConfig } from 'src/types'
44import { backOff } from 'exponential-backoff'
@@ -7,26 +7,6 @@ import { usePlayController } from 'src/hooks/usePlayController'
77import { fetchGameMove , logGameMove , fetchPlayPlayerStats } from 'src/api'
88import { useSound } from 'src/hooks/useSound'
99import { safeUpdateRating } from 'src/lib/ratingUtils'
10- import { MaiaEngineContext } from 'src/contexts'
11-
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- }
3010
3111const playStatsLoader = async ( ) => {
3212 const stats = await fetchPlayPlayerStats ( )
@@ -45,126 +25,6 @@ export const useVsMaiaPlayController = (
4525 const controller = usePlayController ( id , playGameConfig )
4626 const [ stats , incrementStats , updateRating ] = useStats ( playStatsLoader )
4727 const { playMoveSound } = useSound ( )
48- const maiaEngine = useContext ( MaiaEngineContext )
49- const valueHeadModel =
50- playGameConfig . maiaMoveSelectionMode === 'value_head'
51- ? maiaEngine . maia
52- : null
53- const valueHeadStatus =
54- playGameConfig . maiaMoveSelectionMode === 'value_head'
55- ? maiaEngine . status
56- : null
57- const playerRatingForValueHead = stats . rating ?? 1500
58- const valueHeadDebugEnabled = isMaiaValueHeadDebugEnabled ( )
59-
60- const selectValueHeadMove = useCallback ( async ( ) => {
61- if (
62- ! controller . currentNode ||
63- valueHeadStatus !== 'ready' ||
64- ! valueHeadModel
65- ) {
66- if ( valueHeadDebugEnabled ) {
67- console . log ( '[Maia value-head debug] selector unavailable' , {
68- hasCurrentNode : ! ! controller . currentNode ,
69- valueHeadStatus,
70- hasModel : ! ! valueHeadModel ,
71- } )
72- }
73- return null
74- }
75-
76- const currentFen = controller . currentNode . fen
77- const chess = new Chess ( currentFen )
78- const legalMoves = chess . moves ( { verbose : true } ) as Array < {
79- from : string
80- to : string
81- promotion ?: string
82- } >
83-
84- if ( legalMoves . length === 0 ) {
85- return null
86- }
87-
88- const candidateMoves = legalMoves . map (
89- ( move ) => `${ move . from } ${ move . to } ${ move . promotion ?? '' } ` ,
90- )
91- const candidateBoards = candidateMoves . map ( ( moveUci ) => {
92- const board = new Chess ( currentFen )
93- board . move ( moveUci , { sloppy : true } )
94- return board . fen ( )
95- } )
96-
97- const maiaRating = parseInt (
98- playGameConfig . maiaVersion . replace ( 'maia_kdd_' , '' ) ,
99- 10 ,
100- )
101- 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.
106- const { result } = await valueHeadModel . batchEvaluate (
107- candidateBoards ,
108- Array ( candidateBoards . length ) . fill ( playerRatingForValueHead ) ,
109- Array ( candidateBoards . length ) . fill ( modelElo ) ,
110- )
111-
112- const maiaIsWhite = controller . player === 'black'
113- let bestMove = candidateMoves [ 0 ]
114- let bestScore = maiaIsWhite ? result [ 0 ] . value : 1 - result [ 0 ] . value
115-
116- for ( let index = 1 ; index < candidateMoves . length ; index ++ ) {
117- const whiteWinProb = result [ index ] . value
118- const maiaWinProb = maiaIsWhite ? whiteWinProb : 1 - whiteWinProb
119-
120- if ( maiaWinProb > bestScore ) {
121- bestMove = candidateMoves [ index ]
122- bestScore = maiaWinProb
123- }
124- }
125-
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-
150- const estimatedDelaySeconds = Math . min (
151- 3 ,
152- 0.35 + legalMoves . length * 0.04 + Math . random ( ) * 0.25 ,
153- )
154-
155- return {
156- top_move : bestMove ,
157- move_delay : estimatedDelaySeconds ,
158- }
159- } , [
160- controller . currentNode ,
161- controller . player ,
162- playGameConfig . maiaVersion ,
163- playerRatingForValueHead ,
164- valueHeadModel ,
165- valueHeadStatus ,
166- valueHeadDebugEnabled ,
167- ] )
16828
16929 const makePlayerMove = async ( moveUci : string ) => {
17030 const moveTime = controller . updateClock ( )
@@ -188,28 +48,20 @@ export const useVsMaiaPlayController = (
18848 ? parseInt ( controller . timeControl . split ( '+' ) [ 0 ] ) * 60
18949 : 0
19050
191- const maiaMoves =
192- playGameConfig . maiaMoveSelectionMode === 'value_head'
193- ? await selectValueHeadMove ( )
194- : await backOff (
195- ( ) =>
196- fetchGameMove (
197- controller . moveList ,
198- playGameConfig . maiaVersion ,
199- playGameConfig . startFen ,
200- null ,
201- simulateMaiaTime ? initialClock : 0 ,
202- simulateMaiaTime ? maiaClock : 0 ,
203- ) ,
204- {
205- jitter : 'full' ,
206- } ,
207- )
208-
209- if ( ! maiaMoves ?. top_move ) {
210- return
211- }
212-
51+ const maiaMoves = await backOff (
52+ ( ) =>
53+ fetchGameMove (
54+ controller . moveList ,
55+ playGameConfig . maiaVersion ,
56+ playGameConfig . startFen ,
57+ null ,
58+ simulateMaiaTime ? initialClock : 0 ,
59+ simulateMaiaTime ? maiaClock : 0 ,
60+ ) ,
61+ {
62+ jitter : 'full' ,
63+ } ,
64+ )
21365 const nextMove = maiaMoves [ 'top_move' ]
21466 const moveDelay = maiaMoves [ 'move_delay' ]
21567
@@ -255,11 +107,8 @@ export const useVsMaiaPlayController = (
255107 controller . game . termination ,
256108 controller . moveList . length ,
257109 playGameConfig . maiaVersion ,
258- playGameConfig . maiaMoveSelectionMode ,
259110 playGameConfig . startFen ,
260111 simulateMaiaTime ,
261- selectValueHeadMove ,
262- valueHeadStatus ,
263112 ] )
264113
265114 useEffect ( ( ) => {
0 commit comments