Todistettavasti reilu

Tarkista itse jokainen jako, heitto ja paikkojen jako.

Pöytäpelimme ovat taitopelejä: siemen ratkaisee vain sekoituksen, nopat ja sen, kuka istuu missäkin. Jokainen siirto sen jälkeen on sinun. Ennen kuin mitään jaetaan, palvelin sitoutuu salaiseen siemeneen julkaisemalla sen SHA-256-tiivisteen. Oma asiakassiemenesi sekoitetaan mukaan, joten palvelin ei voi valita jakoa nähtyään sen. Kun siemen on paljastettu, kuka tahansa voi laskea uudelleen jokaisen kortin, nopan ja laatan.

Pelisi ja asiakassiemenesi

Näin se toimii

  1. Sitoumus: palvelin valitsee salaisen siemenen (32 satunnaista tavua) ja näyttää sinulle sen SHA-256-tiivisteen ennen ensimmäistä korttia tai noppaa.
  2. Sinun siemenesi: selaimesi lisää asiakassiemenen (satunnaisen tai tallentamasi). Se lähetetään vasta, kun tiiviste on näytetty.
  3. Luvut: jokainen satunnaisluku on HMAC-SHA256(palvelimen siemen, viesti, jossa ovat kaikki asiakassiemenet ja laskuri). Sen ensimmäiset 52 bittiä muodostavat yhden luvun.
  4. Paljastus: pelin jälkeen palvelimen siemen julkaistaan. Sen SHA-256-tiivisteen on vastattava sitoumusta, ja uudelleenlaskennan on tuotettava täsmälleen pelatut jaot.

Luvuista korteiksi, nopiksi ja paikoiksi

52-bittinen luku muutetaan n:ää pienemmäksi kokonaisluvuksi hylkäysotannalla: välin 2^52 ylimpään vajaaseen lohkoon osuva luku ohitetaan ja käytetään seuraavaa laskuria, joten jokainen tulos on täsmälleen yhtä todennäköinen. Sekoitus on Fisher–Yates ylhäältä: kun i kulkee viimeisestä paikasta alas 1:een, valitaan j, joka on pienempi kuin i + 1, ja nämä kaksi vaihdetaan keskenään.

Peli peliltä

  • Backgammon: aloitusheitto ja jokaisen vuoron molemmat nopat ovat int(6) + 1 vuorojärjestyksessä.
  • Tammi ja Xiangqi: ei noppia eikä kortteja. Siemen ratkaisee, kuka aloittaa (paikkajärjestys).
  • Tarneeb, Trix ja Bridge: yksi 52 kortin pakan Fisher–Yates-sekoitus jakoa kohden; paikka i saa kortit 13i:stä korttiin 13i + 12.
  • Baloot: yksi 32 kortin pakan sekoitus kättä kohden; pakan loput sekoitetaan uudelleen toista jakoa varten.
  • Mahjong: jokainen nostettu laatta on int(laattoja jäljellä) muurista sen kiinteässä järjestyksessä – sekoitus laatta kerrallaan.
  • Pokeri: jokainen pöytä käyttää siemenistuntoa. Ennen jokaista jakoa pöytä sitoutuu pakkaan: Merkle-juuri sen 52 paikan yli, ja jokainen paikka on kortti sekä sen oma suola. Kun poistut pöydästä, saat omat taskukorttisi, yhteiset kortit ja niiden Merkle-todisteet, joten voit tarkistaa ne tätä sitoumusta vasten; muiden pelaajien kortteja ei paljasteta. Koko pakka ja siemen julkaistaan, kun istunto vaihtuu: 24 tunnin välein, 500 käden jälkeen, kun pöytä tyhjenee tai kun palvelin käynnistyy uudelleen.
  • Shakki: yhteydelläsi on sitoutettu siemen jo ennen kuin haet peliä, haastat jonkun tai aloitat pelin bottia vastaan. Värit määräytyvät molempien pelaajien siemenistä ja asiakassiemenistä.

Ennen tätä menetelmää pelatut ottelut (match_seed_v1) ovat yhä tarkistettavissa: tarkistin valitsee menetelmän tallenteen perusteella.

Tarkista

Liitä ottelun tallenne (peliesi Tarkista-painike täyttää sen). Jokainen jako ja heitto toistetaan pelin omien sääntöjen mukaan ja verrataan siihen, mitä pelattiin.

Tarkista itse

// 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

Tämän sivun tarkistin toimii selaimessasi eikä ota yhteyttä palvelimeen. Lataa se käyttääksesi sitä ilman verkkoyhteyttä: verifier.js