Så fungerar provably fair i Mines

Så här spelar du Mines på Cloudbet, och så kontrollerar du att varje omgångs minplaceringar är provably fair.

Så spelar du Mines

Varje Mines-omgång på Cloudbet bygger på samma provably fair-grund — server seed, client seed, nonce och SHA3-256/SHAKE256 — som beskrivs i detalj på vår huvudsida om hur provably fair fungerar. Den här sidan handlar om det som är specifikt för Mines: hur den slumpmässiga talföljden blir det faktiska rutnät av minor du spelar mot.

Så blir slumptal ett rutnät av minor

Du väljer antalet minor som ska placeras ut i början av spelet — vi kallar det talet N. Mines 5×5-rutnät kan plattas ut till en enda rad med 25 positioner, var och en med sitt eget index, med start på 1:

Diagram som visar hur Cloudbets bevisbart rättvisa Mines-rutnät — 5×5 rutor märkta A till Y — plattas ut till en enda rad med 25 numrerade positioner för rättvis talgenerering.

Vi hämtar ett tal från den slumpmässiga talföljden (genererad på samma sätt som beskrivs på sidan om provably fair) för att avgöra positionen för den första minan:

Praktiskt exempel på Cloudbets bevisbart rättvisa Mines-algoritm: ett slumptal väljer positionen för den första minan bland alla 25 rutor.

Om fler minor behöver placeras drar vi ett nytt slumptal och upprepar processen — men nu finns bara 24 kvarvarande platser att välja bland:

Praktiskt exempel som visar hur Mines bevisbart rättvisa algoritm väljer nästa mina bland de 24 återstående positionerna efter att den första minan tagits bort från poolen.

Detta upprepas tills alla minor är placerade. Hela rutnätet återskapas sedan och hålls hemligt fram till dess att omgången är slut:

Det slutgiltiga bevisbart rättvisa Mines-rutnätet, återskapat från den utplattade raden när varje minposition har placerats slumpmässigt och verifierbart.

Tekniska detaljer: val av varje minposition

Inför varje val vet vi P — antalet minfria positioner kvar att välja bland. P börjar på 25 och minskar med 1 varje gång en mina placeras ut.

Ett slumpmässigt 4-byte (32-bitars) tal hämtas från SHAKE256-utdatan för varje mina, med värden från 0 till 4 294 967 295. För att välja rättvist bland P positioner tillämpar vi rejection sampling (se det allmänna konceptet förklarat på Så fungerar provably fair), och tar sedan resultatet modulo P för att få ett placeringsindex:

// Pseudo Code
const placementIndex = randomNumber % P;

Flödesschema över Cloudbets bevisbart rättvisa Mines-beräkning: slumpmässiga byte och rejection sampling avgör indexet för varje minas position.

Om fler minor återstår tas den valda positionen bort från listan, P minskar med 1, och processen upprepas.

Spelupplägg

Det färdiga minfyllda rutnätet skapas och döljs på servern innan du gör ditt första drag — det kan inte ändras eller manipuleras oupptäckt i efterhand, tack vare den Commitment som redan delats med dig. Varje ruta du väljer kontrolleras helt enkelt mot detta dolda rutnät.

Kod för oberoende verifiering

Det här programmet motsvarar Mines-implementationen på Cloudbet exakt. Ange din Server Seed, Client Seed, Nonce och antal minor, installera det enda beroendet och kör det själv för att återskapa ditt spels resultat.

Obs: för att avslöja Server Seed för ett avslutat spel på Cloudbet måste du redan ha startat en ny omgång (vilket roterar 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.
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);
})();

Prova nu: testa lyckan på Mines

Vill du se det live? Spela en omgång Cloudbet Mines — och verifiera sedan dina resultat med provably fair-kalkylatorn. Det tar bara några sekunder. Ingen teknisk kunskap krävs.

Du får se processen i praktiken, granska dina seeds och se hur verktyget återskapar ditt resultat från grunden.

Fler förklaringar av provably fair per spel:

  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