Skip to main content

Overview

This example demonstrates how to integrate IOTA Repstation into a gaming platform to build comprehensive player reputation systems. Gaming is an ideal use case because player behavior significantly impacts the experience for others, and reputation can solve many common gaming problems.

Use Cases for Gaming Reputation

Player Behavior & Matchmaking

  • Skill-based matching: Combine skill level with reputation for better matches
  • Toxicity prevention: Track player behavior to reduce toxic interactions
  • Team reliability: Rate players on teamwork, communication, and commitment

Gaming Services & Trading

  • Coaching services: Rate coaches on teaching ability and professionalism
  • Boosting services: Track reliability of players offering account boosting
  • Item trading: Build trust for in-game item exchanges
  • Tournament organization: Verify player reliability for competitive events

Platform Integration

1. Player Reputation Engine

// gaming-reputation.ts
import { RepstationClient } from '@repstation/sdk';

interface PlayerProfile {
  walletAddress: string;
  gamertag: string;
  overallReputation: number;
  skillLevel: number;
  categories: {
    teamwork: number;
    communication: number;
    reliability: number;
    sportsmanship: number;
    skill: number;
  };
  badges: string[];
  riskLevel: 'low' | 'medium' | 'high';
  matchesPlayed: number;
  ratingsReceived: number;
}

interface Match {
  id: string;
  gameType: string;
  players: string[];
  duration: number;
  result: 'completed' | 'abandoned' | 'forfeited';
  reputationDealId?: string;
  startTime: Date;
  endTime?: Date;
}

class GamingReputationEngine {
  private reputation: RepstationClient;
  
  constructor() {
    this.reputation = new RepstationClient({
      network: 'mainnet',
      packageId: process.env.REPSTATION_PACKAGE_ID!
    });
  }
  
  async createMatch(
    gameType: string,
    players: string[],
    organizer: string
  ): Promise<Match> {
    // Create reputation deal for this match
    const dealResult = await this.reputation.openDealAdmin({
      signer: organizer,
      admin_cap_id: process.env.GAMING_ADMIN_CAP!,
      party_b: players[1], // In team games, might need multiple deals
      subject_ref: {
        type: 'GAMING_MATCH',
        id: `${gameType}-${Date.now()}`
      },
      amount: '0' // No monetary value, just for reputation tracking
    });
    
    const match: Match = {
      id: generateMatchId(),
      gameType,
      players,
      duration: 0,
      result: 'completed',
      reputationDealId: dealResult.deal_id,
      startTime: new Date()
    };
    
    return await this.saveMatch(match);
  }
  
  async completeMatch(
    matchId: string,
    result: 'completed' | 'abandoned' | 'forfeited',
    duration: number
  ): Promise<void> {
    const match = await this.getMatch(matchId);
    
    // Update match record
    match.result = result;
    match.duration = duration;
    match.endTime = new Date();
    await this.updateMatch(match);
    
    // Close reputation deal to enable rating
    if (match.reputationDealId) {
      await this.reputation.closeDeal({
        signer: process.env.GAMING_ADMIN_WALLET!,
        deal_id: match.reputationDealId
      });
    }
    
    // Auto-rate based on match behavior
    await this.autoRateMatchBehavior(match);
    
    // Send rating requests to players
    await this.sendPlayerRatingRequests(match);
  }
  
  async calculatePlayerProfile(walletAddress: string): Promise<PlayerProfile> {
    // Get reputation from blockchain
    const reputationResult = await this.reputation.getReputationProfile({
      walletAddress
    });
    
    if (!reputationResult.success || !reputationResult.profile) {
      return this.getDefaultPlayerProfile(walletAddress);
    }
    
    const profile = reputationResult.profile;
    
    // Get gaming-specific reputation
    const gamingReputation = profile.appScoped.find(
      app => app.appOwner === process.env.GAMING_ADMIN_WALLET
    );
    
    // Get detailed stats from database
    const playerStats = await this.getPlayerStats(walletAddress);
    const recentRatings = await this.getRecentRatings(walletAddress, 30);
    
    return {
      walletAddress,
      gamertag: playerStats.gamertag,
      overallReputation: profile.global.average,
      skillLevel: await this.calculateSkillLevel(walletAddress),
      categories: {
        teamwork: this.getCategoryAverage(recentRatings, 'teamwork'),
        communication: this.getCategoryAverage(recentRatings, 'communication'),
        reliability: this.getCategoryAverage(recentRatings, 'reliability'),
        sportsmanship: this.getCategoryAverage(recentRatings, 'sportsmanship'),
        skill: this.getCategoryAverage(recentRatings, 'skill')
      },
      badges: this.calculatePlayerBadges(profile, playerStats),
      riskLevel: this.calculateRiskLevel(profile, playerStats),
      matchesPlayed: playerStats.totalMatches,
      ratingsReceived: profile.global.count
    };
  }
  
