API Reference¶
This page groups and expands the public API by area. Each section includes a brief description of the modules followed by auto-generated API docs (mkdocstrings). Use the grouped structure to locate functionality quickly, then dive into the detailed signatures and docstrings.
Contents
- How to read this reference
- Core
- Components
- Systems
- Utilities
- Levels (Authoring)
- Rendering
- Examples
- Gym Environment
- Registries and enums
How to read this reference¶
-
Modules are grouped by domain. Each entry links to the module’s classes and functions.
-
Headings provide a short summary of what the module does before the auto-doc.
-
Code examples for common tasks live in the Functional API and Guides pages; this reference focuses on signatures and behavior.
-
Tip: If module headings are too large in the Table of Contents, set
heading_level: 3
inmkdocs.yml
under mkdocstrings options so modules render as H3 under these H2 sections.
Core¶
State and stepping¶
-
grid_universe.state
: Immutable State dataclass holding component stores, meta, and RNG seed. -
grid_universe.step
: The main reducer that applies one action and orchestrates all systems in the correct order.
grid_universe.state ¶
Core immutable ECS State
dataclass.
This module defines the frozen :class:State
object that represents the
entire game / simulation snapshot at a single turn. All systems are pure
functions that take a previous State
plus inputs (e.g. an Action
) and
return a new State
; no mutation happens in-place. This makes the engine
deterministic, easy to test, and friendly to functional style reducers.
Design notes:
- Component stores are persistent maps (
pyrsistent.PMap
) keyed byEntityID
. Absence of a key means the entity does not currently possess that component. - Effect components (Immunity, Phasing, Speed, TimeLimit, UsageLimit) are
referenced by :class:
grid_universe.components.properties.Status
which holds orderedeffect_ids
. Several systems (status tick, GC) walk those references. - The
prev_position
andtrail
auxiliary stores are populated by dedicated systems to enable path‑based effects (e.g. trail rendering or damage-on-cross mechanics). win
/lose
flags are mutually exclusive terminal markers. The reducer short‑circuits on terminal states.
Google‑style docstrings throughout the codebase refer back to this structure;
see :mod:grid_universe.step
for how the reducer orchestrates systems.
State
dataclass
¶
Immutable ECS world state.
Instances are value objects; every transition creates a new State
.
Only include persistent / serializable data here (no open handles or
caches). Systems should be pure functions that accept and return State
.
Attributes:
Name | Type | Description |
---|---|---|
width |
int
|
Grid width in tiles. |
height |
int
|
Grid height in tiles. |
move_fn |
MoveFn
|
Movement candidate function used to resolve move actions. |
objective_fn |
ObjectiveFn
|
Predicate evaluated after each step to set |
immunity |
PMap[EntityID, Immunity]
|
Effect component map. |
phasing |
PMap[EntityID, Phasing]
|
Effect component map. |
speed |
PMap[EntityID, Speed]
|
Effect component map. |
time_limit |
PMap[EntityID, TimeLimit]
|
Effect limiter map (remaining steps). |
usage_limit |
PMap[EntityID, UsageLimit]
|
Effect limiter map (remaining uses). |
agent |
PMap[EntityID, Agent]
|
Player / AI controllable entity marker components. |
appearance |
PMap[EntityID, Appearance]
|
Rendering metadata (glyph, layering, groups). |
blocking |
PMap[EntityID, Blocking]
|
Entities that prevent movement into their tile. |
collectible |
PMap[EntityID, Collectible]
|
Items that can be picked up. |
collidable |
PMap[EntityID, Collidable]
|
Entities that can collide (triggering damage, cost, etc.). |
cost |
PMap[EntityID, Cost]
|
Movement or interaction cost applied when entered. |
damage |
PMap[EntityID, Damage]
|
Passive damage applied on collision / contact. |
dead |
PMap[EntityID, Dead]
|
Marker for logically removed entities (awaiting GC). |
exit |
PMap[EntityID, Exit]
|
Tiles that can satisfy the objective when conditions met. |
health |
PMap[EntityID, Health]
|
Health pools for damage / lethal checks. |
inventory |
PMap[EntityID, Inventory]
|
Item/key collections carried by entities. |
key |
PMap[EntityID, Key]
|
Keys that can unlock |
lethal_damage |
PMap[EntityID, LethalDamage]
|
Immediate kill damage sources (pits, hazards). |
locked |
PMap[EntityID, Locked]
|
Lock descriptors requiring matching keys. |
moving |
PMap[EntityID, Moving]
|
Entities currently undergoing movement (inter-step state). |
pathfinding |
PMap[EntityID, Pathfinding]
|
Agents with pathfinding goals and cached paths. |
portal |
PMap[EntityID, Portal]
|
Teleport endpoints / pairs. |
position |
PMap[EntityID, Position]
|
Current grid position of entities. |
pushable |
PMap[EntityID, Pushable]
|
Entities that can be displaced by push actions. |
required |
PMap[EntityID, Required]
|
Items/conditions needed to satisfy Exit / objective. |
rewardable |
PMap[EntityID, Rewardable]
|
Components conferring score rewards when collected or triggered. |
status |
PMap[EntityID, Status]
|
Ordered list container referencing effect component ids. |
prev_position |
PMap[EntityID, Position]
|
Snapshot of positions before movement this step. |
trail |
PMap[Position, PSet[EntityID]]
|
Positions traversed this step mapped to entity ids. |
turn |
int
|
Turn counter (0-based). |
score |
int
|
Accumulated score. |
turn_limit |
int | None
|
Optional maximum number of turns allowed. When
set, reaching this number triggers a |
win |
bool
|
True if objective met. |
lose |
bool
|
True if losing condition met. |
message |
str | None
|
Optional informational / terminal message. |
seed |
int | None
|
Base RNG seed for deterministic rendering or procedural systems. |
Source code in grid_universe/state.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
|
description
property
¶
description
Sparse serialization of non‑empty fields.
Iterates dataclass fields and returns a persistent map including only those that are non‑empty (for component maps) or truthy (for scalars). Useful for lightweight diagnostics / debugging without dumping large empty maps.
Returns:
Type | Description |
---|---|
PMap[str, Any]
|
PMap[str, Any]: Persistent map of field name to value for all |
PMap[str, Any]
|
populated fields. |
grid_universe.step ¶
State reducer and step orchestration.
This module wires together all systems in the correct order to implement a
single turn transition given an Action
. The exported :func:step
is the
only public mutation entry point for gameplay progression and is intentionally
pure: it returns a new :class:grid_universe.state.State
.
Ordering rationale (high level):
position_system
snapshots previous positions (enables trail / cross checks).- Autonomous movers & pathfinding update entities (
moving_system
/pathfinding_system
). status_tick_system
decrements effect limits before applying player action.- Player action sub‑steps (movement may produce multiple sub‑moves via speed effects).
- After each sub‑move we run interaction systems (portal, damage, rewards) to allow chained behaviors (e.g. portal then damage at destination).
- After the entire action we apply GC, tile costs, terminal checks, and bump turn.
All helper _step_*
functions are internal and assume validation of inputs.
step ¶
step(state, action, agent_id=None)
Advance the simulation by one logical action.
Resolves autonomous movement, applies the player's chosen Action
(which
may translate to multiple movement sub‑steps for speed effects), runs
interaction / status systems, and returns a new State
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
Previous immutable world state. |
required |
action
|
Action
|
Player action enum value to apply. |
required |
agent_id
|
EntityID | None
|
Explicit agent entity id. If |
None
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Next state snapshot. If the input state is already terminal (win/lose) or invalid the same object may be returned unchanged. |
Raises:
Type | Description |
---|---|
ValueError
|
If there is no agent or the action is not recognized. |
Source code in grid_universe/step.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
|
Actions and types¶
-
grid_universe.actions
: Agent actions, including movement and non-movement (use key, pick up, wait). -
grid_universe.types
: Core type aliases and enums used across the codebase (EntityID
,MoveFn
,ObjectiveFn
,EffectType
,EffectLimit
). -
grid_universe.objectives
: Built-in objective functions (default, collect, exit, unlock, push), plus a registry for selection by name.
grid_universe.actions ¶
Action enumerations.
Defines both the human readable :class:Action
(string enum) used internally.
MOVE_ACTIONS
is the canonical ordered list of movement actions; checks like
if action in MOVE_ACTIONS
are preferred over enum name comparisons.
Action ¶
Bases: StrEnum
String enum of player actions.
Enum Members
UP: Move one tile up. DOWN: Move one tile down. LEFT: Move one tile left. RIGHT: Move one tile right. USE_KEY: Attempt to unlock a co‑located locked entity with a matching key. PICK_UP: Collect items (powerups / coins / cores / keys) at the current tile. WAIT: Advance a turn without moving (effects still tick).
Source code in grid_universe/actions.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
grid_universe.types ¶
Common type aliases and enumerations.
MoveFn
and ObjectiveFn
are central extension points used in the
State
to allow pluggable movement / win condition behavior.
EffectType ¶
Bases: StrEnum
Effect component categories (reflected in serialized observations).
Source code in grid_universe/types.py
23 24 25 26 27 28 |
|
EffectLimit ¶
Bases: StrEnum
Limit semantics for effects (time or usage based).
Source code in grid_universe/types.py
31 32 33 34 35 |
|
grid_universe.objectives ¶
Objective predicate functions and registry.
Each objective function answers: "Has the agent satisfied the win
condition?" They are pure predicates over a :class:State
and an agent_id
.
The main reducer checks state.objective_fn
after each full action step to
decide whether to set state.win
.
Functions here should be fast (O(number of relevant components)).
OBJECTIVE_FN_REGISTRY
module-attribute
¶
OBJECTIVE_FN_REGISTRY = {'default': default_objective_fn, 'exit': exit_objective_fn, 'collect': collect_required_objective_fn, 'unlock': all_unlocked_objective_fn, 'push': all_pushable_at_exit_objective_fn}
Name → objective predicate mapping for level configuration.
default_objective_fn ¶
default_objective_fn(state, agent_id)
Collect all required items and reach an exit tile.
Source code in grid_universe/objectives.py
17 18 19 20 21 |
|
exit_objective_fn ¶
exit_objective_fn(state, agent_id)
Agent stands on any entity possessing an Exit
component.
Source code in grid_universe/objectives.py
24 25 26 27 28 29 30 31 |
|
collect_required_objective_fn ¶
collect_required_objective_fn(state, agent_id)
All entities marked Required
have been collected (no longer collectible).
Source code in grid_universe/objectives.py
34 35 36 |
|
all_unlocked_objective_fn ¶
all_unlocked_objective_fn(state, agent_id)
No remaining locked entities (doors, etc.).
Source code in grid_universe/objectives.py
39 40 41 |
|
all_pushable_at_exit_objective_fn ¶
all_pushable_at_exit_objective_fn(state, agent_id)
Every Pushable entity currently occupies an exit tile.
Source code in grid_universe/objectives.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Components¶
Property components (authoring/runtime)¶
- Appearance controls rendering and layering; Position locates entities. Other components model gameplay (Blocking, Collectible, etc.).
grid_universe.components.properties.appearance ¶
Rendering appearance component.
Appearance
controls layering and icon/background behavior when composing
tiles. Priority ordering rules:
- For background tiles (
background=True
) the highest priority value wins. - For main foreground selection the lowest priority value wins (allows important items to sit on top even if visually small).
icon=True
marks entities that render as small corner overlays (e.g.
powerups) in addition to the main occupant.
AppearanceName ¶
Bases: StrEnum
Enumeration of built‑in appearance categories.
Source code in grid_universe/components/properties/appearance.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Appearance
dataclass
¶
Visual rendering metadata.
Attributes:
Name | Type | Description |
---|---|---|
name |
AppearanceName
|
Symbolic appearance identifier. |
priority |
int
|
Integer priority used for layering selection. |
icon |
bool
|
If True this entity may render as a small corner icon. |
background |
bool
|
If True counts as a background layer candidate. |
Source code in grid_universe/components/properties/appearance.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
grid_universe.components.properties.position ¶
Position component.
Immutable integer grid coordinates. Stored in State.position
keyed by
entity id. The prev_position
store records the prior turn's component for
trail / cross detection.
Position
dataclass
¶
Grid coordinate.
Attributes:
Name | Type | Description |
---|---|---|
x |
int
|
Column index (0 at left). |
y |
int
|
Row index (0 at top). |
Source code in grid_universe/components/properties/position.py
11 12 13 14 15 16 17 18 19 20 21 |
|
grid_universe.components.properties.agent ¶
Agent marker component.
Presence of :class:Agent
designates the controllable player entity. Only
one agent is typically present; the reducer will select the first if multiple
exist. This component carries no data but enables queries / system routing.
Agent
dataclass
¶
Marker (no fields).
Source code in grid_universe/components/properties/agent.py
11 12 13 14 15 |
|
grid_universe.components.properties.blocking ¶
Blocking component.
Marks an entity as occupying its tile for purposes of movement collision. Ignored for entities with active Phasing effect.
Blocking
dataclass
¶
Marker (no data).
Source code in grid_universe/components/properties/blocking.py
10 11 12 13 14 |
|
grid_universe.components.properties.collectible ¶
Collectible component.
Marks an entity that can be picked up into an agent's inventory. If combined
with :class:Rewardable
or :class:Required
, logic in collectible / objective
systems updates score or win conditions at pickup.
Collectible
dataclass
¶
Marker (no data).
Source code in grid_universe/components/properties/collectible.py
11 12 13 14 15 |
|
grid_universe.components.properties.collidable ¶
Collidable component.
Indicates an entity participates in collision interactions (e.g. portal
entry). Distinct from Blocking
which prevents movement; collidable objects
may coexist with pass-through mechanics.
Collidable
dataclass
¶
Marker (no data).
Source code in grid_universe/components/properties/collidable.py
11 12 13 14 15 |
|
grid_universe.components.properties.cost ¶
Tile movement cost component (per-step penalty).
Cost
dataclass
¶
Movement cost applied once per logical action when on this tile.
Source code in grid_universe/components/properties/cost.py
6 7 8 9 10 |
|
grid_universe.components.properties.damage ¶
Damage component (non-lethal).
Damage
dataclass
¶
Hit point damage applied on contact / crossing.
Source code in grid_universe/components/properties/damage.py
6 7 8 9 10 |
|
grid_universe.components.properties.dead ¶
Dead marker component (post-mortem).
Dead
dataclass
¶
Marker set by health/damage logic when HP reaches zero or lethal hit.
Source code in grid_universe/components/properties/dead.py
6 7 8 9 10 |
|
grid_universe.components.properties.exit ¶
Exit
dataclass
¶
Marks an entity as an exit tile / goal location.
Objective predicates typically search for agents reaching any entity with this component. The component has no fields; presence alone is meaningful.
Source code in grid_universe/components/properties/exit.py
4 5 6 7 8 9 10 11 12 |
|
grid_universe.components.properties.health ¶
Health
dataclass
¶
Tracks current and maximum hit points for damage / healing systems.
Attributes:
Name | Type | Description |
---|---|---|
health |
int
|
Current hit points. Systems should clamp this to |
max_health |
int
|
Upper bound for |
Source code in grid_universe/components/properties/health.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
grid_universe.components.properties.inventory ¶
Inventory
dataclass
¶
Set of owned item entity IDs.
The immutable PSet
enables O(1) sharing across state copies; adding or
removing an item produces a new component instance. Other systems (e.g.
keys, rewards) inspect membership for gating logic.
Attributes:
Name | Type | Description |
---|---|---|
item_ids |
PSet[EntityID]
|
Persistent set of entity identifiers currently held. |
Source code in grid_universe/components/properties/inventory.py
6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
grid_universe.components.properties.key ¶
Key item component (pairs with Locked).
Key
dataclass
¶
Key id string used to unlock matching locked entities.
Source code in grid_universe/components/properties/key.py
6 7 8 9 10 |
|
grid_universe.components.properties.lethal_damage ¶
LethalDamage
dataclass
¶
Marker: entity inflicts fatal damage on contact / interaction.
Systems detecting collisions or overlaps may directly set a target's
Health
to zero (or apply sufficient damage) when encountering an
entity with this component. Presence alone conveys semantics.
Source code in grid_universe/components/properties/lethal_damage.py
4 5 6 7 8 9 10 11 12 13 |
|
grid_universe.components.properties.locked ¶
Locked
dataclass
¶
Indicates the entity is locked and may require a key to unlock.
Attributes:
Name | Type | Description |
---|---|---|
key_id |
str
|
Identifier of the key required. An empty string can represent a generic lock (any key) or a permanently locked state depending on objective / system interpretation. |
Source code in grid_universe/components/properties/locked.py
4 5 6 7 8 9 10 11 12 13 14 15 |
|
grid_universe.components.properties.moving ¶
Autonomous movement component.
Moving
entities advance automatically each turn along a specified axis and
direction with optional bouncing at boundaries and configurable tile step
speed (processed before player action). prev_position
stores the last
position for cross / trail interactions when the movement system updates it.
MovingAxis ¶
Bases: StrEnum
Axis enumeration for autonomous motion.
Source code in grid_universe/components/properties/moving.py
16 17 18 19 20 |
|
Moving
dataclass
¶
Autonomous mover definition.
Attributes:
Name | Type | Description |
---|---|---|
axis |
MovingAxis
|
Axis of travel (horizontal or vertical). |
direction |
int
|
+1 or -1 indicating step direction along the axis. |
bounce |
bool
|
Reverse direction at edge if True; otherwise stop at boundary. |
speed |
int
|
Tile steps attempted per turn. |
prev_position |
Optional[Position]
|
Internal bookkeeping of last position (set by system). |
Source code in grid_universe/components/properties/moving.py
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
grid_universe.components.properties.pathfinding ¶
Pathfinding
dataclass
¶
AI movement directive for automated entities.
Specifies how an entity should compute movement objectives each step.
Attributes:
Name | Type | Description |
---|---|---|
target |
Optional[EntityID]
|
Optional entity ID to follow/approach. If |
type |
PathfindingType
|
Strategy: |
Source code in grid_universe/components/properties/pathfinding.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
grid_universe.components.properties.portal ¶
Portal
dataclass
¶
Teleportation link between two entities.
Attributes:
Name | Type | Description |
---|---|---|
pair_entity |
int
|
Entity ID of the destination/linked portal. When an entity moves onto this portal, movement systems may relocate it to the paired portal's position (often preserving direction or applying post-teleport rules). |
Source code in grid_universe/components/properties/portal.py
4 5 6 7 8 9 10 11 12 13 14 15 |
|
grid_universe.components.properties.pushable ¶
Pushable
dataclass
¶
Marker indicating the entity can be displaced by another's movement.
Push mechanics typically trigger when an agent attempts to move into a tile occupied by a pushable entity; the system tries to move the pushable entity in the same direction if the next tile is free.
Source code in grid_universe/components/properties/pushable.py
4 5 6 7 8 9 10 11 12 13 |
|
grid_universe.components.properties.required ¶
Required
dataclass
¶
Marker signifying an entity must satisfy a condition to progress.
Often attached to goal or exit entities to indicate prerequisites (such as possessing certain items) must be met; interpretation is handled by objective or validation systems.
Source code in grid_universe/components/properties/required.py
4 5 6 7 8 9 10 11 12 13 |
|
grid_universe.components.properties.rewardable ¶
Rewardable
dataclass
¶
Specifies a scalar reward granted upon satisfying a condition.
Systems can award the amount
(e.g., reinforcement learning signal)
when the entity is collected, reached, or otherwise triggered by an agent.
Attributes:
Name | Type | Description |
---|---|---|
amount |
int
|
Numeric reward value to emit; magnitude and sign semantics are up to the environment integration. |
Source code in grid_universe/components/properties/rewardable.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
grid_universe.components.properties.status ¶
Status component.
Holds a set of effect entity ids the holder currently has active. The
ordering is not semantically relevant (set semantics) but systems iterate the
PSet
in deterministic order for reproducibility. Limits (time/usage) are
stored on the effect entities themselves.
Status
dataclass
¶
Active effect references.
Attributes:
Name | Type | Description |
---|---|---|
effect_ids |
PSet[EntityID]
|
Persistent set of effect entity ids. |
Source code in grid_universe/components/properties/status.py
14 15 16 17 18 19 20 21 22 |
|
Effects and limits¶
- Effect entities are referenced from
Status.effect_ids
and may include limits.
grid_universe.components.effects.immunity ¶
Immunity effect component (negates incoming damage instances).
Immunity
dataclass
¶
Marker (no data).
Source code in grid_universe/components/effects/immunity.py
6 7 8 9 10 |
|
grid_universe.components.effects.phasing ¶
Phasing
dataclass
¶
Effect component: entity ignores blocking collisions.
When present, movement systems treat the entity as non-blocking for the purpose of traversing tiles that would normally be obstructed (e.g. walls or other blocking entities). Other entities may still collide with this entity unless they also have logic that skips blocked checks.
Typically combined with a :class:TimeLimit
or :class:UsageLimit
to make
the phasing temporary.
Source code in grid_universe/components/effects/phasing.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
grid_universe.components.effects.speed ¶
Speed effect component.
Multiplies the number of movement sub‑steps performed for a movement action. Each sub‑step triggers post‑movement interaction systems, allowing rapid chain effects (e.g. portal + damage) within a single logical action.
Speed
dataclass
¶
Movement multiplier.
Attributes:
Name | Type | Description |
---|---|---|
multiplier |
int
|
Positive integer factor applied to base 1 movement steps. |
Source code in grid_universe/components/effects/speed.py
11 12 13 14 15 16 17 18 19 |
|
grid_universe.components.effects.time_limit ¶
TimeLimit
dataclass
¶
Decorator effect specifying a maximum number of remaining steps.
Systems decrement the remaining amount
each global step; when it
reaches zero the wrapped effect (or status) is removed. This enables
temporary power-ups (e.g. phasing for 5 turns).
Attributes:
Name | Type | Description |
---|---|---|
amount |
int
|
Number of future steps for which the associated effect/status is
still active. Implementations should treat |
Source code in grid_universe/components/effects/time_limit.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
grid_universe.components.effects.usage_limit ¶
UsageLimit
dataclass
¶
Decorator effect counting down discrete consumptions.
Rather than expiring with time, a UsageLimit
is decremented by a
system whenever the wrapped effect is used (domain-specific). Typical
use cases include limited charges (e.g. three teleports) or a fixed number
of phasing moves.
Attributes:
Name | Type | Description |
---|---|---|
amount |
int
|
Remaining number of uses. When it reaches zero the effect/status should be removed. |
Source code in grid_universe/components/effects/usage_limit.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
grid_universe.components.effects ¶
Effect component aggregates.
This sub-package defines effect components: temporary or conditional modifiers that decorate entities (e.g. immunity, phasing, speed changes) as well as limiting wrappers (usage / time limits). Effects are modeled as plain data objects which systems interpret each step; they do not mutate themselves.
Effect
is provided as a convenience union of the runtime modifying
effects (currently :class:Immunity
, :class:Phasing
, :class:Speed
). Limit
wrappers (:class:TimeLimit
, :class:UsageLimit
) are kept separate because
they can apply to any effect type and are processed by status / GC systems.
Importing::
from grid_universe.components.effects import Effect, Speed
or via the top-level components package::
from grid_universe.components import Speed
Immunity
dataclass
¶
Marker (no data).
Source code in grid_universe/components/effects/immunity.py
6 7 8 9 10 |
|
Phasing
dataclass
¶
Effect component: entity ignores blocking collisions.
When present, movement systems treat the entity as non-blocking for the purpose of traversing tiles that would normally be obstructed (e.g. walls or other blocking entities). Other entities may still collide with this entity unless they also have logic that skips blocked checks.
Typically combined with a :class:TimeLimit
or :class:UsageLimit
to make
the phasing temporary.
Source code in grid_universe/components/effects/phasing.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Speed
dataclass
¶
Movement multiplier.
Attributes:
Name | Type | Description |
---|---|---|
multiplier |
int
|
Positive integer factor applied to base 1 movement steps. |
Source code in grid_universe/components/effects/speed.py
11 12 13 14 15 16 17 18 19 |
|
TimeLimit
dataclass
¶
Decorator effect specifying a maximum number of remaining steps.
Systems decrement the remaining amount
each global step; when it
reaches zero the wrapped effect (or status) is removed. This enables
temporary power-ups (e.g. phasing for 5 turns).
Attributes:
Name | Type | Description |
---|---|---|
amount |
int
|
Number of future steps for which the associated effect/status is
still active. Implementations should treat |
Source code in grid_universe/components/effects/time_limit.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
UsageLimit
dataclass
¶
Decorator effect counting down discrete consumptions.
Rather than expiring with time, a UsageLimit
is decremented by a
system whenever the wrapped effect is used (domain-specific). Typical
use cases include limited charges (e.g. three teleports) or a fixed number
of phasing moves.
Attributes:
Name | Type | Description |
---|---|---|
amount |
int
|
Remaining number of uses. When it reaches zero the effect/status should be removed. |
Source code in grid_universe/components/effects/usage_limit.py
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Systems¶
Movement, pathfinding, position¶
-
movement_system
: Agent’s single-step application obeying Blocking unless Phasing is active. -
moving_system
: Autonomous movers with axis/direction/speed/bounce. -
pathfinding_system
: Greedy or A* pursuit. -
position_system
: Snapshots positions intoprev_position
at turn start.
grid_universe.systems.movement ¶
Player (agent) movement system.
Attempts to move the controlled agent to next_pos
applying effect logic:
- If the agent has an active Phasing effect (consuming a usage/time limit) it ignores blocking components entirely.
- Otherwise the move is allowed only if destination is in-bounds and not blocked by Blocking/Pushable/Collidable entities (push handling occurs in a separate system before this is called).
Returns the original State
if movement is not possible; otherwise a new
State
with updated position (and possibly decremented usage limits).
movement_system ¶
movement_system(state, entity_id, next_pos)
Move agent one tile if allowed.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
Current state. |
required |
entity_id
|
EntityID
|
Agent entity id (ignored if not an agent). |
required |
next_pos
|
Position
|
Desired destination position. |
required |
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Same state if blocked / invalid or updated with new position. |
Source code in grid_universe/systems/movement.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
|
grid_universe.systems.moving ¶
Autonomous linear movement system.
Updates entities with a Moving
component by translating them along their
configured axis and direction up to speed
tiles per step, bouncing (i.e.
reversing direction) if configured and blocked/out-of-bounds.
move ¶
move(state, entity_id, pos, next_pos, state_moving, state_position)
Attempt a single-tile move for a moving entity.
Returns updated moving/position maps and whether movement was blocked.
Source code in grid_universe/systems/moving.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
moving_system ¶
moving_system(state)
Advance all moving entities for the current step.
Source code in grid_universe/systems/moving.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
|
grid_universe.systems.pathfinding ¶
Pathfinding systems.
Provides straight-line heuristic movement and A* shortest path selection for
entities with the Pathfinding
component. Supports effect-based blocking
via usage-limited phasing/immunity status checks before movement.
get_astar_next_position ¶
get_astar_next_position(state, entity_id, target_id)
Compute next step toward target using A* (Manhattan metric).
Ignores collidable/pushable differences and treats only blocking tiles as obstacles. Returns current position if already at goal or no path.
Source code in grid_universe/systems/pathfinding.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
|
get_straight_line_next_position ¶
get_straight_line_next_position(state, entity_id, target_id)
Choose axis-aligned step maximizing dot product toward target.
Source code in grid_universe/systems/pathfinding.py
98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
|
entity_pathfinding ¶
entity_pathfinding(state, usage_limit, entity_id)
Apply pathfinding for a single entity (straight-line or A*).
Source code in grid_universe/systems/pathfinding.py
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
|
pathfinding_system ¶
pathfinding_system(state)
Advance all pathfinding-enabled entities by one tile if possible.
Source code in grid_universe/systems/pathfinding.py
152 153 154 155 156 157 |
|
grid_universe.systems.position ¶
Position snapshot system.
Maintains prev_position
as an immutable snapshot of all entity positions
at the start (or end) of a step. Other systems (e.g. portal teleportation,
trail generation, damage-on-crossing) rely on this historical information to
detect transitions or movement paths.
position_system ¶
position_system(state)
Snapshot current entity positions.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
Current immutable simulation state. |
required |
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
New state with |
Source code in grid_universe/systems/position.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Interactions (portal, damage, push, tile)¶
-
portal_system
: Teleports collidable entrants to the paired portal. -
damage_system
: Applies Damage/LethalDamage on co-location and cross paths; respects Immunity/Phasing. -
push_system
: Pushes Pushable objects if destination is free; moves agent and pushable. -
Tile systems: Reward and Cost handling.
grid_universe.systems.portal ¶
Portal teleportation system.
Moves entering collidable entities from a portal to its paired portal's position. An entity is considered entering if its previous position differs from the current one and it is present in the augmented trail for the portal's tile this step.
portal_system_entity ¶
portal_system_entity(state, augmented_trail, portal_id)
Teleport entities entering the specified portal to its pair.
Source code in grid_universe/systems/portal.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
portal_system ¶
portal_system(state)
Apply portal teleportation for all portals in the state.
Source code in grid_universe/systems/portal.py
51 52 53 54 55 56 57 58 |
|
grid_universe.systems.damage ¶
Damage / lethal damage resolution system.
Rules (inclusion predicates): * Overlap: target_curr == damager_curr * Swap: target_prev == damager_curr AND target_curr == damager_prev * Trail intersection: (target_trail & damager_trail) != ∅ * Endpoint cross: target_curr == damager_prev AND (target_prev ∈ damager_trail OR damager_prev ∈ target_trail)
Exclusion (takes precedence): * Pure vacated origin: target steps onto damager_prev without swap and no path intersection (no trail overlap, target_prev not in damager_trail, and damager_prev/current not in target_trail)
Additionally
- A damager may harm a specific target at most once per turn (tracked via
damage_hits
). - Self‑damage is ignored.
- Trail lookups are cached.
damage_system ¶
damage_system(state)
Resolve damage / lethal interactions for this turn.
O(H * D + T) where
H = # entities with health D = # entities with damage/lethal components T = total trail entries this action
Source code in grid_universe/systems/damage.py
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
|
grid_universe.systems.push ¶
Push interaction system.
Enables entities (typically agents) to push adjacent entities marked with the
Pushable
component into the next cell along the interaction vector,
provided the destination cell is free of blocking/collidable constraints.
Supports multi-entity stacks at the source tile by moving all pushables.
compute_destination ¶
compute_destination(state, current_pos, next_pos)
Compute push destination given current and occupant next positions.
Returns the square beyond next_pos
in the movement direction, applying
wrap logic if the state's move function is the wrapping one. None
if
outside bounds and not wrapping.
Source code in grid_universe/systems/push.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
push_system ¶
push_system(state, eid, next_pos)
Attempt to push any pushable entities at next_pos
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
Current immutable state. |
required |
eid
|
EntityID
|
Entity initiating the push (must have a position). |
required |
next_pos
|
Position
|
Adjacent position the entity is trying to move into. |
required |
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Updated state with moved positions if push succeeds; original state otherwise. |
Source code in grid_universe/systems/push.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
|
grid_universe.systems.tile ¶
Tile interaction systems.
Applies passive score modifications for standing on tiles that carry
Rewardable
(positive) or Cost
(negative) components which are not
collected through the collectible system (i.e. non-pickup surfaces).
get_noncollectible_entities ¶
get_noncollectible_entities(state, pos, component_map)
Return entity IDs at pos
with a component but not collectible.
Source code in grid_universe/systems/tile.py
19 20 21 22 23 24 25 26 27 28 |
|
tile_reward_system ¶
tile_reward_system(state, eid)
Increase score for rewardable non-collectible entities at agent tile.
Source code in grid_universe/systems/tile.py
31 32 33 34 35 36 37 38 39 40 41 42 |
|
tile_cost_system ¶
tile_cost_system(state, eid)
Decrease score for cost-bearing non-collectible entities at agent tile.
Source code in grid_universe/systems/tile.py
45 46 47 48 49 50 51 52 53 54 55 56 |
|
Status and terminal¶
-
Status tick/GC: Decrement time limits and garbage-collect expired/orphaned effects.
-
Terminal: Win/lose conditions.
grid_universe.systems.status ¶
Status effect lifecycle system.
Coordinates ticking and garbage collection of effect entities referenced by a
Status
component. Supports two limiter decorators:
TimeLimit
: decremented each step.UsageLimit
: decremented by specific systems upon use (outside this file).
The system performs two phases: 1. Tick: Decrement all time limits for active effects. 2. GC: Remove orphaned or expired effect IDs, pruning both the owning entity's status set and the global entity map.
tick_time_limit ¶
tick_time_limit(state, status, time_limit)
Decrement per-effect time limits present in status
.
Source code in grid_universe/systems/status.py
22 23 24 25 26 27 28 29 30 31 32 33 |
|
cleanup_effect ¶
cleanup_effect(effect_id, effect_ids)
Remove effect_id
from status if present.
Source code in grid_universe/systems/status.py
36 37 38 39 40 41 42 |
|
is_effect_expired ¶
is_effect_expired(effect_id, time_limit, usage_limit)
Return True if effect's time or usage limit has reached zero.
Source code in grid_universe/systems/status.py
45 46 47 48 49 50 51 52 53 54 55 |
|
garbage_collect ¶
garbage_collect(state, time_limit, usage_limit, status)
Remove orphaned or expired effects from status and entity maps.
Source code in grid_universe/systems/status.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
|
status_tick_system ¶
status_tick_system(state)
Phase 1: decrement all active time limits.
Source code in grid_universe/systems/status.py
83 84 85 86 87 88 89 90 91 92 93 94 95 |
|
status_gc_system ¶
status_gc_system(state)
Phase 2: prune orphaned / expired effects from statuses and entities.
Source code in grid_universe/systems/status.py
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
|
status_system ¶
status_system(state)
Run tick + GC phases for all statuses (public entry point).
Source code in grid_universe/systems/status.py
118 119 120 121 122 |
|
grid_universe.systems.terminal ¶
Terminal condition systems.
Defines win/lose evaluation utilities that set state.win
or state.lose
flags exactly once when conditions are met (objective success or agent death).
These flags are side-channel indicators—other systems may short-circuit when
the state is already terminal.
win_system ¶
win_system(state, agent_id)
Set win
flag if objective function returns True for agent.
Skips evaluation if state already terminal or agent invalid/dead.
Source code in grid_universe/systems/terminal.py
15 16 17 18 19 20 21 22 23 24 25 |
|
lose_system ¶
lose_system(state, agent_id)
Set lose
flag if agent is dead (idempotent).
Source code in grid_universe/systems/terminal.py
28 29 30 31 32 |
|
turn_system ¶
turn_system(state, agent_id)
Set lose
flag if turn limit is reached.
Source code in grid_universe/systems/terminal.py
35 36 37 38 39 40 41 42 43 44 |
|
Utilities¶
Grid and ECS helpers¶
-
Grid helpers: bounds, blocking, wrap, push destination math.
-
ECS helpers: query entities at a position and with components.
grid_universe.utils.grid ¶
Grid math / collision helpers.
Utility predicates used by movement & push systems. Functions here are pure and intentionally lightweight to keep inner loops fast.
is_in_bounds ¶
is_in_bounds(state, pos)
Return True if pos
lies within the level rectangle.
Source code in grid_universe/utils/grid.py
14 15 16 |
|
wrap_position ¶
wrap_position(x, y, width, height)
Toroidal wrap for coordinates (used by wrap movement).
Source code in grid_universe/utils/grid.py
19 20 21 |
|
is_blocked_at ¶
is_blocked_at(state, pos, check_collidable=True, check_pushable=True)
Return True if any blocking entity occupies pos
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
World state. |
required |
pos
|
Position
|
Candidate destination. |
required |
check_collidable
|
bool
|
If True, treat |
True
|
Source code in grid_universe/utils/grid.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
grid_universe.utils.ecs ¶
ECS convenience queries.
Helper functions for querying entity/component relationships without
introducing iteration logic into systems. All functions are pure and operate
on the immutable :class:grid_universe.state.State
snapshot.
Performance: entities_at
uses a cached reverse index of the immutable
State.position
PMap to provide O(1) lookups per state snapshot.
entities_at ¶
entities_at(state, pos)
Return entity IDs whose position equals pos
.
Source code in grid_universe/utils/ecs.py
36 37 38 39 |
|
entities_with_components_at ¶
entities_with_components_at(state, pos, *component_stores)
Return IDs at pos
possessing all provided component stores.
Source code in grid_universe/utils/ecs.py
42 43 44 45 46 47 48 49 |
|
Status/inventory and health helpers¶
-
Status helpers: finding/consuming effects with limits.
-
Inventory helpers: keys lookup; add/remove items.
-
Health helpers: damage application and death check.
grid_universe.utils.status ¶
Status effect utility helpers.
Pure helpers for querying, selecting and consuming effects referenced by a
Status
component. Separation from the system module keeps logic reusable
across movement/pathfinding or interaction systems that need to consult or
exhaust limited effects (e.g., usage-limited phasing).
has_effect ¶
has_effect(state, effect_id)
Return True if effect_id
exists in any runtime effect store.
Source code in grid_universe/utils/status.py
42 43 44 45 46 47 48 |
|
valid_effect ¶
valid_effect(state, effect_id)
Return True if effect has no expired time/usage limit.
Source code in grid_universe/utils/status.py
51 52 53 54 55 56 57 58 |
|
add_status ¶
add_status(status, effect_id)
Return new Status
with effect ID added.
Source code in grid_universe/utils/status.py
61 62 63 |
|
remove_status ¶
remove_status(status, effect_id)
Return new Status
with effect ID removed.
Source code in grid_universe/utils/status.py
66 67 68 |
|
get_status_effect ¶
get_status_effect(effect_ids, effects, time_limit, usage_limit)
Select a valid effect from effect_ids
matching any provided store.
Selection rules: 1. Filter to effect IDs present in at least one supplied effect map. 2. Drop expired effects (time or usage limit <= 0). 3. Prefer effects without usage limits; otherwise lowest EID yields tie.
Source code in grid_universe/utils/status.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
|
use_status_effect ¶
use_status_effect(effect_id, usage_limit)
Consume one use from a usage-limited effect if present.
Source code in grid_universe/utils/status.py
119 120 121 122 123 124 125 126 127 128 129 |
|
use_status_effect_if_present ¶
use_status_effect_if_present(effect_ids, effects, time_limit, usage_limit)
Select and consume an effect (if any) returning updated usage map.
Source code in grid_universe/utils/status.py
132 133 134 135 136 137 138 139 140 141 142 143 |
|
grid_universe.utils.inventory ¶
Inventory manipulation helpers.
add_item ¶
add_item(inventory, item_id)
Return a new inventory with item_id
added.
Source code in grid_universe/utils/inventory.py
9 10 11 |
|
remove_item ¶
remove_item(inventory, item_id)
Return a new inventory with item_id
removed.
Source code in grid_universe/utils/inventory.py
14 15 16 |
|
has_key_with_id ¶
has_key_with_id(inventory, key_store, key_id)
Return ID of a key with key_id
if present in inventory else None.
Source code in grid_universe/utils/inventory.py
19 20 21 22 23 24 25 26 27 |
|
all_keys_with_id ¶
all_keys_with_id(inventory, key_store, key_id)
Return persistent set of all key IDs matching key_id
.
Source code in grid_universe/utils/inventory.py
30 31 32 33 34 35 36 37 38 |
|
grid_universe.utils.health ¶
Health and damage helpers.
apply_damage_and_check_death ¶
apply_damage_and_check_death(health_dict, dead_dict, eid, damage, lethal)
Apply damage to entity and mark dead if lethal or HP reaches zero.
Source code in grid_universe/utils/health.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
Rendering helpers and image ops¶
-
Numpy-based HSV recoloring preserving tone.
-
Direction triangle overlays.
grid_universe.utils.image ¶
Image utilities.
Vectorized helpers for lightweight image post-processing used by the renderer.
The primary goal is fast, deterministic recoloring of small RGBA sprite textures (e.g. 16x16 or 32x32) without introducing heavyweight dependencies or per-pixel Python loops. Operations are implemented with NumPy and operate on float32 buffers to balance precision and performance.
Key Functions¶
recolor_image_keep_tone: Re-hues an image to a target color while preserving original per-pixel luminance (value channel) and optionally original saturation. This enables palette swapping / team coloring while retaining shading.
draw_direction_triangles_on_image
Overlays directional arrow/triangle glyphs onto a texture to visualize movement intent or facing direction, used for debugging pathfinding or animating multi-step movements.
Implementation Notes¶
The HSV <-> RGB conversions are fully vectorized and attempt to minimize branching; alpha is preserved exactly unless explicitly recolored.
recolor_image_keep_tone ¶
recolor_image_keep_tone(base, target_rgb, keep_saturation=True, saturation_mix=0.0, min_saturation=0.0)
Recolor non-transparent pixels by replacing Hue with target color's Hue, preserving per-pixel Value (brightness/tone). Saturation is preserved by default.
Source code in grid_universe/utils/image.py
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
|
draw_direction_triangles_on_image ¶
draw_direction_triangles_on_image(image, size, dx, dy, count)
Draw 'count' filled triangles pointing (dx, dy) on the given RGBA image. Triangles are centered: the centroid of each triangle is symmetrically arranged around the image center. Spacing is between triangle centroids.
Source code in grid_universe/utils/image.py
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
|
Trail and terminal checks¶
-
Trail: record traversed positions between prev and current.
-
Terminal: convenience validation and terminal checks.
grid_universe.utils.trail ¶
Trail aggregation helpers.
Produces augmented trail maps merging current positions of specific entities with previously recorded traversed positions. Used by portal and potential AoE/effect systems to reason about paths taken rather than only endpoints.
get_augmented_trail ¶
get_augmented_trail(state, entity_ids)
Return merged mapping of positions to entity sets (current + historic).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
Current immutable world state containing both live entity positions
and the accumulated historic |
required |
entity_ids
|
PSet[EntityID]
|
Entity ids whose current position should be merged
into the historic trail. Entities absent from |
required |
Returns:
Type | Description |
---|---|
PMap[Position, PSet[EntityID]]
|
PMap[Position, PSet[EntityID]]: Mapping from grid positions to the persistent set of entity ids that have either previously occupied (historic) or currently occupy that position among the provided tracked entities. |
Source code in grid_universe/utils/trail.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
add_trail_position ¶
add_trail_position(state, entity_id, new_pos)
Return new state with entity_id
recorded as having entered new_pos
.
Idempotent for (entity, position) within an action: repeated additions of the same (entity, tile) pair are harmless due to set semantics.
Source code in grid_universe/utils/trail.py
48 49 50 51 52 53 54 55 56 57 |
|
grid_universe.utils.terminal ¶
Terminal condition helper predicates.
is_valid_state ¶
is_valid_state(state, agent_id)
Return True if agent exists and has a position.
Source code in grid_universe/utils/terminal.py
7 8 9 |
|
is_terminal_state ¶
is_terminal_state(state, agent_id)
Return True if state already satisfies win/lose or agent is dead.
Source code in grid_universe/utils/terminal.py
12 13 14 |
|
GC (entity pruning)¶
- Prune entities not reachable from any live structure to keep State compact.
grid_universe.utils.gc ¶
Garbage collection utilities.
Removes unreachable entity/component entries from state maps. Reachable
entities include:
* All IDs in the master entity
map.
* Effect entity IDs referenced by any Status
component.
* Item IDs referenced by any Inventory
component.
The garbage collector prunes orphaned component entries (e.g., an effect map entry for an effect whose owning status no longer references it) which keeps state size bounded and avoids leaking stale objects during long simulations.
compute_alive_entities ¶
compute_alive_entities(state)
Return the closure of entity IDs reachable from registries & references.
Source code in grid_universe/utils/gc.py
22 23 24 25 26 27 28 29 |
|
run_garbage_collector ¶
run_garbage_collector(state)
Prune component maps to only contain reachable entity IDs.
Source code in grid_universe/utils/gc.py
32 33 34 35 36 37 38 39 40 41 42 |
|
Levels (Authoring)¶
Authoring model and factories¶
-
Level: mutable grid of
EntitySpec
for authoring. -
EntitySpec
: bag of components with authoring-only lists and wiring refs. -
Factories: ready-made objects (agent, floor, wall, key/door, portal, box, hazards, enemies, powerups).
grid_universe.levels.grid ¶
Authoring grid representation (pre-immutable State).
Provides a simple editing API (add/remove/move) for building up a level prior
to conversion. Use levels.factories
helpers to create EntitySpec
objects conveniently.
Level
dataclass
¶
Grid-centric, authoring-time level representation.
- grid[y][x]
is a list of EntityObject
instances at that cell.
- Level stores configuration like move_fn, objective_fn, seed, and simple meta (turn/score/etc.).
- This module is State-agnostic. Use the converter (levels.convert.to_state / from_state)
to bridge between Level and the immutable ECS State.
Source code in grid_universe/levels/grid.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
|
add ¶
add(pos, obj)
Place an EntityObject into the cell at pos (x, y).
Source code in grid_universe/levels/grid.py
53 54 55 56 57 58 59 |
|
add_many ¶
add_many(items)
Place multiple EntityObject instances. Each entry is (pos, obj).
Source code in grid_universe/levels/grid.py
61 62 63 64 65 66 |
|
remove ¶
remove(pos, obj)
Remove a specific EntityObject (by identity) from the cell at pos. Returns True if the object was found and removed, False otherwise.
Source code in grid_universe/levels/grid.py
68 69 70 71 72 73 74 75 76 77 78 79 80 |
|
remove_if ¶
remove_if(pos, predicate)
Remove all objects in the cell at pos for which predicate(obj) is True. Returns the number of removed objects.
Source code in grid_universe/levels/grid.py
82 83 84 85 86 87 88 89 90 91 92 93 |
|
move_obj ¶
move_obj(from_pos, obj, to_pos)
Move a specific EntityObject (by identity) from one cell to another. Returns True if moved (i.e., it was found in the source cell), False otherwise.
Source code in grid_universe/levels/grid.py
95 96 97 98 99 100 101 102 103 |
|
clear_cell ¶
clear_cell(pos)
Remove all objects from the cell at pos. Returns the number of removed objects.
Source code in grid_universe/levels/grid.py
105 106 107 108 109 110 111 112 113 |
|
objects_at ¶
objects_at(pos)
Return a shallow copy of the list of objects at pos.
Source code in grid_universe/levels/grid.py
115 116 117 118 119 120 121 |
|
grid_universe.levels.entity_spec ¶
Authoring-time mutable entity specification.
EntitySpec
instances gather optional component instances plus authoring
metadata (inventory/status lists and wiring references). They are converted to
immutable ECS entities by :mod:levels.convert
.
EntitySpec
dataclass
¶
Mutable bag of ECS components for authoring (no Position here). Authoring-only wiring refs: - pathfind_target_ref: reference to another EntityObject to target - pathfinding_type: desired path type when wiring (if target ref set) - portal_pair_ref: reference to another EntityObject to pair with as a portal Authoring-only nested collections: - inventory: list of EntityObject (items carried; materialized as separate entities) - status: list of EntityObject (effects active; materialized as separate entities)
Source code in grid_universe/levels/entity_spec.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
|
iter_components ¶
iter_components()
Yield (store_name, component) for non-None component fields that map to State stores.
Source code in grid_universe/levels/entity_spec.py
129 130 131 132 133 134 135 136 137 138 |
|
grid_universe.levels.factories ¶
Convenience factory functions for authoring EntitySpec
objects.
Each helper returns a preconfigured :class:EntitySpec
with a common pattern
(agent, floor, wall, coin, key, door, portal, hazards, effects, etc.). These
are mutable authoring-time blueprints converted into immutable ECS entities by
levels.convert.to_state
.
create_agent ¶
create_agent(health=5)
Player-controlled agent with health + inventory + empty status.
Source code in grid_universe/levels/factories.py
48 49 50 51 52 53 54 55 56 57 |
|
create_floor ¶
create_floor(cost_amount=1)
Background floor tile with movement cost.
Source code in grid_universe/levels/factories.py
60 61 62 63 64 65 |
|
create_wall ¶
create_wall()
Blocking wall tile.
Source code in grid_universe/levels/factories.py
68 69 70 71 72 73 |
|
create_exit ¶
create_exit()
Exit tile used in objectives.
Source code in grid_universe/levels/factories.py
76 77 78 79 80 81 |
|
create_coin ¶
create_coin(reward=None)
Collectible coin awarding optional score when picked up.
Source code in grid_universe/levels/factories.py
84 85 86 87 88 89 90 |
|
create_core ¶
create_core(reward=None, required=True)
Key objective collectible ("core") optionally giving reward.
Source code in grid_universe/levels/factories.py
93 94 95 96 97 98 99 100 |
|
create_key ¶
create_key(key_id)
Key item unlocking doors with matching key_id
.
Source code in grid_universe/levels/factories.py
103 104 105 106 107 108 109 |
|
create_door ¶
create_door(key_id)
Locked door requiring a key with the same id.
Source code in grid_universe/levels/factories.py
112 113 114 115 116 117 118 |
|
create_portal ¶
create_portal(*, pair=None)
Portal endpoint (optionally auto-paired during authoring).
If pair
is provided we set reciprocal refs so conversion wires the
pair entities with each other's id.
Source code in grid_universe/levels/factories.py
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
|
create_box ¶
create_box(pushable=True, moving_axis=None, moving_direction=None, moving_bounce=True, moving_speed=1)
Pushable / blocking box (optionally not pushable).
Source code in grid_universe/levels/factories.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
|
create_monster ¶
create_monster(damage=3, lethal=False, *, moving_axis=None, moving_direction=None, moving_bounce=True, moving_speed=1, pathfind_target=None, path_type=PathfindingType.PATH)
Basic enemy with damage and optional lethal + pathfinding target.
Source code in grid_universe/levels/factories.py
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
|
create_hazard ¶
create_hazard(appearance, damage, lethal=False, priority=7)
Static damaging (optionally lethal) tile-like hazard.
Source code in grid_universe/levels/factories.py
194 195 196 197 198 199 200 201 202 203 204 205 206 |
|
create_speed_effect ¶
create_speed_effect(multiplier, time=None, usage=None)
Collectible speed effect (optional time / usage limits).
Source code in grid_universe/levels/factories.py
209 210 211 212 213 214 215 216 217 218 219 220 221 |
|
create_immunity_effect ¶
create_immunity_effect(time=None, usage=None)
Collectible immunity effect (optional limits).
Source code in grid_universe/levels/factories.py
224 225 226 227 228 229 230 231 232 233 234 235 |
|
create_phasing_effect ¶
create_phasing_effect(time=None, usage=None)
Collectible phasing effect (optional limits).
Source code in grid_universe/levels/factories.py
238 239 240 241 242 243 244 245 246 247 248 249 |
|
Conversion between Level and State¶
-
to_state
: instantiate entities withPosition
; wire references; materialize nested lists. -
from_state
: reconstruct authoring specs from positioned entities; restore lists and refs.
grid_universe.levels.convert ¶
Conversion utilities between authoring Level
and runtime State
.
Two primary operations:
to_state
: Materialize immutable ECS world from a grid ofEntitySpec
.from_state
: Reconstruct a mutable authoring representation from a live state.
Handles wiring of portals, pathfinding targets, inventory & status effect embedding (nested lists -> separate entities), and assigns deterministic EntityIDs.
to_state ¶
to_state(level)
Convert a Level (grid of EntityObject) into an immutable State.
Semantics: - Copies all present ECS components from each EntityObject (including Inventory, Status) onto a new Entity. - Assigns Position for on-grid entities; nested inventory/effect entities have no Position. - Materializes authoring-only lists: * inventory_list: each item EntityObject becomes a new entity; its id is added to holder's Inventory.item_ids. If holder lacks an Inventory component, an empty one is created. * status_list: each effect EntityObject becomes a new entity; its id is added to holder's Status.effect_ids. If holder lacks a Status component, an empty one is created. - Wiring: * pathfind_target_ref: if set and the referenced object is placed, sets Pathfinding.target to that eid, creating a Pathfinding component if missing (type defaults to PathfindingType.PATH or uses obj.pathfinding_type). * portal_pair_ref: if set (on-grid for both ends), sets reciprocal Portal.pair_entity.
Source code in grid_universe/levels/convert.py
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
|
from_state ¶
from_state(state)
Convert an immutable State back into a mutable Level (grid of EntityObject).
Behavior:
- Positioned entities are placed into Level.grid[y][x]
in ascending eid order (deterministic).
- EntityObject components (including Inventory/Status) are reconstructed for positioned entities.
- Holder inventory_list/status_list are rebuilt from Inventory.item_ids / Status.effect_ids
by reconstructing item/effect EntityObjects (not placed on the grid).
- Authoring-time wiring refs (pathfind_target_ref, portal_pair_ref) are also restored for positioned
entities when their targets/pairs are themselves positioned.
Source code in grid_universe/levels/convert.py
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
|
Rendering¶
Texture renderer¶
TextureRenderer
: Compose background, main, corner icons; pick textures by(AppearanceName, properties)
; recolor groups; draw moving overlays.
grid_universe.renderer.texture ¶
Texture-based renderer utilities.
Transforms a State
into a composited RGBA image using per-entity
Appearance
metadata, property-derived texture variants, optional group
recoloring and motion glyph overlays.
Rendering Model¶
- Entities occupying the same cell are grouped into categories:
- Background(s):
appearance.background=True
(e.g., floor, wall) - Main: highest-priority non-background entity
- Corner Icons: up to four icon entities (
appearance.icon=True
) placed in tile corners (NW, NE, SW, SE) - Others: additional layered entities (drawn between background and main)
- Background(s):
- A texture path is chosen via an object + property signature lookup. If the path refers to a directory, a deterministic random selection occurs.
- Group-based recoloring (e.g., matching keys and locks, portal pairs) applies a hue shift while preserving shading (value channel) and saturation rules.
- Optional movement direction triangles are overlaid for moving entities.
Customization Hooks¶
- Provide a custom
texture_map
for alternative asset packs. - Replace or extend
DEFAULT_GROUP_RULES
to recolor other sets of entities. - Supply
tex_lookup_fn
to implement caching, animation frames, or atlas packing.
Performance Notes¶
- A lightweight cache key (path, size, group, movement vector, speed) helps reuse generated PIL images across frames.
lru_cache
ongroup_to_color
ensures stable, deterministic colors without recomputing HSV conversions.
ObjectRendering
dataclass
¶
Lightweight container capturing render-relevant entity facets.
Attributes:
Name | Type | Description |
---|---|---|
appearance |
Appearance
|
The entity's appearance component (or a default anonymous one). |
properties |
Tuple[str, ...]
|
Property component collection names (e.g. |
group |
str | None
|
Deterministic recolor group identifier. |
move_dir |
tuple[int, int] | None
|
(dx, dy) direction for movement glyph overlay. |
move_speed |
int
|
Movement speed (number of direction triangles to draw). |
Source code in grid_universe/renderer/texture.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
TextureRenderer ¶
Source code in grid_universe/renderer/texture.py
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
|
render ¶
render(state)
Render convenience wrapper using stored configuration.
Source code in grid_universe/renderer/texture.py
591 592 593 594 595 596 597 598 599 600 |
|
derive_groups ¶
derive_groups(state, rules=DEFAULT_GROUP_RULES)
Apply grouping rules to each entity.
Later rendering stages may use groups to recolor related entities with a shared hue (e.g., all portals in a pair share the same color while still using the original texture shading).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
Immutable simulation state. |
required |
rules
|
List[GroupRule]
|
Ordered list of functions; first non-None group returned is used. |
DEFAULT_GROUP_RULES
|
Returns:
Type | Description |
---|---|
Dict[EntityID, Optional[str]]
|
Dict[EntityID, str | None]: Mapping of entity id to chosen group id (or |
Source code in grid_universe/renderer/texture.py
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
|
group_to_color
cached
¶
group_to_color(group_id)
Deterministically map a group string to an RGB color.
Uses the group id as a seed to generate stable but visually distinct HSV values, then converts them to RGB.
Source code in grid_universe/renderer/texture.py
262 263 264 265 266 267 268 269 270 271 272 273 274 |
|
apply_recolor_if_group ¶
apply_recolor_if_group(tex, group)
Recolor wrapper that sets hue to the group's color while preserving tone.
Delegates to :func:recolor_image_keep_tone
; if no group is provided the
texture is returned unchanged.
Source code in grid_universe/renderer/texture.py
277 278 279 280 281 282 283 284 285 286 287 288 289 |
|
load_texture ¶
load_texture(path, size)
Load and resize a texture, returning None if inaccessible or invalid.
Source code in grid_universe/renderer/texture.py
292 293 294 295 296 297 |
|
get_object_renderings ¶
get_object_renderings(state, eids, groups)
Build rendering descriptors for entity IDs in a single cell.
Inspects component PMaps on the State
to infer property labels,
movement direction and speed, then packages them in ObjectRendering
objects for subsequent texture lookup and layering decisions.
Source code in grid_universe/renderer/texture.py
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
|
choose_background ¶
choose_background(object_renderings)
Select the highest-priority background object.
Raises¶
ValueError If no candidate background exists in the cell.
Source code in grid_universe/renderer/texture.py
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
|
choose_main ¶
choose_main(object_renderings)
Select main (foreground) object: lowest appearance priority value.
Returns None
if no non-background objects exist.
Source code in grid_universe/renderer/texture.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
|
choose_corner_icons ¶
choose_corner_icons(object_renderings, main)
Return up to four icon objects (excluding main) sorted by priority.
Source code in grid_universe/renderer/texture.py
380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
|
get_path ¶
get_path(object_asset, texture_hmap)
Resolve a texture path for an object asset signature.
Attempts to find the nearest matching property tuple (maximizing shared properties, minimizing unmatched) to allow textures that only specify a subset of possible property labels.
Source code in grid_universe/renderer/texture.py
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
|
select_texture_from_directory ¶
select_texture_from_directory(dir, seed)
Choose a deterministic random texture file from a directory.
Source code in grid_universe/renderer/texture.py
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
|
render ¶
render(state, resolution=DEFAULT_RESOLUTION, subicon_percent=DEFAULT_SUBICON_PERCENT, texture_map=None, asset_root=DEFAULT_ASSET_ROOT, tex_lookup_fn=None, cache=None)
Render a State
into a PIL Image.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
state
|
State
|
Immutable game state to visualize. |
required |
resolution
|
int
|
Output image width in pixels (height derived from aspect ratio). |
DEFAULT_RESOLUTION
|
subicon_percent
|
float
|
Relative size of corner icons compared to a cell's size. |
DEFAULT_SUBICON_PERCENT
|
texture_map
|
TextureMap | None
|
Mapping from |
None
|
asset_root
|
str
|
Root directory containing the asset hierarchy (e.g. |
DEFAULT_ASSET_ROOT
|
tex_lookup_fn
|
TexLookupFn | None
|
Override for texture loading/recoloring/overlay logic. |
None
|
cache
|
dict | None
|
Mutable memoization dict keyed by |
None
|
Returns:
Type | Description |
---|---|
Image
|
Image.Image: Composited RGBA image of the entire grid. |
Source code in grid_universe/renderer/texture.py
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 |
|
Examples¶
Procedural maze¶
- Rich generator with walls/floors, agent/exit, keys/doors, portals, enemies, hazards, and powerups. Provides knobs for density and counts.
grid_universe.examples.maze ¶
Procedural maze level generator example.
This module demonstrates authoring a parameterized maze-based level using the
Level
authoring API and factory helpers, then converting to an immutable
State
suitable for simulation or Gym-style environments.
Design Goals¶
- Showcase composition of factories (agent, walls, doors, portals, hazards,
power-ups, enemies) with authoring-time references (e.g., portal pairing,
enemy pathfinding target reference to the agent) that are resolved during
to_state
conversion. - Provide tunable difficulty levers: wall density, counts of required objectives, rewards, hazards, enemies, doors, portals and power-ups.
- Illustrate how movement styles (static, directional patrol, straight-line pathfinding, full pathfinding) can be expressed via component choices.
Usage Example¶
from grid_universe.examples import maze
state = maze.generate(width=20, height=20, seed=123)
# Render / step the state using the engine's systems or gym wrapper.
Key Concepts Illustrated¶
Required Items:
Use cores flagged as required=True
which the default objective logic
expects to be collected before reaching the exit.
Power-Ups:
Effects created with optional time or usage limits (speed, immunity,
phasing) acting as pickups.
Enemies:
Configurable movement style and lethality; pathfinding enemies reference
the agent to resolve target entity IDs later.
Essential Path:
Minimal union of shortest paths that touch required items and exit. Other
entities (hazards, enemies, boxes) prefer non-essential cells.
generate ¶
generate(width, height, num_required_items=1, num_rewardable_items=1, num_portals=1, num_doors=1, health=5, movement_cost=1, required_item_reward=10, rewardable_item_reward=10, boxes=DEFAULT_BOXES, powerups=DEFAULT_POWERUPS, hazards=DEFAULT_HAZARDS, enemies=DEFAULT_ENEMIES, wall_percentage=0.8, move_fn=default_move_fn, objective_fn=default_objective_fn, seed=None, turn_limit=None)
Generate a randomized maze game state.
This function orchestrates maze carving, tile classification, entity
placement and authoring-time reference wiring before producing the
immutable simulation State
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
width
|
int
|
Width of the maze grid. |
required |
height
|
int
|
Height of the maze grid. |
required |
num_required_items
|
int
|
Number of required cores that must be collected before exit. |
1
|
num_rewardable_items
|
int
|
Number of optional reward coins. |
1
|
num_portals
|
int
|
Number of portal pairs to place (each pair consumes two open cells). |
1
|
num_doors
|
int
|
Number of door/key pairs; each door is locked by its matching key. |
1
|
health
|
int
|
Initial agent health points. |
5
|
movement_cost
|
int
|
Per-tile movement cost encoded in floor components. |
1
|
required_item_reward
|
int
|
Reward granted for collecting each required item. |
10
|
rewardable_item_reward
|
int
|
Reward granted for each optional reward item (coin). |
10
|
boxes
|
List[BoxSpec]
|
List defining |
DEFAULT_BOXES
|
powerups
|
List[PowerupSpec]
|
Effect specifications converted into pickup entities. |
DEFAULT_POWERUPS
|
hazards
|
List[HazardSpec]
|
Hazard specifications |
DEFAULT_HAZARDS
|
enemies
|
List[EnemySpec]
|
Enemy specifications |
DEFAULT_ENEMIES
|
wall_percentage
|
float
|
Fraction of original maze walls to retain ( |
0.8
|
move_fn
|
MoveFn
|
Movement candidate function injected into the level. |
default_move_fn
|
objective_fn
|
ObjectiveFn
|
Win condition predicate injected into the level. |
default_objective_fn
|
seed
|
int | None
|
RNG seed for deterministic generation. |
None
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Fully wired immutable state ready for simulation. |
Source code in grid_universe/examples/maze.py
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
|
Authored gameplay progression levels¶
-
Curated, hand-authored deterministic levels (L0–L13) that ramp mechanics: movement, maze turns, optional cost-reducing coin tiles, required cores, key–door, hazards, portals, pushable boxes, enemies, and power‑ups (Shield, Ghost, Boots), culminating in an integrated capstone puzzle.
-
Each builder function accepts an optional
seed
(stored on the resultingState
and used by rendering RNG). Usegenerate_task_suite(base_seed=...)
orgenerate_task_suite(seed_list=[...])
to customize seeds while preserving structure.
Key functions:
from grid_universe.examples import gameplay_levels as gp
# Single level with custom seed
state = gp.build_level_power_boots(seed=9001)
# Full suite with deterministic shifted seeds
suite = gp.generate_task_suite(base_seed=5000) # seeds = 5000..5013
# Explicit per-level seeds
suite = gp.generate_task_suite(seed_list=[10*i for i in range(14)])
grid_universe.examples.gameplay_levels ¶
build_level_basic_movement ¶
build_level_basic_movement(seed=100)
L0: Movement smoke test (Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
100
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
|
build_level_maze_turns ¶
build_level_maze_turns(seed=101)
L1: Basic maze turns (Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
101
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
|
build_level_optional_coin ¶
build_level_optional_coin(seed=102)
L2: Optional coin path (Exit).
Coin reduce net cost along that route encouraging detour.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
102
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
|
build_level_required_one ¶
build_level_required_one(seed=103)
L3: One required core (Collect-then-Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
103
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
|
build_level_required_two ¶
build_level_required_two(seed=104)
L4: Two required cores and backtracking (Collect-then-Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
104
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
|
build_level_key_door ¶
build_level_key_door(seed=105)
L5: Key–Door gating (Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
105
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
|
build_level_hazard_detour ¶
build_level_hazard_detour(seed=106)
L6: Hazard detour (damage=2) (Exit).
Hazard imposes only base step cost but reduces health on contact.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
106
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
|
build_level_portal_shortcut ¶
build_level_portal_shortcut(seed=107)
L7: Portal pair shortcut (Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
107
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
|
build_level_pushable_box ¶
build_level_pushable_box(seed=108)
L8: Pushable box in narrow corridor (Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
108
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
|
build_level_enemy_patrol ¶
build_level_enemy_patrol(seed=109)
L9: Enemy patrol (damage=1) with safe avoidance (Exit).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
109
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
|
build_level_power_shield ¶
build_level_power_shield(seed=110)
L10: Shield (Immunity 5 uses) — necessary at a choke.
Unavoidable hazard (2 dmg) in a 1-wide corridor; agent has 2 HP. Without the shield effect the hazard would be lethal.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
110
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
|
build_level_power_ghost ¶
build_level_power_ghost(seed=111)
L11: Ghost (Phasing 5 turns) — necessary to pass a door; no key provided.
Single corridor blocked by a locked door; phasing allows bypassing blocking.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
111
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
|
build_level_power_boots ¶
build_level_power_boots(seed=112)
L12: Boots (Speed ×2, 5 turns) — useful to cross a 2-tile patrol window safely.
With 2× speed the agent traverses both tiles of a patrol gap in one action.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int
|
Deterministic seed stored on resulting |
112
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Authored immutable state. |
Source code in grid_universe/examples/gameplay_levels.py
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
|
generate_task_suite ¶
generate_task_suite(base_seed=None, *, seed_list=None)
Return ordered suite of authored levels (L0..L13) with configurable seeds.
Seeding strategy precedence
seed_list
if provided (must have length 14)base_seed
(seeds becomebase_seed + index
)- Each builder's default seed constant (backwards compatible)
Parameters:
Name | Type | Description | Default |
---|---|---|---|
base_seed
|
int | None
|
Optional base; offsets determine per-level seeds. |
None
|
seed_list
|
list[int] | None
|
Explicit seeds for each level (length must be 14). |
None
|
Returns:
Type | Description |
---|---|
List[State]
|
list[State]: Immutable states for each level. |
Source code in grid_universe/examples/gameplay_levels.py
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
|
Cipher levels (maze-based)¶
- Built on top of the procedural maze generator for structural consistency.
- API:
generate(...)
– backward compatible convenience wrapper.to_cipher_level(state, cipher_text_map, seed=None)
– transform an existing state.
- Supports optional sampling of
(cipher_text, objective_name)
pairs (objective must be registered).
Usage examples:
from grid_universe.examples.cipher_objective_levels import generate, to_cipher_level
from grid_universe.examples import maze
# Direct generation (EXIT objective when num_required_items == 0)
exit_state = generate(width=9, height=7, num_required_items=0, cipher_objective_pairs=[("ABC","exit")], seed=123)
# Generation with required cores switches objective logic
collect_state = generate(width=11, height=9, num_required_items=2, cipher_objective_pairs=[("DATA","default")], seed=456)
# Transform an existing maze state with custom cipher/objective mapping
base = maze.generate(width=9, height=7, seed=999, num_required_items=1)
adapted = to_cipher_level(base, [("HELLO","exit")], seed=999)
grid_universe.examples.cipher_objective_levels ¶
to_cipher_level ¶
to_cipher_level(base_state, cipher_text_pairs, seed=None)
Transform an existing state into a cipher micro-level variant.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
base_state
|
State
|
Source state (e.g. from |
required |
cipher_text_pairs
|
Iterable[CipherObjectivePair]
|
Iterable of (cipher_text, objective_name) pairs (required). At least one valid pair (objective registered) must be present. |
required |
seed
|
Optional[int]
|
Optional seed for deterministic sampling of the pair. |
None
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
New immutable state with updated |
Raises:
Type | Description |
---|---|
ValueError
|
If no valid pairs are supplied. |
Source code in grid_universe/examples/cipher_objective_levels.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
|
generate ¶
generate(width, height, num_required_items, cipher_objective_pairs, seed=None)
Generate a cipher micro-level using the maze generator and adapt it.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
width
|
int
|
Grid width. |
required |
height
|
int
|
Grid height. |
required |
num_required_items
|
int
|
Number of required cores in the base maze. |
required |
cipher_objective_pairs
|
Iterable[CipherObjectivePair]
|
Iterable of (cipher, objective_name) pairs; required. |
required |
seed
|
Optional[int]
|
Deterministic seed for maze + cipher sampling. |
None
|
Returns:
Name | Type | Description |
---|---|---|
State |
State
|
Immutable cipher micro-level state. |
Source code in grid_universe/examples/cipher_objective_levels.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
|
Gym Environment¶
GridUniverseEnv
: Gymnasium-compatible Env returning image observations and structured info; reward is delta score.
grid_universe.gym_env ¶
Gymnasium environment wrapper for Grid Universe.
Provides a structured observation that pairs a rendered RGBA image with rich
info dictionaries (agent status / inventory / active effects, environment
config). Reward is the delta of state.score
per step. terminated
is
True
on win, truncated
on lose (mirrors many Gym environments that
differentiate natural vs forced episode ends).
Observation schema (see docs for full details):
{
"image": np.ndarray(H,W,4),
"info": {
"agent": {...},
"status": {...},
"config": {...},
"message": str # empty string if None
}
}
or
Level # if observation_type="level"
Usage:
`` from grid_universe.gym_env import GridUniverseEnv from grid_universe.examples.maze import generate as maze_generate
env = GridUniverseEnv(initial_state_fn=maze_generate, width=9, height=9, seed=123) ``
Customization hooks
initial_state_fn
: Provide a callable that returns a fully builtState
.render_texture_map
/ resolution let you swap assets or resolution.
The environment is purposely not vectorized; wrap externally if needed.
EffectEntry ¶
Bases: TypedDict
Single active effect entry.
Fields use sentinel defaults in the runtime observation
- Empty string ("") for absent text fields.
- -1 for numeric fields that are logically None / not applicable.
Source code in grid_universe/gym_env.py
71 72 73 74 75 76 77 78 79 80 81 82 83 |
|
InventoryItem ¶
Bases: TypedDict
Inventory item entry (key / core / coin / generic item).
Source code in grid_universe/gym_env.py
86 87 88 89 90 91 92 |
|
HealthInfo ¶
Bases: TypedDict
Health block; -1 indicates missing (agent has no health component).
Source code in grid_universe/gym_env.py
95 96 97 98 99 |
|
AgentInfo ¶
Bases: TypedDict
Agent sub‑observation grouping health, effects and inventory.
Source code in grid_universe/gym_env.py
102 103 104 105 106 107 |
|
StatusInfo ¶
Bases: TypedDict
Environment status (score, phase, current turn).
Source code in grid_universe/gym_env.py
110 111 112 113 114 115 |
|
ConfigInfo ¶
Bases: TypedDict
Static / semi‑static config describing the active level & functions.
Source code in grid_universe/gym_env.py
118 119 120 121 122 123 124 125 126 |
|
InfoDict ¶
Bases: TypedDict
Full structured info payload accompanying every observation.
Source code in grid_universe/gym_env.py
129 130 131 132 133 134 135 |
|
Observation ¶
Bases: TypedDict
Top‑level observation returned by the environment.
image: RGBA image array (H x W x 4, dtype=uint8)
info: Rich structured dictionaries (see :class:InfoDict
).
Source code in grid_universe/gym_env.py
141 142 143 144 145 146 147 148 149 |
|
Action ¶
Bases: IntEnum
Stable integer mapping for integration with Gymnasium Discrete
spaces.
Source code in grid_universe/gym_env.py
152 153 154 155 156 157 158 159 160 161 |
|
GridUniverseEnv ¶
Bases: Env[Union[Observation, Level], integer]
Gymnasium Env
implementation for the Grid Universe.
Parameters mirror the procedural level generator plus rendering knobs. The
action space is Discrete(len(BaseAction))
; see :mod:grid_universe.actions
.
Source code in grid_universe/gym_env.py
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 |
|
__init__ ¶
__init__(initial_state_fn, render_mode='texture', render_resolution=DEFAULT_RESOLUTION, render_texture_map=DEFAULT_TEXTURE_MAP, render_asset_root=DEFAULT_ASSET_ROOT, observation_type='image', **kwargs)
Create a new environment instance.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
render_mode
|
str
|
"texture" to return PIL image frames, "human" to open a window. |
'texture'
|
render_resolution
|
int
|
Width (pixels) of rendered image (height derived). |
DEFAULT_RESOLUTION
|
render_texture_map
|
TextureMap
|
Mapping of |
DEFAULT_TEXTURE_MAP
|
initial_state_fn
|
Callable[..., State]
|
Callable returning an initial |
required |
**kwargs
|
Any
|
Forwarded to |
{}
|
Source code in grid_universe/gym_env.py
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
|
reset ¶
reset(*, seed=None, options=None)
Start a new episode.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
seed
|
int | None
|
Currently unused (procedural seed is passed via kwargs on construction). |
None
|
options
|
dict | None
|
Gymnasium options (unused). |
None
|
Returns:
Type | Description |
---|---|
Tuple[Union[Observation, Level], Dict[str, object]]
|
Tuple[Observation, dict]: Observation dict and empty info dict per Gymnasium API. |
Source code in grid_universe/gym_env.py
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
|
step ¶
step(action)
Apply one environment step.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
action
|
int | integer | Action
|
Integer index (or |
required |
Returns:
Type | Description |
---|---|
Tuple[Union[Observation, Level], float, bool, bool, Dict[str, object]]
|
Tuple[Observation, float, bool, bool, dict]: |
Source code in grid_universe/gym_env.py
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
|
render ¶
render(mode=None)
Render the current state.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
mode
|
str | None
|
"human" to display, "texture" to return PIL image. Defaults to the instance's configured render mode. |
None
|
Source code in grid_universe/gym_env.py
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 |
|
state_info ¶
state_info()
Return structured info
sub-dict used in observations.
Source code in grid_universe/gym_env.py
573 574 575 576 577 578 579 580 581 582 |
|
close ¶
close()
Release any renderer resources (no-op placeholder).
Source code in grid_universe/gym_env.py
619 620 621 |
|
agent_observation_dict ¶
agent_observation_dict(state, agent_id)
Compose structured agent sub‑observation.
Includes health, list of active effect entries, and inventory items.
Missing health is represented by None
values which are later converted
to sentinel numbers in the space definition (-1) when serialized to numpy
arrays (Gym leaves them as ints here).
Source code in grid_universe/gym_env.py
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
|
env_status_observation_dict ¶
env_status_observation_dict(state)
Status portion of observation (score, phase, turn).
Source code in grid_universe/gym_env.py
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
|
env_config_observation_dict ¶
env_config_observation_dict(state)
Config portion of observation (function names, seed, dimensions).
Source code in grid_universe/gym_env.py
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
|
Registries and enums¶
-
Movement registry: Name →
MoveFn
. -
Objective registry: Name →
ObjectiveFn
. -
Actions: string enum for core actions;
-
EffectType
andEffectLimit
enums.
Reference snippets:
from grid_universe.moves import MOVE_FN_REGISTRY
from grid_universe.objectives import OBJECTIVE_FN_REGISTRY
from grid_universe.actions import Action
print(MOVE_FN_REGISTRY.keys())
print(OBJECTIVE_FN_REGISTRY.keys())
print(list(Action))
Schemas and Data Shapes¶
This page captures common data structures you may want to reference without digging into code: observation dicts from the Gym env, texture map keys/values, and group recoloring rules.
Contents
- Gym observation schema
- Texture map schema
- Grouping rules and color mapping
Gym observation schema¶
Observation (obs: Dict[str, Any]
) returned by GridUniverseEnv
:
-
image
-
Type:
numpy.ndarray
-
Shape:
(H, W, 4)
,dtype=uint8
(RGBA)
-
-
info
-
agent
-
health
-
health
: int or-1
-
max_health
: int or-1
-
-
effects
: list of effect entries-
id
: int -
type
: "", "IMMUNITY", "PHASING", "SPEED" -
limit_type
: "", "TIME", "USAGE" -
limit_amount
: int or-1
-
multiplier
: int (SPEED only;-1
otherwise)
-
-
inventory
: list of item entries-
id
: int -
type
: "item" | "key" | "core" | "coin" -
key_id
: str ("" if not a key) -
appearance_name
: str ("" if unknown)
-
-
-
status
-
score
: int -
phase
: "ongoing" | "win" | "lose" -
turn
: int
-
-
config
-
move_fn
: str (function name) -
objective_fn
: str (function name) -
seed
: int (or-1
) -
width
: int -
height
: int
-
-
message
str
(empty string when absent); optional narrative/task hint text
-
Action space:
-
Discrete(7)
, mapping toAction
enum indices:-
0
: UP -
1
: DOWN -
2
: LEFT -
3
: RIGHT -
4
: USE_KEY -
5
: PICK_UP -
6
: WAIT
-
Reward:
- Delta score between steps (float).
Texture map schema¶
TextureMap entry key/value:
-
Key
-
Tuple[AppearanceName, Tuple[str, ...]]
-
Example:
(AppearanceName.BOX, ())
,(AppearanceName.BOX, ("pushable",))
-
-
Value
-
str
: path underasset_root
to a file or directory -
If directory: renderer picks a deterministic file per
state.seed
-
Resolution:
-
Asset path =
f"{asset_root}/{value}"
-
File types:
.png
,.jpg
,.jpeg
,.gif
Property matching:
-
When rendering an entity, the renderer constructs the set of string “properties” for that entity based on which component maps contain its EID (e.g.,
"pushable"
,"pathfinding"
,"dead"
,"locked"
,"required"
). -
The best-matching texture is chosen by maximizing overlap and minimizing unmatched properties among the available keys for that
AppearanceName
.
Grouping rules and color mapping¶
Grouping rules (derive_groups
) assign entities into color groups, e.g.:
-
Keys/doors by key id:
key_door_group_rule → "key:<key_id>"
-
Paired portals:
portal_pair_group_rule → "portal:<min_eid>-<max_eid>"
Color mapping:
-
group_to_color(group_id) → (r, g, b)
- Deterministic mapping using
random.Random(group_id)
to sample HSV; converted to RGB.
- Deterministic mapping using
Recolor:
apply_recolor_if_group(image, group)
replaces hue while preserving per-pixel tone (value) and, by default, saturation.
Extensibility:
- Add new
GroupRule
functions toDEFAULT_GROUP_RULES
(locally in your render wrapper) to recolor custom categories consistently.
from typing import Optional
from grid_universe.state import State
from grid_universe.types import EntityID
def my_group_rule(state: State, eid: EntityID) -> Optional[str]:
if eid in state.pushable:
return "pushable"
return None