GOATED – The #1 Crypto Casino for Bitcoin GamblingGOATED – The #1 Crypto Casino for Bitcoin Gambling

Доказуемая честность

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];
      });
    };
  • Order Up

    const orderUpDealFromSeed = (seed, count) => {
      const pool = [...Array.from(Array(99).keys())].map((index) => index + 1);
      const floats = generateFloats(seed, count);
      return floats.map((float) => {
        const index = Math.floor(float * pool.length);
        return pool.splice(index, 1)[0];
      });
    };
    
    // The payout depends only on how many cards come up in rising order
    // from the left, which is decided entirely by the deal above.
    const orderUpRunLength = (cards) => {
      let k = 1;
      while (k < cards.length && cards[k] > cards[k - 1]) k++;
      return k;
    };
  • 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, HiLo and Baccarat

    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 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 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;
    };
  • Crash

    // Crash rounds are shared by every player in them,
    // so an outcome cannot depend on a single player's seed pair and nonce. 
    // Instead, each round's server seed comes from a pre-committed chain of hashes: 
    // the terminal seed is hashed once for every round still left to play. 
    // Because hashing any round's seed once yields the previous round's seed, 
    // the whole history can be walked backwards and checked against the committed terminal seed
    
    const serverSeedFromRoundNumber = (terminalSeed, roundNumber) => {
      let hash = terminalSeed;
      for (let i = 0; i < 10000000 - roundNumber; i++) {
        hash = crypto.createHash("sha256").update(hash).digest("hex");
      }
      return hash;
    };
    
    const crashPointFromSeed = (serverSeed, clientSeed, gameEdge) => {
      const hmac = crypto.createHmac("sha256", serverSeed).update(clientSeed);
      const int = parseInt(hmac.digest("hex").slice(0, 8), 16);
      return Math.max(1, (2 ** 32 / (int + 1)) * (1 - gameEdge));
    };
  • Punch

    const punchSuccessFromSeed = (seed, turn, action) => {
      const PUNCH_PAYOUT_HUNDREDTHS = {
        ["SLAP"]: 110,
        ["PUNCH"]: 150,
        ["UPPERCUT"]: 200,
        ["SPIN"]: 400,
      };
      const PUNCH_ACTIONS = Object.fromEntries(
        Object.entries(PUNCH_PAYOUT_HUNDREDTHS).map(([action, payout]) => [
          action,
          {
            multiplier: payout / 100,
            success_probability: 100 / payout,
          },
        ])
      );
      // validates seed, turn ∈ [0, PUNCH_MAX_TURNS), action
      const roll = generateFloats(seed, turn + 1)[turn];
      return roll < PUNCH_ACTIONS[action].success_probability;
    };