Skip to content

Commit

Permalink
Add simple Pokemon Gen 1 game wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
Baekalfen committed Oct 10, 2023
1 parent a464e69 commit 4f7b169
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 21 deletions.
16 changes: 8 additions & 8 deletions pyboy/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@

__pdoc__ = {
# docs exclude
"record_replay": False,
"window_headless": False,
"manager_gen": False,
"window_open_gl": False,
"debug": False,
"screenshot_recorder": False,
"manager": False,
"window_sdl2": False,
"auto_pause": False,
"screen_recorder": False,
"window_dummy": False,
"rewind": False,
"window_dummy": False,
"disable_input": False,
"manager_gen": False,
"auto_pause": False,
"manager": False,
"record_replay": False,
"screenshot_recorder": False,
"debug": False,
"window_sdl2": False,
# docs exclude end
}
5 changes: 4 additions & 1 deletion pyboy/plugins/base_plugin.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#

from cpython.array cimport array
from libc.stdint cimport uint8_t, uint32_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t
cimport cython
from pyboy.botsupport.tilemap cimport TileMap
from pyboy.core.mb cimport Motherboard
Expand Down Expand Up @@ -42,6 +42,9 @@ cdef class PyBoyGameWrapper(PyBoyPlugin):
cdef public shape
cdef bint game_has_started
cdef TileMap tilemap_background
cdef TileMap tilemap_window
cdef bint tilemap_use_background
cdef uint16_t sprite_offset