  private async autoRateMatchBehavior(match: Match): Promise<void> {
    // Automatically rate players based on match behavior
    for (const playerWallet of match.players) {
      let behaviorScore = 85; // Base score
      
      // Penalize for abandoning matches
      if (match.result === 'abandoned') {
        behaviorScore = 30;
      } else if (match.result === 'forfeited') {
        behaviorScore = 50;
      }
      
      // Bonus for completing full matches
      if (match.result === 'completed' && match.duration >= this.getExpectedDuration(match.gameType)) {
        behaviorScore = 95;
      }
      
      // Submit behavioral rating
      if (match.reputationDealId) {
        await this.reputation.rate({
          signer: process.env.GAMING_ADMIN_WALLET!,
          deal_id: match.reputationDealId,
          score: behaviorScore,
          category: 'match_completion'
        });
      }
    }
  }
  
  private calculatePlayerBadges(profile: any, stats: any): string[] {
    const badges = [];
    
    // Experience badges
    if (stats.totalMatches >= 1000) badges.push('Veteran Player');
    else if (stats.totalMatches >= 500) badges.push('Experienced Player');
    else if (stats.totalMatches >= 100) badges.push('Regular Player');
    else if (stats.totalMatches < 10) badges.push('Newcomer');
    
    // Reputation badges
    if (profile.global.average >= 95 && profile.global.count >= 50) {
      badges.push('Legendary Player');
    } else if (profile.global.average >= 90 && profile.global.count >= 25) {
      badges.push('Elite Player');
    } else if (profile.global.average >= 85 && profile.global.count >= 10) {
      badges.push('Trusted Player');
    }
    
    // Behavior badges
    if (stats.completionRate >= 98) badges.push('Reliable');
    if (stats.toxicityReports === 0 && stats.totalMatches >= 50) badges.push('Good Sport');
    if (stats.helpfulVotes >= 20) badges.push('Helpful');
    
    // Skill badges
    if (stats.winRate >= 80) badges.push('Champion');
    else if (stats.winRate >= 70) badges.push('Skilled');
    
    return badges;
  }
}

2. Matchmaking System

// matchmaking-service.ts
class SmartMatchmaking {
  private reputation: GamingReputationEngine;
  
  constructor() {
    this.reputation = new GamingReputationEngine();
  }
  
  async findMatch(
    playerWallet: string,
    gameType: string,
    preferredSkillRange: [number, number]
  ): Promise<MatchmakingResult> {
    const playerProfile = await this.reputation.calculatePlayerProfile(playerWallet);
    
    // Get available players in queue
    const availablePlayers = await this.getQueuedPlayers(gameType);
    
    // Filter by skill and reputation compatibility
    const compatiblePlayers = await this.filterCompatiblePlayers(
      playerProfile,
      availablePlayers,
      preferredSkillRange
    );
    
    // Score potential matches
    const scoredMatches = await this.scorePlayerMatches(
      playerProfile,
      compatiblePlayers
    );
    
    // Return best match
    return this.selectBestMatch(scoredMatches);
  }
  
