O quarto input
Toda explicação de jogo provably fair, incluindo a nossa, cita três inputs: uma server seed com a qual a casa se compromete previamente, uma client seed que você pode definir você mesmo, e um nonce que aumenta em um a cada aposta. Alimente-os em um hash com chave e sai a aleatoriedade da rodada. Essa descrição é completa para um coin flip. É incompleta para quase tudo mais, porque um hash produz um número e a maioria dos jogos precisa de vários.
O quarto input é o cursor. Neste site a mensagem que é hasheada não é “client seed-nonce”, mas “client seed-nonce-cursor”, e o cursor é um contador dentro da rodada: 0 para o primeiro número aleatório que o jogo precisa, 1 para o segundo, e assim por diante. Cada valor de cursor gera um digest de 256 bits novo, sem relação alguma com seus vizinhos de forma que você pudesse explorar, e o engine lê os primeiros oito bytes de cada digest como um número entre 0 e 1.
VERIFICADO PELO ENGINE_shared/demoLocal.ts: uAt(pair, cursor) retorna u64(HMAC-SHA256(serverSeed, `${clientSeed}-${nonce}-${cursor}`)), descrito como correspondente ao RandomUtils.generateHash(“clientSeed-nonce-cursor”) do servidor. _shared/rng.ts: u64 lê os primeiros oito bytes do digest como hi/2^32 + lo/2^64, “a mesma construção de arredondamento único” da conversão ULong→Double do servidor. O nonce é um stream único por par de seeds em todos os jogos: cada aposta consome o nonce atual e armazena nonce + 1.
Então uma rodada não é um único número aleatório, mas uma lista numerada deles, tão longa quanto o jogo precisar, e cada item da lista já está fixado no instante em que o par de seeds e o nonce são definidos. O jogo não sorteia conforme avança. Ele lê uma lista que já existe.
O que cada jogo lê
A parte interessante é que a lista é diferente para cada jogo, e cada módulo de derivação declara seu layout em seu cabeçalho. Aqui está o stream de cursores de cada original que se baseia no par de seeds, retirado do código-fonte do engine.
| JOGO | CURSORES LIDOS | NO QUE SE TORNAM |
|---|---|---|
| Limbo | 0 | Um número através da curva 0.99 ÷ (1 − u) |
| Roleta | 0 | O bolso: floor(u × 37) |
| Fukubukuro | 0 | O item, contra a tabela de pesos da sacola |
| Sic Bo | 0, 1, 2 | Os três dados |
| Chinchiro | lado × 9 + tentativa × 3 + dado | Até 18: banca depois jogador, até três lances de três dados cada; um lance que forma uma mão encerra o lado |
| Koban Flip | um por virada | A virada i lê o cursor i; cada face da rodada é fixada quando a primeira moeda é comprada |
| Omikuji | 0, depois 1–3, depois 4 em diante | Nível de sorte; as três células vencedoras em uma vitória; glifos de preenchimento para o restante |
| Pagoda | um por andar | A peça armadilha em cada andar: floor(u × peças) |
| Video Poker | 5, mais um por carta trocada | Uma sequência sem reposição sobre 52 cartas; mantenha tudo e nenhum cursor adicional é lido |
| Casino Hold’em | até 9 | jogador 1, jogador 2, dealer 1, dealer 2, flop 1–3, turn, river; o turn e o river só são lidos em CALL |
| Blackjack | um por carta | Sapata infinita, carta = floor(u × 52), na ordem de distribuição: jogador, dealer visível, jogador, dealer oculto, depois cada nova compra |
| Andar Bahar | 0–4, depois um por carta disputada | Dois buckets potencializados, suas magnitudes, o coringa, depois a disputa até a repetição do rank |
| Hanabi | 15 por giro | Célula = rolo × 3 + linha; o giro grátis j lê a partir de 15 + 15j |
| Bingo | 0–24, depois 25–62 | A cartela, cinco sorteios por coluna; depois as 38 bolas |
Keno e Crash são as exceções: suas rodadas demo usam uma cadeia de hash por bilhete, em vez do nonce do par de seeds, conforme descrito abaixo. Mines e Plinko são resolvidos pelo serviço da casa; a API do mines aponta que todo o layout sai de um único nonce.
VERIFICADO PELO ENGINEComentários de cabeçalho em bingo/derive.ts (“Cursor stream: 0..24 — a cartela … 25..62 — as 38 bolas”), hanabi/engine.ts (“giro base = células 0..14 … giro grátis j = células 15+15j .. 29+15j”), blackjack/derive.ts (“carta i = floor(u × 52) no cursor i … Ordem de distribuição (cursor): player1, dealer-up, player2, dealer-hole”), vpoker/derive.ts (“as cinco primeiras são a distribuição, as substituições continuam a mesma sequência … Manter tudo e nenhum cursor adicional é lido”), holdem/derive.ts (“player1, player2, dealer1, dealer2, flop1, flop2, flop3, turn, river. Os cursores de turn/river só são consumidos em CALL”), andar/derive.ts (cursores 0–4 depois “5.. um cursor por carta disputada”), koban/derive.ts (“a virada i lê o cursor i”), sicbo/derive.ts (“cursores 0/1/2 são os três dados”), roulette/derive.ts (“o cursor 0 sozinho decide a casa: floor(u × 37)”), fuku/derive.ts (“o cursor 0 sozinho sorteia o item”), omikuji/derive.ts (cursor 0 nível, 1..3 células vencedoras, 4.. preenchimentos); demoLocal.ts rollSide (cursor de chinchiro = lado*9 + tentativa*3 + d), trapAt (pagoda: o número do andar é o cursor), demoLimboBet (uAt(p, 0)); mines/minesApi.ts (“todo o layout de minas sai deste único nonce”).
Três coisas se destacam nessa tabela. Primeiro, os jogos que distribuem cartas leem um cursor por carta em ordem fixa, e é por isso que uma mão de blackjack pode ser reproduzida a partir da sua lista de ações: sua decisão de pedir carta não cria uma carta, ela vira a próxima de uma lista que já estava completa antes da distribuição. Segundo, vários jogos param de ler antecipadamente. Uma mão de vídeo pôquer em que você mantém as cinco cartas nunca toca o cursor 5; um fold no Hold'em nunca lê o turn ou o river. Os números estavam lá; o jogo simplesmente não precisou deles. Terceiro, os dois buckets potencializados no Andar Bahar e o nível de sorte no Omikuji são sorteados antes das cartas e células, no início do stream, então os recursos especiais da rodada são fixados pelo mesmo compromisso que os elementos comuns.
Sorteio sem reposição
Um número uniforme entre 0 e 1 é fácil de transformar em uma casa da roleta: multiplique por 37 e arredonde para baixo. Um baralho é mais difícil, porque a segunda carta não pode ser a mesma que a primeira. O engine trata isso com o que seus comentários chamam de pool hop: manter uma lista das cartas ainda no pool, usar o número do cursor para escolher uma posição nela, e trocar a carta escolhida para o fim da lista, de modo que o próximo sorteio escolha entre uma carta a menos. Vídeo Pôquer, Casino Hold'em, Andar Bahar e a cartela do Bingo sorteiam dessa forma, então nenhuma carta ou número se repete dentro de uma rodada.
Blackjack é a exceção deliberada. Sua sapata é infinita: cada carta é floor(u × 52) a partir do seu próprio cursor, e a mesma carta pode aparecer duas vezes em uma mão. Essa é uma escolha de design com um custo publicado, discutido em o artigo sobre pagamento do blackjack, e é exatamente o tipo de coisa que o layout de cursores revela e que uma página de marketing não revelaria.
VERIFICADO PELO ENGINEvpoker/derive.ts: “As cartas vêm de UMA sequência sem reposição sobre o baralho de 52 cartas (pool-hop, cursor por sorteio)”. holdem/derive.ts: “UMA sequência de pool-hop sem reposição (cursor por sorteio)”. bingo/derive.ts: “cinco sorteios de pool-hop por coluna” e “as 38 bolas, pool-hops sobre 1..75”. blackjack/derive.ts: “Sapata infinita: carta i = floor(u × 52) no cursor i”.
As duas exceções
Keno e Crash não leem o nonce do par de seeds em nenhum momento no motor de demonstração. O Keno usa um esquema por bilhete: o bilhete recebe seu próprio hash aleatório e um salt, e cada uma das dez bolas é sorteada aplicando hash novamente sobre o hash anterior, pegando os bits iniciais como um número e escolhendo do pool decrescente de quarenta. O painel de fairness verifica um bilhete de keno a partir dessas duas strings, e não a partir do par de seeds. O Crash segue a convenção do bustabit, uma cadeia de hashes em que o ponto de crash de cada rodada é calculado a partir do seu próprio elo com aritmética de inteiros exata.
VERIFICADO PELO MOTORdemoLocal.ts kenoDraws: “cadeia randomHash por aposta + salt (verificável por bilhete via o modal de fairness) … Não toca o nonce do par de seeds”; o loop aplica HMAC-SHA256(salt, hash anterior) dez vezes, lê os 52 bits iniciais e escolhe o índice floor(norm × restante) de um pool de 40 números com uma troca para o final. rng.ts h52 e o comentário do crash: “ponto de crash do bustabit em centésimos: floor((n·e − h)/(e − h)), e = 2^52, n = 100 … Matemática exata com BigInt para bater com o servidor”.
Nenhuma das exceções é mais fraca que o esquema do cursor; ambas são mais antigas e mais conhecidas. O que importa para o jogador é apenas que o painel mostre qual esquema uma rodada usou, e que cada um possa ser recalculado a partir das strings que exibe.
O que isso significa na mesa
- A rodada já existe antes de você agir. Cada carta, bola e símbolo de rolo de uma rodada é uma função do par de seeds, do nonce e da posição do cursor. Pedir carta, chamar ou manter escolhe quais entradas serão lidas, não o que elas contêm.
- Suas escolhas não podem mudar a lista, e a casa também não. Uma vez que a seed do servidor está comprometida e o nonce está fixado, ninguém de nenhum dos lados pode alterar a entrada 7 sem alterar a seed, o que quebraria a fingerprint que foi mostrada a você.
- Um verificador precisa ler a mesma lista da mesma forma. Recalcular uma rodada de bingo significa derivar todos os 63 cursores na ordem publicada; recalcular um fold de Hold'em significa derivar 7. O painel de seed faz isso no seu navegador, e os layouts na tabela acima são o que ele segue.
- Uma sequência é uma lista, não um estado de espírito. Duas rodadas não compartilham nada além do par de seeds; seus nonces diferem e, portanto, cada digest difere. Esse é o mesmo ponto que o artigo sobre a mão quente faz a partir do outro ângulo.
Três entradas decidem a rodada. A quarta decide quanto tempo dura a rodada. o cursor, em uma linha
Os layouts de cursor acima são lidos dos módulos de derivação do lado cliente que o verificador do navegador e o motor de demonstração compartilham. O serviço da casa é a autoridade pagante; onde um comentário no código-fonte diz que um módulo espelha o servidor, isso é citado como está escrito, não certificado por nós.
Perguntas frequentes
O que é o cursor em uma rodada provably fair?
Um contador dentro da rodada. A mensagem hasheada com a seed do servidor é “client seed-nonce-cursor”; o cursor 0 dá o primeiro número aleatório que o jogo precisa, o cursor 1 o segundo, e assim por diante. Um jogo que precisa de apenas um número lê somente o cursor 0.
Como o cursor é diferente do nonce?
O nonce conta as rodadas e aumenta em um a cada aposta em cada original. O cursor conta os sorteios dentro de uma rodada e reinicia em 0 a cada rodada. Junto com o par de seeds, eles identificam cada número aleatório que o site já produziu.
Quantos números aleatórios uma rodada de bingo usa?
Sessenta e três: os cursores 0 a 24 organizam as 25 células da cartela e os cursores 25 a 62 sorteiam as 38 bolas, tudo sem reposição dentro de seus pools.
Pedir carta no blackjack muda a próxima carta?
Não. Cada carta é derivada do seu próprio cursor na ordem de distribuição, e a lista inteira já existe assim que o par de seeds e o nonce estão fixados. Pedir carta lê a próxima entrada; manter deixa ela não lida.
Todos os jogos usam o esquema de cursor?
Keno e Crash usam cadeias de hash em vez disso: o keno faz hash de um hash aleatório por bilhete e salt dez vezes, e o crash segue a cadeia bustabit com aritmética de inteiros exata. O painel de fairness verifica cada um a partir das strings que exibe.
FONTES & REFERÊNCIAS
- Código-fonte do engine Betkyo: _shared/demoLocal.ts (uAt, rollSide, trapAt, kenoDraws), _shared/rng.ts (u64, h52, crash point), e os módulos de derivação para bingo, hanabi, blackjack, vpoker, holdem, andar, koban, sicbo, roulette, fuku e omikuji
- RFC 2104 — HMAC: Keyed-Hashing for Message Authentication
- Esquema provably fair do Bustabit (a construção do crash-point que o engine espelha)
OS JOGOS DESTE ARTIGO
Blackjack — regras e jogo grátis →Bingo — regras e jogo grátis →Sic Bo — regras e jogo grátis →