Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/treasure-hunt-game/llms.txt
Use this file to discover all available pages before exploring further.
treasure-maze.js is the foundation of the entire game. It owns the rules: what counts as an open cell, how movement deltas are defined, what rewards different actions earn, and how to find the shortest route through any maze. Every other module imports from it — nothing in this file imports from anywhere else. This makes it the ideal place to understand the game’s core data model before reading the agent or UI layers.
Constants
Action Integers
The four movement directions are plain integers so they can be used as object keys, stored in arrays, and compared with=== without any string overhead.
ACTION_NAMES
Maps each action integer to its human-readable string name. Used in log messages and the agent’s lastMove label.
ACTION_DELTAS
Maps each action integer to a [dr, dc] row/column delta. Applying a delta to a current position gives the destination cell.
ORIGINAL_MAZE
The 8×8 fallback maze returned by createRandomMaze if all 70 random-generation attempts produce a solution shorter than 11 moves (fewer than 12 path cells). 1 = open cell, 0 = wall.
[0, 0] (top-left) and the treasure is at [7, 7] (bottom-right).
Utility Functions
cloneMaze(maze)
Returns a deep copy of a maze array. Each row is spread into a new array so mutations to the clone never affect the original.
number[][]
key(row, col)
Serializes a grid coordinate to a string key suitable for use in a Map or Set.
string — format "row,col", e.g. key(3, 5) → "3,5"
sameCell(a, b)
Returns true if two [row, col] pairs refer to the same grid cell.
boolean
shuffle(values)
Returns a new array with the same elements in a randomized order using the Fisher-Yates algorithm. The original array is not mutated.
T[]
isInside(row, col, rows = 8, cols = 8)
Checks whether a coordinate falls within the grid bounds. Defaults to 8×8 but accepts any dimensions.
boolean
openNeighborCount(maze, row, col)
Counts how many of a cell’s four orthogonal neighbors are passable (value 1 and inside the grid). Used by the maze generator to enforce corridor integrity.
number (0–4)
findShortestPath(maze, start, target)
Finds the shortest route between two cells using breadth-first search. The function is used by both createRandomMaze (to validate solution length) and CompetitiveAgent (to compute BFS policy moves every turn).
The 8×8 grid.
1 = open cell, 0 = wall.The
[row, col] starting cell.The
[row, col] destination cell. Defaults to the bottom-right corner.{ path: [row, col][], actions: number[] }
path— ordered list of[row, col]cells fromstarttotarget(inclusive). Empty array if no path exists.actions— ordered list of action integers (LEFT / UP / RIGHT / DOWN) to follow the path. Length ispath.length - 1.
start and target, both arrays are empty: { path: [], actions: [] }.
Algorithm: Standard BFS. The function expands cells level by level, tracks predecessors in a Map, and reconstructs the path by walking backwards from target once it is dequeued.
createRandomMaze({ rows, cols, density })
Procedurally generates a solvable 8×8 maze. The generator carves a main path first (guaranteeing connectivity), then adds dead-end branches to reach the target cell density.
Number of rows in the output grid.
Number of columns in the output grid.
Target proportion of open cells (0–1). Higher values produce more open mazes with more branching dead ends.
app.js passes 0.56 for easy/medium and 0.5 for hard.{ maze: number[][], solution: [row, col][] }
maze— the generated 8×8 grid.solution— the BFS shortest path from[0, 0]to[rows-1, cols-1].
ORIGINAL_MAZE.
TreasureMaze Class
TreasureMaze is the game environment. It tracks player position, visited cells, cumulative reward, and game status. The act() method is the main interaction point — the player (or agent) passes an action integer and receives a result object describing what happened.
Constructor
The 8×8 grid to use. Cloned on construction so the original is never mutated.
Starting position. Must sit on an open cell (
1), otherwise throws.The treasure cell. Must be open, otherwise throws.
Properties
| Property | Type | Description |
|---|---|---|
baseMaze | number[][] | Cloned copy of the original maze. Never changes after construction. |
rows | number | Row count (always 8 for default mazes). |
cols | number | Column count (always 8 for default mazes). |
target | [number, number] | The treasure cell coordinates. |
freeCells | [number, number][] | All open cells that are not the target. |
player | [number, number] | Current player position (mirrored from state). |
state | { row, col, mode } | Full current state. mode is "start", "valid", "invalid", or "blocked". |
visited | Set<string> | Keys of cells the player has already occupied. |
totalReward | number | Running sum of all rewards received this episode. |
minReward | number | Lose threshold: -0.5 * rows * cols (= -32 on 8×8). |
Methods
key(row, col)
Serializes a grid coordinate to a "row,col" string key. Delegates directly to the module-level key() utility, so instance methods and standalone utilities produce identical output.
Row index (0-based).
Column index (0-based).
string — e.g. env.key(3, 5) → "3,5"
isTarget(row, col)
Returns true if the given cell is the treasure cell.
Row index (0-based).
Column index (0-based).
boolean
isInside(row, col)
Bounds-checks a coordinate against the environment’s own rows and cols dimensions. Unlike the module-level isInside(), this instance method uses this.rows and this.cols — no extra arguments required.
Row index to check.
Column index to check.
boolean
canStandOn(row, col)
Returns true if the cell is both within bounds (isInside) and open (baseMaze[row][col] === 1). Used internally by validActions(), updateState(), and the constructor validation.
Row index to check.
Column index to check.
boolean
reset(player?)
Resets the environment to a fresh episode. Clears visited cells, sets totalReward to 0, and repositions the player.
Starting position for the new episode.
number[] — the initial observation from observe().
validActions(cell?)
Returns the list of action integers that would move to an open cell from the given position.
The cell to check from. Defaults to the current
state position.number[] — subset of [LEFT, UP, RIGHT, DOWN].
updateState(action)
Applies an action to the internal state. Marks the current cell as visited, then moves the player if the action is valid or sets mode to "invalid" or "blocked".
Returns: void
getReward()
Reads the current state and returns the scalar reward for the most recent move.
| Condition | Reward |
|---|---|
| Player is on target | 1.0 |
No valid moves ("blocked") | minReward - 1 (≤ -33) |
| Cell already visited | -0.25 |
| Invalid move (hit wall) | -0.75 |
| Valid move to new cell | -0.04 |
number
act(action)
The primary interaction method. Calls updateState, computes the reward, accumulates it into totalReward, and returns a full result object.
One of
LEFT (0), UP (1), RIGHT (2), DOWN (3).gameStatus()
Determines whether the game is still running.
Returns: "win" | "lose" | "not_over"
"win"— player is on the target cell."lose"—totalReward < minReward(player wasted too many moves)."not_over"— game is still in progress.
observe()
Produces a 64-element flat array snapshot of the current board. Open cells are 1, walls are 0, and the player’s current cell is 0.5.
Returns: number[] — length 64 (8 rows × 8 cols).
snapshot(extra?)
Returns a plain object capturing the full environment state. Used by app.js during render() to pass both environment and agent state to the DOM renderer in one call.
Additional properties merged into the snapshot object.
app.js passes { agent: agent.snapshot() }.