Como o provably fair funciona no Pump

Veja como jogar Pump na Cloudbet, e como verificar se a bomba com defeito de cada rodada é provably fair.

Como jogar Pump

Pump é um jogo de "arrisque até onde der": você infla algo (um balão, na versão da Cloudbet) que aumenta um multiplicador a cada bombeada. Saque a qualquer momento para garantir seus ganhos — mas se você for longe demais, o balão estoura, e você perde sua aposta. Antes de começar, você normalmente escolhe um nível de dificuldade que equilibra até onde é seguro ir contra a rapidez com que o multiplicador cresce.

Depois de fazer sua aposta, pressione Pump para inflar o balão um passo de cada vez. Cada bombeada bem-sucedida aumenta seu prêmio potencial. Você pode pressionar Sacar depois de qualquer bombeada para receber seu multiplicador atual — ou continuar bombeando em busca de um maior. Se a próxima bombeada for a defeituosa da rodada, o balão estoura imediatamente e a rodada termina sem pagamento.

Cloudbet Originals Pump

O Pump Original da Cloudbet tem cinco níveis de dificuldade — Fácil, Médio, Difícil, Especialista e Mestre — que controlam quantas das 25 bombeadas da rodada são "defeituosas" (as que estouram o balão). Tem RTP de 99%, prêmio máximo de $1.000.000, e aceita apostas a partir de $0,01. O Modo Autobet permite configurar o valor da sua aposta, bombeadas por rodada, limites de lucro/perda, e número de rodadas para jogar automaticamente — o modo manual ("Pump espera pelo SEU comando") também está sempre disponível.

Para a mecânica geral do provably fair — seed do servidor, seed do cliente, nonce, hashing, e como a Assinatura da Rodada é criada — veja o guia provably fair da Cloudbet. Abaixo está como essa sequência de números aleatórios se transforma especificamente no resultado de uma rodada de Pump.

Transformando números aleatórios em um layout de jogo

As 25 bombeadas do Pump são rotuladas de A a Y para que possamos rastreá-las:

Diagrama mostrando as 25 bombas do Pump rotuladas de A a Y, usadas para acompanhar a seleção de equidade comprovável da bomba defeituosa da Cloudbet.

Todas as bombeadas começam sem defeito. Usando o primeiro número na sequência de números aleatórios do jogo, escolhemos uma bombeada sem defeito por índice e a tornamos defeituosa:

Exemplo prático do algoritmo de equidade comprovável do Pump: a primeira bomba defeituosa é selecionada aleatoriamente entre as 25 bombas não defeituosas.

Essa bombeada é então removida da lista de bombeadas sem defeito, então a próxima seleção considera apenas o que restou:

Diagrama mostrando como o sistema de equidade comprovável do Pump da Cloudbet remove uma bomba defeituosa selecionada da lista antes de sortear a próxima.

Isso se repete até que o número de bombeadas defeituosas do nível de dificuldade da rodada seja atingido (médio = 3 mostrado aqui):

Exemplo prático mostrando o algoritmo de equidade comprovável do Pump selecionando uma segunda bomba defeituosa entre as bombas não defeituosas restantes.

Assim que todas as bombeadas defeituosas da rodada são escolhidas, os números usados para selecioná-las não são mais necessários — o que resta é apenas quais bombeadas são defeituosas:

Diagrama mostrando o layout completo de bombas defeituosas de uma rodada antes de o cálculo de equidade comprovável da Cloudbet simplificá-lo para a única bomba que importa.

Como as bombeadas são acionadas uma de cada vez, em ordem (A, depois B, depois C…), apenas a primeira bombeada defeituosa que o jogador alcança realmente importa — qualquer coisa depois dela nunca é acionada. Então o layout se simplifica para apenas essa bombeada relevante:

Diagrama mostrando o layout de equidade comprovável de bombas defeituosas do Pump simplificado apenas para a primeira bomba defeituosa que o jogador realmente alcançaria.

Detalhes técnicos

Chamamos o número de bombeadas sem defeito de P. Os bytes aleatórios do SHAKE256 são divididos em inteiros sem sinal de 4 bytes (0 a 2³²−1). Para escolher de forma justa qual bombeada está defeituosa, usamos amostragem por rejeição contra P, e então usamos o resto (+1) como o índice da bombeada sem defeito a ser tornada defeituosa. Se ainda forem necessárias mais bombeadas defeituosas, a bombeada selecionada é removida da lista, P é reduzido em 1, e um novo número é amostrado — repetindo-se até que o número de bombeadas defeituosas da rodada seja atingido.

O layout final de bombeadas defeituosas é gerado e salvo no lado do servidor antes do início da rodada, e não pode ser alterado depois sem quebrar o Compromisso já compartilhado com o jogador. Cada bombeada que o jogador pressiona é verificada contra esse layout pré-comprometido.

Verifique você mesmo

Escrevemos um pequeno programa em Node.js fiel à implementação de Pump na Cloudbet — insira sua Seed do Servidor, Seed do Cliente, Nonce e nível de dificuldade para reproduzir o resultado da sua rodada. (Observação: para revelar a Seed do Servidor de uma rodada na Cloudbet, você precisa já ter rotacionado para um novo par de seeds.)

// 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 myDifficultyLevel = 'easy';             // ✍️YOUR_INPUT✍️: Enter your difficulty level here (easy, medium, hard, expert, or master)
const usingCustomData = false;              // Change this to true if you are using your own data

// These are the possible difficulty levels (and how many faulty pumps there are per difficulty level)
const faultyPumpsPerDiff = {
  'easy':       1,
  'medium':     3,
  'hard':       5,
  'expert':     9,
  'master':     12
};

// 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); // shaker256 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: Generate indices of the faulty pumps
function pickUniquePositions(numOfFaultyPumps, totalPumps, rng) {
  const available = Array.from({ length: totalPumps }, (_, i) => i);
  const faultyPumps = [];

  for (let i = 0; i < numOfFaultyPumps; ++i) {
    const index = sample(available.length, rng);

    faultyPumps.push(available[index]);
    available.splice(index, 1); // Remove chosen pump from list of non-faulty pumps
  }
  return faultyPumps;
}
// 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() {
  const totalPumps = 25;
  let difficultyLevel = 'easy';
  let nonce = 1;
  if (usingCustomData) { difficultyLevel = myDifficultyLevel; nonce = myNonce; }

  const numOfFaultyPumps = faultyPumpsPerDiff[difficultyLevel];

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

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = 12 * 2;                 // We have at most 12 faulty pumps, however due to rejection sampling we may need more numbers
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  const faultyPumps = pickUniquePositions(numOfFaultyPumps, totalPumps, rng);
  let firstFaultyPump = Math.min(...faultyPumps);  // Select the smallest/first faulty pump
  firstFaultyPump += 1;                            // Increment the index so that it starts with 1, 2, 3, ... instead of 0, 1, 2

  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('🎯 Difficulty level:', difficultyLevel);
  console.log('💣 First faulty pump (zero-indexed, i.e., 1, 2, 3, ...):', firstFaultyPump);
})();

Instale a única dependência primeiro:

npm install js-sha3

Mais explicações dos jogos provably fair:

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