Lewati ke konten utama

Provably Fair - Peristiwa Game

Apa itu peristiwa game

Sebelumnya, kami telah menghasilkan float acak antara 0 hingga 1 menggunakan fungsi generateFloats dan byteGenerator. Peristiwa game menerjemahkan float yang dihasilkan ini menjadi hasil game yang sebenarnya. Hal ini dilakukan dengan cara yang berbeda untuk setiap game. Dalam Plinko, float membantu menentukan arah jatuhnya bola. Dalam Dice, float membantu menentukan hasil lemparan dadu.

Berikut adalah penjelasan mendetail tentang cara kami menerjemahkan float menjadi peristiwa atau hasil yang adil untuk setiap game original di platform kami.


Tower

Dalam Tower, tujuan game adalah mencapai puncak menara dan menyelamatkan putri dengan menemukan kunci serta menghindari apel beracun. Setiap baris menara berisi jumlah ubin aman (kunci) dan apel beracun yang telah ditentukan sebelumnya, tergantung pada tingkat kesulitan yang dipilih.

Peristiwa game dalam Tower adalah menentukan posisi ubin aman (atau kunci) di setiap baris.

Terdapat total 9 baris di menara. Jumlah kolom serta distribusi kunci dan apel beracun bervariasi berdasarkan tingkat kesulitan yang dipilih:

  • Mudah: 4 kolom - 3 kunci dan 1 apel beracun per baris

  • Sedang: 3 kolom - 2 kunci dan 1 apel beracun per baris

  • Sulit: 2 kolom - 1 kunci dan 1 apel beracun per baris

  • Pakar: 3 kolom: - 1 kunci dan 2 apel beracun per baris

  • Master: 4 kolom - 1 kunci dan 3 apel beracun per baris

Hasil untuk setiap baris ditentukan dengan menghasilkan posisi acak kunci (ubin aman) dalam kolom yang tersedia berdasarkan konfigurasi tingkat kesulitan.
​

const TOWER_DIFFICULTY_TO_CONFIG: Record<TowerDifficulty, DifficultyConfig> = {
[TowerDifficulty.EASY]: {
count: 3,
columns: 4,
},
[TowerDifficulty.MEDIUM]: {
count: 2,
columns: 3,
},
[TowerDifficulty.HARD]: {
count: 1,
columns: 2,
},
[TowerDifficulty.EXPERT]: {
count: 1,
columns: 3,
},
[TowerDifficulty.MASTER]: {
count: 1,
columns: 4,
},
};


// Generate floats to determine the positions of safe tiles (keys) for each row
const floats = generateFloats({
serverSeed,
clientSeed,
nonce,
cursor,
count,
});

// Get the tower configuration based on the selected difficulty
const towerConfig = TOWER_DIFFICULTY_TO_CONFIG[difficulty];
const results: number[][] = [];
const resultPerRow = towerConfig.count;

// Iterate through each row to determine the positions of safe tiles for that row
for (let i = 0; i < TOWER_TOTAL_ROWS.toNumber(); i++) {
const result: number[] = [];
const values = Array.from(Array(towerConfig.columns).keys());

// Get positions for safe tiles in the current row
for (let j = 0; j < resultPerRow; j++) {
const float = floats[i * resultPerRow + j];
if (float === undefined) {
break;
}

const position = Math.floor(float * (towerConfig.columns - j));
result.push(values.splice(position, 1)[0] ?? 0);
}

results.push(result);
}

return results;

// Example: when clientSeed='abc', serverSeed='def', nonce=1, difficulty='easy'
// results: [
// [0, 1, 2],
// [0, 2, 3],
// [0, 2, 3],
// [0, 1, 2],
// [0, 1, 3],
// [0, 1, 2],
// [1, 2, 3],
// [0, 1, 3],
// [0, 1, 3]
// ]
// Each sub-array represents the column indexes of safe tiles for that row
// (e.g., for the first row, safe tiles are in columns 0, 1, and 2)

Chicken

Dalam Chicken, tujuannya adalah menggerakkan ayam melintasi sebanyak mungkin jalur tanpa mendarat di jalur berbahaya.

