Pyz3r is an unofficial python abstraction library for interacting with the alttpr.com API and other randomizer APIs.
This allows developers to create applications that not only generate games on alttpr.com, but also create the ROM as well using the patch data provided by the website.
- Type Hints: Full type annotations for better IDE support and type checking
- Enhanced Logging: Comprehensive logging throughout the library using Python's logging module
- Python 3.8-3.13 Support: Modern Python version support
- Improved Documentation: Google-style docstrings for all functions and classes
- Backwards Compatible: All existing code continues to work without changes
See MIGRATION_GUIDE.md for details on new features.
This module is available on PyPI. It can be installed via pip:
pip install pyz3r- Python: 3.8, 3.9, 3.10, 3.11, 3.12, 3.13
- Platforms: Linux, Windows, macOS
- Requirements: Internet connection to communicate with randomizer APIs
This is an unofficial tool. Please do not submit bug reports on the official ALTTPR repository Github for issues that are related to the use of this library!
If something is broken, please make sure it isn't related to this library before posting the bug report on the alttp_vt_randomizer repository. Feel free to post the bug here first if you're unsure.
Using this library to patch a ROM for racing may not be permitted. Its best to check with an official before using it for racing.
Finally, this library may break when new versions of the Link to the Past Randomizer are released. This version was tested with v31, however it may cease to function in later releases. If it does, please submit a Github issue!
Basic usage is fairly simple.
First you'll need to either generate a new seed or use the hash of a previous seed to get a game.
The below example will generate an item randomizer game with a list of settings:
import pyz3r
async def main():
seed = await pyz3r.ALTTPR.generate(
settings={
"glitches": "none",
"item_placement": "advanced",
"dungeon_items": "standard",
"accessibility": "items",
"goal": "ganon",
"crystals": {
"ganon": "random",
"tower": "4"
},
"mode": "open",
"entrances": "none",
"hints": "on",
"weapons": "randomized",
"item": {
"pool": "normal",
"functionality": "normal"
},
"tournament": True,
"spoilers": "off",
"lang":"en",
"enemizer": {
"boss_shuffle":"none",
"enemy_shuffle":"none",
"enemy_damage":"default",
"enemy_health":"default"
}
}
)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())If the game you want to work with has already been generated, you can load the hash like this:
import pyz3r
async def main()
seed = await pyz3r.ALTTPR.retrieve(
hash='zDvxWLLEMa'
)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())This will return to you a list of strings with the "code" that appears on the file select screen.
code = seed.code
print("Hash: [{hash}]".format(
hash = ' | '.join(code)
))Output:
Hash: [Heart | Empty Bottle | Somaria | Ice Rod | Boots]
You can also get the url quickly as well:
url = seed.url
print("Permalink: {url}".format(
url = url
))Output:
Permalink: https://alttpr.com/h/zDvxWLLEMa
You can access the seed's data via the data variable in the alttpr class. The example below will print the dictionary that has the seed's spoiler data (if available).
print(seed.data['spoiler'])This is the meat and potatoes of the library. There are two ways, the easy and advanced way.
You'll first want to read your unmodified Japan 1.0 ROM of Link to the Past. read_rom() will checksum the file to ensure its the correct ROM.
base_rom will be an array of integers representing the bytes of the original ROM.
You'll then want to use create_patched_game to apply all of the patches required to randomize and customize the ROM.
patched_rom = await seed.create_patched_game(
input_filename="alttp_jp10.sfc", # this should be an ALTTP Japan 1.0 ROM
output_filename="/path/to/output.sfc", # this is the output ROM
heartspeed='normal',
heartcolor='red',
spritename='Link', #can be any sprite listed at https://alttpr.com/sprites
music=False, # true or false, defaults true
quickswap=False,
menu_speed='normal',
msu1_resume=True # true or false, defaults true
)Here you can customize the following:
heartspeed- Optional. The low health beep speed. Acceptable values are'off','quarter','half','double', or'normal'. Defaults is'normal'.heartcolor- Optional. The color of your hearts. Acceptable values are'red','blue','green', or'yellow'. Default is'red'.spritename- Optional. The sprite to use. Acceptable values are thenamekeys in the json file at https://alttpr.com/sprites. Default is'Link'.music- Optional. Whether music should play. Acceptable values areTrueandFalse.Falseallows MSU-1 music to work correctly. Default isTrue.quickswap- Optional. Enable quickswap. Only works on non-tournament games or entrance shuffle. Acceptable values areTrueandFalse. Default isFalse.menu_speed- Optional. This is the menu speed setting. Only works on non-tournament games. Acceptable values areinstant,fast,normal,slow. Default isnormal.msu1_resume- Optional. This can disable the MSU1 Resume feature. Defaults to true.
The result of create_patched_game is a Rom object representing the fully patched game. It also writes the ROM out to the file path specified.
The ALttPR website has a feature where a player may customize a game, including choosing starting equipment, item locations, game settings, drops and prizepack customization. This library has a feature that allows you to generate games using the settings saved on alttpr.com's customizer.
To use it, you'll want to read a customizer-settings.json file that was saved from alttpr.com, then use the provided function customizer.convert2settings() to convert the json file to something that can be be used for game generation.
tournament can be set to True to generate the game as a race rom. This will cause the spoiler log to be unavailable,
along with scrambling the item table within the ROM.
import json
import pyz3r
f = open('customizer-settings.json', "r")
customizer_settings = json.loads(f.read())
f.close()
seed = pyz3r.alttpr(
settings=pyz3r.customizer.convert2settings(customizer_save=customizer_settings, tournament=False)
)Want to build your own mystery games without using SahasrahBot? The pyz3r module now supports mystery games!
Just use the new pyz3r.mystery.generate_random_settings() function and pass in the weights you wish to use. It'll output a dictionary can be fed into a into pyz3r.alttpr().
settings=pyz3r.mystery.generate_random_settings(
weights={
"glitches_required": {
"none": 100,
"overworld_glitches": 0,
"major_glitches": 0,
"no_logic": 0
},
"item_placement": {
"basic": 25,
"advanced": 75
},
"dungeon_items": {
"standard": 60,
"mc": 10,
"mcs": 10,
"full": 20
},
"accessibility": {
"items": 60,
"locations": 10,
"none": 30
},
"goals": {
"ganon": 40,
"fast_ganon": 20,
"dungeons": 10,
"pedestal": 20,
"triforce-hunt": 10
},
"tower_open": {
"0": 5,
"1": 5,
"2": 5,
"3": 5,
"4": 5,
"5": 5,
"6": 5,
"7": 50,
"random": 15
},
"ganon_open": {
"0": 5,
"1": 5,
"2": 5,
"3": 5,
"4": 5,
"5": 5,
"6": 5,
"7": 50,
"random": 15
},
"world_state": {
"standard": 35,
"open": 35,
"inverted": 20,
"retro": 10
},
"entrance_shuffle": {
"none": 60,
"simple": 7,
"restricted": 10,
"full": 10,
"crossed": 10,
"insanity": 2
},
"boss_shuffle": {
"none": 60,
"simple": 10,
"full": 10,
"random": 20
},
"enemy_shuffle": {
"none": 60,
"shuffled": 20,
"random": 20
},
"hints": {
"on": 50,
"off": 50
},
"weapons": {
"randomized": 40,
"assured": 40,
"vanilla": 15,
"swordless": 5
},
"item_pool": {
"normal": 80,
"hard": 15,
"expert": 5,
"crowd_control": 0
},
"item_functionality": {
"normal": 80,
"hard": 15,
"expert": 5
},
"enemy_damage": {
"default": 80,
"shuffled": 10,
"random": 10
},
"enemy_health": {
"default": 80,
"easy": 5,
"hard": 10,
"expert": 5
}
}
)
seed = await pyz3r.alttpr(settings=settings)
print(seed.url)If a game is generated where spoilers is set to on or generate, you may retrieve an Ordered Dictionary of the spoiler log with QOL enhancements used during the spoiler tournament.
spoiler = seed.get_formatted_spoiler()You may then write this to whatever you see fit.
from pyz3r.sm import sm
seed = await sm(
settings={
"smlogic": "normal",
"goal": "defeatboth",
"swordlocation": "randomized",
"morphlocation": "randomized",
"seed": "",
"race": "true",
"gamemode": "normal",
"players": "1"
},
randomizer='smz3',
)
print(seed.url)
print(seed.code)Only generating, not retrieving, SM Varia randomizer games is supported at this time. ROM patching is also not supported.
Usage is pretty simple.
from pyz3r.smvaria import SuperMetroidVaria
seed = await SuperMetroidVaria.create(
skills_preset='regular',
settings_preset='default'
)
print(seed.url)The library includes a comprehensive test suite with 79+ unit tests covering core functionality.
# Install test dependencies
pip install pytest pytest-asyncio pytest-cov
# Run all tests
pytest tests/
# Run with coverage report
pytest tests/ --cov=pyz3r --cov-report=term-missingSee tests/README.md for more details on the test suite.
- Add a feature to verify a settings dictionary before attempting to generate a game. This may become the default behavior, with the ability to override it. This could also just be a separate function that could be invoked as well.
- Veetorp, KatDevsGames, ChristosOwen, Smallhacker, and Dessyreqt for making an incredible randomizer.
- This work is dedicated to my father. May he rest in peace.
- Jaysee87 for his input into specific functionality, and suggesting asyncio support for usage with discord bots.
Github for alttp_vt_randomizer: https://github.com/sporchia/alttp_vt_randomizer Super Metroid + A Link to the Past Combo Randomizer: https://github.com/tewtal/SMZ3Randomizer Super Metroid VARIA Randomizer: https://github.com/theonlydude/RandomMetroidSolver
For a "real"-world usage of this library, check out SahasrahBot at https://github.com/tcprescott/sahasrahbot