diff --git a/docs/api/constants.html b/docs/api/constants.html new file mode 100644 index 000000000..d4b592a0f --- /dev/null +++ b/docs/api/constants.html @@ -0,0 +1,192 @@ + + + + + + +pyboy.api.constants API documentation + + + + + + + + + + +
+
+
+

Module pyboy.api.constants

+
+
+

Memory constants used internally to calculate tile and tile map addresses.

+
+ +Expand source code + +
#
+# License: See LICENSE.md file
+# GitHub: https://github.com/Baekalfen/PyBoy
+#
+"""
+Memory constants used internally to calculate tile and tile map addresses.
+"""
+
+VRAM_OFFSET = 0x8000
+"""
+Start address of VRAM
+"""
+LCDC_OFFSET = 0xFF40
+"""
+LCDC Register
+"""
+OAM_OFFSET = 0xFE00
+"""
+Start address of Object-Attribute-Memory (OAM)
+"""
+LOW_TILEMAP = 0x1800 + VRAM_OFFSET
+"""
+Start address of lower tilemap
+"""
+HIGH_TILEMAP = 0x1C00 + VRAM_OFFSET
+"""
+Start address of high tilemap
+"""
+LOW_TILEDATA = VRAM_OFFSET
+"""
+Start address of lower tile data
+"""
+LOW_TILEDATA_NTILES = 0x100
+"""
+Number of tiles in lower tile data
+"""
+HIGH_TILEDATA = 0x800 + VRAM_OFFSET
+"""
+Start address of high tile data
+"""
+TILES = 384
+"""
+Number of tiles supported on Game Boy DMG (non-color)
+"""
+TILES_CGB = 768
+"""
+Number of tiles supported on Game Boy Color
+"""
+SPRITES = 40
+"""
+Number of sprites supported
+"""
+ROWS = 144
+"""
+Rows (horizontal lines) on the screen
+"""
+COLS = 160
+"""
+Columns (vertical lines) on the screen
+"""
+
+
+
+
+
+

Global variables

+
+
var VRAM_OFFSET
+
+

Start address of VRAM

+
+
var LCDC_OFFSET
+
+

LCDC Register

+
+
var OAM_OFFSET
+
+

Start address of Object-Attribute-Memory (OAM)

+
+
var LOW_TILEMAP
+
+

Start address of lower tilemap

+
+
var HIGH_TILEMAP
+
+

Start address of high tilemap

+
+
var LOW_TILEDATA
+
+

Start address of lower tile data

+
+
var LOW_TILEDATA_NTILES
+
+

Number of tiles in lower tile data

+
+
var HIGH_TILEDATA
+
+

Start address of high tile data

+
+
var TILES
+
+

Number of tiles supported on Game Boy DMG (non-color)

+
+
var TILES_CGB
+
+

Number of tiles supported on Game Boy Color

+
+
var SPRITES
+
+

Number of sprites supported

+
+
var ROWS
+
+

Rows (horizontal lines) on the screen

+
+
var COLS
+
+

Columns (vertical lines) on the screen

+
+
+
+
+
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/docs/api/index.html b/docs/api/index.html new file mode 100644 index 000000000..69d4af7ea --- /dev/null +++ b/docs/api/index.html @@ -0,0 +1,117 @@ + + + + + + +pyboy.api API documentation + + + + + + + + + + +
+
+
+

Module pyboy.api

+
+
+

Tools to help interfacing with the Game Boy hardware

+
+ +Expand source code + +
#
+# License: See LICENSE.md file
+# GitHub: https://github.com/Baekalfen/PyBoy
+#
+"""
+Tools to help interfacing with the Game Boy hardware
+"""
+
+from . import constants
+from .screen import Screen
+from .sprite import Sprite
+from .tile import Tile
+from .tilemap import TileMap
+
+# __pdoc__ = {
+#     "constants": False,
+#     "manager": False,
+# }
+# __all__ = ["API"]
+
+
+
+

Sub-modules

+
+
pyboy.api.constants
+
+

Memory constants used internally to calculate tile and tile map addresses.

+
+
pyboy.api.memory_scanner
+
+
+
+
pyboy.api.screen
+
+

This class gives access to the frame buffer and other screen parameters of PyBoy.

+
+
pyboy.api.sprite
+
+

This class presents an interface to the sprites held in the OAM data on the Game Boy.

+
+
pyboy.api.tile
+
+

The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used both for +Sprite and …

+
+
pyboy.api.tilemap
+
+

The Game Boy has two tile maps, which defines what is rendered on the screen.

+
+
+
+
+
+
+
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/docs/api/memory_scanner.html b/docs/api/memory_scanner.html new file mode 100644 index 000000000..76cc41655 --- /dev/null +++ b/docs/api/memory_scanner.html @@ -0,0 +1,757 @@ + + + + + + +pyboy.api.memory_scanner API documentation + + + + + + + + + + +
+
+
+

Module pyboy.api.memory_scanner

+
+
+
+ +Expand source code + +
from enum import Enum
+
+from pyboy.utils import bcd_to_dec
+
+
+class StandardComparisonType(Enum):
+    """Enumeration for defining types of comparisons that do not require a previous value."""
+    EXACT = 1
+    LESS_THAN = 2
+    GREATER_THAN = 3
+    LESS_THAN_OR_EQUAL = 4
+    GREATER_THAN_OR_EQUAL = 5
+
+
+class DynamicComparisonType(Enum):
+    """Enumeration for defining types of comparisons that require a previous value."""
+    UNCHANGED = 1
+    CHANGED = 2
+    INCREASED = 3
+    DECREASED = 4
+    MATCH = 5
+
+
+class ScanMode(Enum):
+    """Enumeration for defining scanning modes."""
+    INT = 1
+    BCD = 2
+
+
+class MemoryScanner():
+    """A class for scanning memory within a given range."""
+    def __init__(self, pyboy):
+        """
+        Initializes the MemoryScanner with a PyBoy instance.
+
+        Args:
+            pyboy (PyBoy): The PyBoy emulator instance.
+        """
+        self.pyboy = pyboy
+        self._memory_cache = {}
+        self._memory_cache_byte_width = 1
+
+    def scan_memory(
+        self,
+        target_value=None,
+        start_addr=0x0000,
+        end_addr=0xFFFF,
+        standard_comparison_type=StandardComparisonType.EXACT,
+        value_type=ScanMode.INT,
+        byte_width=1,
+        byteorder="little"
+    ):
+        """
+        This function scans a specified range of memory for a target value from the `start_addr` to the `end_addr` (both included).
+
+        Example:
+        ```python
+        >>> current_score = 4 # You write current score in game
+        >>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+        []
+
+        ```
+
+        Args:
+            start_addr (int): The starting address for the scan.
+            end_addr (int): The ending address for the scan.
+            target_value (int or None): The value to search for. If None, any value is considered a match.
+            standard_comparison_type (StandardComparisonType): The type of comparison to use.
+            value_type (ValueType): The type of value (INT or BCD) to consider.
+            byte_width (int): The number of bytes to consider for each value.
+            byteorder (str): The endian type to use. This is only used for 16-bit values and higher. See [int.from_bytes](https://docs.python.org/3/library/stdtypes.html#int.from_bytes) for more details.
+
+        Returns:
+            list of int: A list of addresses where the target value is found.
+        """
+        self._memory_cache = {}
+        self._memory_cache_byte_width = byte_width
+        for addr in range(start_addr, end_addr - (byte_width-1) + 1): # Adjust the loop to prevent reading past end_addr
+            # Read multiple bytes based on byte_width and byteorder
+            value_bytes = self.pyboy.memory[addr:addr + byte_width]
+            value = int.from_bytes(value_bytes, byteorder)
+
+            if value_type == ScanMode.BCD:
+                value = bcd_to_dec(value, byte_width, byteorder)
+
+            if target_value is None or self._check_value(value, target_value, standard_comparison_type.value):
+                self._memory_cache[addr] = value
+
+        return list(self._memory_cache.keys())
+
+    def rescan_memory(
+        self, new_value=None, dynamic_comparison_type=DynamicComparisonType.UNCHANGED, byteorder="little"
+    ):
+        """
+        Rescans the memory and updates the memory cache based on a dynamic comparison type.
+
+        Example:
+        ```python
+        >>> current_score = 4 # You write current score in game
+        >>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+        []
+        >>> for _ in range(175):
+        ...     pyboy.tick(1, True) # Progress the game to change score
+        True...
+        >>> current_score = 8 # You write the new score in game
+        >>> from pyboy.api.memory_scanner import DynamicComparisonType
+        >>> addresses = pyboy.memory_scanner.rescan_memory(current_score, DynamicComparisonType.MATCH)
+        >>> print(addresses) # If repeated enough, only one address will remain
+        []
+
+        ```
+
+        Args:
+            new_value (int, optional): The new value for comparison. If not provided, the current value in memory is used.
+            dynamic_comparison_type (DynamicComparisonType): The type of comparison to use. Defaults to UNCHANGED.
+
+        Returns:
+            list of int: A list of addresses remaining in the memory cache after the rescan.
+        """
+        for addr, value in self._memory_cache.copy().items():
+            current_value = int.from_bytes(
+                self.pyboy.memory[addr:addr + self._memory_cache_byte_width], byteorder=byteorder
+            )
+            if (dynamic_comparison_type == DynamicComparisonType.UNCHANGED):
+                if value != current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.CHANGED):
+                if value == current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.INCREASED):
+                if value >= current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.DECREASED):
+                if value <= current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.MATCH):
+                if new_value == None:
+                    raise ValueError("new_value must be specified when using DynamicComparisonType.MATCH")
+                if current_value != new_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            else:
+                raise ValueError("Invalid comparison type")
+        return list(self._memory_cache.keys())
+
+    def _check_value(self, value, target_value, standard_comparison_type):
+        """
+        Compares a value with the target value based on the specified compare type.
+
+        Args:
+            value (int): The value to compare.
+            target_value (int or None): The target value to compare against.
+            standard_comparison_type (StandardComparisonType): The type of comparison to use.
+
+        Returns:
+            bool: True if the comparison condition is met, False otherwise.
+        """
+        if standard_comparison_type == StandardComparisonType.EXACT.value:
+            return value == target_value
+        elif standard_comparison_type == StandardComparisonType.LESS_THAN.value:
+            return value < target_value
+        elif standard_comparison_type == StandardComparisonType.GREATER_THAN.value:
+            return value > target_value
+        elif standard_comparison_type == StandardComparisonType.LESS_THAN_OR_EQUAL.value:
+            return value <= target_value
+        elif standard_comparison_type == StandardComparisonType.GREATER_THAN_OR_EQUAL.value:
+            return value >= target_value
+        else:
+            raise ValueError("Invalid comparison type")
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class StandardComparisonType +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
+
+

Enumeration for defining types of comparisons that do not require a previous value.

+
+ +Expand source code + +
class StandardComparisonType(Enum):
+    """Enumeration for defining types of comparisons that do not require a previous value."""
+    EXACT = 1
+    LESS_THAN = 2
+    GREATER_THAN = 3
+    LESS_THAN_OR_EQUAL = 4
+    GREATER_THAN_OR_EQUAL = 5
+
+

Ancestors

+
    +
  • enum.Enum
  • +
+

Class variables

+
+
var EXACT
+
+
+
+
var LESS_THAN
+
+
+
+
var GREATER_THAN
+
+
+
+
var LESS_THAN_OR_EQUAL
+
+
+
+
var GREATER_THAN_OR_EQUAL
+
+
+
+
+
+
+class DynamicComparisonType +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
+
+

Enumeration for defining types of comparisons that require a previous value.

+
+ +Expand source code + +
class DynamicComparisonType(Enum):
+    """Enumeration for defining types of comparisons that require a previous value."""
+    UNCHANGED = 1
+    CHANGED = 2
+    INCREASED = 3
+    DECREASED = 4
+    MATCH = 5
+
+

Ancestors

+
    +
  • enum.Enum
  • +
+

Class variables

+
+
var UNCHANGED
+
+
+
+
var CHANGED
+
+
+
+
var INCREASED
+
+
+
+
var DECREASED
+
+
+
+
var MATCH
+
+
+
+
+
+
+class ScanMode +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
+
+

Enumeration for defining scanning modes.

+
+ +Expand source code + +
class ScanMode(Enum):
+    """Enumeration for defining scanning modes."""
+    INT = 1
+    BCD = 2
+
+

Ancestors

+
    +
  • enum.Enum
  • +
+

Class variables

+
+
var INT
+
+
+
+
var BCD
+
+
+
+
+
+
+class MemoryScanner +(pyboy) +
+
+

A class for scanning memory within a given range.

+

Initializes the MemoryScanner with a PyBoy instance.

+

Args

+
+
pyboy : PyBoy
+
The PyBoy emulator instance.
+
+
+ +Expand source code + +
class MemoryScanner():
+    """A class for scanning memory within a given range."""
+    def __init__(self, pyboy):
+        """
+        Initializes the MemoryScanner with a PyBoy instance.
+
+        Args:
+            pyboy (PyBoy): The PyBoy emulator instance.
+        """
+        self.pyboy = pyboy
+        self._memory_cache = {}
+        self._memory_cache_byte_width = 1
+
+    def scan_memory(
+        self,
+        target_value=None,
+        start_addr=0x0000,
+        end_addr=0xFFFF,
+        standard_comparison_type=StandardComparisonType.EXACT,
+        value_type=ScanMode.INT,
+        byte_width=1,
+        byteorder="little"
+    ):
+        """
+        This function scans a specified range of memory for a target value from the `start_addr` to the `end_addr` (both included).
+
+        Example:
+        ```python
+        >>> current_score = 4 # You write current score in game
+        >>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+        []
+
+        ```
+
+        Args:
+            start_addr (int): The starting address for the scan.
+            end_addr (int): The ending address for the scan.
+            target_value (int or None): The value to search for. If None, any value is considered a match.
+            standard_comparison_type (StandardComparisonType): The type of comparison to use.
+            value_type (ValueType): The type of value (INT or BCD) to consider.
+            byte_width (int): The number of bytes to consider for each value.
+            byteorder (str): The endian type to use. This is only used for 16-bit values and higher. See [int.from_bytes](https://docs.python.org/3/library/stdtypes.html#int.from_bytes) for more details.
+
+        Returns:
+            list of int: A list of addresses where the target value is found.
+        """
+        self._memory_cache = {}
+        self._memory_cache_byte_width = byte_width
+        for addr in range(start_addr, end_addr - (byte_width-1) + 1): # Adjust the loop to prevent reading past end_addr
+            # Read multiple bytes based on byte_width and byteorder
+            value_bytes = self.pyboy.memory[addr:addr + byte_width]
+            value = int.from_bytes(value_bytes, byteorder)
+
+            if value_type == ScanMode.BCD:
+                value = bcd_to_dec(value, byte_width, byteorder)
+
+            if target_value is None or self._check_value(value, target_value, standard_comparison_type.value):
+                self._memory_cache[addr] = value
+
+        return list(self._memory_cache.keys())
+
+    def rescan_memory(
+        self, new_value=None, dynamic_comparison_type=DynamicComparisonType.UNCHANGED, byteorder="little"
+    ):
+        """
+        Rescans the memory and updates the memory cache based on a dynamic comparison type.
+
+        Example:
+        ```python
+        >>> current_score = 4 # You write current score in game
+        >>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+        []
+        >>> for _ in range(175):
+        ...     pyboy.tick(1, True) # Progress the game to change score
+        True...
+        >>> current_score = 8 # You write the new score in game
+        >>> from pyboy.api.memory_scanner import DynamicComparisonType
+        >>> addresses = pyboy.memory_scanner.rescan_memory(current_score, DynamicComparisonType.MATCH)
+        >>> print(addresses) # If repeated enough, only one address will remain
+        []
+
+        ```
+
+        Args:
+            new_value (int, optional): The new value for comparison. If not provided, the current value in memory is used.
+            dynamic_comparison_type (DynamicComparisonType): The type of comparison to use. Defaults to UNCHANGED.
+
+        Returns:
+            list of int: A list of addresses remaining in the memory cache after the rescan.
+        """
+        for addr, value in self._memory_cache.copy().items():
+            current_value = int.from_bytes(
+                self.pyboy.memory[addr:addr + self._memory_cache_byte_width], byteorder=byteorder
+            )
+            if (dynamic_comparison_type == DynamicComparisonType.UNCHANGED):
+                if value != current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.CHANGED):
+                if value == current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.INCREASED):
+                if value >= current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.DECREASED):
+                if value <= current_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            elif (dynamic_comparison_type == DynamicComparisonType.MATCH):
+                if new_value == None:
+                    raise ValueError("new_value must be specified when using DynamicComparisonType.MATCH")
+                if current_value != new_value:
+                    self._memory_cache.pop(addr)
+                else:
+                    self._memory_cache[addr] = current_value
+            else:
+                raise ValueError("Invalid comparison type")
+        return list(self._memory_cache.keys())
+
+    def _check_value(self, value, target_value, standard_comparison_type):
+        """
+        Compares a value with the target value based on the specified compare type.
+
+        Args:
+            value (int): The value to compare.
+            target_value (int or None): The target value to compare against.
+            standard_comparison_type (StandardComparisonType): The type of comparison to use.
+
+        Returns:
+            bool: True if the comparison condition is met, False otherwise.
+        """
+        if standard_comparison_type == StandardComparisonType.EXACT.value:
+            return value == target_value
+        elif standard_comparison_type == StandardComparisonType.LESS_THAN.value:
+            return value < target_value
+        elif standard_comparison_type == StandardComparisonType.GREATER_THAN.value:
+            return value > target_value
+        elif standard_comparison_type == StandardComparisonType.LESS_THAN_OR_EQUAL.value:
+            return value <= target_value
+        elif standard_comparison_type == StandardComparisonType.GREATER_THAN_OR_EQUAL.value:
+            return value >= target_value
+        else:
+            raise ValueError("Invalid comparison type")
+
+

