Đầu vào thứ tư
Mọi lời giải thích về provably fair gaming, kể cả của chúng tôi, đều nêu ra ba đầu vào: server seed mà nhà cái cam kết từ trước, client seed mà bạn có thể tự đặt, và nonce tăng thêm một sau mỗi lần cược. Đưa chúng vào một hàm băm có khóa và kết quả là tính ngẫu nhiên của vòng chơi. Mô tả đó là đầy đủ đối với một lần tung đồng xu. Nhưng nó chưa đủ với gần như mọi thứ khác, vì một hàm băm chỉ cho ra một con số, trong khi hầu hết trò chơi cần nhiều hơn.
Đầu vào thứ tư là cursor. Trên trang này, thông điệp được băm không phải là “client seed-nonce” mà là “client seed-nonce-cursor”, và cursor là một bộ đếm bên trong vòng chơi: 0 cho con số ngẫu nhiên đầu tiên mà trò chơi cần, 1 cho con số thứ hai, và cứ thế tiếp tục. Mỗi giá trị cursor cho ra một digest 256-bit hoàn toàn mới, không liên quan theo bất kỳ cách nào bạn có thể khai thác so với các giá trị lân cận, và engine đọc tám byte đầu tiên của mỗi digest như một con số nằm giữa 0 và 1.
ENGINE-VERIFIED_shared/demoLocal.ts: uAt(pair, cursor) trả về u64(HMAC-SHA256(serverSeed, `${clientSeed}-${nonce}-${cursor}`)), được mô tả là khớp với RandomUtils.generateHash(“clientSeed-nonce-cursor”) của server. _shared/rng.ts: u64 đọc tám byte đầu tiên của digest thành hi/2^32 + lo/2^64, “cùng cấu trúc một-lần-làm-tròn” như phép chuyển ULong→Double của server. Nonce là một chuỗi duy nhất cho mỗi cặp seed trên tất cả các trò chơi: mỗi lần cược tiêu thụ nonce hiện tại và lưu lại nonce + 1.
Vậy một vòng chơi không phải là một con số ngẫu nhiên duy nhất mà là một danh sách được đánh số các con số, dài bao nhiêu tùy theo trò chơi cần, và mọi mục trong danh sách đó đã được cố định ngay khi cặp seed và nonce được xác định. Trò chơi không rút số khi diễn ra. Nó chỉ đọc xuống một danh sách đã tồn tại sẵn.
Mỗi trò chơi đọc gì
Điều thú vị là danh sách này khác nhau ở mỗi trò chơi, và mỗi module dẫn xuất nêu rõ cấu trúc của nó trong phần đầu. Đây là chuỗi cursor của mọi trò chơi gốc lấy dữ liệu từ cặp seed, trích từ mã nguồn engine.
| TRÒ CHƠI | CÁC CURSOR ĐƯỢC ĐỌC | CHÚNG TRỞ THÀNH GÌ |
|---|---|---|
| Limbo | 0 | Một con số qua công thức 0.99 ÷ (1 − u) |
| Roulette | 0 | Ô: floor(u × 37) |
| Fukubukuro | 0 | Vật phẩm, dựa theo bảng trọng số của túi |
| Sic Bo | 0, 1, 2 | Ba viên xúc xắc |
| Chinchiro | phía × 9 + lần thử × 3 + xúc xắc | Tối đa 18: nhà cái rồi đến người chơi, tối đa ba lần tung mỗi lần ba viên xúc xắc; một lần tung tạo thành bộ bài sẽ kết thúc phía đó |
| Koban Flip | một cho mỗi lần lật | Lần lật i đọc con trỏ i; mọi mặt của lượt chơi đều được cố định ngay khi mua đồng xu đầu tiên |
| Omikuji | 0, sau đó 1–3, rồi từ 4 trở đi | Cấp độ vận may; ba ô thắng khi thắng cuộc; các ký hiệu độn cho phần còn lại |
| Pagoda | một cho mỗi tầng | Ô bẫy trên mỗi tầng: floor(u × tiles) |
| Video Poker | 5, cộng thêm một cho mỗi lá bài được đổi | Một chuỗi không lặp lại trên 52 lá bài; giữ tất cả và không đọc thêm con trỏ nào nữa |
| Casino Hold’em | tối đa 9 | player 1, player 2, dealer 1, dealer 2, flop 1–3, turn, river; turn và river chỉ được đọc khi CALL |
| Blackjack | một cursor cho mỗi lá bài | Bộ bài vô hạn, card = floor(u × 52), theo thứ tự chia bài: player, dealer up, player, dealer hole, sau đó là mọi lần rút tiếp theo |
| Andar Bahar | 0–4, sau đó một cursor cho mỗi lá bài được đua | Hai bucket được tăng cường, độ lớn của chúng, joker, sau đó là cuộc đua cho đến khi lá bài trùng hạng xuất hiện lại |
| Hanabi | 15 cursor cho mỗi vòng quay | Cell = reel × 3 + row; free spin j đọc từ 15 + 15j trở đi |
| Bingo | 0–24, sau đó 25–62 | Thẻ bingo, năm lượt rút cho mỗi cột; sau đó là 38 quả bóng |
Keno và Crash là ngoại lệ: các vòng demo của chúng dùng một chuỗi hash theo từng vé thay vì cặp seed-nonce, được mô tả bên dưới. Mines và Plinko được xử lý bởi dịch vụ nhà cái; API của mines ghi chú rằng toàn bộ bố cục lấy ra từ một nonce duy nhất.
ENGINE-VERIFIEDCác comment trong header của bingo/derive.ts (“Cursor stream: 0..24 — thẻ bài … 25..62 — 38 quả bóng”), hanabi/engine.ts (“base spin = cells 0..14 … free spin j = cells 15+15j .. 29+15j”), blackjack/derive.ts (“card i = floor(u × 52) tại cursor i … Deal order (cursor): player1, dealer-up, player2, dealer-hole”), vpoker/derive.ts (“năm lá đầu tiên là lượt chia, các lá thay thế tiếp tục cùng chuỗi đó … Hold everything thì không đọc thêm cursor nào”), holdem/derive.ts (“player1, player2, dealer1, dealer2, flop1, flop2, flop3, turn, river. Các cursor turn/river chỉ được dùng khi CALL”), andar/derive.ts (cursor 0–4 sau đó “5.. một cursor cho mỗi lá bài được đua”), koban/derive.ts (“flip i đọc cursor i”), sicbo/derive.ts (“cursor 0/1/2 là ba viên xúc xắc”), roulette/derive.ts (“cursor 0 một mình quyết định ô: floor(u × 37)”), fuku/derive.ts (“cursor 0 một mình rút vật phẩm”), omikuji/derive.ts (cursor 0 là tier, 1..3 là các ô thắng, 4.. là các ô lấp đầy); demoLocal.ts rollSide (chinchiro cursor = side*9 + attempt*3 + d), trapAt (pagoda: số tầng chính là cursor), demoLimboBet (uAt(p, 0)); mines/minesApi.ts (“toàn bộ bố cục mìn lấy ra từ một nonce duy nhất này”).
Có ba điều nổi bật trong bảng đó. Thứ nhất, các trò chơi chia bài đọc một cursor cho mỗi lá bài theo thứ tự cố định, đó là lý do vì sao một ván blackjack có thể được replay lại từ danh sách hành động của nó: quyết định rút thêm bài (hit) của bạn không tạo ra một lá bài mới, nó chỉ lật lá tiếp theo trong một danh sách đã hoàn chỉnh trước khi ván chia bài diễn ra. Thứ hai, một số trò chơi dừng đọc sớm. Một ván video poker mà bạn giữ cả năm lá thì không bao giờ chạm tới cursor 5; một lần fold trong Hold’em không bao giờ đọc turn hay river. Các con số đã ở đó, chỉ là trò chơi không cần dùng đến. Thứ ba, hai bucket được tăng cường trong Andar Bahar và tier may mắn trong Omikuji được rút trước cả bài và ô, ngay đầu chuỗi, nên các tính năng đặc biệt của ván chơi được cố định bởi cùng một commitment như các yếu tố thông thường của nó.
Rút bài không hoàn lại
Một số ngẫu nhiên đồng đều từ 0 đến 1 rất dễ chuyển thành một ô roulette: nhân với 37 rồi làm tròn xuống. Một bộ bài thì khó hơn, vì lá thứ hai không được trùng với lá thứ nhất. Engine xử lý việc này bằng cái mà các comment gọi là pool hop: giữ một danh sách các lá bài còn lại trong pool, dùng số của cursor để chọn một vị trí trong đó, rồi hoán đổi lá bài được chọn ra cuối danh sách để lượt rút tiếp theo chọn từ ít hơn một lá. Video Poker, Casino Hold’em, Andar Bahar và thẻ Bingo đều rút bài theo cách này, nên không có lá bài hay con số nào lặp lại trong một ván.
Blackjack là ngoại lệ có chủ đích. Bộ bài của nó là vô hạn: mọi lá bài đều là floor(u × 52) từ cursor riêng của nó, và cùng một lá bài có thể xuất hiện hai lần trong một ván. Đó là một lựa chọn thiết kế có cái giá đã được công bố, được thảo luận trong bài viết về payout của blackjack, và đó chính xác là kiểu thông tin mà cách bố trí cursor tiết lộ cho bạn mà một trang marketing sẽ không nói.
ENGINE-VERIFIEDvpoker/derive.ts: “Các lá bài đến từ MỘT chuỗi không hoàn lại (without-replacement) trên bộ 52 lá (pool-hop, một cursor cho mỗi lần rút)”. holdem/derive.ts: “MỘT chuỗi pool-hop không hoàn lại (một cursor cho mỗi lần rút)”. bingo/derive.ts: “năm lượt rút pool-hop cho mỗi cột” và “38 quả bóng, pool-hop trên 1..75”. blackjack/derive.ts: “Bộ bài vô hạn: card i = floor(u × 52) tại cursor i”.
Hai ngoại lệ
Keno và Crash hoàn toàn không đọc nonce của cặp seed trong engine demo. Keno sử dụng cơ chế theo từng vé: vé nhận được hash ngẫu nhiên riêng và một salt, và mỗi quả bóng trong số mười quả được rút bằng cách hash lại hash trước đó, lấy các bit đầu làm một số và chọn từ nhóm bốn mươi số đang thu hẹp dần. Bảng công bằng xác minh một vé keno từ hai chuỗi đó chứ không phải từ cặp seed. Crash tuân theo quy ước bustabit, một chuỗi các hash trong đó điểm nổ của mỗi vòng được tính từ liên kết riêng của nó bằng số học số nguyên chính xác.
ĐÃ XÁC MINH BỞI ENGINEdemoLocal.ts kenoDraws: “chuỗi randomHash theo từng cược + salt (có thể xác minh theo từng vé qua modal công bằng) … Không đụng đến nonce của cặp seed”; vòng lặp hash HMAC-SHA256(salt, hash trước đó) mười lần, đọc 52 bit đầu và chọn chỉ số floor(norm × phần còn lại) từ nhóm 40 số với việc hoán đổi ra cuối. rng.ts h52 và bình luận về crash: “điểm nổ crash kiểu bustabit tính theo phần trăm: floor((n·e − h)/(e − h)), e = 2^52, n = 100 … Toán BigInt chính xác để khớp với server”.
Không ngoại lệ nào yếu hơn cơ chế con trỏ (cursor); cả hai đều cũ hơn và được biết đến nhiều hơn. Điều quan trọng với người chơi chỉ là bảng công bằng hiển thị vòng nào đã dùng cơ chế nào, và mỗi cơ chế đều có thể được tính lại từ các chuỗi mà nó hiển thị.
Điều này có ý nghĩa gì tại bàn chơi
- Vòng chơi đã tồn tại trước khi bạn hành động. Mỗi lá bài, quả bóng và biểu tượng cuộn của một vòng đều là hàm của cặp seed, nonce và vị trí con trỏ của nó. Việc rút bài, gọi hay giữ chỉ chọn mục nào được đọc, không phải chúng chứa gì.
- Lựa chọn của bạn không thể thay đổi danh sách, và nhà cái cũng không thể. Một khi seed của server đã được cam kết và nonce đã cố định, không ai ở cả hai phía có thể thay đổi mục thứ 7 mà không làm thay đổi seed, điều này sẽ phá vỡ dấu vân tay mà bạn đã được cho xem.
- Người xác minh phải đọc cùng một danh sách theo cùng một cách. Tính lại một vòng bingo nghĩa là suy ra tất cả 63 con trỏ theo thứ tự đã công bố; tính lại một lượt fold Hold'em nghĩa là suy ra 7 con trỏ. Bảng seed thực hiện điều này ngay trong trình duyệt của bạn, và các bố cục trong bảng trên là những gì nó tuân theo.
- Một chuỗi thắng/thua chỉ là một danh sách, không phải một tâm trạng. Hai vòng chỉ chia sẻ cặp seed; nonce của chúng khác nhau nên mọi digest cũng khác nhau. Đây cũng chính là điểm mà bài viết về hot hand đề cập từ góc nhìn ngược lại.
Ba đầu vào quyết định vòng chơi. Đầu vào thứ tư quyết định vòng chơi kéo dài bao lâu — con trỏ, tóm gọn trong một dòng
Các bố cục con trỏ ở trên được đọc từ các mô-đun suy diễn phía client mà trình xác minh trên trình duyệt và engine demo cùng chia sẻ. Dịch vụ nhà cái là bên thanh toán có thẩm quyền; khi một bình luận trong mã nguồn nói rằng một mô-đun phản ánh server, điều đó được trích dẫn nguyên văn, không phải được chúng tôi chứng thực.
Câu hỏi thường gặp
Con trỏ trong một vòng chơi provably fair là gì?
Một bộ đếm bên trong vòng chơi. Thông điệp được hash cùng với seed của server là “client seed-nonce-cursor”; con trỏ 0 cho ra số ngẫu nhiên đầu tiên mà trò chơi cần, con trỏ 1 cho ra số thứ hai, và cứ thế tiếp tục. Một trò chơi chỉ cần một số thì chỉ đọc con trỏ 0.
Con trỏ khác nonce như thế nào?
Nonce đếm số vòng và tăng thêm một mỗi lần cược trên mọi bản gốc. Con trỏ đếm số lần rút trong một vòng và khởi động lại từ 0 mỗi vòng. Cùng với cặp seed, chúng xác định mọi số ngẫu nhiên mà trang web từng tạo ra.
Một vòng bingo sử dụng bao nhiêu số ngẫu nhiên?
Sáu mươi ba: con trỏ 0 đến 24 bố trí 25 ô của tấm thẻ và con trỏ 25 đến 62 rút 38 quả bóng, tất cả đều không lặp lại trong nhóm của chúng.
Việc rút bài (hit) trong blackjack có làm thay đổi lá bài tiếp theo không?
Không. Mỗi lá bài được suy ra từ con trỏ riêng của nó theo thứ tự chia bài, và toàn bộ danh sách đã tồn tại ngay khi cặp seed và nonce được cố định. Rút bài chỉ đọc mục tiếp theo; dừng lại (stand) để mục đó chưa được đọc.
Tất cả các trò chơi có sử dụng cơ chế con trỏ không?
Keno và Crash sử dụng chuỗi hash thay thế: keno băm một hash ngẫu nhiên riêng cho từng vé cùng với salt mười lần, còn crash tuân theo chuỗi bustabit với phép tính số nguyên chính xác. Bảng công bằng sẽ xác minh từng cái từ các chuỗi mà nó hiển thị.
NGUỒN & THAM KHẢO
- Mã nguồn engine Betkyo: _shared/demoLocal.ts (uAt, rollSide, trapAt, kenoDraws), _shared/rng.ts (u64, h52, crash point), và các module tính toán cho bingo, hanabi, blackjack, vpoker, holdem, andar, koban, sicbo, roulette, fuku và omikuji
- RFC 2104 — HMAC: Keyed-Hashing for Message Authentication
- Cơ chế provably fair của Bustabit (cấu trúc điểm crash mà engine này mô phỏng theo)
CÁC TRÒ CHƠI TRONG BÀI VIẾT NÀY
Blackjack — luật chơi & chơi thử miễn phí →Bingo — luật chơi & chơi thử miễn phí →Sic Bo — luật chơi & chơi thử miễn phí →