Pierādāmi godīgi
Pārbaudi pats katru izdalīšanu, katru metienu un katru vietu sadali.
Mūsu galda spēles ir prasmju spēles: sēkla nosaka tikai jaukšanu, kauliņus un to, kurš kur sēž. Katrs gājiens pēc tam ir tavs. Pirms kaut kas tiek izdalīts, serveris apņemas izmantot slepenu sēklu, publicējot tās SHA-256 hešu. Tava paša klienta sēkla tiek iejaukta, tāpēc serveris nevar izvēlēties izdalīšanu pēc tās ieraudzīšanas. Kad sēkla ir atklāta, ikviens var pārrēķināt katru kārti, katru kauliņu un katru plāksnīti.
Kā tas darbojas
- Saistība: serveris izvēlas slepenu sēklu (32 nejaušus baitus) un parāda tev tās SHA-256 hešu pirms pirmās kārts vai pirmā metiena.
- Tava sēkla: tava pārlūkprogramma pievieno klienta sēklu (nejaušu vai tavu saglabāto). To nosūta tikai pēc tam, kad hešs ir parādīts.
- Skaitļi: katrs nejaušais skaitlis ir HMAC-SHA256(servera sēkla, ziņojums ar visām klientu sēklām un skaitītāju). Tā pirmie 52 biti veido vienu skaitli.
- Atklāšana: pēc spēles servera sēkla tiek publicēta. Tās SHA-256 hešam jāsakrīt ar saistību, un pārrēķinam jādod tieši tās izdalīšanas, kas tika izspēlētas.
No skaitļiem līdz kārtīm, kauliņiem un vietām
52 bitu skaitlis tiek pārvērsts veselā skaitlī, kas mazāks par n, ar noraidīšanas izlasi: skaitlis, kas iekrīt diapazona 2^52 augšējā nepilnajā blokā, tiek izlaists un tiek izmantots nākamais skaitītājs, tāpēc katrs rezultāts ir tieši vienlīdz varbūtīgs. Jaukšana ir Fisher–Yates no augšas: katram i no pēdējās pozīcijas uz leju līdz 1 izvēlas j, kas mazāks par i + 1, un abus samaina vietām.
Spēle pa spēlei
- Bekgemons: sākuma metiens un abi katra gājiena kauliņi ir int(6) + 1, gājienu secībā.
- Dambrete un Xiangqi: nav ne kauliņu, ne kāršu. Sēkla nosaka, kurš sāk (vietu secību).
- Tarneeb, Trix un Bridžs: viena 52 kāršu kavas Fisher–Yates jaukšana katrā izdalīšanā; vieta i saņem kārtis no 13i līdz 13i + 12.
- Baloot: viena 32 kāršu kavas jaukšana katrā partijā; atlikusī kavas daļa otrajai izdalīšanai tiek sajaukta vēlreiz.
- Madžongs: katra paņemtā plāksnīte ir int(atlikušās plāksnītes) no sienas tās noteiktajā secībā — jaukšana pa vienai plāksnītei.
- Pokers: katrs galds darbojas sēklas sesijā. Pirms katras izdalīšanas galds uzņemas saistību par kavu: Merkle sakne pār tās 52 pozīcijām, kur katra pozīcija ir kārts kopā ar savu sāli. Kad tu atstāj galdu, tu saņem savas kārtis, kopīgās kārtis un to Merkle pierādījumus, lai tu varētu tās pārbaudīt pret šo saistību; citu spēlētāju kārtis netiek atklātas. Pilnā kava un sēkla tiek publicēta, kad sesija mainās: ik pēc 24 stundām, pēc 500 rokām, kad galds kļūst tukšs vai kad serveris tiek restartēts.
- Šahs: tavam savienojumam ir apņemta sēkla jau pirms tam, kad tu meklē spēli, izaicini kādu vai sāc spēli pret botu. Krāsas nosaka abu spēlētāju sēklas un klientu sēklas.
Mači, kas izspēlēti pirms šīs shēmas (match_seed_v1), joprojām ir pārbaudāmi: pārbaudītājs izvēlas shēmu pēc ieraksta.
Pārbaudīt
Ielīmē mača ierakstu (to aizpilda poga Pārbaudīt tavās spēlēs). Katra izdalīšana un katrs metiens tiek atkārtots pēc pašas spēles noteikumiem un salīdzināts ar to, kas tika izspēlēts.
Ievadi mača sēklas, lai redzētu, ko tās izdala sākumā.
Ievadi rokas sēklas, lai pārrēķinātu visu tās kavu, vai ielīmē kāršu pierādījumus, ko saņēmi, atstājot galdu, lai pārbaudītu savas kārtis un kopīgās kārtis.
Ievadi abu pušu sēklas, lai pārrēķinātu, kurš spēlē ar baltajiem.
Puse A (meklēšanas vai izaicinājuma autors)
Puse B
Pārbaudi pats
// 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
Šīs lapas pārbaudītājs darbojas tavā pārlūkprogrammā un nesazinās ar serveri. Lejupielādē to, lai palaistu bezsaistē: verifier.js