Demostrablemente justo

Ingrese los detalles del juego para verificar sus resultados

Código

Elige un juego de arriba para inspeccionar el código fuente que produce sus resultados.

import { createHmac } from 'crypto';
import Decimal from 'decimal.js';

const UINT32_MAX = 0x100000000; // 2^32

/**
 * Generates an infinite deterministic stream of cryptographically secure
 * 32-bit unsigned integers derived from HMAC-SHA256.
 *
 * @param key - Secret cryptographic key (server seed). Must remain private until revealed.
 * @param message - Public input string (e.g., client seed, nonce, or domain label).
 *
 * @remarks
 * - Uses HMAC-SHA256 as a pseudorandom function (PRF) to produce a reproducible
 *   and verifiable sequence of numbers.
 * - `step` ensures each 32-bit block is derived from a unique input.
 * - Output is split into 32-bit chunks, suitable for unbiased integer or bit-level
 *   extraction.
 * - Bits are consumed starting from the Most Significant Bit (MSB) to preserve
 *   full numeric precision and maximum entropy.
 * - Fully deterministic: anyone with the same key and message can verify the results.
 */
function* getUint32Stream(key: string, message: string): Iterator<number> {
  let step = 0;

  while (true) {
    const hmac = createHmac('sha256', key);
    hmac.update(`${message}:${step}`);
    const hash = hmac.digest(); // 32 bytes (256 bits)

    for (let i = 0; i < hash.length; i += 4) {
      yield (
        (hash[i] << 24) |
        (hash[i + 1] << 16) |
        (hash[i + 2] << 8) |
        hash[i + 3]
      ) >>> 0; // force unsigned 32-bit
    }

    step++;
  }
}

const survivalMultiplier = (options: {
  total: number;
  safe: number;
  rounds: number;
  houseEdge: number;
}) => {
  const combination = (n: number, r: number) => {
    const k = Math.min(r, n - r);
    let result = new Decimal(1);

    for (let i = 1; i <= k; i++) {
      result = result.mul(n - k + i).div(i);
    }

    return result;
  };

  const { total, safe, rounds, houseEdge } = options;

  const probability = combination(safe, rounds).div(combination(total, rounds));

  return new Decimal(1)
    .minus(houseEdge)
    .div(probability)
    .toDecimalPlaces(2, Decimal.ROUND_HALF_UP)
    .toString();
};


function determineResult(options: {
  difficulty: "easy" | "medium" | "hard" | "expert";
  round: number;
  serverSeed: string;
  clientSeed: string;
  nonce: number;
}) {
  const chickenCrossConfig = {
    easy: {
      get maxRounds() {
        return 24;
      },
      difficultyFactor: 1,
    },
    medium: {
      get maxRounds() {
        return 22;
      },
      difficultyFactor: 3,
    },
    hard: {
      get maxRounds() {
        return 20;
      },
      difficultyFactor: 5,
    },
    expert: {
      get maxRounds() {
        return 15;
      },
      difficultyFactor: 10,
    },
  };

  const { difficultyFactor, maxRounds } = chickenCrossConfig[options.difficulty];

  const stream = getUint32Stream(
    options.serverSeed,
    `${options.clientSeed}:${options.nonce}:chicken-cross`
  );

  const value = stream.next().value;

  if (value === undefined) {
    throw new Error('Failed to generate random value');
  }

  const fairValue = new Decimal(value).plus(1);

  const ev = 4;
  const rtp = new Decimal(100).minus(ev).div(100);

  const hiddenMultiplier = new Decimal(UINT32_MAX).div(fairValue).mul(rtp);

  const roundMultiplier = survivalMultiplier({
    total: difficultyFactor + maxRounds,
    safe: maxRounds,
    rounds: options.round,
    houseEdge: new Decimal(ev).div(100).toNumber(),
  });

  const isWin = hiddenMultiplier.greaterThanOrEqualTo(roundMultiplier);

  return {
    multiplier: isWin ? Number(roundMultiplier) : 0,
    winPercentage: new Decimal(100)
      .minus(ev)
      .div(roundMultiplier)
      .toDecimalPlaces(4, Decimal.ROUND_FLOOR)
      .toString(),
  };
}

console.log(
  determineResult({
    difficulty: 'easy',
    clientSeed: 'chicken-cross-test',
    serverSeed: 'chicken-cross-test',
    round: 1,
    nonce: 0,
  })
)