# Eagle0 AI Scoring System: Technical Documentation

## Overview

The Eagle0 AI scoring system is a sophisticated game state evaluation framework designed for the Shardok tactical combat layer. It uses a combination of immediate and lookahead scoring, handles both deterministic and non-deterministic commands, and employs different strategies for attackers and defenders.

## Architecture

### Main Entry Points

The `AIScoreCalculator` class provides four main entry points:

1. **`GuessedStateScore`** - Evaluates a game state based on the player's role (attacker/defender) and strategy
2. **`BestCommandIndex`** - Finds the best command from available options using lookahead search
3. **`CommandScore`** - Evaluates a specific command's score
4. **`EvaluateCommand`** - Lower-level command evaluation returning both immediate and lookahead scores

### Scoring Pipeline Flow

```
BestCommandIndex
    ├── AICommandFilter::FilterCommands (reduce search space)
    ├── For each filtered command:
    │   ├── Determine command type (deterministic/non-deterministic/has odds)
    │   ├── CalcOne (execute command with appropriate randomness)
    │   │   ├── Create inner engine copy
    │   │   ├── Execute command
    │   │   ├── GuessedStateScore (immediate evaluation)
    │   │   └── BasicLookaheadCalculator (recursive lookahead)
    │   └── Aggregate scores based on command type
    └── Select command with best lookahead score (tiebreak on immediate)
```

## Core Scoring Components

### 1. State Evaluation (`GuessedStateScore`)

The state scorer delegates to strategy-specific evaluators:

**Attacker Strategies:**
- `STRATEGY_ATTACK_CASTLES` - Prioritizes capturing castle positions
- `STRATEGY_ATTACK_UNITS` - Focuses on eliminating defender units
- `STRATEGY_HOLD_CASTLES` - Maintains control of captured castles
- `STRATEGY_CROSS_RIVERS` - Special water crossing objectives
- `STRATEGY_FLEE` - Escape-focused scoring

**Defender Strategies:**
- `STRATEGY_HOLD_CASTLES` - Defend critical castle positions
- `STRATEGY_SCATTER` - Spread units to avoid elimination
- `STRATEGY_FLEE` - Escape-focused scoring

### 2. Unit Value Calculation (`AIUnitScoreCalculator`)

Unit scores are computed using multiple factors:

**Base Unit Value:**
```cpp
battalionValue = battalionTypeMultiplier * (0.5 + armament/100) * 
                 (0.5 + training/100) * (0.5 + morale/100) * battalion.size
heroValue = max(0, kHeroExistenceBuf + statsValue + professionValue + vigorValue)
contextFreeValue = battalionValue + heroValue
```

**Battalion Type Multipliers:**
- Light Infantry: 1.0
- Heavy Infantry/Light Cavalry: 1.5
- Heavy Cavalry: 2.0
- Longbowmen: 1.25
- Undead: 0.25

**Contextual Modifiers:**
- Castle bonus: `1 + kCastleMultiplierBonus * (integrity + 25) / 100`
- On fire penalty: 0.25x multiplier
- Adjacent fire: 0.99x per adjacent fire
- On ice penalty: Based on ice integrity
- VIP in danger: -200 if VIP unit < 200 size and in enemy attack range

**Special Unit Considerations:**
- Undead value decreases with distance from enemies: `value / (1 + minimumDistance)`
- Defenders that attackers must kill (when not targeting castles): +200 existence bonus
- Controlled undead this round: +50 bonus

### 3. Victory Condition Scoring (`AIVictoryConditionScoreCalculator`)

**Critical Tile Holdings:**
- Attacker holding tile with claimable unit: 0 penalty
- Castle on fire: -200 * distance debuff to extinguishing position
- Unoccupied/held by unclaimable: -100 * distance debuff
- Defender-held: Varies based on unit value and distance

**Last Player Standing:**
- -200 per surviving enemy unit * distance debuff

