# Generic Slot Engine Framework - Complete Documentation

## Overview

CoinKrazy's Generic Slot Engine is a production-ready framework that enables your team to create unlimited original slot games without starting from scratch. The engine handles all core functionality: RNG, symbol management, payline calculations, bonus features, animations, and backend integration.

## Architecture

```
client/src/lib/slotEngine/
├── SlotEngine.ts           # Core engine with RNG and spin logic
├── SymbolSystem.ts         # Symbol definitions and payline calculations
├── BonusFeatures.ts        # Bonus round implementations
├── SlotAnimations.ts       # GSAP animations and effects
└── GameConfigs.ts          # Example games and factory

server/routers/
└── slotEngine.ts           # tRPC backend procedures
```

## Quick Start - Creating Your First Game

### Step 1: Use an Example Game

```typescript
import { GameFactory } from '@/lib/slotEngine/GameConfigs';

// Get an existing game template
const gameConfig = GameFactory.getGame('easter_egg');

// Or customize it
const customGame = GameFactory.createCustomGame('easter_egg', {
  name: 'My Easter Game',
  minBet: 0.5,
  maxBet: 100,
  rtp: 95.0,
});
```

### Step 2: Initialize the Slot Engine

```typescript
import { SlotEngine } from '@/lib/slotEngine/SlotEngine';

const engine = new SlotEngine(gameConfig);
const spinResult = engine.spin(betAmount);

console.log(spinResult);
// {
//   reels: [['symbol1', 'symbol2', 'symbol3'], ...],
//   wins: [...],
//   totalWin: 150,
//   featuresTriggered: [...],
//   rngSeed: 'abc123'
// }
```

### Step 3: Handle Animations

```typescript
import { ReelAnimations, WinAnimations } from '@/lib/slotEngine/SlotAnimations';

// Spin reels
await ReelAnimations.spinAllReels(reelElements, [0.5, 0.6, 0.7]);

// Animate wins
for (const win of spinResult.wins) {
  await WinAnimations.highlightWinLine(winLineElement);
}

// Big win celebration
if (spinResult.totalWin > betAmount * 50) {
  await WinAnimations.animateBigWin(celebrationElement);
}
```

### Step 4: Save to Backend

```typescript
const response = await trpc.slotEngine.recordSpin.mutate({
  gameId: 'easter_egg',
  betAmount: 10,
  currency: 'gc',
  spinData: spinResult,
});
```

## Core Components

### 1. SlotEngine - The Heart of the System

**Features:**
- Fair RNG with seed support for replay
- Configurable reel strips and symbol weights
- Automatic payline evaluation
- Feature trigger detection
- RTP compliance

**Key Methods:**
```typescript
engine.spin(betAmount): SpinResult
engine.setSeed(seed): void
engine.getPaylineWins(reels): Win[]
engine.calculatePayout(wins, betAmount): number
```

### 2. SymbolSystem - Symbol Management

**Create Symbols:**
```typescript
// Regular symbol
SymbolSystem.createSymbol('cherry', 'Cherry', { 3: 10, 2: 5 }, weight: 8)

// Wild symbol
SymbolSystem.createWild('wild', weight: 5)

// Scatter symbol
SymbolSystem.createScatter('scatter', weight: 3)

// Bonus symbol
SymbolSystem.createBonus('bonus', 'Bonus Round', weight: 2)
```

**Create Paylines:**
```typescript
// 3x3 grid with 5 paylines
PaylineSystem.create3x3Paylines()

// 5x3 grid with 25 paylines
PaylineSystem.create5x3Paylines()

// Custom paylines
PaylineSystem.createCustomPaylines([
  [0, 0, 0],  // Top line
  [1, 1, 1],  // Middle line
  [2, 2, 2],  // Bottom line
])
```

### 3. BonusFeatures - Bonus Rounds

#### Respin Feature
```typescript
const bonus = RespinFeature.executeRespin(
  engine,
  stickyPositions,  // Positions that stay in place
  betAmount,
  maxRespins: 3
);
```

#### Free Spins
```typescript
const bonus = FreeSpinsFeature.executeFreeSpins(
  engine,
  freeSpinCount: 10,
  betAmount,
  multiplier: 2
);
```

#### Collect & Win
```typescript
const bonus = CollectAndWinFeature.executeCollectAndWin(
  engine,
  initialCoins,
  betAmount,
  maxRespins: 5
);
```

#### Pick Bonus
```typescript
const bonus = PickBonusFeature.executePickBonus(
  itemCount: 9,
  revealCount: 3
);
```

#### Wheel Spin
```typescript
const bonus = WheelSpinFeature.executeWheelSpin();
```

### 4. SlotAnimations - Visual Effects

**Reel Animations:**
```typescript
// Spin a single reel
await ReelAnimations.spinReel(element, duration, rotations, ease);

// Spin all reels with staggered timing
await ReelAnimations.spinAllReels(elements, durations);

// Stop reel at final position
await ReelAnimations.stopReel(element, finalPosition, duration);
```

