SnapEX Games: доказуемая честность

Как результат каждого раунда фиксируется до ставки, формулы всех шести игр и проверка, которая пересчитает любой раунд прямо в браузере.

Как это работает

  1. До ставки вы видите SHA-256 хэш текущего серверного сида (64 случайных hex-символа). С этого момента сид зафиксирован и измениться не может.
  2. Результат зависит и от вашего клиентского сида. Его можно сменить в любой момент; хэш следующего серверного сида публикуется до того, как вы выберете для него клиентский сид.
  3. Каждый раунд берёт следующий nonce вашей пары сидов: 0, 1, 2… Одна пара — на все шесть игр.
  4. Когда вы меняете пару (вкладка «Provably fair» любой игры), серверный сид раскрывается. Сверьте его SHA-256 с хэшем, который видели, и пересчитайте любой раунд на нём — ниже или своим скриптом.

В «Моих ставках» на странице каждой игры у каждого раунда показаны хэш серверного сида, клиентский сид и nonce.

Проверить раунд

Проверка идёт в вашем браузере (Web Crypto), а не на нашем сервере. В полях — пример пары, это не раунд игрока.

Случайные числа

Каждая игра берёт из одного потока столько чисел, сколько ей нужно:

hash(server_seed) = SHA256(server_seed)            -- server_seed is a 64-char hex string, hashed as text

block[cursor] = HMAC_SHA256(key = server_seed (as text),
                            message = client_seed + ":" + nonce + ":" + cursor)   -- cursor = 0, 1, 2...
words = each block split into 8 big-endian uint32 values, blocks taken in order
f[i] = word[i] / 2^32                               -- 0 <= f < 1
scale(word, n) = floor(word * n / 2^32)             -- integer from 0 to n-1

Формулы игр

Dice 1 число · RTP 99%

roll = floor(word[0] * 10001 / 2^32) / 100          -- 0.00 ... 100.00
"Under X" wins if roll < X, chance = X%
"Over X"  wins if roll > X, chance = (100 - X)%
payout = bet * 99 / chance                          -- chance from 0.02% to 98%

Crash 1 число · RTP 99%

crash = max(1.00, floor(99 * 2^32 / (2^32 - word[0])) / 100)
      = max(1.00, floor(99 / (1 - f[0])) / 100)
P(crash >= x) = 0.99 / x  ->  99% RTP for any cash-out target

Раунд останавливается сам на ×10 000 (или там, где выплата при вашей ставке доходит до максимума раунда $10 000).

Mines 24 числа · RTP 99%

pool = [0, 1, ..., 24]
for i = 0 ... 23:  take pool[ scale(word[i], 25 - i) ] out of the pool
the last remaining tile is the 25th
mines = the first N tiles taken
multiplier after k safe tiles = floor(0.99 * C(25, k) / C(25 - N, k) * 100) / 100

Клетки пронумерованы 0–24 слева направо, сверху вниз.

Plinko 8 / 12 / 16 чисел · RTP 99%

for each row r:  word[r] >= 2^31  ->  right,  otherwise left
bin = number of "right" bounces (0 ... rows)
payout = bet * table[rows][risk][bin]

Девять таблиц (множитель каждой лунки слева направо):

8 · низкий 5.6 2.1 1.1 1 0.5 1 1.1 2.1 5.6
8 · средний 13 3 1.3 0.7 0.4 0.7 1.3 3 13
8 · высокий 29 4 1.5 0.3 0.2 0.3 1.5 4 29
12 · низкий 10 3 1.6 1.4 1.1 1 0.5 1 1.1 1.4 1.6 3 10
12 · средний 33 11 4 2 1.1 0.6 0.3 0.6 1.1 2 4 11 33
12 · высокий 170 24 8.1 2 0.7 0.2 0.2 0.2 0.7 2 8.1 24 170
16 · низкий 16 9 2 1.4 1.4 1.2 1.1 1 0.5 1 1.1 1.2 1.4 1.4 2 9 16
16 · средний 110 41 10 5 3 1.5 1 0.5 0.3 0.5 1 1.5 3 5 10 41 110
16 · высокий 1000 130 26 9 4 2 0.2 0.2 0.2 0.2 0.2 2 4 9 26 130 1000

