V0 — Choose completely randomly
Every agent on this site is a policy: a function from the current game state and
the list of legal actions to a probability distribution over those actions. The engine
(written in Rust, exposed to Python as star_engine) enumerates what's legal;
the policy only chooses. V0 is the degenerate case — uniform probability over every legal
action, End Turn included. It exists to be beaten: any policy worth anything must outscore
a coin flip.
This is the entire policy:
lesson_7/policies/v0/__init__.py
"""V0 -- choose completely randomly (uniform over every legal action)."""
from __future__ import annotations
from random import Random
import star_engine as se
from ..base import Actions, Policy
from ..common import uniform
class V0(Policy):
def __init__(self) -> None:
super().__init__("V0 Choose completely randomly")
def get_action_probabilities(
self, game: se.Game, actions: Actions, rng: Random
) -> list[float]:
return uniform(actions)
The one helper it uses:
lesson_7/policies/common.py (excerpt)
def uniform(actions: Actions) -> list[float]:
"""Uniform over every legal action."""
p = 1.0 / len(actions)
return [p] * len(actions)
The interface it implements is shared by every policy V0–V7. Note the design decision: the single abstract primitive is the distribution, not the choice. A deterministic bot returns a one-hot vector; a random bot returns a spread; and the same distribution later doubles as an AlphaZero-style policy target for the learned agents.
lesson_7/policies/base.py (excerpt)
class Policy(ABC):
"""Base class for every policy. Subclasses implement
`get_action_probabilities`; `get_action` is derived."""
def __init__(self, name: str) -> None:
self.name = name
@abstractmethod
def get_action_probabilities(
self, game: se.Game, actions: Actions, rng: Random
) -> list[float]:
"""Probabilities over `actions`, aligned to the list and summing to 1."""
def get_action(
self, game: se.Game, actions: Actions, rng: Random
) -> se.LegalAction:
"""Sample one action from `get_action_probabilities`. Deterministic
whenever that distribution is one-hot."""
probs = self.get_action_probabilities(game, actions, rng)
return rng.choices(actions, weights=probs, k=1)[0]
def __repr__(self) -> str:
return f"{type(self).__name__}({self.name!r})"