{
  "schemaVersion": 1,
  "slug": "dnd-encounter-calculator",
  "category": "gaming",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "88da82737fc0d7b24c0264325dd7e062377a5e42d9d90736c0e47745ad4bd8b0",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/dnd-encounter-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": "B = T(L, tier) × n; X = XP × c; classify X against Low, Moderate and High thresholds",
    "vars": [
      [
        "L",
        "party level"
      ],
      [
        "n",
        "party size"
      ],
      [
        "XP",
        "XP per monster"
      ],
      [
        "c",
        "number of monsters"
      ],
      [
        "B",
        "party XP budget"
      ],
      [
        "T",
        "2024 XP threshold by level and tier"
      ]
    ],
    "note": "2024 budgets: B = T(level, tier)×party size; total monster XP = XP per monster×count, without a count multiplier. Compare with Low, Moderate and High thresholds."
  },
  "externalEvidence": {
    "reviewedAt": "2026-09-25",
    "sources": [
      {
        "name": "D&D Beyond — Basic Rules (2024), Building Encounters",
        "url": "https://www.dndbeyond.com/sources/dnd/br-2024/dms-toolbox"
      }
    ]
  },
  "contract": {
    "compute": "const TH=[[50,75,100],[100,150,200],[150,225,400],[250,375,500],[500,750,1100],[600,1000,1400],[750,1300,1700],[1000,1700,2100],[1300,2000,2600],[1600,2300,3100],[1900,2900,4100],[2200,3700,4700],[2600,4200,5400],[2900,4900,6200],[3300,5400,7800],[3800,6100,9800],[4500,7200,11700],[5000,8700,14200],[5500,10700,17200],[6400,13200,22000]]; const lv=Math.max(1,Math.min(20,Math.round(partyLevel))),count=Math.max(1,Math.round(partySize));const col=difficulty==='low'?0:difficulty==='high'?2:1;const per=TH[lv-1][col],budget=per*count,raw=monsterXP*monsterCount,low=TH[lv-1][0]*count,moderate=TH[lv-1][1]*count,high=TH[lv-1][2]*count;const result=raw<low?'Below low budget':raw<moderate?'Low':raw<high?'Moderate':'High';return {xpBudget:budget,adjustedXP:raw,difficultyResult:result,xpPerCharacter:Math.round(raw/count)};",
    "inputs": [
      {
        "name": "partySize",
        "type": "number",
        "default": 4,
        "min": 1,
        "max": 12,
        "step": 1,
        "slider": true
      },
      {
        "name": "partyLevel",
        "type": "number",
        "default": 5,
        "min": 1,
        "max": 20,
        "step": 1,
        "slider": true
      },
      {
        "name": "difficulty",
        "type": "select",
        "default": "moderate",
        "options": [
          {
            "value": "low"
          },
          {
            "value": "moderate"
          },
          {
            "value": "high"
          }
        ]
      },
      {
        "name": "monsterXP",
        "type": "number",
        "default": 200,
        "min": 10,
        "max": 100000,
        "step": 10
      },
      {
        "name": "monsterCount",
        "type": "number",
        "default": 4,
        "min": 1,
        "max": 100,
        "step": 1,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "xpBudget",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "adjustedXP",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "difficultyResult",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "xpPerCharacter",
        "precision": 0,
        "unit": ""
      }
    ],
    "interpret": null
  }
}
