Spaces:
Running on Zero
Running on Zero
Download laya_chess/encoding.py from datafreak/laya-chess: direct link, hf CLI and curl.
- Browser
- Download file 3.32 kB
-
https://huggingface.co/spaces/datafreak/laya-chess/resolve/main/laya_chess/encoding.py
- Command line
-
hf download hf://spaces/datafreak/laya-chess/laya_chess/encoding.py
-
curl -L -o encoding.py https://huggingface.co/spaces/datafreak/laya-chess/resolve/main/laya_chess/encoding.py
3.32 kB
| """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 | |