### 4. Distance-Based Scoring

The system uses sophisticated distance calculations incorporating:
- Action point distances (movement cost)
- Brave water crossing capability
- Attack location analysis (adjacent, archery, mage, engineer positions)

**Distance Debuff Formula:**
```cpp
distanceDebuff = kMaxProximityBuf / (1 + distance / kDistanceDebufRatio)
where kMaxProximityBuf = 1.5, kDistanceDebufRatio = 8.0
```

## Command Type Handling

### Deterministic Commands
Commands with predictable outcomes (MOVE, CONTROL, END_TURN, etc.):
- Evaluated once with average random value (0.5)
- No repeated simulations needed

### Commands with Odds
Commands with success/failure chances (SCOUT, FEAR, etc.):
- Two evaluations: success case (high roll) and failure case (low roll)
- Final score: `lerp(failureScore, successScore, successChance)`
- Success roll: `1.0 - successChance/2`
- Failure roll: `(1.0 - successChance)/2`

### Non-Deterministic Commands
Commands with variable outcomes (MELEE, ARCHERY, etc.):
- Multiple evaluations with different random seeds
- Default: `maxRepeatCount` iterations (typically 3-5)
- Random values evenly distributed: `i / (maxRepeatCount - 1)`
- Final score: average of all evaluations

## Lookahead Search

The system uses recursive lookahead with:
- Configurable depth (`remainingLookahead` parameter)
- Asynchronous execution for parallelization
- Early termination on END_TURN commands
- Score propagation from future states

## Command Filtering

`AICommandFilter` reduces search space by eliminating obviously bad moves:

**Filtered Actions:**
- Meteor start when >4 hexes from enemies AND castles (attackers only)
- Fire spells not adjacent to enemies (attackers only)
- Fortify when far from objectives (attackers)
- Retreating/fleeing when winning
- Moving away from all enemies when outnumbered
- Abandoning last defender in critical castle

## Key Constants and Multipliers

### Unit Scoring
- `UNITS_BASE_MULTIPLIER`: 0.05
- `FLEE_UNIT_SCORE`: -10,000
- `CAPTURED_UNIT_SCORE`: -10,000
- `CAPTURED_VIP_SCORE`: -25,000
- `kHeroExistenceBuf`: 50
- `kProfessionValue`: 200

### Ranged Attack Values
- `kArcheryPossibleValue`: 38
- `kMeteorDirectTargetingEnemy`: 2 per soldier
- `kMeteorSplashTargetingEnemy`: 1 per soldier
- `kLightningPossibleValue`: 0.05 per soldier

### Victory Condition Values
- `MAX_DEFENDER_HELD_VALUE`: -1,200
- `UNHELD_VALUE`: 100
- `ON_FIRE_VALUE`: 200
- `SURVIVING_ENEMY_VALUE`: -200

## Score Aggregation

Final score calculation:
```cpp
score = UNITS_BASE_MULTIPLIER * roundsMultiplier * unitsTotal + victoryConditionTotal
```

Where:
- `roundsMultiplier = roundsRemaining / maxRounds`
- `unitsTotal` = sum of all unit values (attacker positive, defender negative)
- `victoryConditionTotal` = sum of victory condition scores

## Performance Optimizations

1. **Command Filtering**: Reduces search space by 30-70% on average
2. **Parallel Lookahead**: Async execution of future state evaluations
3. **Cached Distance Calculations**: ActionPointDistances and AttackLocations caching
4. **Early Game/Late Game Differentiation**: Simplified calculations after round 18
5. **Multithreading**: Controlled by `MULTITHREAD` compile flag

## Implementation Notes

### Random Number Generation
- Uses `SequenceRandomGenerator` for deterministic testing
- Multiple random seeds for non-deterministic command evaluation
- Carefully controlled randomness for consistent AI behavior

