Maps & Procedural Generation¶
A PGTG map is a grid of tiles, each a block of cells (9×9 in the default tile set). PGTG can either generate a random map every episode or load a fixed map from a file.
Tiles¶
Maps are assembled from a small set of road tiles. Each tile is characterized by its exits, i.e., which of the four sides (north, east, south, west) connect to a neighbor:
From left to right: straight, turn, T-crossing, four-way crossing, and dead
end. Every rotation of these five shapes is a distinct tile, identified by its
(north, east, south, west) exit tuple of 0/1 flags.
For instance, (1, 0, 1, 0) is a vertical straight and (1, 1, 1, 1) is a four-way crossing.
Procedural generation¶
When map_path is None (the default), a new random map is generated each episode from
the RandomMapContext. The pipeline is:
- Build a connectivity graph. Start from a grid where every adjacent tile pair is an
edge, attach virtual start and goal nodes on the requested borders, then randomly
remove edges down to
connections_percentageof the total. - Convert the graph to tiles. Each tile's exits are read off from which neighbors remain connected.
- Place obstacles. If
obstacle_probability > 0, each non-empty tile independently receives an obstacle type with that probability; the type is drawn fromobs_feature_weightsand stamped onto the tile through an obstacle mask (traffic-light masks are chosen to match the tile's exits).
Key parameters (see Configuration Reference):
| Parameter | Effect |
|---|---|
width, height |
Map size (in tiles). |
connections_percentage |
Density of connections. 0.0 gives a minimal start→goal path; 1.0 a fully connected grid. |
start_position, goal_position |
Where start/goal sit on the border, or "random". |
min_dist_start_goal |
Minimum path length between start and goal (only with random positions). |
obstacle_probability |
Per-tile chance of an obstacle. |
obs_feature_weights |
Relative weights for which obstacle type is chosen. |
from pgtg import PGTGEnv
from pgtg.contexts import RandomMapContext
env = PGTGEnv(
random_map_context=RandomMapContext(
width=3,
height=3,
connections_percentage=0.6,
obstacle_probability=0.3,
obs_feature_weights={"ice": 2.0, "sand": 1.0}, # ice twice as likely as sand
),
)
or equivalently:
import gymnasium as gym
import pgtg
env = gym.make(
"pgtg-v5",
random_map_context={
"width": 3,
"height": 3,
"connections_percentage": 0.6,
"obstacle_probability": 0.3,
"obs_feature_weights": {"ice": 2.0, "sand": 1.0},
},
)
Default benchmark maps¶
For reproducible evaluation, PGTG ships four fixed, preconstructed maps. Unlike random
maps they are identical across runs, which makes them well suited for controlled
comparison. Each is a square grid of tiles, generated with a high connection rate
(connections_percentage of 0.8),
so they are densely connected and offer many alternative routes:
| Name | Size (tiles) | Size (cells) | Discoverable cells |
|---|---|---|---|
benchmark_2x2 |
2 × 2 | 18 × 18 | 163 |
benchmark_3x3 |
3 × 3 | 27 × 27 | 353 |
benchmark_4x4 |
4 × 4 | 36 × 36 | 641 |
benchmark_5x5 |
5 × 5 | 45 × 45 | 1007 |
Discoverable cells counts the number of (x,y) pairs the agent can actually occupy:
everything that is not a wall and is reachable from the start.
In these four maps every non-wall cell is reachable, so the count equals the road area.
Load one by passing its name as map_path. Note that this disables procedural generation:
Loading and authoring map files¶
map_path also accepts a path to your own map file (the .json extension is optional):
Generating and saving a map¶
The pgtg.map.authoring module can generate a single random map and save it to disk, so you
can freeze a specific layout for reuse:
from pgtg.map.authoring import create_custom_map
create_custom_map(
"my_map.json",
width=3,
height=3,
seed=999,
connections_percentage=0.8,
start_position=(0, 2, "west"),
goal_position=(2, 0, "east"),
obstacle_probability=0.0,
)
Extra keyword arguments are forwarded to RandomMapContext.
Note
We plan to publish a tile/map editor tool soon, which will allow interactive editing of tilests and maps, including obstacle placement and traffic lange placement.
Map file format¶
A saved map is a JSON object:
{
"width": 3,
"height": 3,
"map": [
[ {"exits": [1, 0, 0, 0]}, {"exits": [1, 1, 0, 0]}, ... ],
...
],
"start": [0, 0, "west"],
"goal": [2, 2, "east"]
}
width/height— map size in tiles.map— aheight × widthgrid of tile objects. Each tile has anexitslist[north, east, south, west]. A tile may also carry an authored obstacle via"obstacle_type"("ice","sand","broken_road","traffic_light", or a registered custom obstacle name) and an"obstacle_mask"(see Custom Tiles & Maps).start/goal—[x, y, direction], wheredirectionis"north","east","south", or"west". Start and goal must lie on a map border.
For customizing the contents of tiles (walls, lanes, obstacle masks), see Custom Tiles & Maps.








