policy.mjs
javascript · 71 lines · Exact bytes
Version & provenance · browser-local-cm7-v1
Recorded SHA-256 ac175f49a08b01b2c2c74415db78a975dfe67da38856b97e26b83fce14625660Rendered SHA-256 ac175f49a08b01b2c2c74415db78a975dfe67da38856b97e26b83fce14625660
Apache-2.0 License notes. Line links are stable within this snapshot. CM-7 schema-2 state-input browser integration; FP32 selected update2232 checkpoint 29e68a8e85336cbb1f56efdad36bd76217728b6a376d1e5f74e89887d21383da. ONNX Runtime Web 1.24.3, chess.js 1.4.0. Exact integrated source; standard-game UI, fixed masked120-slot prompt,512 raw output classes. Three-ply browser helper is separate from live seven-ply search. Original UI and terminal PGN export preserved; prototype scorer/codec/worker/runner unchanged except adding PGN export utility.
1import {prompt} from './codec.mjs';
2export const uci = m => m.from + m.to + (m.promotion || '');
3export const square = s => s.charCodeAt(0)-97 + 8*(Number(s[1])-1);
4export const triple = m => [square(m.from), square(m.to), ({n:65,b:66,r:67,q:68})[m.promotion] ?? 64];
5export function normalize(values) {
6 if(values.length!==512 || !Array.from(values).every(Number.isFinite)) throw Error('Expected 512 finite logits');
7 const max=Math.max(...values), z=Math.log(Array.from(values).reduce((s,v)=>s+Math.exp(v-max),0));
8 return Array.from(values,v=>v-max-z);
9}
10export const legalMoves = b => b.moves({verbose:true}).sort((a,b)=>uci(a)<uci(b)?-1:uci(a)>uci(b)?1:0);
11// Match python-chess immediate claims, including a draw available by declaring a move.
12export function outcome(b) {
13 if(b.isCheckmate()) return 'checkmate';
14 if(b.isStalemate() || b.isInsufficientMaterial() || b.isThreefoldRepetition() || b.isDrawByFiftyMoves()) return 'draw';
15 for(const m of b.moves({verbose:true})) {
16 b.move(m); const draw=b.isThreefoldRepetition() || (b.isDrawByFiftyMoves() && !b.isCheckmate()); b.undo();
17 if(draw) return 'draw';
18 }
19 return null;
20}
21export function forcedMate(b, {maxNodes=50000, seconds=1}={}) {
22 const attacker=b.turn(), start=performance.now(); let nodes=0;
23 const guard=()=>{if(performance.now()-start>=seconds*1000)throw Error('budget');};
24 function visit(depth) {
25 guard();if(nodes>=maxNodes) throw Error('budget');nodes++;
26 const end=outcome(b);
27 guard();if(end) return end==='checkmate' && b.turn()!==attacker?{}:null;
28 if(!depth) return null;
29 const attack=b.turn()===attacker;
30 const moves=legalMoves(b);guard();
31 for(const m of moves) {
32 guard();
33 b.move(m); let ok; try {ok=visit(depth-1);} finally {b.undo();}
34 guard();if(attack && ok) return {move:uci(m)};
35 if(!attack && !ok) return null;
36 }
37 return !attack&&moves.length?{}:null;
38 }
39 try {
40 for(let depth=1;depth<=3;depth++) {
41 const proof=visit(depth);guard();
42 if(proof) return {status:'proven',move:proof.move,nodes};
43 }
44 return {status:'no_forced_mate',nodes};
45 } catch(e) {if(e.message!=='budget') throw e; return {status:'unknown',nodes};}
46}
47export async function score(board, runner, historyPlies=16) {
48 const hist=prompt(board,historyPlies);
49 const legal=legalMoves(board); if(!legal.length) throw Error('Terminal board');
50 const base=await runner.prefill(hist); const lp=new Map(); let calls=1;
51 const argmax = row => row.indexOf(Math.max(...row));
52 async function tails(rows) {
53 const unique=[...new Map(rows.map(r=>[r.join(','),r])).values()].sort((a,b)=>a[0]-b[0] || (a[1]??0)-(b[1]??0));
54 for(let i=0;i<unique.length;i+=8) {
55 const part=unique.slice(i,i+8), logits=await runner.branch(part,base);
56 part.forEach((r,j)=>lp.set(r.join(','),normalize(logits[j]))); calls++;
57 }
58 }
59 try {
60 lp.set('',normalize(base.logits));
61 const triples=legal.map(triple), raw=[argmax(lp.get(''))];
62 await tails([...triples.map(t=>t.slice(0,1)),raw.slice()]); raw.push(argmax(lp.get(raw.join(','))));
63 await tails([...triples.map(t=>t.slice(0,2)),raw.slice()]); raw.push(argmax(lp.get(raw.join(','))));
64 const scores=triples.map(([a,b,c])=>lp.get('')[a]+lp.get(`${a}`)[b]+lp.get(`${a},${b}`)[c]);
65 const best=argmax(scores);
66 return {move:uci(legal[best]),initialLogits:base.logits,scores,legal:legal.map(uci),raw,forwardCalls:calls};
67 } finally {base.dispose();}
68}
69
70export function exportPgn(board){const end=outcome(board);board.header('Result',end==='checkmate'?(board.turn()==='w'?'0-1':'1-0'):end?'1/2-1/2':'*');return board.pgn();}
71