#!/usr/bin/env python3 """Game-agnostic abstract action-logit API for Phase 4G learned adapters.""" from __future__ import annotations from dataclasses import dataclass import numpy as np @dataclass(frozen=True) class AbstractActionOutput: action_logits: np.ndarray def __post_init__(self) -> None: value = np.asarray(self.action_logits, dtype=np.float32) if value.shape != (4,) or not np.isfinite(value).all(): raise ValueError("action_logits must be finite float32[4]") object.__setattr__(self, "action_logits", value) def deterministic_argmax(self) -> int: return int(np.argmax(self.action_logits)) def sample(self, seed: int, temperature: float = 1.0, epsilon: float = 0.0) -> int: if temperature <= 0 or not 0 <= epsilon <= 1: raise ValueError("temperature must be positive and epsilon in [0,1]") rng = np.random.default_rng(seed) if rng.random() < epsilon: return int(rng.integers(0, 4)) z = self.action_logits.astype(np.float64) / temperature z -= z.max(); p = np.exp(z); p /= p.sum() return int(rng.choice(4, p=p)) ACTION_NAMES = ("ACTION_0", "ACTION_1", "ACTION_2", "ACTION_3")