In my previous post Reverse Engineering A Link to the Past Randomizer I discussed developing Mudora, a Go tool to analyze randomized A Link to the Past ROMs even when they’re encrypted. This post is a follow up to that work, where I wanted to use my new tool to analyze a corpus of randomized games to see if there were any latent features that could speed up optimal play in ALttPR. I may have gone a little overboard…
Generating the ROMs
The good news is, getting randomized ROMs is pretty easy. You can, of course, do this via the website (https://alttpr.com) for one-off ROMs, but I needed a lot of data. Luckily the randomizer is open-source, so after downloading it and installing PHP and some dependencies, I could generate games. I used the default settings:

From there it’s a simple loop over the CLI to generate a ton of ROMs. I kicked off 5 threads running this loop, but it filled up 50GB of my hard drive with ROMs, so I decided to stop early. I ended up with 28,193 randomized Zelda 3 ROMs, which took a handful of hours. I didn’t time it directly, something like ~4 hours spent generating ROMs sounds about right.
for i in {1..10000}; do
php artisan alttp:randomize alttp.sfc outputs \
--goal=ganon \
--state=open \
--weapons=randomized \
--glitches=none \
--crystals_ganon=7 \
--crystals_tower=7 \
--item_placement=advanced \
--dungeon_items=standard \
--accessibility=items \
--hints=on \
--item_pool=normal \
--item_functionality=normal
done
Creating the Dataset
Now the fun begins. I needed to turn this into a usable dataset. I kicked off a thread pool with 5 more workers that handled kicking out to a compiled Mudora binary subprocess which would read the item data. This got parsed into an intermediate JSON representation just for temporary checkpointing purposes, since I lost the data once during processing. Mudora can churn through ROMs pretty quickly, but this part took about 10 minutes to process.
From there, I had pandas suck in the JSON. One of my initial tries saw that Mudora failed to process some of the games (some games had all locations “UNKNOWN”), so I culled those rows. When I repeated the process it didn’t happen again, so there might be some latent bug in Mudora I need to dig into later. Either way, the data was clean.
You can find the full dataset on Kaggle: https://www.kaggle.com/datasets/typorter/a-link-to-the-past-randomizer-item-locations.
Analyzing the Data
I did all the analysis in a Jupyter notebook you can find here (which also includes the dataset creation script if you want to repeat it).
The goal of my analysis is pretty simple:
- Calculate the probability of an item appearing in each location in the game.
- Generate a heatmap so we can visualize any trends – this is perfect because hotspots for items that grant progression are really important and might indicate that you should go out of your way to check them if you’re looking for a specific item.
First Pass
As expected the first pass is a bit of garbage. The signal to noise ratio is incredibly low. There’s a couple of items that have 100% or at least near 100% probability, like finding a small key in the entrance to Swamp Palace or keys in Castle Tower. On the other hand it’s completely impossible to find maps, keys, compasses, crystals, or pendants in the overworld (at least in this mode).
If you look at the far right you can see some special locations called Waterfall Bottle and Pyramid Bottle – these aren’t really items, but instead faerie (fairy) fountains where you can throw an empty bottle to have them filled. They’re listed on the spoiler log on https://alttpr.com, so Mudora faithfully recreates it without them being real items. You don’t actually receive a guaranteed bottle at that location.
Overall, this gives us big vertical and horizontal blanks and completely drowns out the signal.
Filtering Dungeon Items
Next step is to cull out dungeon items like keys, compasses, and maps, as well as dungeon rewards. While we’re at it we’ll also filter out some special locations like the dungeon rewards and the bottle fill locations, as well as locations where you can only get a key.
This is looking a lot better! We’ve gotten rid of all the blank vertical bars and most of the blank horizontal bars. The horizontal ones are now actually valid – the prominent one is now the 0% chance of finding the Moon Pearl in the Dark World (notably with the exception of finding it on the Pyramid Ledge which has a visible point).
One thing that still stands out is consumable items and Heart Pieces now have a really high signal since there are multiple copies of each in the item pool. Unfortunately, these don’t provide direct progression benefit, so we’ll continue to drill down.
Filtering Non-Progression Items
I’m defining non-progression items as items that aren’t equipment (shields, gloves, mail, swords) or an inventory item (Bow, Hammer, Mirror, etc.). While these don’t truly represent progression items in all cases it’s better than nothing. For instance, Ice Rod is a progression item if and only if Turtle Rock rewards a Crystal or defeating Trinexx rewards another progression item. I also included things like Half Magic and all Progressive items (not to be confused with progression items, these are items that have a “level”, like Fighter’s Sword -> Master Sword -> Tempered Sword -> Golden Sword). While these don’t grant progression every time, they make the game so much easier that it’s worth including them.
Unfortunately, since there are multiple copies of Progressive items in the pool, this ends up inflating the percentage for those items similarly to how we looked at consumables before.
For now, I think this is good enough. Finally have some useful signal here:
We can see several low percentage overworld locations.
- Sahasrahla (requires Green Pendant)
- Master Sword Pedestal (requires all Pendants)
- Pyramid Fairy (requires Crystals 5 & 6)
- Lumberjack Cave (requires the Pegasus Boots and defeating Agahnim, a sometimes-optional boss)
- Bombos Tablet
It generally looks like items that require both the Moon Pearl and Magic Mirror are generally progression-less, such as Graveyard Ledge, Bombos Tablet mentioned above, Cave 45, and Lake Hylia Island. This means you likely shouldn’t go out of your way to check those.
We can also see isolated 0% chances – exactly as we would expect because some items can’t logically be placed in certain areas. Particularly: you’ll never access Moon Pearl in a location that requires Dark World access, or Flippers in a location that requires swimming. You can also see horizontal lines for some items in dungeons that require that item (again, Flippers for Swamp Palace).
Several dungeon low-percentage “blocks” exist – this is an artifact of dungeons having keys which fill the chests in those locations. Particularly bad is Palace of Darkness, and you can see it’s bimodal. The front of PoD has 6 chests of which up to 4 can be keys, then the Big Chest has a fairly high probability of containing an item, followed by the back of PoD having a low chance again.
It might be interesting to group the locations by dungeon, but again we’ll run into normalization issues since we’d be combining multiple locations into a single meta-location, which would inflate the probabilities for the meta-location.
What’s Next?
For now I’m satisfied with where this landed. There’s no strong takeaways in terms of playthrough improvements, beyond the already common knowledge “Don’t check low-likelihood, expensive locations” like the Sahasrahla, Pedestal, or Tablets that were called out above. In other words, the game is fairly well randomized other than the heuristics that are applied to make sure that items you need are obtainable. That result is interesting in and of itself and speaks to how human players have effectively optimized how they play through the game.
That’s all for now. Thanks for reading!


