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.
app.js is the only module that touches the browser. It queries every interactive DOM element on startup, attaches keyboard and button event listeners, and owns the race timer that fires the agent’s moves on a setInterval. Every frame it rebuilds the 8×8 grid by calling env.snapshot() and agent.snapshot(), applies CSS classes cell by cell, and syncs seven HUD labels — all without any framework, virtual DOM, or reactive system. The module is loaded as a native ES module so the browser resolves its imports from treasure-maze.js and q-agent.js automatically.
Module Imports
DOM Elements
On startup,app.js queries all interactive and display elements with document.querySelector. All IDs are defined in index.html.
| Variable | Selector | Purpose |
|---|---|---|
board | #maze-board | Container for the 64 cell <div> elements. |
statusPill | #game-status | Status badge (Ready, Race Running, You Found the X, etc.). |
rewardLabel | #reward-label | Displays env.totalReward to two decimal places. |
playerStepsLabel | #player-steps-label | Displays the player’s step count. |
agentStepsLabel | #agent-steps-label | Displays the agent’s step count. |
difficultyLabel | #difficulty-label | Displays the active difficulty label from DIFFICULTY_SETTINGS. |
pathLengthLabel | #path-length-label | Displays BFS shortest-route length (moves, not cells). |
difficultySelect | #difficulty-select | <select> for Easy / Medium / Hard. |
difficultyDescription | #difficulty-description | Narrative text from DIFFICULTY_SETTINGS[difficulty].description. |
startButton | #start-race | Starts the race (disabled while race is running). |
newMazeButton | #new-maze | Generates a fresh randomized maze. |
resetButton | #reset-race | Resets env and agent on the same maze. |
trainingLog | #training-log | <ol> list; newest entries are prepended and the list is capped at 9 items. |
playerLeadLabel | #player-lead-label | Shows relative step lead ("Even", "Player +2", "Agent +3"). |
agentMoveLabel | #agent-move-label | Shows agent.lastMove (e.g. "exploited right", "hesitated"). |
Input Handling
Keyboard (keydown)
The keydown listener on document maps both arrow keys and WASD to the four action integers. Single-character keys are lowercased before lookup to normalise A/a.
event.preventDefault() stops the browser from scrolling when the arrow keys are used.
Button Controls ([data-action])
All four directional buttons in the HTML carry a data-action attribute ("up", "down", "left", "right"). A single delegated listener maps them through actionMap:
Difficulty Select (#difficulty-select)
Changing the select triggers a non-logged race reset followed by a difficulty reconfiguration:
configureDifficulty() reads the new value, updates the description text, and calls agent.configure(...) so the agent immediately adopts the new speed and behavior settings.
Key Functions
render()
Rebuilds the entire #maze-board grid on every call. Clears board.innerHTML, calls env.snapshot({ agent: agent.snapshot() }) to get a single consistent state object, then iterates all 64 cells and applies CSS classes based on what occupies each cell.
CSS Class Mapping
| CSS Class | Applied When |
|---|---|
wall | cellValue === 0 (blocked cell) |
visited | snapshot.visited.has(cellKey) (player has been here) |
agent-trail | snapshot.agent.trail.has(cellKey) (agent has been here) |
treasure | cellKey === targetKey |
player | cellKey === playerKey |
agent | cellKey === agentKey |
both | cellKey === playerKey && cellKey === agentKey (player and agent on same cell) |
<span class="cell-icon"> whose textContent is set to "X" for the treasure, "🤖" for the agent, "●" for the player, or "●🤖" when both share a cell.
updateHud()
Synchronises all seven HUD labels in one call. Called at the end of every render().
startRace()
Starts the race by scheduling the agent’s first move and then setting up the interval timer. If the race was previously over, resetRace(false) is called first.
Race Timer Flow
startDelay and moveDelay come directly from agent.settings, so selecting a harder difficulty makes the first move arrive sooner and subsequent moves arrive faster.
movePlayer(action)
Called by every keyboard and button input handler. Only fires if the race is active and not over.
playerSteps but do apply the -0.75 reward penalty to totalReward inside env.act().
moveAgentOnce()
Called by the race timer on every setInterval tick.
checkWinner()
Checks both win conditions after every player move and every agent tick. Calls endRace() with the appropriate status if either side has won.
endRace(status)
Clears the agent timer, marks the race as inactive, updates the status pill, re-enables the start button, and appends a result line to the game log.
newMaze()
Generates a fresh randomized maze, recreates both env and agent, and resets all counters. Passes a tighter density on Hard to produce corridors with fewer open cells.
resetRace(shouldLog?)
Recreates env and agent on the same mazeData so the board layout is preserved. Used by the Reset button and as a pre-step before startRace() when the previous race is over.
The difficulty
<select> change handler calls resetRace(false) to suppress the “Race reset” log line, then immediately calls configureDifficulty() to apply the new settings and print its own log message instead.