Así funciona el provably fair en Keno
Así es como se juega al Keno en Cloudbet, y así puedes comprobar que cada sorteo es provably fair.
Cómo jugar al Keno
El Keno es un juego de sorteo de números: eliges tus números en el tablero y luego el juego sortea los suyos. Cuantos más de tus números coincidan con los sorteados, mayor será tu premio — cuánto mayor depende de cuántos números elegiste al principio. Una vez que hayas hecho tu selección y tu apuesta, el sorteo se realiza y tus aciertos se revelan al instante.
Cloudbet Originals Keno
El Keno de Cloudbet Originals usa un tablero de 40 números: eliges entre 1 y 10 números, y el juego sortea 10. Tiene un RTP del 99% y acepta apuestas desde $0.01. Cuantos menos números elijas, mayor será la volatilidad y mayores los premios por acierto; cuantos más elijas, ganarás con más frecuencia, pero con menor retorno por acierto. El Auto Bet te permite automatizar un número determinado de rondas.
El sorteo del Keno se genera de la misma forma verificable que cualquier otro Cloudbet Original — consulta nuestra visión general del provably fair para ver cómo el Server Seed, el Client Seed y el Nonce se combinan en un Round Signature y luego en una secuencia de números aleatorios mediante SHAKE256. Esta página cubre lo específico del Keno: cómo esa secuencia de números aleatorios se convierte en el conjunto real de números sorteados.
Cómo se convierten los números aleatorios en un sorteo de Keno
El tablero del Keno está numerado del 1 al 40. Diez de esos 40 números deben sortearse, uno a uno, sin repetir ninguno.

Llamamos P a la cantidad de números aún no sorteados que quedan. P empieza en 40 y baja en 1 cada vez que se sortea un número.

Para cada uno de los 10 sorteos se extrae un número aleatorio de 4 bytes (32 bits) de la salida de SHAKE256, con un rango de 0 a 4,294,967,295. Para elegir de forma justa entre los P números restantes, aplicamos rejection sampling (muestreo por rechazo; consulta nuestra guía sobre rejection sampling), y después calculamos el módulo P del resultado para obtener la posición del siguiente número sorteado. Si aún quedan números por sortear, el número elegido se elimina del conjunto, P baja en 1, y el proceso se repite.

Esto continúa hasta que se han sorteado los 10 números.


Mecánica del juego
Los 10 números sorteados se generan por completo y se ocultan en el servidor antes de que hagas tu selección — no se pueden cambiar ni alterar sin que se detecte después, gracias al Commitment que ya se ha compartido contigo. Tu selección se compara con los números ya sorteados una vez que se resuelve la ronda, y tu premio se calcula según tus aciertos.
Código para verificar tú mismo tus resultados de Keno en Cloudbet
Introduce tu Server Seed, tu Client Seed y tu Nonce para verificar tus propios resultados. Instala la única dependencia necesaria y ejecútala tú mismo para reconstruir el resultado de tu partida.
Nota: para revelar el Server Seed de una partida terminada en Cloudbet, primero debes haber iniciado una nueva ronda (lo que rota a un nuevo par de seeds).
Dependencias:
npm install js-sha3
El código:
// If you are running this yourself, then you can input your own data here
// You can then compare:
// 1) Cloudbet's provided Commitment to the Commitment outputted here. This proves that the Server Seed was not altered during gameplay.
// 2) Cloudbet's provided mine positions to the mine positions outputted here. This proves that your game's outcome was determined solely by the pre-game seeds and was not altered afterwards.
let myServerSeed = 'your_server_seed'; // ✍️YOUR_INPUT✍️: Enter your (unhashed) Server Seed here
let myClientSeed = 'your_client_seed'; // ✍️YOUR_INPUT✍️: Enter your Client Seed here
let myNonce = 1; // ✍️YOUR_INPUT✍️: Enter your Nonce here
const usingCustomData = false; // Change this to true if you are using your own data
// Load libraries
const crypto = require('crypto');
const { sha3_256, shake256 } = require('js-sha3');
// Step 1: Generate a Server Seed and Commitment
// And generate a Client Seed - either by player input or by the player's browser
function generateServerSeed() {
let seed = crypto.randomBytes(32).toString('hex');
if (usingCustomData) { seed = myServerSeed; }
const commitment = sha3_256(seed);
return { 'serverSeed': seed, 'commitment': commitment };
}
function getClientSeed() {
let seed = crypto.randomBytes(32).toString('hex');
if (usingCustomData) { seed = myClientSeed; }
return seed;
}
// Step 2: Create a Round Signature with SHA3 using a Server Seed, Client Seed and Nonce
function createRoundSignature(serverSeed, clientSeed, nonce) {
return sha3_256(`${serverSeed}:${clientSeed}:${nonce}`);
}
// Step 3: Create a SHAKE256 stream generator from a Round Signature.
// To simplify this example program, this function doesn't create a stream, but instead a sequence of custom length.
function* createRandomNumberSequence(numbersToGenerate, roundSignature) {
const numOfBytes = 4 * numbersToGenerate; // 4 bytes (32 bits) per random number we generate
const byteStream = shake256.create(8 * numOfBytes); // shake takes bits as input
byteStream.update(roundSignature); // Use the Round Signature to seed the generator
const buf = Buffer.from(byteStream.digest()); // The Buffer class lets us package every 4 bytes into one 32-bit integer
for (let i = 0; i < numbersToGenerate; i++) {
yield buf.readUInt32BE(i * 4); // Read buf in 4-byte steps
}
}
// Step 4: Pick a number of unique positions
function pickUniquePositions(numOfPicks, totalPositions, rng) {
const available = Array.from({ length: totalPositions }, (_, i) => i);
const picks = [];
for (let i = 0; i < numOfPicks; ++i) {
const index = sample(available.length, rng);
picks.push(available[index]);
available.splice(index, 1); // Remove chosen pump from list of non-faulty pumps
}
return picks;
}
// Rejection sampling for fair results
function sample(P, rng) {
const maxAcceptable = Math.floor(0x100000000 / P) * P;
// Rejection sampling
let rand;
do {
rand = rng.next().value;
} while (rand >= maxAcceptable);
// Modulo operation
const sampled_number = rand % P;
return sampled_number;
}
// Example usage
(function main() {
let nonce = 1;
if (usingCustomData) { nonce = myNonce; }
const numOfHits = 10;
const totalPositions = 40;
const { serverSeed, commitment } = generateServerSeed();
const clientSeed = getClientSeed();
const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
const numbersToGenerate = totalPositions * 2; // We need at least one random number per position, but there is an improbable possibility we'll need two
const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);
let hits = pickUniquePositions(numOfHits, totalPositions, rng);
hits = hits.map((value)=> value + 1 );
hits.sort((a, b)=>a-b);
console.log('📝 Commitment (shown before the game):', commitment);
console.log('🔒 Server Seed (revealed after the game):', serverSeed);
console.log('🎲 Client Seed:', clientSeed);
console.log('🔢 Nonce:', nonce);
console.log('🔁 Round Signature (can be verified after the game):', roundSignature);
console.log(`🎯 Hits:`, hits);
})();
