Initial commit
This commit is contained in:
117
babysnake/__init__.py
Normal file
117
babysnake/__init__.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""BabySnake: a 4×4 grid game where an agent collects food.
|
||||
|
||||
State: (agent_x, agent_y, food_x, food_y) — four integers.
|
||||
The agent starts with 50 energy. Each step costs 1 energy.
|
||||
Collecting food restores 30 energy and adds 1 to the score.
|
||||
The game ends when energy reaches 0.
|
||||
"""
|
||||
|
||||
from random import randint, choice
|
||||
from retro.game import Game
|
||||
|
||||
BOARD_SIZE = 4
|
||||
START_ENERGY = 50
|
||||
FOOD_ENERGY = 30
|
||||
|
||||
class Forager:
|
||||
"""The player agent. Controlled with arrow keys."""
|
||||
name = "Forager"
|
||||
character = '@'
|
||||
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.RIGHT
|
||||
|
||||
def handle_keystroke(self, keystroke, game):
|
||||
if keystroke.name == "KEY_RIGHT":
|
||||
self._direction = self.RIGHT
|
||||
elif keystroke.name == "KEY_UP":
|
||||
self._direction = self.UP
|
||||
elif keystroke.name == "KEY_LEFT":
|
||||
self._direction = self.LEFT
|
||||
elif keystroke.name == "KEY_DOWN":
|
||||
self._direction = self.DOWN
|
||||
|
||||
def play_turn(self, game):
|
||||
x, y = self.position
|
||||
dx, dy = self._direction
|
||||
new_pos = (x + dx, y + dy)
|
||||
if game.on_board(new_pos):
|
||||
self.position = new_pos
|
||||
|
||||
game.state['energy'] -= 1
|
||||
game.state['reward'] -= 0.01
|
||||
|
||||
food = game.get_agent_by_name("Food")
|
||||
if self.position == food.position:
|
||||
food.relocate(game)
|
||||
game.state['energy'] += FOOD_ENERGY
|
||||
game.state['score'] += 1
|
||||
game.state['reward'] += 1.0
|
||||
|
||||
ax, ay = self.position
|
||||
fx, fy = game.get_agent_by_name("Food").position
|
||||
game.state['agent_x'] = ax
|
||||
game.state['agent_y'] = ay
|
||||
game.state['food_x'] = fx
|
||||
game.state['food_y'] = fy
|
||||
|
||||
if game.state['energy'] <= 0:
|
||||
game.end()
|
||||
|
||||
|
||||
class Food:
|
||||
"""The food item. Respawns at a random empty position when collected."""
|
||||
name = "Food"
|
||||
character = '*'
|
||||
color = "yellow_on_black"
|
||||
position = (0, 0)
|
||||
|
||||
def relocate(self, game):
|
||||
bw, bh = game.board_size
|
||||
forager = game.get_agent_by_name("Forager")
|
||||
while True:
|
||||
pos = (randint(0, bw - 1), randint(0, bh - 1))
|
||||
if pos != forager.position:
|
||||
self.position = pos
|
||||
return
|
||||
|
||||
|
||||
def create_game():
|
||||
"""Return a fresh BabySnake game."""
|
||||
forager = Forager()
|
||||
food = Food()
|
||||
bw = bh = BOARD_SIZE
|
||||
game = Game(
|
||||
[forager, food],
|
||||
{
|
||||
'score': 0,
|
||||
'reward': 0.0,
|
||||
'energy': START_ENERGY,
|
||||
'agent_x': 0,
|
||||
'agent_y': 0,
|
||||
'food_x': 0,
|
||||
'food_y': 0,
|
||||
},
|
||||
board_size=(bw, bh),
|
||||
framerate=6,
|
||||
)
|
||||
forager.position = (randint(0, bw - 1), randint(0, bh - 1))
|
||||
food.relocate(game)
|
||||
ax, ay = forager.position
|
||||
fx, fy = food.position
|
||||
game.state['agent_x'] = ax
|
||||
game.state['agent_y'] = ay
|
||||
game.state['food_x'] = fx
|
||||
game.state['food_y'] = fy
|
||||
return game
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
create_game().play()
|
||||
3
babysnake/__main__.py
Normal file
3
babysnake/__main__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from babysnake import create_game
|
||||
|
||||
create_game().play()
|
||||
4
babysnake/pyproject.toml
Normal file
4
babysnake/pyproject.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
[tool.retro-gamer]
|
||||
actions = ["KEY_RIGHT", "KEY_UP", "KEY_LEFT", "KEY_DOWN"]
|
||||
reward = "reward"
|
||||
character_set = ["@", "*"]
|
||||
Reference in New Issue
Block a user