Så fungerar provably fair i Roulette

Så här spelar du Roulette på Cloudbet, och så kontrollerar du att varje snurr är provably fair.

Så spelar du Roulette

Roulette är ett klassiskt casinospel där du satsar på var en snurrande boll landar på ett numrerat hjul. Du placerar en eller flera insatser på nummertabellen, sedan snurrar hjulet och bollen släpps. När den lägger sig i en numrerad ficka är det den vinnande siffran — alla insatser som täcker den betalas ut, och allt annat förlorar.

Att placera insatser

Du kan satsa på utfallet på flera olika sätt, och du är fri att kombinera flera insatser i samma omgång. Inside bets (på specifika nummer) betalar mer men är svårare att träffa; outside bets (på bredare grupper) betalar mindre men träffar oftare:

  1. Straight-up / enskilt nummer (35:1): satsa på ett exakt nummer (0–36, eller 00 i amerikansk roulette).
  2. Split (17:1): två intilliggande nummer.
  3. Street (11:1): tre nummer i rad.
  4. Corner (8:1): fyra nummer som möts i ett hörn.
  5. First Four / Top Line (8:1 europeisk / 6:1 amerikansk): täcker 0 (och 00 i amerikansk), 1, 2, 3.
  6. Six Line (5:1): två intilliggande rader, sex nummer.
  7. Column (2:1): en av tre vertikala kolumner.
  8. Dozens (2:1): 1–12, 13–24, eller 25–36.
  9. Rött/Svart, Udda/Jämnt, Högt/Lågt (1:1 vardera).

Europeisk vs. amerikansk roulette

Cloudbet erbjuder båda varianterna. Europeisk roulette har en enda nolla (37 möjliga utfall, 97,30 % RTP); amerikansk roulette lägger till en andra "00"-ficka (38 möjliga utfall, 97 % RTP), vilket ökar husets fördel något. Vilken variant du spelar bör visas tydligt innan du placerar en insats. Båda inkluderar ett Autoplay-läge för kontinuerliga snurr med förinställda marker-värden och antal snurr.

När dina insatser är placerade och omgången avgörs betalas alla vinnande insatser ut automatiskt enligt sina odds, och bordet återställs inför nästa omgång.

Den här sidan handlar om hur Roulette omvandlar en provably fair-talföljd till ett vinnande nummer. För hur den talföljden i första hand genereras — server seed, client seed, nonce, hashning, SHAKE256 — se Cloudbets förklaring av provably fair.

Så blir slumptal ett Roulette-resultat

För Roulette måste provably fair-systemet bevisa att det vinnande numret väljs genom verifierbar slumpmässighet, inte väljs i efterhand.

Processen är enkel: i europeisk roulette hämtas ett heltal mellan 0 och 36 från slumptalsföljden — det är det vinnande numret. I amerikansk roulette kan det hämtade talet variera mellan 0 och 37, där "37" motsvarar den dubbla nollan, "00"; resultaten 0 till 36 tas som de är.

Så väljs det vinnande numret

De slumpmässiga bytesen som genereras av SHAKE256 (se vår sida om provably fair) delas upp i 4-byte osignerade heltal, från 0 till 2³²−1. Roulettes vinnande nummer väljs bland 37 möjliga utfall (europeisk) eller 38 (amerikansk) — så i stegen nedan är P antingen 37 eller 38.

För att rättvist välja ett vinnande nummer bland dessa P utfall tillämpar vi rejection sampling (se Så fungerar provably fair), och tar sedan det resulterande talet modulo P — resten blir det vinnande numret (för amerikansk roulette visas "37" som "00").

Flödesschema som visar hur Cloudbets bevisbart rättvisa Roulette-system använder rejection sampling och en modulo-operation för att avgöra det vinnande numret.

När det väl är genererat sparas och döljs det vinnande numret serversidan — det kan inte ändras eller manipuleras oupptäckt efter att omgången har startat. När du har placerat din insats jämförs den mot detta i förväg fastställda nummer för att avgöra omgången.

Verifiera dina egna Roulette-resultat

Vi har skrivit ett litet Node.js-program som motsvarar Cloudbets Roulette-implementation (både europeisk och amerikansk). Ange din Server Seed, Client Seed och Nonce för att återskapa ditt resultat. (Påminnelse: för att avslöja en omgångs Server Seed på Cloudbet måste du redan ha roterat till ett nytt seed-par.)

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.
const Version = Object.freeze({ AMERICAN: "american", EUROPEAN: "european" }); // Here the possible versions are listed
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 myRouletteVersion = Version.AMERICAN;   // ✍️YOUR_INPUT✍️: Enter your roulette version (either Version.AMERICAN or Version.EUROPEAN)
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(rouletteVersion, rng) {
  let numOfOutcomes = 0;
  if (rouletteVersion === Version.EUROPEAN) {
    numOfOutcomes = 37;
  } else if (rouletteVersion === Version.AMERICAN) {
    numOfOutcomes = 38;
  }

  let result = String(sample(numOfOutcomes, rng))

  // Convert the result '37' to '00'
  if (result === '37') {
    result = '00';
  }
  return result;
}
// Sample a number from P number of outcomes using Rejection Sampling
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() {
  let nonce = 1;
  let rouletteVersion = Version.AMERICAN;
  if (usingCustomData) { nonce = myNonce; rouletteVersion = myRouletteVersion; }

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

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = 1 * 2; // We need 1 random number, but there is an improbable case we'll need two
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  const value = getGameResult(rouletteVersion, 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(`🎯 Winning Number (in the ${rouletteVersion} version):`, value);
})();

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 Dice: Golden Ticket
  6. Provably fair Limbo
  7. Provably fair Keno