NetHack
The DevTeam's 1987 descendant of Rogue and Hack, read through the NetHack 3.6.7 source.
On this page
NetHack’s README calls it “a distant descendent of Rogue and Hack”1. The atlas reads NetHack 3.6.7, whose README is dated February 2023, so every claim here names that version.
The hunger-clock starts at 900 nutrition, lower than Rogue’s 1,300, and drops by 1 a turn, with more taken by rings, amulets, a heavy load, and some properties2,3. A hero turns Hungry at 150 and Weak at 50, and starves at a depth set by Constitution4. Where Rogue caps a meal at 2,000, NetHack can choke a hero who keeps eating while Satiated5,6.
The melee roll keeps Rogue’s rate of 5% per point of level and armor class, and adds Luck7. An ordinary level fills free rectangles with rooms and joins them from left to right8. One level copies Rogue’s 3 × 3 grid instead9.
A death ends the program unless an amulet of life saving or a debug mode intervenes10. Restoring deletes the save, except in wizard and explore modes11, and those modes ask “Die?” at a death12. Unidentified items’ descriptions are shuffled each game13.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Hunger clock | Overeating chokes | 900 nutrition at the start, less 1 a turn plus rings, amulets, and loads; Hungry at 150, Weak at 50, starved below −(100 + 10 × Constitution); a Satiated meal past 2,000 can choke |
| Hit chance | d20 against armor class | a 1–20 roll below 1 + Luck + level + armor class + bonuses: 5% a point, as in Rogue |
| Procedural dungeon | Free rectangles | up to 40 rooms in free rectangles, joined from left to right, plus extra corridors that can dead-end |
| Procedural dungeon | Room grid | one level in Rogue's 3 × 3 style, each cell an intersection 1 time in 5 |
| Permadeath | Whole run | restoring deletes the save, except in wizard and explore modes, which also offer to refuse a death |
| Item identification | Shuffled appearances | descriptions shuffled within seven classes and four armor groups |
Every claim about this game, by concept (12)
References
- ^NetHack's README calls the game “a distant descendent of Rogue and Hack”.DeveloperNetHack 3.6.7 source
How we know
4which is a distant descendent of Rogue and Hack, and a direct descendent ofREADME lines 3–5 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^NetHack 3.6.7 starts the hero at 900 nutrition. Once per game turn, and again for each melee attack the hero makes, it takes 1, unless the hero has slow digestion, is polymorphed into a form that doesn't eat, or is praying under divine protection, and only at a chance of 1 in 10 while the hero is asleep or fainted. On odd turns, regeneration (not from an artifact) and a Stressed or heavier load each take 1 more; on even turns, the Hunger property and Conflict (not from an artifact) do, and once every 20 turns each worn ring that has a charge other than 0 or takes none, the worn amulet, and carrying the Amulet of Yendor take 1 each.CodeNetHack 3.6.7 sourcerevised
How we know
119 u.uhunger = 900;src/eat.c lines 116–120 at ed600d9 · upstream ↗ 176 /* once-per-turn things go here */src/allmain.c lines 172–176 at ed600d9 · upstream ↗ 281 gethungry();src/allmain.c line 281 at ed600d9 · upstream ↗ 2799 if ((!Unaware || !rn2(10)) /* slow metabolic rate while asleep */src/eat.c lines 2790–2804 at ed600d9 · upstream ↗ 2821 switch ((int) (moves % 20)) { /* note: use even cases only */src/eat.c lines 2806–2841 at ed600d9 · upstream ↗ 373#define Unaware (multi < 0 && (unconscious() || is_fainted()))include/youprop.h line 373 at ed600d9 · upstream ↗ 25 SLT_ENCUMBER = 1, /* Burdened */include/hack.h line 25 at ed600d9 · upstream ↗ 1873 if (p_type == 3 && !Inhell) { ⋮ 1877 u.uinvulnerable = TRUE;
src/pray.c lines 1873–1877 at ed600d9 · upstream ↗ 1978overexertion() ⋮ 1983 gethungry();
src/hack.c lines 1975–1983 at ed600d9 · upstream ↗ 408 || overexertion()) 409 goto atk_done;
src/uhitm.c lines 406–409 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Revised after a check by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^Rogue 5.4 starts the hero with 1,300 food. The
stomachdaemon runs once per pass of the command loop, after the hero's move, or after both moves when the hero is hasted. While food is above 0 it takes 1, plus each worn ring's cost, and 1 less while the hero carries the Amulet of Yendor, so with the Amulet and no rings the count stops.CodeRogue 5.4.4 sourceHow we know
29 food_left = HUNGERTIME;init.c line 29 at cf9bd26 · upstream ↗ 117#define HUNGERTIME 1300rogue.h line 117 at cf9bd26 · upstream ↗ 150 start_daemon(stomach, 0, AFTER);main.c line 150 at cf9bd26 · upstream ↗ 32 if (on(player, ISHASTE))command.c lines 32–39 at cf9bd26 · upstream ↗ 444 do_daemons(AFTER);command.c line 444 at cf9bd26 · upstream ↗ 163 food_left -= ring_eat(LEFT) + ring_eat(RIGHT) + 1 - amulet;daemons.c lines 160–163 at cf9bd26 · upstream ↗ 148 amulet = TRUE;pack.c lines 147–148 at cf9bd26 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^NetHack 3.6.7 counts the hero Satiated above 1,000 nutrition, Not Hungry above 150, Hungry above 50, Weak above 0, and Fainting at 0 or below. While Fainting, a hero not already fainted or helpless faints on entering the state, and later at a chance that grows as nutrition falls (1 in 20 at 0), for 10 turns plus a tenth of the deficit; on an update where neither happens, the hero starves once nutrition is below −(100 + 10 × Constitution).CodeNetHack 3.6.7 source
How we know
2939 : (h > 50) ? HUNGRY : (h > 0) ? WEAK : FAINTING;src/eat.c lines 2936–2939 at ed600d9 · upstream ↗ 2983 if (u.uhs <= WEAK || rn2(20 - uhunger_div_by_10) >= 19) {src/eat.c lines 2977–2999 at ed600d9 · upstream ↗ 3004 } else if (u.uhunger < -(100 + 10 * (int) ACURR(A_CON))) {src/eat.c lines 3001–3008 at ed600d9 · upstream ↗ 2842 newuhs(TRUE);src/eat.c line 2842 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^Eating any food in Rogue 5.4 first raises a negative count to 0, then adds 1,100 to 1,499 (
HUNGERTIME - 200 + rnd(400)), capped at 2,000.CodeRogue 5.4.4 sourceHow we know
298 if ((food_left += HUNGERTIME - 200 + rnd(400)) > STOMACHSIZE)misc.c lines 296–300 at cf9bd26 · upstream ↗ 119#define STOMACHSIZE 2000rogue.h line 119 at cf9bd26 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^In NetHack 3.6.7, food that brings nutrition to 2,000 or more chokes a hero who was Satiated (for a meal, at its start). A hero who doesn't breathe always vomits and loses 1,000 nutrition instead; a hero being strangled always dies; any other hero vomits 1 time in 20 and otherwise dies.CodeNetHack 3.6.7 sourcerevised
How we know
2864 if (u.uhunger >= 2000) {src/eat.c lines 2863–2865 at ed600d9 · upstream ↗ 2543 context.victual.canchoke = (u.uhs == SATIATED);src/eat.c line 2543 at ed600d9 · upstream ↗ 252 if (Breathless || (!Strangled && !rn2(20))) {src/eat.c lines 238–260 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^Computed from both games' code, NetHack 3.6.7's melee roll keeps the rate of Rogue 5.4's
swing: each point of the attacker's level or the target's armor class adds 5% to the chance to hit, which is (level + armor class + bonuses) / 20 in Rogue and (level + armor class + Luck + bonuses) / 20 in NetHack.CodeNetHack 3.6.7 source · Rogue 5.4.4 sourcerevisedHow we know
266 tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitincsrc/uhitm.c lines 266–267 at ed600d9 · upstream ↗ 613 int mhit = (tmp > dieroll || u.uswallow);src/uhitm.c lines 612–613 at ed600d9 · upstream ↗ 172 x = RND(x) + 1;src/rnd.c line 172 at ed600d9 · upstream ↗ 381 int need = (20 - at_lvl) - op_arm;fight.c lines 378–384 at cf9bd26 · upstream ↗ 188 return range == 0 ? 0 : abs((int) RN) % range;main.c lines 186–189 at cf9bd26 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^An ordinary NetHack 3.6.7 level places rooms, one of which may be a vault, in free rectangles until none is left, a room fails to fit, or it has 40. It sorts them by left edge, and joins each to the next, stopping early at a chance of 1 in 50 per join. It then joins each room to the one two along when they aren't yet connected, then any pair still unconnected. With 3 or more rooms it makes 4 to (room count + 3) attempts at another corridor; an attempt gives up before digging when the square outside its door is already open, and stops short, leaving a dead end, at a chance of 1 in 35 per step.CodeNetHack 3.6.7 sourcerevised
How we know
229 while (nroom < MAXNROFROOMS && rnd_rect()) {src/mklev.c lines 227–239 at ed600d9 · upstream ↗ 330#define MAXNROFROOMS 40 /* max number of rooms per level */include/global.h line 330 at ed600d9 · upstream ↗ 62 if (x->lx < y->lx)src/mklev.c lines 62–64 at ed600d9 · upstream ↗ 704 sort_rooms();src/mklev.c lines 700–704 at ed600d9 · upstream ↗ 341 for (i = rn2(nroom) + 4; i; i--) {src/mklev.c lines 319–348 at ed600d9 · upstream ↗ 294 if (nxcor && levl[xx + dx][yy + dy].typ)src/mklev.c lines 294–295 at ed600d9 · upstream ↗ 2248 if (cct++ > 500 || (nxcor && !rn2(35)))src/sp_lev.c line 2248 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^NetHack 3.6.7's main dungeon, the Dungeons of Doom, holds one level built in Rogue's style. Its code's comment says “Rogue levels are structured 3 by 3, with each section containing a room or an intersection”, and each of the nine cells is an intersection at a chance of 1 in 5, so long as at least one room is made.CodeNetHack 3.6.7 source
How we know
20LEVEL: "rogue" "R" @ (15, 4)dat/dungeon.def lines 17–21 at ed600d9 · upstream ↗ Rogue levels are structured 3 by 3, with each section containing
src/extralev.c#L209-L213 · upstream ↗ 233 if (!rn2(5) && (nroom || (x < 2 && y < 2))) {src/extralev.c lines 228–243 at ed600d9 · upstream ↗ 700 makeroguerooms();src/mklev.c lines 698–701 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^When a NetHack 3.6.7 hero dies, unless a worn amulet of life saving or the wizard and explore modes' “Die?” prompt saves the hero, the game records the score and ends the program.CodeNetHack 3.6.7 sourcerevised
How we know
1201 really_done(how);src/end.c lines 1168–1201 at ed600d9 · upstream ↗ 1626 nh_terminate(EXIT_SUCCESS);src/end.c lines 1618–1626 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^Restoring a NetHack 3.6.7 game deletes the save file once it is read, unless the game is in wizard (debug) or explore mode; in those modes the Unix build asks “Do you want to keep the save file?”. While a restore runs, the Unix build makes the file unreadable, to disallow parallel restores.CodeNetHack 3.6.7 source
How we know
901 if (!wizard && !discover)src/restore.c lines 901–902 at ed600d9 · upstream ↗ 295 (void) chmod(fq_save, 0); /* disallow parallel restores */sys/unix/unixmain.c line 295 at ed600d9 · upstream ↗ 312 if (yn("Do you want to keep the save file?") == 'n') {sys/unix/unixmain.c lines 307–319 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^When a NetHack 3.6.7 hero dies in wizard (debug) or explore mode, from any cause of death from an ordinary death to genocide (not a panic, quitting, escaping, or ascending), and a worn amulet of life saving hasn't already saved the hero, the game asks “Die?”; answering no saves the hero's life and play goes on.CodeNetHack 3.6.7 sourcerevised
How we know
1188 if (!survive && (wizard || discover) && how <= GENOCIDED 1189 && !paranoid_query(ParanoidDie, "Die?")) { ⋮ 1192 savelife(how); 1193 survive = TRUE;
src/end.c lines 1187–1194 at ed600d9 · upstream ↗ 1168 if (Lifesaved && (how <= GENOCIDED)) {src/end.c lines 1168–1186 at ed600d9 · upstream ↗ 148 GENOCIDED = 10,include/hack.h lines 137–153 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Revised after a check by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^When a NetHack 3.6.7 game starts, it shuffles the descriptions of amulets, potions, rings, scrolls, spellbooks, wands, and venoms within each class, and of helmets, gloves, cloaks, and boots within each of those groups. Water keeps a fixed description, and scrolls, spellbooks, and amulets keep the unique or non-magic ones fixed; a type whose name is already known keeps its description.CodeNetHack 3.6.7 source
How we know
179 shuffle_all();src/o_init.c lines 178–179 at ed600d9 · upstream ↗ 247 AMULET_CLASS, POTION_CLASS, RING_CLASS, SCROLL_CLASS,src/o_init.c lines 243–266 at ed600d9 · upstream ↗ 88 if (objects[j].oc_name_known)src/o_init.c lines 81–95 at ed600d9 · upstream ↗ 208 case POTION_CLASS: ⋮ 211 *hi_p = POT_WATER - 1; 212 break; ⋮ 219 if (objects[i].oc_unique || !objects[i].oc_magic) 220 break; 221 *hi_p = i - 1;
src/o_init.c lines 208–221 at ed600d9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
Sources
Code
- NetHack DevTeam. 2023. “NetHack 3.6.7 source code.” Commit ed600d9. NetHack/NetHack @ ed600d9 · licence, all uses
- Toy, Michael, Ken Arnold, and Glenn Wichman. 2016. “Rogue 5.4.4 source code.” Commit cf9bd26. Davidslv/rogue @ cf9bd26 · licence, all uses