Procedural Frozen Lake — a Gymnasium environment with generated maps
Project description
Procedural Frozen Lake
A Gymnasium environment that extends Frozen Lake with procedurally generated maps.
Procedural-FrozenLake-v1 optionally supports:
- Random lake shapes — jagged shorelines and variable tree borders on a fixed canvas
- Tree tiles (
T) — impassable obstacles around the lake, with optional patches inside - Mirror ice tiles (
M) — sprinkle slippery tiles where you want them, instead of a global slippery switch - Warp sleigh tiles (
W) — paired tiles that teleport you between them - Multiple starts and goals — pin exact tiles or place them randomly, with a different reward per goal
- Fresh maps without rebuilding — regenerate the layout from
reset()in the same env instance - Supervision signals in
info— the map blueprint (emit_map) and optimal Q-values for the current state (emit_q_star) - Fog of war — unvisited tiles render as
?until explored (on by default; render-only) - Shuffled state / action IDs — relabel agent-facing observation and action numbers with
permute_obs/permute_actions
Every knob is detailed under Constructor parameters.
News
- 2026-07-08 — v1.0.0 is out! — Stable API: border-based lake generation (
width/height/min_border/max_border/shoreline_jaggedness),info["map"]as a dict,mirror_prob, and clearer docs. See CHANGELOG.md. - 2026-07-07 — v0.4.0 — Observation and action permutations, tile letter rename (
T/M/W), fog of war on by default, exact rewards inenv.P. See CHANGELOG.md.
See CHANGELOG.md for the full release history.
Install
pip install procedural-frozenlake
For GUI / rgb_array rendering you also need pygame (via Gymnasium's toy-text extra):
pip install "gymnasium[toy-text]"
Or install the examples extra: pip install "procedural-frozenlake[examples]".
For development:
git clone https://github.com/micahr234/procedural-frozenlake.git
cd procedural-frozenlake
source scripts/install.sh
Quick start
Importing the package registers the environment with Gymnasium:
import gymnasium as gym
import procedural_frozenlake # registers Procedural-FrozenLake-v1
env = gym.make(
"Procedural-FrozenLake-v1",
map_seed=0,
emit_map=True,
emit_q_star=True,
)
obs, info = env.reset(seed=1)
print(info["map"]["board"]) # list of row strings
print(info["q_star"]) # shape (4,) Q* for the current state
for _ in range(100):
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
if terminated or truncated:
obs, info = env.reset()
env.close()
See examples/rollout.ipynb for a tutorial notebook: multi-episode rollout, multiple starts and goals with per-goal rewards, fog-of-war, Q* labels, and an embedded replay video.
Environment
ID: Procedural-FrozenLake-v1
This env follows the conventions of Gymnasium's FrozenLake: the same four actions, flat state indices (row * width + col), and reward on reaching a goal. What differs:
- One map per env by default. The map is generated lazily on the first
reset()and reused across episodes. Passoptions={"regenerate_map": True}toreset()when you want a fresh layout. - Two independent random streams.
map_seedfixes the map — lake shape, tile placement, goal rewards, and any permutations.reset(seed=…)only affects episode randomness — which start tile you begin on, which way you slip on mirror ice. A reset seed never draws a new map.
Tile legend
| Icon | Tile | Name | Behavior |
|---|---|---|---|
S |
Start | Walkable; deterministic movement | |
F |
Frozen | Normal safe ice; deterministic movement | |
M |
Mirror ice | Slippery ice (stochastic sliding when standing on it) | |
W |
Warp sleigh | Warp to paired sleigh on entry; both tiles in a pair share the same numbered badge | |
H |
Hole | Terminal — fall through; reward 0 |
|
G |
Goal | Terminal — success; reward shown in badge, bow tinted yellow (low) to green (high) | |
T |
Tree | Impassable shoreline and optional interior patches |
Constructor parameters
Map generation
| Parameter | Default | Description |
|---|---|---|
map_seed |
None |
Seed for map generation (independent of reset seed) |
fixed_map |
None |
Fixed layout: list of row strings, or a blueprint dict (board / rewards / sleighs / permutations — same shape as info["map"]). Disables random generation and cannot be combined with start_pos/goal_pos options |
width, height |
8, 8 |
Fixed canvas dimensions; state indices run 0 .. width*height-1 |
min_border, max_border |
1, 2 |
Tree margin sampled uniformly on every side; playable lake ice fills the interior inset |
shoreline_jaggedness |
1 |
Max tiles of shoreline variation per edge (0 = smooth rectangle; higher = deeper bays and longer peninsulas into the border band) |
hole_prob |
0.2 |
Probability a tile becomes a hole H |
tree_prob |
0.0 |
Probability a frozen ice tile becomes an interior tree T after the lake is carved |
mirror_prob |
0.0 |
Probability a frozen (F) tile becomes mirror ice M |
sleigh_pair_count |
0 |
Number of sleigh warp pairs (2 × count W tiles); pairs linked in row-major order |
start_pos, start_pos_prob |
None, None |
Fixed start tile(s) as canonical flat index / list of indices, or per-tile Bernoulli probability of placing starts |
goal_pos, goal_pos_prob |
None, None |
Same for goals |
min_hops |
3 |
Minimum shortest-path length from start to goal (deterministic BFS; ignores mirror slip) |
max_tries |
10_000 |
Generation attempts before giving up with an error |
Dynamics and rewards
| Parameter | Default | Description |
|---|---|---|
slippery_success_rate |
1/3 |
Intended-direction success rate on mirror ice M |
goal_reward_low, goal_reward_high |
1.0, 1.0 |
Per-goal reward sampling bounds (low must be <= high) |
Supervision signals in info
| Parameter | Default | Description |
|---|---|---|
emit_map |
False |
Inject map layout dict in info["map"] on every reset() and step() |
emit_q_star |
False |
Inject optimal Q-values for the current state in info["q_star"] (shape (4,); zero at terminal states) |
q_star_gamma |
0.999 |
Discount for Q* value iteration over env.P |
Relabeling
| Parameter | Default | Description |
|---|---|---|
permute_obs |
False |
Relabel observations with a random permutation of canvas state indices |
permute_actions |
False |
Relabel the four actions with a random permutation |
Both permutations are sampled with the map (from map_seed) and resampled when the map regenerates. Only agent-facing IDs change — the board in info["map"] and the position kwargs stay in canonical grid coordinates.
permute_obs— the agent no longer sees the canonical flat index. If the agent stands at canonical index5andinfo["map"]["obs_permutation"][5]is17, thenreset/stepreturn observation17. Decode with the permutation, or keep using the canonical board ininfo["map"].permute_actions— the four movement IDs are shuffled the same way. Ifaction_permutationis[2, 0, 3, 1], external action0means Right,1means Left, and so on.info["q_star"]is already ordered by these external action IDs.
Rendering
| Parameter | Default | Description |
|---|---|---|
fog_of_war |
True |
Hide unvisited tiles as ? in render modes only (trees revealed when visited or bumped); set False to render the full map |
render_mode |
None |
Standard Gymnasium render mode: "ansi", "human", or "rgb_array" |
info["map"] schema
When emit_map=True, info["map"] is a blueprint dict you can pass back as fixed_map to rebuild the same layout:
| Key | Type | Description |
|---|---|---|
board |
list[str] |
Row strings of tile letters (size is implied by the board) |
rewards |
dict[int, float] |
Goal reward by canonical flat state index |
sleighs |
dict |
{"pairs": [[a, b], …]} canonical state indices (row-major pairing) |
obs_permutation |
list[int] |
Present when observations were relabeled |
action_permutation |
list[int] |
Present when actions were relabeled |
Example:
obs, info = env.reset()
clone = gym.make("Procedural-FrozenLake-v1", fixed_map=info["map"])
Reset options
Pass options={"regenerate_map": True} to reset() to generate a new map. Not available when fixed_map is set. Unknown option keys raise ValueError.
Contributing
See CONTRIBUTING.md.
License
GNU General Public License v3.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file procedural_frozenlake-1.0.0.tar.gz.
File metadata
- Download URL: procedural_frozenlake-1.0.0.tar.gz
- Upload date:
- Size: 52.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f1f7eddc663a28b58dd7c0772c90daa2297ab6c55ea970651ffe95aec19fe11
|
|
| MD5 |
cd7c6fa0258db34be5406c47500019c9
|
|
| BLAKE2b-256 |
5fd960bc94f431a78b6aa613dab54337b7f6233ece20dcbc4c3757ee676f2c9e
|
Provenance
The following attestation bundles were made for procedural_frozenlake-1.0.0.tar.gz:
Publisher:
publish.yml on micahr234/procedural-frozenlake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
procedural_frozenlake-1.0.0.tar.gz -
Subject digest:
4f1f7eddc663a28b58dd7c0772c90daa2297ab6c55ea970651ffe95aec19fe11 - Sigstore transparency entry: 2122448885
- Sigstore integration time:
-
Permalink:
micahr234/procedural-frozenlake@45f0ae9bc6a26fb40f3036762fc0cff97ea26912 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/micahr234
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@45f0ae9bc6a26fb40f3036762fc0cff97ea26912 -
Trigger Event:
push
-
Statement type:
File details
Details for the file procedural_frozenlake-1.0.0-py3-none-any.whl.
File metadata
- Download URL: procedural_frozenlake-1.0.0-py3-none-any.whl
- Upload date:
- Size: 44.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adf47ab99ce9db473a95ef284c8c6d2c957d93bba0fcecebeb7f7bb4aa9acc97
|
|
| MD5 |
858c8c4c6384e58fb4074784944fb28d
|
|
| BLAKE2b-256 |
c693001ee69a73515747fc7917d369b031538197440dd1a64e9276e7457a38e4
|
Provenance
The following attestation bundles were made for procedural_frozenlake-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on micahr234/procedural-frozenlake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
procedural_frozenlake-1.0.0-py3-none-any.whl -
Subject digest:
adf47ab99ce9db473a95ef284c8c6d2c957d93bba0fcecebeb7f7bb4aa9acc97 - Sigstore transparency entry: 2122449082
- Sigstore integration time:
-
Permalink:
micahr234/procedural-frozenlake@45f0ae9bc6a26fb40f3036762fc0cff97ea26912 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/micahr234
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@45f0ae9bc6a26fb40f3036762fc0cff97ea26912 -
Trigger Event:
push
-
Statement type: