162 lines
4.4 KiB
Python
162 lines
4.4 KiB
Python
"""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()
|