# Shardok Latency Hiding Strategies

## Problem Statement

With Shardok running on Hetzner (Helsinki) and Eagle on DigitalOcean (US), the round-trip latency for human commands is ~200-400ms:

```
Human posts command:
Unity → Eagle (DO) → Shardok (Hetzner) → Eagle (DO) → Unity
         ~10ms         ~100ms              ~100ms        ~10ms
                    Total: ~220ms round-trip
```

This latency is acceptable for AI turns (users watch animations anyway), but creates noticeable lag when humans post commands.

---

## Strategy 1: Client-Side Animation Masking

### Concept

Start animations immediately when the user clicks, before server confirmation arrives. The animation duration masks the network latency.

### Implementation by Command Type

**Movement Commands** (deterministic):
- Client knows the destination hex and movement path
- Start movement animation immediately on click
- Server confirms the move (should always match)
- If server rejects (invalid state), snap unit back to origin

**Attack Commands** (RNG-dependent):
- Show attack animation immediately (unit swings sword, fires arrow)
- Wait for server to return dice roll result
- Show damage numbers / hit effects when server responds
- Animation typically takes 300-500ms, masking most of the latency

**End Turn**:
- Latency not noticeable (user expects transition delay)

### Unity Implementation Sketch

```csharp
// In CommandHandler.cs
public void OnCommandSelected(Command command) {
    // Start animation immediately
    if (command.Type == CommandType.Move) {
        unitController.StartMoveAnimation(command.TargetHex);
    } else if (command.Type == CommandType.Attack) {
        unitController.StartAttackAnimation(command.TargetUnit);
    }

    // Send to server in parallel
    connection.SendCommand(command, (response) => {
        if (response.Success) {
            // Animation continues, apply result
            ApplyCommandResult(response);
        } else {
            // Rollback animation
            unitController.CancelAnimation();
            ShowError(response.ErrorMessage);
        }
    });
}
```

### Pros
- Simple implementation
- No server-side changes
- Works with existing architecture

### Cons
- Doesn't eliminate latency for attacks with RNG (must wait for dice roll)
- Rollback needed if server rejects command (rare but possible)

### Estimated Improvement
- Movement: ~200ms latency hidden (feels instant)
- Attacks: ~100-200ms hidden by animation, ~100ms visible wait for dice result

---

## Strategy 2: Split Shardok Architecture

### Concept

Run two Shardok instances:
- **Shardok-Primary (DigitalOcean)**: Handles command processing, source of truth
- **Shardok-AI (Hetzner)**: AI computation only

Human commands go to the nearby Primary for low latency. AI computation uses the powerful Hetzner instance.

### Architecture

```
                    Human commands (low latency ~20ms)
Unity ←→ Eagle ←→ Shardok-Primary (DigitalOcean)
                         ↓ state sync (when AI turn starts)
                    Shardok-AI (Hetzner)
                         ↑ AI command response
```

### How It Works

**Human Turn:**
1. Human posts command → Eagle → Shardok-Primary (DO)
2. Primary processes command immediately (~10ms local)
3. Primary streams result to client via Eagle (~10ms)
4. **Total latency: ~20ms** (vs ~220ms current)

**AI Turn:**
1. When AI's turn starts, Primary sends game state snapshot to Hetzner
2. Shardok-AI computes best command using full CPU power
3. Shardok-AI returns command index to Primary
4. Primary executes command locally and streams to client
5. Repeat until AI turn ends

### Protocol Changes

```protobuf
// New service for AI-only computation
service ShardokAIService {
    // Send game state, receive AI's chosen command
    rpc GetAICommand(AICommandRequest) returns (AICommandResponse);
}

message AICommandRequest {
    bytes game_state = 1;           // Serialized game state
    int32 player_id = 2;            // Which AI player
    repeated bytes available_commands = 3;  // Available command descriptors
}

message AICommandResponse {
    int32 command_index = 1;        // Index into available_commands
    int32 search_depth = 2;         // For debugging
    double best_score = 3;          // For debugging
}
```

### Shardok-Primary Requirements

Shardok-Primary on DigitalOcean needs to:
- Process all commands (human and AI)
- Maintain authoritative game state
- Serialize/deserialize game state for AI requests
- Run on minimal CPU (command processing is fast)

This is essentially the current Shardok, but without running the AI search.

### Shardok-AI Requirements

Shardok-AI on Hetzner needs to:
- Receive game state snapshots
- Run AI evaluation (IterativeDeepeningAI or MCTS)
- Return best command index
- No persistent state (stateless worker)

### AI Turn Latency

Each AI command has ~200ms network latency. This is acceptable because:
1. User is watching animations anyway
2. Natural pacing lets user observe AI decisions
3. AI computation is fast on Hetzner's 16 cores

For a typical AI turn with 5 commands: 5 × 200ms = 1 second network overhead, plus AI thinking time. With animations, this feels natural.

### Implementation Phases

**Phase 1: Add Shardok-Primary (minimal)**
- Deploy existing Shardok container to DigitalOcean
- Configure Eagle to use local Shardok for all commands
- Human latency immediately improves

**Phase 2: Add AI offload**
- Implement `ShardokAIService` RPC
- Shardok-Primary calls Hetzner for AI commands
- Shardok-AI processes requests statelessly

**Phase 3: Optimize**
- Batch multiple AI actions if possible
- Pre-warm Shardok-AI connection
- Add fallback if Hetzner unavailable

### Pros
- Human command latency drops from ~220ms to ~20ms
- AI still gets Hetzner's CPU power
- Clear separation of concerns
- Shardok-Primary can fall back to local AI if Hetzner unavailable

### Cons
- Two Shardok instances to maintain
- State serialization overhead for AI requests
- Each AI action has network round-trip (acceptable with animations)

---

## Comparison

| Approach | Human Latency | AI Throughput | Complexity | Changes Required |
|----------|---------------|---------------|------------|------------------|
| Current | ~220ms | High | - | - |
| Animation masking | ~220ms (perceived ~50ms) | High | Low | Unity only |
| Split architecture | ~20ms | High | Medium | New RPC, two deployments |

---

## Recommendation

**Phase 1 (now)**: Implement animation masking in Unity client
- Quick win, no server changes
- Improves perceived latency significantly for movement
- Attacks still show dice animation while waiting

**Phase 2 (future)**: Split architecture if animation masking insufficient
- Only needed if users complain about attack latency
- More complex but provides true low latency
- Natural evolution of current architecture
