From a4f1a08d871321d5e5c222a1877c2c9a7b9ffea9 Mon Sep 17 00:00:00 2001 From: wpeguero Date: Thu, 27 Feb 2025 20:41:20 -0500 Subject: [PATCH 1/3] CHANGE: Added Set of Values The purpose of these values is to allow the machine learning model to gain information that it would not know of without it being complex. Currently there is only access to the total number of items to provide agents to some information in regards to their items. The total number of pokemon in the party will allow the user to provide rewards based on the number of pokemon the agent has gathered. The time will provide the user with some insight as to how many in game hours the model took to reach certain check points or flags found within the game. Added a battle state variable which will allow the user to either use models that are specific to the battle scenarios within the game and the game overworld. Currently working on developing the flags for the badges. Have added it on for now. --- pyboy/plugins/game_wrapper_pokemon_gen1.py | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pyboy/plugins/game_wrapper_pokemon_gen1.py b/pyboy/plugins/game_wrapper_pokemon_gen1.py index 0c31be9cd..c65deff4c 100644 --- a/pyboy/plugins/game_wrapper_pokemon_gen1.py +++ b/pyboy/plugins/game_wrapper_pokemon_gen1.py @@ -15,6 +15,22 @@ logger = pyboy.logging.get_logger(__name__) +# Set of addresses for information on pokemon +# Based on the following link: https://datacrystal.tcrf.net/wiki/Pok%C3%A9mon_Red_and_Blue/RAM_map +ADDR_NUMBER_OF_POKEMON = 0xD163 # In party + +#Item related values +ADDR_TOTAL_ITEMS = 0xD53A +ADDR_BADGES = 0xD356 # Currently requires conversion (each bit is an independent switch and represents a badge) + +# Game Time +ADDR_HOURS = 0xDA40 +ADDR_MINUTES = 0xDA42 +ADDR_SECONDS = 0xDA44 + +# Battle mode +ADDR_BATTLE = 0xD057 + class GameWrapperPokemonGen1(PyBoyGameWrapper): """ @@ -36,6 +52,22 @@ def post_tick(self): self._tile_cache_invalid = True self._sprite_cache_invalid = True + self.number_of_pokemon = self.pyboy.memory[ADDR_NUMBER_OF_POKEMON] + self.total_items = self.pyboy.memory[ADDR_TOTAL_ITEMS] + # Extract the time in game: + _hours = self.pyboy.memory[ADDR_HOURS] + _minutes = self.pyboy.memory[ADDR_MINUTES] + _seconds = self.pyboy.memory[ADDR_SECONDS] + self.game_time = f'{_hours}:{_minutes}:{_seconds}' + + # Check whether the player is in battle mode or out of battle + if self.pyboy.memory[ADDR_BATTLE] == 0: + self.battle_state = 'In Battle' + else: + self.battle_state = 'Overworld' + + self.badges = self.pyboy.memory[ADDR_BADGES] #Research how this works. + scanline_parameters = self.pyboy.screen.tilemap_position_list # WX = scanline_parameters[0][2] WY = scanline_parameters[0][3] From 580653945e9e7b117d26b7c326eadc8228de6b7d Mon Sep 17 00:00:00 2001 From: Wilson Peguero Rosario Date: Mon, 3 Mar 2025 18:53:46 -0500 Subject: [PATCH 2/3] CHANGE: Added Parameters to the Game Wrapper When the game wrapper is called upon, it now prints out the number of pokemon in the party, the amount of time spent in game, the total number of items in backpack as well as whether the pokemon trainer is in battle or in the overworld. --- pyboy/plugins/game_wrapper_pokemon_gen1.py | 28 ++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/pyboy/plugins/game_wrapper_pokemon_gen1.py b/pyboy/plugins/game_wrapper_pokemon_gen1.py index c65deff4c..e3d3ae2c1 100644 --- a/pyboy/plugins/game_wrapper_pokemon_gen1.py +++ b/pyboy/plugins/game_wrapper_pokemon_gen1.py @@ -17,6 +17,7 @@ # Set of addresses for information on pokemon # Based on the following link: https://datacrystal.tcrf.net/wiki/Pok%C3%A9mon_Red_and_Blue/RAM_map +ADDR_TITLE_SCREEN = 0x1FC3 ADDR_NUMBER_OF_POKEMON = 0xD163 # In party #Item related values @@ -44,6 +45,24 @@ class GameWrapperPokemonGen1(PyBoyGameWrapper): def __init__(self, *args, **kwargs): super().__init__(*args, game_area_section=(0, 0, 20, 18), game_area_follow_scxy=True, **kwargs) self.sprite_offset = 0 + if self.pyboy.memory[ADDR_TITLE_SCREEN] == 0: + pass + else: + self.number_of_pokemon = self.pyboy.memory[ADDR_NUMBER_OF_POKEMON] + self.total_items = self.pyboy.memory[ADDR_TOTAL_ITEMS] + # Extract the time in game: + _hours = self.pyboy.memory[ADDR_HOURS] + _minutes = self.pyboy.memory[ADDR_MINUTES] + _seconds = self.pyboy.memory[ADDR_SECONDS] + self.game_time = f'{_hours}:{_minutes}:{_seconds}' + + # Check whether the player is in battle mode or out of battle + if self.pyboy.memory[ADDR_BATTLE] == 1: + self.battle_state = 'In Battle' + else: + self.battle_state = 'Overworld' + + self.badges = self.pyboy.memory[ADDR_BADGES] #Research how this works. def enabled(self): return (self.pyboy.cartridge_title == "POKEMON RED") or (self.pyboy.cartridge_title == "POKEMON BLUE") @@ -61,7 +80,7 @@ def post_tick(self): self.game_time = f'{_hours}:{_minutes}:{_seconds}' # Check whether the player is in battle mode or out of battle - if self.pyboy.memory[ADDR_BATTLE] == 0: + if self.pyboy.memory[ADDR_BATTLE] == 1: self.battle_state = 'In Battle' else: self.battle_state = 'Overworld' @@ -110,4 +129,9 @@ def game_area_collision(self): return game_area def __repr__(self): - return "Pokemon Gen 1:\n" + super().__repr__() + return ("Pokemon Gen 1:\n" + + f"Pokemon in Party: {self.number_of_pokemon}\n" + + f"Battle State: {self.battle_state}\n" + + f"Game Time: {self.game_time}\n" + + f"Total items: {self.total_items}\n" + + super().__repr__()) From 3631a15a8fff673d87bb3e9a829ab36fa7c203f7 Mon Sep 17 00:00:00 2001 From: Wilson Peguero Rosario Date: Mon, 10 Mar 2025 22:02:21 -0400 Subject: [PATCH 3/3] CHANGE: Reformat Time & Removed Code on Init Removed the code that looked up the addresses when the game wrapper class is initialized. This actually caused an error when using pyboy through the command line. After reading up and studying on bytes and how nintendo stores time, the game_time variable was reformatted by using the bcd_to_dec and _bcd_to_dec functions to extract the hours and minutes of in game time. Made some changes in the comments to include more information. Excluded the use of the badges until I become able to view the individual bits of the byte which stores the badge information. --- pyboy/plugins/game_wrapper_pokemon_gen1.py | 33 ++++++---------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/pyboy/plugins/game_wrapper_pokemon_gen1.py b/pyboy/plugins/game_wrapper_pokemon_gen1.py index e3d3ae2c1..4d2259bce 100644 --- a/pyboy/plugins/game_wrapper_pokemon_gen1.py +++ b/pyboy/plugins/game_wrapper_pokemon_gen1.py @@ -10,6 +10,7 @@ import numpy as np import pyboy +from pyboy.utils import _bcd_to_dec, bcd_to_dec from .base_plugin import PyBoyGameWrapper @@ -29,7 +30,7 @@ ADDR_MINUTES = 0xDA42 ADDR_SECONDS = 0xDA44 -# Battle mode +# Battle mode - May remove this due to not knowing exactly what it does. ADDR_BATTLE = 0xD057 @@ -45,24 +46,6 @@ class GameWrapperPokemonGen1(PyBoyGameWrapper): def __init__(self, *args, **kwargs): super().__init__(*args, game_area_section=(0, 0, 20, 18), game_area_follow_scxy=True, **kwargs) self.sprite_offset = 0 - if self.pyboy.memory[ADDR_TITLE_SCREEN] == 0: - pass - else: - self.number_of_pokemon = self.pyboy.memory[ADDR_NUMBER_OF_POKEMON] - self.total_items = self.pyboy.memory[ADDR_TOTAL_ITEMS] - # Extract the time in game: - _hours = self.pyboy.memory[ADDR_HOURS] - _minutes = self.pyboy.memory[ADDR_MINUTES] - _seconds = self.pyboy.memory[ADDR_SECONDS] - self.game_time = f'{_hours}:{_minutes}:{_seconds}' - - # Check whether the player is in battle mode or out of battle - if self.pyboy.memory[ADDR_BATTLE] == 1: - self.battle_state = 'In Battle' - else: - self.battle_state = 'Overworld' - - self.badges = self.pyboy.memory[ADDR_BADGES] #Research how this works. def enabled(self): return (self.pyboy.cartridge_title == "POKEMON RED") or (self.pyboy.cartridge_title == "POKEMON BLUE") @@ -71,12 +54,13 @@ def post_tick(self): self._tile_cache_invalid = True self._sprite_cache_invalid = True - self.number_of_pokemon = self.pyboy.memory[ADDR_NUMBER_OF_POKEMON] - self.total_items = self.pyboy.memory[ADDR_TOTAL_ITEMS] + self.number_of_pokemon = bcd_to_dec(self.pyboy.memory[ADDR_NUMBER_OF_POKEMON]) + _total_items = bcd_to_dec(self.pyboy.memory[ADDR_TOTAL_ITEMS]) + self.total_items = _total_items - 1 # This is because the cancel button seems to be counted as an item. # Extract the time in game: - _hours = self.pyboy.memory[ADDR_HOURS] - _minutes = self.pyboy.memory[ADDR_MINUTES] - _seconds = self.pyboy.memory[ADDR_SECONDS] + _hours = _bcd_to_dec(self.pyboy.memory[ADDR_HOURS: ADDR_HOURS+2]) + _minutes = _bcd_to_dec(self.pyboy.memory[ADDR_MINUTES: ADDR_MINUTES+2]) + _seconds = bcd_to_dec(self.pyboy.memory[ADDR_SECONDS]) self.game_time = f'{_hours}:{_minutes}:{_seconds}' # Check whether the player is in battle mode or out of battle @@ -85,7 +69,6 @@ def post_tick(self): else: self.battle_state = 'Overworld' - self.badges = self.pyboy.memory[ADDR_BADGES] #Research how this works. scanline_parameters = self.pyboy.screen.tilemap_position_list # WX = scanline_parameters[0][2]