Cách công bằng có thể kiểm chứng hoạt động trong Keno
Đây là cách chơi Keno trên Cloudbet, và cách kiểm tra xem mỗi lượt quay có thực sự công bằng và có thể kiểm chứng hay không.
Cách chơi Keno
Keno là trò chơi rút số: bạn chọn một tập hợp số trên bảng, sau đó trò chơi tự rút ra tập số của riêng nó. Số lượng con số bạn chọn trùng với số được rút càng nhiều, tiền thắng của bạn càng lớn — mức độ lớn tùy thuộc vào số lượng con số bạn đã chọn ban đầu. Sau khi chọn số và đặt cược, lượt quay diễn ra và mọi kết quả trùng khớp được hiển thị ngay lập tức.
Cloudbet Originals Keno
Keno Originals của riêng Cloudbet sử dụng bảng 40 số: bạn chọn từ 1 đến 10 số, và trò chơi rút ra 10 số. Trò chơi có RTP 99% và chấp nhận cược từ $0.01. Chọn ít số hơn đồng nghĩa với độ biến động cao hơn và tiền thắng mỗi lần trúng lớn hơn; chọn nhiều số hơn đồng nghĩa với thắng thường xuyên hơn nhưng tiền thắng mỗi lần trúng thấp hơn. Bạn có thể dùng Auto Bet để tự động hóa nhiều vòng chơi liên tiếp.
Lượt quay của Keno được tạo ra theo cách có thể kiểm chứng giống như mọi trò chơi Cloudbet Original khác — xem tổng quan về công bằng có thể kiểm chứng của chúng tôi để biết Server Seed, Client Seed và Nonce kết hợp như thế nào để tạo ra Round Signature, sau đó là một chuỗi số ngẫu nhiên thông qua SHAKE256. Trang này tập trung vào phần đặc thù của Keno: chuỗi số ngẫu nhiên đó trở thành tập số được rút thực tế như thế nào.
Biến số ngẫu nhiên thành lượt quay Keno
Bảng Keno được đánh số từ 1 đến 40. Mười trong số 40 con số này cần được rút ra, từng số một, không lặp lại bất kỳ số nào.

Chúng tôi gọi số lượng các con số chưa được rút còn lại là P. P bắt đầu từ 40 và giảm đi 1 mỗi khi một số được rút ra.

Một số ngẫu nhiên 4 byte (32 bit) được rút từ đầu ra SHAKE256 cho mỗi lượt trong 10 lượt rút, trong khoảng từ 0 đến 4,294,967,295. Để chọn công bằng trong số P con số còn lại, chúng tôi áp dụng phương pháp lấy mẫu loại bỏ — rejection sampling (xem hướng dẫn về rejection sampling của chúng tôi), sau đó lấy kết quả modulo P để xác định vị trí của số được rút tiếp theo. Nếu vẫn còn số cần rút, số vừa chọn sẽ bị loại khỏi nhóm, P giảm đi 1 và quá trình lặp lại.

Quá trình này tiếp tục cho đến khi cả 10 số đều đã được rút.


