"""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])