Methods

+
+
+def scan_memory(self, target_value=None, start_addr=0, end_addr=65535, standard_comparison_type=StandardComparisonType.EXACT, value_type=ScanMode.INT, byte_width=1, byteorder='little') +
+
+

This function scans a specified range of memory for a target value from the start_addr to the end_addr (both included).

+

Example:

+
>>> current_score = 4 # You write current score in game
+>>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+[]
+
+
+

Args

+
+
start_addr : int
+
The starting address for the scan.
+
end_addr : int
+
The ending address for the scan.
+
target_value : int or None
+
The value to search for. If None, any value is considered a match.
+
standard_comparison_type : StandardComparisonType
+
The type of comparison to use.
+
value_type : ValueType
+
The type of value (INT or BCD) to consider.
+
byte_width : int
+
The number of bytes to consider for each value.
+
byteorder : str
+
The endian type to use. This is only used for 16-bit values and higher. See int.from_bytes for more details.
+
+

Returns

+
+
list of int
+
A list of addresses where the target value is found.
+
+
+ +Expand source code + +
def scan_memory(
+    self,
+    target_value=None,
+    start_addr=0x0000,
+    end_addr=0xFFFF,
+    standard_comparison_type=StandardComparisonType.EXACT,
+    value_type=ScanMode.INT,
+    byte_width=1,
+    byteorder="little"
+):
+    """
+    This function scans a specified range of memory for a target value from the `start_addr` to the `end_addr` (both included).
+
+    Example:
+    ```python
+    >>> current_score = 4 # You write current score in game
+    >>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+    []
+
+    ```
+
+    Args:
+        start_addr (int): The starting address for the scan.
+        end_addr (int): The ending address for the scan.
+        target_value (int or None): The value to search for. If None, any value is considered a match.
+        standard_comparison_type (StandardComparisonType): The type of comparison to use.
+        value_type (ValueType): The type of value (INT or BCD) to consider.
+        byte_width (int): The number of bytes to consider for each value.
+        byteorder (str): The endian type to use. This is only used for 16-bit values and higher. See [int.from_bytes](https://docs.python.org/3/library/stdtypes.html#int.from_bytes) for more details.
+
+    Returns:
+        list of int: A list of addresses where the target value is found.
+    """
+    self._memory_cache = {}
+    self._memory_cache_byte_width = byte_width
+    for addr in range(start_addr, end_addr - (byte_width-1) + 1): # Adjust the loop to prevent reading past end_addr
+        # Read multiple bytes based on byte_width and byteorder
+        value_bytes = self.pyboy.memory[addr:addr + byte_width]
+        value = int.from_bytes(value_bytes, byteorder)
+
+        if value_type == ScanMode.BCD:
+            value = bcd_to_dec(value, byte_width, byteorder)
+
+        if target_value is None or self._check_value(value, target_value, standard_comparison_type.value):
+            self._memory_cache[addr] = value
+
+    return list(self._memory_cache.keys())
+
+
+
+def rescan_memory(self, new_value=None, dynamic_comparison_type=DynamicComparisonType.UNCHANGED, byteorder='little') +
+
+

Rescans the memory and updates the memory cache based on a dynamic comparison type.

+

Example:

+
>>> current_score = 4 # You write current score in game
+>>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+[]
+>>> for _ in range(175):
+...     pyboy.tick(1, True) # Progress the game to change score
+True...
+>>> current_score = 8 # You write the new score in game
+>>> from pyboy.api.memory_scanner import DynamicComparisonType
+>>> addresses = pyboy.memory_scanner.rescan_memory(current_score, DynamicComparisonType.MATCH)
+>>> print(addresses) # If repeated enough, only one address will remain
+[]
+
+
+

Args

+
+
new_value : int, optional
+
The new value for comparison. If not provided, the current value in memory is used.
+
dynamic_comparison_type : DynamicComparisonType
+
The type of comparison to use. Defaults to UNCHANGED.
+
+

Returns

+
+
list of int
+
A list of addresses remaining in the memory cache after the rescan.
+
+
+ +Expand source code + +
def rescan_memory(
+    self, new_value=None, dynamic_comparison_type=DynamicComparisonType.UNCHANGED, byteorder="little"
+):
+    """
+    Rescans the memory and updates the memory cache based on a dynamic comparison type.
+
+    Example:
+    ```python
+    >>> current_score = 4 # You write current score in game
+    >>> pyboy.memory_scanner.scan_memory(current_score, start_addr=0xC000, end_addr=0xDFFF)
+    []
+    >>> for _ in range(175):
+    ...     pyboy.tick(1, True) # Progress the game to change score
+    True...
+    >>> current_score = 8 # You write the new score in game
+    >>> from pyboy.api.memory_scanner import DynamicComparisonType
+    >>> addresses = pyboy.memory_scanner.rescan_memory(current_score, DynamicComparisonType.MATCH)
+    >>> print(addresses) # If repeated enough, only one address will remain
+    []
+
+    ```
+
+    Args:
+        new_value (int, optional): The new value for comparison. If not provided, the current value in memory is used.
+        dynamic_comparison_type (DynamicComparisonType): The type of comparison to use. Defaults to UNCHANGED.
+
+    Returns:
+        list of int: A list of addresses remaining in the memory cache after the rescan.
+    """
+    for addr, value in self._memory_cache.copy().items():
+        current_value = int.from_bytes(
+            self.pyboy.memory[addr:addr + self._memory_cache_byte_width], byteorder=byteorder
+        )
+        if (dynamic_comparison_type == DynamicComparisonType.UNCHANGED):
+            if value != current_value:
+                self._memory_cache.pop(addr)
+            else:
+                self._memory_cache[addr] = current_value
+        elif (dynamic_comparison_type == DynamicComparisonType.CHANGED):
+            if value == current_value:
+                self._memory_cache.pop(addr)
+            else:
+                self._memory_cache[addr] = current_value
+        elif (dynamic_comparison_type == DynamicComparisonType.INCREASED):
+            if value >= current_value:
+                self._memory_cache.pop(addr)
+            else:
+                self._memory_cache[addr] = current_value
+        elif (dynamic_comparison_type == DynamicComparisonType.DECREASED):
+            if value <= current_value:
+                self._memory_cache.pop(addr)
+            else:
+                self._memory_cache[addr] = current_value
+        elif (dynamic_comparison_type == DynamicComparisonType.MATCH):
+            if new_value == None:
+                raise ValueError("new_value must be specified when using DynamicComparisonType.MATCH")
+            if current_value != new_value:
+                self._memory_cache.pop(addr)
+            else:
+                self._memory_cache[addr] = current_value
+        else:
+            raise ValueError("Invalid comparison type")
+    return list(self._memory_cache.keys())
+
+
+
+
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/docs/botsupport/screen.html b/docs/api/screen.html similarity index 55% rename from docs/botsupport/screen.html rename to docs/api/screen.html index 27f9864d0..ebb8ccd15 100644 --- a/docs/botsupport/screen.html +++ b/docs/api/screen.html @@ -4,7 +4,7 @@ -pyboy.botsupport.screen API documentation +pyboy.api.screen API documentation @@ -18,7 +18,7 @@
-

Module pyboy.botsupport.screen

+

Module pyboy.api.screen

This class gives access to the frame buffer and other screen parameters of PyBoy.

@@ -34,13 +34,14 @@

Module pyboy.botsupport.screen

This class gives access to the frame buffer and other screen parameters of PyBoy. """ -import logging - import numpy as np +from pyboy import utils +from pyboy.logging import get_logger + from .constants import COLS, ROWS -logger = logging.getLogger(__name__) +logger = get_logger(__name__) try: from PIL import Image @@ -54,117 +55,122 @@

Module pyboy.botsupport.screen

to make it possible to read this buffer out. If you're making an AI or bot, it's highly recommended to _not_ use this class for detecting objects on the screen. - It's much more efficient to use `pyboy.botsupport.BotSupportManager.tilemap_background`, `pyboy.botsupport.BotSupportManager.tilemap_window`, and - `pyboy.botsupport.BotSupportManager.sprite` instead. + It's much more efficient to use `pyboy.tilemap_background`, `pyboy.tilemap_window`, and `pyboy.sprite` instead. """ def __init__(self, mb): self.mb = mb - def tilemap_position(self): + self.raw_buffer = self.mb.lcd.renderer._screenbuffer """ - These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note - that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer - to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site - of the tile map. + Provides a raw, unfiltered `bytes` object with the data from the screen. Check + `Screen.raw_buffer_format` to see which dataformat is used. **The returned type and dataformat are + subject to change.** The screen buffer is row-major. - For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf), - or the Pan Docs under [LCD Position and Scrolling](http://bgb.bircd.org/pandocs.htm#lcdpositionandscrolling). + Use this, only if you need to bypass the overhead of `Screen.image` or `Screen.ndarray`. Returns ------- - tuple: - Returns the tuple of registers ((SCX, SCY), (WX - 7, WY)) + bytes: + 92160 bytes of screen data in a `bytes` object. """ - return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos()) - - def tilemap_position_list(self): + self.raw_buffer_dims = self.mb.lcd.renderer.buffer_dims """ - This function provides the screen (SCX, SCY) and window (WX. WY) position for each horizontal line in the - screen buffer. These parameters are often used for visual effects, and some games will reset the registers at - the end of each call to `pyboy.PyBoy.tick()`. For such games, `Screen.tilemap_position` becomes useless. - - See `Screen.tilemap_position` for more information. + Returns the dimensions of the raw screen buffer. The screen buffer is row-major. Returns ------- - list: - Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off. + tuple: + A two-tuple of the buffer dimensions. E.g. (144, 160). """ - if self.mb.lcd._LCDC.lcd_enable: - return [[line[0], line[1], line[2], line[3]] for line in self.mb.lcd.renderer._scanlineparameters] - else: - return [[0, 0, 0, 0] for line in range(144)] - - def raw_screen_buffer(self): + self.raw_buffer_format = self.mb.lcd.renderer.color_format """ - Provides a raw, unfiltered `bytes` object with the data from the screen. Check - `Screen.raw_screen_buffer_format` to see which dataformat is used. The returned type and dataformat are - subject to change. - - Use this, only if you need to bypass the overhead of `Screen.screen_image` or `Screen.screen_ndarray`. + Returns the color format of the raw screen buffer. **This format is subject to change.** Returns ------- - bytes: - 92160 bytes of screen data in a `bytes` object. + str: + Color format of the raw screen buffer. E.g. 'RGBA'. """ - return self.mb.lcd.renderer._screenbuffer_raw.tobytes() - - def raw_screen_buffer_dims(self): + self.image = None """ - Returns the dimensions of the raw screen buffer. + Reference to a PIL Image from the screen buffer. **Remember to copy, resize or convert this object** if you + intend to store it. The backing buffer will update, but it will be the same `PIL.Image` object. + + The screen buffer is internally row-major, but PIL hides this. + + Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which + case, read up on the `pyboy.api` features, [Pan Docs](https://gbdev.io/pandocs/) on tiles/sprites, + and join our Discord channel for more help. Returns ------- - tuple: - A two-tuple of the buffer dimensions. E.g. (160, 144). + PIL.Image: + RGB image of (160, 144) pixels """ - return self.mb.lcd.renderer.buffer_dims + if not Image: + logger.warning("Cannot generate screen image. Missing dependency \"Pillow\".") - def raw_screen_buffer_format(self): + else: + self.image = Image.frombuffer( + self.mb.lcd.renderer.color_format, self.mb.lcd.renderer.buffer_dims[::-1], + self.mb.lcd.renderer._screenbuffer_raw + ) + + self.ndarray = np.frombuffer( + self.mb.lcd.renderer._screenbuffer_raw, + dtype=np.uint8, + ).reshape(ROWS, COLS, 4) """ - Returns the color format of the raw screen buffer. + References the screen data in NumPy format. **Remember to copy this object** if you intend to store it. + The backing buffer will update, but it will be the same `ndarray` object. + + The format is given by `pyboy.api.screen.Screen.raw_buffer_format`. The screen buffer is row-major. Returns ------- - str: - Color format of the raw screen buffer. E.g. 'RGB'. + numpy.ndarray: + Screendata in `ndarray` of bytes with shape (144, 160, 3) """ - return self.mb.lcd.renderer.color_format - def screen_ndarray(self): + @property + def tilemap_position_list(self): """ - Provides the screen data in NumPy format. The dataformat is always RGB. + This function provides the screen (SCX, SCY) and window (WX. WY) position for each horizontal line in the + screen buffer. These parameters are often used for visual effects, and some games will reset the registers at + the end of each call to `pyboy.PyBoy.tick()`. + + See `Screen.get_tilemap_position` for more information. Returns ------- - numpy.ndarray: - Screendata in `ndarray` of bytes with shape (160, 144, 3) + list: + Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off. """ - return np.frombuffer(self.mb.lcd.renderer._screenbuffer_raw, dtype=np.uint8).reshape(ROWS, COLS, 4)[:, :, 1:] + # self.tilemap_position_list = np.asarray(self.mb.lcd.renderer._scanlineparameters, dtype=np.uint8).reshape(144, 5)[:, :4] + # self.tilemap_position_list = self.mb.lcd.renderer._scanlineparameters - def screen_image(self): + # # return self.mb.lcd.renderer._scanlineparameters + if self.mb.lcd._LCDC.lcd_enable: + return [[line[0], line[1], line[2], line[3]] for line in self.mb.lcd.renderer._scanlineparameters] + else: + return [[0, 0, 0, 0] for line in range(144)] + + def get_tilemap_position(self): """ - Generates a PIL Image from the screen buffer. + These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note + that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer + to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site + of the tile map. - Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which - case, read up on the `pyboy.botsupport` features, [Pan Docs](http://bgb.bircd.org/pandocs.htm) on tiles/sprites, - and join our Discord channel for more help. + For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf), + or the Pan Docs under [LCD Position and Scrolling](https://gbdev.io/pandocs/Scrolling.html). Returns ------- - PIL.Image: - RGB image of (160, 144) pixels + tuple: + Returns the tuple of registers ((SCX, SCY), (WX - 7, WY)) """ - if not Image: - logger.error("Cannot generate screen image. Missing dependency \"Pillow\".") - return None - - # NOTE: Might have room for performance improvement - # It's not possible to use the following, as the byte-order (endianess) isn't supported in Pillow - # Image.frombytes('RGBA', self.buffer_dims, self.screen_buffer()).show() - # FIXME: FORMAT IS BGR NOT RGB!!! - return Image.fromarray(self.screen_ndarray()[:, :, [2, 1, 0]], "RGB") + return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos())
@@ -176,7 +182,7 @@

Module pyboy.botsupport.screen

Classes

-
+
class Screen (mb)
@@ -184,8 +190,7 @@

Classes

As part of the emulation, we generate a screen buffer in 32-bit RGBA format. This class has several helper methods to make it possible to read this buffer out.

If you're making an AI or bot, it's highly recommended to not use this class for detecting objects on the screen. -It's much more efficient to use BotSupportManager.tilemap_background(), BotSupportManager.tilemap_window(), and -BotSupportManager.sprite() instead.

+It's much more efficient to use pyboy.tilemap_background, pyboy.tilemap_window, and pyboy.sprite instead.

Expand source code @@ -196,165 +201,131 @@

Classes

