Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
24a9476
Stubbed out metroid II wrapper with minimum funcitonallity
benlagreca02 Jan 17, 2025
328bf01
Added most memory values from the ram mapping website. Next, need to …
benlagreca02 Jan 23, 2025
f9dfd63
game_start implemented, may be able to shave time off but it works
benlagreca02 Jan 23, 2025
43fc5b7
Great progress, getting lots of intermediary values, may still want t…
benlagreca02 Jan 27, 2025
18f0d88
Start function doesn't render anymore, fixed small bug with pixels/ar…
benlagreca02 Jan 27, 2025
ed59aa0
removed print statement left over from debugging
benlagreca02 Jan 28, 2025
89e3040
Minor change to reset function
benlagreca02 Jan 30, 2025
7436cf3
pre-commit all files with ruff linting and formating
Baekalfen Nov 15, 2024
d1a9dc5
Fix placement of log_level in PyBoy.__init__
Baekalfen Nov 15, 2024
556da6f
Introduce PyBoyException
Baekalfen Nov 15, 2024
2aa0eb8
Introduce exceptions to all load_state
Baekalfen Nov 15, 2024
8d6a06d
Track if compiled with Cython from utils
Baekalfen Nov 15, 2024
c15abf7
Introduce PyBoyDependencyError and PillowImportError
Baekalfen Nov 15, 2024
dee0ffd
Convert existing Exception to PyBoyException
Baekalfen Nov 21, 2024
18623f0
Update Discord invite to channel "rules"
Baekalfen Nov 21, 2024
3cdb781
Replace asserts with PyBoyExceptions
Baekalfen Nov 21, 2024
0bf4232
Add button test
Baekalfen Nov 21, 2024
8f417f7
Fix use of noexcept to remove warnings
Baekalfen Nov 21, 2024
715cbb0
Move CGB rendering from Renderer to CGBRenderer
Baekalfen Nov 21, 2024
80c2df3
Actually release the GIL and benchmark to test it
Baekalfen Nov 21, 2024
28b6711
Update tests to new exceptions
Baekalfen Nov 21, 2024
20a960a
Mock cython.gil/nogil when Cython is not present (PyPy)
Baekalfen Nov 23, 2024
9822969
Clean up unused "is_pypy"
Baekalfen Nov 23, 2024
0ef64b1
Skip doctests if compiled with Cython instead of failing
Baekalfen Nov 23, 2024
17dc6f3
Add pytest.ini options to pyproject.toml; ignore SDL2 warning; disabl…
Baekalfen Nov 23, 2024
9053e4a
Add benchmarking to CI
Baekalfen Nov 23, 2024
0ef41fb
Remove Python 3.8 EOL
Baekalfen Nov 23, 2024
daa2224
Automatically add documentation for plugins to PyBoy constructor
Baekalfen Dec 6, 2024
6089c29
Add missing gameshark doc
Baekalfen Dec 6, 2024
6ef4a4a
Add set_time_left to Mario wrapper, change error to exception
Baekalfen Dec 11, 2024
b9279d4
Improve Makefile docs
Baekalfen Dec 11, 2024
56b1097
Remove .stop() from tests as it saves RAM, while __del__ does not. Th…
Baekalfen Dec 11, 2024
8b92512
Move color_palette and cgb_color_palette to PyBoy constructor
Baekalfen Dec 11, 2024
58d39d0
Fix no_input, test and move it from plugin to PyBoy
Baekalfen Dec 11, 2024
baa4168
Fix singlestepping after CPU cycles_target has been introduced
Baekalfen Dec 27, 2024
43090e8
Add vectroid to tests. Not booting
Baekalfen Dec 16, 2024
e763918
Fixed punctuation
Baekalfen Dec 13, 2024
aeec17e
Move test-skips from "is_pypy" to "cython_compiled"
Baekalfen Dec 16, 2024
55f9137
Comprehensive testing of save/load state
Baekalfen Feb 11, 2025
3c45e45
Introduce DEBUG var in setup.py
Baekalfen Dec 27, 2024
ce0f6e5
Add TurtleTests
Baekalfen Dec 23, 2024
e4e1b2a
Fix events not cleared on pause and verify with toggle
Baekalfen Jan 17, 2025
99b8e35
Fix missing return in breakpoint symbol lookup
Baekalfen Jan 17, 2025
1631883
Add '.inner_loop' to default_rom for testing
Baekalfen Dec 29, 2024
2d198f6
Added log debug to CGB speed switching
Baekalfen Jan 17, 2025
f99a4be
Correct screen documentation to say "memoryview" instead of "bytes"
Baekalfen Jan 20, 2025
687224f
BasePlugin: Only run frame limiter when speed limit is not 0
Baekalfen Feb 10, 2025
e5b2957
Force closing of debug windows
Baekalfen Feb 9, 2025
68f67da
Fix wrong serialization of scanline parameters in saved state
Baekalfen Dec 15, 2024
c57e94e
Fix save/load state of STATRegister not restoring mode correctly
Baekalfen Dec 15, 2024
0d3a22a
Fix deprecation warning for RGBDS
Baekalfen Feb 9, 2025
3792008
Convert RTC usage of float to double, and remove cast to int
Baekalfen Jan 20, 2025
e85ab7a
Fix duplicate test_tiles_cgb and correct test to work
Baekalfen Feb 11, 2025
9aa166e
Add _cycles_to_interrupt to Timer save/load state
Baekalfen Dec 15, 2024
603c456
Fix LCD save/load state frame cycles and frame_done
Baekalfen Feb 11, 2025
a030553
Updating Blargg sound test results, although sound is not implemented
Baekalfen Feb 11, 2025
084935e
Most changes requested by Baekalfen made. Still need to change game o…
benlagreca02 Feb 12, 2025
18b656e
PyBoyPlugin Cython move cgb attribute
BackrndSource Feb 10, 2025
8d6a2eb
game_wrapper.game_area should work without rendering screen
BackrndSource Feb 10, 2025
17fa2cd
Wrap WindowEvents in class to avoid issues with mixed types
BackrndSource Feb 11, 2025
293a5fc
Stubbed out metroid II wrapper with minimum funcitonallity
benlagreca02 Jan 17, 2025
1ba2744
Added most memory values from the ram mapping website. Next, need to …
benlagreca02 Jan 23, 2025
1a8ef18
game_start implemented, may be able to shave time off but it works
benlagreca02 Jan 23, 2025
139f6ff
Great progress, getting lots of intermediary values, may still want t…
benlagreca02 Jan 27, 2025
1b7fd56
Start function doesn't render anymore, fixed small bug with pixels/ar…
benlagreca02 Jan 27, 2025
0e8466a
removed print statement left over from debugging
benlagreca02 Jan 28, 2025
e7bd4cd
Minor change to reset function
benlagreca02 Jan 30, 2025
c5e9747
Most changes requested by Baekalfen made. Still need to change game o…
benlagreca02 Feb 12, 2025
63cfc5d
Merge fixed hopefully?
benlagreca02 Feb 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .github/workflows/pr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: [3.8, 3.9, "3.10", 3.11, 3.12, 3.13]
python-version: [3.9, "3.10", 3.11, 3.12, 3.13]

steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -50,6 +50,13 @@ jobs:
TEST_NO_UI: 1
run: |
python -m pytest tests/ -n auto -v
- name: Run PyTest Benchmark
env:
PYTEST_SECRETS_KEY: ${{ secrets.PYTEST_SECRETS_KEY }}
TEST_VERBOSE_IMAGES: 0
TEST_NO_UI: 1
run: |
python -m pytest tests/test_benchmark.py --benchmark-enable --benchmark-min-rounds=10
- name: Build wheel
run: |
echo "Building wheel"
Expand Down Expand Up @@ -122,7 +129,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['cp38-cp38', 'cp39-cp39', 'cp310-cp310', 'cp311-cp311', 'cp312-cp312', 'cp313-cp313']
python-version: ['cp39-cp39', 'cp310-cp310', 'cp311-cp311', 'cp312-cp312', 'cp313-cp313']
manylinux-version: ['manylinux_2_28_x86_64', 'musllinux_1_2_x86_64'] # GHA doesn't support manylinux_2_24_aarch64

steps:
Expand Down Expand Up @@ -170,10 +177,10 @@ jobs:
echo "Dists built:"
ls -lah dist/

- name: Set up Python 3.8 (just for PyPi upload)
- name: Set up Python 3.11 (just for PyPi upload)
uses: actions/setup-python@v3
with:
python-version: 3.8
python-version: 3.11
- name: Upload wheel
if: ${{ github.event_name == 'release' && github.event.action == 'published' && !github.event.release.prerelease }}
run: |
Expand Down
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

.PHONY: build clean run install test
.PHONY: build clean run install test benchmark docs

all: build

Expand Down Expand Up @@ -65,7 +65,7 @@ uninstall:
${PY} -m pip uninstall pyboy

test: export DEBUG=1
test: clean test_pypy test_cpython_doctest build test_cython
test: clean test_pypy test_cpython_doctest build test_cython benchmark