Setiap langkah maju yang berhasil meningkatkan pengali pembayaran. Pemain dapat melakukan cash out kapan saja sebelum ayam melompat ke jalur berbahaya. Jika ayam mencapai zona bahaya sebelum pemain melakukan cash out, taruhan akan hilang.

Peristiwa game utama dalam Chicken adalah menentukan jalur berbahaya — jalur spesifik tempat ayam akan kalah.

Terdapat 4 tingkat kesulitan dalam Chicken. Jumlah jalur yang dapat dilintasi ayam bervariasi tergantung pada tingkat kesulitan yang dipilih:

  • Mudah: 19 Jalur

  • Sedang: 17 Jalur

  • Sulit: 15 Jalur

  • Pakar: 10 Jalur

const TOTAL_LANES = 20

// Number of lanes that the chicken can cross for each difficulty
const DIFFICULTY_TO_VALID_LANE_COUNT: Record<ChickenDifficulty, number> = {
[ChickenDifficulty.EASY]: 19,
[ChickenDifficulty.MEDIUM]: 17,
[ChickenDifficulty.HARD]: 15,
[ChickenDifficulty.EXPERT]: 10,
};

// Number of gameResults generated depends on the difficulty
// Example with Hard difficulty, we generate 20 - 15 = 5 numbers
// and the smallest number in the array is the danger lane
const gameResults = generateFloats({
serverSeed,
clientSeed,
nonce,
cursor: 0,
count: TOTAL_LANES - DIFFICULTY_TO_VALID_LANE_COUNT[difficulty] // difficulty selected for the game
}).map((val, index) => Math.floor(val * (TOTAL_LANES - index)));

// Danger lane is determined by the smallest number in the game results array
const dangerLane = Math.min(...gameResults)

// Example: Hard difficulty - gameResults = [10, 4, 6, 15, 8]
// Danger lane will be on 4 (this is zero based index)
// Lane 0 - 3 are safe and lane 4 will be the danger lane
// The chicken will lose upon reaching lane 4 if not cashed out earlier

Plinko

Dalam Plinko, pemain dapat memilih piramida dengan tinggi 8 hingga 16. Saat bola dilepaskan dari atas, bola akan jatuh ke kiri atau kanan sebanyak 8 hingga 16 kali setiap kali mengenai pin pada papan Plinko. Peristiwa game dalam Plinko adalah menentukan apakah bola jatuh ke kiri atau kanan di setiap level, yang direpresentasikan oleh 0 dan 1.

// Create multiple floats between 0 and 1 
// Example: floats = [0.8320531346835196, 0.8531235849950463, 0.18227359023876488, 0.774284637067467, 0.18627392360940576, 0.6012540922965854, 0.608081541955471, 0.46018488449044526] when clientSeed='abc', serverSeed='def', nonce=1, count=8
// count refers to the number of plinko rows
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor, count });

// Example: [1, 1, 0, 1, 0, 1, 1, 0] Each 1 represents a drop to the right whereas 0 represents a drop to the left
const dropDirections = floats.map(val => Math.floor(val * 2));

// This sums up all the values in the array to get the final position of the ball from the left.

// Example: finalPositionFromLeft = 5 (the ball will land on the 6th slot from the left)
const finalPositionFromLeft = dropDirections.reduce((cur, acc) => cur + acc, 0)

Dice

Dalam Dice, lemparan dadu dapat bernilai antara 0.00 hingga 100.00. Peristiwa game dalam Dice adalah menentukan nilai ini. Kami melakukannya dengan menggunakan (floats * 10,001) / 100). Karena kami menyertakan nilai hingga 2 tempat desimal, dan angka 0, terdapat total 10,000 + 1 = 10,001 kemungkinan hasil (termasuk tempat desimal).

// Creates an array of random numbers 
// Example: With clientSeed = 'abc', serverSeed = 'def', nonce = 1, floats = [0.43053962965495884]
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: 1 });

// Converts the value in the array to a number between 0.00 to 100.00 and return the value

// Example: resultValue = [43.05]
const resultValue = floats.map(val => Math.floor(floats * 10001) / 100);
return resultValue[0];
// Example: 43.05 is returned as the result of the game