**Win Animations:**
```typescript
// Highlight winning payline
await WinAnimations.highlightWinLine(lineElement, duration);

// Collect coins to balance
await WinAnimations.animateCoinCollection(coinElement, targetElement);

// Big win celebration
await WinAnimations.animateBigWin(element, duration);

// Screen shake effect
await WinAnimations.shakeScreen(element, intensity, duration);
```

**Particle Effects:**
```typescript
// Confetti celebration
await ParticleEffects.createConfetti(container, particleCount, duration);

// Explosion effect
await ParticleEffects.createExplosion(container, x, y, particleCount, duration);
```

**UI Animations:**
```typescript
// Animate balance update
await UIAnimations.animateBalanceUpdate(element, fromValue, toValue, duration);

// Button click effect
await UIAnimations.animateButtonClick(element, duration);

// Fade in/out
await UIAnimations.fadeIn(element, duration);
await UIAnimations.fadeOut(element, duration);
```

## Creating Custom Games

### Method 1: Modify Existing Template

```typescript
const myGame = GameFactory.createCustomGame('fantasy_wizard', {
  name: 'My Custom Wizard Game',
  minBet: 1,
  maxBet: 500,
  rtp: 96.5,
  volatility: 'high',
  theme: {
    name: 'Dark Fantasy',
    backgroundColor: '#1a0033',
    accentColor: '#FF00FF',
  },
});

GameFactory.registerTemplate('my_wizard_game', myGame);
```

### Method 2: Build from Scratch

```typescript
const myCustomGame: GameConfig = {
  id: 'my_game',
  name: 'My Original Game',
  reels: 5,
  rows: 3,
  paylines: PaylineSystem.create5x3Paylines(),
  symbols: [
    SymbolSystem.createSymbol('symbol1', 'Symbol 1', { 3: 25, 2: 10 }, 10),
    SymbolSystem.createSymbol('symbol2', 'Symbol 2', { 3: 20, 2: 8 }, 10),
    SymbolSystem.createSymbol('symbol3', 'Symbol 3', { 3: 15, 2: 5 }, 10),
    SymbolSystem.createWild('wild', 5),
    SymbolSystem.createScatter('scatter', 3),
  ],
  minBet: 0.1,
  maxBet: 100,
  rtp: 96.0,
  volatility: 'medium',
  features: [
    {
      id: 'wild_feature',
      name: 'Wild',
      type: 'wild',
      triggerSymbol: 'wild',
      config: { substitutesAll: true },
    },
    {
      id: 'free_spins',
      name: 'Free Spins',
      type: 'free_spins',
      triggerSymbol: 'scatter',
      triggerCount: 3,
      config: { freeSpins: 10, multiplier: 2 },
    },
  ],
  theme: {
    name: 'Custom',
    backgroundColor: '#000000',
    accentColor: '#FFFFFF',
  },
};

const engine = new SlotEngine(myCustomGame);
```

## Backend Integration

### Recording Spins

```typescript
const response = await trpc.slotEngine.recordSpin.mutate({
  gameId: 'my_game',
  betAmount: 10,
  currency: 'gc',
  spinData: spinResult,
});

// Returns:
// {
//   success: true,
//   spinId: 'spin_1234567890',
//   newBalance: { gc: 990, sc: 500 },
//   payout: 150
// }
```

### Getting Game Statistics

```typescript
const stats = await trpc.slotEngine.getGameStats.query({
  gameId: 'my_game',
});

// Returns:
// {
//   totalSpins: 1000,
//   totalBet: 10000,
//   totalWin: 9500,
//   rtp: 95.0,
//   averageWin: 9.5,
//   biggestWin: 500,
//   lastPlayedAt: Date
// }
```

### Getting Leaderboard

```typescript
const leaderboard = await trpc.slotEngine.getLeaderboard.query({
  gameId: 'my_game',
  timeframe: 'week',
  limit: 10,
});
```

## Example: Complete Game Implementation

