Så fungerar provably fair i Dice: Golden Ticket

Så här spelar du Dice: Golden Ticket på Cloudbet, och så kontrollerar du att varje kast och Golden Ticket-dragning är provably fair.

Så spelar du Dice: Golden Ticket

Dice: Golden Ticket (DGT) spelas som Cloudbets klassiska Dice-spel, med ett extra lager: ett dolt bonusmål som kan ge en bonusmultiplikator ovanpå din vanliga vinst.

Grundspelet: välj Roll Over (2,00–99,99) eller Roll Under (0,01–98,00), och placera din insats. Om tärningen hamnar på din sida av målet vinner du enligt den utdelning ditt mål innebär (ett snävare mål = större multiplikator).

Golden Ticket-bonusen: varje omgång sätts även ett andra, dolt värde — Golden Ticket — ut på brädet, med en egen bonusmultiplikator mellan 3x och 200x. Om ditt tärningskast hamnar inom ±1,75 från Golden Tickets position vinner du bonusutdelningen med den multiplikatorn, direkt och automatiskt — ingen extra insats krävs, och den läggs ovanpå din vanliga vinst.

Du siktar inte på Golden Ticket direkt — det är inget du kan påverka eller styra, det finns bara alltid någonstans på brädet varje omgång, och att hamna nära det är en bonus ovanpå ditt vanliga spel.

Cloudbet Originals Dice: Golden Ticket

Cloudbets egen Originals DGT har en RTP på 99 %, en maximal tärningsmultiplikator på x6 730, och en maxvinst på $1 000 000.

Dice: Golden Ticket (DGT) använder samma provably fair-grund som alla Cloudbet Originals — se Så fungerar provably fair för de allmänna mekanismerna (Server Seed, Client Seed, Nonce, SHAKE256, rejection sampling). Tärningsvärdets urval fungerar också på samma sätt som i vanliga Dice — DGT lägger till Golden Ticket-värdet och multiplikatorn ovanpå. Nedan följer hur den talföljden specifikt blir ditt DGT-resultat.

Att omvandla slumptal till ett spelresultat

DGT måste bevisa att två värden avgörs av verifierbar slumpmässighet: tärningens värde och Golden Tickets värde.

Processen är enkel: först hämtas ett heltal mellan 0 och 10000 (inklusive) som blir tärningens slutgiltiga värde. Sedan hämtas ytterligare ett heltal mellan 175 och 9825 (inklusive) som blir Golden Tickets slutgiltiga värde. Båda talen divideras med 100 så att de får två decimaler var — och sträcker sig från 0 till 100 (tärning) respektive 1,75 till 98,25 (Golden Ticket).

Golden Tickets intervall hålls medvetet till 1,75–98,25, inte 0–100, så att det — oavsett var det hamnar — alltid finns en hel 3,5 bred "gyllene zon" runt det där tärningens värde kan hamna och vinna bonusen. Om intervallet gick ända ut till kanterna (0 eller 100) skulle en Golden Ticket nära en kant få en mindre gyllene zon — och sämre odds — än en som hamnar i mitten.

DGT har också en unik funktion: Golden Tickets multiplikator genereras i sig av en provably fair-process. Enkelt uttryckt: ett tredje slumptal mellan 0 och 1 hämtas, och omvandlas sedan till en multiplikator mellan 3 och 200 med hjälp av en trunkerad Pareto-fördelning.

Diagram som visar hur Cloudbets bevisbart rättvisa system genererar tre sammanlänkade värden i Dice: Golden Ticket — tärningsresultatet, Golden Ticket-positionen och dess bonusmultiplikator.

Tekniska detaljer

De slumpmässiga bytes som genereras av SHAKE256 delas upp i 4-byte osignerade heltal, från 0 till 2³²-1. Eftersom tärningsvärdet har 10001 möjliga utfall (0–10000 inklusive) används P = 10001 för rejection sampling; Golden Tickets värde har 9651 möjliga utfall (175–9825 inklusive), så dess P = 9651. (Se sidan om provably fair för varför detta steg är viktigt.)

Val av tärningens och Golden Tickets värden: tärningens slumptal tas modulo 10001 för att få dess värde. Detsamma görs för Golden Tickets slumptal med 9651, varefter resultatet ökas med 175 (så att det hamnar mellan 175–9825). Båda divideras sedan med 100.

Val av Golden Tickets multiplikator (viss förtrogenhet med statistik är till hjälp här): ett slumpmässigt 32-bitars tal hämtas och divideras med sitt maximalt möjliga värde, vilket ger ett jämnt fördelat tal mellan 0 och 1. Det värdet omvandlas till en trunkerad Pareto-fördelning (undre gräns 3, övre gräns 200, alfa 1,343 — valt så att multiplikatorns förväntade värde är 9, vilket upprätthåller DGT:s mål på 99 % RTP), och avrundas sedan till två decimaler.

Spelupplägg

Både tärningens och Golden Tickets värden genereras serversidan och hålls dolda tills omgången är slut — de kan inte ändras oupptäckt när spelet väl har börjat. Efter att du placerat din insats jämförs ditt valda tröskelvärde med tärningens värde, och Golden Tickets avstånd från tärningens värde kontrolleras mot den 1,75 "gyllene zonen" för att se om du vinner bonusen.

Kod för oberoende verifiering

Motsvarar DGT-implementationen på Cloudbet exakt — ange din Server Seed, Client Seed och Nonce för att verifiera dina egna spel. (För att avslöja ett spels Server Seed på Cloudbet måste du redan ha gått vidare till en ny Server Seed.)

Beroenden:

npm install js-sha3

Koden:

// 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);
})();

Fler förklaringar av provably fair per spel:

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