{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "game": {
    "code": "ketapola-dice",
    "displayName": "Ketapola Dice",
    "category": "dice",
    "numberOfOutcomes": 6,
    "roundModel": "single-die-two-side",
    "provablyFair": true,
    "rngAlgorithm": "HMAC-SHA256(serverSeed, clientSeed + \":\" + nonce)",
    "rngPrimitivesSource": "packages/rng-core/src/index.ts",
    "gameOutcomeSource": "games/ketapola-dice/src/outcome.ts",
    "testVectorsSource": "games/ketapola-dice/fixtures/rng-test-vectors.json",
    "testVectorsHumanReadable": "games/ketapola-dice/docs/rng-test-vectors.md"
  },
  "faces": {
    "LOW": [3, 6, 9],
    "HIGH": [12, 15, 18]
  },
  "defaultConfig": {
    "lowWeight": 48,
    "highWeight": 48,
    "payoutMultiplier": 2.0,
    "commissionMicroPerUnit": 3000,
    "commissionFraction": 0.03,
    "bettingWindowMs": 15000,
    "rollingWindowMs": 4000,
    "cooldownMs": 3000
  },
  "probabilities": {
    "atDefaultConfig": {
      "P_LOW": 0.5,
      "P_HIGH": 0.5,
      "P_face_given_side": 0.3333333333333333,
      "perFace": {
        "3": 0.16666666666666666,
        "6": 0.16666666666666666,
        "9": 0.16666666666666666,
        "12": 0.16666666666666666,
        "15": 0.16666666666666666,
        "18": 0.16666666666666666
      },
      "modularBias": {
        "source": "faceValue % 3 over uniform 32-bit",
        "magnitude": 2.3283064365386963e-10,
        "threshold": 1e-8,
        "passes": true
      }
    }
  },
  "payouts": {
    "formula": "gross = 2.0 * stake; net = gross * (1 - commissionFraction)",
    "atDefaultConfig": {
      "grossOnWin": "2.00 * stake",
      "commissionOnWin": "0.03 * (2.00 * stake) = 0.06 * stake",
      "netOnWin": "1.94 * stake",
      "netPlayerGainOnWin": "0.94 * stake",
      "netPlayerLossOnLoss": "-1.00 * stake"
    },
    "freeBet": {
      "stakeDebited": false,
      "onWin": "credit net payout; player gain = +netPayout",
      "onLoss": "no debit; player gain = 0"
    }
  },
  "rtp": {
    "formula": "RTP = P(win) * payoutMultiplier * (1 - commissionFraction)",
    "atDefaultConfig": {
      "theoretical": 0.97,
      "theoreticalPct": "97.00%",
      "houseEdge": 0.03,
      "houseEdgePct": "3.00%"
    },
    "regressionGate": {
      "source": "tests/games/ketapola-dice/rtp-regression.spec.ts",
      "rounds": 10000000,
      "toleranceAbs": 0.005,
      "expectedStandardError": 0.00032
    }
  },
  "volatility": {
    "hitFrequency": 0.5,
    "hitFrequencyPct": "50.00%",
    "perUnitStakeReturn": {
      "grossMean": 1.0,
      "grossVariance": 1.0,
      "grossStdDev": 1.0
    },
    "netAtDefaultConfig": {
      "expectedReturn": 0.97,
      "standardDeviation": 0.97,
      "coefficientOfVariation": 1.031
    },
    "classification": "medium"
  },
  "maxExposurePerBet": {
    "formula": "2 * maxBetMicro (gross) | (2 - commissionFraction) * maxBetMicro (net)",
    "notes": "Operators cap this via maxBetMicro. Per-player per-round concurrency is operator-enforced."
  },
  "changeManagement": {
    "regatedFiles": [
      "packages/rng-core/src/index.ts",
      "games/ketapola-dice/src/outcome.ts",
      "games/ketapola-dice/src/settle.ts",
      "games/ketapola-dice/src/config.ts",
      "games/ketapola-dice/docs/par-sheet.md",
      "games/ketapola-dice/docs/par-sheet.json",
      "games/ketapola-dice/docs/rng-test-vectors.md",
      "games/ketapola-dice/fixtures/rng-test-vectors.json"
    ],
    "regatedConfigFields": [
      "OperatorGameConfig.lowWeight",
      "OperatorGameConfig.highWeight",
      "OperatorGameConfig.commissionMicro",
      "OperatorGameConfig.minBetMicro",
      "OperatorGameConfig.maxBetMicro"
    ],
    "ciGate": "rng-change-gate requires per-scope attestation in PR body: CERT-ATTEST-CORE for packages/rng-core/src/, CERT-ATTEST-KETAPOLA_DICE for games/ketapola-dice/src/{outcome,settle,config}.ts",
    "unregatedFields": [
      "OperatorGameConfig.bettingWindowMs",
      "OperatorGameConfig.rollingWindowMs",
      "OperatorGameConfig.cooldownMs"
    ]
  },
  "specVersion": "1.0.0",
  "lastUpdated": "2026-04-23"
}
