Så fungerar provably fair i Plinko

Så här spelar du Plinko på Cloudbet, och så kontrollerar du att varje bolls väg är provably fair.

Så spelar du Plinko

Plinko är ett slumpspel där du släpper en boll ner genom en pyramid av stift och ser den studsa sig fram till en utdelning. Innan du släpper bollen ställer du in din insats, hur många rader pyramiden har, och en risknivå. Ju fler rader i spel och ju högre risk, desto större möjliga multiplikatorer — men desto svårare att träffa dem. När du väl släpper bollen studsar den vänster eller höger vid varje rad av stift tills den landar i en av hinkarna längst ner, var och en med sin egen multiplikator. Många Plinko-spel låter dig släppa flera bollar samtidigt för snabbare spel.

Cloudbet Originals Plinko

Cloudbets egen Originals Plinko låter dig välja 8, 10, 12 eller 16 rader, fyra risknivåer (Low, Medium, High, Extreme), och 1, 5, 10, 20, 50 eller 100 bollar per släpp. Den har en RTP på 99 %. En valfri BOOST-funktion (ett påslag på 20 % av din insats) lägger till speciella 2x-, 5x- och 10x-bollmultiplikatorer, vilket höjer maxmultiplikatorn till 100 000x — med ett tak på $1 000 000 i total utbetalning.

För en allmän introduktion till provably fair — server seeds, client seeds, nonces, hashning och hur man verifierar ett resultat — se Cloudbets provably fair-system. Nedan ser du hur det tillämpas specifikt på Plinko.

Så blir en bolls väg en hink

Varje bolls väg ner genom pyramiden är en serie studsar mot stift, och varje studs är antingen åt vänster eller åt höger.

Diagram som visar hur varje studs i Cloudbets bevisbart rättvisa Plinko-spel kodas som ett val åt vänster eller höger.

Det valet kan kodas som en "0" (vänster) eller "1" (höger). En väg som "vänster, vänster, höger" blir koden "001," och att följa den låter bollen landa i Hink #2.

Exempel som visar en Plinko-bolls bana kodad som en sekvens av 0:or och 1:or inom Cloudbets bevisbart rättvisa system, där bollen hamnar i Fack #2.

Här är det viktiga mönstret: en väg landar i hinken som matchar antalet högerstudsar, plus ett. Så "100," "010," och "001" — alla med exakt en "1" — landar i Hink #2 (1 + 1).

Diagram som visar flera olika bevisbart rättvisa Plinko-bollbanor som matematiskt alla hamnar i samma fack.

Det betyder att en slumpmässig väg kan genereras och kodas i ett enda steg: ta ett slumpmässigt 32-bitars tal, läs dess första N bitar (där N är antalet rader), och räkna hur många av de bitarna som är "1." Lägg till ett, och det är hinken bollen landar i. Hinken beräknas och låses in på servern innan omgången börjar, och hålls dold tills omgången är slut och sedan avslöjas.

Verifiera rättvisan i dina egna Plinko-spel

Koden nedan motsvarar Cloudbets Plinko-implementation exakt. Ange din Server Seed, Client Seed, Nonce, antal bollar och antal rader, installera det enda beroendet, och kör den.

npm install js-sha3

Efter att du installerat beroendet, mata in din egen data i kodblocket nedan.

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

Obs: för att avslöja ett spels Server Seed på Cloudbet måste du först rotera till en ny Server Seed.

Fler förklaringar av provably fair per spel:

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