{
  "schemaVersion": 1,
  "slug": "escape-room-timer-calculator",
  "category": "fun",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "4cca650df7616eae5c7de64e22bef9349e687da84edde37af854a9123be6bbae",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/escape-room-timer-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "t = N_p × m ÷ (1 + 0.05 × max(0, n − 2)) + n_h × p",
    "vars": [
      [
        "N_p",
        "Number of puzzles"
      ],
      [
        "m",
        "Minutes per puzzle"
      ],
      [
        "n",
        "Number of players"
      ],
      [
        "n_h",
        "Hints used"
      ],
      [
        "p",
        "Penalty per hint (min)"
      ],
      [
        "t",
        "Final time (min)"
      ]
    ],
    "note": "The 5% teamwork reduction per player above two and the hint penalty are scenario assumptions, not measured escape-room standards. The per-player figure divides base puzzle time by player count and excludes hint penalties."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const teamwork = 1 + Math.max(0, players - 2)*0.05; const base = puzzles*minutesPerPuzzle/teamwork; const pen = hintsUsed*hintPenalty; const total = base + pen; return { baseTime: base, hintPenaltyTotal: pen, finalTime: total, timeRemaining: timeLimit - total, perPlayerShare: players > 0 ? base/players : 0, result: total <= timeLimit ? 'Escaped with time to spare' : total <= timeLimit*1.15 ? 'Just missed it' : 'Would not escape in time' };",
    "inputs": [
      {
        "name": "puzzles",
        "type": "number",
        "default": 6,
        "min": 1,
        "max": 50,
        "step": 1,
        "slider": true
      },
      {
        "name": "minutesPerPuzzle",
        "type": "number",
        "default": 8,
        "min": 0.5,
        "max": 60,
        "step": 0.5,
        "slider": true
      },
      {
        "name": "players",
        "type": "number",
        "default": 4,
        "min": 1,
        "max": 12,
        "step": 1,
        "slider": true
      },
      {
        "name": "hintsUsed",
        "type": "number",
        "default": 2,
        "min": 0,
        "max": 50,
        "step": 1,
        "slider": true
      },
      {
        "name": "hintPenalty",
        "type": "number",
        "default": 3,
        "min": 0,
        "max": 30,
        "step": 0.5,
        "slider": true
      },
      {
        "name": "timeLimit",
        "type": "number",
        "default": 60,
        "min": 5,
        "max": 240,
        "step": 5,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "baseTime",
        "precision": 1,
        "unit": "minutes"
      },
      {
        "name": "hintPenaltyTotal",
        "precision": 1,
        "unit": "minutes"
      },
      {
        "name": "finalTime",
        "precision": 1,
        "unit": "minutes"
      },
      {
        "name": "timeRemaining",
        "precision": 1,
        "unit": "minutes"
      },
      {
        "name": "perPlayerShare",
        "precision": 1,
        "unit": "minutes"
      },
      {
        "name": "result",
        "precision": 0,
        "unit": "",
        "type": "text"
      }
    ],
    "interpret": null
  }
}
