หลักการทำงานของความยุติธรรมที่พิสูจน์ได้ใน Dice: Golden Ticket
นี่คือวิธีเล่น Dice: Golden Ticket บน Cloudbet และวิธีตรวจสอบว่าการทอยและการออกรางวัล Golden Ticket ในแต่ละรอบเป็นไปตามหลักความยุติธรรมที่พิสูจน์ได้
วิธีเล่น Dice: Golden Ticket
Dice: Golden Ticket (DGT) เล่นคล้ายกับเกม Dice แบบดั้งเดิมของ Cloudbet โดยมีอีกชั้นหนึ่งเพิ่มเข้ามา: เป้าหมายโบนัสที่ซ่อนอยู่ซึ่งสามารถจ่ายตัวคูณโบนัสเพิ่มจากเงินรางวัลปกติของคุณ
เกมพื้นฐาน: เลือก Roll Over (2.00–99.99) หรือ Roll Under (0.01–98.00) แล้ววางเดิมพัน หากลูกเต๋าออกมาอยู่ฝั่งเป้าหมายของคุณ คุณจะชนะตามอัตราจ่ายที่เป้าหมายของคุณกำหนดไว้ (เป้าหมายที่แคบกว่า = ตัวคูณที่สูงกว่า)
โบนัส Golden Ticket: ในแต่ละรอบ จะมีค่าที่สองซึ่งซ่อนอยู่ นั่นคือ Golden Ticket ถูกกำหนดไว้บนกระดานด้วยเช่นกัน พร้อมตัวคูณโบนัสของตัวเองระหว่าง 3 เท่าถึง 200 เท่า หากผลการทอยลูกเต๋าของคุณอยู่ในช่วง ±1.75 จากตำแหน่งของ Golden Ticket คุณจะได้รับเงินรางวัลโบนัสตามตัวคูณนั้นทันทีโดยอัตโนมัติ โดยไม่ต้องเดิมพันเพิ่ม และจะรวมเข้ากับเงินรางวัลปกติของคุณ
คุณไม่สามารถเล็งไปที่ Golden Ticket ได้โดยตรง มันไม่ใช่สิ่งที่คุณกำหนดเป้าหมายหรือมีอิทธิพลได้ แต่จะปรากฏอยู่บนกระดานเสมอในทุกรอบ และการทอยได้ใกล้กับมันคือโบนัสเพิ่มเติมนอกเหนือจากการเล่นปกติของคุณ
Cloudbet Originals Dice: Golden Ticket
เกม DGT ของ Cloudbet Originals มี RTP 99% ตัวคูณลูกเต๋าสูงสุด x6,730 และเงินรางวัลสูงสุด $1,000,000
Dice: Golden Ticket (DGT) ใช้พื้นฐานความยุติธรรมที่พิสูจน์ได้แบบเดียวกับ Cloudbet Original ทุกเกม ดูกลไกทั่วไปได้ที่หลักการทำงานของความยุติธรรมที่พิสูจน์ได้ (Server Seed, Client Seed, Nonce, SHAKE256, Rejection Sampling) การสุ่มค่าลูกเต๋าของเกมนี้ก็ทำงานแบบเดียวกับ Dice ธรรมดา โดย DGT จะเพิ่มค่าและตัวคูณของ Golden Ticket เข้าไปอีกชั้นหนึ่ง ด้านล่างนี้คือวิธีที่ลำดับตัวเลขนั้นกลายเป็นผลลัพธ์ DGT ของคุณโดยเฉพาะ
แปลงตัวเลขสุ่มให้กลายเป็นผลลัพธ์เกม
DGT ต้องพิสูจน์ว่าค่าสองค่าถูกกำหนดโดยความสุ่มที่ตรวจสอบได้ ได้แก่ ค่าของลูกเต๋า และ ค่าของ Golden Ticket
กระบวนการนี้ง่ายมาก: อันดับแรก ตัวเลขเต็มระหว่าง 0 ถึง 10000 (รวมทั้งสองค่า) จะถูกสุ่มเพื่อเป็นค่าของลูกเต๋าในที่สุด จากนั้นตัวเลขเต็มอีกตัวระหว่าง 175 ถึง 9825 (รวมทั้งสองค่า) จะถูกสุ่มเพื่อเป็นค่าของ Golden Ticket ในที่สุด ตัวเลขทั้งสองจะถูกหารด้วย 100 เพื่อให้แต่ละค่ามีทศนิยมสองตำแหน่ง อยู่ในช่วง 0 ถึง 100 (ลูกเต๋า) และ 1.75 ถึง 98.25 (Golden Ticket)
ช่วงของ Golden Ticket ถูกจำกัดไว้ที่ 1.75–98.25 โดยเจตนา ไม่ใช่ 0–100 เพื่อให้ไม่ว่ามันจะตกอยู่ตรงไหน จะมี "โซนทอง" กว้าง 3.5 อยู่รอบๆ เสมอ ซึ่งค่าลูกเต๋าสามารถตกลงไปและชนะโบนัสได้ หากช่วงขยายไปจนถึงขอบ (0 หรือ 100) Golden Ticket ที่ตกใกล้ขอบจะมีโซนทองที่เล็กกว่า และมีโอกาสแย่กว่า เมื่อเทียบกับตัวที่ตกอยู่ตรงกลาง
DGT ยังมีฟีเจอร์พิเศษอีกอย่างหนึ่ง: ตัวคูณ ของ Golden Ticket เองก็ถูกสร้างขึ้นด้วยกระบวนการที่ยุติธรรมที่พิสูจน์ได้ พูดง่ายๆ คือ: ตัวเลขสุ่มตัวที่สามระหว่าง 0 ถึง 1 จะถูกสุ่มขึ้นมา แล้วแปลงเป็นตัวคูณระหว่าง 3 ถึง 200 โดยใช้การกระจายแบบ Truncated Pareto