Limbo

Dalam Limbo, tujuan game adalah menebak pengali >1. Jika hasil game lebih besar daripada pengali, pengguna memenangkan game. Peristiwa game adalah menghasilkan nilai float yang berada di atas 1 dan mengikuti distribusi kurva hiperbolik.

// Generate float between 0 and 1 
// Example: floats = [0.43053962965495884], float = 0.43053962965495884,
// when clientSeed='abc', serverSeed='def', nonce=1
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: 1 });
const float = floats[0]
// generate limbo game event results with house edge (1%)
// using a hyperbolic 1/x curve
// multiplying by this and dividing by (MAX_VALUE + 1) prevents division by 0 error

const MAX_VALUE = 2 ** 24;
const rtp = 0.99;

// Example: rawValue = 2.298905
const rawValue = (rtp * MAX_VALUE) / (MAX_VALUE * float + 1)

// resultValue = 2.29
const resultValue = (0.99 * MAX_VALUE / (MAX_VALUE * float +1)).toFixed(2,ROUND_DOWN);
return resultValue;

Keno

Dalam Keno, peristiwa game memerlukan identifikasi 10 penempatan permata unik pada papan keno. Untuk melakukannya, kami menghasilkan 10 float berbeda antara 1 hingga 40., yang merepresentasikan setiap lokasi pada papan keno. Kemudian sebuah digunakan untuk menangani float duplikat dan mengembalikan 10 lokasi permata akhir

// Generate 10 different floats as there are 10 positions that are selected when the game is played 
// Example: floats = [18, 26, 7, 22, 31, 10, 23, 8, 24, 13] when clientSeed='abc', serverSeed='def', nonce=1

const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: 10 }) .map((val, index) => Math.ceil(val * (40 - index)))

// array used to store the position of the gems const gemLocations: number[] = [];

// this loop helps to prevent duplicate gem positions by skipping positions that already contain a gem
for (const val of floats) { let currentGem = val;
let i = 0;
while (i <= currentGem) {
if (gemLocations.includes(i)) {
currentGem++;
}
i++;
}

gemLocations.push(currentGem);
}

// returns the location of the gem on the board
// Example: [18, 27, 7, 24, 35, 11, 28, 9, 31, 16] return gemLocations;

Mines

Dalam Mines, peristiwa game memerlukan identifikasi penempatan ranjau pada papan Mines berubin 25. Berdasarkan pilihan pengguna atas jumlah ranjau, kami menghasilkan daftar bilangan bulat dari 0 hingga 24 untuk merepresentasikan 25 ruang pada papan.

// Generate 10 different floats 
// Example: [20, 20, 4] when clientSeed='abc', serverSeed='def', nonce=1, numberOfBombs=3
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor: 0, count: numberOfBombs })
.map((val, index) => Math.floor(val * (25 - index)));

// array used to store the position of the bombs
const mineLocations: number[] = [];

// this loop helps to prevent duplicate bomb positions by skipping positions that already contain a mine
for (const val of floats) {
let currentMine = val;
let i = 0;

while (i <= currentMine) {
if (mineLocations.includes(i)) {
currentMine++;
} i++; } mineLocations.push(currentMine); }

// returns the location of the bombs on the board
// Example: [20, 21, 4]
return mineLocations;

Roulette

Roulette kami didasarkan pada roda tunggal ‘0’ bergaya Eropa, dengan 37 kantong berbeda yang berkisar dari 0 hingga 36. Peristiwa game dalam Roulette adalah menghitung kantong dari 37 kantong tempat bola akan mendarat.

// Generate a float between 0 and 1 
// Example: floats = [0.43053962965495884]
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor: 0, count: 1 });

// generate an integer from 0 to 37 to represent the pocket to land on
// Example: resultValue = 15
const resultValue = floats.map(val => Math.floor(val * 37))[0];
return resultValue

Wheel

Dalam Wheel, pengguna akan memilih jumlah segmen pada roda. Bergantung pada segmen tempat penunjuk berhenti, pengguna akan memenangkan pengali pada segmen tersebut. Peristiwa game adalah menentukan segmen.

