factoriax.engine.envs.base#
The environment object a training loop talks to, and the hooks that shape it.
FactoriaxEnv wraps the engine in the gymnax interface. reset_env
builds a world, step_env advances it, and the two space methods describe
what an agent sends and what it receives.
The engine itself knows nothing about a task. It has no reward, no goal, and no end except the end of the time budget. An author builds a scenario with functions that the constructor takes, and not with a subclass. Those functions turn the same simulation into mining practice or into a rocket launch:
achievement_fnReads a state and returns the achievement bits that hold in it.
reward_fnScores a step. Without it, every step scores zero.
done_fnEnds an episode early. The environment ORs it with the timeout and never replaces the timeout.
terrain_fnandlevelDecide which world a reset produces.
step_hooksandreset_hooksChange a state after the engine finishes with it.
The constructor captures each one. Two environments that differ in one of them are therefore two JIT cache entries, and each one pays for its own compile.
- class factoriax.engine.envs.base.FactoriaxEnv(achievement_fn: Callable[[EnvState], Array] | None = None, level: Level | None = None, terrain_fn: Callable[[Array, EnvParams], Array] | None = None, step_hooks: tuple[Callable[[Array, EnvState, EnvParams], EnvState], ...] = (), reset_hooks: tuple[Callable[[Array, EnvState, EnvParams], EnvState], ...] = (), reward_fn: Callable[[EnvState, EnvState, EnvParams], Array] | None = None, done_fn: Callable[[EnvState, EnvParams], Array] | None = None, obs: str = 'x_ray_global', obs_radius: int = 7, map_width: int = 32, map_height: int = 32, num_players: int = 1, max_machines: int = 0)#
The JAX grid environment of Factoriax.
The class advances the world state, which holds the terrain, the machines, and the player inventories. It also evaluates an optional achievement-condition function in each step. The reward and the other parts that shape a policy belong in the gymnax wrappers around this environment.
factoriax.engine.renderer.JaxRendererdraws the pixels. This class exposes no render method.The constructor captures
achievement_fn, andstep_env()folds its result intostate.achievements_unlocked. Two different functions give two JIT cache entries, throughstatic_argnames=("self",)onstep(). PassNoneto skip that pass, which then costs nothing.terrain_fnandleveldecide what a reset builds, in that order. With aterrain_fn,reset_env()calls it with the PRNG key, and the scenario owns the world generation. Without one, alevelofNonemeans a generated world from the key, and aLevelgoes throughbuild_state(), which ignores the key for the layout. Thereset_hooksthen change the new state on every reset path, for example to fill a player inventory. They take the same arguments asstep_hooks.reset_env(key, params)is the one reset method, as gymnax requires.- Parameters:
achievement_fn – Reads a state and returns
bool[MAX_ACHIEVEMENTS]. The environment evaluates it in every step, and a True bit latches intostate.achievements_unlocked.Noneskips the pass.level – Fixed
Levelforreset_env()to build.Nonemeans a world generated from the PRNG key.terrain_fn – Builds a terrain grid from the key. It wins over
level, so a scenario that supplies both gets generated terrain and no placed machines.step_hooks – Hooks that run in order after every step. Each one takes
(key, state, params)and returns a state.reset_hooks – Hooks that run in order after every reset, with the same arguments. Use them to fill a player inventory or to place the machines of a scenario.
reward_fn – Scores each step from
(prev_state, new_state, params).Nonemeans that every step returns 0.0.done_fn – Ends an episode from
(state, params). The environment ORs it with the timeout, so it can end an episode early but can never extend one.obs – Key into
OBSERVATIONSthat names the profile and the view, for example"x_ray_global". An unknown key raisesValueError.obs_radius – Half-width of the window for a
localobservation. A global view ignores it.map_width – Map width in tiles. The environment ignores it when
levelsupplies the map.map_height – Map height in tiles. The environment ignores it when
levelsupplies the map.num_players – Number of players in the world. Only
selected_playeracts.max_machines – Number of entity slots to allocate. A value of 0 asks for a size from the map area. The value is fixed for the episode, and a map with every slot in use refuses a new placement.
- action_space(params: EnvParams) Discrete#
Describe the action space, which holds every action of the engine.
The size is
NUM_ACTIONSfor every scenario. A scenario whose recipe book cannot make an item still exposes theCRAFT_action of that item. That action does nothing, and it is not an error. A wrapper masks the actions that a scenario cannot use.- Parameters:
params – The method does not read this argument. It is present for the gymnax signature.
- Returns:
A space of size
NUM_ACTIONS.- Return type:
spaces.Discrete
- get_obs(state: EnvState, params: EnvParams) Array#
Build the observation of the selected player.
The method always observes
state.selected_player. With more than one player, the result is therefore the view of one agent, and not a stack with one view for each player.- Parameters:
state – State to observe.
params – Environment parameters.
- Returns:
1-D float32.
observation_space()gives its length.- Return type:
- is_terminal(state: EnvState, params: EnvParams) Array#
Report whether the episode is at its end.
The timeout always applies. The method ORs a
done_fnon top of it, so a scenario can end an episode early. No scenario can make an episode run longer thanparams.max_timesteps.- Parameters:
state – State to test.
params – Supplies
max_timesteps.
- Returns:
Scalar bool.
- Return type:
- observation_space(params: EnvParams) Box#
Describe the observation space of the selected variant.
The bounds are 0.0 to 1.0, and every channel scales to stay inside them. This includes the x_ray facing readouts, which divide each count by a value equal to or larger than the
MACHINE_MAX_STACKof every machine.- Parameters:
params – The method does not read this argument. The shape comes from the constructor arguments, so it is fixed for the life of the environment.
- Returns:
1-D float32, shape
(obs_size,).- Return type:
spaces.Box
- reset_env(key: Array, params: EnvParams) tuple[Array, EnvState]#
Reset the environment to a start state.
There are three ways to build a world, and the method tries them in this order:
terrain_fn, then a boundlevel, then the terrain generator. With alevel, the method callsbuild_state(), which ignores the key for the layout.CAUTION: A scenario that supplies both a
terrain_fnand alevelgets the terrain and loses the machines of the level. Few scenarios want that.The
reset_hooksrun after the build, on every path.- Parameters:
key – PRNG key for the world generation. A bound
levelignores it for the layout, but the method still passes it to the hooks.params – Environment parameters.
- Returns:
(obs, state), withtimestepat 0.- Return type:
- step(key: Array, state: EnvState, action: int | Array, params: EnvParams | None = None) tuple[Array, EnvState, Array, Array, dict[str, Any]]#
Step the environment, with no auto-reset.
This method overrides the gymnax
step(). That method callsreset_envin every tick for its auto-reset. It therefore runs the whole terrain generation in every step, even when the episode is not done, and about triples the cost of a step. This method callsstep_envdirectly instead.Use
AutoResetWrapperwhen you need auto-reset for alax.scantraining loop.- Parameters:
key – PRNG key. The method passes it to the hooks. The step itself is deterministic.
state – State to advance.
action – Action for the selected player.
params – Environment parameters.
Nonetakesdefault_params.
- Returns:
(obs, new_state, reward, done, info). There is no auto-reset. Whendoneis True, the state in the result is the final state of the episode, and not the start of a new one.- Return type:
- step_env(key: Array, state: EnvState, action: int | Array, params: EnvParams) tuple[Array, EnvState, Array, Array, dict[str, Any]]#
Run one environment step.
The method runs the game rules. It then evaluates the optional achievement condition function and folds the result into
state.achievements_unlockedwith OR. It skips that pass when the constructor received noachievement_fn. The JIT trace resolves theis Nonetest, so that path costs no branch in a step.The order matters. The engine runs first, then the
step_hooks, then the reward against the state after the hooks, then the end test, then the observation. A hook can therefore change the value of a step, andreward_fnsees the state that the agent observes.- Parameters:
key – PRNG key. The method passes it to the hooks.
state – State to advance.
action – Action for the selected player. The method casts it to int32.
params – Environment parameters.
- Returns:
(obs, new_state, reward, done, info).rewardis 0.0 when the constructor received noreward_fn, andinfois always empty.- Return type:
- factoriax.engine.envs.base.achievement_hook(condition_fn: Callable[[EnvState], Array]) Callable[[Array, EnvState, EnvParams], EnvState]#
Wrap an achievement condition into a step hook that latches its bits.
The latch makes an achievement a milestone and not a state flag. Once a bit is set, it stays set for the rest of the episode, even after the condition stops holding.
factoriax.engine.rewards.achievement_reward()therefore pays for it exactly one time.- Parameters:
condition_fn – Reads a state and returns the bits that hold now. Shape
(MAX_ACHIEVEMENTS,), bool.- Returns:
A hook that folds those bits into
achievements_unlockedwith OR. The hook ignores its key argument and its params argument.- Return type:
StepHook