So funktioniert Provably Fair bei Mines

So spielst du Mines auf Cloudbet, und so überprüfst du, dass die Minenplatzierung jeder Runde provably fair ist.

So spielst du Mines

Jede Mines-Runde auf Cloudbet basiert auf demselben Provably-Fair-Fundament — Server-Seed, Client-Seed, Nonce und SHA3-256/SHAKE256 —, das ausführlich auf unserer Hub-Seite „Wie Provably Fair funktioniert" beschrieben wird. Diese Seite behandelt, was bei Mines spezifisch ist: wie diese Zufallszahlenfolge zum tatsächlichen Minenraster wird, gegen das du spielst.

Zufallszahlen werden zu einem Minenraster

Du wählst zu Beginn des Spiels die Anzahl der zu platzierenden Minen — wir nennen diese Zahl N. Das 5×5-Mines-Raster lässt sich zu einer einzigen Reihe mit 25 Positionen abflachen, jede mit ihrem eigenen Index, beginnend bei 1:

Diagramm, das zeigt, wie Cloudbets nachweisbar faires Mines-Raster — 5×5 Felder mit den Bezeichnungen A bis Y — zu einer einzigen Reihe aus 25 nummerierten Positionen für eine faire Zahlengenerierung abgeflacht wird.

Wir ziehen eine Zahl aus der Zufallsfolge (auf dieselbe Weise generiert, wie auf der Provably-Fair-Seite beschrieben), um die Position der ersten Mine zu bestimmen:

Praktisches Beispiel für Cloudbets nachweisbar fairen Mines-Algorithmus: Eine Zufallszahl wählt die Position der ersten Mine aus allen 25 Feldern aus.

Müssen weitere Minen platziert werden, ziehen wir eine weitere Zufallszahl und wiederholen den Vorgang — nur stehen jetzt noch 24 verbleibende Felder zur Auswahl:

Praktisches Beispiel, das zeigt, wie der nachweisbar faire Algorithmus von Mines die nächste Mine aus den 24 verbleibenden Positionen auswählt, nachdem die erste Mine aus dem Pool entfernt wurde.

Das wiederholt sich, bis jede Mine platziert ist. Das vollständige Raster wird dann rekonstruiert und bis zum Ende der Runde geheim gehalten:

Das finale, nachweisbar faire Mines-Raster, rekonstruiert aus der abgeflachten Reihe, nachdem jede Minenposition zufällig und überprüfbar platziert wurde.

Technische Details: Auswahl jeder Minenposition

Vor jeder Ziehung kennen wir P — die Anzahl der minenfreien Positionen, die noch zur Auswahl stehen. P startet bei 25 und sinkt bei jeder platzierten Mine um 1.

Für jede Mine wird aus dem SHAKE256-Output eine zufällige 4-Byte-(32-Bit-)Zahl gezogen, im Bereich von 0 bis 4.294.967.295. Um fair unter P Positionen auszuwählen, wenden wir Rejection Sampling an (das allgemeine Konzept wird auf Wie Provably Fair funktioniert erklärt), und nehmen dann das Ergebnis modulo P, um einen Platzierungsindex zu erhalten:

// Pseudo Code
const placementIndex = randomNumber % P;

Flussdiagramm der nachweisbar fairen Mines-Berechnung von Cloudbet: zufällige Bytes und Rejection Sampling bestimmen den Index jeder Minenposition.

Sind weitere Minen übrig, wird die gewählte Position aus der Liste entfernt, P sinkt um 1, und der Vorgang wiederholt sich.

Spielablauf

Das fertige, minenbestückte Raster wird auf dem Server erstellt und verborgen, bevor du deinen ersten Zug machst — es kann danach nicht unbemerkt geändert oder verändert werden, dank des Commitments, das dir bereits mitgeteilt wurde. Jedes Feld, das du auswählst, wird einfach mit diesem verborgenen Raster abgeglichen.

Code zur unabhängigen Verifizierung

Dieses Programm entspricht exakt der Mines-Implementierung auf Cloudbet. Gib deinen Server-Seed, Client-Seed, Nonce und die Anzahl der Minen ein, installiere die eine Abhängigkeit, und führe es selbst aus, um das Ergebnis deines Spiels zu rekonstruieren.

Hinweis: Um den Server-Seed einer abgeschlossenen Runde auf Cloudbet offenzulegen, musst du bereits eine neue Runde gestartet haben (wodurch zu einem neuen Seed-Paar rotiert wird).

Abhängigkeiten:

npm install js-sha3

Der Code:

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

Jetzt ausprobieren: Teste dein Glück bei Mines

Möchtest du es live erleben? Spiele eine Runde Cloudbet Mines — und überprüfe anschließend deine Ergebnisse mit dem Provably-Fair-Rechner. Es dauert nur wenige Sekunden. Kein technisches Know-how erforderlich.

Du wirst den Prozess in Aktion sehen, deine Seeds prüfen und beobachten, wie das Tool dein Ergebnis von Grund auf rekonstruiert.

Weitere Provably-Fair-Spielerklärungen:

  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