Рулетка 1 число · RTP 97.3%

number = scale(word[0], 37)                         -- 0 ... 36, each 1/37
pays (stake included): number x36, dozen / column x3, red-black / even-odd / 1-18 / 19-36 x2
zero loses every bet except a bet on 0

Однорукий бандит 3 числа · RTP 96%

stop[i] = scale(word[i], 32)                        -- i = 0, 1, 2; each reel strip has 32 symbols
window of reel i = strip[i][stop-1], strip[i][stop], strip[i][stop+1]   (wrapping around)
lines: middle, top, bottom, two diagonals
payout = bet / 5 * sum of line pays

Ленты барабанов (стоп — индекс от 0) и выплаты линий:

Барабан 1 0:melon 1:lemon 2:bell 3:melon 4:orange 5:cherry 6:seven 7:lemon 8:orange 9:lemon 10:grapes 11:gem 12:melon 13:orange 14:melon 15:cherry 16:grapes 17:bell 18:cherry 19:bell 20:lemon 21:orange 22:grapes 23:cherry 24:orange 25:cherry 26:grapes 27:gem 28:cherry 29:lemon 30:cherry 31:lemon
Барабан 2 0:cherry 1:grapes 2:gem 3:bell 4:lemon 5:cherry 6:seven 7:cherry 8:orange 9:melon 10:grapes 11:lemon 12:grapes 13:cherry 14:lemon 15:cherry 16:bell 17:orange 18:bell 19:orange 20:melon 21:orange 22:cherry 23:lemon 24:melon 25:lemon 26:gem 27:grapes 28:cherry 29:orange 30:lemon 31:melon
Барабан 3 0:melon 1:cherry 2:lemon 3:cherry 4:lemon 5:grapes 6:cherry 7:bell 8:gem 9:orange 10:cherry 11:grapes 12:melon 13:lemon 14:melon 15:grapes 16:cherry 17:orange 18:gem 19:bell 20:lemon 21:orange 22:melon 23:orange 24:lemon 25:bell 26:lemon 27:cherry 28:seven 29:cherry 30:orange 31:grapes

Выплаты линий (единицы; выплата = ставка ÷ 5 × сумма линий)

  • Семёрка × 3 — 1 000
  • Алмаз × 3 — 150
  • Колокол × 3 — 80
  • Арбуз × 3 — 40
  • Виноград × 3 — 30
  • Апельсин × 3 — 25
  • Лимон × 3 — 20
  • Вишня × 3 — 15
  • Любые три из колокол / алмаз / семёрка — 15
  • Вишня + вишня на барабанах 1–2 — 6

Линии: средняя, верхняя, нижняя и две диагонали.

Проверьте сами (Python 3, без библиотек)

import hmac, hashlib, struct

def words(server_seed, client_seed, nonce, count):
    out, cursor = [], 0
    while len(out) < count:
        msg = f"{client_seed}:{nonce}:{cursor}".encode()
        out += struct.unpack(">8I", hmac.new(server_seed.encode(), msg, hashlib.sha256).digest())
        cursor += 1
    return out[:count]

def scale(w, n): return w * n // 2**32

def mines(w, n):
    pool, order = list(range(25)), []
    for i in range(24): order.append(pool.pop(scale(w[i], 25 - i)))
    return sorted(order[:n])

s, c, n = "7e347011f1144055ee29838050723a5663fa660519a1f039aca9ec17924bcbf7", "80ca3b044457", 329
w = words(s, c, n, 24)
print("hash    ", hashlib.sha256(s.encode()).hexdigest())
print("dice    ", scale(w[0], 10001) / 100)
print("crash   ", max(100, 99 * 2**32 // (2**32 - w[0])) / 100)
print("roulette", scale(w[0], 37))
print("plinko16", sum(x >= 2**31 for x in w[:16]))
print("mines(3)", mines(w, 3))
print("slots   ", [scale(x, 32) for x in w[:3]])

Для пары из примера скрипт печатает те же результаты, что и проверка выше.

Возврат игроку (RTP)

Mines 99%, Crash 99%, Dice 99% (точно 9900 / 10001 = 98,99%), Plinko 98,9–99,1% в зависимости от таблицы, Roulette 97,3% (европейская, одно зеро), Slots 96,07%.