// Generate a float between 0 and 1 
// Example: floats = [0.43053962965495884]
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:1 });

// Get an integer representing the number of segments set in the game
// Example: resultSegment = 8
const resultSegment = floats.map(val => Math.floor(val * numberOfSegments))[0];

return resultSegment

Blackjack

Dalam Blackjack, game dimainkan dengan dek kartu tanpa batas. Oleh karena itu, setiap giliran akan selalu memiliki probabilitas yang sama. Peristiwa game dalam Blackjack adalah menentukan kartu yang ditarik jika pemain atau banker menarik kartu dari dek.

// Generate a float between 0 and 1 
// Example: floats = [0.43053962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (52 elements are generated but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });

const CARDS = [ ♦2, ♥2, ♠2, ♣2, ♦3, ♥3, ♠3, ♣3, ♦4, ♥4, ♠4, ♣4, ♦5, ♥5, ♠5, ♣5, ♦6, ♥6, ♠6, ♣6, ♦7, ♥7, ♠7, ♣7, ♦8, ♥8, ♠8, ♣8, ♦9, ♥9, ♠9, ♣9, ♦10, ♥10, ♠10, ♣10, ♦J, ♥J, ♠J, ♣J, ♦Q, ♥Q, ♠Q, ♣Q, ♦K, ♥K, ♠K, ♣K, ♦A, ♥A, ♠A, ♣A
];

// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));

// Example: cards = [♠7, ♠10, ♥4, ♥9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;

Hilo

Dalam Hilo, game dimainkan dengan dek kartu tanpa batas. Oleh karena itu, setiap giliran akan selalu memiliki probabilitas yang sama. Tujuan game adalah menebak apakah kartu berikutnya memiliki nilai lebih tinggi atau lebih rendah daripada kartu saat ini. Peristiwa game dalam Hilo adalah menentukan kartu berikutnya yang ditarik pengguna (jika ditarik).

// Generate a float between 0 and 1 
// Example: floats = [0.43053962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (an unlimited number of cards can be drawn but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });

const CARDS = [
♦2, ♥2, ♠2, ♣2, ♦3, ♥3, ♠3, ♣3, ♦4, ♥4, ♠4, ♣4, ♦5, ♥5, ♠5, ♣5, ♦6, ♥6, ♠6, ♣6, ♦7, ♥7, ♠7, ♣7, ♦8, ♥8, ♠8, ♣8, ♦9, ♥9, ♠9, ♣9, ♦10, ♥10, ♠10, ♣10, ♦J, ♥J, ♠J, ♣J, ♦Q, ♥Q, ♠Q, ♣Q, ♦K, ♥K, ♠K, ♣K, ♦A, ♥A, ♠A, ♣A
];

// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));


// Example: cards = [♠7, ♠10, ♥4, ♥9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;

Crash

Terdapat 2 bagian dalam perhitungan hasil game Crash

  • Hash blok Bitcoin

  • Seed

Hash Bitcoin

Rinciannya ada dalam tweet ini:

