factoriax.engine.step#
Advance the environment one step.
A step is one player action, and then one run of the whole factory.
factoriax_step() holds the order. The action lands first, then every
machine on the map moves, then the science labs consume their packs, then the
clock advances. An agent therefore sees the result of its action only in the
next observation.
The actions are compound. There is no cursor that selects a slot and no separate key that acts on it. Each action names its own target: one action for each placeable item, one for each craftable item, and one for each depositable item. The action space is therefore wide, and each action means something on its own. No field in the state has to hold a selection.
Every player action here targets one tile, the tile that the player faces. Each one uses a mask and not a branch. An action that cannot happen writes the state back unchanged and reports nothing.
A position is (x, y), the column first. Every grid lookup is [y, x].
The player transfers here obey the same per-machine capacities as the machine
passes in factoriax.engine.machines. Both read MACHINE_MAX_STACK. A
player cannot put more into a belt than a miner can.
- factoriax.engine.step.deposit_to_adjacent(state: EnvState, player_idx: int | Array, item_type: int | Array) EnvState#
Hand one item to the machine in front of the player.
One action moves one item, for every machine kind.
withdraw_from_adjacent()is different: it empties a whole slot in one action.The machine decides where the item lands. An assembler, a furnace, or a science lab takes it into
ent_asm_in. It uses slot 0 when that slot is empty or already holds the same item, and slot 1 in every other case. Every other machine takes it intoent_buf, and only when the buffer is empty or already holds the same item.A miner is the one machine that always refuses a deposit. Its buffer is an output.
Both routes obey
MACHINE_MAX_STACK, the same table that the machine passes read. A belt therefore fills to 3, and a rocket refuses every item. A refused deposit leaves the item in the inventory of the player.- Parameters:
state – State to read.
player_idx – Player that deposits the item.
item_type – Item to hand over. The action itself names it, and there is no selection cursor.
- Returns:
New state, with
player_inventory,ent_buf_type,ent_buf_count,ent_asm_in_type, andent_asm_in_countupdated. The state is unchanged when the function refuses the deposit.- Return type:
- factoriax.engine.step.factoriax_step(rng: Array, state: EnvState, action: int | Array, params: EnvParams) EnvState#
Apply one action, run the factory, and advance the clock.
The order is fixed and is part of the contract. The player acts first. Then
factoriax.engine.machines.update_all_machines()runs every machine. Thenrun_labs()consumes the packs in the science labs. Thentimestepincreases by 1.The player acts first, so a player deposit reaches a machine in time for that machine to use it in the same step. The labs run last, so a pack that an arm delivered in this step is also consumed in this step.
Only
state.selected_playeracts. Every other player keeps its position. There is no per-player action vector here.- Parameters:
rng – The function does not read this argument. Nothing in a step is random. It is present so that the signature matches the gymnax-style step API around it.
state – State to advance.
action – Action for the selected player.
params – The function passes this to the action handlers and to the machine passes.
- Returns:
New state, one step later, with
timestepone higher.science_consumed_stepholds the totals of this step, not the totals of the step before.- Return type:
- factoriax.engine.step.get_block_at(state: EnvState, position: Array) Array#
Read the terrain block at a position.
A position outside the map returns
BlockType.OUT_OF_BOUNDS, and the function raises nothing. A caller can therefore read past an edge with no test first. The function clips the index before the read, so nothing indexes outside the array.- Parameters:
state – State to read.
position – Tile to read as
(x, y), column first.
- Returns:
Scalar block id, or
BlockType.OUT_OF_BOUNDSoutside the map.- Return type:
- factoriax.engine.step.is_game_over(state: EnvState, params: EnvParams) Array#
Report whether the episode has run out of time.
Time is the only end condition in the base engine. There is no state in which a player loses, and no goal that stops a run early. A scenario that wants one adds it in a wrapper.
- Parameters:
state – State to test.
params – Supplies
max_timesteps.
- Returns:
Scalar bool. True once
timestepreachesmax_timesteps.- Return type:
- factoriax.engine.step.is_position_in_bounds(position: Array, map_width: int, map_height: int) Array#
Report whether a position falls on the map.
- Parameters:
position – Tile to test as
(x, y), column first.map_width – Map width in tiles.
map_height – Map height in tiles.
- Returns:
Scalar bool. The value is False for a negative coordinate, and for a coordinate at the far edge or past it.
- Return type:
- factoriax.engine.step.is_position_walkable(state: EnvState, position: Array) Array#
Report whether a player can stand on a position.
Two things block a tile: a block in
SOLID_BLOCKS, such as stone or water, and a machine. A conveyor belt is the one exception, and a player can walk over it. A belt line therefore does not close a player out of its own factory.The function ignores the other players, so two players can stand on one tile.
A position outside the map fails, because
OUT_OF_BOUNDSis a solid block.- Parameters:
state – State to read.
position – Tile to test as
(x, y), column first.
- Returns:
Scalar bool.
- Return type:
- factoriax.engine.step.mine_block(state: EnvState, player_idx: int | Array, params: EnvParams) EnvState#
Mine the block on the tile in front of the player.
The function targets the tile in the facing direction of the player. It does nothing in four cases: the tile is outside the map, no player can mine that block, the tile holds no ore left, or the inventory of the player has no room for the yield. An action that works extracts up to
params.player_mining_yieldunits. The ore left in the tile and the stack space in the inventory both limit that amount.This function is the only way to get ore with no miner on the map, and it is how a run starts. A player must craft the first miner from ore that they mined by hand.
A tile with no ore left becomes
BlockType.DIRT, the same as a tile under a miner.- Parameters:
state – State to read.
player_idx – Player that mines the block.
params – Supplies
player_mining_yield, the amount for one action before the tile limit and the inventory limit apply.
- Returns:
New state, with
map,player_inventory,block_resources, anditems_minedupdated. The state is unchanged when the action mined nothing. This function adds every mined item toitems_mined, and not only the five ore types.factoriax.engine.machines.run_miners()counts the five ore types only.- Return type:
- factoriax.engine.step.move_player(state: EnvState, action: int | Array, player_idx: int | Array) EnvState#
Move a player one tile, or turn them without moving.
The facing matters more than the position, because every other player action targets the tile in front. Two actions set it. A movement action turns the player, and then moves them when the target tile is walkable. A
FACE_action turns them and does not move them.A movement action into a blocked tile still turns the player, and this is deliberate. One action can therefore always aim at a wall, a machine, or an ore patch, and the next action can act on it.
An action that is neither a movement nor a
FACE_leaves the position and the facing as they were. A caller can therefore call this function for every action.- Parameters:
state – State to read.
action – Action id. Only the four movement values and the four
FACE_values have an effect.player_idx – Player to move.
- Returns:
New state, with
player_positionsandplayer_directionsupdated for that player.- Return type:
- factoriax.engine.step.run_labs(state: EnvState) EnvState#
Consume every science pack sitting in any SCIENCE_LAB input slot.
A lab is a sink. It has no countdown and no output. It consumes everything in its two input slots at this point in the step. The function records the amount for each type in
science_consumed_step, and the reward functions and the achievements read that field.factoriax_stepsets the field to zero at the start of every step, so it is a per-step delta and not a total.The function consumes science packs only. An item that is not a pack stays in the input slot of the lab and blocks that slot.
The function reads
ent_asm_inonly. It never touches theent_bufof a lab, and that is why a belt that points at a lab is a dead end. Seefactoriax.engine.machines.run_conveyor_belts().- Parameters:
state – State to read.
- Returns:
New state, with the consumed slots of every lab cleared in
ent_asm_in_typeandent_asm_in_count, andscience_consumed_stepset to the totals of this step for each type. The totals are zero when no lab held a pack.- Return type:
- factoriax.engine.step.withdraw_from_adjacent(state: EnvState, player_idx: int | Array) EnvState#
Withdraw from the output slot of the machine in front of the player.
A machine exposes exactly one output slot at a time. That slot is
ent_asm_outfor an assembler or a furnace inside a cycle, andent_bufin every other case. One WITHDRAW action takes as many items as fit: the smaller of the count in the slot and the free inventory space of the player. An agent therefore does not spend 100 steps to empty a pallet of 100 ore, one item at a time.The contents decide which slot the function reads, not the machine kind. It reads
ent_asm_outwhen that slot holds items, andent_bufin every other case. In practice the two never hold items at the same time, because an assembler and a furnace write onlyasm_out, and a buffer machine writes onlybuf.An empty slot loses its item type as well as its count, because the rest of the engine reads a cleared type as free to accept any item.
- Parameters:
state – State to read.
player_idx – Player that withdraws the items.
- Returns:
New state, with
player_inventory,ent_buf_type,ent_buf_count,ent_asm_out_type, andent_asm_out_countupdated. The state is unchanged when the tile holds no machine, when the machine holds nothing, or when the player has no room for the item.- Return type: