Így működik a provably fair a Plinkóban
Íme, hogyan játssz Plinkót a Cloudbeten, és hogyan ellenőrizheted, hogy minden labda útja provably fair.
Hogyan játssz Plinkót
A Plinko egy szerencsejáték, ahol egy labdát ejtesz le egy tűkből álló piramison, és nézed, ahogy az kifizetés felé pattog. Az ejtés előtt beállítod a téted összegét, hogy a piramisnak hány sora legyen, és egy kockázati szintet. Minél több sor van játékban, és minél magasabb a kockázat, annál nagyobbak a lehetséges szorzók — de annál nehezebb is eltalálni őket. Miután leejted a labdát, az minden tűsoron balra vagy jobbra pattan, amíg landol az alján lévő egyik zsebben, amelyek mindegyikéhez saját szorzó tartozik. Sok Plinko-játékban egyszerre több labdát is leejthetsz a gyorsabb játékhoz.
Cloudbet Originals Plinko
A Cloudbet saját Originals Plinko játékában 8, 10, 12 vagy 16 sor közül választhatsz, négy kockázati szint közül (Alacsony, Közepes, Magas, Extrém), és 1, 5, 10, 20, 50 vagy 100 labda közül ejtésenként. RTP-je 99%. Az opcionális BOOST funkció (a tétedre rakott 20%-os felár) speciális 2x, 5x és 10x labdaszorzókat ad hozzá, a maximális szorzót 100 000x-re tolva — az összes kifizetés felső határa 1 000 000 $.
A provably fair általános bemutatásáért — server seedek, client seedek, nonce-ok, hash-elés, és hogyan ellenőrizhető egy eredmény — lásd a Cloudbet provably fair rendszerét. Alább az látható, hogy ez konkrétan a Plinkóra hogyan vonatkozik.
Hogyan válik egy labda útja zsebbé
Minden labda piramison lefelé vezető útja egy sor tűről való pattanásból áll, és minden egyes pattanás vagy balra, vagy jobbra történik.

Ez a választás kódolható „0”-ként (balra) vagy „1”-ként (jobbra). Egy „balra, balra, jobbra” útvonalból „001” kód lesz, amelyet követve a labda a 2-es számú zsebben landol.

Itt a kulcsfontosságú minta: egy útvonal abban a zsebben landol, amelynek száma megegyezik a jobbra történő pattanások számával, plusz egy. Így a „100”, „010” és „001” — mindegyikben pontosan egy „1” van — a 2-es számú zsebben landol (1 + 1).

Ez azt jelenti, hogy egy véletlenszerű útvonal egyetlen lépésben legenerálható és kódolható: veszünk egy véletlenszerű 32 bites számot, beolvassuk az első N bitjét (ahol N a sorok száma), és megszámoljuk, hogy ezek közül a bitek közül hány „1”. Hozzáadunk egyet, és ez lesz a zseb, amelyben a labda landol. A zseb a szerveren kerül kiszámításra és rögzítésre a kör kezdete előtt, rejtve marad a kör végéig, majd felfedésre kerül.
Ellenőrizd saját Plinko-játékaid provably fair működését
Az alábbi kód pontosan megfelel a Cloudbet Plinko implementációjának. Add meg a Server Seededet, a Client Seededet, a Nonce-odat, a labdák és a sorok számát, telepítsd az egyetlen függőséget, majd futtasd le.
npm install js-sha3
A függőség telepítése után add meg a saját adataidat az alábbi kódrészletben.
// 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);
})();
Megjegyzés: ahhoz, hogy egy játék Server Seedjét felfedd a Cloudbeten, előbb új Server Seedre kell váltanod.
