{
  "schemaVersion": 1,
  "slug": "fortune-cookie-generator-calculator",
  "category": "fun",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "c20302984acd6b5fbd1ce83b84f386e18904230c6e16a6ca18290fae4e2ffbff",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/fortune-cookie-generator-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": "a_k = (a_{k-1} + 0x6D2B79F5) mod 2^32 -> u_k = g(a_k), u_k ∈ [0,1); i = floor(u_1 x 10) mod 10; v_k = 1 + floor(u_k x M)",
    "vars": [
      [
        "s",
        "seed (integer)"
      ],
      [
        "M",
        "max lucky number"
      ],
      [
        "a_k",
        "PRNG state"
      ],
      [
        "u_k",
        "uniform value in [0,1)"
      ],
      [
        "i",
        "fortune index (0-9)"
      ],
      [
        "v_k",
        "drawn lucky number"
      ],
      [
        "g",
        "state-to-[0,1) mapping"
      ]
    ],
    "note": "Deterministic PRNG (mulberry32-style) seeded with floor(s). The fortune text and theme come from a 10-entry table at index i. Lucky numbers are drawn without replacement until 6 distinct values are collected (or 500 attempts), then sorted ascending; M >= 6 is needed for six numbers."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const fortunes = ['Good things come to those who wait, but better things come to those who act.', 'A small kindness you offer today will return to you threefold.', 'The obstacle in your path is the path.', 'You will soon receive news that changes a plan for the better.', 'Patience now buys speed later.', 'Someone is quietly grateful for what you did last week.', 'A journey you keep postponing is the one worth taking.', 'Your instinct about the quiet option is correct.', 'What you practise alone will be noticed in public.', 'An old friendship will prove more valuable than a new opportunity.']; const cats = ['Luck', 'Wisdom', 'Kindness', 'Patience', 'Courage', 'Friendship', 'Fortune', 'Instinct', 'Discipline', 'Loyalty']; const rng = (s) => { let a = Math.floor(s) >>> 0; return () => { a = (a + 0x6D2B79F5) >>> 0; let t = a; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0)/4294967296; }; }; const r = rng(seed); const fi = Math.floor(r()*fortunes.length) % fortunes.length; const nums = []; const seen = {}; let guard = 0; while (nums.length < 6 && guard < 500) { guard++; const v = 1 + Math.floor(r()*Math.max(1, Math.round(luckyMax))); if (seen[v]) continue; seen[v] = 1; nums.push(v); } return { fortune: fortunes[fi], luckyNumbers: nums.sort((a, b) => a - b).join(', '), numberOfLuckyNumbers: nums.length, category: cats[fi] };",
    "inputs": [
      {
        "name": "seed",
        "type": "number",
        "default": 77,
        "min": 0,
        "max": 1000000,
        "step": 1
      },
      {
        "name": "luckyMax",
        "type": "number",
        "default": 49,
        "min": 5,
        "max": 1000,
        "step": 1,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "fortune",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "luckyNumbers",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "numberOfLuckyNumbers",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "category",
        "precision": 0,
        "unit": "",
        "type": "text"
      }
    ],
    "interpret": null
  }
}