### Distance Calculations
- **Action Point Distances**: Accounts for movement costs, terrain, water crossing
- **Attack Locations**: Pre-computed valid attack positions for units
- **Caching**: Expensive distance calculations are cached and reused

### Strategy Selection
- Attackers use `AIAttackerStrategySelector` to choose appropriate strategy
- Defenders use `AIDefenderStrategySelector` based on game state
- Strategy affects unit valuations and objective prioritization

### Score Interpretation
- **Positive scores**: Favor the evaluating player
- **Negative scores**: Favor the opponent
- **Magnitude**: Indicates confidence/importance of the evaluation
- **Relative scoring**: Only score differences matter, not absolute values

This scoring system provides a robust framework for tactical AI decision-making, balancing immediate tactical gains with strategic objectives while handling the uncertainty inherent in combat outcomes.

## AIScoreCalculator Function Reference

### Public Interface Functions

#### `GuessedStateScore`
**Purpose**: Evaluates the score of a game state from the perspective of the current AI strategy without performing any commands.

**Parameters**:
- `isDefender`: Whether the AI is playing as defender
- `state`: Current game state to evaluate
- `aiStrategy`: Strategy being used (attack castles, hold castles, scatter, etc.)
- `allCastleCoords`: Set of all castle coordinates on the map
- `settingsGetter`: Game configuration and rules
- `apdCache`: Cached action point distances for movement calculations
- `alCache`: Cached attack locations for combat calculations

**Returns**: Score value representing how favorable the state is for the evaluating player (positive = good, negative = bad)

#### `CommandScore`
**Purpose**: Evaluates the score for a specific command using lookahead search to consider future consequences.

**Parameters**:
- `pid`: Player ID executing the command
- `isDefender`: Whether the player is a defender
- `remainingLookahead`: Depth of recursive search remaining
- `maxRepeatCount`: Number of random simulations for non-deterministic commands
- `guessedEngine`: Current game engine state
- `attackerStrategy`: Strategy being used by attackers
- `currentUtility`: Current game state score before command execution
- `settingsGetter`: Game configuration
- `allCastleCoords`: Castle locations
- `apdCache` & `alCache`: Cached distance/attack calculations
- `commandIndex`: Index of command to evaluate
- `deadline`: Time limit for computation

**Returns**: Future containing the final score after lookahead evaluation

### Internal Core Functions

#### `BuildDecisionTree` (NEW)
**Purpose**: Builds a complete decision tree containing all evaluated command paths up to the specified depth.

**Process**:
1. Filters commands using `AICommandFilter` to reduce search space
2. For each command, calls `ExecuteCommandForTree` to build complete subtrees
3. Returns full tree with all possible moves and their consequences
4. Identifies best command within the complete tree structure

**Returns**: `std::future<CommandDecisionTree>` containing the complete decision tree

#### `BestCommandIndex` (Legacy - Wrapper)
**Purpose**: Backward compatibility wrapper that uses `BuildDecisionTree` but returns traditional `IndexAndScore`.

**Process**:
1. Calls `BuildDecisionTree` to get complete tree
2. Extracts best command information for compatibility
3. Returns only the optimal command details in legacy format

#### `ExecuteCommandForTree` (NEW)
**Purpose**: Executes a command and creates a tree node with the resulting game state and scores.

**Process**:
1. Creates engine copy and executes the command with given random seed
2. Creates `CommandTreeNode` with command results and game state
3. Calculates immediate score using `GuessedStateScore`
4. Calls `RecursiveTreeBuilder` to populate child nodes if depth allows
5. Calculates lookahead score from children (or uses immediate score)

**Returns**: `std::unique_ptr<CommandTreeNode>` containing the command execution results and subtree

#### `RecursiveTreeBuilder` (NEW)
**Purpose**: Recursively populates child nodes of a tree node by building subtrees for subsequent moves.

