Spaces:
Running on Zero
Running on Zero
File size: 3,322 Bytes
a162612 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | """Position + move -> Laya `score` question. Must match the training notebook exactly."""
import chess
import numpy as np
NAMES = {chess.PAWN: "pawn", chess.KNIGHT: "knight", chess.BISHOP: "bishop",
chess.ROOK: "rook", chess.QUEEN: "queen", chess.KING: "king"}
ORDER = [chess.KING, chess.QUEEN, chess.ROOK, chess.BISHOP, chess.KNIGHT, chess.PAWN]
DEFAULT_N_LEVELS = 10
DEFAULT_INS_TMPL = "{side} plays {san} ({piece} {frm}-{to}{extra}). Win chance for {side}?"
def piece_list(board, color):
out = []
for pt in ORDER:
for sq in sorted(board.pieces(pt, color)):
out.append(chess.piece_symbol(pt).upper() + chess.square_name(sq))
return " ".join(out)
def board_state(board):
return {
"to_move": "white" if board.turn else "black",
"white": piece_list(board, chess.WHITE),
"black": piece_list(board, chess.BLACK),
"castling": board.castling_xfen() if board.castling_rights else "-",
"en_passant": chess.square_name(board.ep_square) if board.has_legal_en_passant() else "-",
}
class MoveEncoder:
"""Builds the token ids for "how good is `move` in `board`?" exactly as in training.
Level labels and the instruction template come from the checkpoint's chess_meta.json, so the
engine always asks the question the model was trained on.
"""
def __init__(self, tokenizer, laya_cfg, meta=None):
from laya.common import build_sequence
meta = meta or {}
if meta.get("state_format", "piece lists v2") != "piece lists v2":
raise ValueError(f"checkpoint uses state format {meta['state_format']!r}, engine supports 'piece lists v2'")
self.n_levels = int(meta.get("n_levels", DEFAULT_N_LEVELS))
edges = np.linspace(0, 1, self.n_levels + 1)
self.centers = ((edges[:-1] + edges[1:]) / 2).astype(np.float32)
self.crit = meta.get("crit") or [f"{edges[i]*100:.0f}-{edges[i+1]*100:.0f}%" for i in range(self.n_levels)]
self.ins_tmpl = meta.get("ins_template") or DEFAULT_INS_TMPL
self._build_sequence = build_sequence
self.tokenizer, self.max_len, self.head_max_len = tokenizer, laya_cfg["max_len"], laya_cfg["head_max_len"]
def question(self, board, move):
piece = board.piece_at(move.from_square)
extra = ""
if board.is_capture(move):
cap = board.piece_at(move.to_square)
extra += f", takes {NAMES[cap.piece_type] if cap else 'pawn'}"
if move.promotion:
extra += f", promotes to {NAMES[move.promotion]}"
if board.gives_check(move):
extra += ", check"
side = "white" if board.turn else "black"
ins = self.ins_tmpl.format(side=side, san=board.san(move), piece=NAMES[piece.piece_type],
frm=chess.square_name(move.from_square), to=chess.square_name(move.to_square),
extra=extra)
return {"t": "score", "ins": ins, "crit": self.crit}
def encode(self, board, move, state=None):
state = state if state is not None else board_state(board)
ids, markers = self._build_sequence(self.tokenizer, state, self.question(board, move),
self.max_len, self.head_max_len)[:2]
return ids, markers
|