Cách chơi
Toàn bộ 10 số được rút ra được tạo và ẩn trên máy chủ trước khi bạn chọn số — nó không thể bị thay đổi hoặc chỉnh sửa mà không bị phát hiện sau đó, nhờ vào Commitment đã được chia sẻ với bạn từ trước. Các lựa chọn của bạn chỉ đơn giản được đối chiếu với tập số đã được rút sẵn này khi vòng chơi kết thúc, và tiền thắng của bạn được tính dựa trên số lượng trùng khớp bạn có.
Mã để tự xác minh kết quả Keno của bạn trên Cloudbet
Nhập Server Seed, Client Seed và Nonce của bạn để tự kiểm chứng kết quả của mình. Cài đặt thư viện phụ thuộc duy nhất, rồi tự chạy để tái tạo lại kết quả ván chơi của bạn.
Lưu ý: để xem Server Seed của một ván đã hoàn thành trên Cloudbet, bạn cần đã bắt đầu một vòng chơi mới trước đó (thao tác này chuyển sang một cặp seed mới).
Thư viện phụ thuộc:
npm install js-sha3
Mã nguồn:
// If you are running this yourself, then you can input your own data here
// You can then compare:
// 1) Cloudbet's provided Commitment to the Commitment outputted here. This proves that the Server Seed was not altered during gameplay.
// 2) Cloudbet's provided mine positions to the mine positions outputted here. This proves that your game's outcome was determined solely by the pre-game seeds and was not altered afterwards.
let myServerSeed = 'your_server_seed'; // ✍️YOUR_INPUT✍️: Enter your (unhashed) Server Seed here
let myClientSeed = 'your_client_seed'; // ✍️YOUR_INPUT✍️: Enter your Client Seed here
let myNonce = 1; // ✍️YOUR_INPUT✍️: Enter your Nonce here
const usingCustomData = false; // Change this to true if you are using your own data
// Load libraries
const crypto = require('crypto');
const { sha3_256, shake256 } = require('js-sha3');
// Step 1: Generate a Server Seed and Commitment
// And generate a Client Seed - either by player input or by the player's browser
function generateServerSeed() {
let seed = crypto.randomBytes(32).toString('hex');
if (usingCustomData) { seed = myServerSeed; }
const commitment = sha3_256(seed);
return { 'serverSeed': seed, 'commitment': commitment };
}
function getClientSeed() {
let seed = crypto.randomBytes(32).toString('hex');
if (usingCustomData) { seed = myClientSeed; }
return seed;
}
// Step 2: Create a Round Signature with SHA3 using a Server Seed, Client Seed and Nonce
function createRoundSignature(serverSeed, clientSeed, nonce) {
return sha3_256(`${serverSeed}:${clientSeed}:${nonce}`);
}
// Step 3: Create a SHAKE256 stream generator from a Round Signature.
// To simplify this example program, this function doesn't create a stream, but instead a sequence of custom length.
function* createRandomNumberSequence(numbersToGenerate, roundSignature) {
const numOfBytes = 4 * numbersToGenerate; // 4 bytes (32 bits) per random number we generate
const byteStream = shake256.create(8 * numOfBytes); // shake takes bits as input
byteStream.update(roundSignature); // Use the Round Signature to seed the generator
const buf = Buffer.from(byteStream.digest()); // The Buffer class lets us package every 4 bytes into one 32-bit integer
for (let i = 0; i < numbersToGenerate; i++) {
yield buf.readUInt32BE(i * 4); // Read buf in 4-byte steps
}
}
// Step 4: Pick a number of unique positions
function pickUniquePositions(numOfPicks, totalPositions, rng) {
const available = Array.from({ length: totalPositions }, (_, i) => i);
const picks = [];
for (let i = 0; i < numOfPicks; ++i) {
const index = sample(available.length, rng);
picks.push(available[index]);
available.splice(index, 1); // Remove chosen pump from list of non-faulty pumps
}
return picks;
}
// Rejection sampling for fair results
function sample(P, rng) {
const maxAcceptable = Math.floor(0x100000000 / P) * P;
// Rejection sampling
let rand;
do {
rand = rng.next().value;
} while (rand >= maxAcceptable);
// Modulo operation
const sampled_number = rand % P;
return sampled_number;
}
// Example usage
(function main() {
let nonce = 1;
if (usingCustomData) { nonce = myNonce; }
const numOfHits = 10;
const totalPositions = 40;
const { serverSeed, commitment } = generateServerSeed();
const clientSeed = getClientSeed();
const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
const numbersToGenerate = totalPositions * 2; // We need at least one random number per position, but there is an improbable possibility we'll need two
const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);
let hits = pickUniquePositions(numOfHits, totalPositions, rng);
hits = hits.map((value)=> value + 1 );
hits.sort((a, b)=>a-b);
console.log('📝 Commitment (shown before the game):', commitment);
console.log('🔒 Server Seed (revealed after the game):', serverSeed);
console.log('🎲 Client Seed:', clientSeed);
console.log('🔢 Nonce:', nonce);
console.log('🔁 Round Signature (can be verified after the game):', roundSignature);
console.log(`🎯 Hits:`, hits);
})();
