Summary
fix_moon_phase=True promises deterministic time-based game effects when seeds are set (per the option docstring: moon phase, friday 13th, night, midnight). The implementation pins the struct tm path via nle_getlt_maybe_fixed() in src/hacklib.c, but ubirthday is still assigned from raw time(NULL) in src/u_init.c, and NetHack hashes ubirthday to vary flavor between games without consuming game RNG:
src/shknam.c:517 — shopkeeper name selection (nseed = ubirthday / 257)
src/shk.c:2085 — shop surcharge parity (gameplay-relevant)
src/mkroom.c:492 — antholemon() ant species (gameplay-relevant)
src/read.c:72 — scroll label hash
src/files.c:752 — bones pool selection
getyear() in src/hacklib.c also calls raw getlt(), bypassing the fixed-tm wrapper.
Observed impact
Fully seeded runs (env.seed(core, disp, reseed=False), fix_moon_phase=True, NLE 1.3.0, NetHackChallenge-derived env) of the same seed differ across wall-clock sessions:
- Shopkeeper names change between sessions (e.g. one seed produced "Ms. Possogroenoe" vs "Mr. Kabalebo" vs "Ms. Manlobbi" in sessions hours apart), while turns/score/deaths stay identical.
- A longer/shorter name can push a game message across a display page boundary, changing the keypress sequence an agent needs (extra/missing Space), so byte-exact action-trace replay breaks across sessions. In a 1000-trace corpus we saw a ~1.8% cross-session replay-validation failure rate from this.
- The price-parity and ant-species channels can in principle alter gameplay outcomes, not just cosmetics.
Suggested fix
Derive ubirthday from settings.time_seed when fix_moon_phase && time_seed_is_set, using the same ISAAC64 pattern as nle_fill_fixed_tm(), and route getyear() through nle_getlt_maybe_fixed(). No behavior change when fix_moon_phase is off.
Working patch (verified: same seed run in sessions 96s apart now yields identical action SHA-256 and identical shopkeeper name; no change to any seeded outcome in our regression fixtures): kenforthewin@c8ddc0a
Happy to open a PR from that branch if the approach looks right.
Summary
fix_moon_phase=Truepromises deterministic time-based game effects when seeds are set (per the option docstring: moon phase, friday 13th, night, midnight). The implementation pins thestruct tmpath vianle_getlt_maybe_fixed()insrc/hacklib.c, butubirthdayis still assigned from rawtime(NULL)insrc/u_init.c, and NetHack hashesubirthdayto vary flavor between games without consuming game RNG:src/shknam.c:517— shopkeeper name selection (nseed = ubirthday / 257)src/shk.c:2085— shop surcharge parity (gameplay-relevant)src/mkroom.c:492—antholemon()ant species (gameplay-relevant)src/read.c:72— scroll label hashsrc/files.c:752— bones pool selectiongetyear()insrc/hacklib.calso calls rawgetlt(), bypassing the fixed-tm wrapper.Observed impact
Fully seeded runs (
env.seed(core, disp, reseed=False),fix_moon_phase=True, NLE 1.3.0, NetHackChallenge-derived env) of the same seed differ across wall-clock sessions:Suggested fix
Derive
ubirthdayfromsettings.time_seedwhenfix_moon_phase && time_seed_is_set, using the same ISAAC64 pattern asnle_fill_fixed_tm(), and routegetyear()throughnle_getlt_maybe_fixed(). No behavior change whenfix_moon_phaseis off.Working patch (verified: same seed run in sessions 96s apart now yields identical action SHA-256 and identical shopkeeper name; no change to any seeded outcome in our regression fixtures): kenforthewin@c8ddc0a
Happy to open a PR from that branch if the approach looks right.