A Nuzlocke turns any Pokémon game into a one-life puzzle, and an emulator can either protect that promise or quietly erase it. Most runs happen on a Game Boy Advance, DS or 3DS title, and the stable builds that matter on October 22, 2026 are mGBA 0.10.5, melonDS 1.1 and Azahar 2126.2. This tutorial walks through 13 steps and about 40 minutes of setup, then hands you a working Python project that logs encounters and flags save rollbacks.
The hard part is not installing software. Emulators ship with quick save, rewind and cheat menus that make a hardcore ruleset easy to break by accident, and a single misplaced hotkey can undo a gym leader wipe. Our comparison of mGBA, WatermelonDS and Azahar covers which emulator suits which era. This guide goes a step further and shows how to configure all three so the pokemon nuzlocke you start tonight survives for the whole playthrough.
You need your own legally obtained game files, a Windows, macOS or Linux computer, and a free evening. Everything below uses free, open-source emulators. We do not link to game downloads, and neither should you. Dump the cartridges and consoles you already own, then follow along.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
Why Emulators Suit a Pokémon Nuzlocke (and Where They Tempt You)
The Nuzlocke challenge is a fan-made ruleset, not an official Pokémon mode. Two rules form the core. First, you may catch only the first wild Pokémon you meet in each new area, and if it faints or escapes, that area is closed to you. Second, any Pokémon that faints counts as dead and leaves your team for good. Almost every community adds a third rule: nickname everything you catch, because a name makes a loss sting and makes tracking easier.
Popular extras sit on top of those rules. A dupes clause lets you skip a first encounter if you already own that species. A shiny clause lets you catch a shiny regardless of the area rule. Level caps stop you from out-leveling the next gym leader’s strongest Pokémon, and a whiteout usually ends the run. Our rule file in Step 1 captures all of these as plain data, so you and any viewers know exactly what counts.
Emulators help a Nuzlocke in practical ways. You can fast-forward through long walks and text, back up the save file in seconds, play on a big screen, and put a tracker window beside the game. A cartridge battery can also die and wipe a save, while a copied file on your drive cannot. Those are real advantages, and they explain why the nuzlocke emulator crowd keeps growing.
The same tools are also the danger. Save states let you rewind a critical hit with one keypress. Rewind lets you take back a bad switch. Cheat menus add rare candies with two clicks. None of that is cheating if you never touch it, but a run that lasts a month will hit a tired evening where the shortcut looks harmless. Good setups therefore do two things: they make rule-breaking inconvenient, and they make accidents visible afterward.
That is the philosophy behind the 13 steps. Steps 1 to 3 define the rules and install the emulators. Steps 4 to 6 add your games and strip out the shortcuts. Steps 7 and 8 build a backup habit. Steps 9 and 10 set up a tracker. Steps 11 to 13 install the save guard script, play your first route and audit before each gym. If you prefer to browse more setups first, the gaming section lists every emulator guide we publish.
Prerequisites and Tested Versions
Pin your versions before you start a run. An emulator update halfway through a month-long playthrough can change save handling, controls or graphics defaults, and you do not want to discover that after a gym leader wipe. The table lists what this tutorial assumes, with the stable release of each tool as of October 22, 2026.
| Component | Version used | Role in this guide | Price |
|---|---|---|---|
| mGBA | 0.10.5 (current stable) | Game Boy, Game Boy Color and GBA games | Free, MPL 2.0 |
| melonDS | 1.1 (current stable) | Nintendo DS games | Free, GPL-3.0 |
| Azahar | 2126.2 (current stable) | Nintendo 3DS games | Free, open source |
| Python | Not listed | Runs the save guard project | Free |
| Operating system | Windows 11, macOS or a current Linux distro | Host for all tools | Varies |
| Game files | Dumped from your own cartridges or console | The games you actually play | Cost of the game |
Two version details deserve attention. First, Azahar is the 3DS emulator that continues the Citra line after Citra was discontinued in 2024. The project’s newest development build is 2127.0 Alpha 3, but that is a prerelease, so a Nuzlocke belongs on stable 2126.2. The project reportedly shipped 2126.2 with a fix for a security advisory that affected official 2126.1.2 builds on Windows and Linux, which is another reason to start on the newer stable release. Azahar is strictly a 3DS emulator, so it cannot run DS games.
Second, the official melonDS site still points to 1.1 as the stable desktop release. Unofficial builds exist, such as a Project PM multiplayer fork and a community melonDS Android port, but they are not the upstream program. Stay on the official desktop build for a hardcore run. On the GBA side, mGBA has no newer numbered stable release than 0.10.5, although its repository keeps receiving development commits. Download the tagged release, not a nightly, because nightlies can change behavior without notice. Our mGBA setup guide for Pokémon goes deeper on video and audio options if you want to tune those first.
Hardware needs are modest. Any PC from the last ten years handles GBA and DS emulation without effort. 3DS emulation asks more of the graphics card, so a Nuzlocke on Pokémon X, Y or Sun and Moon wants a recent integrated GPU at minimum. You also need a few gigabytes of free disk space for emulators, backups and your game library. Python is only required for the project in Steps 11 to 13, and you can skip that part if you would rather track everything by hand.
Which Emulator Fits Which Pokémon Generation
Pick one emulator per generation and never switch mid-run. Save files are tied to the emulator that wrote them, and moving a save between programs risks corruption. The table maps each generation to the emulator we recommend, along with a Nuzlocke-friendly example title.
| Generation | Platform | Emulator | Example titles | Save type |
|---|---|---|---|---|
| Gen 1 and 2 | Game Boy, Game Boy Color | mGBA 0.10.5 | Red, Blue, Gold, Crystal | Battery save file |
| Gen 3 | Game Boy Advance | mGBA 0.10.5 | Emerald, FireRed, Ruby | Battery save file |
| Gen 4 and 5 | Nintendo DS | melonDS 1.1 | HeartGold, Platinum, Black 2 | Cartridge save file |
| Gen 6 and 7 | Nintendo 3DS | Azahar 2126.2 | X, Omega Ruby, Sun | Folder-based save data |
| Gen 8 and 9 | Nintendo Switch | Outside this guide | Not covered here | Not covered here |
mGBA covers the widest range because the project describes itself as an emulator for Game Boy, Game Boy Color and Game Boy Advance. That means one install handles the first three generations. If you plan a Gen 1 or Gen 2 run and want to compare dedicated options, our Game Boy Color emulator roundup lists alternatives. For a Nuzlocke, though, the extra alternatives matter less than a predictable save file and removable shortcuts, and mGBA delivers both.
melonDS earns the DS slot because it is the actively maintained, free, open-source option, and its repository showed activity as recently as October 21, 2026. A 3DS Nuzlocke needs Azahar, since melonDS cannot run 3DS software at all. That split is easy to forget when you search for a single best emulator for pokemon, because no one program handles every handheld generation. Plan to keep three small installs side by side.
Choose the game itself with difficulty in mind. Emerald and FireRed give a classic Gen 3 feel with well-documented routes. HeartGold and SoulSilver stretch across two regions, which makes for a long run. Black 2 and White 2 are known among Nuzlocke fans for tougher trainers. Omega Ruby and X offer big areas but fewer hard trainers, so many players add a level cap there. These are opinions, not benchmarks. Pick the game you want to finish, because motivation decides more Nuzlockes than difficulty ever does.
Steps 1-3: Write the Rules, Choose Emulators, Install Everything
Step 1: Write your ruleset as a file. A written ruleset prevents the mid-run argument about whether a gift Pokémon counts. Create a folder called nuzlocke and save the file below as rules.json. The save guard script in Step 11 stores its own copy of the core rules, but this file is the human-readable contract you can show to friends or an audience.
{
"run_name": "Emerald hardcore",
"game": "Pokemon Emerald",
"first_encounter_only": true,
"dupes_clause": true,
"shiny_clause": true,
"nickname_everything": true,
"faint_means_dead": true,
"whiteout_ends_run": true,
"save_states": "banned",
"rewind": "banned",
"cheats": "banned",
"fast_forward": "allowed",
"level_cap": "ace of the next gym leader"
}
Decide now what happens with edge cases. Gift Pokémon, static legendary encounters and trade evolutions are the usual sources of dispute. Most communities treat gifts as free picks and static encounters as their own area, but the right answer is whatever you write down. If you want a harder base game, a fan-made ROM hack can reset expectations, and our Pokémon Unbound setup with mGBA shows the patching steps for one popular example.
Step 2: Match each game to its emulator. Use the generation table above. Write the decision at the top of your rules file or in a note. A run on Emerald uses mGBA only. A run on HeartGold uses melonDS only. A run on Omega Ruby uses Azahar only. If you plan several runs at once, give each its own folder, its own save location and its own entry in the project from Step 11.
Step 3: Install the three emulators into versioned folders. Download each program from its official site only, and avoid mirror sites, because emulator names attract fake installers. Put each build in a folder that carries its version number, so a later update never replaces the build you started the run on. The layout below keeps games, saves and emulators separate.
nuzlocke/
rules.json
roms/
gba/ (Emerald, FireRed)
nds/ (HeartGold, Black 2)
3ds/ (X, Omega Ruby)
emulators/
mgba-0.10.5/
melonDS-1.1/
azahar-2126.2/
notes/
graveyard.txt
On Windows, unzip each download into its folder and run the executable once to confirm it opens. On macOS, the first launch may trigger a Gatekeeper warning, which you clear through System Settings under Privacy and Security. On Linux, AppImage builds need the execute permission set before they start. Check your Python install while you are here, since Step 11 depends on it.
python3 --version
# Windows users can try: py --version
# Expected: Python 3.9 or newer
Do not install the Azahar 2127.0 alpha builds for this project. The developers label them as prereleases, and a prerelease may change how save data is stored between alpha numbers. If a feature in a later alpha tempts you, finish the current run first, then test the new build on a throwaway game.
Steps 4-6: Add Your Games, Then Strip Out Save States, Rewind and Cheats
Step 4: Add your own game files and system files. Copy the dumps you made from your own cartridges and consoles into the matching roms subfolder. Azahar’s own project description says users must supply legally usable games and system files, and the same principle applies to every emulator here. For DS games, melonDS can run with its built-in replacement firmware for most titles, but dumping the BIOS and firmware from your own DS gives the most faithful behavior. Keep the file names simple and avoid renaming a game after the first launch, because some emulators tie the save file name to the ROM name.
Launch each game once and reach the title screen. Do not play yet. Confirm the right language, the right region and a working audio output. Then close the emulator cleanly through its menu. The goal of this pass is to confirm the file loads, not to touch the save. If a game refuses to start, jump to the troubleshooting table near the end.
Step 5: Lock down mGBA. Open mGBA, then use Tools and Settings. Menu labels shift slightly between builds, so look for the pages named Keyboard, Shortcuts and Emulation. On the shortcut pages, clear every binding for quick save, quick load, load state slots and save state slots. On the Emulation page, set the rewind history to zero or untick the rewind option, and clear any option that loads a state when a game starts or saves a state when it closes. Leave the cheat manager alone, and simply never open Tools then Cheats during the run.
Be realistic about what this achieves. Removing hotkeys stops accidents, but the menu entries for save states still exist, so a determined player can click them. Real enforcement comes from the audit step later, which detects a rewound save. Think of the lockdown as a seatbelt rather than a vault, and treat the ruleset as an agreement with yourself.
Step 6: Lock down melonDS and Azahar. In melonDS, open the Config menu and find the input and hotkeys dialog. Its hotkey tab lists actions such as quick save, quick load and undo of a state load, and each can be cleared. Also look for the cheat dialog and leave it empty. If you plan to trade between two DS games later, the melonDS multiplayer setup covers the networking side without touching your hardcore save.
In Azahar, open the Emulation menu and then Configure, go to Controls and find the Hotkeys tab. Clear quick save and quick load, and avoid the slot-based Save State and Load State entries in the Emulation menu. Azahar also offers a turbo speed toggle, which is a convenient way to fast-forward overworld travel. Keep it for walking and menus, and be careful in battles, because a missed text box can hide a faint. The table lists where to look in each program.
| Feature to disable | mGBA 0.10.5 | melonDS 1.1 | Azahar 2126.2 |
|---|---|---|---|
| Quick save and quick load hotkeys | Tools, Settings, Shortcuts | Config, Input and hotkeys, Hotkeys | Configure, Controls, Hotkeys |
| Slot-based save states | Clear slot shortcuts, avoid the Savestates menu | Avoid the save state entries under the system menu | Avoid Save State and Load State in the Emulation menu |
| Rewind | Settings, Emulation, rewind history to zero | No rewind option found in the stable build | No rewind option found in the stable build |
| Cheats | Leave the cheat manager closed | Leave the cheat dialog empty | Leave the cheats window closed |
| Autoload or autosave of states | Untick in Emulation settings | Check config for state options | Check config for state options |
Menu names in this table reflect the current stable builds, but each project reorganizes dialogs from time to time. If you cannot find an entry, search the settings window for the words state, rewind or hotkey. Take a screenshot of your cleaned settings for each emulator and store it in the notes folder. That small record helps you notice if an update silently restores a default.
Steps 7-8: Set Up In-Game Saves and Automatic Backups
Step 7: Learn where each emulator keeps the real save. Pokémon games use an in-game save, which the emulator stores in a file. In mGBA, that is normally a .sav file written next to the ROM or in the folder you chose in the Paths settings. It appears only after you save in the game for the first time, so do not panic if the folder looks empty on day one. In melonDS, the DS cartridge save is also a .sav file that sits beside the ROM by default. In Azahar, the save lives deep inside the emulator’s user folder under the sdmc directory, inside the data folder for that game’s title ID, and for Pokémon it is typically named 00000001.sav. Use the file menu to open the user folder rather than hunting by hand.
Write the three paths in your notes file now. You will pass one of them to the save guard in Step 11. Then adopt a single saving habit: use the in-game Save command, wait for the confirmation message, and only then quit the emulator. Closing the window while a save is in progress is the quickest way to corrupt a file, and that is true for every emulator on this list. The same applies to a crash, so avoid pushing your luck with background downloads during long sessions.
Step 8: Build a backup routine. A Nuzlocke has a built-in failure mode that normal playthroughs do not: the save is the run. Lose it and the story ends. Copy the save file to a dated backup before each gym, before each major evolution decision and at the end of every session. If you use cloud sync for your documents folder, keep backups in a separate folder, since a sync tool that overwrites the live save with an older copy can quietly undo hours of play. Our Pokémon emulator save backup guide walks through manual and automated methods for more setups.
Manual copies work, but they depend on memory, and memory is the first thing to fail at midnight. The project in Steps 11 to 13 automates the copies and adds a second benefit that plain backups lack. Each backup records a SHA-256 fingerprint of the save in a ledger. If a later save matches an earlier fingerprint, the script knows the file went backward in time, and it prints a warning. That is the closest an emulator can get to the tamper-evident seal of a cartridge.
Keep at least two backup generations outside the emulator folders, ideally on a second drive or a cloud folder that only receives copies. Test a restore once, early. Copy a backup over the live save with the emulator closed, reopen the game and confirm it loads. A backup you have never restored is a hope, not a plan.
Steps 9-10: Pick and Configure a Nuzlocke Tracker
Step 9: Choose a tracker. A tracker records each area, your first encounter, the result and who died. Several free web trackers exist, and none is an official Pokémon product. The site nuzlocketracker.org describes itself as free, login-free and compatible with games from Emerald through Generation 9, with customizable rulesets. Nuzlocke Redux presents itself as an actively maintained successor to the older nuzlocke.app, with multi-run encounter tracking, boss movesets and team matchup analysis. The older nuzlocke.app itself is reportedly paused, so avoid starting a new run there. The leaderboard site nuzlockes.gg calls itself a Nuzlocke leaderboard and attempt tracker and listed 129 games and 1,496 runs on the day we checked.
Mobile options exist too. An iPhone app called Stacked: Nuzlocke lists a free starting tier and a paid Pro upgrade, though its App Store page showed two different Pro prices when we looked, so check the current price before you buy. Other fan projects bundle emulators with a tracker, and one called MonHub advertises integrated mGBA, melonDS and Azahar support. We have not tested these, so treat them as leads rather than recommendations, and never run an unknown bundle on the PC that holds your main save backups.
Judge any tracker against a short checklist. These are the features that matter once a run passes the first two gyms:
- It supports your exact game version, including hacks if you plan to play one.
- It lets you edit the ruleset, so your clauses match the rules file from Step 1.
- It stores the encounter list as an exportable file, not only in browser memory.
- It shows level caps or boss data, which saves tab-switching during a gym fight.
- It works offline or exports to a format you can keep, because websites change and disappear.
Step 10: Configure the tracker to match your rules. Create a new run and select the exact game. Turn on the dupes clause and the shiny clause if your rules file lists them. Set the death rule to permanent, add your planned level cap source and give the run the same name as the folder on your drive. If the tracker supports exports, save a first export right away and store it in notes. A browser tracker can lose data when you clear cookies, and an export is the cheap insurance.
A tracker and the command-line ledger from Step 11 overlap on purpose. The tracker is better for planning and visuals. The ledger is better as a tamper-evident record, because it ties each entry to a time and keeps the save fingerprints next to it. Using both feels like double entry for the first hour, then becomes automatic. If they ever disagree, the save file wins, and you correct the tracker to match.
Steps 11-13: Install the Save Guard, Play Route 1, Audit Before Every Gym
Step 11: Install and initialize the save guard. The full script appears in the next section. Save it as nuzlocke_kit.py inside your nuzlocke folder. It needs only the Python standard library, so there is nothing to install with pip. Initialize it by pointing at the live save file from Step 7. The command below assumes an Emerald run in mGBA.
python3 nuzlocke_kit.py init --game "Emerald" --save "~/nuzlocke/roms/gba/Pokemon Emerald.sav"
python3 nuzlocke_kit.py snapshot
python3 nuzlocke_kit.py watch --interval 60
The init command creates a run folder in your home directory called nuzlocke-run, writes the configuration and starts the ledger. The snapshot command copies the save if it changed. The watch command repeats that check every 60 seconds, so leave it running in a terminal window while you play. On Windows, replace python3 with py, and use a normal Windows path in quotes.
Step 12: Play the first route and log every encounter. Start the game, pick your starter, nickname it and save inside the game. Walk onto the first route and note the first wild Pokémon you meet. Log it immediately, before the battle, so you cannot talk yourself into a better roll afterward. The command rejects a second entry for the same route and rejects a duplicate species when the dupes clause is on.
python3 nuzlocke_kit.py encounter "Route 101" Zigzagoon --nick Zig
python3 nuzlocke_kit.py encounter "Route 102" Ralts --nick Ralph
python3 nuzlocke_kit.py encounter "Route 103" Wingull --result fled
python3 nuzlocke_kit.py faint Zig --cause "Roxanne's Geodude"
Use --result with caught, fled, failed or skipped. A fled or failed first encounter still uses up the area, which is exactly what the rule demands. When a Pokémon faints, run the faint command right away, then move the Pokémon into a box you name Graveyard so it can never rejoin your party. Add a line to graveyard.txt with the cause of death. Fans of the format love those notes, and they help you spot patterns, like repeated losses to the same trainer type.
Step 13: Audit before every gym and at the end of every session. Run a snapshot, then a report. A clean run shows zero rollback warnings. If the report shows one, open the ledger, check the timestamp and decide whether it was an honest mistake, such as restoring a backup after a crash. Write that decision in your notes so the run stays honest. Then compare your party with the tracker and confirm every team member is on the alive list.
python3 nuzlocke_kit.py snapshot
python3 nuzlocke_kit.py report
# Pre-gym checklist
# 1. Snapshot taken and report shows 0 rollback warnings
# 2. Party matches the tracker's alive list
# 3. Level cap checked against the gym leader's ace
# 4. Graveyard box contains every fainted Pokemon
# 5. Tracker export saved to notes/
For a deeper audit, open the live save in a save editor in read-only fashion and compare the party and box contents with your ledger. Our guide to PKHeX for Pokémon saves explains how to inspect a save safely. Use it only to look, never to edit, because edits count as cheating under almost every Nuzlocke ruleset.
The Complete Project: nuzlocke_kit.py
Here is the full working script. It runs about 170 lines, uses no outside libraries and stores everything in plain files you can read, copy and archive. We ran it on Python 3.12 against a test save and confirmed that it blocks duplicate routes, rejects duplicate species and warns when a save returns to an earlier fingerprint. Edit the RUN_DIR line at the top if you want the data somewhere other than your home folder.
#!/usr/bin/env python3
"""nuzlocke_kit.py: save guard, encounter ledger and run report for emulator Nuzlockes."""
import argparse
import hashlib
import json
import shutil
import sys
import time
from datetime import datetime
from pathlib import Path
RUN_DIR = Path.home() / "nuzlocke-run"
CONFIG = RUN_DIR / "run.json"
LEDGER = RUN_DIR / "ledger.jsonl"
BACKUPS = RUN_DIR / "backups"
DEFAULT_RULES = {
"first_encounter_only": True,
"dupes_clause": True,
"nickname_everything": True,
"faint_means_dead": True,
"level_cap_note": "Do not out-level the next gym leader's ace",
}
def now():
return datetime.now().isoformat(timespec="seconds")
def sha256(path):
digest = hashlib.sha256()
with open(path, "rb") as handle:
for chunk in iter(lambda: handle.read(65536), b""):
digest.update(chunk)
return digest.hexdigest()
def load_events():
if not LEDGER.exists():
return []
with open(LEDGER, encoding="utf-8") as handle:
return [json.loads(line) for line in handle if line.strip()]
def append(event):
event["time"] = now()
RUN_DIR.mkdir(parents=True, exist_ok=True)
with open(LEDGER, "a", encoding="utf-8") as handle:
handle.write(json.dumps(event) + "\n")
def load_config():
if not CONFIG.exists():
sys.exit("No run found. Start one with: nuzlocke_kit.py init --game NAME --save PATH")
return json.loads(CONFIG.read_text(encoding="utf-8"))
def cmd_init(args):
RUN_DIR.mkdir(parents=True, exist_ok=True)
BACKUPS.mkdir(exist_ok=True)
config = {"game": args.game, "save": str(Path(args.save).expanduser()), "rules": DEFAULT_RULES}
CONFIG.write_text(json.dumps(config, indent=2), encoding="utf-8")
append({"type": "init", "game": args.game})
print("Run created in", RUN_DIR)
def cmd_snapshot(args):
config = load_config()
save = Path(config["save"])
if not save.exists():
sys.exit("Save file not found: " + str(save))
current = sha256(save)
saves = [e for e in load_events() if e["type"] == "save"]
if saves and saves[-1]["sha"] == current:
print("No change since the last snapshot.")
return
seen = [e["sha"] for e in saves]
rolled_back = current in seen
BACKUPS.mkdir(exist_ok=True)
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
target = BACKUPS / (stamp + "_" + current[:8] + ".sav")
shutil.copy2(save, target)
append({"type": "save", "sha": current, "file": target.name, "rollback": rolled_back})
if rolled_back:
print("WARNING: this save matches an older snapshot. A reload or state load happened.")
else:
print("Snapshot stored:", target.name)
def cmd_watch(args):
print("Watching every", args.interval, "seconds. Press Ctrl+C to stop.")
try:
while True:
cmd_snapshot(args)
time.sleep(args.interval)
except KeyboardInterrupt:
print("Stopped.")
def cmd_encounter(args):
config = load_config()
events = load_events()
routes = {e["route"].lower() for e in events if e["type"] == "encounter"}
species = {e["species"].lower() for e in events
if e["type"] == "encounter" and e["result"] == "caught"}
if config["rules"]["first_encounter_only"] and args.route.lower() in routes:
sys.exit("Route already used: " + args.route + ". First encounter only.")
if config["rules"]["dupes_clause"] and args.species.lower() in species:
sys.exit(args.species + " is a duplicate. Skip it and roll the next encounter.")
append({"type": "encounter", "route": args.route, "species": args.species,
"result": args.result, "nick": args.nick or ""})
print("Logged:", args.route, "->", args.species, "(" + args.result + ")")
def cmd_faint(args):
events = load_events()
nicks = {e["nick"].lower() for e in events if e["type"] == "encounter" and e["nick"]}
if args.nick.lower() not in nicks:
sys.exit("No logged Pokemon with the nickname " + args.nick)
append({"type": "faint", "nick": args.nick, "cause": args.cause})
print(args.nick, "is dead. Move it to the graveyard box.")
def cmd_report(args):
events = load_events()
caught = [e for e in events if e["type"] == "encounter" and e["result"] == "caught"]
dead = {e["nick"].lower() for e in events if e["type"] == "faint"}
alive = [e for e in caught if e["nick"].lower() not in dead]
rollbacks = [e for e in events if e["type"] == "save" and e["rollback"]]
snapshots = [e for e in events if e["type"] == "save"]
print("Encounters logged :", len([e for e in events if e["type"] == "encounter"]))
print("Caught :", len(caught))
print("Alive :", len(alive))
print("Dead :", len(dead))
print("Snapshots :", len(snapshots))
print("Rollback warnings :", len(rollbacks))
for entry in alive:
print(" alive:", entry["nick"] or "(no nickname)", "the", entry["species"], "from", entry["route"])
for entry in rollbacks:
print(" rollback at", entry["time"], "file", entry["file"])
return 1 if rollbacks else 0
def build_parser():
parser = argparse.ArgumentParser(description="Nuzlocke save guard and ledger")
sub = parser.add_subparsers(dest="command", required=True)
p = sub.add_parser("init")
p.add_argument("--game", required=True)
p.add_argument("--save", required=True)
p.set_defaults(func=cmd_init)
p = sub.add_parser("snapshot")
p.set_defaults(func=cmd_snapshot)
p = sub.add_parser("watch")
p.add_argument("--interval", type=int, default=60)
p.set_defaults(func=cmd_watch)
p = sub.add_parser("encounter")
p.add_argument("route")
p.add_argument("species")
p.add_argument("--result", choices=["caught", "fled", "failed", "skipped"], default="caught")
p.add_argument("--nick")
p.set_defaults(func=cmd_encounter)
p = sub.add_parser("faint")
p.add_argument("nick")
p.add_argument("--cause", default="unknown")
p.set_defaults(func=cmd_faint)
p = sub.add_parser("report")
p.set_defaults(func=cmd_report)
return parser
if __name__ == "__main__":
arguments = build_parser().parse_args()
sys.exit(arguments.func(arguments) or 0)
The design is deliberately simple. Every action appends one JSON line to ledger.jsonl, and nothing ever edits or deletes a line. That append-only habit makes the ledger easy to audit. The snapshot function computes a SHA-256 hash of the save, compares it with the last stored hash and copies the file into backups only when the hash changed. If the new hash already exists in the history but is not the latest, a rollback happened, and the script flags that entry.
The encounter command replays the ledger to check the first-encounter and dupes rules. The faint command looks up the nickname and records the death. The report command summarizes caught, alive and dead counts and exits with a non-zero code if any rollback warnings exist, which is handy if you later wire it into a small shell script or a stream overlay. You can extend it with a shiny clause flag, a level cap table or a command that exports the ledger to CSV for your tracker.
Two limits are worth stating plainly. The script detects a save that goes backward in time, but it cannot tell whether you used a save state and then saved in the game again at a later point, because that produces a new, legitimate-looking fingerprint. It also cannot see inside the save file, so it does not know your party. Treat it as a tripwire plus a diary, not as a referee.
Output Examples: What a Healthy Run Log Looks Like
A normal first session
Seeing the expected output saves time when something looks wrong. The first block shows a normal first session: initialization, a snapshot, a second snapshot with no change and three encounters, including one that the rules rejected.
$ python3 nuzlocke_kit.py init --game Emerald --save ~/Emerald.sav
Run created in /home/you/nuzlocke-run
$ python3 nuzlocke_kit.py snapshot
Snapshot stored: 20261022-183608_d0ae613c.sav
$ python3 nuzlocke_kit.py snapshot
No change since the last snapshot.
$ python3 nuzlocke_kit.py encounter "Route 101" Zigzagoon --nick Zig
Logged: Route 101 -> Zigzagoon (caught)
$ python3 nuzlocke_kit.py encounter "Route 101" Poochyena --nick Pooch
Route already used: Route 101. First encounter only.
$ python3 nuzlocke_kit.py encounter "Route 102" Zigzagoon --nick Zag
Zigzagoon is a duplicate. Skip it and roll the next encounter.
The rollback tripwire
The second block shows the rollback tripwire. In this test we replaced the live save with an earlier copy, which is what a loaded save state or a restored backup looks like to the script. The warning appears at the moment the next snapshot runs, and the final report counts it.
$ python3 nuzlocke_kit.py snapshot
WARNING: this save matches an older snapshot. A reload or state load happened.
$ python3 nuzlocke_kit.py faint Zig --cause "Roxanne's Geodude"
Zig is dead. Move it to the graveyard box.
$ python3 nuzlocke_kit.py report
Encounters logged : 2
Caught : 2
Alive : 1
Dead : 1
Snapshots : 3
Rollback warnings : 1
alive: Ralph the Ralts from Route 102
rollback at 2026-10-22T18:36:08 file 20261022-183608_d0ae613c.sav
Read the report from top to bottom. Encounters should equal the number of areas you have visited. Caught minus dead should equal the number of living team and box members. Snapshots should climb steadily as you play, and rollback warnings should stay at zero unless you restored a backup on purpose. If the snapshot count never moves while you play, the script is watching the wrong file, and the first troubleshooting row below fixes that.
Common Pitfalls That Break Emulator Nuzlockes
These mistakes cost players runs far more often than hard gym leaders do. Each one has a simple prevention step.
- Trusting the honor system alone. Hotkeys return after a settings reset or an update. Photograph your settings after setup and recheck them whenever you update an emulator.
- Updating the emulator mid-run. A new build can change save handling or controls. Keep the version folders from Step 3 and update only between runs, especially for Azahar, where the 2127.0 alpha builds are prereleases.
- Switching emulators for the same game. A save written by one program may not load cleanly in another. Pick one emulator per run and stay on it.
- Quitting during a save. Closing the window while the game writes its data can corrupt the file. Wait for the confirmation message, then close.
- Logging encounters after the fight. If you log afterward, you can bend the rule in your favor without noticing. Log before the first move.
- Renaming the ROM after the first save. Some emulators link the save file name to the game file name, so a rename can look like a lost save. Rename only before you start.
- Relying on one backup location. A single drive, a single sync folder or a single cloud copy can fail or overwrite the wrong version. Keep two generations in two places.
- Over-trusting fast-forward. Turbo through a battle and you may skip the message that a Pokémon fainted. Slow down for any fight that could cost you a team member.
One more habit pays off over a long run: write down the reason for every rule exception the moment you grant it. If a glitch freezes the game during a catch, note it, take a screenshot and decide the outcome before you reopen the file. Future you will not remember the details, and an unwritten exception quickly becomes an excuse.
Troubleshooting: 10 Common Problems and Fixes
| Problem | Likely cause | Fix |
|---|---|---|
| Script says “Save file not found” | Wrong path or no in-game save yet | Save once inside the game, then confirm the exact path and quote it, especially if it has spaces |
| Snapshots never increase | Script watches a different file than the emulator writes | Check the emulator’s save folder setting and rerun init with the correct path |
| Game starts from the beginning after reopening | The game was never saved in-game, or the save file moved | Use the in-game Save command, and check the emulator’s Paths settings for the save location |
| Rollback warning after a crash | You restored a backup on purpose | Record the reason in notes, accept the entry as a documented restore, and continue |
| Azahar game will not boot | Damaged or encrypted dump, or missing system files | Re-dump from your own console and confirm the system files are in place |
| melonDS shows a new empty save | ROM file name changed or the save folder changed | Restore the old name or point the save path back to the original folder |
| mGBA ignores my cleared hotkeys | Settings were saved in a different config location | Close all instances, reopen mGBA, reapply the changes and check the portable config option |
| “python3” is not recognized on Windows | Windows uses the py launcher | Run py nuzlocke_kit.py or install Python from the official site |
| Permission error when copying the save | The emulator holds the file open at that moment | Wait a few seconds and retry, or take the snapshot after you close the emulator |
| Tracker shows the wrong encounter table | Wrong game version selected | Create the run again with the exact version and import your exported data if needed |
If none of those rows fits, work backward from the file. Check the emulator, then the ROM name, then the save path, then the backup copy. Most problems on this list come down to a path that points at the wrong place, so print the full path with a file manager before you assume the software is broken. When in doubt, restore the newest good backup into a scratch copy of the emulator folder and test there, which keeps the live run untouched.
Advanced Tips for Long Runs
Add level caps you can verify. Create a short table in your notes with each gym leader’s strongest Pokémon level and update your cap before every gym. Many players treat the cap as a soft rule, but a written number turns it into a testable one. You can store the number in the ledger by adding a new command that records the cap, then check your team against it during the audit.
Try a handheld or phone for the commute. Community ports exist for Android, including a melonDS Android build identified as version 2.0.1 from May 2026, though it is not an official melonDS release, and Azahar is reported for Android as well. If you want to carry a run on a phone, our Android Pokémon emulator guide covers the apps and controller setup. Keep one device as the home of the live save, and copy backups across instead of syncing both ways.
Race a friend with the same ledger format. Because the ledger is plain text, two players can compare runs line by line and share each other’s death logs. If you want to test trading in a challenge, our guide to trading between emulators explains the options, but remember that trades change the encounter rules, so decide whether traded Pokémon count as caught.
Stream the run with overlays. Capture the emulator window in OBS, add the tracker in a browser source and show the report output in a terminal window. Viewers trust a run more when the rollback counter is visible. If your stream has chat commands, you can let viewers vote on nicknames, which keeps the nickname rule fun rather than a chore.
Harden the setup further. Make the live save read-only between sessions with your operating system’s file permissions, then release the lock only when you play. Mount the backup folder as append-only if your system supports it. Keep the emulator folders on a drive you do not sync, and consider a second player or friend who holds the weekly backup, because an outside witness is the strongest anti-cheat of all. The takeaway: small friction plus a visible record keeps a hardcore run honest, and that matters more than any single setting.
FAQ: Pokémon Nuzlocke on Emulators
Which emulator is best for a Pokémon Nuzlocke?
Use the one that matches the game’s platform. mGBA 0.10.5 handles Game Boy, Game Boy Color and GBA titles, melonDS 1.1 handles DS games and Azahar 2126.2 handles 3DS games. No single program covers all three, and a run should stay on one emulator from start to finish.
Can I use save states in a Nuzlocke?
Standard rules say no, because a save state lets you undo a faint. Some casual groups allow them for crash recovery only. Whatever you decide, write it in your rules file and disable the hotkeys so you cannot trigger them by accident.
Is it legal to play Pokémon on an emulator?
Emulators themselves are generally legal software. The legal risk comes from game files. Use dumps from cartridges and consoles you own, and do not download ROMs from the internet. Laws differ by country, so check the rules where you live.
Do I need the Azahar alpha builds for a Nuzlocke?
No. Azahar 2126.2 is the stable build as of October 22, 2026, while 2127.0 Alpha 3 is a prerelease. Stable builds are the safer choice for a run that may last weeks, and you can test alpha builds on a throwaway game later.
How do I stop myself from cheating by accident?
Clear the quick save, quick load and rewind bindings, leave cheat menus closed and run the save guard so a rollback shows up in a report. Friction plus a visible record is more reliable than willpower, especially during late-night sessions.
Which Nuzlocke tracker should I use?
Pick one that supports your exact game and lets you export data. Free web trackers such as nuzlocketracker.org and Nuzlocke Redux cover many games, and the ledger from this guide works alongside any of them. Always keep an exported copy of the run.
What happens if my save file gets corrupted?
Restore the newest backup into the live save location with the emulator closed, then reopen the game. Log the restore in your notes so the next audit makes sense. Most Nuzlocke groups treat a corruption restore as legitimate when you can show the backup trail.
Can I use the same setup for ROM hacks?
Yes, with care. Hacks often use their own encounter tables, so confirm that your tracker supports them or log encounters by hand with the ledger. Patched ROMs can also change the save type, so test the save and restore cycle before you commit to a run.





