Cómo funciona provably fair en Roulette

Así se juega a Roulette en Cloudbet, y así puedes comprobar que cada tirada es provably fair.

Cómo jugar a Roulette

Roulette es un juego de casino clásico en el que apuestas a dónde caerá una bola en una rueda numerada en movimiento. Colocas una o varias apuestas en la mesa de números, la rueda gira y se suelta la bola. Cuando esta se detiene en una casilla numerada, ese es el número ganador: las apuestas que lo cubren pagan, y el resto pierde.

Realizar apuestas

Puedes apostar de varias formas y puedes combinar varias apuestas en la misma ronda. Las apuestas internas (a números concretos) pagan más pero son más difíciles de acertar; las apuestas externas (a grupos más amplios) pagan menos pero aciertan con más frecuencia:

  1. Pleno / Número único (35:1): apuesta a un número exacto (0–36, o 00 en la Roulette americana).
  2. División (17:1): dos números adyacentes.
  3. Calle (11:1): tres números seguidos.
  4. Esquina (8:1): cuatro números que se tocan en una esquina.
  5. Primeros cuatro / Línea superior (8:1 europea / 6:1 americana): cubre el 0 (y el 00 en la americana), 1, 2, 3.
  6. Seis números (5:1): dos filas adyacentes, seis números.
  7. Columna (2:1): una de las tres columnas verticales.
  8. Docenas (2:1): 1–12, 13–24 o 25–36.
  9. Rojo/Negro, Par/Impar, Alto/Bajo (1:1 cada una).

Roulette europea frente a americana

Cloudbet ofrece ambas versiones. La Roulette europea tiene un único cero (37 resultados posibles, RTP del 97,30 %); la Roulette americana añade una segunda casilla «00» (38 resultados posibles, RTP del 97 %), lo que aumenta ligeramente la ventaja de la casa. La versión que estás jugando debería mostrarse claramente antes de realizar una apuesta. Ambas incluyen un modo Autoplay para tiradas continuas con valores de ficha y número de tiradas preestablecidos.

Una vez colocadas tus apuestas y resuelta la ronda, las apuestas ganadoras se pagan automáticamente según sus probabilidades, y la mesa se reinicia para la siguiente ronda.

Esta página explica cómo Roulette convierte una secuencia de números aleatorios provably fair en un número ganador. Para saber cómo se genera esa secuencia aleatoria en primer lugar —semilla del servidor, semilla del cliente, nonce, hashing, SHAKE256—, consulta la explicación de imparcialidad comprobable de Cloudbet.

Convertir números aleatorios en un resultado de Roulette

En Roulette, el sistema provably fair tiene que demostrar que el número ganador se elige mediante aleatoriedad verificable, y no a posteriori.

El proceso es sencillo: en la Roulette europea, se muestrea de la secuencia aleatoria un número entero entre 0 y 36; ese es el número ganador. En la Roulette americana, el número muestreado puede ir de 0 a 37, con el «37» asignado al doble cero, «00»; los resultados del 0 al 36 se toman tal cual.

Cómo se elige el número ganador

Los bytes aleatorios generados por SHAKE256 (consulta nuestra página de imparcialidad comprobable) se dividen en enteros sin signo de 4 bytes, en un rango de 0 a 2³²−1. El número ganador de Roulette se elige entre 37 resultados posibles (europea) o 38 (americana), así que en los pasos siguientes P es 37 u 38.

Para elegir un número ganador de forma justa entre esos P resultados, aplicamos muestreo por rechazo (consulta Cómo funciona provably fair) y después tomamos el número resultante módulo P: el resto se convierte en el número ganador (en la Roulette americana, el «37» se muestra como «00»).

Diagrama de flujo que muestra cómo el sistema de equidad demostrable de Roulette de Cloudbet utiliza el muestreo por rechazo y una operación de módulo para determinar el número ganador.

Una vez generado, el número ganador se guarda y se oculta en el servidor; no se puede cambiar ni alterar sin ser detectado después de que empiece la ronda. Una vez realizada tu apuesta, se compara con este número predeterminado para resolver la ronda.

Verifica tus propios resultados de Roulette

Hemos escrito un pequeño programa en Node.js fiel a la implementación de Roulette de Cloudbet (tanto europea como americana). Introduce tu Semilla del servidor, Semilla del cliente y Nonce para reproducir tu resultado. (Recuerda: para revelar la Semilla del servidor de una ronda en Cloudbet, debes haber rotado ya a un nuevo par de semillas.)

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 game outcome to the game outcome outputted here. This proves that your game's outcome was determined solely by the pre-game seeds and was not altered afterwards.
const Version = Object.freeze({ AMERICAN: "american", EUROPEAN: "european" }); // Here the possible versions are listed
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
let myRouletteVersion = Version.AMERICAN;   // ✍️YOUR_INPUT✍️: Enter your roulette version (either Version.AMERICAN or Version.EUROPEAN)
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
  }
  throw new Error("No more numbers in the random number sequence! Fix: make 'numbersToGenerate' larger.");
}

// Step 4: Draw from the random number sequence to calculate the game result
function getGameResult(rouletteVersion, rng) {
  let numOfOutcomes = 0;
  if (rouletteVersion === Version.EUROPEAN) {
    numOfOutcomes = 37;
  } else if (rouletteVersion === Version.AMERICAN) {
    numOfOutcomes = 38;
  }

  let result = String(sample(numOfOutcomes, rng))

  // Convert the result '37' to '00'
  if (result === '37') {
    result = '00';
  }
  return result;
}
// Sample a number from P number of outcomes using Rejection Sampling
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;
  let rouletteVersion = Version.AMERICAN;
  if (usingCustomData) { nonce = myNonce; rouletteVersion = myRouletteVersion; }

  const { serverSeed, commitment } = generateServerSeed();
  const clientSeed = getClientSeed();

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = 1 * 2; // We need 1 random number, but there is an improbable case we'll need two
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  const value = getGameResult(rouletteVersion, rng);

  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(`🎯 Winning Number (in the ${rouletteVersion} version):`, value);
})();

Más explicaciones de provably fair por juego:

  1. Provably fair en Mines
  2. Provably fair en Pump
  3. Provably fair en Plinko
  4. Provably fair en Dice
  5. Provably fair en Dice: Golden Ticket
  6. Provably fair en Limbo
  7. Provably fair en Keno