**Process**:
1. Gets available commands for the next player
2. Filters commands to reduce search space
3. For each command, calls `ExecuteCommandForTree` to create child nodes
4. Handles different command types (deterministic, odds-based, random)
5. Populates the parent node's children vector with complete subtrees

#### `CalcOne` (Legacy)
**Purpose**: Executes a single command simulation with specified randomness and returns both immediate and lookahead scores.

**Process**:
1. Creates engine copy and executes the command with given random seed
2. Calculates immediate score using `GuessedStateScore`
3. Initiates recursive lookahead calculation if depth remains
4. Handles timeouts gracefully by returning default scores

#### `EvaluateCommand`
**Purpose**: Lower-level command evaluation that handles different command types appropriately.

**Command Type Handling**:
- **Deterministic**: Single evaluation with average randomness (0.5)
- **Odds-based**: Two evaluations (success/failure) weighted by success probability
- **Non-deterministic**: Multiple evaluations with distributed random values, averaged

#### `BasicLookaheadCalculator`
**Purpose**: Recursive lookahead search that finds the best future command sequence and propagates scores backward.

**Features**:
- Uses transposition table to cache previously computed positions
- Handles depth limits and terminal states
- Returns futures for asynchronous computation
- Stores results in transposition table for reuse

### Strategy-Specific Scoring Functions

#### `AttackerScoreForState`
**Purpose**: Calculates state score from attacker perspective based on strategy type.

**Strategy Support**:
- `STRATEGY_ATTACK_CASTLES`: Prioritizes capturing castle positions
- `STRATEGY_ATTACK_UNITS`: Focuses on eliminating defender units
- `STRATEGY_HOLD_CASTLES`: Maintains control of captured castles
- `STRATEGY_CROSS_RIVERS`: Special water crossing objectives
- `STRATEGY_FLEE`: Escape-focused scoring

#### `DefenderScoreForState`
**Purpose**: Calculates state score from defender perspective.

**Strategy Support**:
- `STRATEGY_HOLD_CASTLES`: Defend critical castle positions
- `STRATEGY_SCATTER`: Spread units to avoid elimination
- `STRATEGY_FLEE`: Escape-focused scoring

#### `AttackerUnitsScore`
**Purpose**: Core unit valuation function that calculates total value of all units on the board with contextual modifiers.

**Features**:
- Uses `UnitValue` for individual unit calculations
- Applies distance multipliers based on proximity to objectives
- Handles special cases like undead, VIP units, and scattered defenders
- Incorporates castle bonuses and environmental penalties

### Specialized Strategy Functions

#### `DefenderScatterStrategyScoreForState`
**Purpose**: Implements scatter strategy scoring that rewards defensive units for staying far from enemies and friendlies.

#### `DefenderHoldCastlesStrategyScoreForState`
**Purpose**: Implements castle defense strategy with victory condition scoring.

#### `FleeStrategyScoreForState`
**Purpose**: Implements flee strategy that heavily penalizes remaining on the battlefield.

### Utility Functions

#### `AttackerMultiplierForTargetDistance`
**Purpose**: Calculates distance-based scoring multipliers for attackers based on proximity to priority targets.

**Features**:
- Uses recursive priority list evaluation
- Accounts for occupied vs. unoccupied targets
- Incorporates brave water crossing capabilities
- Uses cached action point distances for efficiency

#### `CommandSorter`
**Purpose**: Comparison function for ranking commands by lookahead score (primary) and immediate score (tiebreaker).

#### `IsDeterministic`
**Purpose**: Determines if a command type has predictable outcomes or requires random simulation.

### Performance and Caching

#### `EffectiveDistanceCache`
**Purpose**: Memoization cache for expensive distance calculations between units and targets.

#### `AttackerScorePerformanceLogger`
**Purpose**: Performance monitoring system that tracks call frequency and timing for `AttackerScoreForState`.

The function architecture supports parallel evaluation, caching, and recursive lookahead while maintaining separation between strategy-specific logic and core evaluation mechanics.

