Initial commit
This commit is contained in:
0
retro_gamer/examples/__init__.py
Normal file
0
retro_gamer/examples/__init__.py
Normal file
17
retro_gamer/examples/beast/__init__.py
Normal file
17
retro_gamer/examples/beast/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from retro.game import Game
|
||||
from retro_gamer.examples.beast.board import Board
|
||||
|
||||
WIDTH = 40
|
||||
HEIGHT = 20
|
||||
NUM_BEASTS = 10
|
||||
|
||||
def create_game():
|
||||
"""Return a fresh, initialized Beast game."""
|
||||
board = Board(WIDTH, HEIGHT, num_beasts=NUM_BEASTS)
|
||||
state = {'beasts_killed': 0}
|
||||
game = Game(board.get_agents(), state, board_size=(WIDTH, HEIGHT))
|
||||
game.num_beasts = NUM_BEASTS
|
||||
return game
|
||||
|
||||
if __name__ == '__main__':
|
||||
create_game().play()
|
||||
0
retro_gamer/examples/beast/agents/__init__.py
Normal file
0
retro_gamer/examples/beast/agents/__init__.py
Normal file
67
retro_gamer/examples/beast/agents/beast.py
Normal file
67
retro_gamer/examples/beast/agents/beast.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from retro_gamer.examples.beast.helpers import add, distance, get_occupant
|
||||
from random import random, choice
|
||||
|
||||
class Beast:
|
||||
"""A beast that hunts the player."""
|
||||
character = "H"
|
||||
color = "red"
|
||||
probability_of_moving = 0.03
|
||||
probability_of_random_move = 0.2
|
||||
deadly = True
|
||||
|
||||
def __init__(self, position):
|
||||
self.position = position
|
||||
|
||||
def handle_push(self, vector, game):
|
||||
future_position = add(self.position, vector)
|
||||
on_board = game.on_board(future_position)
|
||||
obstacle = get_occupant(game, future_position)
|
||||
if obstacle or not on_board:
|
||||
self.die(game)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def play_turn(self, game):
|
||||
if self.should_move():
|
||||
possible_moves = []
|
||||
for position in self.get_adjacent_positions():
|
||||
if game.is_empty(position) and game.on_board(position):
|
||||
possible_moves.append(position)
|
||||
if possible_moves:
|
||||
if self.should_move_randomly():
|
||||
self.position = choice(possible_moves)
|
||||
else:
|
||||
self.position = self.choose_best_move(possible_moves, game)
|
||||
player = game.get_agent_by_name("player")
|
||||
if player and player.position == self.position:
|
||||
player.die(game)
|
||||
|
||||
def get_adjacent_positions(self):
|
||||
"""Returns all eight adjacent positions, including diagonals."""
|
||||
positions = []
|
||||
for i in [-1, 0, 1]:
|
||||
for j in [-1, 0, 1]:
|
||||
if i or j:
|
||||
positions.append(add(self.position, (i, j)))
|
||||
return positions
|
||||
|
||||
def should_move(self):
|
||||
return random() < self.probability_of_moving
|
||||
|
||||
def should_move_randomly(self):
|
||||
return random() < self.probability_of_random_move
|
||||
|
||||
def choose_best_move(self, possible_moves, game):
|
||||
player = game.get_agent_by_name("player")
|
||||
move_distances = [[distance(player.position, move), move] for move in possible_moves]
|
||||
shortest_distance, best_move = sorted(move_distances)[0]
|
||||
return best_move
|
||||
|
||||
def die(self, game):
|
||||
game.remove_agent(self)
|
||||
game.num_beasts -= 1
|
||||
game.state['beasts_killed'] += 1
|
||||
if game.num_beasts == 0:
|
||||
game.state["message"] = "You win!"
|
||||
game.end()
|
||||
25
retro_gamer/examples/beast/agents/block.py
Normal file
25
retro_gamer/examples/beast/agents/block.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from retro_gamer.examples.beast.helpers import add, get_occupant
|
||||
|
||||
class Block:
|
||||
"""A static block that can be pushed by the player."""
|
||||
character = "█"
|
||||
color = "green4"
|
||||
deadly = False
|
||||
|
||||
def __init__(self, position):
|
||||
self.position = position
|
||||
|
||||
def handle_push(self, vector, game):
|
||||
"""Responds to a push in the direction of vector.
|
||||
Returns True when the push succeeds in creating empty space.
|
||||
"""
|
||||
future_position = add(self.position, vector)
|
||||
on_board = game.on_board(future_position)
|
||||
obstacle = get_occupant(game, future_position)
|
||||
if obstacle:
|
||||
success = obstacle.handle_push(vector, game)
|
||||
else:
|
||||
success = on_board
|
||||
if success:
|
||||
self.position = future_position
|
||||
return success
|
||||
39
retro_gamer/examples/beast/agents/player.py
Normal file
39
retro_gamer/examples/beast/agents/player.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from retro_gamer.examples.beast.helpers import add, get_occupant
|
||||
|
||||
direction_vectors = {
|
||||
"KEY_RIGHT": (1, 0),
|
||||
"KEY_UP": (0, -1),
|
||||
"KEY_LEFT": (-1, 0),
|
||||
"KEY_DOWN": (0, 1),
|
||||
}
|
||||
|
||||
class Player:
|
||||
character = "*"
|
||||
color = "white"
|
||||
name = "player"
|
||||
deadly = False
|
||||
|
||||
def __init__(self, position):
|
||||
self.position = position
|
||||
|
||||
def handle_keystroke(self, keystroke, game):
|
||||
if keystroke.name in direction_vectors:
|
||||
vector = direction_vectors[keystroke.name]
|
||||
self.try_to_move(vector, game)
|
||||
|
||||
def try_to_move(self, vector, game):
|
||||
future_position = add(self.position, vector)
|
||||
on_board = game.on_board(future_position)
|
||||
obstacle = get_occupant(game, future_position)
|
||||
if obstacle:
|
||||
if obstacle.deadly:
|
||||
self.die(game)
|
||||
elif obstacle.handle_push(vector, game):
|
||||
self.position = future_position
|
||||
elif on_board:
|
||||
self.position = future_position
|
||||
|
||||
def die(self, game):
|
||||
self.color = "black_on_red"
|
||||
game.state["message"] = "The beasties win!"
|
||||
game.end()
|
||||
44
retro_gamer/examples/beast/board.py
Normal file
44
retro_gamer/examples/beast/board.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from random import shuffle
|
||||
from retro_gamer.examples.beast.agents.player import Player
|
||||
from retro_gamer.examples.beast.agents.beast import Beast
|
||||
from retro_gamer.examples.beast.agents.block import Block
|
||||
|
||||
class Board:
|
||||
"""Creates the agents needed at the beginning of the game and assigns their positions."""
|
||||
|
||||
def __init__(self, width, height, block_density=0.3, num_beasts=10):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.block_density = block_density
|
||||
self.num_blocks = round(width * height * block_density)
|
||||
self.num_empty_spaces = width * height - self.num_blocks
|
||||
self.num_beasts = num_beasts
|
||||
self.validate()
|
||||
|
||||
def validate(self):
|
||||
if self.block_density < 0 or self.block_density > 1:
|
||||
raise ValueError("block density must be between 0 and 1.")
|
||||
if self.num_empty_spaces < self.num_beasts + 1:
|
||||
raise ValueError("Not enough space on the board.")
|
||||
|
||||
def get_agents(self):
|
||||
"""Returns a list of agents initialized in their starting positions."""
|
||||
positions = self.get_all_positions()
|
||||
shuffle(positions)
|
||||
|
||||
player_position = positions[0]
|
||||
beast_positions = positions[1:self.num_beasts + 1]
|
||||
block_positions = positions[-self.num_blocks:]
|
||||
|
||||
player = [Player(player_position)]
|
||||
beasts = [Beast(pos) for pos in beast_positions]
|
||||
blocks = [Block(pos) for pos in block_positions]
|
||||
return player + beasts + blocks
|
||||
|
||||
def get_all_positions(self):
|
||||
"""Returns a list of all positions on the board."""
|
||||
positions = []
|
||||
for i in range(self.width):
|
||||
for j in range(self.height):
|
||||
positions.append((i, j))
|
||||
return positions
|
||||
18
retro_gamer/examples/beast/helpers.py
Normal file
18
retro_gamer/examples/beast/helpers.py
Normal file
@@ -0,0 +1,18 @@
|
||||
def add(vec0, vec1):
|
||||
"""Adds two vectors."""
|
||||
x0, y0 = vec0
|
||||
x1, y1 = vec1
|
||||
return (x0 + x1, y0 + y1)
|
||||
|
||||
def get_occupant(game, position):
|
||||
"""Returns the agent at position, if there is one."""
|
||||
positions_with_agents = game.get_agents_by_position()
|
||||
if position in positions_with_agents:
|
||||
agents_at_position = positions_with_agents[position]
|
||||
return agents_at_position[0]
|
||||
|
||||
def distance(vec0, vec1):
|
||||
"""Returns the Manhattan distance between two positions."""
|
||||
x0, y0 = vec0
|
||||
x1, y1 = vec1
|
||||
return abs(x1 - x0) + abs(y1 - y0)
|
||||
6
retro_gamer/examples/beast/pyproject.toml
Normal file
6
retro_gamer/examples/beast/pyproject.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[tool.retro-gamer]
|
||||
actions = ["KEY_RIGHT", "KEY_UP", "KEY_LEFT", "KEY_DOWN"]
|
||||
reward = "beasts_killed"
|
||||
character_set = ["*", "H", "█"]
|
||||
spatial = true
|
||||
observe_state = []
|
||||
Reference in New Issue
Block a user