Come funziona il provably fair in Mines
Ecco come giocare a Mines su Cloudbet, e come verificare che il posizionamento delle mine in ogni round sia provably fair.
Come giocare a Mines
Ogni round di Mines su Cloudbet si basa sulla stessa base provably fair — server seed, client seed, nonce e SHA3-256/SHAKE256 — descritta in dettaglio nella nostra pagina hub su Come funziona il Provably Fair. Questa pagina copre ciò che è specifico di Mines: come quella sequenza di numeri casuali diventa la vera e propria griglia di mine contro cui giochi.
Trasformare i numeri casuali in una griglia di mine
Scegli il numero di mine da posizionare all'inizio della partita — chiamiamo questo numero N. La griglia 5×5 di Mines può essere appiattita in un'unica riga di 25 posizioni, ciascuna con il proprio indice, a partire da 1:

Campioniamo un numero dalla sequenza casuale (generata nello stesso modo descritto nella pagina Provably Fair) per determinare la posizione della prima mina:

Se è necessario posizionare altre mine, estraiamo un altro numero casuale e ripetiamo — ma ora ci sono solo 24 spazi rimanenti tra cui scegliere:

Questo si ripete finché tutte le mine non sono posizionate. La griglia completa viene poi ricostruita e mantenuta segreta fino alla fine del round:

Dettagli tecnici: selezione di ogni posizione della mina
Prima di ogni estrazione, conosciamo P — il numero di posizioni libere da mine rimaste tra cui scegliere. P parte da 25 e diminuisce di 1 ogni volta che viene posizionata una mina.
Per ogni mina viene estratto un numero casuale di 4 byte (32 bit) dall'output di SHAKE256, compreso tra 0 e 4.294.967.295. Per scegliere in modo equo tra le P posizioni, applichiamo il rejection sampling (vedi il concetto generale spiegato in Come funziona il Provably Fair), quindi prendiamo il risultato modulo P per ottenere un indice di posizionamento:
// Pseudo Code
const placementIndex = randomNumber % P;

Se restano altre mine da posizionare, la posizione scelta viene rimossa dall'elenco, P diminuisce di 1 e il processo si ripete.
Il gioco
La griglia finale con le mine viene costruita e nascosta sul server prima che tu faccia una mossa — non può essere cambiata o alterata senza essere rilevata in seguito, grazie al Commitment già condiviso con te. Ogni casella che scegli viene semplicemente confrontata con questa griglia nascosta.
Codice per la verifica indipendente
Questo programma è fedele all'implementazione di Mines su Cloudbet. Inserisci il tuo Server Seed, Client Seed, Nonce e numero di mine, installa l'unica dipendenza necessaria ed eseguilo tu stesso per ricostruire il risultato della tua partita.
Nota: per rivelare il Server Seed di una partita completata su Cloudbet, devi aver già iniziato un nuovo round (che comporta la rotazione a 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 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);
})();
Provalo subito: metti alla prova la tua fortuna su Mines
Vuoi vederlo dal vivo? Gioca una partita a Cloudbet Mines — poi verifica i tuoi risultati con il calcolatore provably fair. Ci vogliono pochi secondi. Non serve alcuna competenza tecnica.
Potrai vedere il processo in azione, ispezionare i tuoi seed e osservare come lo strumento ricostruisce il tuo risultato da zero.
