Пользовательские игры - Provably Fair
Как используется в пользовательских играх
Каждая ставка в YEET Originals начинается с раундового сида. Раундовый сид — это HMAC-SHA256 вашего клиентского сида и нонса ставки, зашифрованный серверным сидом, выбранным YEET. Затем игра извлекает каждое необходимое для этой ставки случайное значение из раундового сида. Серверный сид отображается вам в хэшированном виде до начала игры и раскрывается при смене пары сидов, поэтому вы можете пересчитать раундовый сид любой прошлой ставки и убедиться, что результат не был изменен после того, как вы сделали ставку.
Другие верификаторы могут не подойти
Сайты для проверки честности, созданные для других казино, могут генерировать случайные числа иначе, поэтому проверка ставки YEET на них может дать другой результат. Проверяйте ставки YEET во вкладке «Честность» (Fairness) в окне игры или с помощью кода на этой странице.
Преобразования
Конвертация превращает энтропию сида раунда в результат игры. Фундаментальный принцип один для всех Original-игр: unbiasedUint выбирает одну из n равновероятных позиций, а игра проверяет, является ли эта позиция выигрышной. Шаг с s выигрышными позициями из n имеет вероятность s / n.
На этой странице указаны не все конвертации для каждой игры. Пример ниже демонстрирует принцип работы на одной из них.
Пример: Lamb Chop
В игре Lamb Chop 16 прыжков, и каждый прыжок — это один розыгрыш из одного и того же seed раунда. На прыжке h в ряду totalStart − h позиций, из которых safeStart − h безопасны. Ролл, результат которого меньше количества безопасных позиций, засчитывается как успех. Первый же ролл, не попавший в безопасный диапазон, завершает раунд, а множитель определяется по таблице выплат в зависимости от количества пройденных прыжков.
const LAMB_CHOP_TOTAL_HOPS = 16;
const LAMB_CHOP_LANES = {
easy: [19, 21],
medium: [19, 23],
hard: [19, 25],
very_hard: [19, 27],
death_wish: [20, 32],
};
const LAMB_CHOP_PAYTABLE = {
easy: [
1.0721, 1.1912, 1.3313, 1.4977, 1.6975, 1.94, 2.2384, 2.6115,
3.0863, 3.7036, 4.5266, 5.6583, 7.275, 9.7, 13.58, 20.37,
],
medium: [
1.1742, 1.4351, 1.7728, 2.216, 2.8069, 3.6089, 4.7194, 6.2925,
8.5807, 12.013, 17.3522, 26.0283, 40.9016, 68.1694, 122.705, 245.41,
],
hard: [
1.2763, 1.7017, 2.3023, 3.1657, 4.432, 6.3315, 9.2537, 13.8806,
21.4519, 34.323, 57.2051, 100.1089, 185.9166, 371.8333, 818.0333, 2045.0833,
],
very_hard: [
1.3784, 1.991, 2.928, 4.392, 6.7344, 10.5826, 17.0951, 28.4918,
49.2132, 88.5838, 167.325, 334.65, 717.1071, 1673.25, 4350.45, 13051.35,
],
death_wish: [
1.552, 2.5322, 4.2203, 7.1994, 12.5989, 22.6781, 42.1166, 80.9934,
161.9869, 338.7, 745.1401, 1738.6604, 4346.651, 11798.0529, 35394.1588, 120340.14,
],
};
async function verifyLambChopBet(serverSeed, clientSeed, nonce, volatility) {
const seed = await deriveGameSeed(serverSeed, clientSeed, nonce);
const result = await verifyLambChopFromSeed(seed, volatility);
return { serverSeed, clientSeed, nonce, ...result };
}
async function verifyLambChopFromSeed(seed, volatility) {
const lane = LAMB_CHOP_LANES[volatility];
if (!lane) throw new Error(`No lamb-chop lane geometry for risk profile '${volatility}'.`);
const [safeStart, totalStart] = lane;
const entropy = new GameEntropy(hexToBytes(seed));
const hops = [];
let bustIndex = LAMB_CHOP_TOTAL_HOPS;
for (let hop = 0; hop < LAMB_CHOP_TOTAL_HOPS; hop++) {
const safePositions = safeStart - hop;
const totalPositions = totalStart - hop;
const roll = await unbiasedUint(entropy, totalPositions);
const survived = roll < safePositions;
hops.push({
hop,
safePositions,
totalPositions,
chance: safePositions / totalPositions,
roll,
survived,
});
if (!survived) {
bustIndex = hop;
break;
}
}
return {
seed,
entropyCursor: entropy.cursor,
volatility,
hops,
bustIndex,
clearedAllHops: bustIndex >= LAMB_CHOP_TOTAL_HOPS,
maxReachableMultiplier: lambChopMultiplierFor(volatility, bustIndex),
paytable: LAMB_CHOP_PAYTABLE[volatility],
};
}
function lambChopMultiplierFor(volatility, hopCount) {
if (hopCount < 1) return 0;
const row = LAMB_CHOP_PAYTABLE[volatility];
if (!row) throw new Error(`No lamb-chop paytable for risk profile '${volatility}'.`);
const multiplier = row[hopCount - 1];
if (multiplier === undefined) {
throw new Error(`No lamb-chop paytable row for ${volatility} at ${hopCount} hop(s).`);
}
return multiplier;
}
// Use the revealed server seed (64 hex characters) and the client seed and nonce shown on the bet.
console.log(await verifyLambChopBet(
'REVEALED_SERVER_SEED',
'yeetdemo-7f3a91',
'1842',
'easy'
));