Come funziona l'equità verificabile nel Keno

Ecco come giocare a Keno su Cloudbet e come verificare che ogni estrazione sia equa e verificabile.

Come giocare a Keno

Il Keno è un gioco di estrazione di numeri: scegli una serie di numeri su un tabellone, poi il gioco estrae la propria serie di numeri. Più numeri indovini tra quelli estratti, maggiore sarà la vincita — l'importo della vincita dipende da quanti numeri hai scelto all'inizio. Una volta effettuate le scelte e piazzata la puntata, l'estrazione avviene e i numeri indovinati vengono rivelati all'istante.

Cloudbet Originals Keno

Il Keno Originals di Cloudbet utilizza un tabellone da 40 numeri: puoi scegliere da 1 a 10 numeri e il gioco ne estrae 10. Ha un RTP del 99% e accetta puntate a partire da $0.01. Meno numeri scegli, maggiore sarà la volatilità e più alte le vincite per ogni numero indovinato; più numeri scegli, più frequenti saranno le vincite ma con importi più bassi per ogni numero indovinato. La funzione Auto Bet è disponibile per automatizzare più round.

L'estrazione del Keno viene generata con lo stesso metodo verificabile di ogni altro Cloudbet Original — consulta la nostra panoramica sull'equità verificabile per scoprire come Server Seed, Client Seed e Nonce si combinano per creare una Round Signature e, di conseguenza, una sequenza di numeri casuali tramite SHAKE256. Questa pagina illustra ciò che è specifico del Keno: come quella sequenza di numeri casuali diventa l'effettiva serie di numeri estratti.

Come i numeri casuali diventano un'estrazione del Keno

Il tabellone del Keno è numerato da 1 a 40. Dieci di questi 40 numeri devono essere estratti, uno alla volta, senza ripetizioni.

Diagramma che mostra il tabellone Keno provably fair di Cloudbet — 40 caselle numerate, ciascuna con il proprio indice per una selezione equa dei numeri.

Chiamiamo P il numero di numeri non ancora estratti rimasti. P parte da 40 e diminuisce di 1 ogni volta che viene estratto un numero.

Esempio pratico dell'algoritmo di estrazione provably fair di Keno: il primo numero viene selezionato casualmente tra tutte le 40 posizioni del tabellone.

Per ciascuna delle 10 estrazioni viene generato un numero casuale a 4 byte (32 bit) dall'output di SHAKE256, compreso tra 0 e 4.294.967.295. Per scegliere in modo equo tra i P numeri rimanenti, applichiamo il campionamento per rigetto (rejection sampling) (vedi la nostra guida al campionamento per rigetto), poi calcoliamo il resto della divisione del risultato per P per ottenere la posizione del prossimo numero estratto. Se restano altri numeri da estrarre, il numero scelto viene rimosso dal gruppo, P diminuisce di 1 e il processo si ripete.

3. Esempio pratico che mostra l'algoritmo provably fair di Keno mentre estrae il numero successivo tra le 39 posizioni rimanenti dopo la rimozione della prima estrazione.

Il processo continua finché non sono stati estratti tutti e 10 i numeri.

4. Il tabellone Keno provably fair finale con tutti i 10 numeri estratti in modo casuale e verificabile evidenziati.

5. Diagramma di flusso del calcolo provably fair di Keno di Cloudbet: byte casuali e rejection sampling determinano la posizione di ogni numero estratto sul tabellone.

Come si gioca

L'intera serie di 10 numeri estratti viene generata e nascosta sul server prima che tu faccia le tue scelte — non può essere modificata o alterata senza essere rilevata in seguito, grazie al Commitment già condiviso con te. Le tue scelte vengono semplicemente confrontate con questa serie pre-estratta al termine del round, e la vincita viene calcolata in base a quanti numeri hai indovinato.

Codice per verificare autonomamente i tuoi risultati di Keno su Cloudbet

Inserisci il tuo Server Seed, Client Seed e Nonce per verificare i tuoi risultati. Installa l'unica dipendenza necessaria ed eseguila tu stesso per ricostruire il risultato della tua partita.

Nota: per rivelare il Server Seed di una partita conclusa su Cloudbet, devi aver già avviato un nuovo round (che genera una nuova coppia di seed).

Dipendenze:

npm install js-sha3

Il codice:

// 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 mine positions to the mine positions 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
  }
}

// Step 4: Pick a number of unique positions
function pickUniquePositions(numOfPicks, totalPositions, rng) {
  const available = Array.from({ length: totalPositions }, (_, i) => i);
  const picks = [];

  for (let i = 0; i < numOfPicks; ++i) {
    const index = sample(available.length, rng);

    picks.push(available[index]);
    available.splice(index, 1); // Remove chosen pump from list of non-faulty pumps
  }
  return picks;
}
// Rejection sampling for fair results
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;
  if (usingCustomData) { nonce = myNonce; }
  const numOfHits = 10;
  const totalPositions = 40;

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

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = totalPositions * 2; // We need at least one random number per position, but there is an improbable possibility  we'll need two
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  let hits = pickUniquePositions(numOfHits, totalPositions, rng);
  hits = hits.map((value)=> value + 1 );
  hits.sort((a, b)=>a-b);

  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(`🎯 Hits:`, hits);
})();