Разработка игры Dice на блокчейне

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка игры Dice на блокчейне
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1258
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    873
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1092
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    830

Разработка игры Dice на блокчейне

Dice — базовая игра крипто-казино: игрок выбирает число (порог) и направление (roll over / roll under), делает ставку. Если результат броска соответствует условию — выигрыш. Простейшая механика, но реализация требует внимания к математике house edge и обязательного verifiable randomness.

Математика Dice

Стандартный диапазон: 1-100 (или 0.00-99.99 в дробном варианте).

При ставке «roll over 50»: шанс выиграть = 50%, справедливый множитель = 2x. Реальный множитель с house edge 1% = 1.98x.

Формула: multiplier = (100 - houseEdge) / winProbability

При roll over 75: winProbability = 25%, multiplier = 99/25 = 3.96x. При roll under 10: winProbability = 9% (числа 1-9), multiplier = 99/9 = 11x.

Диапазон допустимых ставок: обычно roll over 2-97 и roll under 3-98 (чтобы house edge оставался разумным).

Smart contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract BlockchainDice is VRFConsumerBaseV2Plus {
    uint256 public houseEdge = 100; // 1% в basis points (10000 = 100%)
    
    struct DiceBet {
        address player;
        uint256 amount;
        uint8 target;      // 1-99
        bool isOver;       // roll over или roll under
        uint256 potentialPayout;
        bool settled;
    }
    
    mapping(uint256 => DiceBet) public bets;
    
    event BetPlaced(uint256 indexed requestId, address player, uint256 amount, uint8 target, bool isOver, uint256 payout);
    event BetResult(uint256 indexed requestId, uint8 roll, bool win, uint256 payout);
    
    function roll(uint8 target, bool isOver) external payable returns (uint256 requestId) {
        require(msg.value >= MIN_BET && msg.value <= getMaxBet(), "Invalid bet");
        require(target >= 2 && target <= 98, "Invalid target");
        
        uint256 payout = calculatePayout(msg.value, target, isOver);
        require(address(this).balance >= payout, "Insufficient bankroll");
        
        requestId = _requestVRF();
        
        bets[requestId] = DiceBet({
            player: msg.sender,
            amount: msg.value,
            target: target,
            isOver: isOver,
            potentialPayout: payout,
            settled: false,
        });
        
        emit BetPlaced(requestId, msg.sender, msg.value, target, isOver, payout);
    }
    
    function calculatePayout(
        uint256 betAmount,
        uint8 target,
        bool isOver
    ) public view returns (uint256) {
        uint256 winProbability;
        
        if (isOver) {
            winProbability = 100 - uint256(target);  // числа от target+1 до 100
        } else {
            winProbability = uint256(target) - 1;    // числа от 1 до target-1
        }
        
        require(winProbability > 0 && winProbability < 100, "Invalid probability");
        
        // multiplier = (10000 - houseEdge) / winProbability / 100
        uint256 multiplier = ((10000 - houseEdge) * 100) / winProbability;
        
        return (betAmount * multiplier) / 10000;
    }
    
    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) 
        internal override 
    {
        DiceBet storage bet = bets[requestId];
        require(!bet.settled, "Already settled");
        bet.settled = true;
        
        // Генерируем число 1-100
        uint8 roll = uint8((randomWords[0] % 100) + 1);
        
        bool win = bet.isOver ? roll > bet.target : roll < bet.target;
        
        if (win) {
            payable(bet.player).transfer(bet.potentialPayout);
        }
        
        emit BetResult(requestId, roll, win, win ? bet.potentialPayout : 0);
    }
    
    // Функция верификации: воспроизвести результат из requestId
    function verifyResult(uint256 requestId, uint256 vrfOutput) external view returns (uint8 roll, bool win) {
        DiceBet storage bet = bets[requestId];
        roll = uint8((vrfOutput % 100) + 1);
        win = bet.isOver ? roll > bet.target : roll < bet.target;
    }
    
    function getMaxBet() public view returns (uint256) {
        // Максимальная ставка = bankroll / 100 (не рискуем более 1% банкролла)
        return address(this).balance / 100;
    }
}

High-Low вариант (расширенная механика)

Популярный вариант: игрок выбирает диапазон (например, от 25 до 75), и выигрывает если roll попадает в диапазон. Более интуитивный UI.

function rollRange(uint8 lowerBound, uint8 upperBound) external payable {
    require(upperBound > lowerBound, "Invalid range");
    require(lowerBound >= 1 && upperBound <= 100);
    
    uint8 winRange = upperBound - lowerBound + 1; // включительно
    
    // Минимальный выигрышный диапазон = 3 (иначе house edge > 30%)
    require(winRange >= 3 && winRange <= 97);
    
    uint256 payout = ((10000 - houseEdge) * msg.value * 100) / (uint256(winRange) * 10000);
    // ... запрос VRF
}

UX для быстрого gameplay

Dice — это быстрая игра. Игроки делают десятки ставок в минуту. UX должен это поддерживать:

  • Быстрые пресеты (½x, 2x ставка, max bet)
  • Автоматический режим с настраиваемыми стратегиями
  • Мгновенная обратная связь (анимация кубиков, звуковые эффекты)
  • История ставок с результатами

Для массовой игры на L2 (Arbitrum, Polygon) VRF delay 3-15 секунд — приемлемо. На Ethereum mainnet может быть некомфортно.

Разработка Dice смарт-контракта с VRF — 2-3 недели. С frontend (React + анимации) и auto-режимом — 4-5 недель.