Using PyBoy with Gym (original) (raw)

As most people opted to modify the OpenAI Gym that PyBoy used to have, we've decided to remove the Gym Env from the codebase itself and replace it with this example. To use it, copy it into your codebase, and modify it to your needs.

To install the requirements, run: pip install pyboy numpy gymnasium.

Thanks to Nicole Faye for providing the basis for this sample code. If you have questions or improvements to the code, come and tell us on Discord.

Discord

Adopted from https://github.com/NicoleFaye/PyBoy/blob/rl-test/PokemonPinballEnv.py

import gymnasium as gym from gymnasium import spaces import numpy as np from pyboy import PyBoy

actions = ['','a', 'b', 'left', 'right', 'up', 'down', 'start', 'select']

matrix_shape = (16, 20) game_area_observation_space = spaces.Box(low=0, high=255, shape=matrix_shape, dtype=np.uint8)

class GenericPyBoyEnv(gym.Env):

def __init__(self, pyboy, debug=False):
    super().__init__()
    self.pyboy = pyboy
    self._fitness=0
    self._previous_fitness=0
    self.debug = debug

    if not self.debug:
        self.pyboy.set_emulation_speed(0)

    self.action_space = spaces.Discrete(len(actions))
    self.observation_space = game_area_observation_space

    self.pyboy.game_wrapper.start_game()

def step(self, action):
    assert self.action_space.contains(action), "%r (%s) invalid" % (action, type(action))

    # Move the agent
    if action == 0:
        pass
    else:
        self.pyboy.button(actions[action])

    # Consider disabling renderer when not needed to improve speed:
    # self.pyboy.tick(1, False)
    self.pyboy.tick(1)

    done = self.pyboy.game_wrapper.game_over

    self._calculate_fitness()
    reward=self._fitness-self._previous_fitness

    observation=self.pyboy.game_area()
    info = {}
    truncated = False

    return observation, reward, done, truncated, info

def _calculate_fitness(self):
    self._previous_fitness=self._fitness

    # NOTE: Only some game wrappers will provide a score
    # If not, you'll have to investigate how to score the game yourself
    self._fitness=self.pyboy.game_wrapper.score

def reset(self, **kwargs):
    self.pyboy.game_wrapper.reset_game()
    self._fitness=0
    self._previous_fitness=0

    observation=self.pyboy.game_area()
    info = {}
    return observation, info

def render(self, mode='human'):
    pass

def close(self):
    self.pyboy.stop()