รายละเอียดทางเทคนิค
ไบต์สุ่มที่สร้างโดย SHAKE256 จะถูกแบ่งออกเป็นจำนวนเต็มไม่มีเครื่องหมายขนาด 4 ไบต์ มีค่าตั้งแต่ 0 ถึง 2³²-1 เนื่องจากค่าลูกเต๋ามีผลลัพธ์ที่เป็นไปได้ 10001 แบบ (0–10000 รวมทั้งสองค่า) การทำ Rejection Sampling จึงใช้ P = 10001 ส่วนค่า Golden Ticket มีผลลัพธ์ที่เป็นไปได้ 9651 แบบ (175–9825 รวมทั้งสองค่า) ดังนั้น P ของมันคือ 9651 (ดูเหตุผลว่าทำไมขั้นตอนนี้ถึงสำคัญได้ที่หน้าความยุติธรรมที่พิสูจน์ได้)
การเลือกค่าลูกเต๋าและ Golden Ticket: ตัวเลขสุ่มของลูกเต๋าจะถูกนำมา Modulo กับ 10001 เพื่อให้ได้ค่าของมัน ทำแบบเดียวกันกับตัวเลขสุ่มของ Golden Ticket โดยใช้ 9651 แล้วเพิ่มผลลัพธ์ด้วย 175 (เพื่อให้อยู่ในช่วง 175–9825) จากนั้นทั้งสองค่าจะถูกหารด้วย 100
การเลือกตัวคูณของ Golden Ticket (ความรู้พื้นฐานด้านสถิติจะช่วยให้เข้าใจส่วนนี้ได้ง่ายขึ้น): ตัวเลขสุ่ม 32 บิตจะถูกสุ่มขึ้นมาแล้วหารด้วยค่าสูงสุดที่เป็นไปได้ ทำให้ได้ตัวเลขที่กระจายแบบสม่ำเสมอระหว่าง 0 ถึง 1 ค่านั้นจะถูกแปลงเป็นการกระจายแบบ Truncated Pareto (ขอบเขตล่าง 3 ขอบเขตบน 200 อัลฟา 1.343 ซึ่งเลือกมาเพื่อให้ค่าคาดหวังของตัวคูณเท่ากับ 9 อันเป็นไปตามเป้าหมาย RTP 99% ของ DGT) แล้วปัดเศษเป็นทศนิยมสองตำแหน่ง
การเล่นเกม
ทั้งค่าลูกเต๋าและค่าของ Golden Ticket จะถูกสร้างขึ้นฝั่งเซิร์ฟเวอร์และซ่อนไว้จนกว่ารอบจะจบ และไม่สามารถเปลี่ยนแปลงได้โดยไม่ถูกตรวจจับเมื่อเริ่มเล่นแล้ว หลังจากคุณวางเดิมพัน เป้าหมายที่คุณเลือกจะถูกนำไปเปรียบเทียบกับค่าของลูกเต๋า และระยะห่างระหว่าง Golden Ticket กับค่าของลูกเต๋าจะถูกตรวจสอบกับ "โซนทอง" 1.75 เพื่อดูว่าคุณได้รับโบนัสหรือไม่
โค้ดสำหรับตรวจสอบผลลัพธ์ด้วยตัวเอง
ตรงกับการทำงานของ DGT บน Cloudbet ใส่ Server Seed, Client Seed และ Nonce ของคุณเพื่อตรวจสอบเกมของคุณเอง (หากต้องการเปิดเผย Server Seed ของเกมบน Cloudbet คุณต้องได้เปลี่ยนไปใช้ Server Seed ใหม่แล้ว)
ไลบรารีที่จำเป็น:
npm install js-sha3
โค้ด:
// 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 game outcome to the game outcome 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
}
throw new Error("No more numbers in the random number sequence! Fix: make 'numbersToGenerate' larger.");
}
// Step 4: Draw from the random number sequence to calculate the game result
function getGameResult(rng) {
// Dice
// Ranges from 0 to 100, with two decimal places
const diceMinValue = 0;
const diceMaxValue = 10000;
let diceValue = sampleFromRange(diceMinValue, diceMaxValue, rng);
diceValue = diceValue / 100; // Now the value ranges from 0 to 100
// Golden Ticket
// Ranges from 1.75 to 98.25 due to fairness
// (If it landed on 99, for example, then the chance of the dice landing near
// it would be smaller than the guarantee of 3.5%)
const gtMinValue = 175;
const gtMaxValue = 9825;
let goldenTicketValue = sampleFromRange(gtMinValue, gtMaxValue, rng);
goldenTicketValue = goldenTicketValue / 100;
// Golden Ticket Multiplier
const multiplier = getRandomNumberTruncatedPareto(rng); // Complicated maths, can be ignored ;)
return { diceValue, goldenTicketValue, multiplier };
}
// Sample a number using the truncated Pareto distribution
function getRandomNumberTruncatedPareto(rng) {
// Get a uniformly distributed number between 0 and 1
let randomInt32 = rng.next().value;
const fraction = randomInt32 / 0xFFFFFFFF;
// Truncated Pareto distribution. The expected value is 9.00
const m0 = 3;
const m1 = 200;
const alpha = 1.343;
const r = Math.pow(m0 / m1, alpha);
const inner = 1 - fraction * (1 - r);
let randomNumber = m0 / Math.pow(inner, 1 / alpha);
randomNumber = Math.round(randomNumber * 100) / 100; // Round to 2 decimal places
return randomNumber;
}
// Sample a number between 'min' to 'max' (inclusive) using Rejection Sampling
function sampleFromRange(min, max, rng) {
const P = max - min + 1;
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 + min;
}
// Example usage
(function main() {
let nonce = 1;
if (usingCustomData) { nonce = myNonce; }
const { serverSeed, commitment } = generateServerSeed();
const clientSeed = getClientSeed();
const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
const numbersToGenerate = 3 * 2; // We need 3 random numbers, however due to rejection sampling we may need more
const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);
const { diceValue, goldenTicketValue, multiplier } = getGameResult(rng);
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('🎯 Dice landed on:', diceValue);
console.log('🎫 Golden ticket landed on:', goldenTicketValue);
console.log('⭐ Multiplier:', multiplier);
})();
