Cum funcționează Provably Fair în Dice: Golden Ticket

Iată cum se joacă Dice: Golden Ticket pe Cloudbet și cum poți verifica dacă fiecare aruncare și extragere Golden Ticket este provably fair.

Cum se joacă Dice: Golden Ticket

Dice: Golden Ticket (DGT) se joacă la fel ca jocul clasic Dice de la Cloudbet, cu un strat suplimentar: o țintă bonus ascunsă care poate plăti un multiplicator bonus peste câștigul tău normal.

Jocul de bază: alege Roll Over (2,00–99,99) sau Roll Under (0,01–98,00), apoi plasează-ți pariul. Dacă zarul aterizează de partea ta a țintei, câștigi la plata implicată de ținta ta (o țintă mai strânsă = multiplicator mai mare).

Bonusul Golden Ticket: în fiecare rundă, o a doua valoare ascunsă — Golden Ticket — este de asemenea stabilită pe tablă, cu propriul multiplicator bonus între 3x și 200x. Dacă aruncarea ta de zar aterizează la ±1,75 față de poziția Golden Ticket, câștigi plata bonus la acel multiplicator, instant și automat — fără pariu suplimentar necesar, iar acesta se adaugă la câștigul tău obișnuit.

Nu vizezi direct Golden Ticket — nu este ceva ce poți ținti sau influența, este pur și simplu mereu undeva pe tablă în fiecare rundă, iar aterizarea aproape de el este un bonus peste jocul tău obișnuit.

Cloudbet Originals Dice: Golden Ticket

DGT-ul propriu al Cloudbet Originals are un RTP de 99%, un multiplicator maxim al zarului de x6.730 și un câștig maxim de 1.000.000 $.

Dice: Golden Ticket (DGT) folosește aceeași bază provably fair ca fiecare joc Cloudbet Original — vezi Cum funcționează Provably Fair pentru mecanica generală (Server Seed, Client Seed, Nonce, SHAKE256, rejection sampling). Eșantionarea valorii zarului funcționează, de asemenea, la fel ca la Dice obișnuit — DGT adaugă valoarea și multiplicatorul Golden Ticket peste aceasta. Mai jos vezi cum devine acea secvență rezultatul tău specific la DGT.

Transformarea numerelor aleatorii într-un rezultat al jocului

DGT trebuie să demonstreze că două valori sunt determinate de aleatoriu verificabil: valoarea zarului și valoarea Golden Ticket.

Procesul este simplu: mai întâi, este eșantionat un număr întreg între 0 și 10000 (inclusiv) care va fi valoarea finală a zarului. Apoi este eșantionat un alt număr întreg între 175 și 9825 (inclusiv) care va fi valoarea finală a Golden Ticket. Ambele numere sunt împărțite la 100, astfel încât fiecare are două zecimale — variind de la 0 la 100 (zar) și 1,75 la 98,25 (Golden Ticket).

Intervalul Golden Ticket este păstrat în mod deliberat la 1,75–98,25, nu 0–100, astfel încât, oriunde ar aterizat, există întotdeauna o „zonă de aur” completă de 3,5 lățime în jurul său unde valoarea zarului poate ateriza și câștiga bonusul. Dacă intervalul ar ajunge până la margini (0 sau 100), un Golden Ticket aterizat aproape de o margine ar avea o zonă de aur mai mică — și șanse mai proaste — decât unul aterizat la mijloc.

DGT are, de asemenea, o caracteristică unică: multiplicatorul Golden Ticket este el însuși generat printr-un proces provably fair. În termeni simpli: este extras un al treilea număr aleatoriu între 0 și 1, apoi convertit într-un multiplicator între 3 și 200 folosind o distribuție Pareto trunchiată.

Diagramă care arată cum sistemul cu corectitudine demonstrabilă al Cloudbet generează trei valori interconectate în Dice: Golden Ticket — rezultatul zarului, poziția Golden Ticket și multiplicatorul său bonus.

Detalii tehnice

Octeții aleatorii generați de SHAKE256 sunt împărțiți în numere întregi fără semn de 4 octeți, variind de la 0 la 2³²-1. Deoarece valoarea zarului are 10001 rezultate posibile (0–10000 inclusiv), rejection sampling folosește P = 10001; valoarea Golden Ticket are 9651 rezultate posibile (175–9825 inclusiv), deci P-ul său este 9651. (Vezi pagina provably fair pentru motivul pentru care acest pas contează.)

