-
Notifications
You must be signed in to change notification settings - Fork 475
Example Tetris
Mads Ynddal edited this page Mar 16, 2024
·
1 revision
PyBoy is loadable as an object in Python. This means, it can be initialized from another script, and be controlled and probed by the script. Take a look at the example below, which interacts with the game.
All external components can be found in the PyBoy Documentation. If more features are needed, or if you find a bug, don't hesitate to make an issue here on GitHub, or write on our Discord channel.
For Game Boy documentation in general, have a look at the Pan Docs, which has clear-cut details about every conceivable topic.
First frame | GIF | Last frame |
>>> pyboy = PyBoy(tetris_rom)
>>> pyboy.set_emulation_speed(0)
>>> assert pyboy.cartridge_title == "TETRIS"
>>>
>>> tetris = pyboy.game_wrapper
>>> tetris.game_area_mapping(tetris.mapping_compressed, 0)
>>> tetris.start_game(timer_div=0x00) # The timer_div works like a random seed in Tetris
>>> pyboy.tick() # To render screen after `.start_game`
True
>>> pyboy.screen.image.save("Tetris1.png")
>>>
>>> from pyboy.utils import WindowEvent
>>> pyboy.send_input(WindowEvent.SCREEN_RECORDING_TOGGLE)
>>>
>>> tetromino_at_0x00 = tetris.next_tetromino()
>>> assert tetromino_at_0x00 == "Z", tetris.next_tetromino()
>>> assert tetris.score == 0
>>> assert tetris.level == 0
>>> assert tetris.lines == 0
>>>
>>> # Checking that a reset on the same `timer_div` results in the same Tetromino
>>> tetris.reset_game(timer_div=0x00)
>>> assert tetris.next_tetromino() == tetromino_at_0x00, tetris.next_tetromino()
>>>
>>> blank_tile = 0
>>> first_brick = False
>>> for frame in range(1000): # Enough frames for the test. Otherwise do: `while pyboy.tick():`
... pyboy.tick(1, True)
...
... # The playing "technique" is just to move the Tetromino to the right.
... if frame % 2 == 0: # Even frames to let PyBoy release the button on odd frames
... pyboy.button("right")
...
... # Illustrating how we can extract the game board quite simply. This can be used to read the tile identifiers.
... game_area = tetris.game_area()
... # game_area is accessed as [<row>, <column>].
... # 'game_area[-1,:]' is asking for all (:) the columns in the last row (-1)
... if not first_brick and any(filter(lambda x: x != blank_tile, game_area[-1, :])):
... first_brick = True
... print("First brick touched the bottom!")
... print(tetris)
True
...
First brick touched the bottom!
Tetris:
Score: 0
Level: 0
Lines: 0
Sprites on screen:
Sprite [4]: Position: (72, 128), Shape: (8, 8), Tiles: (Tile: 129), On screen: True
Sprite [5]: Position: (80, 128), Shape: (8, 8), Tiles: (Tile: 129), On screen: True
Sprite [6]: Position: (88, 128), Shape: (8, 8), Tiles: (Tile: 129), On screen: True
Sprite [7]: Position: (88, 136), Shape: (8, 8), Tiles: (Tile: 129), On screen: True
Sprite [8]: Position: (120, 112), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Sprite [9]: Position: (128, 112), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Sprite [10]: Position: (128, 120), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Sprite [11]: Position: (136, 120), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Tiles on screen:
0 1 2 3 4 5 6 7 8 9
____________________________________________
0 | 0 0 0 0 0 0 0 0 0 0
1 | 0 0 0 0 0 0 0 0 0 0
2 | 0 0 0 0 0 0 0 0 0 0
3 | 0 0 0 0 0 0 0 0 0 0
4 | 0 0 0 0 0 0 0 0 0 0
5 | 0 0 0 0 0 0 0 0 0 0
6 | 0 0 0 0 0 0 0 0 0 0
7 | 0 0 0 0 0 0 0 0 0 0
8 | 0 0 0 0 0 0 0 0 0 0
9 | 0 0 0 0 0 0 0 0 0 0
10 | 0 0 0 0 0 0 0 0 0 0
11 | 0 0 0 0 0 0 0 0 0 0
12 | 0 0 0 0 0 0 0 0 0 0
13 | 0 0 0 0 0 0 0 0 0 0
14 | 0 0 0 0 0 0 0 0 0 0
15 | 0 0 0 0 0 0 0 0 0 0
16 | 0 0 0 0 0 0 0 1 1 1
17 | 0 0 0 0 0 0 0 0 0 1
...
>>>
>>> # Final game board:
>>> print(tetris)
Tetris:
Score: 0
Level: 0
Lines: 0
Sprites on screen:
Sprite [4]: Position: (72, 24), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Sprite [5]: Position: (80, 24), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Sprite [6]: Position: (80, 32), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Sprite [7]: Position: (88, 32), Shape: (8, 8), Tiles: (Tile: 130), On screen: True
Sprite [8]: Position: (120, 112), Shape: (8, 8), Tiles: (Tile: 133), On screen: True
Sprite [9]: Position: (128, 112), Shape: (8, 8), Tiles: (Tile: 133), On screen: True
Sprite [10]: Position: (136, 112), Shape: (8, 8), Tiles: (Tile: 133), On screen: True
Sprite [11]: Position: (128, 120), Shape: (8, 8), Tiles: (Tile: 133), On screen: True
Tiles on screen:
0 1 2 3 4 5 6 7 8 9
____________________________________________
0 | 0 0 0 0 0 0 0 0 0 0
1 | 0 0 0 0 0 0 0 0 0 0
2 | 0 0 0 0 0 0 0 0 0 0
3 | 0 0 0 0 0 0 0 2 2 0
4 | 0 0 0 0 0 0 0 0 2 2
5 | 0 0 0 0 0 0 0 0 0 0
6 | 0 0 0 0 0 0 0 0 0 0
7 | 0 0 0 0 0 0 0 0 0 0
8 | 0 0 0 0 0 0 0 0 0 0
9 | 0 0 0 0 0 0 0 0 0 0
10 | 0 0 0 0 0 0 0 0 0 0
11 | 0 0 0 0 0 0 0 0 0 0
12 | 0 0 0 0 0 0 0 0 0 0
13 | 0 0 0 0 0 0 0 0 0 0
14 | 0 0 0 0 0 0 0 0 0 0
15 | 0 0 0 0 0 0 0 0 0 0
16 | 0 0 0 0 0 0 0 1 1 1
17 | 0 0 0 0 0 0 0 0 0 1
>>> pyboy.screen.image.save("Tetris2.png")
>>> pyboy.send_input(WindowEvent.SCREEN_RECORDING_TOGGLE)
>>>
>>> # We shouldn't have made any progress with the moves we made
>>> assert tetris.score == 0
>>> assert tetris.level == 0
>>> assert tetris.lines == 0
>>>
>>> # Assert there is something on the bottom of the game area
>>> assert any(filter(lambda x: x != blank_tile, game_area[-1, :]))
>>> tetris.reset_game(timer_div=0x00)
>>> assert tetris.next_tetromino() == tetromino_at_0x00, tetris.next_tetromino()
>>>
>>> tetris.reset_game(timer_div=0x00)
>>> assert tetris.next_tetromino() == tetromino_at_0x00, tetris.next_tetromino()
>>> # After reseting, we should have a clean game area
>>> assert all(filter(lambda x: x != blank_tile, game_area[-1, :]))
>>>
>>> tetris.reset_game(timer_div=0x55) # The timer_div works like a random seed in Tetris
>>> assert tetris.next_tetromino() != tetromino_at_0x00, tetris.next_tetromino()
>>>
>>> # Testing that it defaults to random Tetrominos
>>> selection = set()
>>> for _ in range(10):
... tetris.reset_game()
... selection.add(tetris.next_tetromino())
>>> assert len(selection) > 1 # If it's random, we will see more than one kind
>>>
>>> pyboy.stop()