When using Jericho to evaluate LLM agents, most callers would expect FrotzEnv without a seed to be stochastic, so that multiple experimental runs would be independent from each other. The paper explicitly defines a fixed random seed as a "handicap" that authors should consciously choose and disclose:
Handicaps: In summary, to ease the difficulty of IF games, Jericho optionally provides the following handicaps: 1) Fixed random seed to enforce determinism... For reproducibility, we report the handicaps used by all algorithms in this paper and encourage future work to do the same.
However, the jericho-python API silently enables this handicap by default. Right now, the unseeded default does the exact opposite of what most callers assume: it locks the environment to a single, hardcoded seed.
Problem
FrotzEnv.seed() resolves an unspecified seed like this:
|
def seed(self, seed=None): |
|
''' |
|
Changes seed used for the emulator's random number generator. |
|
|
|
:param seed: Seed the random number generator used by the emulator. |
|
Default: use walkthrough's seed if it exists, |
|
otherwise use value of -1 which changes with time. |
|
:returns: The value of the seed. |
|
|
|
.. note:: :meth:`jericho.FrotzEnv.reset()` must be called before the seed takes effect. |
|
|
|
''' |
|
seed = seed or self.bindings.get('seed', -1) |
|
self._seed = seed |
|
return seed |
For any game with walkthrough bindings (every game in the bundled benchmark suite) self.bindings.get('seed', -1) returns a fixed, positive integer (12 for Zork I), not -1.
-1 (the time-randomized fallback) only applies to a ROM without bindings, which I assume is the least common scenario.
This is the reverse of common conventions (omit a seed → get randomness), so, for most games, it's easy to construct FrotzEnv(rom) with no seed argument and reasonably assume you have independent, stochastic episodes when you don't.
Why this matters
This introduces multiple risks for researchers who inadvertently make incorrect assumptions.
- Findings artificially weakened: Researchers testing agents typically assume "no seed passed" means "stochastic" (the standard convention in most environment APIs). When evaluating games with inherent randomness (like Zork), a fixed seed collapses the possibility space. The agent is not being tested on its ability to handle dynamic situations, varying combat outcomes, or bad luck; it is just replaying a single deterministic state path. This significantly weakens the validity of the evaluation.
- Undisclosed handicaps: The Jericho paper asks authors to disclose fixed-seed handicaps, but researchers cannot disclose a handicap they do not know was implicitly applied for them.
- Invisible provenance: The implicit seed's origin is invisible from a caller's code. You cannot tell whether a given run is on the walkthrough-bound seed or a real one without reading Jericho's source.
Additional Context
FrotzEnv.reset()'s own docstring already documents a use_walkthrough_seed parameter:
|
def reset(self): |
|
''' |
|
Resets the game. |
|
|
|
:param use_walkthrough_seed: Seed the emulator to reproduce the walkthrough. |
|
:returns: A tuple containing the initial observation,\ |
|
and a dictionary of info. |
|
:rtype: string, dictionary |
|
|
|
''' |
But reset(self) takes no arguments, so this parameter doesn't exist anywhere in the implementation presently.
Suggest actually implementing the toggle this docstring already implies was intended, as a way to make the choice explicit rather than implicit.
When using Jericho to evaluate LLM agents, most callers would expect
FrotzEnvwithout a seed to be stochastic, so that multiple experimental runs would be independent from each other. The paper explicitly defines a fixed random seed as a "handicap" that authors should consciously choose and disclose:However, the
jericho-pythonAPI silently enables this handicap by default. Right now, the unseeded default does the exact opposite of what most callers assume: it locks the environment to a single, hardcoded seed.Problem
FrotzEnv.seed()resolves an unspecified seed like this:jericho/jericho/jericho.py
Lines 432 to 446 in b6b48b5
For any game with walkthrough bindings (every game in the bundled benchmark suite)
self.bindings.get('seed', -1)returns a fixed, positive integer (12for Zork I), not-1.-1(the time-randomized fallback) only applies to a ROM without bindings, which I assume is the least common scenario.This is the reverse of common conventions (omit a seed → get randomness), so, for most games, it's easy to construct
FrotzEnv(rom)with no seed argument and reasonably assume you have independent, stochastic episodes when you don't.Why this matters
This introduces multiple risks for researchers who inadvertently make incorrect assumptions.
Additional Context
FrotzEnv.reset()'s own docstring already documents ause_walkthrough_seedparameter:jericho/jericho/jericho.py
Lines 448 to 457 in b6b48b5
But
reset(self)takes no arguments, so this parameter doesn't exist anywhere in the implementation presently.Suggest actually implementing the toggle this docstring already implies was intended, as a way to make the choice explicit rather than implicit.