Keno'da kanıtlanabilir adillik nasıl çalışır

Cloudbet'te Keno'nun nasıl oynanacağını ve her çekilişin kanıtlanabilir şekilde adil olduğunu nasıl kontrol edeceğinizi burada bulabilirsiniz.

Keno nasıl oynanır

Keno bir sayı çekilişi oyunudur: tablodan bir dizi sayı seçersiniz, ardından oyun kendi sayı setini çeker. Seçtiğiniz sayılardan ne kadar çoğu çekilen sayılarla eşleşirse kazancınız o kadar artar — ne kadar artacağı başlangıçta kaç sayı seçtiğinize bağlıdır. Seçimlerinizi yapıp bahsinizi oynadıktan sonra çekiliş gerçekleşir ve eşleşmeler anında ortaya çıkar.

Cloudbet Originals Keno

Cloudbet'in kendi Originals Keno oyunu 40 sayılık bir tablo kullanır: 1 ila 10 arasında sayı seçersiniz ve oyun 10 sayı çeker. %99 RTP sunar ve $0,01'den başlayan bahisleri kabul eder. Daha az sayı seçmek daha yüksek volatilite ve tutan sayı başına daha büyük ödemeler anlamına gelirken; daha çok sayı seçmek daha sık kazanç fakat tutan sayı başına daha düşük getiri anlamına gelir. Ardışık turları otomatikleştirmek için Auto Bet özelliğini kullanabilirsiniz.

Keno'nun çekilişi, diğer her Cloudbet Original'de olduğu gibi aynı doğrulanabilir yöntemle üretilir — Server Seed, Client Seed ve Nonce'un nasıl bir Round Signature'a ve ardından SHAKE256 aracılığıyla bir rastgele sayı akışına dönüştüğünü öğrenmek için genel kanıtlanabilir adillik sayfamıza göz atın. Bu sayfa Keno'ya özgü olan kısmı ele alır: bu rastgele sayı dizisinin nasıl gerçek çekilen sayı setine dönüştüğünü.

Rastgele sayıların Keno çekilişine dönüşmesi

Keno tablosu 1'den 40'a kadar numaralandırılmıştır. Bu 40 sayıdan onu, hiçbiri tekrar etmeden, birer birer çekilmelidir.

Cloudbet'in provably fair Keno tablosunu gösteren diyagram — adil sayı seçimi için her biri kendi indeksine sahip 40 numaralı kutu.

Henüz çekilmemiş kalan sayıların sayısına P diyoruz. P, 40'tan başlar ve her sayı çekildiğinde 1 azalır.

Keno'nun provably fair çekiliş algoritmasının işleyiş örneği: ilk sayı, tablodaki 40 pozisyonun tamamı arasından rastgele seçilir.

10 çekilişin her biri için SHAKE256 çıktısından, 0 ile 4.294.967.295 arasında değişen rastgele 4 baytlık (32 bitlik) bir sayı çekilir. Kalan P sayı arasından adil bir şekilde seçim yapmak için reddetme örneklemesi (rejection sampling) uygularız (bkz. reddetme örneklemesi rehberimiz), ardından sonucu P'ye göre mod alarak bir sonraki çekilen sayının konumunu buluruz. Çekilmesi gereken başka sayı varsa, seçilen sayı havuzdan çıkarılır, P bir azalır ve süreç tekrarlanır.

3. İlk çekiliş kaldırıldıktan sonra kalan 39 pozisyon arasından Keno'nun provably fair algoritmasının bir sonraki sayıyı nasıl çektiğini gösteren işleyiş örneği.

Bu, 10 sayının tamamı çekilene kadar devam eder.

4. Rastgele ve doğrulanabilir şekilde çekilen 10 sayının tamamının vurgulandığı nihai provably fair Keno tablosu.

5. Cloudbet'in provably fair Keno hesaplamasının akış şeması: rastgele baytlar ve rejection sampling, tablodaki her çekilen sayının konumunu belirler.

Oynanış

Çekilen 10 sayının tamamı, siz seçimlerinizi yapmadan önce sunucuda oluşturulur ve gizlenir — sizinle önceden paylaşılan Commitment sayesinde, sonrasında fark edilmeden değiştirilemez veya oynanamaz. Seçimleriniz, tur sonuçlandığında yalnızca bu önceden belirlenmiş sayı setiyle karşılaştırılır ve kazancınız kaç eşleşmeniz olduğuna göre hesaplanır.

Cloudbet Keno sonuçlarınızı bağımsız olarak doğrulamak için kod

Kendi sonuçlarınızı doğrulamak için Server Seed, Client Seed ve Nonce değerlerinizi girin. Tek bağımlılığı kurun ve oyununuzun sonucunu yeniden oluşturmak için kodu kendiniz çalıştırın.

Not: Cloudbet'te tamamlanmış bir oyunun Server Seed'ini görebilmek için, önce yeni bir tur başlatmış olmanız gerekir (bu işlem yeni bir seed çiftine geçiş yapar).

Bağımlılıklar:

npm install js-sha3

Kod:

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