## Decision Tree Data Structures (NEW)

### CommandTreeNode
**Purpose**: Represents a single command execution and its consequences in the decision tree.

**Key Fields**:
- `commandIndex`: Index of the command in the original command list
- `commandType`: Type of command (MOVE, MELEE, END_TURN, etc.)
- `immediateScore`: Score of the game state immediately after this command
- `lookaheadScore`: Best achievable score considering future moves
- `resultingGameState`: Game state after command execution
- `children`: Vector of child nodes representing subsequent possible moves
- `playerId`, `depth`, `isDefender`: Metadata about the command context

**Features**:
- Stores complete game state for each decision point
- Maintains parent-child relationships for tree traversal
- Supports both immediate and lookahead scoring
- Contains metadata for debugging and analysis

### CommandDecisionTree
**Purpose**: Complete decision tree containing all evaluated command paths from a given position.

**Key Fields**:
- `rootNodes`: All possible first moves from the starting position
- `bestCommand`: Pointer to the optimal root command
- `maxDepth`: Maximum lookahead depth of the tree
- `totalNodes`: Total number of nodes in the tree (for statistics)

**Features**:
- Provides complete visibility into AI decision-making process
- Enables analysis of alternative moves and their consequences
- Supports tree statistics and debugging information
- Maintains backward compatibility through `GetBestCommandIndex()`

**Memory Management**:
- Uses `std::unique_ptr` for automatic memory cleanup
- `GameStateW` objects are stored directly (not shared pointers for simplicity)
- Tree structure ensures proper cleanup when nodes go out of scope

### Tree vs. Legacy Approach Comparison

| Aspect | Legacy (Single Best) | Tree-Based (Complete) |
|--------|---------------------|----------------------|
| **Output** | Best command only | Complete decision tree |
| **Memory** | Minimal | Higher (stores all paths) |
| **Analysis** | Limited visibility | Full decision transparency |
| **Debugging** | Single command info | Complete move sequences |
| **Performance** | Slightly faster | Comparable (same calculations) |
| **Compatibility** | Direct usage | Wrapper maintains compatibility |

### Usage Patterns

**For AI Decision Making**:
```cpp
auto treeFuture = BuildDecisionTree(pid, isDefender, depth, maxRepeat,
                                   engine, strategy, utility, settings,
                                   castles, apdCache, alCache, deadline);
CommandDecisionTree tree = treeFuture.get();
size_t bestCommand = tree.bestCommand->commandIndex;
```

**For Analysis and Debugging**:
```cpp
CommandDecisionTree tree = treeFuture.get();
// Examine all possible moves
for (const auto& rootNode : tree.rootNodes) {
    std::cout << "Command " << rootNode->commandIndex
              << " Score: " << rootNode->lookaheadScore << std::endl;
    // Traverse children to see consequences
    for (const auto& child : rootNode->children) {
        // ... analyze child moves
    }
}
```

**Legacy Compatibility**:
```cpp
// Existing code continues to work unchanged
auto indexScoreFuture = BestCommandIndex(pid, isDefender, ...);
IndexAndScore result = indexScoreFuture.get();
size_t bestCommand = result.index;
```

The tree-based approach provides complete decision transparency while maintaining full backward compatibility with existing AI code.

## MCTS Alternative: Randomness Handling Recommendations

The new MCTS-based AI system is available in `MCTSAI.hpp/.cpp` and provides an alternative to the iterative deepening approach. However, the current MCTS implementation uses simplified randomness handling compared to the sophisticated approach in the original system.

### Current MCTS Limitations

1. **Expansion Phase**: Uses average rolls (0.5) for all commands during tree expansion
2. **Simulation Phase**: Uses random command selection with average rolls
3. **Missing**: No explicit chance nodes for commands with `HasOdds()`
4. **Missing**: No multi-sample evaluation for stochastic commands