```typescript
import { SlotEngine } from '@/lib/slotEngine/SlotEngine';
import { GameFactory } from '@/lib/slotEngine/GameConfigs';
import { ReelAnimations, WinAnimations, ParticleEffects } from '@/lib/slotEngine/SlotAnimations';
import { trpc } from '@/lib/trpc';

export function MySlotGame() {
  const [balance, setBalance] = useState(1000);
  const [betAmount, setBetAmount] = useState(10);
  const [spinning, setSpinning] = useState(false);

  const gameConfig = GameFactory.getGame('easter_egg');
  const engine = new SlotEngine(gameConfig);

  const handleSpin = async () => {
    if (balance < betAmount) return;

    setSpinning(true);
    setBalance(balance - betAmount);

    // Spin reels
    const spinResult = engine.spin(betAmount);
    const reelElements = document.querySelectorAll('.reel');

    // Animate reels
    await ReelAnimations.spinAllReels(
      Array.from(reelElements) as HTMLElement[],
      [0.5, 0.6, 0.7]
    );

    // Animate wins
    for (const win of spinResult.wins) {
      await WinAnimations.highlightWinLine(
        document.querySelector(`[data-payline="${win.paylineId}"]`) as HTMLElement
      );
    }

    // Big win celebration
    if (spinResult.totalWin > betAmount * 50) {
      await ParticleEffects.createConfetti(
        document.querySelector('.game-container') as HTMLElement,
        50,
        2
      );
      await WinAnimations.animateBigWin(
        document.querySelector('.win-display') as HTMLElement
      );
    }

    // Update balance
    const newBalance = balance - betAmount + spinResult.totalWin * betAmount;
    setBalance(newBalance);

    // Record to backend
    await trpc.slotEngine.recordSpin.mutate({
      gameId: 'easter_egg',
      betAmount,
      currency: 'gc',
      spinData: spinResult,
    });

    setSpinning(false);
  };

  return (
    <div className="slot-game">
      <div className="reels">
        {/* Reel elements */}
      </div>
      <div className="controls">
        <input
          type="number"
          value={betAmount}
          onChange={(e) => setBetAmount(Number(e.target.value))}
          disabled={spinning}
        />
        <button onClick={handleSpin} disabled={spinning || balance < betAmount}>
          {spinning ? 'Spinning...' : 'Spin'}
        </button>
      </div>
      <div className="balance">Balance: {balance.toFixed(2)}</div>
    </div>
  );
}
```

## Performance Optimization

### RNG Seeding for Replay
```typescript
// Record seed for later replay
const seed = engine.generateSeed();
const result1 = engine.spin(10);

// Later, replay the same spin
engine.setSeed(seed);
const result2 = engine.spin(10);

// result1 === result2 (deterministic)
```

### Caching Game Configurations
```typescript
// Cache at app level
const gameCache = new Map<string, GameConfig>();

function getGameConfig(gameId: string): GameConfig {
  if (!gameCache.has(gameId)) {
    gameCache.set(gameId, GameFactory.getGame(gameId)!);
  }
  return gameCache.get(gameId)!;
}
```

### Lazy Loading Animations
```typescript
// Only load animation library when needed
const ReelAnimations = await import('@/lib/slotEngine/SlotAnimations');
```

## Testing

### Unit Tests for RNG
```typescript
test('RNG produces different results', () => {
  const engine = new SlotEngine(gameConfig);
  const result1 = engine.spin(10);
  const result2 = engine.spin(10);
  expect(result1).not.toEqual(result2);
});

test('Seeded RNG is deterministic', () => {
  const engine = new SlotEngine(gameConfig);
  const seed = engine.generateSeed();
  
  engine.setSeed(seed);
  const result1 = engine.spin(10);
  
  engine.setSeed(seed);
  const result2 = engine.spin(10);
  
  expect(result1).toEqual(result2);
});
```

### Integration Tests
```typescript
test('Complete game flow', async () => {
  const engine = new SlotEngine(gameConfig);
  const result = engine.spin(10);
  
  const response = await trpc.slotEngine.recordSpin.mutate({
    gameId: 'test_game',
    betAmount: 10,
    currency: 'gc',
    spinData: result,
  });
  
  expect(response.success).toBe(true);
  expect(response.newBalance).toBeDefined();
});
```

## Compliance & Regulations

- **RNG Certification**: The engine uses cryptographically secure random number generation
- **RTP Compliance**: Configure RTP per game to meet regulatory requirements
- **Audit Trail**: All spins are recorded with timestamps and seeds for replay
- **Fairness**: Paylines and symbol weights are transparent and configurable

## Troubleshooting

### Issue: Animations not playing
**Solution**: Ensure DOM elements exist before calling animation methods
```typescript
const element = document.querySelector('.reel');
if (element) {
  await ReelAnimations.spinReel(element);
}
```

### Issue: Incorrect payouts
**Solution**: Verify symbol weights and payline definitions
```typescript
const stats = GameFactory.getGameStats('my_game');
console.log(stats); // Check symbol count and paylines
```

### Issue: Performance lag
**Solution**: Use requestAnimationFrame and consider reducing particle count
```typescript
// Reduce particles for lower-end devices
const particleCount = isMobile ? 20 : 50;
await ParticleEffects.createConfetti(container, particleCount, 2);
```

## Next Steps

1. **Create Your First Game**: Use GameFactory to customize an existing template
2. **Test Thoroughly**: Run unit tests and play multiple spins
3. **Integrate with Backend**: Wire up recordSpin and analytics
4. **Deploy**: Push to production and monitor player engagement
5. **Iterate**: Gather feedback and add new features

## Support

For issues or questions:
1. Check the example games in GameConfigs.ts
2. Review the inline documentation in each module
3. Test with the provided unit tests
4. Check backend logs for spin recording issues

---

**Version**: 1.0.0  
**Last Updated**: April 2026  
**Maintainer**: CoinKrazy Development Team
