Equità dimostrabile
Verifica tu stesso ogni smazzata, ogni lancio e ogni assegnazione dei posti.
I nostri giochi da tavolo sono giochi di abilità: il seed decide solo il mescolamento, i dadi e chi siede dove. Ogni mossa successiva è tua. Prima di distribuire qualsiasi cosa, il server si impegna su un seed segreto pubblicandone l'hash SHA-256. Il tuo seed client viene mescolato al suo, così il server non può scegliere una smazzata dopo averlo visto. Una volta rivelato il seed, chiunque può ricalcolare ogni carta, ogni dado e ogni tessera.
Le tue partite e il tuo seed client
Come funziona
- Impegno: il server sceglie un seed segreto (32 byte casuali) e ti mostra il suo hash SHA-256 prima della prima carta o del primo dado.
- Il tuo seed: il tuo browser aggiunge un seed client (casuale o quello che hai salvato). Viene inviato solo dopo che l'hash è stato mostrato.
- Estrazione: ogni numero casuale è HMAC-SHA256(seed del server, un messaggio con tutti i seed client e un contatore). I suoi primi 52 bit formano un numero.
- Rivelazione: dopo la partita il seed del server viene pubblicato. Il suo hash SHA-256 deve corrispondere all'impegno e il ricalcolo deve restituire esattamente le smazzate giocate.
Dai numeri a carte, dadi e posti
Un numero a 52 bit diventa un intero minore di n tramite campionamento per rifiuto: un numero che cade nel blocco parziale superiore dell'intervallo 2^52 viene scartato e si usa il contatore successivo, così ogni risultato è esattamente equiprobabile. Un mescolamento è Fisher–Yates dall'alto: per i dall'ultima posizione fino a 1, si sceglie j minore di i + 1 e si scambiano i due.
Gioco per gioco
- Backgammon: il lancio d'apertura ed entrambi i dadi di ogni turno sono int(6) + 1, in ordine di turno.
- Dama e Xiangqi: niente dadi e niente carte. Il seed stabilisce chi muove per primo (l'ordine dei posti).
- Tarneeb, Trix e Bridge: un mescolamento Fisher–Yates del mazzo da 52 carte per smazzata; il posto i riceve le carte da 13i a 13i + 12.
- Baloot: un mescolamento del mazzo da 32 carte per mano; il resto del mazzo viene rimescolato per la seconda distribuzione.
- Mahjong: ogni tessera pescata è int(tessere rimaste) dal muro nel suo ordine fisso, un mescolamento fatto una tessera alla volta.
- Poker: ogni tavolo usa una sessione di seed. Prima di ogni smazzata il tavolo si impegna sul mazzo: una radice di Merkle sulle sue 52 posizioni, dove ogni posizione è una carta più il suo sale. Quando lasci il tavolo ricevi le tue carte private, le carte comuni e le relative prove di Merkle, così puoi verificarle rispetto a quell'impegno; le carte degli altri giocatori non vengono rivelate. Il mazzo completo e il seed vengono pubblicati quando la sessione ruota: ogni 24 ore, dopo 500 mani, quando il tavolo si svuota o quando il server si riavvia.
- Scacchi: la tua connessione detiene un seed impegnato prima che tu cerchi una partita, lanci una sfida o inizi una partita contro un bot. I colori derivano dai seed e dai seed client di entrambi i giocatori.
Le partite giocate prima di questo schema (match_seed_v1) restano verificabili: il verificatore sceglie lo schema in base al registro.
Verifica
Incolla un registro di partita (il pulsante Verifica nelle tue partite lo compila). Ogni smazzata e ogni lancio vengono rigiocati con le regole del gioco e confrontati con quanto giocato.
Inserisci i seed di una partita per vedere cosa distribuiscono all'inizio.
Inserisci i seed di una mano per ricalcolarne l'intero mazzo, oppure incolla le prove delle carte ricevute quando hai lasciato il tavolo per verificare le tue carte e le carte comuni.
Inserisci i seed di entrambi i lati per ricalcolare chi gioca con il Bianco.
Lato A (autore della ricerca o della sfida)
Lato B
Verifica tu stesso
// Node.js 18+ — check a table-game match yourself (scheme match_seed_v2)
const { createHash, createHmac } = require('crypto');
const sha256 = (s) => createHash('sha256').update(s).digest('hex');
const word = (key, msg) => BigInt('0x' + createHmac('sha256', key).update(msg).digest('hex').slice(0, 13));
console.log(sha256(serverSeed) === commitment); // 1. the reveal matches the commitment
let k = 0; // 2. the draw stream: one counter per match
const seeds = clientSeeds.map((s, i) => i + '=' + s).join(',');
const next = () => word(serverSeed, 'pf2:' + game + ':' + matchId + ':' + seeds + ':' + (k++));
const SPAN = 1n << 52n;
function int(n) { // 3. whole number below n, no modulo bias
const N = BigInt(n), limit = SPAN - (SPAN % N);
for (;;) { const w = next(); if (w < limit) return Number(w % N); }
}
const die = () => int(6) + 1;
function shuffle(a) { // 4. Fisher–Yates from the top
a = a.slice();
for (let i = a.length - 1; i > 0; i--) { const j = int(i + 1); [a[i], a[j]] = [a[j], a[i]]; }
return a;
}
// seat order (pairings): shuffle([0, 1]) first, then the game's opening deal / roll
// Poker (scheme poker_hmac_v1): hand n of seed session S at table T
const handValue = createHmac('sha256', serverSeed).update('pk1:hand:' + T + ':' + S + ':' + n).digest('hex');
console.log(sha256(handValue) === handCommitment); // stored before the deal
// deck = shuffle([0..51]); key = handValue; message = pk1:T:S:n:{seat}={seed},…:{k}; seats ↑
// rank = id % 13 → 23456789TJQKA; suit = Math.floor(id / 13) → c d h s
// deck commitment (before the deal): salt_i = HMAC(handValue, 'pk1:salt:' + i); leaf_i = sha256('pk1:leaf:' + i + ':' + card_i + ':' + salt_i)
// 64 leaves (52..63 = sha256('pk1:pad')), parent = sha256('pk1:node:' + left + right) → root; on leave you get your leaves + paths
// Chess colour (scheme chess_colour_v1): A = owner of the seek / challenge, B = the other side (a bot adds no seed)
const m = (k) => 'cc1:' + commitA + ':' + (commitB || '-') + ':0=' + clientA + ',1=' + clientB + ':' + k;
// w = word(seedA, m(k)) XOR word(seedB, m(k)); int(2) over w as above → 0 means A plays White
Il verificatore di questa pagina gira nel tuo browser e non effettua chiamate al server. Scaricalo per eseguirlo offline: verifier.js