1- import { Chess } from " chess.js" ;
1+ import { Chess } from ' chess.js'
22
3- type StockfishEvals = Record < string , number > ; // UCI → eval (white-positive)
4- type MaiaEvals = Record < string , number [ ] > ; // UCI → 9-length probability array
3+ type StockfishEvals = Record < string , number > // UCI → eval (white-positive)
4+ type MaiaEvals = Record < string , number [ ] > // UCI → 9-length probability array
55
6- const A = 1 , B = 0.8 , EPS = 0.08 ;
6+ const A = 1
7+ const B = 0.8
8+ const EPS = 0.08 // 8 %
79
8- const winRate = ( p : number ) => 1 / ( 1 + Math . exp ( - ( p - A ) / B ) ) ;
9- const wdl = ( p : number ) => { const w = winRate ( p ) , l = winRate ( - p ) ; return { w, d : 1 - w - l } ; } ;
10+ const winRate = ( p : number ) => 1 / ( 1 + Math . exp ( - ( p - A ) / B ) )
11+ const wdl = ( p : number ) => {
12+ const w = winRate ( p )
13+ const l = winRate ( - p )
14+ return { w, d : 1 - w - l }
15+ }
1016
1117const legalMovesUci = ( fen : string ) => {
12- const c = new Chess ( fen ) ;
13- const s = new Set < string > ( ) ;
14- c . moves ( { verbose : true } ) . forEach ( m => s . add ( m . from + m . to + ( m . promotion ?? "" ) ) ) ;
15- return s ;
16- } ;
18+ const c = new Chess ( fen )
19+ const s = new Set < string > ( )
20+ c
21+ . moves ( { verbose : true } )
22+ . forEach ( m => s . add ( m . from + m . to + ( m . promotion ?? '' ) ) )
23+ return s
24+ }
1725
1826export function describePosition (
1927 fen : string ,
@@ -22,91 +30,96 @@ export function describePosition(
2230 whiteToMove : boolean ,
2331 eps = EPS
2432) : string {
25- const legal = legalMovesUci ( fen ) ;
26- const moves = Object . keys ( sf ) . filter ( m => legal . has ( m ) ) ;
27- if ( ! moves . length ) return " No legal moves available." ;
33+ const legal = legalMovesUci ( fen )
34+ const moves = Object . keys ( sf ) . filter ( m => legal . has ( m ) )
35+ if ( ! moves . length ) return ' No legal moves available.'
2836
29- const seval : Record < string , number > = { } ;
30- moves . forEach ( m => seval [ m ] = ( whiteToMove ? 1 : - 1 ) * sf [ m ] ) ;
37+ const seval : Record < string , number > = { }
38+ moves . forEach ( m => {
39+ seval [ m ] = ( whiteToMove ? 1 : - 1 ) * sf [ m ]
40+ } )
3141
32- const opt = moves . reduce ( ( a , b ) => seval [ a ] > seval [ b ] ? a : b ) ;
33- const { w : wOpt , d : dOpt } = wdl ( seval [ opt ] ) ;
42+ const opt = moves . reduce ( ( a , b ) => ( seval [ a ] > seval [ b ] ? a : b ) )
43+ const { w : wOpt , d : dOpt } = wdl ( seval [ opt ] )
3444
3545 const good = moves . filter ( m => {
36- const { w, d } = wdl ( seval [ m ] ) ;
37- return Math . abs ( w - wOpt ) <= eps && Math . abs ( d - dOpt ) <= eps ;
38- } ) ;
46+ const { w, d } = wdl ( seval [ m ] )
47+ return Math . abs ( w - wOpt ) <= eps && Math . abs ( d - dOpt ) <= eps
48+ } )
3949
40- const nGood = good . length ;
50+ const nGood = good . length
4151 const abundance =
42- nGood === 1 ? "only one move"
43- : nGood === 2 ? "two moves"
44- : "several moves" ;
52+ nGood === 1 ? 'only one move' : nGood === 2 ? 'two moves' : 'several moves'
4553
46- const avgGood = good . reduce ( ( s , m ) => s + seval [ m ] , 0 ) / nGood ;
54+ const avgGood = good . reduce ( ( s , m ) => s + seval [ m ] , 0 ) / nGood
4755
48- let outcome : string ;
49- if ( avgGood > 2.5 ) outcome = " to cleanly win" ;
50- else if ( avgGood > 1.0 ) outcome = " to win" ;
51- else if ( avgGood > 0.35 ) outcome = " for an advantage" ;
52- else if ( avgGood >= - 0.35 ) outcome = " to keep the balance" ;
53- else if ( avgGood >= - 1.0 ) outcome = " to hold the position" ;
54- else outcome = " to stay in the game" ;
56+ let outcome : string
57+ if ( avgGood > 2.5 ) outcome = ' to cleanly win'
58+ else if ( avgGood > 1.0 ) outcome = ' to win'
59+ else if ( avgGood > 0.35 ) outcome = ' for an advantage'
60+ else if ( avgGood >= - 0.35 ) outcome = ' to keep the balance'
61+ else if ( avgGood >= - 1.0 ) outcome = ' to hold the position'
62+ else outcome = ' to stay in the game'
5563
56- let setLevels = 0 , optLevels = 0 , temptLevels = 0 ;
64+ let setLevels = 0
65+ let optLevels = 0
66+ let temptLevels = 0
5767
5868 for ( let lvl = 0 ; lvl < 9 ; lvl ++ ) {
5969 const probs = moves
6070 . map ( m => [ maia [ m ] ?. [ lvl ] ?? 0 , m ] as [ number , string ] )
61- . sort ( ( a , b ) => b [ 0 ] - a [ 0 ] ) ;
71+ . sort ( ( a , b ) => b [ 0 ] - a [ 0 ] )
6272
63- const [ p1 , m1 ] = probs [ 0 ] ;
64- const [ p2 , m2 ] = probs [ 1 ] ?? [ 0 , "" ] ;
65- const [ p3 , m3 ] = probs [ 2 ] ?? [ 0 , "" ] ;
73+ const [ p1 , m1 ] = probs [ 0 ]
74+ const [ p2 , m2 ] = probs [ 1 ] ?? [ 0 , '' ]
75+ const [ p3 , m3 ] = probs [ 2 ] ?? [ 0 , '' ]
6676
67- const inGood = good . includes ( m1 ) ;
68- if ( inGood ) setLevels ++ ;
69- if ( m1 === opt ) optLevels ++ ;
77+ const inGood = good . includes ( m1 )
78+ if ( inGood ) setLevels ++
79+ if ( m1 === opt ) optLevels ++
7080
71- const nearTop = ( prob : number ) => ( p1 - prob ) <= eps ;
81+ const nearTop = ( prob : number ) => p1 - prob <= eps
7282 const tempting =
73- inGood && (
74- ( m2 && ! good . includes ( m2 ) && nearTop ( p2 ) ) ||
75- ( m3 && ! good . includes ( m3 ) && nearTop ( p3 ) )
76- ) ;
83+ inGood &&
84+ ( ( m2 && ! good . includes ( m2 ) && nearTop ( p2 ) ) ||
85+ ( m3 && ! good . includes ( m3 ) && nearTop ( p3 ) ) )
7786
78- if ( tempting ) temptLevels ++ ;
87+ if ( tempting ) temptLevels ++
7988 }
8089
81- const tier = ( k : number ) => k <= 2 ? 0 : k <= 6 ? 1 : 2 ;
82- const setTier = tier ( setLevels ) ;
83- const optTier = tier ( optLevels ) ;
90+ const tier = ( k : number ) => ( k <= 2 ? 0 : k <= 6 ? 1 : 2 )
91+ const setTier = tier ( setLevels )
92+ const optTier = tier ( optLevels )
8493
8594 const phrSet =
86- setTier === 0 ? "hard for players to find"
87- : setTier === 1 ? "findable for skilled players"
88- : "straightforward for players across skill levels to find" ;
95+ setTier === 0
96+ ? 'hard for players to find'
97+ : setTier === 1
98+ ? 'findable for skilled players'
99+ : 'straightforward for players across skill levels to find'
89100
90101 let phrBest =
91- optTier === 0 ? "hard for players to find"
92- : optTier === 1 ? "findable for skilled players"
93- : "straightforward for players across skill levels to find" ;
102+ optTier === 0
103+ ? 'hard for players to find'
104+ : optTier === 1
105+ ? 'findable for skilled players'
106+ : 'straightforward for players across skill levels to find'
94107
95- if ( optTier === 1 ) phrBest = " only " + phrBest ;
108+ if ( optTier === 1 ) phrBest = ' only ' + phrBest
96109
97- const verb = nGood === 1 ? "is" : " are" ;
98- const pron = nGood === 1 ? " it is" : " they are" ;
110+ const verb = nGood === 1 ? 'is' : ' are'
111+ const pron = nGood === 1 ? ' it is' : ' they are'
99112
100- const hasTempting = setLevels > 0 && temptLevels > setLevels / 2 ;
101- const temptText = hasTempting ? " There are also tempting alternatives." : "" ;
113+ const hasTempting = setLevels > 0 && temptLevels > setLevels / 2
114+ const temptText = hasTempting ? ' There are also tempting alternatives.' : ''
102115
103116 if ( nGood === 1 ) {
104- return `There ${ verb } ${ abundance } ${ outcome } , and ${ pron } ${ phrSet } .${ temptText } ` ;
117+ return `There ${ verb } ${ abundance } ${ outcome } , and ${ pron } ${ phrSet } .${ temptText } `
105118 }
106119
107120 if ( optTier < setTier ) {
108- return `There ${ verb } ${ abundance } ${ outcome } , and ${ pron } ${ phrSet } , but the best move is ${ phrBest } .${ temptText } ` ;
121+ return `There ${ verb } ${ abundance } ${ outcome } , and ${ pron } ${ phrSet } , but the best move is ${ phrBest } .${ temptText } `
109122 }
110123
111- return `There ${ verb } ${ abundance } ${ outcome } , and ${ pron } ${ phrSet } .${ temptText } ` ;
124+ return `There ${ verb } ${ abundance } ${ outcome } , and ${ pron } ${ phrSet } .${ temptText } `
112125}
0 commit comments