### Recommended Improvements: Chance Node Integration

#### 1. **Explicit Chance Nodes** (Highest Priority)

For commands with `HasOdds()`, create explicit chance nodes in the MCTS tree:

```cpp
// During MCTSExpansion
if (descriptor->HasOdds()) {
    // Create TWO child nodes: success and failure
    auto successNode = CreateMCTSNode(commandIndex, SUCCESS_VARIANT);
    auto failureNode = CreateMCTSNode(commandIndex, FAILURE_VARIANT);

    // Execute with deterministic rolls (matching original system)
    ExecuteWithRoll(successNode, 1.0 - successChance/2.0);  // High roll
    ExecuteWithRoll(failureNode, (1.0 - successChance)/2.0); // Low roll

    // Set probability weights for selection
    successNode->probabilityWeight = successChance;
    failureNode->probabilityWeight = 1.0 - successChance;
}
```

#### 2. **Weighted Selection for Chance Nodes**

Modify `MCTSSelection` to handle chance nodes:

```cpp
if (node->isChanceNode) {
    // Select based on probability distribution, not UCB1
    return SelectByProbability(node->children);
} else {
    // Normal UCB1 selection for decision nodes
    return node->GetBestChild(explorationConstant);
}
```

#### 3. **Probability-Weighted Backpropagation**

Update backpropagation to account for chance node probabilities:

```cpp
void MCTSBackpropagation(MCTSNode* node, double reward) {
    while (node) {
        node->visitCount++;

        // Weight reward by probability for chance nodes
        double weightedReward = reward;
        if (node->parent && node->parent->isChanceNode) {
            weightedReward *= node->probabilityWeight;
        }

        node->totalReward += weightedReward;
        node->averageReward = node->totalReward / node->visitCount;
        node = node->parent;
    }
}
```

#### 4. **Multi-Sample Commands**

For commands without explicit odds but with randomness, use stratified sampling:

```cpp
// During expansion, create multiple child nodes with different rolls
for (int sample = 0; sample < numSamples; ++sample) {
    double roll = static_cast<double>(sample) / (numSamples - 1);
    auto sampleNode = CreateMCTSNodeWithRoll(commandIndex, roll);
    sampleNode->probabilityWeight = 1.0 / numSamples;
}
```

### Benefits of Chance Node Integration

1. **Accurate Evaluation**: Preserves the sophisticated randomness handling from the original system
2. **Better Convergence**: MCTS can properly explore both success/failure outcomes
3. **Realistic Simulations**: Tree accurately represents game's probability distributions
4. **Comparable Results**: Makes MCTS results directly comparable to iterative deepening

### Implementation Priority

1. **Phase 1**: Add explicit chance nodes for `HasOdds()` commands
2. **Phase 2**: Implement probability-weighted selection and backpropagation
3. **Phase 3**: Add multi-sample support for general stochastic commands
4. **Phase 4**: Optimize performance with lazy expansion of chance nodes

### Alternative: Determinization Approach

If explicit chance nodes prove too complex, consider **determinization**:

- Run multiple MCTS trees with different fixed random seeds
- Aggregate results across all determinizations
- Simpler to implement but potentially less accurate than explicit chance nodes

### Switching Between AI Systems

Both AI systems (`IterativeDeepeningAI` and `MCTSAI`) implement compatible interfaces. The algorithm is selected at **runtime** via the ShardokAIClient constructor:

```cpp
// Using Iterative Deepening (default)
ShardokAIClient client(playerId, isDefender, hexMap, settings);
// Or explicitly:
ShardokAIClient client(playerId, isDefender, hexMap, settings,
                      AIAlgorithmType::ITERATIVE_DEEPENING);

// Using MCTS
ShardokAIClient client(playerId, isDefender, hexMap, settings,
                      AIAlgorithmType::MCTS);

// Note: MCTS configuration can be customized via MCTSConfig:
// - maxIterations: 10000 (max MCTS iterations per move)
// - maxSimulationDepth: 10 (depth for rollout phase)
// - maxTreeDepth: 20 (max tree depth to prevent stack overflow)
// - explorationConstant: 1.414 (UCB1 exploration vs exploitation)
// - useMultithreading: true (APD cache is thread-safe with TLS + mutex protection)
// - numThreads: 4
```

