Files
lab_reinforcement_learning/snake_observation.py
Chris Proctor edbf76071e Migrate snake example to observation_function and refresh its training run
The egocentric/egocentric_player/egocentric_radius flags were removed
from retro_gamer in favor of an explicit observation_function.
snake_observation.py reproduces the old egocentric+board+extras
behavior by calling egocentric_board/encode_board/encode_state
directly, and runs/snake/config.toml now points at it.

That config change made the prior 12,000-episode checkpoint history
incompatible (retro_gamer's checkpoint compatibility checker can't
verify a new observation_function is behaviorally equivalent to the
old flags, so it conservatively refuses to resume), so the old
checkpoints were deleted and a fresh 20,000-episode run was recorded.
Track only the four checkpoints the lab actually references
(ep_1300, ep_2300, ep_4000, ep_20000) instead of all 200, and update
.gitignore so future student runs of runs/snake aren't committed by
default. snake_training.md's Q5 training-curve table and Q6 checkpoint
episodes are updated to match the real numbers from this run.
2026-06-24 07:50:41 -04:00

32 lines
1.2 KiB
Python

"""A custom observation_function for retro.examples.snake.
Crops the board to a window centered on the snake's head before encoding,
using egocentric_board()/encode_board() from retro_gamer.observation as
building blocks. This replaces retro_gamer's old built-in egocentric flags —
cropping is now just something an observation_function does for itself.
Point a run's config.toml at this with:
[metadata]
observation_function = "snake_observation:egocentric_observation"
board_size = [17, 17] # must match RADIUS below: 2*8+1 = 17
"""
import numpy as np
from retro.views.headless import HeadlessView
from retro_gamer.observation import egocentric_board, encode_board, encode_state
CHARACTER_SET = ["@", "*", ">", "<", "^", "v"]
RADIUS = 8
def egocentric_observation(game):
"""Board cropped to a (2*RADIUS+1)^2 window around the snake's head, plus apple_dx/apple_dy."""
view = HeadlessView()
view.on_game_start(game)
view.render(game)
head = game.get_agent_by_name("Snake head")
cropped = egocentric_board(view.board_characters, head.position, RADIUS)
board_vec = encode_board(cropped, CHARACTER_SET).flatten()
extras = encode_state(game.state, ["apple_dx", "apple_dy"])
return np.concatenate([board_vec, extras])