SnakeGame
The SnakeGame class implements the core Snake game logic with an event-driven architecture. Each game step produces a stream of events that capture the causal structure of gameplay.
Constructor
SnakeGame(width=10, height=10, seed=None)
Create a new Snake game instance.
Random seed for reproducible food spawning and initial direction
Methods
reset()
def reset(self) -> SnakeState
Initialize a new game. The snake starts at the center facing a random direction with length 1.
Initial game state with:
head: Center position (width//2, height//2)
body: List containing only the head position
direction: Random Action (UP/DOWN/LEFT/RIGHT)
food: Random unoccupied position
score: 0
alive: True
tick: 0
step()
def step(self, action: Action) -> tuple[SnakeState, list[Event], bool]
Execute one game tick with the given action.
Direction to move (Action.UP, Action.DOWN, Action.LEFT, or Action.RIGHT)
return
tuple[SnakeState, list[Event], bool]
Returns a 3-tuple:
- SnakeState: New game state after the step
- list[Event]: Ordered list of events produced this tick
- bool: True if game ended (death), False otherwise
Attempting to move opposite to current direction is ignored - the snake continues in its current direction.
legal_actions()
def legal_actions(self, state: SnakeState) -> list[Action]
Get all legal actions for a given state. Returns all directions except the opposite of the current direction.
List of 3 legal actions (all except the reverse direction)
Event Types
Each step() call produces an ordered sequence of events with the following types:
Player pressed UP. Payload: {"action": "UP"}
Player pressed DOWN. Payload: {"action": "DOWN"}
Player pressed LEFT. Payload: {"action": "LEFT"}
Player pressed RIGHT. Payload: {"action": "RIGHT"}
Movement Events
Snake moved to a new position. Payload: {"pos": (x, y)}
Collision Events
Snake hit a wall and died. Payload: {"pos": (x, y)}
Snake hit its own body and died. Payload: {"pos": (x, y)}
Rule Effect Events
Snake ate food. Payload: {"pos": (x, y)}
Snake grew longer. Payload: {"length": int}
New food appeared. Payload: {"pos": (x, y)}
Score increased. Payload: {"score": int}
Event Structure
All events are instances of the Event dataclass:
@dataclass(frozen=True)
class Event:
type: str # Event type (e.g., "MOVE", "EAT")
entity: str # Entity that triggered it ("player" or "food")
payload: dict # Event-specific data
tick: int # Game tick when event occurred
salience: Salience # Priority level for ordering
Salience Levels
class Salience(IntEnum):
TICK = 0
MOVEMENT = 1 # INPUT_*, MOVE
COLLISION = 2 # DIE_WALL, DIE_SELF
RULE_EFFECT = 3 # EAT, GROW, FOOD_SPAWN, SCORE
PHASE = 4
SnakeState
The game state is represented by a dataclass:
@dataclass
class SnakeState:
head: tuple[int, int] # Current head position
body: list[tuple[int, int]] # All body segments (includes head)
direction: Action # Current facing direction
food: tuple[int, int] # Food position
score: int # Current score
alive: bool # Whether snake is alive
tick: int # Current game tick
Usage Example
from game_grammar import SnakeGame, Action
# Create a 10x10 game with seed for reproducibility
game = SnakeGame(width=10, height=10, seed=42)
# Initialize game
state = game.reset()
print(f"Starting at {state.head}, facing {state.direction.value}")
print(f"Food at {state.food}")
# Play several steps
for step_num in range(5):
# Get legal actions
legal = game.legal_actions(state)
print(f"\nStep {step_num + 1}: Legal actions: {[a.value for a in legal]}")
# Take an action
action = Action.RIGHT
state, events, done = game.step(action)
# Print events
for event in events:
print(f" {event.type}: {event.payload}")
if done:
print(f"Game over! Final score: {state.score}")
break
else:
print(f" Snake at {state.head}, score: {state.score}")
Example Output:
Starting at (5, 5), facing UP
Food at (3, 7)
Step 1: Legal actions: ['UP', 'LEFT', 'RIGHT']
INPUT_R: {'action': 'RIGHT'}
MOVE: {'pos': (6, 5)}
Snake at (6, 5), score: 0
Step 2: Legal actions: ['UP', 'DOWN', 'RIGHT']
INPUT_R: {'action': 'RIGHT'}
MOVE: {'pos': (7, 5)}
Snake at (7, 5), score: 0
...
Event Flow Example
When the snake eats food, a typical event sequence is:
[
Event(type="INPUT_R", entity="player", payload={"action": "RIGHT"}, tick=10, salience=1),
Event(type="EAT", entity="player", payload={"pos": (5, 3)}, tick=10, salience=3),
Event(type="MOVE", entity="player", payload={"pos": (5, 3)}, tick=10, salience=1),
Event(type="GROW", entity="player", payload={"length": 4}, tick=10, salience=3),
Event(type="FOOD_SPAWN", entity="food", payload={"pos": (8, 2)}, tick=10, salience=3),
Event(type="SCORE", entity="player", payload={"score": 3}, tick=10, salience=3),
]