"""Pick a move with the released network. One forward pass, no search. python predict.py # from the initial position python predict.py "FEN ..." # from any FEN The three steps that matter, and the order they go in: 1. encode the board in the **canonical frame** -- the side to move is always "white, playing up the board", so a position and its colour-reversed twin share one representation; 2. read the policy logits **only at the legal move indices**, because the head emits all 4096 from/to pairs and roughly 99% of them are illegal here; 3. softmax over that short list, not over the 4096. Skipping step 2 is the usual way to get nonsense out of this model. """ import sys import chess import numpy as np import torch from encoding import board_to_planes, legal_move_indices from network import ChessNetwork CHECKPOINT = "RDTChess.pt" @torch.no_grad() def evaluate(model: ChessNetwork, board: chess.Board, device: str = "cpu"): """Return the legal moves, their probabilities, and the value of `board`. `value` is in [-1, 1] and always answers "how good is this for the side to move", which is what the canonical frame buys. """ moves, indices = legal_move_indices(board) if not moves: return [], np.empty(0), 0.0 planes = torch.from_numpy(board_to_planes(board)).unsqueeze(0).to(device) logits, value = model(planes) legal = logits[0, torch.as_tensor(indices.astype(np.int64), device=device)] probabilities = torch.softmax(legal.float(), dim=0).cpu().numpy() return moves, probabilities, float(value.item()) def main() -> int: board = chess.Board(sys.argv[1]) if len(sys.argv) > 1 else chess.Board() device = "cuda" if torch.cuda.is_available() else "cpu" model = ChessNetwork.from_checkpoint(CHECKPOINT, device=device) moves, probabilities, value = evaluate(model, board, device) if not moves: print("no legal move -- the game is over") return 0 order = np.argsort(-probabilities) print(board) print(f"\n{'white' if board.turn else 'black'} to move") print(f"value {value:+.3f} (positive favours the side to move)\n") print("top 5 moves") for rank in order[:5]: print(f" {board.san(moves[rank]):8s} {probabilities[rank] * 100:5.1f} %") print(f"\nplayed: {board.san(moves[order[0]])}") return 0 if __name__ == "__main__": raise SystemExit(main())