Refactoring lab

This commit is contained in:
Chris Proctor
2026-06-26 13:27:11 -04:00
parent e752bb848b
commit a9385f8296
32 changed files with 644 additions and 686 deletions

161
games/frogger/__init__.py Normal file
View File

@@ -0,0 +1,161 @@
"""Frogger: guide a frog across busy traffic lanes to reach the far side.
The frog starts at the bottom row. Cars move across the lanes between the
start and goal. Each lane has cars moving at different speeds in alternating
directions. The frog earns +10 for each row advanced, +50 for reaching the
top row, and -10 for being hit by a car or falling off the edge. Episodes end
when the frog reaches the top, gets hit, or energy runs out.
"""
from random import randint
from retro.game import Game
BOARD_WIDTH = 20
BOARD_HEIGHT = 12
NUM_LANES = BOARD_HEIGHT - 2
START_ENERGY = 200
class Frog:
name = "Frog"
character = 'O'
color = "green_on_black"
position = (0, 0)
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
def __init__(self):
self._direction = self.UP
def handle_keystroke(self, keystroke, game):
if keystroke.name == "KEY_UP":
self._direction = self.UP
elif keystroke.name == "KEY_DOWN":
self._direction = self.DOWN
elif keystroke.name == "KEY_LEFT":
self._direction = self.LEFT
elif keystroke.name == "KEY_RIGHT":
self._direction = self.RIGHT
def play_turn(self, game):
bw, bh = game.board_size
x, y = self.position
dx, dy = self._direction
nx, ny = x + dx, y + dy
if not (0 <= nx < bw):
game.state['reward'] -= 10
game.state['energy'] -= 50
self._reset(game)
return
if not (0 <= ny < bh):
if ny < 0:
game.state['score'] += 50
game.state['reward'] += 50
else:
game.state['reward'] -= 5
self._reset(game)
return
prev_y = y
self.position = (nx, ny)
game.state['energy'] -= 1
game.state['reward'] -= 0.01
if ny < prev_y:
advancement = prev_y - ny
game.state['score'] += advancement * 10
game.state['reward'] += advancement * 5
for agent in game.agents:
if hasattr(agent, '_is_car') and agent.position == self.position:
game.state['reward'] -= 10
game.state['energy'] -= 50
self._reset(game)
return
bw, bh = game.board_size
fx, fy = self.position
game.state['frog_x'] = fx / bw
game.state['frog_y'] = fy / bh
if game.state['energy'] <= 0:
game.end()
def _reset(self, game):
bw, bh = game.board_size
self.position = (bw // 2, bh - 1)
self._direction = self.UP
if game.state['energy'] <= 0:
game.end()
class Car:
_is_car = True
character = 'X'
color = "red_on_black"
def __init__(self, lane, speed, direction, start_x, board_width):
self.name = f"Car {lane}_{start_x}"
self._lane = lane
self._speed = speed
self._direction = direction
self._board_width = board_width
self._step = 0
self.position = (start_x, lane)
def play_turn(self, game):
self._step += 1
if self._step < self._speed:
return
self._step = 0
x, y = self.position
x = (x + self._direction) % self._board_width
self.position = (x, y)
frog = game.get_agent_by_name("Frog")
if frog.position == self.position:
frog._reset(game)
game.state['reward'] -= 10
game.state['energy'] -= 50
def create_game():
bw, bh = BOARD_WIDTH, BOARD_HEIGHT
frog = Frog()
frog.position = (bw // 2, bh - 1)
agents = [frog]
car_id = 0
for lane_idx, row in enumerate(range(1, bh - 1)):
direction = 1 if lane_idx % 2 == 0 else -1
speed = 2 + (lane_idx % 3)
num_cars = 2 + (lane_idx % 3)
spacing = bw // num_cars
for i in range(num_cars):
start_x = (i * spacing + lane_idx * 3) % bw
agents.append(Car(row, speed, direction, start_x, bw))
car_id += 1
game = Game(
agents,
{
'score': 0,
'reward': 0.0,
'energy': START_ENERGY,
'frog_x': (bw // 2) / bw,
'frog_y': (bh - 1) / bh,
},
board_size=(bw, bh),
framerate=8,
show_state=['score', 'energy'],
)
return game
if __name__ == '__main__':
create_game().play()

View File

@@ -0,0 +1,3 @@
from games.frogger import create_game
create_game().play()

View File

@@ -0,0 +1,11 @@
[project]
name = "frogger"
version = "0.1.0"
description = "Frogger: guide a frog across traffic lanes to train an RL agent"
requires-python = ">=3.11"
dependencies = ["retro-games>=2.5.0"]
[tool.retro-gamer]
actions = ["KEY_UP", "KEY_DOWN", "KEY_LEFT", "KEY_RIGHT"]
reward = "reward"
character_set = ["O", "X"]