Hash blok 779588 dapat ditemukan di penjelajah blok BTC (https://btcscan.org/block/000000000000000000033ab71b5fa7edcafe388e5f49719f1de8e1f8d0670460), dan sesuai dengan hash yang ditampilkan dalam tweet.

Seed

Proses pembuatan seed untuk 10 juta game Crash dirancang untuk memastikan keadilan dan transparansi, serta mencegah kemungkinan manipulasi oleh Shuffle. Berikut cara seed dibuat dan diverifikasi:

  1. Pembuatan Seed Awal: Seed untuk game ke-10 juta dipilih secara acak oleh Shuffle.

  2. Hashing Berurutan untuk Pembuatan Seed: Untuk membuat seed bagi semua game lainnya, kami menggunakan metode hashing mundur:

    • Seed untuk setiap game adalah hasil hashing seed game berikutnya. Secara khusus, hash untuk game ke-9,999,999 adalah hash SHA256 dari hash game ke-10 juta.

    • Rantai ini berlanjut sehingga seed setiap game berasal dari hashing hash game berikutnya, yang berakhir dengan seed game pertama berasal dari hash game kedua.

  3. Verifikasi Integritas Seed:

    • Dengan memublikasikan nilai hash dari hash yang digunakan dalam game pertama, pemain dapat secara mandiri memverifikasi integritas hash untuk semua game hingga game ke-10 juta.

    • Dengan metode ini, jika Anda mengetahui hash untuk game tertentu, Anda dapat memverifikasi seed untuk semua game sebelumnya. Misalnya, jika Anda mengetahui seed game 1323185, Anda dapat melakukan hashing seed ini untuk memverifikasi bahwa seed tersebut cocok dengan seed game 1323184, dan seterusnya hingga kembali ke game pertama.

  4. Transparansi dan Keamanan:

    • Dengan menggunakan hash blok BTC masa depan yang berada di luar kendali Shuffle dan memublikasikan versi hash dari seed game pertama, pengguna dapat membuktikan bahwa hasil game Crash adil dan tidak dimanipulasi.

Slide

Slide menggunakan proses provably fair dan pembuatan seed yang sama seperti Crash. Pembuatan seed dilakukan secara publik di x.com, dan postingan tersebut dapat ditemukan di sini.

Hash blok 952294 dapat ditemukan di penjelajah blok BTC (https://btcscan.org/block/00000000000000000000a382760ff19cdb7f9f22147b9123f3e471fb3c6d129a) dan sesuai dengan hash yang ditampilkan dalam tweet.

Baccarat

Dalam Baccarat, game dimainkan dengan dek kartu tanpa batas. Oleh karena itu, setiap giliran akan selalu memiliki probabilitas yang sama. Peristiwa game dalam Baccarat adalah menentukan kartu yang dibagikan kepada tangan Player dan Banker dalam pembagian awal serta penarikan tambahan sesuai aturan penarikan Baccarat standar.

Kartu dibagikan dalam urutan berikut dari urutan float yang dihasilkan:

  1. Kartu pertama Player - indeks float 0

  2. Kartu pertama Banker - indeks float 1

  3. Kartu kedua Player - indeks float 2

  4. Kartu kedua Banker - indeks float 3

  5. Kartu ketiga Player (jika ditarik) - indeks float 4

  6. Kartu ketiga Banker (jika ditarik)

    1. indeks float 4 - jika pemain tidak menarik kartu ketiga

    2. indeks float 5 - jika pemain menarik kartu ketiga

// Generate a float between 0 and 1
// Example: floats = [0.40353962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (an unlimited number of cards can be drawn but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });

const CARDS = [
♠2, ♥2, ♦2, ♣2, ♠3, ♥3, ♦3, ♣3, ♠4, ♥4,
♦4, ♣4, ♠5, ♥5, ♦5, ♣5, ♠6, ♥6, ♦6, ♣6,
♠7, ♥7, ♦7, ♣7, ♠8, ♥8, ♦8, ♣8, ♠9, ♥9,
♦9, ♣9, ♠10, ♥10, ♦10, ♣10, ♠J, ♥J, ♦J,
♣J, ♠Q, ♥Q, ♦Q, ♣Q, ♠K, ♥K, ♦K, ♣K, ♠A,
♥A, ♦A, ♣A
];

// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));

// Example: cards = [♠7, ♠10, ♥4, ♠9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;

Blitz

Dalam Blitz, game dimainkan dengan dek kartu tanpa batas. Oleh karena itu, setiap giliran akan selalu memiliki probabilitas yang sama. Peristiwa game dalam Blitz adalah menentukan kartu yang dibagikan kepada pemain sesuai jumlah penarikan yang dipilih, serta mengevaluasi apakah semua kartu yang ditarik unik.

// Generate a float between 0 and 1
// Example: floats = [0.43053962965495884, 0.6645898742135614, 0.17454026662744582, 0.5729992990382016, 0.850033157505095] (an unlimited number of cards can be drawn but only 5 are shown here for brevity)
const floats = generateFloats({serverSeed, clientSeed, nonce, cursor, count:52 });

const CARDS = [
♠2, ♥2, ♣2, ♦2, ♠3, ♥3, ♣3, ♦3, ♠4, ♥4,
♣4, ♦4, ♠5, ♥5, ♣5, ♦5, ♠6, ♥6, ♣6, ♦6,
♠7, ♥7, ♣7, ♦7, ♠8, ♥8, ♣8, ♦8, ♠9, ♥9,
♣9, ♦9, ♠10, ♥10, ♣10, ♦10, ♠J, ♥J, ♣J, ♦J,
♠Q, ♥Q, ♣Q, ♦Q, ♠K, ♥K, ♣K, ♦K, ♠A,
♥A, ♣A, ♦A
];

// Get an integer 0 to 51, representing the index of the cards
// Example: cardFloats = [22, 34, 9, 29, 44]
const cardFloats = floats.map(val => Math.floor(val * 52));

// Example: cards = [♠7, ♠10, ♥4, ♠9, ♦K]
const cards = cardFloats.map(val => CARDS[val])
return cards;

Coinflip

Dalam Coinflip, tujuannya adalah menebak dengan benar sisi tempat koin yang dilempar mendarat. Peristiwa game dalam Coinflip adalah menentukan hasil (HEADS atau TAILS) dari setiap lemparan koin. Coinflip memiliki tiga mode — Classic (satu lemparan), Classic Progressive (lempar berulang kali dan cash out kapan saja), dan Target (pilih jumlah lemparan serta jumlah minimum tebakan benar yang diperlukan untuk menang) — tetapi ketiganya menentukan hasil lemparan dengan cara yang sama.

Setiap lemparan ditentukan dengan menghasilkan float dan memetakannya ke 0 atau 1, sama seperti Plinko memetakan float-nya ke jatuhan kiri/kanan:

// Generate floats to determine each flip outcome
// Example: floats = [0.43053962965495884, 0.6645898742135614, ...] when clientSeed='abc', serverSeed='def', nonce=1
const floats = generateFloats({ serverSeed, clientSeed, nonce, cursor, count });

// Each float maps to 0 or 1, which maps to HEADS or TAILS
// Example: [0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1]
const flipValues = floats.map(val => Math.floor(val * 2));
const flipResults = flipValues.map(val => (val === 0 ? 'HEADS' : 'TAILS'));


// Example: [HEADS, TAILS, HEADS, TAILS, TAILS, HEADS, TAILS, HEADS, TAILS, HEADS,
// TAILS, HEADS, TAILS, HEADS, HEADS, TAILS, HEADS, HEADS, HEADS, TAILS]
return flipResults;


Classic — satu lemparan dihasilkan (count = 1). Pemain menang jika sisi yang dipilih sesuai dengan hasil lemparan.

Classic Progressive — lemparan dihasilkan satu per satu saat pemain melanjutkan, tetapi hasil setiap lemparan telah ditetapkan sejak awal ronde: lemparan ke-N selalu diperoleh dengan menghasilkan lemparan 1 hingga N dan mengambil yang terakhir, sehingga mengungkapkan lemparan satu per satu saat pemain maju tidak mengubah hasil lemparan mana pun. Pemain dapat melakukan cash out setelah setiap lemparan yang berhasil; ronde berakhir pada kegagalan pertama.

Target — pemain memilih jumlah total lemparan (hingga 20) dan jumlah minimum tebakan benar yang diperlukan untuk menang. Semua lemparan dihasilkan sebagai satu batch (count = totalFlips), dan ronde dimenangkan jika jumlah lemparan yang sesuai dengan tebakan pemain setidaknya sama dengan minimum yang dipilih.

Floor is Lava

Tujuan: Melintasi grid 7×7 (49 ubin) dalam tiga level berturut-turut tanpa menginjak ubin yang berubah menjadi lava.

Setiap level berlangsung dalam "ronde" berukuran tetap — sekumpulan ubin jatuh ke lava bersama-sama pada setiap ronde — dan Anda dapat melakukan cash out setelah bertahan dalam ronde apa pun.

Pembuatan papan

Sebelum ronde dimulai, tata letak ubin untuk ketiga level diundi dalam satu shuffle yang Provably Fair: permutasi acak dari bilangan bulat 0..146 (49 ubin × 3 level) diundi tanpa pengembalian dari generator ber-seed, lalu dibagi berdasarkan level:

// Draw a random permutation of 0..146 without replacement 
// Example: raw = [103, 12, 140, 76, 3, ...]
const boards = [[], [], []];
for (const value of raw) {
boards[Math.floor(value / 49)].push(value % 49); // bucket = level, residue = tile index }
// boards[0], boards[1], boards[2] are each a uniformly random ordering of tiles 0..48

Mengambil satu permutasi bernilai 147 alih-alih tiga 49 bernilai shuffle terpisah hanyalah trik efisiensi — karena tepat 49 dari 147 nilai berada dalam rentang setiap level, setiap level tetap mendapatkan urutan independen dan acak secara seragam untuk 49 ubinnya sendiri.

Ronde: cara lava turun

Urutan 49 ubin setiap level dibagi menjadi potongan berurutan yang tidak tumpang tindih: sejumlah ubin tetap per ronde (dropsPerRound), untuk sejumlah ronde tetap (roundsPerLevel), dengan sisa di bagian akhir menjadi ubin yang bertahan sepanjang level:

tilesDroppedInRound(level, dropsPerRound, roundIndex) = levelBoard.slice(roundIndex * dropsPerRound, (roundIndex + 1) * dropsPerRound) 

tilesThatSurviveLevel(level, survivingCount) = levelBoard.slice(-survivingCount)

Karena shuffle telah menetapkan urutan ubin sejak awal, kumpulan lava ronde tidak dipilih sebagai reaksi terhadap pilihan Anda — kumpulan tersebut adalah potongan dari daftar yang telah di-shuffle yang "jatuh tempo" pada ronde ini.

Pada setiap pilihan, Anda memilih satu ubin (selectedTile). Ubin tersebut tidak boleh sudah termasuk dalam batch lava dari ronde sebelumnya pada level ini. Ronde kemudian diselesaikan dengan membandingkan satu ubin Anda dengan seluruh batch lava ronde (selectedTile):

isLoss = droppedTiles.includes(selectedTile) 

Jika ubin Anda adalah salah satu ubin yang jatuh pada ronde ini, permainan berakhir. Jika tidak, Anda bertahan dalam ronde tersebut — terlepas dari berapa banyak ubin lain yang jatuh bersamanya.

Tingkat kesulitan konfigurasi

Empat tingkat kesulitan menyeimbangkan ukuran ronde dengan jumlah ronde. Masing-masing tetap berjumlah hingga papan penuh berisi 49 ubin (dropsPerRound × roundsPerLevel + survivingTiles = 49):

Tingkat kesulitan

Jatuhan per ronde

Ronde per level

Ubin yang bertahan

Mudah

7

6

7

Sedang

11

4

5

Sulit

23

2

3

Toxic

48

1

1

Progres level dan game

Satu game memiliki 3 level, masing-masing dengan papan berisi 49 ubin yang di-shuffle secara independen. Bertahan dalam semua ronde pada suatu level akan menyelesaikan level tersebut dan memindahkan Anda ke papan level berikutnya pada ronde pertama; menyelesaikan level ketiga menyelesaikan game.

Cash out

Anda dapat melakukan cash out setelah bertahan dalam ronde apa pun — pilihan berhasil pertama Anda sudah membuka akses untuk melakukannya.

Pengali

multiplier = edgeMultiplier * 49^(levelsCleared+1) / (tilesRemainingInLevel * survivingTiles^levelsCleared) 

levelsCleared adalah jumlah level yang telah sepenuhnya diselesaikan sejauh ini, tilesRemainingInLevel berkurang sebesar jumlah ubin yang jatuh pada ronde setiap kali Anda bertahan dalam satu ronde di level saat ini, dan survivingTiles adalah jumlah penyintas per level untuk tingkat kesulitan tersebut dari tabel di atas.

Ini adalah harga peluang yang adil: kebalikan dari probabilitas kumulatif untuk mencapai titik tersebut secara tepat (level yang diselesaikan × ronde yang bertahan) dalam penempatan ubin acak seragam, yang disesuaikan dengan house edge.

Apakah pertanyaan Anda terjawab?