  private async filterCompatiblePlayers(
    player: PlayerProfile,
    availablePlayers: string[],
    skillRange: [number, number]
  ): Promise<PlayerProfile[]> {
    const compatible = [];
    
    for (const playerWallet of availablePlayers) {
      const profile = await this.reputation.calculatePlayerProfile(playerWallet);
      
      // Skill level compatibility
      if (profile.skillLevel < skillRange[0] || profile.skillLevel > skillRange[1]) {
        continue;
      }
      
      // Reputation compatibility - avoid toxic players
      if (profile.riskLevel === 'high' && player.riskLevel === 'low') {
        continue; // Don't match high-risk with low-risk players
      }
      
      // Minimum reputation threshold
      if (profile.overallReputation < 60) {
        continue;\n      }\n      \n      compatible.push(profile);\n    }\n    \n    return compatible;\n  }\n  \n  private async scorePlayerMatches(\n    player: PlayerProfile,\n    candidates: PlayerProfile[]\n  ): Promise<ScoredMatch[]> {\n    return candidates.map(candidate => {\n      let score = 0;\n      \n      // Skill similarity (prefer close skill levels)\n      const skillDiff = Math.abs(player.skillLevel - candidate.skillLevel);\n      score += Math.max(100 - skillDiff * 2, 0);\n      \n      // Reputation compatibility\n      const repDiff = Math.abs(player.overallReputation - candidate.overallReputation);\n      score += Math.max(100 - repDiff, 0);\n      \n      // Teamwork compatibility\n      if (player.categories.teamwork >= 80 && candidate.categories.teamwork >= 80) {\n        score += 50; // Bonus for both being good teammates\n      }\n      \n      // Communication compatibility\n      if (player.categories.communication >= 80 && candidate.categories.communication >= 80) {\n        score += 30;\n      }\n      \n      // Avoid repeatedly matching with toxic players\n      if (candidate.riskLevel === 'high') {\n        score -= 100;\n      }\n      \n      return {\n        player: candidate,\n        compatibilityScore: score\n      };\n    }).sort((a, b) => b.compatibilityScore - a.compatibilityScore);\n  }\n}\n\ninterface ScoredMatch {\n  player: PlayerProfile;\n  compatibilityScore: number;\n}\n\ninterface MatchmakingResult {\n  match?: {\n    players: PlayerProfile[];\n    estimatedQuality: number;\n  };\n  waitTime?: number;\n  reason?: string;\n}\n```\n\n### 3. Gaming Services Marketplace\n\n```typescript\n// gaming-services.ts\ninterface GamingService {\n  id: string;\n  provider: string;\n  type: 'coaching' | 'boosting' | 'item_trading' | 'tournament_org';\n  title: string;\n  description: string;\n  price: number;\n  gameTypes: string[];\n  requirements?: {\n    minReputation?: number;\n    maxRiskLevel?: 'low' | 'medium' | 'high';\n  };\n}\n\ninterface ServiceBooking {\n  id: string;\n  serviceId: string;\n  client: string;\n  provider: string;\n  status: 'pending' | 'active' | 'completed' | 'disputed';\n  reputationDealId?: string;\n  agreedTerms: string;\n  startTime?: Date;\n  endTime?: Date;\n}\n\nclass GamingServicesMarketplace {\n  private reputation: RepstationClient;\n  \n  constructor() {\n    this.reputation = new RepstationClient({\n      network: 'mainnet',\n      packageId: process.env.REPSTATION_PACKAGE_ID!\n    });\n  }\n  \n  async createService(\n    providerWallet: string,\n    service: Omit<GamingService, 'id' | 'provider'>\n  ): Promise<GamingService> {\n    // Verify provider reputation meets service requirements\n    const providerProfile = await this.getPlayerProfile(providerWallet);\n    \n    if (!this.meetsServiceRequirements(providerProfile, service.type)) {\n      throw new Error('Insufficient reputation to offer this service type');\n    }\n    \n    const newService: GamingService = {\n      id: generateServiceId(),\n      provider: providerWallet,\n      ...service\n    };\n    \n    return await this.saveService(newService);\n  }\n  \n  async bookService(\n    serviceId: string,\n    clientWallet: string,\n    terms: string\n  ): Promise<ServiceBooking> {\n    const service = await this.getService(serviceId);\n    \n    // Create reputation deal for the service\n    const dealResult = await this.reputation.openDealAdmin({\n      signer: process.env.GAMING_ADMIN_WALLET!,\n      admin_cap_id: process.env.GAMING_ADMIN_CAP!,\n      party_b: clientWallet,\n      subject_ref: {\n        type: 'GAMING_SERVICE',\n        id: service.type + '-' + serviceId\n      },\n      amount: service.price.toString()\n    });\n    \n    const booking: ServiceBooking = {\n      id: generateBookingId(),\n      serviceId,\n      client: clientWallet,\n      provider: service.provider,\n      status: 'pending',\n      reputationDealId: dealResult.deal_id,\n      agreedTerms: terms\n    };\n    \n    return await this.saveBooking(booking);\n  }\n  \n  async startService(bookingId: string, clientSignature: string): Promise<void> {\n    const booking = await this.getBooking(bookingId);\n    \n    // Accept the reputation deal\n    await this.reputation.acceptDeal({\n      signer: booking.client,\n      deal_id: booking.reputationDealId!\n    });\n    \n    // Update booking status\n    booking.status = 'active';\n    booking.startTime = new Date();\n    await this.updateBooking(booking);\n  }\n  \n  async completeService(\n    bookingId: string,\n    completionDetails: any\n  ): Promise<void> {\n    const booking = await this.getBooking(bookingId);\n    \n    // Close the reputation deal\n    await this.reputation.closeDeal({\n      signer: process.env.GAMING_ADMIN_WALLET!,\n      deal_id: booking.reputationDealId!\n    });\n    \n    // Update booking\n    booking.status = 'completed';\n    booking.endTime = new Date();\n    await this.updateBooking(booking);\n    \n    // Send rating requests\n    await this.sendServiceRatingRequests(booking);\n  }\n  \n  private meetsServiceRequirements(profile: PlayerProfile, serviceType: string): boolean {\n    const requirements = {\n      'coaching': {\n        minReputation: 85,\n        minSkill: 80,\n        maxRiskLevel: 'low'\n      },\n      'boosting': {\n        minReputation: 80,\n        minSkill: 90,\n        maxRiskLevel: 'medium'\n      },\n      'item_trading': {\n        minReputation: 75,\n        maxRiskLevel: 'medium'\n      },\n      'tournament_org': {\n        minReputation: 90,\n        minReliability: 95,\n        maxRiskLevel: 'low'\n      }\n    };\n    \n    const req = requirements[serviceType];\n    if (!req) return true;\n    \n    if (req.minReputation && profile.overallReputation < req.minReputation) {\n      return false;\n    }\n    \n    if (req.minSkill && profile.skillLevel < req.minSkill) {\n      return false;\n    }\n    \n    if (req.minReliability && profile.categories.reliability < req.minReliability) {\n      return false;\n    }\n    \n    const riskLevels = ['low', 'medium', 'high'];\n    if (req.maxRiskLevel && riskLevels.indexOf(profile.riskLevel) > riskLevels.indexOf(req.maxRiskLevel)) {\n      return false;\n    }\n    \n    return true;\n  }\n}\n```\n\n### 4. Player Rating System\n\n```typescript\n// player-rating.ts\ninterface PlayerRating {\n  matchId?: string;\n  serviceId?: string;\n  rater: string;\n  ratee: string;\n  categories: {\n    teamwork?: number;\n    communication?: number;\n    reliability?: number;\n    sportsmanship?: number;\n    skill?: number;\n    teaching?: number; // For coaches\n    professionalism?: number; // For service providers\n  };\n  comment?: string;\n  context: 'match' | 'coaching' | 'boosting' | 'trading' | 'tournament';\n}\n\nclass PlayerRatingSystem {\n  private reputation: RepstationClient;\n  \n  constructor() {\n    this.reputation = new RepstationClient({\n      network: 'mainnet',\n      packageId: process.env.REPSTATION_PACKAGE_ID!\n    });\n  }\n  \n  async ratePlayer(\n    rating: PlayerRating,\n    dealId: string\n  ): Promise<void> {\n    // Submit ratings for each category\n    for (const [category, score] of Object.entries(rating.categories)) {\n      if (score !== undefined) {\n        await this.reputation.rate({\n          signer: rating.rater,\n          deal_id: dealId,\n          score,\n          category: `${rating.context}_${category}`\n        });\n      }\n    }\n    \n    // Store detailed rating in database\n    await this.saveDetailedRating(rating);\n  }\n  \n  async getPlayerReputationSummary(walletAddress: string): Promise<ReputationSummary> {\n    const profile = await this.reputation.getReputationProfile({ walletAddress });\n    \n    if (!profile.success || !profile.profile) {\n      return this.getDefaultReputationSummary(walletAddress);\n    }\n    \n    // Get category breakdowns\n    const categories = await this.getCategoryBreakdowns(walletAddress);\n    const badges = await this.getPlayerBadges(walletAddress);\n    const recentTrend = await this.getReputationTrend(walletAddress, 30);\n    \n    return {\n      overall: profile.profile.global,\n      categories,\n      badges,\n      trend: recentTrend,\n      riskAssessment: this.assessPlayerRisk(profile.profile)\n    };\n  }\n}\n\ninterface ReputationSummary {\n  overall: { average: number; count: number };\n  categories: {\n    [key: string]: { average: number; count: number };\n  };\n  badges: string[];\n  trend: 'improving' | 'stable' | 'declining';\n  riskAssessment: {\n    level: 'low' | 'medium' | 'high';\n    factors: string[];\n  };\n}\n```\n\n### 5. Gaming UI Components\n\n```tsx\n// components/PlayerCard.tsx\nimport React, { useState, useEffect } from 'react';\n\ninterface PlayerCardProps {\n  walletAddress: string;\n  showDetailed?: boolean;\n  context: 'matchmaking' | 'service' | 'tournament';\n}\n\nexport function PlayerCard({ walletAddress, showDetailed = false, context }: PlayerCardProps) {\n  const [profile, setProfile] = useState<PlayerProfile | null>(null);\n  const [loading, setLoading] = useState(true);\n  \n  useEffect(() => {\n    loadPlayerProfile();\n  }, [walletAddress]);\n  \n  const loadPlayerProfile = async () => {\n    try {\n      const reputation = new GamingReputationEngine();\n      const playerProfile = await reputation.calculatePlayerProfile(walletAddress);\n      setProfile(playerProfile);\n    } catch (error) {\n      console.error('Failed to load player profile:', error);\n    } finally {\n      setLoading(false);\n    }\n  };\n  \n  if (loading) return <div className=\"player-card loading\">Loading...</div>;\n  if (!profile) return <div className=\"player-card error\">Failed to load</div>;\n  \n  return (\n    <div className={`player-card ${profile.riskLevel}-risk`}>\n      <div className=\"player-header\">\n        <div className=\"gamertag\">{profile.gamertag}</div>\n        <div className=\"reputation-score\">\n          <span className=\"score\">{profile.overallReputation}</span>\n          <span className=\"max\">/100</span>\n        </div>\n      </div>\n      \n      <div className=\"player-badges\">\n        {profile.badges.slice(0, 3).map((badge, index) => (\n          <span key={index} className={`badge ${badge.toLowerCase().replace(' ', '-')}`}>\n            {badge}\n          </span>\n        ))}\n      </div>\n      \n      {showDetailed && (\n        <div className=\"detailed-stats\">\n          <div className=\"skill-level\">\n            <label>Skill Level:</label>\n            <div className=\"skill-bar\">\n              <div className=\"fill\" style={{ width: `${profile.skillLevel}%` }} />\n              <span>{profile.skillLevel}/100</span>\n            </div>\n          </div>\n          \n          <div className=\"category-breakdown\">\n            {Object.entries(profile.categories).map(([category, score]) => (\n              <div key={category} className=\"category\">\n                <label>{category.charAt(0).toUpperCase() + category.slice(1)}:</label>\n                <span className=\"score\">{score}/100</span>\n              </div>\n            ))}\n          </div>\n          \n          <div className=\"match-stats\">\n            <div className=\"stat\">\n              <label>Matches:</label>\n              <span>{profile.matchesPlayed}</span>\n            </div>\n            <div className=\"stat\">\n              <label>Ratings:</label>\n              <span>{profile.ratingsReceived}</span>\n            </div>\n          </div>\n        </div>\n      )}\n      \n      <TrustIndicator profile={profile} context={context} />\n    </div>\n  );\n}\n\nfunction TrustIndicator({ profile, context }: { profile: PlayerProfile; context: string }) {\n  const getTrustMessage = () => {\n    switch (context) {\n      case 'matchmaking':\n        if (profile.riskLevel === 'low') return '✓ Recommended teammate';\n        if (profile.riskLevel === 'medium') return '⚠ Average reliability';\n        return '⚠ Potential match issues';\n        \n      case 'service':\n        if (profile.overallReputation >= 90) return '✓ Highly trusted provider';\n        if (profile.overallReputation >= 75) return '✓ Reliable service';\n        return '⚠ New or unproven provider';\n        \n      case 'tournament':\n        if (profile.categories.reliability >= 90) return '✓ Tournament ready';\n        return '⚠ May have commitment issues';\n        \n      default:\n        return '';\n    }\n  };\n  \n  return (\n    <div className={`trust-indicator ${profile.riskLevel}`}>\n      {getTrustMessage()}\n    </div>\n  );\n}\n```\n\n## Key Benefits\n\n### For Gaming Platforms\n- **Better Matchmaking**: Match players based on skill AND behavior\n- **Toxicity Reduction**: Track and prevent toxic player interactions\n- **Service Quality**: Verify coaches, boosters, and service providers\n- **Tournament Organization**: Ensure reliable players for competitive events\n\n### For Players\n- **Better Matches**: Play with similarly skilled and well-behaved players\n- **Trusted Services**: Find reliable coaches and service providers\n- **Recognition**: Build reputation that follows across games\n- **Opportunities**: Access to tournaments and premium services\n\n### For Game Developers\n- **Community Health**: Improve overall player experience\n- **Retention**: Players stay longer in positive environments\n- **Monetization**: Enable trusted player-to-player services\n- **Competitive Integrity**: Better tournament and ranked play\n\nThis gaming platform example shows how IOTA Repstation can solve real trust and behavior problems in gaming communities, creating better experiences for all players.\n