to make it possible to read this buffer out. If you're making an AI or bot, it's highly recommended to _not_ use this class for detecting objects on the screen. - It's much more efficient to use `pyboy.botsupport.BotSupportManager.tilemap_background`, `pyboy.botsupport.BotSupportManager.tilemap_window`, and - `pyboy.botsupport.BotSupportManager.sprite` instead. + It's much more efficient to use `pyboy.tilemap_background`, `pyboy.tilemap_window`, and `pyboy.sprite` instead. """ def __init__(self, mb): self.mb = mb - def tilemap_position(self): + self.raw_buffer = self.mb.lcd.renderer._screenbuffer """ - These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note - that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer - to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site - of the tile map. + Provides a raw, unfiltered `bytes` object with the data from the screen. Check + `Screen.raw_buffer_format` to see which dataformat is used. **The returned type and dataformat are + subject to change.** The screen buffer is row-major. - For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf), - or the Pan Docs under [LCD Position and Scrolling](http://bgb.bircd.org/pandocs.htm#lcdpositionandscrolling). + Use this, only if you need to bypass the overhead of `Screen.image` or `Screen.ndarray`. Returns ------- - tuple: - Returns the tuple of registers ((SCX, SCY), (WX - 7, WY)) + bytes: + 92160 bytes of screen data in a `bytes` object. """ - return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos()) - - def tilemap_position_list(self): + self.raw_buffer_dims = self.mb.lcd.renderer.buffer_dims """ - This function provides the screen (SCX, SCY) and window (WX. WY) position for each horizontal line in the - screen buffer. These parameters are often used for visual effects, and some games will reset the registers at - the end of each call to `pyboy.PyBoy.tick()`. For such games, `Screen.tilemap_position` becomes useless. - - See `Screen.tilemap_position` for more information. + Returns the dimensions of the raw screen buffer. The screen buffer is row-major. Returns ------- - list: - Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off. + tuple: + A two-tuple of the buffer dimensions. E.g. (144, 160). """ - if self.mb.lcd._LCDC.lcd_enable: - return [[line[0], line[1], line[2], line[3]] for line in self.mb.lcd.renderer._scanlineparameters] - else: - return [[0, 0, 0, 0] for line in range(144)] - - def raw_screen_buffer(self): + self.raw_buffer_format = self.mb.lcd.renderer.color_format """ - Provides a raw, unfiltered `bytes` object with the data from the screen. Check - `Screen.raw_screen_buffer_format` to see which dataformat is used. The returned type and dataformat are - subject to change. - - Use this, only if you need to bypass the overhead of `Screen.screen_image` or `Screen.screen_ndarray`. + Returns the color format of the raw screen buffer. **This format is subject to change.** Returns ------- - bytes: - 92160 bytes of screen data in a `bytes` object. + str: + Color format of the raw screen buffer. E.g. 'RGBA'. """ - return self.mb.lcd.renderer._screenbuffer_raw.tobytes() - - def raw_screen_buffer_dims(self): + self.image = None """ - Returns the dimensions of the raw screen buffer. + Reference to a PIL Image from the screen buffer. **Remember to copy, resize or convert this object** if you + intend to store it. The backing buffer will update, but it will be the same `PIL.Image` object. + + The screen buffer is internally row-major, but PIL hides this. + + Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which + case, read up on the `pyboy.api` features, [Pan Docs](https://gbdev.io/pandocs/) on tiles/sprites, + and join our Discord channel for more help. Returns ------- - tuple: - A two-tuple of the buffer dimensions. E.g. (160, 144). + PIL.Image: + RGB image of (160, 144) pixels """ - return self.mb.lcd.renderer.buffer_dims + if not Image: + logger.warning("Cannot generate screen image. Missing dependency \"Pillow\".") - def raw_screen_buffer_format(self): + else: + self.image = Image.frombuffer( + self.mb.lcd.renderer.color_format, self.mb.lcd.renderer.buffer_dims[::-1], + self.mb.lcd.renderer._screenbuffer_raw + ) + + self.ndarray = np.frombuffer( + self.mb.lcd.renderer._screenbuffer_raw, + dtype=np.uint8, + ).reshape(ROWS, COLS, 4) """ - Returns the color format of the raw screen buffer. + References the screen data in NumPy format. **Remember to copy this object** if you intend to store it. + The backing buffer will update, but it will be the same `ndarray` object. + + The format is given by `pyboy.api.screen.Screen.raw_buffer_format`. The screen buffer is row-major. Returns ------- - str: - Color format of the raw screen buffer. E.g. 'RGB'. + numpy.ndarray: + Screendata in `ndarray` of bytes with shape (144, 160, 3) """ - return self.mb.lcd.renderer.color_format - def screen_ndarray(self): + @property + def tilemap_position_list(self): """ - Provides the screen data in NumPy format. The dataformat is always RGB. + This function provides the screen (SCX, SCY) and window (WX. WY) position for each horizontal line in the + screen buffer. These parameters are often used for visual effects, and some games will reset the registers at + the end of each call to `pyboy.PyBoy.tick()`. + + See `Screen.get_tilemap_position` for more information. Returns ------- - numpy.ndarray: - Screendata in `ndarray` of bytes with shape (160, 144, 3) + list: + Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off. """ - return np.frombuffer(self.mb.lcd.renderer._screenbuffer_raw, dtype=np.uint8).reshape(ROWS, COLS, 4)[:, :, 1:] + # self.tilemap_position_list = np.asarray(self.mb.lcd.renderer._scanlineparameters, dtype=np.uint8).reshape(144, 5)[:, :4] + # self.tilemap_position_list = self.mb.lcd.renderer._scanlineparameters - def screen_image(self): + # # return self.mb.lcd.renderer._scanlineparameters + if self.mb.lcd._LCDC.lcd_enable: + return [[line[0], line[1], line[2], line[3]] for line in self.mb.lcd.renderer._scanlineparameters] + else: + return [[0, 0, 0, 0] for line in range(144)] + + def get_tilemap_position(self): """ - Generates a PIL Image from the screen buffer. + These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note + that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer + to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site + of the tile map. - Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which - case, read up on the `pyboy.botsupport` features, [Pan Docs](http://bgb.bircd.org/pandocs.htm) on tiles/sprites, - and join our Discord channel for more help. + For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf), + or the Pan Docs under [LCD Position and Scrolling](https://gbdev.io/pandocs/Scrolling.html). Returns ------- - PIL.Image: - RGB image of (160, 144) pixels + tuple: + Returns the tuple of registers ((SCX, SCY), (WX - 7, WY)) """ - if not Image: - logger.error("Cannot generate screen image. Missing dependency \"Pillow\".") - return None - - # NOTE: Might have room for performance improvement - # It's not possible to use the following, as the byte-order (endianess) isn't supported in Pillow - # Image.frombytes('RGBA', self.buffer_dims, self.screen_buffer()).show() - # FIXME: FORMAT IS BGR NOT RGB!!! - return Image.fromarray(self.screen_ndarray()[:, :, [2, 1, 0]], "RGB")
+ return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos())
-

Methods

+

Instance variables

-
-def tilemap_position(self) -
-
-

These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note -that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer -to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site -of the tile map.

-

For more details, see "7.4 Viewport" in the report, -or the Pan Docs under LCD Position and Scrolling.

-

Returns

-
-
tuple:
-
Returns the tuple of registers ((SCX, SCY), (WX - 7, WY))
-
-
- -Expand source code - -
def tilemap_position(self):
-    """
-    These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note
-    that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer
-    to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site
-    of the tile map.
-
-    For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf),
-    or the Pan Docs under [LCD Position and Scrolling](http://bgb.bircd.org/pandocs.htm#lcdpositionandscrolling).
-
-    Returns
-    -------
-    tuple:
-        Returns the tuple of registers ((SCX, SCY), (WX - 7, WY))
-    """
-    return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos())
-
-
-
-def tilemap_position_list(self) -
+
var tilemap_position_list

This function provides the screen (SCX, SCY) and window (WX. WY) position for each horizontal line in the screen buffer. These parameters are often used for visual effects, and some games will reset the registers at -the end of each call to PyBoy.tick(). For such games, Screen.tilemap_position() becomes useless.

-

See Screen.tilemap_position() for more information.

+the end of each call to PyBoy.tick().

+

See Screen.get_tilemap_position() for more information.

Returns

list:
@@ -364,175 +335,123 @@

Returns

Expand source code -
def tilemap_position_list(self):
+
@property
+def tilemap_position_list(self):
     """
     This function provides the screen (SCX, SCY) and window (WX. WY) position for each horizontal line in the
     screen buffer. These parameters are often used for visual effects, and some games will reset the registers at
-    the end of each call to `pyboy.PyBoy.tick()`. For such games, `Screen.tilemap_position` becomes useless.
+    the end of each call to `pyboy.PyBoy.tick()`.
 
-    See `Screen.tilemap_position` for more information.
+    See `Screen.get_tilemap_position` for more information.
 
     Returns
     -------
     list:
         Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off.
     """
+    # self.tilemap_position_list = np.asarray(self.mb.lcd.renderer._scanlineparameters, dtype=np.uint8).reshape(144, 5)[:, :4]
+    # self.tilemap_position_list = self.mb.lcd.renderer._scanlineparameters
+
+    # # return self.mb.lcd.renderer._scanlineparameters
     if self.mb.lcd._LCDC.lcd_enable:
         return [[line[0], line[1], line[2], line[3]] for line in self.mb.lcd.renderer._scanlineparameters]
     else:
         return [[0, 0, 0, 0] for line in range(144)]
-
-def raw_screen_buffer(self) -
+
var raw_buffer

Provides a raw, unfiltered bytes object with the data from the screen. Check -Screen.raw_screen_buffer_format() to see which dataformat is used. The returned type and dataformat are -subject to change.

-

Use this, only if you need to bypass the overhead of Screen.screen_image() or Screen.screen_ndarray().

+Screen.raw_buffer_format to see which dataformat is used. The returned type and dataformat are +subject to change. The screen buffer is row-major.

+

Use this, only if you need to bypass the overhead of Screen.image or Screen.ndarray.

Returns

bytes:
92160 bytes of screen data in a bytes object.
-
- -Expand source code - -
def raw_screen_buffer(self):
-    """
-    Provides a raw, unfiltered `bytes` object with the data from the screen. Check
-    `Screen.raw_screen_buffer_format` to see which dataformat is used. The returned type and dataformat are
-    subject to change.
-
-    Use this, only if you need to bypass the overhead of `Screen.screen_image` or `Screen.screen_ndarray`.
-
-    Returns
-    -------
-    bytes:
-        92160 bytes of screen data in a `bytes` object.
-    """
-    return self.mb.lcd.renderer._screenbuffer_raw.tobytes()
-
-
-def raw_screen_buffer_dims(self) -
+
var raw_buffer_dims
-

Returns the dimensions of the raw screen buffer.

+

Returns the dimensions of the raw screen buffer. The screen buffer is row-major.

Returns

tuple:
-
A two-tuple of the buffer dimensions. E.g. (160, 144).
+
A two-tuple of the buffer dimensions. E.g. (144, 160).
-
- -Expand source code - -
def raw_screen_buffer_dims(self):
-    """
-    Returns the dimensions of the raw screen buffer.
-
-    Returns
-    -------
-    tuple:
-        A two-tuple of the buffer dimensions. E.g. (160, 144).
-    """
-    return self.mb.lcd.renderer.buffer_dims
-
-
-def raw_screen_buffer_format(self) -
+
var raw_buffer_format
-

Returns the color format of the raw screen buffer.

+

Returns the color format of the raw screen buffer. This format is subject to change.

Returns

str:
-
Color format of the raw screen buffer. E.g. 'RGB'.
+
Color format of the raw screen buffer. E.g. 'RGBA'.
-
- -Expand source code - -
def raw_screen_buffer_format(self):
-    """
-    Returns the color format of the raw screen buffer.
-
-    Returns
-    -------
-    str:
-        Color format of the raw screen buffer. E.g. 'RGB'.
-    """
-    return self.mb.lcd.renderer.color_format
-
-
-def screen_ndarray(self) -
+
var image
-

Provides the screen data in NumPy format. The dataformat is always RGB.

+

Reference to a PIL Image from the screen buffer. Remember to copy, resize or convert this object if you +intend to store it. The backing buffer will update, but it will be the same PIL.Image object.

+

The screen buffer is internally row-major, but PIL hides this.

+

Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which +case, read up on the pyboy.api features, Pan Docs on tiles/sprites, +and join our Discord channel for more help.

+

Returns

+
+
PIL.Image:
+
RGB image of (160, 144) pixels
+
+
+
var ndarray
+
+

References the screen data in NumPy format. Remember to copy this object if you intend to store it. +The backing buffer will update, but it will be the same ndarray object.

+

The format is given by Screen.raw_buffer_format. The screen buffer is row-major.

Returns

numpy.ndarray:
-
Screendata in ndarray of bytes with shape (160, 144, 3)
+
Screendata in ndarray of bytes with shape (144, 160, 3)
-
- -Expand source code - -
def screen_ndarray(self):
-    """
-    Provides the screen data in NumPy format. The dataformat is always RGB.
-
-    Returns
-    -------
-    numpy.ndarray:
-        Screendata in `ndarray` of bytes with shape (160, 144, 3)
-    """
-    return np.frombuffer(self.mb.lcd.renderer._screenbuffer_raw, dtype=np.uint8).reshape(ROWS, COLS, 4)[:, :, 1:]
-
-
-def screen_image(self) +
+

Methods

+
+
+def get_tilemap_position(self)
-

Generates a PIL Image from the screen buffer.

-

Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which -case, read up on the pyboy.botsupport features, Pan Docs on tiles/sprites, -and join our Discord channel for more help.

+

These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note +that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer +to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site +of the tile map.

+

For more details, see "7.4 Viewport" in the report, +or the Pan Docs under LCD Position and Scrolling.

Returns

-
PIL.Image:
-
RGB image of (160, 144) pixels
+
tuple:
+
Returns the tuple of registers ((SCX, SCY), (WX - 7, WY))
Expand source code -
def screen_image(self):
+
def get_tilemap_position(self):
     """
-    Generates a PIL Image from the screen buffer.
+    These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note
+    that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer
+    to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site
+    of the tile map.
 
-    Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which
-    case, read up on the `pyboy.botsupport` features, [Pan Docs](http://bgb.bircd.org/pandocs.htm) on tiles/sprites,
-    and join our Discord channel for more help.
+    For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf),
+    or the Pan Docs under [LCD Position and Scrolling](https://gbdev.io/pandocs/Scrolling.html).
 
     Returns
     -------
-    PIL.Image:
-        RGB image of (160, 144) pixels
+    tuple:
+        Returns the tuple of registers ((SCX, SCY), (WX - 7, WY))
     """
-    if not Image:
-        logger.error("Cannot generate screen image. Missing dependency \"Pillow\".")
-        return None
-
-    # NOTE: Might have room for performance improvement
-    # It's not possible to use the following, as the byte-order (endianess) isn't supported in Pillow
-    # Image.frombytes('RGBA', self.buffer_dims, self.screen_buffer()).show()
-    # FIXME: FORMAT IS BGR NOT RGB!!!
-    return Image.fromarray(self.screen_ndarray()[:, :, [2, 1, 0]], "RGB")
+ return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos())
@@ -548,21 +467,21 @@