Selectarea valorilor zarului și Golden Ticket: numărul aleatoriu al zarului este luat modulo 10001 pentru a obține valoarea sa. Același lucru se face pentru numărul aleatoriu al Golden Ticket folosind 9651, apoi rezultatul este incrementat cu 175 (astfel încât variază între 175–9825). Ambele sunt apoi împărțite la 100.

Selectarea multiplicatorului Golden Ticket (unele cunoștințe de statistică ajută aici): este extras un număr aleatoriu de 32 de biți și împărțit la valoarea sa maximă posibilă, producând un număr distribuit uniform între 0 și 1. Această valoare este transformată într-o distribuție Pareto trunchiată (limită inferioară 3, limită superioară 200, alfa 1,343 — aleasă astfel încât valoarea așteptată a multiplicatorului să fie 9, ceea ce susține ținta RTP de 99% a DGT), apoi rotunjită la două zecimale.

Desfășurarea jocului

Atât valoarea zarului, cât și cea a Golden Ticket sunt generate pe server și ascunse până la finalul rundei — nu pot fi schimbate nedetectat odată ce jocul a început. După ce plasezi pariul, pragul tău ales este comparat cu valoarea zarului, iar distanța Golden Ticket față de valoarea zarului este verificată în raport cu „zona de aur” de 1,75 pentru a vedea dacă câștigi bonusul.

Cod pentru verificare independentă

Fidel implementării DGT de pe Cloudbet — introdu Server Seed-ul, Client Seed-ul și Nonce-ul pentru a-ți verifica propriile jocuri. (Pentru a dezvălui Server Seed-ul unui joc pe Cloudbet, trebuie să fi trecut deja la un nou Server Seed.)

Dependențe:

npm install js-sha3

Codul:

// 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
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(rng) {
  // Dice
  // Ranges from 0 to 100, with two decimal places
  const diceMinValue  = 0;
  const diceMaxValue  = 10000;
  let diceValue = sampleFromRange(diceMinValue, diceMaxValue, rng);
  diceValue = diceValue / 100;          // Now the value ranges from 0 to 100

  // Golden Ticket
  // Ranges from 1.75 to 98.25 due to fairness
  // (If it landed on 99, for example, then the chance of the dice landing near
  // it would be smaller than the guarantee of 3.5%)
  const gtMinValue  = 175;
  const gtMaxValue  = 9825;
  let goldenTicketValue = sampleFromRange(gtMinValue, gtMaxValue, rng);
  goldenTicketValue = goldenTicketValue / 100;

  // Golden Ticket Multiplier
  const multiplier = getRandomNumberTruncatedPareto(rng);  // Complicated maths, can be ignored ;)

  return { diceValue, goldenTicketValue, multiplier };
}
// Sample a number using the truncated Pareto distribution
function getRandomNumberTruncatedPareto(rng) {
  // Get a uniformly distributed number between 0 and 1
  let randomInt32 = rng.next().value;
  const fraction = randomInt32 / 0xFFFFFFFF;

  // Truncated Pareto distribution. The expected value is 9.00
  const m0 = 3;
  const m1 = 200;
  const alpha = 1.343;

  const r = Math.pow(m0 / m1, alpha);
  const inner = 1 - fraction * (1 - r);
  let randomNumber = m0 / Math.pow(inner, 1 / alpha);
  randomNumber = Math.round(randomNumber * 100) / 100;  // Round to 2 decimal places
  return randomNumber;
}
// Sample a number between 'min' to 'max' (inclusive) using Rejection Sampling
function sampleFromRange(min, max, rng) {
  const P = max - min + 1;
  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 + min;
}

// Example usage
(function main() {
  let nonce = 1;
  if (usingCustomData) { nonce = myNonce; }

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

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = 3 * 2; // We need 3 random numbers, however due to rejection sampling we may need more
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  const { diceValue, goldenTicketValue, multiplier } = getGameResult(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('🎯 Dice landed on:', diceValue);
  console.log('🎫 Golden ticket landed on:', goldenTicketValue);
  console.log('⭐ Multiplier:', multiplier);
})();

Mai multe ghiduri Provably Fair pentru jocuri:

  1. Provably Fair la Mines
  2. Provably Fair la Pump
  3. Provably Fair la Plinko
  4. Provably Fair la Dice
  5. Provably Fair la Limbo
  6. Provably Fair la Roulette
  7. Provably Fair la Keno