다이스(Dice)에서 입증된 공정성이 작동하는 방식
Cloudbet에서 다이스(Dice)를 플레이하는 방법과, 모든 주사위 결과가 입증된 공정성을 갖추고 있는지 확인하는 방법을 알아보세요.
다이스(Dice) 플레이 방법
다이스는 무작위로 생성된 숫자가 직접 설정한 목표값보다 높을지 낮을지에 베팅하는 빠른 템포의 게임입니다. 목표 숫자를 정하고 오버(Roll Over)와 언더(Roll Under) 중 하나에 베팅합니다. 목표값이 범위의 가장자리에 가까울수록 당첨 확률은 낮아지지만, 당첨 시 배당금은 더 커집니다. 베팅을 하면 결과가 즉시 공개됩니다.
Cloudbet Originals 다이스
Cloudbet 자체 제작 Originals 다이스는 0.00~100.00 범위의 목표값, 99%의 RTP, 최대 x9,800 배수를 제공하며, $0.01~$10,000 사이의 베팅을 받습니다. 오토 베팅(Auto Bet) 기능으로 여러 라운드를 자동으로 진행할 수 있습니다.
이 페이지에서는 다이스가 검증된 무작위 숫자를 실제 주사위 결과로 바꾸는 과정을 다룹니다. 무작위성 자체가 어떻게 생성되고 보호되는지(서버 시드, 클라이언트 시드, 논스, 해싱)에 대해서는 입증된 공정성 페이지를 참고하세요. Dice: Golden Ticket을 플레이하시나요? 이 게임도 여기에서 설명하는 것과 동일한 방식으로 주사위 값을 추출한 다음, 그 위에 Golden Ticket 값과 배수를 추가합니다.
무작위 숫자를 다이스 결과로 바꾸는 과정
다이스의 결과는 다른 모든 Cloudbet Originals와 동일하게 검증 가능한 무작위성에 입증 가능한 방식으로 연결되어야 합니다. 라운드의 무작위 숫자 시퀀스가 생성되면(SHAKE256을 통해 — 해당 과정은 입증된 공정성 프로세스 참고), 다이스는 다음과 같이 이를 사용합니다.
시퀀스에서 0에서 10,000 사이(포함)의 정수 하나를 추출한 다음 100으로 나눕니다. 이렇게 하면 소수점 둘째 자리까지 있는 0에서 100 사이의 값이 나오며, 이것이 다이스 결과입니다.
추출 방식
다이스는 10,001가지의 결과(0.00~100.00, 0.01 단위)를 가지므로, 이 게임에서는 P = 10,001입니다.
이 10,001가지 결과 중 하나를 공정하게 선택하기 위해, 다이스는 입증된 공정성 가이드에서 설명한 것과 동일한 거부 샘플링(rejection sampling) 방식을 사용합니다. 무작위 32비트 숫자를 생성한 다음, 결과에 편향을 줄 수 있다면 거부하고 공정한 값이 나올 때까지 다시 추출합니다. 그런 다음 이 숫자를 10,001로 나눈 나머지를 구해 0에서 10,000 사이의 값을 얻고, 이를 100으로 나누어 최종 결과를 도출합니다.

게임 진행
결과는 베팅을 하기 전에 서버에서 생성되어 고정되며, 라운드가 시작된 후에는 감지되지 않고 변경될 수 없습니다. 베팅 후에는 선택한 기준값이 이 사전에 생성된 숫자와 비교되어 예측이 맞았는지를 판단합니다.
다이스의 입증된 공정성을 독립적으로 검증하는 코드
이 프로그램은 Cloudbet의 다이스 구현과 정확히 일치합니다. 서버 시드, 클라이언트 시드, 논스를 입력해 직접 결과를 검증해 보세요. 참고: Cloudbet에서 게임의 서버 시드를 공개하려면 먼저 새로운 서버 시드로 교체해야 합니다.
의존성:
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 getDiceResult(rng) {
const diceMinValue = 0;
const diceMaxValue = 10000;
let number = sampleFromRange(diceMinValue, diceMaxValue, rng);
number = number / 100; // So that the dice's value ranges from 0 to 100
return number;
}
// 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 = 1 * 2; // We need 1 random number, however due to rejection sampling we may need more
const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);
const diceValue = getDiceResult(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);
})();