The selection is made per AI client instance, allowing different algorithms for different players or game situations within the same server process.

#### Direct AI Usage (Lower Level)

Both AI systems can also be used directly:

```cpp
// Using Iterative Deepening directly
auto iterativeAI = IterativeDeepeningAI(playerId, isDefender, strategy,
                                       castleCoords, apdCache, alCache);
auto result = iterativeAI.IterativeSearch(settings, state, commands, budget);

// Using MCTS directly
auto mctsAI = MCTSAI(playerId, isDefender, strategy,
                    castleCoords, apdCache, alCache);
auto result = mctsAI.Search(settings, state, commands, budget);
```

#### Algorithm Comparison

| Feature | Iterative Deepening | MCTS |
|---------|-------------------|------|
| **Randomness Handling** | Sophisticated (chance nodes, multi-sample) | Simplified (average rolls) |
| **Performance** | Single-threaded | Multithreaded |
| **Search Type** | Fixed depth with iterative deepening | Adaptive with time budget |
| **Memory Usage** | Lower | Higher (maintains tree) |
| **Max Tree Depth** | Limited by lookahead setting | Limited by `maxTreeDepth` config (default: 20) |
| **Tree Destruction** | Not applicable | Iterative (avoids stack overflow) |
| **Best For** | Precise evaluation, production | Performance testing, fast decisions |

The MCTS implementation provides a solid foundation. Known limitations:
1. **Randomness Handling**: Simplified compared to iterative deepening (no explicit chance nodes)
2. **Simulation Quality**: Uses random rollouts instead of sophisticated evaluation

Note: The APD cache is fully thread-safe using thread-local storage and mutex-protected shared cache.

Adding chance node handling and ensuring thread safety would make it a superior replacement for the iterative deepening approach while maintaining the sophisticated randomness evaluation that makes the current system effective.

## MCTS Configuration Options

The MCTS AI system provides extensive configuration through the `MCTSConfig` structure:

### Core MCTS Parameters

```cpp
struct MCTSConfig {
    double explorationConstant = 1.414;  // UCB1 constant (sqrt(2) by default)
    int maxSimulationDepth = 1000;       // Maximum depth for rollout
    int maxTreeDepth = 2000;             // Maximum tree depth to prevent stack overflow
    bool useMultithreading = true;       // Enable parallel MCTS
    int numThreads = 16;                 // Number of threads for parallel MCTS (when enabled)
    MCTSSimulationPolicy simulationPolicy = MCTSSimulationPolicy::BEST_IMMEDIATE;
    bool enableTranspositionDetection = true;  // Enable pruning of duplicate states
    double immediateScoreTieBreakThreshold = 5.0;  // When avg rewards differ by less than this, prefer higher immediate score
    double visitCountTolerance = 0.05;  // Treat visit counts as equal if within this % of best count
    bool enableImmediateScoreInUCB1 = true;  // Apply immediate score tie-breaking in UCB1 selection too
};
```

### Exploration vs Exploitation

- **`explorationConstant`**: Controls the exploration vs exploitation balance in UCB1 selection
  - Higher values (>1.414): More exploration of unvisited nodes
  - Lower values (<1.414): More exploitation of known good moves
  - Default: 1.414 (√2, theoretical optimum for UCB1)

### Tree Structure Limits

- **`maxTreeDepth`**: Prevents stack overflow in deep game trees
  - Default: 2000 (very high limit for most tactical scenarios)
  - Terminal detection stops expansion when this depth is reached

