Розробка гри Plinko на блокчейні

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка гри Plinko на блокчейні
Середній
~3-5 днів
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1288
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    859

Розробка блокчейн-гри Plinko

Plinko — гра, де кулька падає через сітку штирьків, відхиляючись вліво або вправо при кожному зіткненні, і потрапляє в комірку з певним множником виграшу. Проста механіка, висока дисперсія та ефектна візуалізація роблять її популярною в крипто-казино (Stake.com, BC.Game).

Математика Plinko

Plinko — це трикутна сітка з N рядами штирьків. Кулька робить N виборів (ліво/право), остаточна позиція = кількість «правих» відхилень. Позиції розподіляються за біномісальним розподілом.

При 16 рядах (стандарт): 17 позицій (0-16). Позиція 8 (центр) — найймовірніша (~12.5%), позиції 0 та 16 — найменш вірогідні (~0.002%). Множники обернено пропорційні вірогідності приземлення.

House edge вбудований через зниження множників відносно теоретично чесних значень:

Чесний множник для позиції p з N рядами:
fair_multiplier = 1 / P(position=p) = 2^N / C(N, p)

Реальний множник = fair_multiplier * (1 - house_edge)

При 16 рядах, 1% house edge:

  • Позиція 0 або 16: ~588x (чесно ~1000x)
  • Позиція 1 або 15: ~130x
  • Позиція 8 (центр): ~0.5x (менше ставки)

Smart Contract

contract BlockchainPlinko is VRFConsumerBaseV2Plus {
    uint8 constant MAX_ROWS = 16;
    uint8 constant MIN_ROWS = 8;
    
    // Множники для кожної конфігурації (rows, risk level)
    // Індекс: [rows][risk][position] → множник в basis points
    uint256[17] public multipliersLow16;   // low risk 16 rows
    uint256[17] public multipliersMed16;   // medium risk 16 rows
    uint256[17] public multipliersHigh16;  // high risk 16 rows
    
    struct PlinkoRequest {
        address player;
        uint256 betAmount;
        uint8 rows;
        RiskLevel risk;
    }
    
    enum RiskLevel { LOW, MEDIUM, HIGH }
    
    mapping(uint256 => PlinkoRequest) public pendingDrops;
    
    function dropBall(uint8 rows, RiskLevel risk) 
        external payable returns (uint256 requestId) 
    {
        require(rows >= MIN_ROWS && rows <= MAX_ROWS, "Invalid rows");
        require(msg.value >= MIN_BET && msg.value <= getMaxBet(), "Invalid bet");
        
        requestId = _requestRandomWords(1);
        
        pendingDrops[requestId] = PlinkoRequest({
            player: msg.sender,
            betAmount: msg.value,
            rows: rows,
            risk: risk,
        });
    }
    
    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) 
        internal override 
    {
        PlinkoRequest memory drop = pendingDrops[requestId];
        delete pendingDrops[requestId];
        
        uint256 random = randomWords[0];
        
        // Симулюємо шлях кульки: кожен біт random = ліво (0) або право (1)
        uint8 rightCount = 0;
        for (uint8 i = 0; i < drop.rows; i++) {
            if ((random >> i) & 1 == 1) {
                rightCount++;
            }
        }
        
        // rightCount = остаточна позиція (0 to rows)
        uint256 multiplier = getMultiplier(drop.rows, drop.risk, rightCount);
        uint256 payout = (drop.betAmount * multiplier) / 10000;
        
        if (payout > 0) {
            payable(drop.player).transfer(payout);
        }
        
        emit BallDropped(
            requestId,
            drop.player,
            drop.rows,
            rightCount,
            multiplier,
            payout,
            random
        );
    }
    
    // Верифікація: відтворити шлях кульки з випадкового числа
    function simulatePath(uint256 random, uint8 rows) 
        public pure returns (bool[16] memory path, uint8 position) 
    {
        for (uint8 i = 0; i < rows; i++) {
            path[i] = (random >> i) & 1 == 1; // true = право
            if (path[i]) position++;
        }
    }
}

Візуалізація Фронтенду

Plinko вимагає якісної анімації — без неї гра психологічно не працює. Рекомендується Pixi.js або Matter.js (physics engine):

import * as PIXI from "pixi.js";
import Matter from "matter-js";

class PlinkoVisualizer {
  private engine: Matter.Engine;
  private render: Matter.Render;
  private pixiApp: PIXI.Application;
  
  async animateDrop(
    rows: number,
    finalPosition: number,
    path: boolean[]
  ): Promise<void> {
    // Створюємо фізичну симуляцію
    const { engine, ball } = this.setupPhysics(rows);
    
    // Направляємо кульку на бажану остаточну позицію
    // Застосовуємо невеликі бічні імпульси на кожному рядку
    for (let i = 0; i < rows; i++) {
      await this.waitForRow(ball, i);
      
      const direction = path[i] ? 1 : -1;
      Matter.Body.applyForce(ball, ball.position, {
        x: direction * 0.0005,
        y: 0,
      });
    }
    
    // Чекаємо приземлення в комірку
    await this.waitForLanding(ball);
    
    // Ефект виграшу/програшу
    this.showResult(finalPosition);
  }
  
  private showResult(position: number) {
    const cell = this.multiplierCells[position];
    
    // GSAP flash анімація
    gsap.to(cell, {
      duration: 0.1,
      backgroundColor: "#FFD700",
      yoyo: true,
      repeat: 5,
    });
    
    // Particle effect для великих множників
    if (this.multipliers[position] > 10) {
      this.playWinParticles(cell.x, cell.y);
    }
  }
}

Режими гри

Manual: гравець натискає «Drop» для кожної кульки.

Auto: автоматичні кидки з налаштуваннями — кількість кидків, зупинка при програшу X%, зупинка при виграші Y%, зміна ставки після програшу/виграшу (martingale-подібні стратегії).

class AutoPlinko {
  private stats = { totalBets: 0, totalWon: 0, totalLost: 0, streak: 0 };
  
  async runAuto(config: AutoConfig): Promise<void> {
    let currentBet = config.initialBet;
    let dropped = 0;
    
    while (dropped < config.numberOfDrops && !this.shouldStop(config)) {
      const result = await this.drop(currentBet, config.rows, config.risk);
      
      this.stats.totalBets += currentBet;
      
      if (result.win) {
        this.stats.totalWon += result.payout;
        this.stats.streak = Math.max(0, this.stats.streak) + 1;
        currentBet = config.onWin === "reset" ? config.initialBet :
                     config.onWin === "increase" ? currentBet * config.increaseMultiplier :
                     currentBet;
      } else {
        this.stats.totalLost += currentBet;
        this.stats.streak = Math.min(0, this.stats.streak) - 1;
        currentBet = config.onLoss === "reset" ? config.initialBet :
                     config.onLoss === "increase" ? currentBet * config.increaseMultiplier :
                     currentBet;
      }
      
      // Лімити ставок
      currentBet = Math.max(config.minBet, Math.min(config.maxBet, currentBet));
      dropped++;
      
      await sleep(config.dropInterval || 1000);
    }
  }
  
  private shouldStop(config: AutoConfig): boolean {
    if (config.stopOnProfit && this.stats.totalWon - this.stats.totalLost >= config.stopOnProfit) {
      return true;
    }
    if (config.stopOnLoss && this.stats.totalLost >= config.stopOnLoss) {
      return true;
    }
    return false;
  }
}

Розробка Plinko: смарт-контракт + VRF + базовий фронтенд — 3-4 тижні. З якісними анімаціями (physics, particles) та auto-режимом — 5-7 тижнів.