test_cpython_doctest:
${PY} -m pytest pyboy/ ${PYTEST_ARGS}
Expand All @@ -78,9 +78,13 @@ test_pypy:

test_all: test

benchmark:
${PY} -m pytest -m benchmark tests/test_benchmark.py --benchmark-enable --benchmark-min-rounds=10

docs: clean
bash -O extglob -c 'rm -rf -- ${ROOT_DIR}/docs/!(templates|CNAME)'
find ./docs -type f ! -path "./docs/templates/*" ! -path "./docs/CNAME" ! -name "*.png" -exec rm -rf {} +
mkdir -p ${ROOT_DIR}/docs/templates
cd ${ROOT_DIR}/pyboy/plugins && ${PY} manager_gen.py
pdoc --html --force -c latex_math=True -c sort_identifiers=False -c show_type_annotations=True --template-dir docs/templates pyboy
cp -r html/pyboy/ ${ROOT_DIR}/docs/
rm -rf html
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<img src="extras/README/pyboy.svg" width="480">
</p>

__If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/Zrf2nyH).__
__If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/wUbag3KNqQ).__

[![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/Zrf2nyH)
[![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/wUbag3KNqQ)

<!---
Generate GIF with the layout and captions
Expand Down Expand Up @@ -98,7 +98,7 @@ If you are looking to make a bot or AI, then these resources are a good place to
* [Example: Tetris](https://github.com/Baekalfen/PyBoy/wiki/Example-Tetris)
* [Example: Super Mario Land](https://github.com/Baekalfen/PyBoy/wiki/Example-Super-Mario-Land)
* [Code Examples](https://github.com/Baekalfen/PyBoy/tree/master/extras/examples)
* [Discord](https://discord.gg/Zrf2nyH)
* [Discord](https://discord.gg/wUbag3KNqQ)


When the emulator is running, you can easily access [PyBoy's API](https://baekalfen.github.io/PyBoy/index.html):
Expand Down Expand Up @@ -186,9 +186,9 @@ do 3160 hours of gameplay in 1 hour.

Contributing
============
Any contribution is appreciated. The currently known problems are tracked in [the Issues tab](https://github.com/Baekalfen/PyBoy/issues). Feel free to take a swing at any one of them. If you have something original in mind, come and [discuss it on on Discord](https://discord.gg/Zrf2nyH).
Any contribution is appreciated. The currently known problems are tracked in [the Issues tab](https://github.com/Baekalfen/PyBoy/issues). Feel free to take a swing at any one of them. If you have something original in mind, come and [discuss it on on Discord](https://discord.gg/wUbag3KNqQ).

[![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/Zrf2nyH)
[![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/wUbag3KNqQ)

For the more major features, there are the following that you can give a try. They are also described in more detail in the [project list in the Wiki](https://github.com/Baekalfen/PyBoy/wiki/Student-Projects):
* Hacking games
Expand Down
20 changes: 10 additions & 10 deletions extras/bootrom/bootrom_common.asm
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ main:

; Sound Setup
ld A, $80
ldh [$26], A ; Enable sound - NR52
ldh [$11], A ; Use 50% duty cycle - NR11
ldh [$FF00+$26], A ; Enable sound - NR52
ldh [$FF00+$11], A ; Use 50% duty cycle - NR11
ld A, $F3
ldh [$12], A
ldh [$25], A
ldh [$FF00+$12], A
ldh [$FF00+$25], A
ld A, $77
ldh [$24], A
ldh [$FF00+$24], A

; #########################
; Graphics effect and wait
Expand Down Expand Up @@ -123,24 +123,24 @@ main:
.play_sound
; Adjust frequency sweep
ld A, %0010011
ldh [$10], A
ldh [$FF00+$10], A

ld A, $48
ldh [$13], A
ldh [$FF00+$13], A

; Trigger
ld A, $81
ldh [$14], A
ldh [$FF00+$14], A
ret

.adj_sound
; Adjust frequency sweep
ld A, %0011001
ldh [$10], A
ldh [$FF00+$10], A

; Trigger
ld A, $81
ldh [$14], A
ldh [$FF00+$14], A
ret


Expand Down
2 changes: 1 addition & 1 deletion extras/default_rom/default_rom.asm
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Main:
ld a, [rWY]
cp a, 90
jr c, .loop

.inner_loop ; Used in test
ldh a, [rDIV]
cp a, 255
jp z, .move
Expand Down
1 change: 1 addition & 0 deletions extras/default_rom/default_rom.sym
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
00:0187 Main.readTilemap
00:01a0 Main.clearOAM
00:01ab Main.loop
00:01b2 Main.inner_loop
00:01bb Main.sync
00:01c2 Main.move
00:01cd Tileset
1 change: 1 addition & 0 deletions extras/default_rom/default_rom_cgb.sym
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
00:0187 Main.readTilemap
00:01a0 Main.clearOAM
00:01ab Main.loop
00:01b2 Main.inner_loop
00:01bb Main.sync
00:01c2 Main.move
00:01cd Tileset
4 changes: 2 additions & 2 deletions extras/examples/gamewrapper_kirby.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
file_path = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, file_path + "/../..")

from pyboy import PyBoy # isort:skip
from pyboy.utils import WindowEvent # isort:skip
from pyboy import PyBoy # noqa
from pyboy.utils import WindowEvent # noqa

# Check if the ROM is given through argv
if len(sys.argv) > 1:
Expand Down
4 changes: 2 additions & 2 deletions extras/examples/gamewrapper_mario.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
file_path = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, file_path + "/../..")

from pyboy import PyBoy # isort:skip
from pyboy.utils import WindowEvent # isort:skip
from pyboy import PyBoy # noqa
from pyboy.utils import WindowEvent # noqa

# Check if the ROM is given through argv
if len(sys.argv) > 1:
Expand Down
4 changes: 2 additions & 2 deletions extras/examples/gamewrapper_tetris.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
file_path = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, file_path + "/../..")

from pyboy import PyBoy # isort:skip
from pyboy.utils import WindowEvent # isort:skip
from pyboy import PyBoy # noqa
from pyboy.utils import WindowEvent # noqa

# Check if the ROM is given through argv
if len(sys.argv) > 1:
Expand Down
11 changes: 7 additions & 4 deletions pyboy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import os

import pyboy
from pyboy import PyBoy, core, utils
from pyboy import PyBoy
from pyboy.plugins.manager import parser_arguments
from pyboy.pyboy import defaults
from pyboy.utils import PyBoyInvalidInputException

logger = pyboy.logging.get_logger(__name__)

Expand All @@ -20,13 +21,15 @@

def color_tuple(string):
color_palette = [int(c.strip(), 16) for c in string.split(",")]
assert len(color_palette) == 4, f"Not the correct amount of colors! Expected four, got {len(color_palette)}"
if not (len(color_palette) == 4):
raise PyBoyInvalidInputException(f"Not the correct amount of colors! Expected four, got {len(color_palette)}")
return color_palette


def cgb_color_tuple(string):
color_palette = [int(c.strip(), 16) for c in string.split(",")]
assert len(color_palette) == 12, f"Not the correct amount of colors! Expected twelve, got {len(color_palette)}"
if not (len(color_palette) == 12):
raise PyBoyInvalidInputException(f"Not the correct amount of colors! Expected twelve, got {len(color_palette)}")
return [color_palette[0:4], color_palette[4:8], color_palette[8:12]]


Expand All @@ -43,6 +46,7 @@ def valid_file_path(path):
)
parser.add_argument("ROM", type=valid_file_path, help="Path to a Game Boy compatible ROM file")
parser.add_argument("-b", "--bootrom", dest="bootrom", type=valid_file_path, help="Path to a boot-ROM file")
parser.add_argument("--no-input", action="store_true", help="Disable all user-input (mostly for autonomous testing)")
parser.add_argument(
"--log-level",
default=defaults["log_level"],
Expand Down Expand Up @@ -168,7 +172,6 @@ def main():
with open(state_path, "rb") as f:
pyboy.load_state(f)

render = not argv.no_renderer
while pyboy.tick():
pass

Expand Down
9 changes: 8 additions & 1 deletion pyboy/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@
# "constants": False,
# "manager": False,
# }
# __all__ = ["API"]
__all__ = [
constants,
GameShark,
Screen,
Sprite,
Tile,
TileMap,
]
4 changes: 2 additions & 2 deletions pyboy/api/gameshark.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ cdef class GameShark:

cdef uint8_t _get_value(self, int, int) noexcept
cdef void _set_value(self, int, int, int) noexcept
cdef tuple _convert_cheat(self, str code) noexcept
cdef tuple _convert_cheat(self, str code)
cpdef void add(self, str code) noexcept
cpdef void remove(self, str code, bint restore_value=*) noexcept
cpdef void clear_all(self, bint restore_value=*) noexcept
cdef void tick(self) noexcept
cdef void tick(self) noexcept with gil
2 changes: 1 addition & 1 deletion pyboy/api/gameshark.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def remove(self, code, restore_value=True):
restore_value (bool): True to restore original value at address, otherwise don't restore
"""

if not code in self.cheats:
if code not in self.cheats:
raise ValueError("GameShark code cannot be removed. Hasn't been applied.")

original_value, (_type, _, address) = self.cheats.pop(code)
Expand Down
2 changes: 1 addition & 1 deletion pyboy/api/memory_scanner.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ cdef class MemoryScanner:
cdef int _memory_cache_byte_width

cpdef list rescan_memory(self, object new_value=*, dynamic_comparison_type=*, byteorder=*)
cpdef list scan_memory(self, object target_value=*, int start_addr=*, int end_addr=*, standard_comparison_type=*, value_type=*, int byte_width=*, byteorder=*) noexcept
cpdef list scan_memory(self, object target_value=*, int start_addr=*, int end_addr=*, standard_comparison_type=*, value_type=*, int byte_width=*, byteorder=*)
cpdef bint _check_value(self, uint64_t, uint64_t, int)noexcept
2 changes: 1 addition & 1 deletion pyboy/api/memory_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def rescan_memory(
else:
self._memory_cache[addr] = current_value
elif dynamic_comparison_type == DynamicComparisonType.MATCH:
if new_value == None:
if new_value is None:
raise ValueError("new_value must be specified when using DynamicComparisonType.MATCH")
if current_value != new_value:
self._memory_cache.pop(addr)
Expand Down
5 changes: 2 additions & 3 deletions pyboy/api/screen.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ from pyboy.core.mb cimport Motherboard

cdef class Screen:
cdef Motherboard mb
cpdef ((int, int), (int, int)) get_tilemap_position(self)
cpdef ((int, int), (int, int)) get_tilemap_position(self) noexcept
# cdef readonly list tilemap_position_list
# cdef readonly uint8_t[:,:] tilemap_position_list
cdef readonly uint32_t[:,:] raw_buffer
cdef readonly (int, int) raw_buffer_dims
cdef readonly str raw_buffer_format
# cdef readonly uint8_t[144][160][4] memoryview
# cpdef np.ndarray[np.uint8_t, ndim=3] get_ndarray(self)

cdef readonly object ndarray
cdef readonly object image
9 changes: 5 additions & 4 deletions pyboy/api/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def __init__(self, mb):

Returns
-------
bytes:
92160 bytes of screen data in a `bytes` object.
memoryview:
92160 bytes memoryview of screen data.
"""
self.raw_buffer_dims = self.mb.lcd.renderer.buffer_dims
"""
Expand Down Expand Up @@ -121,6 +121,7 @@ def __init__(self, mb):
"""
if not Image:
logger.warning('Cannot generate screen image. Missing dependency "Pillow".')
self.image = utils.PillowImportError()
else:
self._set_image()

Expand Down Expand Up @@ -173,7 +174,7 @@ def _set_image(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
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()`.

Expand Down Expand Up @@ -206,7 +207,7 @@ def tilemap_position_list(self):
"""

if self.mb.lcd._LCDC.lcd_enable:
return [[line[0], line[1], line[2], line[3]] for line in self.mb.lcd._scanlineparameters]
return [[line[0], line[1], line[2] - 7, line[3]] for line in self.mb.lcd._scanlineparameters]
else:
return [[0, 0, 0, 0] for line in range(144)]

Expand Down
4 changes: 3 additions & 1 deletion pyboy/api/sprite.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""

from pyboy.core.lcd import LCDCRegister
from pyboy.utils import PyBoyOutOfBoundsException

from .constants import LCDC_OFFSET, OAM_OFFSET, SPRITES
from .tile import Tile
Expand All @@ -30,7 +31,8 @@ def __init__(self, mb, sprite_index):
By knowing the tile identifiers of players, enemies, power-ups and so on, you'll be able to search for them
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})"
if not (0 <= sprite_index < SPRITES):
raise PyBoyOutOfBoundsException(f"Sprite index of {sprite_index} is out of range (0-{SPRITES})")
self.mb = mb
self._offset = sprite_index * 4

Expand Down
4 changes: 2 additions & 2 deletions pyboy/api/tile.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ cdef class Tile:
cdef readonly int tile_identifier
cdef readonly int data_address
cdef readonly tuple shape
cpdef object image(self) noexcept
cpdef object ndarray(self) noexcept
cpdef object image(self)
cpdef object ndarray(self)

cdef uint32_t[:,:] data # TODO: Add to locals instead
@cython.locals(byte1=uint8_t, byte2=uint8_t, colorcode=uint64_t)
Expand Down
Loading