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';
const UINT32_MAX = 0x100000000; // 2^32
/**
* Generates an infinite deterministic stream of cryptographically secure
* 32-bit unsigned integers derived from HMAC-SHA512.
*
* @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-SHA512 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.
* 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('sha512', key);
hmac.update(`${message}:${step}`);
const hash = hmac.digest(); // 64 bytes (512 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++;
}
}
/**
* Converts a 32-bit random number into an unbiased integer within [0, range).
*
* @param uint32StreamFactory - Function that returns a stream of 32-bit integers.
* @param range - The desired upper bound (exclusive) for the output.
*
* @remarks
* - Uses rejection sampling to avoid modulo bias.
* - Every integer within the range is equally likely.
* - Critical for fair gameplay and provably fair verification.
*/
function getUnbiasedIntFromStream(
stream: Iterator<number>,
range: number
) {
if (!Number.isInteger(range) || range <= 0) {
throw new Error('Invalid range');
}
const maxAcceptable = UINT32_MAX - (UINT32_MAX % range);
let next = stream.next();
while (!next.done) {
const value = next.value;
if (value < maxAcceptable) {
return value % range;
}
next = stream.next();
}
throw new Error('Failed to generate unbiased value: entropy stream exhausted');
}
export function createLazyDrawWithoutReplacement<T>(
uint32StreamFactory: () => Iterator<number>,
data: readonly T[]
) {
const records = [...data];
const stream = uint32StreamFactory();
let drawnCount = 0;
function drawOne(): T {
const remaining = records.length - drawnCount;
if (remaining <= 0) {
throw new Error('No items remaining');
}
const swapIndex = getUnbiasedIntFromStream(stream, remaining);
const targetIndex = remaining - 1;
[records[targetIndex], records[swapIndex]] = [records[swapIndex]!, records[targetIndex]!];
drawnCount++;
return records[targetIndex]!;
}
return {
drawMany(count: number) {
if (!Number.isInteger(count) || count < 0) {
throw new Error('Invalid count');
}
if (count > records.length - drawnCount) {
throw new Error('Not enough items remaining');
}
return Array.from({ length: count }, () => drawOne());
}
};
}
export const CardRank = [
'A',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'T',
'J',
'Q',
'K',
] as const;
export const CardSuit = ['c', 'd', 'h', 's'] as const;
function getShoe(
options: {
nonce: number,
clientSeed: string,
serverSeed: string
}) {
const baseDeck = CardSuit.flatMap(suit =>
CardRank.map(rank => [rank, suit])
)
const deckCount = 8;
const decks = Array.from({ length: deckCount }, () => [...baseDeck]).flat();
const uint32Generator = getUint32Stream(options.serverSeed, `${options.clientSeed}:${options.nonce}:blackjack`);
const shoeDrawer = createLazyDrawWithoutReplacement(() => uint32Generator, decks);
return shoeDrawer.drawMany(16);
}
console.log(
getShoe({
clientSeed: 'blackjack-tests',
serverSeed: 'blackjack-tests',
nonce: 2,
})
)
