Game
游戏事件是将随机生成的浮动转换为与游戏特定相关的结果。这包括从掷骰子的结果到一副牌中的卡片顺序,甚至是矿井游戏中每个炸弹的位置。
以下是我们如何将浮动翻译成我们平台上每个不同游戏的事件的详细说明。
赫罗
在标准扑克牌中,有52种独特的可能结果。当在我们的平台上玩二十一点、Hilo和百家乐时,我们在生成游戏事件时使用无限数量的牌组,因此每次发牌的概率始终相同。为了计算这一点,我们将每个随机生成的浮动乘以52,然后根据以下索引将该结果转换为特定的牌:
// Index of 0 to 51 : ♦2 to ♣A
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
];
// Game event translation
const card = CARDS[Math.floor(float * 52)];
这些游戏中唯一的区别因素是,在Hilo和二十一点中,有一个光标为13,以生成52个可能的游戏事件,以应对需要发放大量牌的情况,而在百家乐中,我们只需要生成6个游戏事件,以覆盖最多的可玩牌。
钻石
玩钻石时,有7种可能的结果,以宝石的形式呈现。为此,我们在将每个生成的浮动值转换为相应的宝石之前,将其乘以7,并使用以下索引:
// Index of 0 to 51 : ♦2 to ♣A
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
];
// Game event translation
const card = CARDS[Math.floor(float * 52)];
骰子
在我们的骰子版本中,我们涵盖了00.00到100.00的可能掷骰范围,拥有10,001种可能的结果。游戏事件的翻译是通过将浮动值乘以可能结果的数量,然后除以100,以确保所得数字符合我们所声明的骰子范围。
// Game event translation
const roll = (float * 10001) / 100;
硬币翻转
在玩硬币翻转时,有2种可能的结果,以硬币的形式呈现。为此,我们在将每个生成的浮动值转换为相应的宝石之前,将其乘以2,并使用以下索引:
// Index of 0 & 1 : batman & joker
const COINS = [ 0, 1 ];
// Game event translation
const coin = COINS[Math.floor(float * 2)];
林波
当涉及到Limbo时,我们使用一个两步过程。首先,我们将浮动值乘以最大可能的倍增器和赌场优势。然后,为了生成具有 概率分布 我们将最大可能的倍增器除以第一步的结果,以创建以崩溃点形式呈现的游戏事件。
// Game event translation with houseEdge of 0.99 (1%)
const floatPoint = 1e8 / (float * 1e8) * houseEdge;
// Crash point rounded down to required denominator
const crashPoint = Math.floor(floatPoint * 100) / 100;
// Consolidate all crash points below 1
const result = Math.max(crashPoint, 1);
Plinko
对于任何Plinko游戏,生成的结果基于下落球的路径。游戏事件确定下落球在下落过程中每个级别的方向。玩家可以选择8到16个钉子,这决定了生成从上到下的完整路径所需的游戏事件数量。由于只有两个可能的方向(左或右),因此翻译通过将每个浮动值乘以2来完成,映射到以下索引:
// Index of 0 to 1 : left to right
const DIRECTIONS = [ left, right ];
// Game event translation
const direction = CARDS[Math.floor(float * 2)];
Keno
传统的Keno游戏要求选择10个可能的游戏事件,形式为在棋盘上的击打。
// Index of 0 to 39 : 1 to 40
const SQUARES = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40
];
const hit = SQUARES[Math.floor(float * 40)];
Fisher-Yates 洗牌实现用于防止生成重复的可能命中。
地雷
一个矿山游戏由 24 个独立的游戏事件生成,以矿井的形式出现在棋盘上。每个浮动值乘以棋盘上仍然剩余的可能唯一瓦片的数量。这是通过在使用相应浮动生成的每个游戏事件结果的迭代中,将剩余瓦片的数量减去 1 来完成的。矿山的位置是从左到右、从上到下绘制的网格位置。
Fisher-Yates 洗牌实现用于防止生成重复的可能命中。根据所选设置,使用 1 到 24 个游戏事件结果。
轮盘
游戏事件编号是通过将浮动乘以段中的可能结果来计算的。然后它用于确定游戏事件结果作为乘数,使用以下索引:
// Index per payout configuration
const PAYOUTS = {
'10': {
low: [ 1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0 ],
medium: [ 0, 1.9, 0, 1.5, 0, 2, 0, 1.5, 0, 3 ],
high: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9.9 ]
},
'20': {
low: [
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0
],
medium: [
1.5, 0, 2, 0, 2, 0, 2, 0, 1.5, 0,
3, 0, 1.8, 0, 2, 0, 2, 0, 2, 0
],
high: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 19.8
]
},
'30': {
low: [
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0
],
medium: [
1.5, 0, 1.5, 0, 2, 0, 1.5, 0, 2, 0,
2, 0, 1.5, 0, 3, 0, 1.5, 0, 2, 0,
2, 0, 1.7, 0, 4, 0, 1.5, 0, 2, 0
],
high: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 29.7
]
},
'40': {
low: [
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0
],
medium: [
2, 0, 3, 0, 2, 0, 1.5, 0, 3, 0,
1.5, 0, 1.5, 0, 2, 0, 1.5, 0, 3, 0,
1.5, 0, 2, 0, 2, 0, 1.6, 0, 2, 0,
1.5, 0, 3, 0, 1.5, 0, 2, 0, 1.5, 0
],
high: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 39.6
]
},
'50': {
low: [
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0,
1.5, 1.2, 1.2, 1.2, 0, 1.2, 1.2, 1.2, 1.2, 0
],
medium: [
2, 0, 1.5, 0, 2, 0, 1.5, 0, 3, 0,
1.5, 0, 1.5, 0, 2, 0, 1.5, 0, 3, 0,
1.5, 0, 2, 0, 1.5, 0, 2, 0, 2, 0,
1.5, 0, 3, 0, 1.5, 0, 2, 0, 1.5, 0,
1.5, 0, 5, 0, 1.5, 0, 2, 0, 1.5, 0
],
high: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 49.5
]
}
};
// Game event translation
const spin = PAYOUTS[segments][risk][float * segments];
崩溃
为了证明我们的公平性,我们生成了一个包含10,000,000个SHA256哈希的链,其中每个哈希都是前一个哈希的十六进制表示的哈希。链中的最后一个哈希是:78a9757d3be42b74a3f70239078ad9317125fe9ee630d5bdada46de963e56752
生成游戏结果的公式:
const gameHash = hashChain.pop()
const hmac = createHmac('sha256', gameHash);
// blockHash is the hash of bitcoin block 584,500
hmac.update(blockHash);
const hex = hmac.digest('hex').substr(0, 8);
const int = parseInt(hex, 16);
// 0.01 will result in 1% house edge with a lowest crashpoint of 1
const crashpoint = Math.max(1, (2 ** 32 / (int + 1)) * (1 - 0.01))
双倍
我们的双人游戏源自欧洲版,轮盘由 15 个不同的口袋组成,范围从 0 到 14。游戏事件是通过将浮动值乘以 15 计算得出的,然后使用以下索引转换为相应的口袋:
// Index of 0 to 14
const POCKETS = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14
];
// Game event translation
const pocket = POCKETS[Math.floor(float * 15)];
塔
塔游戏由9个独立的游戏事件生成,呈现为塔上的各个层级。我们根据每个层级的难度生成一定数量的鸡蛋,并且用整数表示鸡蛋可以在的瓷砖范围。
每个生成的浮点数都转换为整数,以确定每行的鸡蛋位置。例如:一个简单难度的关卡表示为:[0, 1, 3] - 鸡蛋将出现在第1、2和4个瓷砖上。
// count represents the number of eggs
// size represents the number of possible squares
const LEVEL_MAP = {
easy: { count: 3, size: 4 },
medium: { count: 2, size: 3 },
hard: { count: 1, size: 2 },
expert: { count1, size: 3 },
master: { count: 1, size: 4 },}
Fisher-Yates 洗牌实现用于防止同一行中出现重复的鸡蛋。