- **`maxSimulationDepth`**: Controls rollout length during simulation phase
  - Default: 1000 (sufficient for most tactical scenarios)
  - Longer simulations provide more accurate estimates but use more time

### Multithreading Configuration

- **`useMultithreading`**: Enable/disable parallel MCTS execution
  - Default: true (takes advantage of modern multi-core CPUs)
  - Requires thread-safe game engine and scoring components

- **`numThreads`**: Number of worker threads for parallel tree building
  - Default: 16 (adjust based on available CPU cores)
  - More threads can improve search speed but with diminishing returns

### Simulation Policies

The `MCTSSimulationPolicy` enum controls how commands are selected during the rollout phase:

- **`RANDOM`**: Pure random selection from all available commands
  - Fastest but least informed simulations
  - Good baseline for testing MCTS convergence

- **`FILTERED_RANDOM`**: Random selection from AICommandFilter-approved commands
  - Eliminates obviously bad moves (moving away from objectives, etc.)
  - Better simulation quality with minimal overhead

- **`BEST_IMMEDIATE`**: Always choose command with highest immediate score
  - Most informed simulations
  - Slower but higher quality rollouts
  - Default setting for production use

- **`WEIGHTED_BEST_IMMEDIATE`**: Random selection weighted by immediate score ranking
  - Balances exploration with informed choice
  - Alternative to pure greedy selection

### Transposition Detection

- **`enableTranspositionDetection`**: Enable pruning of duplicate game states
  - Default: true (improves search efficiency)
  - Uses hash-based state identification
  - Prevents wasted computation on equivalent positions reached via different move sequences

### Immediate Score Tie-Breaking

These settings address MCTS's tendency to choose indirect paths when direct paths lead to the same outcome:

- **`immediateScoreTieBreakThreshold`**: Score difference threshold for tie-breaking
  - Default: 5.0 (when backpropagated rewards differ by less than this, prefer immediate score)
  - Helps AI choose direct moves over equivalent indirect sequences
  - Improves user experience by reducing unnecessary intermediate moves

- **`visitCountTolerance`**: Visit count equality threshold for tie-breaking
  - Default: 0.05 (5% tolerance - visit counts within this percentage are considered equal)
  - Prevents minor visit count differences from overriding immediate score preferences

- **`enableImmediateScoreInUCB1`**: Apply immediate score tie-breaking during exploration
  - Default: true (consistent tie-breaking in both exploration and final selection)
  - When UCB1 values are very close, prefer nodes with higher immediate scores
  - Improves convergence on direct paths to objectives

### Usage Example

```cpp
// Custom MCTS configuration for performance testing
MCTSConfig config;
config.explorationConstant = 2.0;  // More exploration
config.simulationPolicy = MCTSSimulationPolicy::FILTERED_RANDOM;  // Faster rollouts
config.numThreads = 8;  // Reduce threads for testing environment
config.immediateScoreTieBreakThreshold = 10.0;  // More aggressive tie-breaking

MCTSAI ai(playerId, isDefender, strategy, castleCoords, apdCache, alCache, config);
```

### Configuration Recommendations

**For Production Use:**
- Use default settings for balanced performance and quality
- Consider reducing `numThreads` on systems with limited CPU cores
- `BEST_IMMEDIATE` simulation policy provides highest quality decisions

**For Performance Testing:**
- `FILTERED_RANDOM` or `RANDOM` simulation policies for faster rollouts
- Lower `explorationConstant` (1.0) for more exploitation
- Disable transposition detection for baseline comparison

**For Analysis/Debugging:**
- Single-threaded execution (`useMultithreading = false`) for deterministic results
- Higher `immediateScoreTieBreakThreshold` to emphasize direct paths
- `BEST_IMMEDIATE` simulation for most predictable behavior

The configuration system allows fine-tuning MCTS behavior for different scenarios while maintaining compatibility with the existing AI infrastructure.