Équité Vérifiable
Entrez les détails du jeu pour vérifier vos résultats
Code
Choisissez un jeu ci-dessus pour inspecter le code source qui produit ses résultats.
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,
})
)
