factoriax.engine.observations#
Observation constructors for Factoriax.
Each function maps (state, params, player_idx) -> jax.Array. Each one is
JAX-native and works under JIT. rgb is the exception. It returns a NumPy RGB
image from the pixel renderer, and JIT cannot compile it.
Two profiles and two view extents give four top-level builders.
x_ray sees through every machine to its slot contents, and sees the ore left under each terrain tile. It has 10 spatial channels and 81 player scalars.
superficial sees only what is visible from outside. It has 3 spatial channels and 72 player scalars, with no view into a slot and no view of the tile in front.
Each profile pairs with a view extent. global_* flattens the whole map.
local_* takes a (2r+1) x (2r+1) window with the selected player at the
centre.
global_* keeps the map in absolute coordinates, and gives the position of
the observing player as two scalars only. It fits a function approximator that
uses the spatial channels, such as a CNN over the unflattened grid. The
training code must add a player-position plane for that CNN. A flat MLP usually
cannot find the player in this encoding. The local_* variants are
egocentric and translation-invariant, and they fit an MLP better. With
radius >= map size - 1 the window still covers the whole map, and the
OUT_OF_BOUNDS padding marks the edges, so the absolute position stays
readable. One measurement: on Mining-v1, PPO with a 64x64 MLP stops near 7/30
on superficial_global and reaches 30/30 on superficial_local.
The 10 x_ray spatial channels:
block_type, the terrain.machine_type, theMachineon each tile, or NONE.block_resources, the ore count under the tile.slot{0,1,2}_typeandslot{0,1,2}_count, a uniform 3-slot view of the contents of every machine. Slots 0 and 1 carry the twoent_asm_incolumns, which covers the inputs of an assembler, the packs of a science lab, and the two axis buffers of a crossing. Slot 2 carriesent_asm_outwhere a recipe runs, andent_buffor every other machine that holds items.machine_direction, theent_directionof each tile, 1 to 4 or 0.
The 3 superficial spatial channels: block_type, machine_type, and
machine_direction.
The x_ray scalars are the pose, the affordability of each item, the player inventory, and the readouts of the machine in front: 81 floats. The superficial scalars are the pose, the affordability of each item, and the player inventory: 72 floats. The 9 facing readouts are absent.
Module Attributes
Number of scalars in each profile. |
|
The observation variants. |
- factoriax.engine.observations.NUM_PLAYER_SCALARS: dict[str, int] = {'superficial': 72, 'x_ray': 81}#
Number of scalars in each profile. The number of recipes in the
RecipeTableof a scenario does not change it, because an item type indexes theaffordblock, not a recipe id.
- factoriax.engine.observations.OBSERVATIONS: dict[str, Array] = {'superficial_global': <function global_superficial>, 'superficial_local': <function local_superficial>, 'x_ray_global': <function global_x_ray>, 'x_ray_local': <function local_x_ray>}#
The observation variants. The environment takes the name, and a radius for a
_localvariant, and dispatches through this dict.
- factoriax.engine.observations.global_superficial(state: EnvState, params: EnvParams, player_idx: int | Array) Array#
Full-map superficial observation for one player.
The result holds three flat spatial channels, and then the superficial scalar vector. The channels are the block type, the machine type, and the machine direction.
- Parameters:
state – State to read.
params – The function passes this to the scalar builders.
player_idx – Player that observes.
- Returns:
1-D float32, of length
observation_size(profile="superficial", view="global", ...). It holds three flat(H, W)channels, and then the superficial scalars.- Return type:
- factoriax.engine.observations.global_x_ray(state: EnvState, params: EnvParams, player_idx: int | Array) Array#
Full-map x_ray observation for one player.
The result holds ten flat spatial channels, and then the x_ray scalar vector. The channels are the block type, the machine type, the ore resources, the six slot channels, and the machine direction.
- Parameters:
state – State to read.
params – The function passes this to the scalar builders.
player_idx – Player that observes. Only the scalar block depends on it. The spatial channels are the same for every player.
- Returns:
1-D float32, of length
observation_size(profile="x_ray", view="global", ...). It holds ten flat(H, W)channels in_X_RAY_SPATIAL_CHANNEL_NAMESorder, and then the x_ray scalars. A reader that wants the grid back must reshape it.- Return type:
- factoriax.engine.observations.local_superficial(state: EnvState, params: EnvParams, player_idx: int | Array, radius: int = 10) Array#
Local superficial observation in a window around one player.
The function takes a
(2*radius+1) x (2*radius+1)patch from the three superficial spatial channels, and adds the superficial scalar vector after it. That vector holdsNUM_PLAYER_SCALARS["superficial"]floats.- Parameters:
state – State to read.
params – The function passes this to the scalar builders.
player_idx – Player that observes. This player is at the centre of the window.
radius – Half-width of the window in tiles. The value is static under
jit.
- Returns:
1-D float32, of length
observation_size(profile="superficial", view="local", radius=radius). The padding is the same as inlocal_x_ray().- Return type:
- factoriax.engine.observations.local_x_ray(state: EnvState, params: EnvParams, player_idx: int | Array, radius: int = 10) Array#
Local x_ray observation in a window around one player.
The function takes a
(2*radius+1) x (2*radius+1)patch from the ten x_ray spatial channels, and adds the x_ray scalar vector after it.- Parameters:
state – State to read.
params – The function passes this to the scalar builders.
player_idx – Player that observes. This player is at the centre of the window.
radius – Half-width of the window in tiles. The window is
(2 * radius + 1)tiles square, so it always has a centre tile. The value is static underjit, and a new value traces again.
- Returns:
1-D float32, of length
observation_size(profile="x_ray", view="local", radius=radius). The padding for a tile past the map edge isBlockType.OUT_OF_BOUNDSon the terrain channel and zero on every other channel, so an agent can still find the edge.- Return type:
- factoriax.engine.observations.observation_size(*, profile: str, view: str, radius: int = 7, map_height: int = 32, map_width: int = 32) int#
Return the flat observation size for one profile, view, and map size.
- factoriax.engine.observations.rgb(state: EnvState, block_pixel_size: int = 32) ndarray#
Render the full map as an RGB image.
This is the one observation here that is not JAX-native. It returns a NumPy array, and JAX cannot trace it, so a caller must run it outside a
jitboundary.The image shows less than the symbolic profiles carry: the terrain, the machine kind and facing, and the player position and facing. There is no inventory, no craft affordability, and no machine contents.
CAUTION: A pixel agent cannot learn to craft from this observation alone.
ISSUES.mdrecords this.- Parameters:
state – State to render.
block_pixel_size – Tile side length in pixels. Each different value builds its own renderer and holds it in a cache, so the atlas build and the JIT compile happen one time for each size, and not one time for each call. Many different sizes therefore grow the cache without bound.
- Returns:
Shape
(H * block_pixel_size, W * block_pixel_size, 3), uint8 RGB.- Return type: