Cách provably fair hoạt động trong Mines

Đây là cách chơi Mines trên Cloudbet, và cách kiểm tra xem vị trí mìn của mỗi ván có phải là provably fair hay không.

Cách chơi Mines

Mỗi ván Mines trên Cloudbet đều sử dụng nền tảng provably fair giống nhau — server seed, client seed, nonce và SHA3-256/SHAKE256 — được trình bày chi tiết trên trang trung tâm Cách Provably Fair Hoạt Động của chúng tôi. Trang này trình bày những gì đặc thù của Mines: chuỗi số ngẫu nhiên đó trở thành lưới mìn thực tế mà bạn chơi cùng như thế nào.

Biến số ngẫu nhiên thành lưới mìn

Bạn chọn số lượng mìn cần đặt khi bắt đầu ván chơi — chúng ta gọi số này là N. Lưới Mines 5×5 có thể được trải phẳng thành một hàng gồm 25 vị trí, mỗi vị trí có chỉ số riêng, bắt đầu từ 1:

Sơ đồ cho thấy lưới Mines công bằng có thể chứng minh của Cloudbet — các ô 5×5 được đánh dấu từ A đến Y — được trải phẳng thành một hàng gồm 25 vị trí đánh số để tạo số ngẫu nhiên công bằng.

Chúng tôi lấy mẫu một số từ chuỗi ngẫu nhiên (được tạo theo cách giống như mô tả trên trang Provably Fair) để xác định vị trí của quả mìn đầu tiên:

Ví dụ minh họa thuật toán công bằng có thể chứng minh của Mines từ Cloudbet: một số ngẫu nhiên chọn vị trí của quả mìn đầu tiên trong tổng số 25 ô.

Nếu cần đặt thêm mìn, chúng tôi lấy thêm một số ngẫu nhiên khác và lặp lại — nhưng lúc này chỉ còn 24 ô trống để chọn:

Ví dụ minh họa cách thuật toán công bằng có thể chứng minh của Mines chọn quả mìn tiếp theo trong số 24 vị trí còn lại sau khi quả mìn đầu tiên đã được loại khỏi danh sách.

Việc này lặp lại cho đến khi tất cả các mìn được đặt xong. Toàn bộ lưới sau đó được xây dựng lại và giữ bí mật cho đến khi ván chơi kết thúc:

Lưới Mines công bằng có thể chứng minh cuối cùng, được tái dựng từ hàng đã trải phẳng sau khi mọi vị trí mìn được đặt một cách ngẫu nhiên và có thể xác minh.

Chi tiết kỹ thuật: chọn từng vị trí mìn

Trước mỗi lượt chọn, chúng ta biết P — số vị trí không có mìn còn lại để chọn. P bắt đầu ở 25 và giảm đi 1 mỗi khi một quả mìn được đặt.

Một số ngẫu nhiên 4 byte (32 bit) được lấy từ đầu ra của SHAKE256 cho mỗi quả mìn, có giá trị từ 0 đến 4.294.967.295. Để chọn công bằng trong số P vị trí, chúng tôi áp dụng rejection sampling (xem khái niệm chung được giải thích trên Cách Provably Fair Hoạt Động), sau đó lấy kết quả modulo P để có chỉ số vị trí:

// Pseudo Code
const placementIndex = randomNumber % P;

Sơ đồ luồng tính toán công bằng có thể chứng minh của Mines từ Cloudbet: các byte ngẫu nhiên và phương pháp lấy mẫu loại trừ xác định chỉ số vị trí của từng quả mìn.

Nếu còn mìn cần đặt, vị trí đã chọn sẽ được loại khỏi danh sách, P giảm đi 1, và quá trình lặp lại.

Cách chơi

Lưới mìn hoàn chỉnh được xây dựng và giấu kín trên máy chủ trước khi bạn thực hiện lượt đi đầu tiên — nó không thể bị thay đổi hay can thiệp mà không bị phát hiện sau đó, nhờ vào Commitment đã được chia sẻ với bạn từ trước. Mỗi ô bạn chọn chỉ đơn giản là được đối chiếu với lưới ẩn này.

Mã nguồn để kiểm chứng độc lập

Chương trình này chính xác với cách triển khai Mines trên Cloudbet. Nhập Server Seed, Client Seed, Nonce và số lượng mìn của bạn, cài đặt thư viện duy nhất cần thiết, và tự chạy để tái tạo lại kết quả ván chơi của bạn.

Lưu ý: để công bố 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án mới (thao tác này sẽ làm mới sang một cặp seed mới).

Các thư viện cần cài đặt:

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 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
let myNumberOfMines   = 3;                  // ✍️YOUR_INPUT✍️: Enter your number of mines 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); // shaker256 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: Generate the mine locations
function pickUniquePositions(mineCount, totalTiles, rng) {
  const available = Array.from({ length: totalTiles }, (_, i) => i);
  let result = [];

  for (let i = 0; i < mineCount; ++i) {
    const index = sample(available.length, rng);

    result.push(available[index]);
    available.splice(index, 1);         // Remove chosen position from list of available positions
  }
  result.sort((a, b) => a - b);         // Sort the mine positions from smallest to largest
  result = result.map(num => num + 1);  // Increment positions by one so that they start with 1, 2, 3, ...
  return result;
}
// 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() {
  const gridSize = 25;
  let mineCount = 3;
  let nonce = 1;
  if (usingCustomData) { mineCount = myNumberOfMines; nonce = myNonce; }

  const { serverSeed, commitment } = generateServerSeed();
  const clientSeed = getClientSeed();

  const roundSignature = createRoundSignature(serverSeed, clientSeed, nonce);
  const numbersToGenerate = 24 * 2; // There are max. 24 mines, however due to rejection sampling we may need more random numbers
  const rng = createRandomNumberSequence(numbersToGenerate, roundSignature);

  const minePositions = pickUniquePositions(mineCount, gridSize, 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('💣 Mine Positions (1 = top-left square, 5 = top-right square, 25 = bottom-right square):', minePositions);
})();

Thử ngay: kiểm tra vận may của bạn với Mines

Muốn tự mình trải nghiệm? Chơi một ván Cloudbet Mines — sau đó kiểm chứng kết quả bằng công cụ provably fair. Chỉ mất vài giây. Không cần kiến thức kỹ thuật.

Bạn sẽ được tận mắt xem quy trình diễn ra, kiểm tra các seed của mình và quan sát cách công cụ tái tạo lại kết quả từ đầu.

Các bài giải thích khác về provably fair:

  1. Provably fair Pump
  2. Provably fair Plinko
  3. Provably fair Dice
  4. Provably fair Dice: Golden Ticket
  5. Provably fair Limbo
  6. Provably fair Roulette
  7. Provably fair Keno