@@ -155,6 +155,29 @@ function evaluatePosition(state: GameState, color: Color): number {
155155 return score ;
156156}
157157
158+ /**
159+ * Cheap position score for large boards: no allLegalMoves calls.
160+ * Computes material directly (avoiding materialScore's mobility allLegalMoves),
161+ * skips gameResult check and reply lookahead.
162+ */
163+ function fastEvaluatePosition ( state : GameState , color : Color ) : number {
164+ // Material count without mobility bonus (avoids allLegalMoves)
165+ let score = 0 ;
166+ for ( const row of state . board ) for ( const piece of row ) {
167+ if ( ! piece ) continue ;
168+ const v = PIECE_DANGER_VALUES [ piece . type ] ?? 150 ;
169+ score += ( piece . color === color ? 1 : - 1 ) * v ;
170+ }
171+ score += pieceDevelopmentBonus ( state , color ) ;
172+ score += centerControlBonus ( state , color ) ;
173+ score -= hangingPiecePenalty ( state , color ) ;
174+ if ( inCheck ( state , opposite ( color ) ) ) score += 40 ;
175+ if ( inCheck ( state , color ) ) score -= 55 ;
176+ const lastMove = state . history [ state . history . length - 1 ] ;
177+ if ( lastMove ?. promotion ) score += 120 ;
178+ return score ;
179+ }
180+
158181function gradeFromLoss ( loss : number ) : MoveGrade {
159182 if ( loss <= 2 ) return 5 ;
160183 if ( loss <= 6 ) return 4 ;
@@ -285,6 +308,12 @@ function exchangeCredit(state: GameState, move: Move): number {
285308}
286309
287310function evaluateMoveLine ( next : GameState , color : Color ) : number {
311+ // Fast path for non-standard boards: skip gameResult (allLegalMoves) and
312+ // reply lookahead — both are O(moves) which explodes on large boards.
313+ if ( boardWidth ( next ) * boardHeight ( next ) > 64 ) {
314+ return fastEvaluatePosition ( next , color ) ;
315+ }
316+
288317 const immediate = evaluatePosition ( next , color ) ;
289318 const result = gameResult ( next ) ;
290319 if ( result . kind !== "ongoing" ) return immediate ;
0 commit comments