Skip to main content

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.

The CompetitiveAgent is not a fixed opponent — its behavior is governed by a DIFFICULTY_SETTINGS configuration object that controls four independent dials: how long it waits between moves, how long it waits before making its first move at all, how often it abandons the optimal path to explore, and how often it simply skips a turn. Tuning all four values together produces three distinct characters: a forgiving, slow-starting agent on Easy; a balanced but occasionally unpredictable opponent on Medium; and a relentless, near-perfect runner on Hard.

The DIFFICULTY_SETTINGS Object

export const DIFFICULTY_SETTINGS = {
  easy: {
    label: "Easy",
    moveDelay: 1150,
    startDelay: 1600,
    mistakeRate: 0.42,
    hesitateRate: 0.2,
    description: "The agent explores more, hesitates sometimes, and gives the player a head start."
  },
  medium: {
    label: "Medium",
    moveDelay: 780,
    startDelay: 900,
    mistakeRate: 0.18,
    hesitateRate: 0.08,
    description: "The agent usually follows the learned route but still makes occasional exploration choices."
  },
  hard: {
    label: "Hard",
    moveDelay: 470,
    startDelay: 350,
    mistakeRate: 0,
    hesitateRate: 0,
    description: "The agent exploits the optimal path aggressively and rarely gives the player breathing room."
  }
};

What Each Property Controls

moveDelay (milliseconds) — the interval between consecutive agent moves after the race has started. A lower value means the agent ticks faster. Easy gives the player a comfortable 1.15 seconds between enemy steps; Hard fires every 470 ms. startDelay (milliseconds) — how long the agent waits after the race begins before taking its very first move. This is the window where the player can build an early lead. Easy’s 1.6-second head start is generous; Hard’s 350 ms is barely a blink. mistakeRate (0–1 probability) — the chance that the agent will call chooseExplorationMove() instead of choosePolicyMove() on any given turn. An exploration move picks a random unvisited valid neighbor rather than following the BFS shortest path. At 0.42, Easy will explore on nearly half of all turns; Hard never explores at all. hesitateRate (0–1 probability) — the chance that the agent skips a move entirely for that tick, logging "hesitated" without advancing. Easy hesitates 20% of the time; Hard never hesitates.

Difficulty Comparison Table

PropertyEasyMediumHard
moveDelay (ms)1150780470
startDelay (ms)1600900350
mistakeRate0.420.180
hesitateRate0.200.080

How move() Uses These Values

Each agent tick calls move(), which applies the difficulty settings in a strict decision order:
move() {
  // 1. Hesitation check — skip the turn entirely
  if (Math.random() < this.settings.hesitateRate) {
    this.lastMove = "hesitated";
    return this.snapshot();
  }

  // 2. Mistake check — explore instead of following policy
  const useExploration = Math.random() < this.settings.mistakeRate;
  const move = useExploration ? this.chooseExplorationMove() : this.choosePolicyMove();

  // 3. Safety fallback — never get stuck with a null move
  const safeMove = move ?? this.choosePolicyMove() ?? this.chooseExplorationMove();

  if (!safeMove) {
    this.lastMove = "blocked";
    return this.snapshot();
  }

  this.position = [safeMove.row, safeMove.col];
  this.trail.add(key(safeMove.row, safeMove.col));
  this.steps += 1;
  this.lastMove = `${useExploration ? "explored" : "exploited"} ${ACTION_NAMES[safeMove.action]}`;
  return this.snapshot();
}
1

Hesitation roll

Math.random() < hesitateRate — if true, the agent logs "hesitated" and returns without moving. The race timer continues ticking.
2

Exploration vs. policy roll

Math.random() < mistakeRate — if true, chooseExplorationMove() picks a random unvisited valid neighbor. Otherwise choosePolicyMove() runs a fresh BFS from the agent’s current position and returns the next cell on the shortest path.
3

Safety fallback

If the chosen move resolves to null (e.g., exploration found no unvisited neighbors), the engine falls back to choosePolicyMove(), then to chooseExplorationMove(). If still null, the agent is blocked and logs "blocked".
4

Position update

The agent’s position is updated, the new cell is added to trail, steps is incremented, and lastMove is set for HUD display.

Mode Descriptions

ModeDescription
EasyThe agent explores more, hesitates sometimes, and gives the player a head start.
MediumThe agent usually follows the learned route but still makes occasional exploration choices.
HardThe agent exploits the optimal path aggressively and rarely gives the player breathing room.
Choosing your difficulty:
  • Pick Easy if you are learning the maze layout for the first time or testing a new randomized board — the long start delay and frequent exploration give you real room to experiment.
  • Pick Medium for a balanced race where the agent feels competitive but not unbeatable; occasional exploration moves keep the outcome uncertain even if you know a good route.
  • Pick Hard only when you are confident you can navigate efficiently — the agent’s 470 ms tick and zero-mistake policy mean you must find a near-optimal path from the very first move to have any chance of winning.

Build docs developers (and LLMs) love