cdef bint _tile_cache_invalid
cdef array _cached_game_area_tiles_raw
Expand Down
31 changes: 20 additions & 11 deletions pyboy/plugins/base_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ class PyBoyGameWrapper(PyBoyPlugin):
def __init__(self, *args, game_area_section=(0, 0, 32, 32), game_area_wrap_around=False, **kwargs):
super().__init__(*args, **kwargs)
self.tilemap_background = self.pyboy.botsupport_manager().tilemap_background()
self.tilemap_window = self.pyboy.botsupport_manager().tilemap_window()
self.tilemap_use_background = True
self.sprite_offset = 0
self.game_has_started = False
self._tile_cache_invalid = True
self._sprite_cache_invalid = True
Expand Down Expand Up @@ -167,12 +170,6 @@ def game_over(self):
"""
raise NotImplementedError("game_over not implemented in game wrapper")

def game_over(self):
"""
After calling `start_game`, you can call this method at any time to know if the game is over.
"""
raise NotImplementedError("game_over not implemented in game wrapper")

def _sprites_on_screen(self):
if self._sprite_cache_invalid:
self._cached_sprites_on_screen = []
Expand All @@ -199,14 +196,25 @@ def _game_area_tiles(self):
for x in range(width):
_x = (xx+x+SCX) % 32
_y = (yy+y+SCY) % 32
self._cached_game_area_tiles[y][x] = self.tilemap_background.tile_identifier(_x, _y)
if self.tilemap_use_background:
self._cached_game_area_tiles[y][x] = self.tilemap_background.tile_identifier(_x, _y)
else:
self._cached_game_area_tiles[y][x] = self.tilemap_window.tile_identifier(_x, _y)
else:
self._cached_game_area_tiles = np.asarray(
self.tilemap_background[xx:xx + width, yy:yy + height], dtype=np.uint32
)
if self.tilemap_use_background:
self._cached_game_area_tiles = np.asarray(
self.tilemap_background[xx:xx + width, yy:yy + height], dtype=np.uint32
)
else:
self._cached_game_area_tiles = np.asarray(
self.tilemap_window[xx:xx + width, yy:yy + height], dtype=np.uint32
)
self._tile_cache_invalid = False
return self._cached_game_area_tiles

def use_background(self, value):
self.tilemap_use_background = value

def game_area(self):
"""
This method returns a cut-out of the screen as a simplified matrix for use in machine learning applications.
Expand All @@ -226,7 +234,8 @@ def game_area(self):
_x = (s.x // 8) - xx
_y = (s.y // 8) - yy
if 0 <= _y < height and 0 <= _x < width:
tiles_matrix[_y][_x] = s.tile_identifier
tiles_matrix[_y][
_x] = s.tile_identifier + self.sprite_offset # Adding offset to try to seperate sprites from tiles
return tiles_matrix

def _game_area_np(self, observation_type="tiles"):
Expand Down
10 changes: 10 additions & 0 deletions pyboy/plugins/game_wrapper_pokemon_gen1.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#
# License: See LICENSE.md file
# GitHub: https://github.com/Baekalfen/PyBoy
#
from libc.stdint cimport uint8_t
from pyboy.plugins.base_plugin cimport PyBoyGameWrapper
cimport cython

cdef class GameWrapperPokemonGen1(PyBoyGameWrapper):
pass
70 changes: 70 additions & 0 deletions pyboy/plugins/game_wrapper_pokemon_gen1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#
# License: See LICENSE.md file
# GitHub: https://github.com/Baekalfen/PyBoy
#
__pdoc__ = {
"GameWrapperPokemonGen1.cartridge_title": False,
"GameWrapperPokemonGen1.post_tick": False,
}

import logging

from pyboy.utils import WindowEvent

from .base_plugin import PyBoyGameWrapper

logger = logging.getLogger(__name__)

try:
from cython import compiled
cythonmode = compiled
except ImportError:
cythonmode = False


class GameWrapperPokemonGen1(PyBoyGameWrapper):
"""
This class wraps Pokemon Red/Blue, and provides basic access for AIs.
If you call `print` on an instance of this object, it will show an overview of everything this object provides.
"""
cartridge_title = None

def __init__(self, *args, **kwargs):
self.shape = (20, 18)
super().__init__(*args, game_area_section=(0, 0) + self.shape, game_area_wrap_around=True, **kwargs)
self.sprite_offset = 0x1000

def enabled(self):
return self.pyboy_argv.get("game_wrapper") and ((self.pyboy.cartridge_title() == "POKEMON RED") or
(self.pyboy.cartridge_title() == "POKEMON BLUE"))

def post_tick(self):
self._tile_cache_invalid = True
self._sprite_cache_invalid = True

scanline_parameters = self.pyboy.botsupport_manager().screen().tilemap_position_list()
WX = scanline_parameters[0][2]
WY = scanline_parameters[0][3]
self.use_background(WY != 0)

def __repr__(self):
adjust = 4
# yapf: disable
return (
f"Pokemon Gen 1:\n" +
"Sprites on screen:\n" +
"\n".join([str(s) for s in self._sprites_on_screen()]) +
"\n" +
"Tiles on screen:\n" +
" "*5 + "".join([f"{i: <4}" for i in range(10)]) + "\n" +
"_"*(adjust*20+4) +
"\n" +
"\n".join(
[
f"{i: <3}| " + "".join([str(tile).ljust(adjust) for tile in line])
for i, line in enumerate(self.game_area())
]
)
)
# yapf: enable
3 changes: 3 additions & 0 deletions pyboy/plugins/manager.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ from pyboy.plugins.screenshot_recorder cimport ScreenshotRecorder
from pyboy.plugins.game_wrapper_super_mario_land cimport GameWrapperSuperMarioLand
from pyboy.plugins.game_wrapper_tetris cimport GameWrapperTetris
from pyboy.plugins.game_wrapper_kirby_dream_land cimport GameWrapperKirbyDreamLand
from pyboy.plugins.game_wrapper_pokemon_gen1 cimport GameWrapperPokemonGen1
# imports end
from pyboy.plugins.base_plugin cimport PyBoyPlugin, PyBoyWindowPlugin

Expand All @@ -42,6 +43,7 @@ cdef class PluginManager:
cdef public GameWrapperSuperMarioLand game_wrapper_super_mario_land
cdef public GameWrapperTetris game_wrapper_tetris
cdef public GameWrapperKirbyDreamLand game_wrapper_kirby_dream_land
cdef public GameWrapperPokemonGen1 game_wrapper_pokemon_gen1
cdef bint window_sdl2_enabled
cdef bint window_open_gl_enabled
cdef bint window_headless_enabled
Expand All @@ -56,6 +58,7 @@ cdef class PluginManager:
cdef bint game_wrapper_super_mario_land_enabled
cdef bint game_wrapper_tetris_enabled
cdef bint game_wrapper_kirby_dream_land_enabled
cdef bint game_wrapper_pokemon_gen1_enabled
# plugin_cdef end

cdef list handle_events(self, list)
Expand Down
15 changes: 15 additions & 0 deletions pyboy/plugins/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
from pyboy.plugins.game_wrapper_super_mario_land import GameWrapperSuperMarioLand # isort:skip
from pyboy.plugins.game_wrapper_tetris import GameWrapperTetris # isort:skip
from pyboy.plugins.game_wrapper_kirby_dream_land import GameWrapperKirbyDreamLand # isort:skip
from pyboy.plugins.game_wrapper_pokemon_gen1 import GameWrapperPokemonGen1 # isort:skip
# imports end
from pyboy.plugins.base_plugin import PyBoyGameWrapper


def parser_arguments():
Expand All @@ -37,6 +39,7 @@ def parser_arguments():
yield GameWrapperSuperMarioLand.argv
yield GameWrapperTetris.argv
yield GameWrapperKirbyDreamLand.argv
yield GameWrapperPokemonGen1.argv
# yield_plugins end
pass

Expand Down Expand Up @@ -74,6 +77,8 @@ def __init__(self, pyboy, mb, pyboy_argv):
self.game_wrapper_tetris_enabled = self.game_wrapper_tetris.enabled()
self.game_wrapper_kirby_dream_land = GameWrapperKirbyDreamLand(pyboy, mb, pyboy_argv)
self.game_wrapper_kirby_dream_land_enabled = self.game_wrapper_kirby_dream_land.enabled()
self.game_wrapper_pokemon_gen1 = GameWrapperPokemonGen1(pyboy, mb, pyboy_argv)
self.game_wrapper_pokemon_gen1_enabled = self.game_wrapper_pokemon_gen1.enabled()
# plugins_enabled end

def gamewrapper(self):
Expand All @@ -84,6 +89,8 @@ def gamewrapper(self):
return self.game_wrapper_tetris
if self.game_wrapper_kirby_dream_land_enabled:
return self.game_wrapper_kirby_dream_land
if self.game_wrapper_pokemon_gen1_enabled:
return self.game_wrapper_pokemon_gen1
# gamewrapper end
return None

Expand Down Expand Up @@ -119,6 +126,8 @@ def handle_events(self, events):
events = self.game_wrapper_tetris.handle_events(events)
if self.game_wrapper_kirby_dream_land_enabled:
events = self.game_wrapper_kirby_dream_land.handle_events(events)
if self.game_wrapper_pokemon_gen1_enabled:
events = self.game_wrapper_pokemon_gen1.handle_events(events)
# foreach end
return events

Expand All @@ -142,6 +151,8 @@ def post_tick(self):
self.game_wrapper_tetris.post_tick()
if self.game_wrapper_kirby_dream_land_enabled:
self.game_wrapper_kirby_dream_land.post_tick()
if self.game_wrapper_pokemon_gen1_enabled:
self.game_wrapper_pokemon_gen1.post_tick()
# foreach end

self._post_tick_windows()
Expand Down Expand Up @@ -235,6 +246,8 @@ def window_title(self):
title += self.game_wrapper_tetris.window_title()
if self.game_wrapper_kirby_dream_land_enabled:
title += self.game_wrapper_kirby_dream_land.window_title()
if self.game_wrapper_pokemon_gen1_enabled:
title += self.game_wrapper_pokemon_gen1.window_title()
# foreach end
return title

Expand Down Expand Up @@ -270,6 +283,8 @@ def stop(self):
self.game_wrapper_tetris.stop()
if self.game_wrapper_kirby_dream_land_enabled:
self.game_wrapper_kirby_dream_land.stop()
if self.game_wrapper_pokemon_gen1_enabled:
self.game_wrapper_pokemon_gen1.stop()
# foreach end
pass

Expand Down
4 changes: 3 additions & 1 deletion pyboy/plugins/manager_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
# Plugins and priority!
# E.g. DisableInput first
windows = ["WindowSDL2", "WindowOpenGL", "WindowHeadless", "WindowDummy", "Debug"]
game_wrappers = ["GameWrapperSuperMarioLand", "GameWrapperTetris", "GameWrapperKirbyDreamLand"]
game_wrappers = [
"GameWrapperSuperMarioLand", "GameWrapperTetris", "GameWrapperKirbyDreamLand", "GameWrapperPokemonGen1"
]
plugins = [
"DisableInput", "AutoPause", "RecordReplay", "Rewind", "ScreenRecorder", "ScreenshotRecorder"
] + game_wrappers
Expand Down

0 comments on commit 4f7b169

Please sign in to comment.