Index

  • Super-module

  • Classes

    diff --git a/docs/botsupport/sprite.html b/docs/api/sprite.html similarity index 77% rename from docs/botsupport/sprite.html rename to docs/api/sprite.html index 7c6c40099..7c119dff7 100644 --- a/docs/botsupport/sprite.html +++ b/docs/api/sprite.html @@ -4,7 +4,7 @@ -pyboy.botsupport.sprite API documentation +pyboy.api.sprite API documentation @@ -18,7 +18,7 @@
    -

    Module pyboy.botsupport.sprite

    +

    Module pyboy.api.sprite

    This class presents an interface to the sprites held in the OAM data on the Game Boy.

    @@ -56,7 +56,7 @@

    Module pyboy.botsupport.sprite

    call to `pyboy.PyBoy.tick`, so make sure to verify the `Sprite.tile_identifier` hasn't changed. By knowing the tile identifiers of players, enemies, power-ups and so on, you'll be able to search for them - using `pyboy.botsupport.BotSupportManager.sprite_by_tile_identifier` and feed it to your bot or AI. + using `pyboy.sprite_by_tile_identifier` and feed it to your bot or AI. """ assert 0 <= sprite_index < SPRITES, f"Sprite index of {sprite_index} is out of range (0-{SPRITES})" self.mb = mb @@ -99,7 +99,7 @@

    Module pyboy.botsupport.sprite

    self.tile_identifier = self.mb.getitem(OAM_OFFSET + self._offset + 2) """ The identifier of the tile the sprite uses. To get a better representation, see the method - `pyboy.botsupport.sprite.Sprite.tiles`. + `pyboy.api.sprite.Sprite.tiles`. For double-height sprites, this will only give the identifier of the first tile. The second tile will always be the one immediately following the first (`tile_identifier + 1`). @@ -114,7 +114,7 @@

    Module pyboy.botsupport.sprite

    self.attr_obj_bg_priority = _bit(attr, 7) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -125,7 +125,7 @@

    Module pyboy.botsupport.sprite

    self.attr_y_flip = _bit(attr, 6) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -136,7 +136,7 @@

    Module pyboy.botsupport.sprite

    self.attr_x_flip = _bit(attr, 5) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -144,10 +144,21 @@

    Module pyboy.botsupport.sprite

    The state of the bit in the attributes lookup. """ - self.attr_palette_number = _bit(attr, 4) + self.attr_palette_number = 0 """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). + + Returns + ------- + int: + The state of the bit(s) in the attributes lookup. + """ + + self.attr_cgb_bank_number = 0 + """ + To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -155,8 +166,14 @@

    Module pyboy.botsupport.sprite

    The state of the bit in the attributes lookup. """ + if self.mb.cgb: + self.attr_palette_number = attr & 0b111 + self.attr_cgb_bank_number = _bit(attr, 3) + else: + self.attr_palette_number = _bit(attr, 4) + LCDC = LCDCRegister(self.mb.getitem(LCDC_OFFSET)) - sprite_height = 16 if LCDC.sprite_height else 8 + sprite_height = 16 if LCDC._get_sprite_height() else 8 self.shape = (8, sprite_height) """ Sprites can be set to be 8x8 or 8x16 pixels (16 pixels tall). This is defined globally for the rendering @@ -176,12 +193,12 @@

    Module pyboy.botsupport.sprite

    immediately following the identifier given, and render it below the first. More information can be found in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam) + (OAM)](https://gbdev.io/pandocs/OAM.html) Returns ------- list: - A list of `pyboy.botsupport.tile.Tile` object(s) representing the graphics data for the sprite + A list of `pyboy.api.tile.Tile` object(s) representing the graphics data for the sprite """ if sprite_height == 16: self.tiles += [Tile(self.mb, self.tile_identifier + 1)] @@ -222,7 +239,7 @@

    Module pyboy.botsupport.sprite

    Classes

    -
    +
    class Sprite (mb, sprite_index)
    @@ -234,9 +251,9 @@

    Classes

    grid-size of 8x8 pixels precision, and can have no transparency.

    Sprites on the Game Boy are tightly associated with tiles. The sprites can be seen as "upgraded" tiles, as the image data still refers back to one (or two) tiles. The tile that a sprite will show, can change between each -call to PyBoy.tick(), so make sure to verify the Sprite.tile_identifier hasn't changed.

    +call to PyBoy.tick(), so make sure to verify the Sprite.tile_identifier hasn't changed.

    By knowing the tile identifiers of players, enemies, power-ups and so on, you'll be able to search for them -using BotSupportManager.sprite_by_tile_identifier() and feed it to your bot or AI.

    +using pyboy.sprite_by_tile_identifier and feed it to your bot or AI.

    Expand source code @@ -257,7 +274,7 @@

    Classes

    call to `pyboy.PyBoy.tick`, so make sure to verify the `Sprite.tile_identifier` hasn't changed. By knowing the tile identifiers of players, enemies, power-ups and so on, you'll be able to search for them - using `pyboy.botsupport.BotSupportManager.sprite_by_tile_identifier` and feed it to your bot or AI. + using `pyboy.sprite_by_tile_identifier` and feed it to your bot or AI. """ assert 0 <= sprite_index < SPRITES, f"Sprite index of {sprite_index} is out of range (0-{SPRITES})" self.mb = mb @@ -300,7 +317,7 @@

    Classes

    self.tile_identifier = self.mb.getitem(OAM_OFFSET + self._offset + 2) """ The identifier of the tile the sprite uses. To get a better representation, see the method - `pyboy.botsupport.sprite.Sprite.tiles`. + `pyboy.api.sprite.Sprite.tiles`. For double-height sprites, this will only give the identifier of the first tile. The second tile will always be the one immediately following the first (`tile_identifier + 1`). @@ -315,7 +332,7 @@

    Classes

    self.attr_obj_bg_priority = _bit(attr, 7) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -326,7 +343,7 @@

    Classes

    self.attr_y_flip = _bit(attr, 6) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -337,7 +354,7 @@

    Classes

    self.attr_x_flip = _bit(attr, 5) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -345,10 +362,21 @@

    Classes

    The state of the bit in the attributes lookup. """ - self.attr_palette_number = _bit(attr, 4) + self.attr_palette_number = 0 """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam). + (OAM)](https://gbdev.io/pandocs/OAM.html). + + Returns + ------- + int: + The state of the bit(s) in the attributes lookup. + """ + + self.attr_cgb_bank_number = 0 + """ + To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table + (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- @@ -356,8 +384,14 @@

    Classes

    The state of the bit in the attributes lookup. """ + if self.mb.cgb: + self.attr_palette_number = attr & 0b111 + self.attr_cgb_bank_number = _bit(attr, 3) + else: + self.attr_palette_number = _bit(attr, 4) + LCDC = LCDCRegister(self.mb.getitem(LCDC_OFFSET)) - sprite_height = 16 if LCDC.sprite_height else 8 + sprite_height = 16 if LCDC._get_sprite_height() else 8 self.shape = (8, sprite_height) """ Sprites can be set to be 8x8 or 8x16 pixels (16 pixels tall). This is defined globally for the rendering @@ -377,12 +411,12 @@

    Classes

    immediately following the identifier given, and render it below the first. More information can be found in the [Pan Docs: VRAM Sprite Attribute Table - (OAM)](http://bgb.bircd.org/pandocs.htm#vramspriteattributetableoam) + (OAM)](https://gbdev.io/pandocs/OAM.html) Returns ------- list: - A list of `pyboy.botsupport.tile.Tile` object(s) representing the graphics data for the sprite + A list of `pyboy.api.tile.Tile` object(s) representing the graphics data for the sprite """ if sprite_height == 16: self.tiles += [Tile(self.mb, self.tile_identifier + 1)] @@ -410,7 +444,7 @@

    Classes

    Instance variables

    -
    var y
    +
    var y

    The Y-coordinate on the screen to show the Sprite. The (x,y) coordinate points to the top-left corner of the sprite.

    Returns

    @@ -419,7 +453,7 @@

    Returns

    Y-coordinate
-
var x
+
var x

The X-coordinate on the screen to show the Sprite. The (x,y) coordinate points to the top-left corner of the sprite.

Returns

@@ -428,10 +462,10 @@

Returns

X-coordinate
-
var tile_identifier
+
var tile_identifier

The identifier of the tile the sprite uses. To get a better representation, see the method -Sprite.tiles.

+Sprite.tiles.

For double-height sprites, this will only give the identifier of the first tile. The second tile will always be the one immediately following the first (tile_identifier + 1).

Returns

@@ -440,9 +474,9 @@

Returns

unsigned tile index
-
var attr_obj_bg_priority
+
var attr_obj_bg_priority
-

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table +

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).

Returns

@@ -450,9 +484,9 @@

Returns

The state of the bit in the attributes lookup.
-
var attr_y_flip
+
var attr_y_flip
-

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table +

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).

Returns

@@ -460,9 +494,9 @@

Returns

The state of the bit in the attributes lookup.
-
var attr_x_flip
+
var attr_x_flip
-

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table +

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).

Returns

@@ -470,9 +504,19 @@

Returns

The state of the bit in the attributes lookup.
-
var attr_palette_number
+
var attr_palette_number
+
+

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table +(OAM).

+

Returns

+
+
int:
+
The state of the bit(s) in the attributes lookup.
+
+
+
var attr_cgb_bank_number
-

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table +

To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).

Returns

@@ -480,7 +524,7 @@

Returns

The state of the bit in the attributes lookup.
-
var shape
+
var shape

Sprites can be set to be 8x8 or 8x16 pixels (16 pixels tall). This is defined globally for the rendering hardware, so it's either all sprites using 8x16 pixels, or all sprites using 8x8 pixels.

@@ -488,20 +532,20 @@

Returns

(int, int): The width and height of the sprite.

-
var tiles
+
var tiles

The Game Boy support sprites of single-height (8x8 pixels) and double-height (8x16 pixels).

In the single-height format, one tile is used. For double-height sprites, the Game Boy will also use the tile immediately following the identifier given, and render it below the first.

-

More information can be found in the Pan Docs: VRAM Sprite Attribute Table +

More information can be found in the Pan Docs: VRAM Sprite Attribute Table (OAM)

Returns

list:
-
A list of Tile object(s) representing the graphics data for the sprite
+
A list of Tile object(s) representing the graphics data for the sprite
-
var on_screen
+
var on_screen

To disable sprites from being rendered on screen, developers will place the sprite outside the area of the screen. This is often a good way to determine if the sprite is inactive.

@@ -526,24 +570,25 @@

Index

  • Super-module

  • Classes

    diff --git a/docs/botsupport/tile.html b/docs/api/tile.html similarity index 62% rename from docs/botsupport/tile.html rename to docs/api/tile.html index 54fdfe878..e18332394 100644 --- a/docs/botsupport/tile.html +++ b/docs/api/tile.html @@ -4,9 +4,9 @@ -pyboy.botsupport.tile API documentation +pyboy.api.tile API documentation +`pyboy.api.sprite.Sprite` and …" /> @@ -19,11 +19,11 @@
    -

    Module pyboy.botsupport.tile

    +

    Module pyboy.api.tile

    The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used both for -Sprite and TileMap, when refering to graphics.

    +Sprite and TileMap, when refering to graphics.

    Expand source code @@ -34,50 +34,58 @@

    Module pyboy.botsupport.tile

    # """ The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used both for -`pyboy.botsupport.sprite.Sprite` and `pyboy.botsupport.tilemap.TileMap`, when refering to graphics. +`pyboy.api.sprite.Sprite` and `pyboy.api.tilemap.TileMap`, when refering to graphics. """ -import logging - import numpy as np + +import pyboy from pyboy import utils -from .constants import LOW_TILEDATA, VRAM_OFFSET +from .constants import LOW_TILEDATA, TILES, TILES_CGB, VRAM_OFFSET -logger = logging.getLogger(__name__) +logger = pyboy.logging.get_logger(__name__) try: from PIL import Image except ImportError: Image = None +try: + from cython import compiled + cythonmode = compiled +except ImportError: + cythonmode = False + class Tile: def __init__(self, mb, identifier): """ The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used for - `pyboy.botsupport.BotSupportManager.tile`, `pyboy.botsupport.sprite.Sprite` and `pyboy.botsupport.tilemap.TileMap`, when - refering to graphics. + `pyboy.tile`, `pyboy.api.sprite.Sprite` and `pyboy.api.tilemap.TileMap`, when refering to graphics. This class is not meant to be instantiated by developers reading this documentation, but it will be created - internally and returned by `pyboy.botsupport.sprite.Sprite.tiles` and - `pyboy.botsupport.tilemap.TileMap.tile`. + internally and returned by `pyboy.api.sprite.Sprite.tiles` and + `pyboy.api.tilemap.TileMap.tile`. The data of this class is static, apart from the image data, which is loaded from the Game Boy's memory when needed. Beware that the graphics for the tile can change between each call to `pyboy.PyBoy.tick`. """ self.mb = mb - assert 0 <= identifier < 384, "Identifier out of range" + if self.mb.cgb: + assert 0 <= identifier < TILES_CGB, "Identifier out of range" + else: + assert 0 <= identifier < TILES, "Identifier out of range" - self.data_address = LOW_TILEDATA + (16*identifier) + self.data_address = LOW_TILEDATA + (16 * (identifier%TILES)) """ The tile data is defined in a specific area of the Game Boy. This function returns the address of the tile data - corresponding to the tile identifier. It is advised to use `pyboy.botsupport.tile.Tile.image` or one of the + corresponding to the tile identifier. It is advised to use `pyboy.api.tile.Tile.image` or one of the other `image`-functions if you want to view the tile. You can read how the data is read in the - [Pan Docs: VRAM Tile Data](http://bgb.bircd.org/pandocs.htm#vramtiledata). + [Pan Docs: VRAM Tile Data](https://gbdev.io/pandocs/Tile_Data.html). Returns ------- @@ -85,10 +93,16 @@

    Module pyboy.botsupport.tile

    address in VRAM where tile data starts """ - self.tile_identifier = (self.data_address - LOW_TILEDATA) // 16 + if identifier < TILES: + self.vram_bank = 0 + else: + self.vram_bank = 1 + + self.tile_identifier = identifier """ The Game Boy has a slightly complicated indexing system for tiles. This identifier unifies the otherwise - complicated indexing system on the Game Boy into a single range of 0-383 (both included). + complicated indexing system on the Game Boy into a single range of 0-383 (both included) or 0-767 for Game Boy + Color. Returns ------- @@ -106,9 +120,19 @@

    Module pyboy.botsupport.tile

    The width and height of the tile. """ + self.raw_buffer_format = self.mb.lcd.renderer.color_format + """ + Returns the color format of the raw screen buffer. + + Returns + ------- + str: + Color format of the raw screen buffer. E.g. 'RGBA'. + """ + def image(self): """ - Use this function to get an easy-to-use `PIL.Image` object of the tile. The image is 8x8 pixels in RGBA colors. + Use this function to get an `PIL.Image` object of the tile. The image is 8x8 pixels. The format or "mode" might change at any time. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. @@ -120,13 +144,17 @@

    Module pyboy.botsupport.tile

    if Image is None: logger.error(f"{__name__}: Missing dependency \"Pillow\".") return None - return Image.frombytes("RGBA", (8, 8), bytes(self.image_data())) + + if cythonmode: + return Image.fromarray(self._image_data().base, mode=self.raw_buffer_format) + else: + return Image.frombytes(self.raw_buffer_format, (8, 8), self._image_data()) def image_ndarray(self): """ - Use this function to get an easy-to-use `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4) - and each value is of `numpy.uint8`. The values corresponds to and RGBA image of 8x8 pixels with each sub-color - in a separate cell. + Use this function to get an `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4) + and each value is of `numpy.uint8`. The values corresponds to an image of 8x8 pixels with each sub-color + in a separate cell. The format is given by `pyboy.api.tile.Tile.raw_buffer_format`. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. @@ -135,9 +163,11 @@

    Module pyboy.botsupport.tile

    numpy.ndarray : Array of shape (8, 8, 4) with data type of `numpy.uint8`. """ - return np.asarray(self.image_data()).view(dtype=np.uint8).reshape(8, 8, 4) + # The data is laid out as (X, red, green, blue), where X is currently always zero, but this is not guarenteed + # across versions of PyBoy. + return np.asarray(self._image_data()).view(dtype=np.uint8).reshape(8, 8, 4) - def image_data(self): + def _image_data(self): """ Use this function to get the raw tile data. The data is a `memoryview` corresponding to 8x8 pixels in RGBA colors. @@ -147,23 +177,25 @@

    Module pyboy.botsupport.tile

    Returns ------- memoryview : - Image data of tile in 8x8 pixels and RGBA colors. + Image data of tile in 8x8 pixels and RGB colors. """ self.data = np.zeros((8, 8), dtype=np.uint32) for k in range(0, 16, 2): # 2 bytes for each line - byte1 = self.mb.lcd.VRAM0[self.data_address + k - VRAM_OFFSET] - byte2 = self.mb.lcd.VRAM0[self.data_address + k + 1 - VRAM_OFFSET] + if self.vram_bank == 0: + byte1 = self.mb.lcd.VRAM0[self.data_address + k - VRAM_OFFSET] + byte2 = self.mb.lcd.VRAM0[self.data_address + k + 1 - VRAM_OFFSET] + else: + byte1 = self.mb.lcd.VRAM1[self.data_address + k - VRAM_OFFSET] + byte2 = self.mb.lcd.VRAM1[self.data_address + k + 1 - VRAM_OFFSET] for x in range(8): colorcode = utils.color_code(byte1, byte2, 7 - x) - # NOTE: ">> 8 | 0xFF000000" to keep compatibility with earlier code - old_A_format = 0xFF000000 - self.data[k // 2][x] = self.mb.lcd.BGP.getcolor(colorcode) >> 8 | old_A_format - + alpha_mask = 0x00FFFFFF + self.data[k // 2][x] = self.mb.lcd.BGP.getcolor(colorcode) & alpha_mask return self.data def __eq__(self, other): - return self.data_address == other.data_address + return self.data_address == other.data_address and self.vram_bank == other.vram_bank def __repr__(self): return f"Tile: {self.tile_identifier}" @@ -178,17 +210,16 @@

    Module pyboy.botsupport.tile

    Classes

    -
    +
    class Tile (mb, identifier)

    The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used for -BotSupportManager.tile(), Sprite and TileMap, when -refering to graphics.

    +pyboy.tile, Sprite and TileMap, when refering to graphics.

    This class is not meant to be instantiated by developers reading this documentation, but it will be created -internally and returned by Sprite.tiles and -TileMap.tile().

    +internally and returned by Sprite.tiles and +TileMap.tile().

    The data of this class is static, apart from the image data, which is loaded from the Game Boy's memory when needed. Beware that the graphics for the tile can change between each call to PyBoy.tick().

    @@ -199,28 +230,30 @@

    Classes

    def __init__(self, mb, identifier): """ The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used for - `pyboy.botsupport.BotSupportManager.tile`, `pyboy.botsupport.sprite.Sprite` and `pyboy.botsupport.tilemap.TileMap`, when - refering to graphics. + `pyboy.tile`, `pyboy.api.sprite.Sprite` and `pyboy.api.tilemap.TileMap`, when refering to graphics. This class is not meant to be instantiated by developers reading this documentation, but it will be created - internally and returned by `pyboy.botsupport.sprite.Sprite.tiles` and - `pyboy.botsupport.tilemap.TileMap.tile`. + internally and returned by `pyboy.api.sprite.Sprite.tiles` and + `pyboy.api.tilemap.TileMap.tile`. The data of this class is static, apart from the image data, which is loaded from the Game Boy's memory when needed. Beware that the graphics for the tile can change between each call to `pyboy.PyBoy.tick`. """ self.mb = mb - assert 0 <= identifier < 384, "Identifier out of range" + if self.mb.cgb: + assert 0 <= identifier < TILES_CGB, "Identifier out of range" + else: + assert 0 <= identifier < TILES, "Identifier out of range" - self.data_address = LOW_TILEDATA + (16*identifier) + self.data_address = LOW_TILEDATA + (16 * (identifier%TILES)) """ The tile data is defined in a specific area of the Game Boy. This function returns the address of the tile data - corresponding to the tile identifier. It is advised to use `pyboy.botsupport.tile.Tile.image` or one of the + corresponding to the tile identifier. It is advised to use `pyboy.api.tile.Tile.image` or one of the other `image`-functions if you want to view the tile. You can read how the data is read in the - [Pan Docs: VRAM Tile Data](http://bgb.bircd.org/pandocs.htm#vramtiledata). + [Pan Docs: VRAM Tile Data](https://gbdev.io/pandocs/Tile_Data.html). Returns ------- @@ -228,10 +261,16 @@

    Classes

    address in VRAM where tile data starts """ - self.tile_identifier = (self.data_address - LOW_TILEDATA) // 16 + if identifier < TILES: + self.vram_bank = 0 + else: + self.vram_bank = 1 + + self.tile_identifier = identifier """ The Game Boy has a slightly complicated indexing system for tiles. This identifier unifies the otherwise - complicated indexing system on the Game Boy into a single range of 0-383 (both included). + complicated indexing system on the Game Boy into a single range of 0-383 (both included) or 0-767 for Game Boy + Color. Returns ------- @@ -249,9 +288,19 @@

    Classes

    The width and height of the tile. """ + self.raw_buffer_format = self.mb.lcd.renderer.color_format + """ + Returns the color format of the raw screen buffer. + + Returns + ------- + str: + Color format of the raw screen buffer. E.g. 'RGBA'. + """ + def image(self): """ - Use this function to get an easy-to-use `PIL.Image` object of the tile. The image is 8x8 pixels in RGBA colors. + Use this function to get an `PIL.Image` object of the tile. The image is 8x8 pixels. The format or "mode" might change at any time. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. @@ -263,13 +312,17 @@

    Classes

    if Image is None: logger.error(f"{__name__}: Missing dependency \"Pillow\".") return None - return Image.frombytes("RGBA", (8, 8), bytes(self.image_data())) + + if cythonmode: + return Image.fromarray(self._image_data().base, mode=self.raw_buffer_format) + else: + return Image.frombytes(self.raw_buffer_format, (8, 8), self._image_data()) def image_ndarray(self): """ - Use this function to get an easy-to-use `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4) - and each value is of `numpy.uint8`. The values corresponds to and RGBA image of 8x8 pixels with each sub-color - in a separate cell. + Use this function to get an `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4) + and each value is of `numpy.uint8`. The values corresponds to an image of 8x8 pixels with each sub-color + in a separate cell. The format is given by `pyboy.api.tile.Tile.raw_buffer_format`. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. @@ -278,9 +331,11 @@

    Classes

    numpy.ndarray : Array of shape (8, 8, 4) with data type of `numpy.uint8`. """ - return np.asarray(self.image_data()).view(dtype=np.uint8).reshape(8, 8, 4) + # The data is laid out as (X, red, green, blue), where X is currently always zero, but this is not guarenteed + # across versions of PyBoy. + return np.asarray(self._image_data()).view(dtype=np.uint8).reshape(8, 8, 4) - def image_data(self): + def _image_data(self): """ Use this function to get the raw tile data. The data is a `memoryview` corresponding to 8x8 pixels in RGBA colors. @@ -290,67 +345,79 @@

    Classes

    Returns ------- memoryview : - Image data of tile in 8x8 pixels and RGBA colors. + Image data of tile in 8x8 pixels and RGB colors. """ self.data = np.zeros((8, 8), dtype=np.uint32) for k in range(0, 16, 2): # 2 bytes for each line - byte1 = self.mb.lcd.VRAM0[self.data_address + k - VRAM_OFFSET] - byte2 = self.mb.lcd.VRAM0[self.data_address + k + 1 - VRAM_OFFSET] + if self.vram_bank == 0: + byte1 = self.mb.lcd.VRAM0[self.data_address + k - VRAM_OFFSET] + byte2 = self.mb.lcd.VRAM0[self.data_address + k + 1 - VRAM_OFFSET] + else: + byte1 = self.mb.lcd.VRAM1[self.data_address + k - VRAM_OFFSET] + byte2 = self.mb.lcd.VRAM1[self.data_address + k + 1 - VRAM_OFFSET] for x in range(8): colorcode = utils.color_code(byte1, byte2, 7 - x) - # NOTE: ">> 8 | 0xFF000000" to keep compatibility with earlier code - old_A_format = 0xFF000000 - self.data[k // 2][x] = self.mb.lcd.BGP.getcolor(colorcode) >> 8 | old_A_format - + alpha_mask = 0x00FFFFFF + self.data[k // 2][x] = self.mb.lcd.BGP.getcolor(colorcode) & alpha_mask return self.data def __eq__(self, other): - return self.data_address == other.data_address + return self.data_address == other.data_address and self.vram_bank == other.vram_bank def __repr__(self): return f"Tile: {self.tile_identifier}"

    Instance variables

    -
    var data_address
    +
    var data_address

    The tile data is defined in a specific area of the Game Boy. This function returns the address of the tile data -corresponding to the tile identifier. It is advised to use Tile.image() or one of the +corresponding to the tile identifier. It is advised to use Tile.image() or one of the other image-functions if you want to view the tile.

    You can read how the data is read in the -Pan Docs: VRAM Tile Data.

    +Pan Docs: VRAM Tile Data.

    Returns

    int:
    address in VRAM where tile data starts
    -
    var tile_identifier
    +
    var tile_identifier

    The Game Boy has a slightly complicated indexing system for tiles. This identifier unifies the otherwise -complicated indexing system on the Game Boy into a single range of 0-383 (both included).

    +complicated indexing system on the Game Boy into a single range of 0-383 (both included) or 0-767 for Game Boy +Color.

    Returns

    int:
    Unique identifier for the tile
    -
    var shape
    +
    var shape

    Tiles are always 8x8 pixels.

    Returns

    (int, int): The width and height of the tile.

    +
    var raw_buffer_format
    +
    +

    Returns the color format of the raw screen buffer.

    +

    Returns

    +
    +
    str:
    +
    Color format of the raw screen buffer. E.g. 'RGBA'.
    +
    +

    Methods

    -
    +
    def image(self)
    -

    Use this function to get an easy-to-use PIL.Image object of the tile. The image is 8x8 pixels in RGBA colors.

    +

    Use this function to get an PIL.Image object of the tile. The image is 8x8 pixels. The format or "mode" might change at any time.

    Be aware, that the graphics for this tile can change between each call to PyBoy.tick().

    Returns

    @@ -363,7 +430,7 @@

    Returns

    def image(self):
         """
    -    Use this function to get an easy-to-use `PIL.Image` object of the tile. The image is 8x8 pixels in RGBA colors.
    +    Use this function to get an `PIL.Image` object of the tile. The image is 8x8 pixels. The format or "mode" might change at any time.
     
         Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`.
     
    @@ -375,16 +442,20 @@ 

    Returns

    if Image is None: logger.error(f"{__name__}: Missing dependency \"Pillow\".") return None - return Image.frombytes("RGBA", (8, 8), bytes(self.image_data()))
    + + if cythonmode: + return Image.fromarray(self._image_data().base, mode=self.raw_buffer_format) + else: + return Image.frombytes(self.raw_buffer_format, (8, 8), self._image_data())
-
+
def image_ndarray(self)
-

Use this function to get an easy-to-use numpy.ndarray object of the tile. The array has a shape of (8, 8, 4) -and each value is of numpy.uint8. The values corresponds to and RGBA image of 8x8 pixels with each sub-color -in a separate cell.

+

Use this function to get an numpy.ndarray object of the tile. The array has a shape of (8, 8, 4) +and each value is of numpy.uint8. The values corresponds to an image of 8x8 pixels with each sub-color +in a separate cell. The format is given by Tile.raw_buffer_format.

Be aware, that the graphics for this tile can change between each call to PyBoy.tick().

Returns

@@ -397,9 +468,9 @@

Returns

def image_ndarray(self):
     """
-    Use this function to get an easy-to-use `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4)
-    and each value is of `numpy.uint8`. The values corresponds to and RGBA image of 8x8 pixels with each sub-color
-    in a separate cell.
+    Use this function to get an `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4)
+    and each value is of `numpy.uint8`. The values corresponds to an image of 8x8 pixels with each sub-color
+    in a separate cell. The format is given by `pyboy.api.tile.Tile.raw_buffer_format`.
 
     Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`.
 
