Provably Fair
Common Functions
const generateBytes = (seed, cursor) => { return crypto .createHmac("sha256", `${seed.server_seed}`) .update(`${seed.client_seed}:${seed.nonce}:${cursor}`) .digest(); }; const generateFloats = (seed, count) => { const numberOfSHARoundsRequired = Math.ceil(count / 8); const buffer = Buffer.alloc(numberOfSHARoundsRequired * 32); for (let i = 0; i < numberOfSHARoundsRequired; i++) { generateBytes(seed, i).copy(buffer, i * 32); } const floats: number[] = []; for (let i = 0; i < count; i++) { let curr = new BigNumber(0); const start = i * 4; for (let j = 0; j < 4; j++) { curr = curr.plus( new BigNumber(buffer[start + j]).div(new BigNumber(256).pow(j + 1)) ); } floats.push(curr.toNumber()); } return floats; };
Game Specific Implementations
Dice
// Implementation during 1% edge const diceRollFromSeed = (seed) => { const randomFloat = generateFloats(seed, 1)[0]; return Math.floor(randomFloat * 10001) / 100; }; // Implementation during 0.95% edge const diceRollFromSeed = (seed) => { const randomFloat = generateFloats(seed, 1)[0]; return Math.round(randomFloat * 10000) / 100; };Plinko
const plinkoPathFromSeed = (seed, numberOfRows) => { const floats = generateFloats(seed, numberOfRows); return [...floats].map((float) => (float < 0.5 ? "L" : "R")); };Keno
const kenoSelectionFromSeed = (seed) => { const startingSet = [...Array.from(Array(40).keys())]; const floats = generateFloats(seed, 10); return floats.map((float) => { const index = Math.floor(float * startingSet.length); return startingSet.splice(index, 1)[0]; }); };Wheel
const wheelSpinFromSeed = (seed, segments) => { const floats = generateFloats(seed, 1); return Math.floor(floats[0] * segments); };Diamonds
const diamondSlotsFromSeed = (seed) => { const floats = generateFloats(seed, 5); return floats.map((float) => Math.floor(float * 7)); };Roulette
const rouletteRollFromSeed = (seed) => { const randomFloat = generateFloats(seed, 1)[0]; return Math.floor(randomFloat * 37); };Limbo
const limboMultiplierFromSeed = (seed, gameEdge) => { const float = generateFloats(seed, 1)[0]; const largeNum = new BigNumber(16777216); const numerator = largeNum; const denominator = new BigNumber(float).multipliedBy(largeNum) .plus(1).integerValue(BigNumber.ROUND_FLOOR); const floatWithHouseEdge = numerator .dividedBy(denominator) .multipliedBy(1 - gameEdge / 100); return BigNumber.maximum(1, floatWithHouseEdge.multipliedBy(100) .integerValue(BigNumber.ROUND_FLOOR).dividedBy(100)) .toNumber(); };Blackjack and HiLo
const cards = [ "2D", "2H", "2S", "2C", "3D", "3H", "3S", "3C", "4D", "4H", "4S", "4C", "5D", "5H", "5S", "5C", "6D", "6H", "6S", "6C", "7D", "7H", "7S", "7C", "8D", "8H", "8S", "8C", "9D", "9H", "9S", "9C", "10D", "10H", "10S", "10C", "JD", "JH", "JS", "JC", "QD", "QH", "QS", "QC", "KD", "KH", "KS", "KC", "AD", "AH", "AS", "AC", ]; const shuffledDeckFromSeed = (seed, numCards = 52) => { const floats = generateFloats(seed, numCards); return floats.map((float) => { const cardIdx = Math.floor(float * cards.length); return cards[cardIdx]; }); };Tower
const towerSafePositionsFromSeed = (seed, risk) => { const safePositionsPerRow = TowerConfig[risk].num_safe_positions; const floats = generateFloats(seed, 9 * safePositionsPerRow); const ret = []; let floatIdx = 0; for (let rowIdx = 0; rowIdx < 9; rowIdx++) { const safePositionsInCurrentRow = []; const arr = Array(TowerConfig[risk].num_positions).fill(0).map((_, j) => j); for (let j = 0; j < safePositionsPerRow; j++) { const index = Math.floor(floats[floatIdx] * arr.length); safePositionsInCurrentRow.push(arr.splice(index, 1)[0]); floatIdx += 1; } ret.push(safePositionsInCurrentRow); } return ret; };Mines
const mineLocationsFromSeed = (seed, numberOfMines) => { const gridCellsRasterOrder = [...Array(25).keys()]; const floats = generateFloats(seed, numberOfMines); return floats.map((float) => { const index = Math.floor(float * gridCellsRasterOrder.length); return gridCellsRasterOrder.splice(index, 1)[0]; }); };Goat Run
const goatRunAccidentalLanesFromSeed = (seed, numAccidentalLanes) => { const laneNumbers = Array.from({length: 25}, (_, i) => i + 1); const floats = generateFloats(seed, numAccidentalLanes); return floats.map((float) => { const index = Math.floor(float * laneNumbers.length); return laneNumbers.splice(index, 1)[0]; }); };G3 Cards
const deckFromSeed = (seed) => { const deck = [...cards]; const floats = generateFloats(seed, 52); const shuffledDeck = []; for (const float of floats) { const cardIdx = Math.floor(float * deck.length); shuffledDeck.push(deck.splice(cardIdx, 1)[0]); } return shuffledDeck; };