Cómo funciona provably fair en Plinko

Así se juega a Plinko en Cloudbet, y así puedes comprobar que el recorrido de cada bola es provably fair.

Cómo jugar a Plinko

Plinko es un juego de azar en el que sueltas una bola por una pirámide de clavijas y observas cómo rebota hasta un premio. Antes de soltarla, fijas tu apuesta, cuántas filas tiene la pirámide y un nivel de riesgo. Cuantas más filas haya en juego y mayor sea el riesgo, mayores serán los multiplicadores posibles, pero más difícil será alcanzarlos. Al soltar la bola, esta rebota a izquierda o derecha en cada fila de clavijas hasta caer en uno de los cubos de la parte inferior, cada uno con su propio multiplicador. Muchas versiones de Plinko permiten soltar varias bolas a la vez para jugar más rápido.

Plinko de Cloudbet Originals

El Plinko propio de Cloudbet Originals te permite elegir 8, 10, 12 o 16 filas, cuatro niveles de riesgo (Bajo, Medio, Alto, Extremo) y 1, 5, 10, 20, 50 o 100 bolas por tirada. Tiene un RTP del 99 %. Una función opcional BOOST (un recargo del 20 % sobre tu apuesta) añade multiplicadores especiales de bola de 2x, 5x y 10x, elevando el multiplicador máximo a 100 000x, con un pago total máximo de 1 000 000 $.

Para una introducción general a provably fair —semillas del servidor, semillas del cliente, nonces, hashing y cómo verificar un resultado—, consulta el sistema de imparcialidad comprobable de Cloudbet. A continuación se explica cómo se aplica específicamente a Plinko.

Cómo el recorrido de una bola se convierte en un cubo

El recorrido de cada bola por la pirámide es una serie de rebotes en clavijas, y cada rebote es hacia la izquierda o hacia la derecha.

Diagrama que muestra cómo cada rebote en el juego de equidad demostrable Plinko de Cloudbet se codifica como una elección hacia la izquierda o hacia la derecha.

Esa elección se puede codificar como «0» (izquierda) o «1» (derecha). Un recorrido como «izquierda, izquierda, derecha» se convierte en el código «001», y siguiendo ese camino la bola cae en el Cubo #2.

Ejemplo que muestra la trayectoria de una bola de Plinko codificada como una secuencia de ceros y unos dentro del sistema de equidad demostrable de Cloudbet, terminando en el Cubo n.º 2.

Aquí está el patrón clave: un recorrido cae en el cubo que corresponde a su número de rebotes hacia la derecha, más uno. Por eso «100», «010» y «001» —todos con exactamente un «1»— caen en el Cubo #2 (1 + 1).

Diagrama que muestra varias trayectorias distintas de bolas de Plinko, verificablemente justas, que matemáticamente terminan todas en el mismo cubo.

Esto significa que un recorrido aleatorio se puede generar y codificar en un solo paso: se toma un número aleatorio de 32 bits, se leen sus primeros N bits (donde N es el número de filas) y se cuenta cuántos de esos bits son «1». Se suma uno, y ese es el cubo en el que cae la bola. El cubo se calcula y se fija en el servidor antes de que empiece la ronda, permaneciendo oculto hasta que esta termina y se revela.

Verifica la imparcialidad comprobable de tus propias partidas de Plinko

El código de abajo es fiel a la implementación de Plinko de Cloudbet. Introduce tu Semilla del servidor, Semilla del cliente, Nonce, número de bolas y número de filas, instala la única dependencia y ejecútalo.

npm install js-sha3

Tras instalar la dependencia, introduce tus propios datos en el siguiente bloque de 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.
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 myNumberOfBalls   = 1;                  // ✍️YOUR_INPUT✍️: Enter your number of balls here
let myNumberOfRows    = 12;                 // ✍️YOUR_INPUT✍️: Enter your number of rows 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 with a 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: Sample bits to determine ball paths
function pickBuckets(numOfRows, numOfBalls, rng) {
  let bucketIndices = [];
  for (let i = 0; i < numOfBalls; i++) {
    const rawUint32 = rng.next().value;
    let pathArray = rawUint32.toString(2).padStart(32, '0').split("");  // Convert the number into an array of bits
    pathArray.splice(numOfRows);                                        // Select the the first bits for the number of rows
    bucketIndices.push(getBucketIndexFromPath(pathArray));
  }
  return bucketIndices;
}

// Calculates which bucket the ball would land in an array of 1's and 0's where 0 = leftwards, 1 = rightwards
// In principle it counts the amount of times the ball bounces rightwards
function getBucketIndexFromPath(pathArray) {
  let rightCounter = 0;
  for(let i = 0; i < pathArray.length; ++i) {
    if (pathArray[i] === '1') {
      rightCounter += 1;
    } else {
      rightCounter += 0;
    }
  }
  return rightCounter + 1; // Add one so that the left-most bucket is bucket number one
}

// Example usage
(function main() {
  let numOfRows = 12;
  let numOfBalls = 1;
  let nonce = 1;
  if (usingCustomData) { numOfRows = myNumberOfRows; numOfBalls = myNumberOfBalls; nonce = myNonce; }

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

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = numOfBalls; // Only need to generate one number per ball
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature); 

  const bucketIndices = pickBuckets(numOfRows, numOfBalls, 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('🔁 Round Signature (can be verified after the game):', roundSignature);
  console.log('📋 Number of rows:', numOfRows);
  console.log('🏁 Buckets landed in (Bucket #1 is the leftern-most bucket):', bucketIndices);
})();

Nota: para revelar la Semilla del servidor de una partida en Cloudbet, primero debes rotar a una nueva Semilla del servidor.

Más explicaciones de provably fair por juego:

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