@@ -408,49 +479,9 @@ 

Returns

numpy.ndarray : Array of shape (8, 8, 4) with data type of `numpy.uint8`. """ - return np.asarray(self.image_data()).view(dtype=np.uint8).reshape(8, 8, 4)
- -
-
-def image_data(self) -
-
-

Use this function to get the raw tile data. The data is a memoryview corresponding to 8x8 pixels in RGBA -colors.

-

Be aware, that the graphics for this tile can change between each call to PyBoy.tick().

-

Returns

-
-
memoryview :
-
Image data of tile in 8x8 pixels and RGBA colors.
-
-
- -Expand source code - -
def image_data(self):
-    """
-    Use this function to get the raw tile data. The data is a `memoryview` corresponding to 8x8 pixels in RGBA
-    colors.
-
-    Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`.
-
-    Returns
-    -------
-    memoryview :
-        Image data of tile in 8x8 pixels and RGBA colors.
-    """
-    self.data = np.zeros((8, 8), dtype=np.uint32)
-    for k in range(0, 16, 2): # 2 bytes for each line
-        byte1 = self.mb.lcd.VRAM0[self.data_address + k - VRAM_OFFSET]
-        byte2 = self.mb.lcd.VRAM0[self.data_address + k + 1 - VRAM_OFFSET]
-
-        for x in range(8):
-            colorcode = utils.color_code(byte1, byte2, 7 - x)
-            # NOTE: ">> 8 | 0xFF000000" to keep compatibility with earlier code
-            old_A_format = 0xFF000000
-            self.data[k // 2][x] = self.mb.lcd.BGP.getcolor(colorcode) >> 8 | old_A_format
-
-    return self.data
+ # The data is laid out as (X, red, green, blue), where X is currently always zero, but this is not guarenteed + # across versions of PyBoy. + return np.asarray(self._image_data()).view(dtype=np.uint8).reshape(8, 8, 4)
@@ -466,20 +497,20 @@

Index

+

Class variables

+
+
var cartridge_title
+
+
+
+
var mapping_minimal
+
+

Example mapping of 1:1

+
+

Methods

@@ -536,7 +538,10 @@

Args

""" if not self.pyboy.frame_count == 0: - logger.warning("Calling start_game from an already running game. This might not work.")
+ logger.warning("Calling start_game from an already running game. This might not work.") + self.game_has_started = True + self.saved_state.seek(0) + self.pyboy.save_state(self.saved_state)
@@ -621,7 +626,7 @@

Returns

memoryview: Simplified 2-dimensional memoryview of the screen """ - tiles_matrix = self._game_area_tiles() + tiles_matrix = self.mapping[self._game_area_tiles()] sprites = self._sprites_on_screen() xx = self.game_area_section[0] yy = self.game_area_section[1] @@ -631,11 +636,25 @@

Returns

_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 + self.sprite_offset # Adding offset to try to seperate sprites from tiles + tiles_matrix[_y][_x] = self.mapping[ + s.tile_identifier] + self.sprite_offset # Adding offset to try to seperate sprites from tiles return tiles_matrix
+
+def game_area_mapping(self, mapping, sprite_offest) +
+
+
+
+ +Expand source code + +
def game_area_mapping(self, mapping, sprite_offest):
+    self.mapping = np.asarray(mapping, dtype=np.uint32)
+    self.sprite_offset = sprite_offest
+
+
@@ -656,12 +675,15 @@

Index

  • PyBoyGameWrapper

    - diff --git a/docs/plugins/game_wrapper_kirby_dream_land.html b/docs/plugins/game_wrapper_kirby_dream_land.html index 0140e758e..480c9dd91 100644 --- a/docs/plugins/game_wrapper_kirby_dream_land.html +++ b/docs/plugins/game_wrapper_kirby_dream_land.html @@ -34,24 +34,18 @@

    Module pyboy.plugins.game_wrapper_kirby_dream_landModule pyboy.plugins.game_wrapper_kirby_dream_landModule pyboy.plugins.game_wrapper_kirby_dream_landModule pyboy.plugins.game_wrapper_kirby_dream_landClasses

    (*args, **kwargs)
    -

    This class wraps Kirby Dream Land, and provides easy access to score and a "fitness" score for AIs.

    +

    This class wraps Kirby Dream Land, and provides easy access for AIs.

    If you call print on an instance of this object, it will show an overview of everything this object provides.

    @@ -240,7 +215,7 @@

    Classes

    class GameWrapperKirbyDreamLand(PyBoyGameWrapper):
         """
    -    This class wraps Kirby Dream Land, and provides easy access to score and a "fitness" score for AIs.
    +    This class wraps Kirby Dream Land, and provides easy access for AIs.
     
         If you call `print` on an instance of this object, it will show an overview of everything this object provides.
         """
    @@ -257,35 +232,26 @@ 

    Classes

    """The lives remaining provided by the game""" self._game_over = False """The game over state""" - self.fitness = 0 - """ - A built-in fitness scoring. Taking score, health, and lives left into account. - .. math:: - fitness = score \\cdot health \\cdot lives\\_left - """ - super().__init__(*args, game_area_section=(0, 0) + self.shape, game_area_wrap_around=True, **kwargs) + super().__init__(*args, game_area_section=(0, 0) + self.shape, game_area_follow_scxy=True, **kwargs) def post_tick(self): self._tile_cache_invalid = True self._sprite_cache_invalid = True self.score = 0 - score_digits = 5 + score_digits = 4 for n in range(score_digits): - self.score += self.pyboy.get_memory_value(0xD06F + n) * 10**(score_digits-n) + self.score += self.pyboy.memory[0xD070 + n] * 10**(score_digits - n) # Check if game is over prev_health = self.health - self.health = self.pyboy.get_memory_value(0xD086) + self.health = self.pyboy.memory[0xD086] if self.lives_left == 0: if prev_health > 0 and self.health == 0: self._game_over = True - self.lives_left = self.pyboy.get_memory_value(0xD089) - 1 - - if self.game_has_started: - self.fitness = self.score * self.health * self.lives_left + self.lives_left = self.pyboy.memory[0xD089] - 1 def start_game(self, timer_div=None): """ @@ -302,33 +268,24 @@

    Classes

    # Boot screen while True: - self.pyboy.tick() - self.tilemap_background.refresh_lcdc() + self.pyboy.tick(1, False) if self.tilemap_background[0:3, 16] == [231, 224, 235]: # 'HAL' on the first screen break # Wait for transition to finish (start screen) - for _ in range(25): - self.pyboy.tick() - - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) + self.pyboy.tick(25, False) + self.pyboy.button("start") self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) # Wait for transition to finish (exit start screen, enter level intro screen) - for _ in range(60): - self.pyboy.tick() + self.pyboy.tick(60, False) # Skip level intro - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) + self.pyboy.button("start") self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) # Wait for transition to finish (exit level intro screen, enter game) - for _ in range(60): - self.pyboy.tick() - - self.game_has_started = True + self.pyboy.tick(60, False) self.saved_state.seek(0) self.pyboy.save_state(self.saved_state) @@ -389,7 +346,6 @@

    Classes

    f"Score: {self.score}\n" + f"Health: {self.health}\n" + f"Lives left: {self.lives_left}\n" + - f"Fitness: {self.fitness}\n" + "Sprites on screen:\n" + "\n".join([str(s) for s in self._sprites_on_screen()]) + "\n" + @@ -429,11 +385,6 @@

    Instance variables

    The lives remaining provided by the game

    -
    var fitness
    -
    -

    A built-in fitness scoring. Taking score, health, and lives left into account.

    -

    fitness = score \cdot health \cdot lives\_left

    -

    Methods

    @@ -466,33 +417,24 @@

    Kwargs

    # Boot screen while True: - self.pyboy.tick() - self.tilemap_background.refresh_lcdc() + self.pyboy.tick(1, False) if self.tilemap_background[0:3, 16] == [231, 224, 235]: # 'HAL' on the first screen break # Wait for transition to finish (start screen) - for _ in range(25): - self.pyboy.tick() - - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) + self.pyboy.tick(25, False) + self.pyboy.button("start") self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) # Wait for transition to finish (exit start screen, enter level intro screen) - for _ in range(60): - self.pyboy.tick() + self.pyboy.tick(60, False) # Skip level intro - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) + self.pyboy.button("start") self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) # Wait for transition to finish (exit level intro screen, enter game) - for _ in range(60): - self.pyboy.tick() - - self.game_has_started = True + self.pyboy.tick(60, False) self.saved_state.seek(0) self.pyboy.save_state(self.saved_state) @@ -595,6 +537,7 @@

    Inherited members

  • PyBoyGameWrapper:
@@ -625,7 +568,6 @@

score
  • health
  • lives_left
  • -
  • fitness
  • diff --git a/docs/plugins/game_wrapper_pokemon_gen1.html b/docs/plugins/game_wrapper_pokemon_gen1.html index 12b1630d9..1590bd537 100644 --- a/docs/plugins/game_wrapper_pokemon_gen1.html +++ b/docs/plugins/game_wrapper_pokemon_gen1.html @@ -34,21 +34,15 @@

    Module pyboy.plugins.game_wrapper_pokemon_gen1Module pyboy.plugins.game_wrapper_pokemon_gen1Classes

    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 + super().__init__(*args, game_area_section=(0, 0) + self.shape, game_area_follow_scxy=True, **kwargs) + self.sprite_offset = 0 def enabled(self): - return self.pyboy_argv.get("game_wrapper") and ((self.pyboy.cartridge_title() == "POKEMON RED") or - (self.pyboy.cartridge_title() == "POKEMON BLUE")) + return (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() + scanline_parameters = self.pyboy.screen.tilemap_position_list WX = scanline_parameters[0][2] WY = scanline_parameters[0][3] self.use_background(WY != 0) def _get_screen_background_tilemap(self): - ### SIMILAR TO CURRENT pyboy.game_wrapper()._game_area_np(), BUT ONLY FOR BACKGROUND TILEMAP, SO NPC ARE SKIPPED + ### SIMILAR TO CURRENT pyboy.game_wrapper.game_area(), BUT ONLY FOR BACKGROUND TILEMAP, SO NPC ARE SKIPPED bsm = self.pyboy.botsupport_manager() ((scx, scy), (wx, wy)) = bsm.screen().tilemap_position() - tilemap = np.array(bsm.tilemap_background()[:, :]) + tilemap = np.array(bsm.tilemap_background[:, :]) return np.roll(np.roll(tilemap, -scy // 8, axis=0), -scx // 8, axis=1)[:18, :20] def _get_screen_walkable_matrix(self): walkable_tiles_indexes = [] - collision_ptr = self.pyboy.get_memory_value(0xD530) + (self.pyboy.get_memory_value(0xD531) << 8) - tileset_type = self.pyboy.get_memory_value(0xFFD7) + collision_ptr = self.pyboy.memory[0xD530] + (self.pyboy.memory[0xD531] << 8) + tileset_type = self.pyboy.memory[0xFFD7] if tileset_type > 0: - grass_tile_index = self.pyboy.get_memory_value(0xD535) + grass_tile_index = self.pyboy.memory[0xD535] if grass_tile_index != 0xFF: walkable_tiles_indexes.append(grass_tile_index + 0x100) for i in range(0x180): - tile_index = self.pyboy.get_memory_value(collision_ptr + i) + tile_index = self.pyboy.memory[collision_ptr + i] if tile_index == 0xFF: break else: @@ -257,8 +249,7 @@

    Methods

    Expand source code
    def enabled(self):
    -    return self.pyboy_argv.get("game_wrapper") and ((self.pyboy.cartridge_title() == "POKEMON RED") or
    -                                                    (self.pyboy.cartridge_title() == "POKEMON BLUE"))
    + return (self.pyboy.cartridge_title == "POKEMON RED") or (self.pyboy.cartridge_title == "POKEMON BLUE")
    @@ -289,6 +280,7 @@

    Inherited members

    diff --git a/docs/plugins/game_wrapper_super_mario_land.html b/docs/plugins/game_wrapper_super_mario_land.html index bc127196b..d885b015c 100644 --- a/docs/plugins/game_wrapper_super_mario_land.html +++ b/docs/plugins/game_wrapper_super_mario_land.html @@ -34,20 +34,14 @@

    Module pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landModule pyboy.plugins.game_wrapper_super_mario_landClasses

    (*args, **kwargs)
    -

    This class wraps Super Mario Land, and provides easy access to score, coins, lives left, time left, world and a -"fitness" score for AIs.

    +

    This class wraps Super Mario Land, and provides easy access to score, coins, lives left, time left and world for AIs.

    Only world 1-1 is officially supported at the moment. Support for more worlds coming soon.

    If you call print on an instance of this object, it will show an overview of everything this object provides.

    @@ -394,17 +373,21 @@

    Classes

    class GameWrapperSuperMarioLand(PyBoyGameWrapper):
         """
    -    This class wraps Super Mario Land, and provides easy access to score, coins, lives left, time left, world and a
    -    "fitness" score for AIs.
    +    This class wraps Super Mario Land, and provides easy access to score, coins, lives left, time left and world for AIs.
     
         __Only world 1-1 is officially supported at the moment. Support for more worlds coming soon.__
     
         If you call `print` on an instance of this object, it will show an overview of everything this object provides.
         """
         cartridge_title = "SUPER MARIOLAN"
    -    tiles_compressed = tiles_compressed
    -    tiles_minimal = tiles_minimal
    -
    +    mapping_compressed = mapping_compressed
    +    """
    +    Compressed mapping for `pyboy.PyBoy.game_area_mapping`
    +    """
    +    mapping_minimal = mapping_minimal
    +    """
    +    Minimal mapping for `pyboy.PyBoy.game_area_mapping`
    +    """
         def __init__(self, *args, **kwargs):
             self.shape = (20, 16)
             """The shape of the game area"""
    @@ -420,39 +403,26 @@ 

    Classes

    """The number of seconds left to finish the level""" self.level_progress = 0 """An integer of the current "global" X position in this level. Can be used for AI scoring.""" - self._level_progress_max = 0 - self.fitness = 0 - """ - A built-in fitness scoring. Taking points, level progression, time left, and lives left into account. - - .. math:: - fitness = (lives\\_left \\cdot 10000) + (score + time\\_left \\cdot 10) + (\\_level\\_progress\\_max \\cdot 10) - """ - super().__init__(*args, game_area_section=(0, 2) + self.shape, game_area_wrap_around=True, **kwargs) + super().__init__(*args, game_area_section=(0, 2) + self.shape, game_area_follow_scxy=True, **kwargs) def post_tick(self): self._tile_cache_invalid = True self._sprite_cache_invalid = True - world_level = self.pyboy.get_memory_value(ADDR_WORLD_LEVEL) + world_level = self.pyboy.memory[ADDR_WORLD_LEVEL] self.world = world_level >> 4, world_level & 0x0F blank = 300 self.coins = self._sum_number_on_screen(9, 1, 2, blank, -256) - self.lives_left = _bcm_to_dec(self.pyboy.get_memory_value(ADDR_LIVES_LEFT)) + self.lives_left = _bcm_to_dec(self.pyboy.memory[ADDR_LIVES_LEFT]) self.score = self._sum_number_on_screen(0, 1, 6, blank, -256) self.time_left = self._sum_number_on_screen(17, 1, 3, blank, -256) - level_block = self.pyboy.get_memory_value(0xC0AB) - mario_x = self.pyboy.get_memory_value(0xC202) - scx = self.pyboy.botsupport_manager().screen().tilemap_position_list()[16][0] + level_block = self.pyboy.memory[0xC0AB] + mario_x = self.pyboy.memory[0xC202] + scx = self.pyboy.screen.tilemap_position_list[16][0] self.level_progress = level_block*16 + (scx-7) % 16 + mario_x - if self.game_has_started: - self._level_progress_max = max(self.level_progress, self._level_progress_max) - end_score = self.score + self.time_left * 10 - self.fitness = self.lives_left * 10000 + end_score + self._level_progress_max * 10 - def set_lives_left(self, amount): """ Set the amount lives to any number between 0 and 99. @@ -468,11 +438,11 @@

    Classes

    if 0 <= amount <= 99: tens = amount // 10 ones = amount % 10 - self.pyboy.set_memory_value(ADDR_LIVES_LEFT, (tens << 4) | ones) - self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY, tens) - self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY + 1, ones) + self.pyboy.memory[ADDR_LIVES_LEFT] = (tens << 4) | ones + self.pyboy.memory[ADDR_LIVES_LEFT_DISPLAY] = tens + self.pyboy.memory[ADDR_LIVES_LEFT_DISPLAY + 1] = ones else: - logger.error(f"{amount} is out of bounds. Only values between 0 and 99 allowed.") + logger.error("%d is out of bounds. Only values between 0 and 99 allowed.", amount) def set_world_level(self, world, level): """ @@ -484,7 +454,7 @@

    Classes

    """ for i in range(0x450, 0x461): - self.pyboy.override_memory_value(0, i, 0x00) + self.pyboy.memory[0, i] = 0x00 patch1 = [ 0x3E, # LD A, d8 @@ -492,7 +462,7 @@

    Classes

    ] for i, byte in enumerate(patch1): - self.pyboy.override_memory_value(0, 0x451 + i, byte) + self.pyboy.memory[0, 0x451 + i] = byte def start_game(self, timer_div=None, world_level=None, unlock_level_select=False): """ @@ -520,28 +490,24 @@

    Classes

    # Boot screen while True: - self.pyboy.tick() + self.pyboy.tick(1, False) if self.tilemap_background[6:11, 13] == [284, 285, 266, 283, 285]: # "START" on the main menu break - self.pyboy.tick() - self.pyboy.tick() - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) + self.pyboy.tick(3, False) + self.pyboy.button("start") + self.pyboy.tick(1, False) while True: if unlock_level_select and self.pyboy.frame_count == 71: # An arbitrary frame count, where the write will work - self.pyboy.set_memory_value(ADDR_WIN_COUNT, 2 if unlock_level_select else 0) + self.pyboy.memory[ADDR_WIN_COUNT] = 2 if unlock_level_select else 0 break - self.pyboy.tick() - self.tilemap_background.refresh_lcdc() + self.pyboy.tick(1, False) # "MARIO" in the title bar and 0 is placed at score if self.tilemap_background[0:5, 0] == [278, 266, 283, 274, 280] and \ self.tilemap_background[5, 1] == 256: - self.game_has_started = True + # Game has started break self.saved_state.seek(0) @@ -603,7 +569,7 @@

    Classes

    def game_over(self): # Apparantly that address is for game over # https://datacrystal.romhacking.net/wiki/Super_Mario_Land:RAM_map - return self.pyboy.get_memory_value(0xC0A4) == 0x39 + return self.pyboy.memory[0xC0A4] == 0x39 def __repr__(self): adjust = 4 @@ -615,7 +581,6 @@

    Classes

    f"Score: {self.score}\n" + f"Time left: {self.time_left}\n" + f"Level progress: {self.level_progress}\n" + - f"Fitness: {self.fitness}\n" + "Sprites on screen:\n" + "\n".join([str(s) for s in self._sprites_on_screen()]) + "\n" + @@ -639,13 +604,9 @@

    Ancestors

    Class variables

    -
    var tiles_compressed
    -
    -
    -
    -
    var tiles_minimal
    +
    var mapping_compressed
    -
    +

    Compressed mapping for PyBoy.game_area_mapping()

    Instance variables

    @@ -678,11 +639,6 @@

    Instance variables

    An integer of the current "global" X position in this level. Can be used for AI scoring.

    -
    var fitness
    -
    -

    A built-in fitness scoring. Taking points, level progression, time left, and lives left into account.

    -

    fitness = (lives\_left \cdot 10000) + (score + time\_left \cdot 10) + (\_level\_progress\_max \cdot 10)

    -

    Methods

    @@ -716,11 +672,11 @@

    Args

    if 0 <= amount <= 99: tens = amount // 10 ones = amount % 10 - self.pyboy.set_memory_value(ADDR_LIVES_LEFT, (tens << 4) | ones) - self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY, tens) - self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY + 1, ones) + self.pyboy.memory[ADDR_LIVES_LEFT] = (tens << 4) | ones + self.pyboy.memory[ADDR_LIVES_LEFT_DISPLAY] = tens + self.pyboy.memory[ADDR_LIVES_LEFT_DISPLAY + 1] = ones else: - logger.error(f"{amount} is out of bounds. Only values between 0 and 99 allowed.")
    + logger.error("%d is out of bounds. Only values between 0 and 99 allowed.", amount)
    @@ -749,7 +705,7 @@

    Args

    """ for i in range(0x450, 0x461): - self.pyboy.override_memory_value(0, i, 0x00) + self.pyboy.memory[0, i] = 0x00 patch1 = [ 0x3E, # LD A, d8 @@ -757,7 +713,7 @@

    Args

    ] for i, byte in enumerate(patch1): - self.pyboy.override_memory_value(0, 0x451 + i, byte)
    + self.pyboy.memory[0, 0x451 + i] = byte
    @@ -806,28 +762,24 @@

    Kwargs

    # Boot screen while True: - self.pyboy.tick() + self.pyboy.tick(1, False) if self.tilemap_background[6:11, 13] == [284, 285, 266, 283, 285]: # "START" on the main menu break - self.pyboy.tick() - self.pyboy.tick() - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) + self.pyboy.tick(3, False) + self.pyboy.button("start") + self.pyboy.tick(1, False) while True: if unlock_level_select and self.pyboy.frame_count == 71: # An arbitrary frame count, where the write will work - self.pyboy.set_memory_value(ADDR_WIN_COUNT, 2 if unlock_level_select else 0) + self.pyboy.memory[ADDR_WIN_COUNT] = 2 if unlock_level_select else 0 break - self.pyboy.tick() - self.tilemap_background.refresh_lcdc() + self.pyboy.tick(1, False) # "MARIO" in the title bar and 0 is placed at score if self.tilemap_background[0:5, 0] == [278, 266, 283, 274, 280] and \ self.tilemap_background[5, 1] == 256: - self.game_has_started = True + # Game has started break self.saved_state.seek(0) @@ -945,6 +897,7 @@

    Inherited members

  • PyBoyGameWrapper:
  • @@ -980,9 +933,7 @@

    score
  • time_left
  • level_progress
  • -
  • fitness
  • -
  • tiles_compressed
  • -
  • tiles_minimal
  • +
  • mapping_compressed
  • diff --git a/docs/plugins/game_wrapper_tetris.html b/docs/plugins/game_wrapper_tetris.html index 3d4a533e4..9878c3373 100644 --- a/docs/plugins/game_wrapper_tetris.html +++ b/docs/plugins/game_wrapper_tetris.html @@ -34,21 +34,16 @@

    Module pyboy.plugins.game_wrapper_tetris

    "GameWrapperTetris.post_tick": False, } -import logging from array import array import numpy as np + +import pyboy 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 +logger = pyboy.logging.get_logger(__name__) # Table for translating game-representation of Tetromino types (8-bit int) to string tetromino_table = { @@ -68,29 +63,34 @@

    Module pyboy.plugins.game_wrapper_tetris

    TILES = 384 # Compressed assigns an ID to each Tetromino type -tiles_compressed = np.zeros(TILES, dtype=np.uint8) +mapping_compressed = np.zeros(TILES, dtype=np.uint8) # BLANK, J, Z, O, L, T, S, I, BLACK tiles_types = [[47], [129], [130], [131], [132], [133], [134], [128, 136, 137, 138, 139, 143], [135]] for tiles_type_ID, tiles_type in enumerate(tiles_types): for tile_ID in tiles_type: - tiles_compressed[tile_ID] = tiles_type_ID + mapping_compressed[tile_ID] = tiles_type_ID # Minimal has 3 id's: Background, Tetromino and "losing tile" (which fills the board when losing) -tiles_minimal = np.ones(TILES, dtype=np.uint8) # For minimal everything is 1 -tiles_minimal[47] = 0 # Except BLANK which is 0 -tiles_minimal[135] = 2 # And background losing tiles BLACK which is 2 +mapping_minimal = np.ones(TILES, dtype=np.uint8) # For minimal everything is 1 +mapping_minimal[47] = 0 # Except BLANK which is 0 +mapping_minimal[135] = 2 # And background losing tiles BLACK which is 2 class GameWrapperTetris(PyBoyGameWrapper): """ - This class wraps Tetris, and provides easy access to score, lines, level and a "fitness" score for AIs. + This class wraps Tetris, and provides easy access to score, lines and level for AIs. If you call `print` on an instance of this object, it will show an overview of everything this object provides. """ cartridge_title = "TETRIS" - tiles_compressed = tiles_compressed - tiles_minimal = tiles_minimal - + mapping_compressed = mapping_compressed + """ + Compressed mapping for `pyboy.PyBoy.game_area_mapping` + """ + mapping_minimal = mapping_minimal + """ + Minimal mapping for `pyboy.PyBoy.game_area_mapping` + """ def __init__(self, *args, **kwargs): self.shape = (10, 18) """The shape of the game area""" @@ -100,25 +100,14 @@

    Module pyboy.plugins.game_wrapper_tetris

    """The current level""" self.lines = 0 """The number of cleared lines""" - self.fitness = 0 - """ - A built-in fitness scoring. The scoring is equals to `score`. - .. math:: - fitness = score - """ super().__init__(*args, **kwargs) ROWS, COLS = self.shape self._cached_game_area_tiles_raw = array("B", [0xFF] * (ROWS*COLS*4)) + self._cached_game_area_tiles = memoryview(self._cached_game_area_tiles_raw).cast("I", shape=(ROWS, COLS)) - if cythonmode: - self._cached_game_area_tiles = memoryview(self._cached_game_area_tiles_raw).cast("I", shape=(ROWS, COLS)) - else: - v = memoryview(self._cached_game_area_tiles_raw).cast("I") - self._cached_game_area_tiles = [v[i:i + COLS] for i in range(0, COLS * ROWS, COLS)] - - super().__init__(*args, game_area_section=(2, 0) + self.shape, game_area_wrap_around=True, **kwargs) + super().__init__(*args, game_area_section=(2, 0) + self.shape, game_area_follow_scxy=False, **kwargs) def _game_area_tiles(self): if self._tile_cache_invalid: @@ -131,14 +120,10 @@

    Module pyboy.plugins.game_wrapper_tetris

    self._sprite_cache_invalid = True blank = 47 - self.tilemap_background.refresh_lcdc() self.score = self._sum_number_on_screen(13, 3, 6, blank, 0) self.level = self._sum_number_on_screen(14, 7, 4, blank, 0) self.lines = self._sum_number_on_screen(14, 10, 4, blank, 0) - if self.game_has_started: - self.fitness = self.score - def start_game(self, timer_div=None): """ Call this function right after initializing PyBoy. This will navigate through menus to start the game at the @@ -155,25 +140,18 @@

    Module pyboy.plugins.game_wrapper_tetris

    # Boot screen while True: - self.pyboy.tick() - self.tilemap_background.refresh_lcdc() + self.pyboy.tick(1, False) if self.tilemap_background[2:9, 14] == [89, 25, 21, 10, 34, 14, 27]: # '1PLAYER' on the first screen break # Start game. Just press Start when the game allows us. for i in range(2): - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) - - for _ in range(6): - self.pyboy.tick() + self.pyboy.button("start") + self.pyboy.tick(7, False) self.saved_state.seek(0) self.pyboy.save_state(self.saved_state) - self.game_has_started = True - self.reset_game(timer_div=timer_div) def reset_game(self, timer_div=None): @@ -186,13 +164,8 @@

    Module pyboy.plugins.game_wrapper_tetris

    PyBoyGameWrapper.reset_game(self, timer_div=timer_div) self._set_timer_div(timer_div) - - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) - - for _ in range(6): - self.pyboy.tick() + self.pyboy.button("start") + self.pyboy.tick(7, False) def game_area(self): """ @@ -252,7 +225,7 @@

    Module pyboy.plugins.game_wrapper_tetris

    * `"T"`: T-shape """ # Bitmask, as the last two bits determine the direction - return inverse_tetromino_table[self.pyboy.get_memory_value(NEXT_TETROMINO_ADDR) & 0b11111100] + return inverse_tetromino_table[self.pyboy.memory[NEXT_TETROMINO_ADDR] & 0b11111100] def set_tetromino(self, shape): """ @@ -291,7 +264,7 @@

    Module pyboy.plugins.game_wrapper_tetris

    ] for i, byte in enumerate(patch1): - self.pyboy.override_memory_value(0, 0x206E + i, byte) + self.pyboy.memory[0, 0x206E + i] = byte patch2 = [ 0x3E, # LD A, Tetromino @@ -299,7 +272,7 @@

    Module pyboy.plugins.game_wrapper_tetris

    ] for i, byte in enumerate(patch2): - self.pyboy.override_memory_value(0, 0x20B0 + i, byte) + self.pyboy.memory[0, 0x20B0 + i] = byte def game_over(self): """ @@ -317,7 +290,6 @@

    Module pyboy.plugins.game_wrapper_tetris

    f"Score: {self.score}\n" + f"Level: {self.level}\n" + f"Lines: {self.lines}\n" + - f"Fitness: {self.fitness}\n" + "Sprites on screen:\n" + "\n".join([str(s) for s in self._sprites_on_screen()]) + "\n" + @@ -328,7 +300,7 @@

    Module pyboy.plugins.game_wrapper_tetris

    "\n".join( [ f"{i: <3}| " + "".join([str(tile).ljust(adjust) for tile in line]) - for i, line in enumerate(self._game_area_np()) + for i, line in enumerate(self.game_area()) ] ) ) @@ -349,7 +321,7 @@

    Classes

    (*args, **kwargs)
    -

    This class wraps Tetris, and provides easy access to score, lines, level and a "fitness" score for AIs.

    +

    This class wraps Tetris, and provides easy access to score, lines and level for AIs.

    If you call print on an instance of this object, it will show an overview of everything this object provides.

    @@ -357,14 +329,19 @@

    Classes

    class GameWrapperTetris(PyBoyGameWrapper):
         """
    -    This class wraps Tetris, and provides easy access to score, lines, level and a "fitness" score for AIs.
    +    This class wraps Tetris, and provides easy access to score, lines and level for AIs.
     
         If you call `print` on an instance of this object, it will show an overview of everything this object provides.
         """
         cartridge_title = "TETRIS"
    -    tiles_compressed = tiles_compressed
    -    tiles_minimal = tiles_minimal
    -
    +    mapping_compressed = mapping_compressed
    +    """
    +    Compressed mapping for `pyboy.PyBoy.game_area_mapping`
    +    """
    +    mapping_minimal = mapping_minimal
    +    """
    +    Minimal mapping for `pyboy.PyBoy.game_area_mapping`
    +    """
         def __init__(self, *args, **kwargs):
             self.shape = (10, 18)
             """The shape of the game area"""
    @@ -374,25 +351,14 @@ 

    Classes

    """The current level""" self.lines = 0 """The number of cleared lines""" - self.fitness = 0 - """ - A built-in fitness scoring. The scoring is equals to `score`. - .. math:: - fitness = score - """ super().__init__(*args, **kwargs) ROWS, COLS = self.shape self._cached_game_area_tiles_raw = array("B", [0xFF] * (ROWS*COLS*4)) + self._cached_game_area_tiles = memoryview(self._cached_game_area_tiles_raw).cast("I", shape=(ROWS, COLS)) - if cythonmode: - self._cached_game_area_tiles = memoryview(self._cached_game_area_tiles_raw).cast("I", shape=(ROWS, COLS)) - else: - v = memoryview(self._cached_game_area_tiles_raw).cast("I") - self._cached_game_area_tiles = [v[i:i + COLS] for i in range(0, COLS * ROWS, COLS)] - - super().__init__(*args, game_area_section=(2, 0) + self.shape, game_area_wrap_around=True, **kwargs) + super().__init__(*args, game_area_section=(2, 0) + self.shape, game_area_follow_scxy=False, **kwargs) def _game_area_tiles(self): if self._tile_cache_invalid: @@ -405,14 +371,10 @@

    Classes

    self._sprite_cache_invalid = True blank = 47 - self.tilemap_background.refresh_lcdc() self.score = self._sum_number_on_screen(13, 3, 6, blank, 0) self.level = self._sum_number_on_screen(14, 7, 4, blank, 0) self.lines = self._sum_number_on_screen(14, 10, 4, blank, 0) - if self.game_has_started: - self.fitness = self.score - def start_game(self, timer_div=None): """ Call this function right after initializing PyBoy. This will navigate through menus to start the game at the @@ -429,25 +391,18 @@

    Classes

    # Boot screen while True: - self.pyboy.tick() - self.tilemap_background.refresh_lcdc() + self.pyboy.tick(1, False) if self.tilemap_background[2:9, 14] == [89, 25, 21, 10, 34, 14, 27]: # '1PLAYER' on the first screen break # Start game. Just press Start when the game allows us. for i in range(2): - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) - - for _ in range(6): - self.pyboy.tick() + self.pyboy.button("start") + self.pyboy.tick(7, False) self.saved_state.seek(0) self.pyboy.save_state(self.saved_state) - self.game_has_started = True - self.reset_game(timer_div=timer_div) def reset_game(self, timer_div=None): @@ -460,13 +415,8 @@

    Classes

    PyBoyGameWrapper.reset_game(self, timer_div=timer_div) self._set_timer_div(timer_div) - - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) - - for _ in range(6): - self.pyboy.tick() + self.pyboy.button("start") + self.pyboy.tick(7, False) def game_area(self): """ @@ -526,7 +476,7 @@

    Classes

    * `"T"`: T-shape """ # Bitmask, as the last two bits determine the direction - return inverse_tetromino_table[self.pyboy.get_memory_value(NEXT_TETROMINO_ADDR) & 0b11111100] + return inverse_tetromino_table[self.pyboy.memory[NEXT_TETROMINO_ADDR] & 0b11111100] def set_tetromino(self, shape): """ @@ -565,7 +515,7 @@

    Classes

    ] for i, byte in enumerate(patch1): - self.pyboy.override_memory_value(0, 0x206E + i, byte) + self.pyboy.memory[0, 0x206E + i] = byte patch2 = [ 0x3E, # LD A, Tetromino @@ -573,7 +523,7 @@

    Classes

    ] for i, byte in enumerate(patch2): - self.pyboy.override_memory_value(0, 0x20B0 + i, byte) + self.pyboy.memory[0, 0x20B0 + i] = byte def game_over(self): """ @@ -591,7 +541,6 @@

    Classes

    f"Score: {self.score}\n" + f"Level: {self.level}\n" + f"Lines: {self.lines}\n" + - f"Fitness: {self.fitness}\n" + "Sprites on screen:\n" + "\n".join([str(s) for s in self._sprites_on_screen()]) + "\n" + @@ -602,7 +551,7 @@

    Classes

    "\n".join( [ f"{i: <3}| " + "".join([str(tile).ljust(adjust) for tile in line]) - for i, line in enumerate(self._game_area_np()) + for i, line in enumerate(self.game_area()) ] ) ) @@ -615,13 +564,9 @@

    Ancestors

    Class variables

    -
    var tiles_compressed
    +
    var mapping_compressed
    -
    -
    -
    var tiles_minimal
    -
    -
    +

    Compressed mapping for PyBoy.game_area_mapping()

    Instance variables

    @@ -642,11 +587,6 @@

    Instance variables

    The number of cleared lines

    -
    var fitness
    -
    -

    A built-in fitness scoring. The scoring is equals to score.

    -

    fitness = score

    -

    Methods

    @@ -680,25 +620,18 @@

    Kwargs

    # Boot screen while True: - self.pyboy.tick() - self.tilemap_background.refresh_lcdc() + self.pyboy.tick(1, False) if self.tilemap_background[2:9, 14] == [89, 25, 21, 10, 34, 14, 27]: # '1PLAYER' on the first screen break # Start game. Just press Start when the game allows us. for i in range(2): - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) - - for _ in range(6): - self.pyboy.tick() + self.pyboy.button("start") + self.pyboy.tick(7, False) self.saved_state.seek(0) self.pyboy.save_state(self.saved_state) - self.game_has_started = True - self.reset_game(timer_div=timer_div)
    @@ -723,13 +656,8 @@

    Kwargs

    PyBoyGameWrapper.reset_game(self, timer_div=timer_div) self._set_timer_div(timer_div) - - self.pyboy.send_input(WindowEvent.PRESS_BUTTON_START) - self.pyboy.tick() - self.pyboy.send_input(WindowEvent.RELEASE_BUTTON_START) - - for _ in range(6): - self.pyboy.tick()
    + self.pyboy.button("start") + self.pyboy.tick(7, False)
    @@ -852,7 +780,7 @@

    Returns

    * `"T"`: T-shape """ # Bitmask, as the last two bits determine the direction - return inverse_tetromino_table[self.pyboy.get_memory_value(NEXT_TETROMINO_ADDR) & 0b11111100]
    + return inverse_tetromino_table[self.pyboy.memory[NEXT_TETROMINO_ADDR] & 0b11111100]
    @@ -919,7 +847,7 @@

    Args

    ] for i, byte in enumerate(patch1): - self.pyboy.override_memory_value(0, 0x206E + i, byte) + self.pyboy.memory[0, 0x206E + i] = byte patch2 = [ 0x3E, # LD A, Tetromino @@ -927,7 +855,7 @@

    Args

    ] for i, byte in enumerate(patch2): - self.pyboy.override_memory_value(0, 0x20B0 + i, byte)
    + self.pyboy.memory[0, 0x20B0 + i] = byte
    @@ -950,6 +878,14 @@

    Args

    +

    Inherited members

    + @@ -980,9 +916,7 @@

    score
  • level
  • lines
  • -
  • fitness
  • -
  • tiles_compressed
  • -
  • tiles_minimal
  • +
  • mapping_compressed
  • diff --git a/docs/plugins/index.html b/docs/plugins/index.html index 414c1fe5e..9632b8256 100644 --- a/docs/plugins/index.html +++ b/docs/plugins/index.html @@ -36,19 +36,19 @@

    Module pyboy.plugins

    __pdoc__ = { # docs exclude - "window_headless": False, - "window_open_gl": False, - "screen_recorder": False, - "rewind": False, - "window_dummy": False, "disable_input": False, - "manager_gen": False, - "auto_pause": False, - "manager": False, - "record_replay": False, + "rewind": False, + "window_sdl2": False, "screenshot_recorder": False, + "debug_prompt": False, + "screen_recorder": False, "debug": False, - "window_sdl2": False, + "manager": False, + "record_replay": False, + "manager_gen": False, + "window_open_gl": False, + "auto_pause": False, + "window_null": False, # docs exclude end }
    diff --git a/docs/utils.html b/docs/utils.html new file mode 100644 index 000000000..34ddbd65d --- /dev/null +++ b/docs/utils.html @@ -0,0 +1,889 @@ + + + + + + +pyboy.utils API documentation + + + + + + + + + + +
    +
    +
    +

    Module pyboy.utils

    +
    +
    +
    + +Expand source code + +
    #
    +# License: See LICENSE.md file
    +# GitHub: https://github.com/Baekalfen/PyBoy
    +#
    +
    +__all__ = ["WindowEvent", "dec_to_bcd", "bcd_to_dec"]
    +
    +STATE_VERSION = 10
    +
    +##############################################################
    +# Buffer classes
    +
    +
    +class IntIOInterface:
    +    def __init__(self, buf):
    +        pass
    +
    +    def write(self, byte):
    +        raise Exception("Not implemented!")
    +
    +    def write_64bit(self, value):
    +        self.write(value & 0xFF)
    +        self.write((value >> 8) & 0xFF)
    +        self.write((value >> 16) & 0xFF)
    +        self.write((value >> 24) & 0xFF)
    +        self.write((value >> 32) & 0xFF)
    +        self.write((value >> 40) & 0xFF)
    +        self.write((value >> 48) & 0xFF)
    +        self.write((value >> 56) & 0xFF)
    +
    +    def read_64bit(self):
    +        a = self.read()
    +        b = self.read()
    +        c = self.read()
    +        d = self.read()
    +        e = self.read()
    +        f = self.read()
    +        g = self.read()
    +        h = self.read()
    +        return a | (b << 8) | (c << 16) | (d << 24) | (e << 32) | (f << 40) | (g << 48) | (h << 56)
    +
    +    def write_32bit(self, value):
    +        self.write(value & 0xFF)
    +        self.write((value >> 8) & 0xFF)
    +        self.write((value >> 16) & 0xFF)
    +        self.write((value >> 24) & 0xFF)
    +
    +    def read_32bit(self):
    +        a = self.read()
    +        b = self.read()
    +        c = self.read()
    +        d = self.read()
    +        return int(a | (b << 8) | (c << 16) | (d << 24))
    +
    +    def write_16bit(self, value):
    +        self.write(value & 0xFF)
    +        self.write((value >> 8) & 0xFF)
    +
    +    def read_16bit(self):
    +        a = self.read()
    +        b = self.read()
    +        return int(a | (b << 8))
    +
    +    def read(self):
    +        raise Exception("Not implemented!")
    +
    +    def seek(self, pos):
    +        raise Exception("Not implemented!")
    +
    +    def flush(self):
    +        raise Exception("Not implemented!")
    +
    +    def new(self):
    +        raise Exception("Not implemented!")
    +
    +    def commit(self):
    +        raise Exception("Not implemented!")
    +
    +    def seek_frame(self, _):
    +        raise Exception("Not implemented!")
    +
    +    def tell(self):
    +        raise Exception("Not implemented!")
    +
    +
    +class IntIOWrapper(IntIOInterface):
    +    """
    +    Wraps a file-like object to allow writing integers to it.
    +    This allows for higher performance, when writing to a memory map in rewind.
    +    """
    +    def __init__(self, buf):
    +        self.buffer = buf
    +
    +    def write(self, byte):
    +        assert isinstance(byte, int)
    +        assert 0 <= byte <= 0xFF
    +        return self.buffer.write(byte.to_bytes(1, "little"))
    +
    +    def read(self):
    +        # assert count == 1, "Only a count of 1 is supported"
    +        data = self.buffer.read(1)
    +        assert len(data) == 1, "No data"
    +        return ord(data)
    +
    +    def seek(self, pos):
    +        self.buffer.seek(pos)
    +
    +    def flush(self):
    +        self.buffer.flush()
    +
    +    def tell(self):
    +        return self.buffer.tell()
    +
    +
    +##############################################################
    +# Misc
    +
    +
    +# TODO: Would a lookup-table increase performance? For example a lookup table of each 4-bit nibble?
    +# That's 16**2 = 256 values. Index calculated as: (byte1 & 0xF0) | ((byte2 & 0xF0) >> 4)
    +# and then: (byte1 & 0x0F) | ((byte2 & 0x0F) >> 4)
    +# Then could even be preloaded for each color palette
    +def color_code(byte1, byte2, offset):
    +    """Convert 2 bytes into color code at a given offset.
    +
    +    The colors are 2 bit and are found like this:
    +
    +    Color of the first pixel is 0b10
    +    | Color of the second pixel is 0b01
    +    v v
    +    1 0 0 1 0 0 0 1 <- byte1
    +    0 1 1 1 1 1 0 0 <- byte2
    +    """
    +    return (((byte2 >> (offset)) & 0b1) << 1) + ((byte1 >> (offset)) & 0b1)
    +
    +
    +##############################################################
    +# Window Events
    +# Temporarily placed here to not be exposed on public API
    +
    +
    +class WindowEvent:
    +    """
    +    All supported events can be found in the class description below.
    +
    +    It can be used as follows:
    +
    +    ```python
    +    >>> from pyboy.utils import WindowEvent
    +    >>> pyboy.send_input(WindowEvent.PAUSE)
    +
    +    ```
    +
    +    Just for button presses, it might be easier to use: `pyboy.PyBoy.button`,
    +    `pyboy.PyBoy.button_press` and `pyboy.PyBoy.button_release`.
    +    """
    +
    +    # ONLY ADD NEW EVENTS AT THE END OF THE LIST!
    +    # Otherwise, it will break replays, which depend on the id of the event
    +    (
    +        QUIT,
    +        PRESS_ARROW_UP,
    +        PRESS_ARROW_DOWN,
    +        PRESS_ARROW_RIGHT,
    +        PRESS_ARROW_LEFT,
    +        PRESS_BUTTON_A,
    +        PRESS_BUTTON_B,
    +        PRESS_BUTTON_SELECT,
    +        PRESS_BUTTON_START,
    +        RELEASE_ARROW_UP,
    +        RELEASE_ARROW_DOWN,
    +        RELEASE_ARROW_RIGHT,
    +        RELEASE_ARROW_LEFT,
    +        RELEASE_BUTTON_A,
    +        RELEASE_BUTTON_B,
    +        RELEASE_BUTTON_SELECT,
    +        RELEASE_BUTTON_START,
    +        _INTERNAL_TOGGLE_DEBUG,
    +        PRESS_SPEED_UP,
    +        RELEASE_SPEED_UP,
    +        STATE_SAVE,
    +        STATE_LOAD,
    +        PASS,
    +        SCREEN_RECORDING_TOGGLE,
    +        PAUSE,
    +        UNPAUSE,
    +        PAUSE_TOGGLE,
    +        PRESS_REWIND_BACK,
    +        PRESS_REWIND_FORWARD,
    +        RELEASE_REWIND_BACK,
    +        RELEASE_REWIND_FORWARD,
    +        WINDOW_FOCUS,
    +        WINDOW_UNFOCUS,
    +        _INTERNAL_RENDERER_FLUSH,
    +        _INTERNAL_MOUSE,
    +        _INTERNAL_MARK_TILE,
    +        SCREENSHOT_RECORD,
    +        DEBUG_MEMORY_SCROLL_DOWN,
    +        DEBUG_MEMORY_SCROLL_UP,
    +        MOD_SHIFT_ON,
    +        MOD_SHIFT_OFF,
    +        FULL_SCREEN_TOGGLE,
    +    ) = range(42)
    +
    +    def __init__(self, event):
    +        self.event = event
    +
    +    def __eq__(self, x):
    +        if isinstance(x, int):
    +            return self.event == x
    +        else:
    +            return self.event == x.event
    +
    +    def __int__(self):
    +        return self.event
    +
    +    def __str__(self):
    +        return (
    +            "QUIT",
    +            "PRESS_ARROW_UP",
    +            "PRESS_ARROW_DOWN",
    +            "PRESS_ARROW_RIGHT",
    +            "PRESS_ARROW_LEFT",
    +            "PRESS_BUTTON_A",
    +            "PRESS_BUTTON_B",
    +            "PRESS_BUTTON_SELECT",
    +            "PRESS_BUTTON_START",
    +            "RELEASE_ARROW_UP",
    +            "RELEASE_ARROW_DOWN",
    +            "RELEASE_ARROW_RIGHT",
    +            "RELEASE_ARROW_LEFT",
    +            "RELEASE_BUTTON_A",
    +            "RELEASE_BUTTON_B",
    +            "RELEASE_BUTTON_SELECT",
    +            "RELEASE_BUTTON_START",
    +            "_INTERNAL_TOGGLE_DEBUG",
    +            "PRESS_SPEED_UP",
    +            "RELEASE_SPEED_UP",
    +            "STATE_SAVE",
    +            "STATE_LOAD",
    +            "PASS",
    +            "SCREEN_RECORDING_TOGGLE",
    +            "PAUSE",
    +            "UNPAUSE",
    +            "PAUSE_TOGGLE",
    +            "PRESS_REWIND_BACK",
    +            "PRESS_REWIND_FORWARD",
    +            "RELEASE_REWIND_BACK",
    +            "RELEASE_REWIND_FORWARD",
    +            "WINDOW_FOCUS",
    +            "WINDOW_UNFOCUS",
    +            "_INTERNAL_RENDERER_FLUSH",
    +            "_INTERNAL_MOUSE",
    +            "_INTERNAL_MARK_TILE",
    +            "SCREENSHOT_RECORD",
    +            "DEBUG_MEMORY_SCROLL_DOWN",
    +            "DEBUG_MEMORY_SCROLL_UP",
    +            "MOD_SHIFT_ON",
    +            "MOD_SHIFT_OFF",
    +            "FULL_SCREEN_TOGGLE",
    +        )[self.event]
    +
    +
    +class WindowEventMouse(WindowEvent):
    +    def __init__(
    +        self, *args, window_id=-1, mouse_x=-1, mouse_y=-1, mouse_scroll_x=-1, mouse_scroll_y=-1, mouse_button=-1
    +    ):
    +        super().__init__(*args)
    +        self.window_id = window_id
    +        self.mouse_x = mouse_x
    +        self.mouse_y = mouse_y
    +        self.mouse_scroll_x = mouse_scroll_x
    +        self.mouse_scroll_y = mouse_scroll_y
    +        self.mouse_button = mouse_button
    +
    +
    +##############################################################
    +# Memory Scanning
    +#
    +
    +
    +def dec_to_bcd(value, byte_width=1, byteorder="little"):
    +    """
    +    Converts a decimal value to Binary Coded Decimal (BCD).
    +
    +    Args:
    +        value (int): The integer value to convert.
    +        byte_width (int): The number of bytes to consider for each value.
    +        byteorder (str): The endian type to use. This is only used for 16-bit values and higher. See [int.from_bytes](https://docs.python.org/3/library/stdtypes.html#int.from_bytes) for more details.
    +
    +    Example:
    +    ```python
    +    >>> from pyboy.utils import dec_to_bcd
    +    >>> f"{dec_to_bcd(30):08b}"
    +    '00110000'
    +    >>> f"{dec_to_bcd(32):08b}"
    +    '00110010'
    +
    +    ```
    +
    +    Returns:
    +        int: The BCD equivalent of the decimal value.
    +    """
    +    bcd_result = []
    +    for _ in range(byte_width):
    +        tens = ((value%100) // 10) << 4
    +        units = value % 10
    +        bcd_byte = (tens | units) & 0xFF
    +        bcd_result.append(bcd_byte)
    +        value //= 100
    +    return int.from_bytes(bcd_result, byteorder)
    +
    +
    +def bcd_to_dec(value, byte_width=1, byteorder="little"):
    +    """
    +    Converts a Binary Coded Decimal (BCD) value to decimal.
    +
    +    Args:
    +        value (int): The BCD value to convert.
    +        byte_width (int): The number of bytes to consider for each value.
    +        byteorder (str): The endian type to use. This is only used for 16-bit values and higher. See [int.to_bytes](https://docs.python.org/3/library/stdtypes.html#int.to_bytes) for more details.
    +
    +    Example:
    +    ```python
    +    >>> from pyboy.utils import bcd_to_dec
    +    >>> bcd_to_dec(0b00110000)
    +    30
    +    >>> bcd_to_dec(0b00110010)
    +    32
    +
    +    ```
    +
    +    Returns:
    +        int: The decimal equivalent of the BCD value.
    +    """
    +    decimal_value = 0
    +    multiplier = 1
    +
    +    bcd_bytes = value.to_bytes(byte_width, byteorder)
    +
    +    for bcd_byte in bcd_bytes:
    +        decimal_value += ((bcd_byte >> 4) * 10 + (bcd_byte & 0x0F)) * multiplier
    +        multiplier *= 100
    +
    +    return decimal_value
    +
    +
    +
    +
    +
    +
    +
    +

    Functions

    +
    +
    +def dec_to_bcd(value, byte_width=1, byteorder='little') +
    +
    +

    Converts a decimal value to Binary Coded Decimal (BCD).

    +

    Args

    +
    +
    value : int
    +
    The integer value to convert.
    +
    byte_width : int
    +
    The number of bytes to consider for each value.
    +
    byteorder : str
    +
    The endian type to use. This is only used for 16-bit values and higher. See int.from_bytes for more details.
    +
    +

    Example:

    +
    >>> from pyboy.utils import dec_to_bcd
    +>>> f"{dec_to_bcd(30):08b}"
    +'00110000'
    +>>> f"{dec_to_bcd(32):08b}"
    +'00110010'
    +
    +
    +

    Returns

    +
    +
    int
    +
    The BCD equivalent of the decimal value.
    +
    +
    + +Expand source code + +
    def dec_to_bcd(value, byte_width=1, byteorder="little"):
    +    """
    +    Converts a decimal value to Binary Coded Decimal (BCD).
    +
    +    Args:
    +        value (int): The integer value to convert.
    +        byte_width (int): The number of bytes to consider for each value.
    +        byteorder (str): The endian type to use. This is only used for 16-bit values and higher. See [int.from_bytes](https://docs.python.org/3/library/stdtypes.html#int.from_bytes) for more details.
    +
    +    Example:
    +    ```python
    +    >>> from pyboy.utils import dec_to_bcd
    +    >>> f"{dec_to_bcd(30):08b}"
    +    '00110000'
    +    >>> f"{dec_to_bcd(32):08b}"
    +    '00110010'
    +
    +    ```
    +
    +    Returns:
    +        int: The BCD equivalent of the decimal value.
    +    """
    +    bcd_result = []
    +    for _ in range(byte_width):
    +        tens = ((value%100) // 10) << 4
    +        units = value % 10
    +        bcd_byte = (tens | units) & 0xFF
    +        bcd_result.append(bcd_byte)
    +        value //= 100
    +    return int.from_bytes(bcd_result, byteorder)
    +
    +
    +
    +def bcd_to_dec(value, byte_width=1, byteorder='little') +
    +
    +

    Converts a Binary Coded Decimal (BCD) value to decimal.

    +

    Args

    +
    +
    value : int
    +
    The BCD value to convert.
    +
    byte_width : int
    +
    The number of bytes to consider for each value.
    +
    byteorder : str
    +
    The endian type to use. This is only used for 16-bit values and higher. See int.to_bytes for more details.
    +
    +

    Example:

    +
    >>> from pyboy.utils import bcd_to_dec
    +>>> bcd_to_dec(0b00110000)
    +30
    +>>> bcd_to_dec(0b00110010)
    +32
    +
    +
    +

    Returns

    +
    +
    int
    +
    The decimal equivalent of the BCD value.
    +
    +
    + +Expand source code + +
    def bcd_to_dec(value, byte_width=1, byteorder="little"):
    +    """
    +    Converts a Binary Coded Decimal (BCD) value to decimal.
    +
    +    Args:
    +        value (int): The BCD value to convert.
    +        byte_width (int): The number of bytes to consider for each value.
    +        byteorder (str): The endian type to use. This is only used for 16-bit values and higher. See [int.to_bytes](https://docs.python.org/3/library/stdtypes.html#int.to_bytes) for more details.
    +
    +    Example:
    +    ```python
    +    >>> from pyboy.utils import bcd_to_dec
    +    >>> bcd_to_dec(0b00110000)
    +    30
    +    >>> bcd_to_dec(0b00110010)
    +    32
    +
    +    ```
    +
    +    Returns:
    +        int: The decimal equivalent of the BCD value.
    +    """
    +    decimal_value = 0
    +    multiplier = 1
    +
    +    bcd_bytes = value.to_bytes(byte_width, byteorder)
    +
    +    for bcd_byte in bcd_bytes:
    +        decimal_value += ((bcd_byte >> 4) * 10 + (bcd_byte & 0x0F)) * multiplier
    +        multiplier *= 100
    +
    +    return decimal_value
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class WindowEvent +(event) +
    +
    +

    All supported events can be found in the class description below.

    +

    It can be used as follows:

    +
    >>> from pyboy.utils import WindowEvent
    +>>> pyboy.send_input(WindowEvent.PAUSE)
    +
    +
    +

    Just for button presses, it might be easier to use: PyBoy.button(), +PyBoy.button_press() and PyBoy.button_release().

    +
    + +Expand source code + +
    class WindowEvent:
    +    """
    +    All supported events can be found in the class description below.
    +
    +    It can be used as follows:
    +
    +    ```python
    +    >>> from pyboy.utils import WindowEvent
    +    >>> pyboy.send_input(WindowEvent.PAUSE)
    +
    +    ```
    +
    +    Just for button presses, it might be easier to use: `pyboy.PyBoy.button`,
    +    `pyboy.PyBoy.button_press` and `pyboy.PyBoy.button_release`.
    +    """
    +
    +    # ONLY ADD NEW EVENTS AT THE END OF THE LIST!
    +    # Otherwise, it will break replays, which depend on the id of the event
    +    (
    +        QUIT,
    +        PRESS_ARROW_UP,
    +        PRESS_ARROW_DOWN,
    +        PRESS_ARROW_RIGHT,
    +        PRESS_ARROW_LEFT,
    +        PRESS_BUTTON_A,
    +        PRESS_BUTTON_B,
    +        PRESS_BUTTON_SELECT,
    +        PRESS_BUTTON_START,
    +        RELEASE_ARROW_UP,
    +        RELEASE_ARROW_DOWN,
    +        RELEASE_ARROW_RIGHT,
    +        RELEASE_ARROW_LEFT,
    +        RELEASE_BUTTON_A,
    +        RELEASE_BUTTON_B,
    +        RELEASE_BUTTON_SELECT,
    +        RELEASE_BUTTON_START,
    +        _INTERNAL_TOGGLE_DEBUG,
    +        PRESS_SPEED_UP,
    +        RELEASE_SPEED_UP,
    +        STATE_SAVE,
    +        STATE_LOAD,
    +        PASS,
    +        SCREEN_RECORDING_TOGGLE,
    +        PAUSE,
    +        UNPAUSE,
    +        PAUSE_TOGGLE,
    +        PRESS_REWIND_BACK,
    +        PRESS_REWIND_FORWARD,
    +        RELEASE_REWIND_BACK,
    +        RELEASE_REWIND_FORWARD,
    +        WINDOW_FOCUS,
    +        WINDOW_UNFOCUS,
    +        _INTERNAL_RENDERER_FLUSH,
    +        _INTERNAL_MOUSE,
    +        _INTERNAL_MARK_TILE,
    +        SCREENSHOT_RECORD,
    +        DEBUG_MEMORY_SCROLL_DOWN,
    +        DEBUG_MEMORY_SCROLL_UP,
    +        MOD_SHIFT_ON,
    +        MOD_SHIFT_OFF,
    +        FULL_SCREEN_TOGGLE,
    +    ) = range(42)
    +
    +    def __init__(self, event):
    +        self.event = event
    +
    +    def __eq__(self, x):
    +        if isinstance(x, int):
    +            return self.event == x
    +        else:
    +            return self.event == x.event
    +
    +    def __int__(self):
    +        return self.event
    +
    +    def __str__(self):
    +        return (
    +            "QUIT",
    +            "PRESS_ARROW_UP",
    +            "PRESS_ARROW_DOWN",
    +            "PRESS_ARROW_RIGHT",
    +            "PRESS_ARROW_LEFT",
    +            "PRESS_BUTTON_A",
    +            "PRESS_BUTTON_B",
    +            "PRESS_BUTTON_SELECT",
    +            "PRESS_BUTTON_START",
    +            "RELEASE_ARROW_UP",
    +            "RELEASE_ARROW_DOWN",
    +            "RELEASE_ARROW_RIGHT",
    +            "RELEASE_ARROW_LEFT",
    +            "RELEASE_BUTTON_A",
    +            "RELEASE_BUTTON_B",
    +            "RELEASE_BUTTON_SELECT",
    +            "RELEASE_BUTTON_START",
    +            "_INTERNAL_TOGGLE_DEBUG",
    +            "PRESS_SPEED_UP",
    +            "RELEASE_SPEED_UP",
    +            "STATE_SAVE",
    +            "STATE_LOAD",
    +            "PASS",
    +            "SCREEN_RECORDING_TOGGLE",
    +            "PAUSE",
    +            "UNPAUSE",
    +            "PAUSE_TOGGLE",
    +            "PRESS_REWIND_BACK",
    +            "PRESS_REWIND_FORWARD",
    +            "RELEASE_REWIND_BACK",
    +            "RELEASE_REWIND_FORWARD",
    +            "WINDOW_FOCUS",
    +            "WINDOW_UNFOCUS",
    +            "_INTERNAL_RENDERER_FLUSH",
    +            "_INTERNAL_MOUSE",
    +            "_INTERNAL_MARK_TILE",
    +            "SCREENSHOT_RECORD",
    +            "DEBUG_MEMORY_SCROLL_DOWN",
    +            "DEBUG_MEMORY_SCROLL_UP",
    +            "MOD_SHIFT_ON",
    +            "MOD_SHIFT_OFF",
    +            "FULL_SCREEN_TOGGLE",
    +        )[self.event]
    +
    +

    Subclasses

    +
      +
    • pyboy.utils.WindowEventMouse
    • +
    +

    Class variables

    +
    +
    var QUIT
    +
    +
    +
    +
    var PRESS_ARROW_UP
    +
    +
    +
    +
    var PRESS_ARROW_DOWN
    +
    +
    +
    +
    var PRESS_ARROW_RIGHT
    +
    +
    +
    +
    var PRESS_ARROW_LEFT
    +
    +
    +
    +
    var PRESS_BUTTON_A
    +
    +
    +
    +
    var PRESS_BUTTON_B
    +
    +
    +
    +
    var PRESS_BUTTON_SELECT
    +
    +
    +
    +
    var PRESS_BUTTON_START
    +
    +
    +
    +
    var RELEASE_ARROW_UP
    +
    +
    +
    +
    var RELEASE_ARROW_DOWN
    +
    +
    +
    +
    var RELEASE_ARROW_RIGHT
    +
    +
    +
    +
    var RELEASE_ARROW_LEFT
    +
    +
    +
    +
    var RELEASE_BUTTON_A
    +
    +
    +
    +
    var RELEASE_BUTTON_B
    +
    +
    +
    +
    var RELEASE_BUTTON_SELECT
    +
    +
    +
    +
    var RELEASE_BUTTON_START
    +
    +
    +
    +
    var PRESS_SPEED_UP
    +
    +
    +
    +
    var RELEASE_SPEED_UP
    +
    +
    +
    +
    var STATE_SAVE
    +
    +
    +
    +
    var STATE_LOAD
    +
    +
    +
    +
    var PASS
    +
    +
    +
    +
    var SCREEN_RECORDING_TOGGLE
    +
    +
    +
    +
    var PAUSE
    +
    +
    +
    +
    var UNPAUSE
    +
    +
    +
    +
    var PAUSE_TOGGLE
    +
    +
    +
    +
    var PRESS_REWIND_BACK
    +
    +
    +
    +
    var PRESS_REWIND_FORWARD
    +
    +
    +
    +
    var RELEASE_REWIND_BACK
    +
    +
    +
    +
    var RELEASE_REWIND_FORWARD
    +
    +
    +
    +
    var WINDOW_FOCUS
    +
    +
    +
    +
    var WINDOW_UNFOCUS
    +
    +
    +
    +
    var SCREENSHOT_RECORD
    +
    +
    +
    +
    var DEBUG_MEMORY_SCROLL_DOWN
    +
    +
    +
    +
    var DEBUG_MEMORY_SCROLL_UP
    +
    +
    +
    +
    var MOD_SHIFT_ON
    +
    +
    +
    +
    var MOD_SHIFT_OFF
    +
    +
    +
    +
    var FULL_SCREEN_TOGGLE
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + + + \ No newline at end of file