Jak działa provably fair w grze Mines

Oto jak grać w Mines na Cloudbet oraz jak sprawdzić, że rozmieszczenie min w każdej rundzie jest provably fair.

Jak grać w Mines

Każda runda Mines na Cloudbet opiera się na tym samym fundamencie provably fair — seed serwera, seed klienta, nonce oraz SHA3-256/SHAKE256 — opisanym szczegółowo na naszej stronie głównej Jak działa provably fair. Ta strona opisuje to, co jest specyficzne dla Mines: jak ta sekwencja liczb losowych staje się faktyczną siatką min, z którą grasz.

Zamiana liczb losowych w siatkę min

Na początku gry wybierasz liczbę min do rozmieszczenia — nazwiemy tę liczbę N. Siatkę Mines 5×5 można spłaszczyć do pojedynczego rzędu 25 pozycji, z których każda ma swój indeks, zaczynając od 1:

Diagram pokazujący, jak siatka gry Mines o weryfikowalnej uczciwości od Cloudbet — pola 5×5 oznaczone od A do Y — zostaje spłaszczona do jednego rzędu 25 ponumerowanych pozycji na potrzeby uczciwego losowania liczb.

Losujemy liczbę z sekwencji losowej (generowanej w sposób opisany na stronie Provably Fair), aby określić pozycję pierwszej miny:

Praktyczny przykład algorytmu o weryfikowalnej uczciwości gry Mines od Cloudbet: losowa liczba wybiera pozycję pierwszej miny spośród wszystkich 25 pól.

Jeśli trzeba rozmieścić więcej min, losujemy kolejną liczbę i powtarzamy proces — ale teraz do wyboru pozostaje już tylko 24 wolne miejsca:

Praktyczny przykład pokazujący, jak algorytm o weryfikowalnej uczciwości gry Mines wybiera kolejną minę spośród 24 pozostałych pozycji po usunięciu pierwszej miny z puli.

Powtarza się to, aż wszystkie miny zostaną rozmieszczone. Cała siatka jest następnie odtwarzana i utrzymywana w tajemnicy do końca rundy:

Ostateczna siatka gry Mines o weryfikowalnej uczciwości, odtworzona ze spłaszczonego rzędu po tym, jak wszystkie pozycje min zostały losowo i w sposób weryfikowalny rozmieszczone.

Szczegóły techniczne: wybór każdej pozycji miny

Przed każdym losowaniem znamy P — liczbę wolnych od min pozycji pozostałych do wyboru. P zaczyna się od 25 i zmniejsza się o 1 za każdym razem, gdy mina zostaje umieszczona.

Dla każdej miny z wyjścia SHAKE256 losowana jest liczba 4-bajtowa (32-bitowa), z zakresu od 0 do 4 294 967 295. Aby uczciwie wybrać spośród P pozycji, stosujemy próbkowanie odrzucające (zobacz ogólną koncepcję wyjaśnioną na stronie Jak działa provably fair), a następnie bierzemy wynik modulo P, aby uzyskać indeks umieszczenia:

// Pseudo Code
const placementIndex = randomNumber % P;

Schemat blokowy obliczeń o weryfikowalnej uczciwości gry Mines od Cloudbet: losowe bajty i próbkowanie odrzucające wyznaczają indeks pozycji każdej miny.

Jeśli pozostały jeszcze miny do rozmieszczenia, wybrana pozycja jest usuwana z listy, P zmniejsza się o 1, a proces się powtarza.

Rozgrywka

Ostateczna siatka wypełniona minami jest budowana i ukrywana na serwerze, zanim wykonasz jakikolwiek ruch — nie można jej później zmienić ani zmodyfikować niezauważenie, dzięki wcześniej udostępnionemu Zobowiązaniu. Każde wybrane przez Ciebie pole jest po prostu sprawdzane względem tej ukrytej siatki.

Kod do niezależnej weryfikacji

Ten program jest zgodny z implementacją Mines na Cloudbet. Wprowadź swój Seed serwera, Seed klienta, Nonce oraz liczbę min, zainstaluj jedną zależność i uruchom go samodzielnie, aby odtworzyć wynik swojej gry.

Uwaga: aby ujawnić Seed serwera zakończonej gry na Cloudbet, musisz już rozpocząć nową rundę (co powoduje rotację na nową parę seedów).

Zależności:

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 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 myNumberOfMines   = 3;                  // ✍️YOUR_INPUT✍️: Enter your number of mines 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); // shaker256 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: Generate the mine locations
function pickUniquePositions(mineCount, totalTiles, rng) {
  const available = Array.from({ length: totalTiles }, (_, i) => i);
  let result = [];

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

    result.push(available[index]);
    available.splice(index, 1);         // Remove chosen position from list of available positions
  }
  result.sort((a, b) => a - b);         // Sort the mine positions from smallest to largest
  result = result.map(num => num + 1);  // Increment positions by one so that they start with 1, 2, 3, ...
  return result;
}
// 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() {
  const gridSize = 25;
  let mineCount = 3;
  let nonce = 1;
  if (usingCustomData) { mineCount = myNumberOfMines; nonce = myNonce; }

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

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = 24 * 2; // There are max. 24 mines, however due to rejection sampling we may need more random numbers
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  const minePositions = pickUniquePositions(mineCount, gridSize, 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('💣 Mine Positions (1 = top-left square, 5 = top-right square, 25 = bottom-right square):', minePositions);
})();

Wypróbuj teraz: sprawdź swoje szczęście w Mines

Chcesz zobaczyć to na żywo? Zagraj rundę Cloudbet Mines — a następnie zweryfikuj swoje wyniki za pomocą kalkulatora provably fair. Zajmuje to kilka sekund. Nie jest wymagana żadna wiedza techniczna.

Zobaczysz cały proces w akcji, sprawdzisz swoje seedy i przekonasz się, jak narzędzie odtwarza Twój wynik od podstaw.

Więcej wyjaśnień provably fair dla poszczególnych gier:

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