How ChessGate's AI Personalities Work
A complete guide to how ChessGate selects moves that feel like real historical chess players.
The Big Picture (For Everyone)
When you play against Karpov, Tal, or Fischer in ChessGate, you're not playing against a standard chess engine. You're playing against a system that:
- Asks a strong chess engine for the 10 best moves in the position
- Scores each move based on how likely the real player would have chosen it
- Picks the most "in character" move that doesn't lose the game
Think of it like an actor playing a role: the engine provides the chess knowledge (what moves are good), and the personality system provides the character (which good move this player would prefer).
A Simple Example
Imagine it's Karpov's turn. The engine finds 10 good moves:
| Move | Engine Rank | Engine Eval | Style Score | What It Is |
|---|---|---|---|---|
| Nd5 | #1 (best) | +0.45 | 0.82 | Quiet knight outpost |
| Bxf7 | #2 | +0.40 | 0.35 | Aggressive sacrifice |
| Qh5 | #3 | +0.38 | 0.28 | Attacking queen move |
| c4 | #4 | +0.35 | 0.91 | Quiet pawn advance |
| Rd1 | #5 | +0.30 | 0.78 | Rook to open file |
Karpov's style system scores c4 highest (0.91) because Karpov loved quiet positional moves. Even though Nd5 is the engine's #1, the system picks c4 because:
- It's only 0.10 pawns worse than the best move (within the safety threshold)
- It matches Karpov's known style perfectly
- It's the kind of move the real Karpov played in thousands of games
If Bxf7 were the only good move (all others losing), the safety filters would force the engine to play it regardless of style.
The Key Question: "But Every Game Is Different!"
This is the most common question about chess personality engines: chess has more possible positions than atoms in the universe. How can a database of past games help in a position the player has never seen?
The answer: we don't match positions. We match characteristics.
The Principle: Characteristics, Not Positions
Imagine you're an art expert trying to identify a painting as a Monet. You don't compare it pixel-by-pixel to every known Monet painting. Instead you look for characteristics: soft brush strokes, light effects, water themes, pastel colors. Even if it's a scene Monet never painted, you'd still recognize his style.
ChessGate works the same way. We extract characteristics from the position and match them against characteristics from thousands of games. The system never asks "has Karpov seen this exact position?" — it asks "does this position have features that triggered specific behavior in Karpov's games?"
What We Actually Match: 6 Levels of Abstraction
Each level is progressively more abstract, matching in positions that are increasingly different from anything in the database.
Level 1: Exact Position (Rare — Opening Book Only)
The board is identical to a position from a real game. This only happens in the opening (moves 1-15). The opening book stores these exactly.
Database position: rnbqkb1r/pppppppp/5n2/8/4P3/8/PPPP1PPP/RNBQKBNR
Current position: rnbqkb1r/pppppppp/5n2/8/4P3/8/PPPP1PPP/RNBQKBNR
Match: 100% identical → Play the book move
Level 2: Pawn Structure Matching (Very Common)
Pawns change slowly. Two positions with the same pawn structure face similar strategic problems even if the pieces are on different squares.
Database game: Karpov had pawns on c4-d5-e4 vs opponent e6-d5
→ He played a minority attack on the queenside
Current game: We have pawns on c4-d5-e4 vs opponent e6-d5
→ Different piece positions, but SAME pawn structure
→ The minority attack plan APPLIES
Why this works: Pawn structure determines long-term strategy.
Karpov's plan worked because of the PAWNS, not
because his knight happened to be on f3.
The plan matcher uses structure hashing — it computes a hash of the pawn positions and opponent king location, then looks up plans from games with the same hash. This is the primary fuzzy matching mechanism:
Tier 1: Exact structure hash → Pawns + king side identical
Tier 2: Pawn-only hash → Same pawns, king moved
Tier 3: Structure category → Same pawn type (isolated d-pawn, etc.)
From 60,000+ stored plans, typically 5-50 match any given middlegame position.
Level 3: Theme Detection (Always Available)
Strategic themes are abstract patterns that apply regardless of specific piece placement. The system detects themes live from the current position:
| Theme | How It's Detected | What It Means |
|---|---|---|
file_control |
Rook on open file (no pawns) | Pressure down the file |
outpost_occupation |
Knight on rank 5+ (or 4- for Black) | Strong piece placement |
king_attack |
2+ pieces attacking enemy king | Mating attack possible |
passed_pawn_advance |
Pawn on rank 5+ with no opposing pawn | Promotion threat |
centralization |
Piece on d4/e4/d5/e5 | Central dominance |
The system then finds plans from the database that involved the same themes:
Current position has: outpost_occupation + file_control
Database plan #4712: "Karpov occupied outpost, then doubled rooks on the file"
→ Themes match! This plan scores high regardless of exact position.
Level 4: Opponent Weakness Matching (Context-Aware)
Plans don't just match OUR position — they match what's wrong with the OPPONENT's position. The system detects:
- Backward pawns
- Isolated pawns
- Weak squares
- Exposed king
- Bishop pair vs knight pair
- Fianchetto structures
If the opponent has an isolated d-pawn and the database has 200 plans where Karpov exploited isolated d-pawns, those plans activate — even in a position Karpov never saw.
Level 5: Harmonics Profile (Always Available)
The harmonics engine computes 10 numerical metrics for any position. These are compared against what the player historically valued:
Current position metrics:
mobility: 0.65, initiative: 0.40, space: 0.72, pawn_structure: 0.81
Karpov's harmonics weights (from decision formula):
mobility: 0.12, initiative: 0.08, space: 0.16, pawn_structure: 0.18
Move A improves space by +0.15 → Karpov boost: 0.15 × 0.16 = 0.024
Move B improves mobility by +0.20 → Karpov boost: 0.20 × 0.12 = 0.024
Move C improves initiative by +0.30 → Karpov boost: 0.30 × 0.08 = 0.024
For Tal (who weights initiative at 0.22):
Move C initiative boost: 0.30 × 0.22 = 0.066 ← Tal prefers this move!
This works in ANY position because it measures abstract properties, not specific squares.
Level 6: Causality Chains (Strategic Thinking)
The most abstract level: cause-and-effect relationships between themes. These are statistical patterns discovered across thousands of games:
In Karpov's games:
pawn_storm → minority_attack (3.2x more likely after pawn storm)
file_control → king_attack (2.8x more likely after file control)
outpost → passed_pawn_advance (2.1x more likely after outpost)
These apply to ANY position. If a pawn storm is happening, the system knows Karpov typically followed up with a minority attack — even in a position that's completely novel.
Why This Works: The 60,000-Plan Database
A single game generates 10-30 plans. Across 3,000+ games per player, we extract 60,000+ plans. With that volume:
- Almost every pawn structure the player will face has been seen before
- Almost every combination of themes has been seen before
- The harmonics weights average over thousands of positions
- The causality chains represent deep statistical tendencies
It's like how a doctor doesn't need to have seen your exact illness before — they recognize symptoms, patterns, and typical progressions from thousands of cases.
The 11-Dimension Scoring System
When the system finds candidate plans, it scores each plan's relevance to the current position across 11 dimensions:
| Dimension | Weight | What It Measures |
|---|---|---|
| Structure hash match | 20% | How similar are the pawn structures? |
| Theme overlap | 15% | How many themes match? |
| Trigger conditions | 10% | What triggered this plan originally? |
| Success rating | 10% | Did this plan lead to a win? |
| Plan outcome | 10% | Win/draw/loss in the original game |
| Piece count proximity | 8% | Similar number of pieces on board? |
| Weakness overlap | 7% | Same opponent weaknesses? |
| Eval gain | 7% | How much did the plan improve eval? |
| King-side match | 5% | Is the opponent's king on the same side? |
| Bishop pair/fianchetto | 5% | Similar piece characteristics? |
| Context eval | 3% | Is current eval similar to plan start? |
The top 5 plans by combined score are used for move scoring. This multi-dimensional matching ensures plans are relevant even when no single dimension is a perfect match.
The 10-Tier Decision Pipeline
Every time a personality needs to make a move, it goes through 10 tiers in strict order. Once a tier makes a decision, all later tiers are skipped.
┌─────────────────────────────────────────────────┐
│ POSITION: It's the AI's turn to move │
└─────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ TIER 1: OPENING BOOK │
│ "Has this player played this exact position │
│ before in their career? Play their move." │
│ │
│ Data: karpov_opening_book.json (3500+ games) │
│ Speed: Instant (dictionary lookup) │
│ Only active: Moves 1-15 │
└─────────────┬───────────────────────────────────┘
│ No book move found
▼
┌─────────────────────────────────────────────────┐
│ TIER 2: ASK THE ENGINE │
│ Send position to the C engine via UCI protocol │
│ Get back 10 candidate moves with evaluations │
│ │
│ Engine: ultimate_engine_v3_drawfix.exe │
│ Protocol: UCI (Universal Chess Interface) │
│ Output: 10 moves ranked by evaluation │
└─────────────┬───────────────────────────────────┘
│ Candidates ready
▼
┌─────────────────────────────────────────────────┐
│ TIERS 3-8: EMERGENCY SHORTCUTS │
│ Skip all personality if the position demands │
│ pure engine play: │
│ │
│ 3. Mate found → play it immediately │
│ 4. Forced win (few pieces) → engine best │
│ 5. Tablebase position → perfect play │
│ 6. Overwhelming material → just convert │
│ 7. Big material lead → don't get fancy │
│ 8. Winning endgame → technique, not style │
└─────────────┬───────────────────────────────────┘
│ Position is balanced/complex
▼
┌─────────────────────────────────────────────────┐
│ TIER 9: EXACT POSITION MATCH │
│ "Has this player faced this EXACT position?" │
│ If yes AND their move is safe → play it │
│ │
│ Safety: Move must be within 50cp of best │
│ Data: Player's game database │
└─────────────┬───────────────────────────────────┘
│ No exact match
▼
┌─────────────────────────────────────────────────┐
│ TIER 10: FULL PERSONALITY SCORING │
│ This is where the magic happens. │
│ 12 independent scoring layers analyze each │
│ candidate move for style compatibility. │
│ │
│ Then: Select best move within safety bounds │
│ Then: Verify it doesn't allow forced mate │
└─────────────────────────────────────────────────┘
How the Engine Communicates (UCI Protocol)
ChessGate talks to the chess engine using UCI (Universal Chess Interface), a standard text protocol. It works like a conversation:
ChessGate → Engine: "position fen rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
(Here's the board position after 1.e4)
ChessGate → Engine: "go depth 8"
(Search 8 moves deep, give me your best moves)
Engine → ChessGate: "info depth 8 multipv 1 score cp 45 pv c7c5"
(Best move: c5, evaluation +0.45 pawns)
Engine → ChessGate: "info depth 8 multipv 2 score cp 38 pv e7e5"
(2nd best: e5, evaluation +0.38 pawns)
Engine → ChessGate: "info depth 8 multipv 3 score cp 30 pv d7d5"
(3rd best: d5, evaluation +0.30 pawns)
...
Engine → ChessGate: "bestmove c7c5"
(Done searching)
The engine runs as a separate program (ultimate_engine_v3_drawfix.exe). ChessGate starts it as a subprocess and communicates via stdin/stdout pipes.
# Simplified version of what happens
self.engine_process = subprocess.Popen(
[engine_path],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
# Send a command
self.engine_process.stdin.write("go depth 8\n")
# Read responses until "bestmove"
while True:
line = self.engine_process.stdout.readline()
if line.startswith("bestmove"):
break
# Parse "info" lines for candidate moves and evaluations
What "MultiPV 10" Means
Normal chess engines return only the best move. By setting MultiPV 10, we ask for the 10 best moves ranked by evaluation. This gives the personality system choices — it can pick the 3rd-best engine move if that move better matches the player's style.
The 12 Style Scoring Layers (Tier 10)
This is where each move gets scored for how well it matches the player's historical style. Each layer contributes independently to a total score between 0 and 1.
Layer 1: Harmonics Analysis (Weight: 0.30)
What it measures: How much a move improves the position's "harmony" — the coordination between your pieces.
How it works: Analyzes 10 positional metrics before and after the move:
| Metric | What It Measures |
|---|---|
| initiative | Who's dictating play? |
| mobility | How many squares can your pieces reach? |
| outposts | Are your knights/bishops on strong squares? |
| king_safety | Is your king well protected? |
| space_advantage | Do you control more of the board? |
| piece_activity | Are your pieces actively placed? |
| pawn_structure | Are your pawns healthy or weak? |
| passed_pawns | Do you have pawns close to promoting? |
| piece_exchanges | Is this a good time to trade pieces? |
| weak_squares | Does the opponent have exploitable weaknesses? |
Each personality values these metrics differently. Tal values initiative and king_safety (of the opponent). Karpov values pawn_structure and space_advantage.
# Push the candidate move, measure the position, pop it back
board.push(candidate_move)
metrics_after = harmonics.analyze(board)
board.pop()
harmony_score = metrics_after['harmony_score'] # -1 to +1
Layer 2: Style Rules (Variable Weight)
What it measures: Does this move match known rules about the player's style?
Data file: karpov_style_rules.json
Example rules:
{
"rule": "quiet_pawn_advance",
"phase": "middlegame",
"boost": 0.15,
"description": "Karpov preferred quiet pawn advances over sharp tactics"
}
Layer 3: Comprehensive Style Scoring (Variable Weight)
What it measures: Broader style characteristics — does this move look like something this player would do?
Compares the candidate move to historical patterns of how the player differed from engine recommendations.
Layer 4: Pattern Matching (Weight: 0.20)
What it measures: Does this move match tactical or positional patterns from the player's games?
Data file: karpov_prime_patterns.json
Patterns include piece maneuvers, pawn structures, attack themes, and endgame motifs extracted from thousands of games.
Layer 5: Sequence Matching (Weight: 0.30)
What it measures: Does this move continue a known move sequence from the player's games?
Data file: karpov_prime_sequences.json
If Karpov often played Nd2-f1-e3-d5, and we're at the Nf1 stage, the system boosts Ne3.
Layer 6: Strategic Plan Matching (Weight: 0.60 — Highest!)
What it measures: Does this move advance a strategic plan this player is known for?
Data file: karpov_strategic_plans.json (60,000+ plans!)
This is the most important scoring layer. Plans are multi-move strategies extracted by analyzing games backwards — from the final result to the moves that created it.
Example plan from the data:
{
"name": "outpost_occupation+passed_pawn_creation",
"themes": [
{
"theme": "outpost_occupation",
"primary_piece": 2,
"squares": ["d5"],
"description": "Knight occupies d5 outpost"
},
{
"theme": "passed_pawn_creation",
"primary_piece": 1,
"squares": ["c5", "c6"],
"description": "Create passed c-pawn"
}
],
"collaborations": [
{
"pieces": [["rook", "attacker"], ["queen", "supporter"]],
"convergence_squares": ["d5", "d6"]
}
]
}
How scoring works: - Move lands on a plan's target square → +0.10 - Moving piece matches the plan's piece type → +0.15 - Move is the plan's "achievement move" (the payoff) → +0.90 - Move advances multiple themes simultaneously → extra bonus
Layer 7: Decision Formula (Variable Weight)
What it measures: Does this move change the position in ways this player historically cared about?
Data file: karpov_decision_formula.json
{
"harmonics_weights": {
"initiative": 0.08,
"mobility": 0.12,
"pawn_structure": 0.18,
"king_safety": 0.14,
"space_advantage": 0.16
},
"avg_eval_sacrifice": 45.33,
"quiet_move_preference": 0.90
}
If Karpov's mobility weight is high (0.12) and a move increases mobility, that move gets a boost.
Layer 8: Piece Placement Preferences (Weight: up to 0.25)
What it measures: Did this player historically prefer this piece on this square?
If Karpov put knights on d5 65% more often than average, any move placing a knight on d5 gets a +0.15 boost.
Layer 9: Move Type Preferences (Weight: up to 0.20)
What it measures: In this game phase, did the player prefer quiet moves, captures, or checks?
| Phase | Karpov's Preference | Tal's Preference |
|---|---|---|
| Opening | Quiet moves (1.15x) | Checks (1.20x) |
| Middlegame | Captures (1.20x) | Sacrifices (1.30x) |
| Endgame | King moves (1.15x) | Passed pawns (1.25x) |
Layer 10: Multi-Theme Plan Bonus (Variable Weight)
What it measures: Does this move advance multiple strategic themes at once?
If a single move advances both "pawn_storm" and "space_advantage", it gets an extra bonus. This rewards moves that serve multiple purposes — exactly what strong players do.
Layer 11: Causality Chain Scoring (Weight: 2.5x amplification!)
What it measures: Does this move start or continue a cause-and-effect chain between strategic themes?
Data file: karpov_causality.json
{
"prerequisite": "pawn_storm",
"enables": "minority_attack",
"lift": 3.21,
"occurrences": 150
}
This means: in Karpov's games, after a pawn storm was executed, a minority attack was 3.21x more likely to follow. So if a pawn storm is active and a move enables a minority attack, it gets a large boost.
This captures how grandmasters think in chains: "First I'll advance my pawns, which will create weaknesses, which I'll attack with my minor pieces."
Layer 12: Plan Memory Continuity (Weight: 2.0x amplification!)
What it measures: Does this move continue a plan we already started?
Real grandmasters commit to plans for 5-10 moves. Without plan memory: - Move 20: Nd4 (start plan) - Move 21: h3 (random) - Move 22: Qe2 (different plan)
With plan memory: - Move 20: Nd4 (start plan) - Move 21: c3 (supports plan) - Move 22: Rad1 (completes plan)
The plan memory tracks active plans across moves and boosts moves that continue them.
Safety Filters (Preventing Blunders)
Before any style scoring happens, every candidate move must pass safety filters. These ensure the personality never makes a catastrophic mistake.
Filter 1: Absolute Blunder Threshold
Any move losing more than the blunder threshold is rejected completely.
| Game Phase | Blunder Threshold |
|---|---|
| Opening | 80 centipawns |
| Middlegame | 120 centipawns |
| Endgame | 100 centipawns |
Filter 2: Hanging Piece Detection
Checks if a move leaves a valuable piece undefended on an attacked square. Exception: the engine's #1 move is never rejected (deep search already accounts for it).
Filter 3: Hard Penalty
Moves losing 35-80cp get a severe style score reduction (but not complete rejection).
Filter 4: Repetition Penalty
When winning, moves that allow threefold repetition (draw) are rejected.
Filter 5: Soft Penalty
Moves beyond the "acceptable loss" threshold get a gradual reduction.
Move Selection: Choosing the Final Move
After scoring, the system selects the best move using a formula that balances style and engine evaluation:
Final Score = (Style Score × 60%) + (Engine Rank Bonus × 40%)
Engine Rank Bonus: | Rank | Bonus | |------|-------| | #1 (engine best) | 0.85 | | #2 | 0.65 | | #3 | 0.50 | | #4 | 0.35 | | #5+ | 0.25 or less |
This means the engine's best move always has a strong advantage. A style move needs to score significantly higher in style to overcome the rank bonus.
The Acceptable Threshold
Moves are only considered if they're within a tight evaluation window:
| Game Phase | Max Loss Allowed |
|---|---|
| Opening | 15-35 centipawns |
| Middlegame | 25-45 centipawns |
| Endgame | 20-40 centipawns |
This is the core safety guarantee: the personality never plays a move that's significantly worse than the engine's best.
Post-Selection Verification (The Safety Net)
After the personality picks a move, one final check runs:
Mate Check (Always Runs)
1. Take the selected move
2. Play it on a copy of the board
3. Let the engine search from the OPPONENT's perspective
4. If opponent has forced mate in 5 or less → REJECT
5. Play the engine's best move instead
This catches quiet moves that accidentally allow forced mate — exactly the kind of tactical oversight a style system might make.
Evaluation Verification (Conditional)
If the selected move isn't the engine's best and the gap is >30cp, run a quick verification search. If the verification shows >150cp loss, fall back to engine best.
The Data Behind Each Personality
Every personality has 8-10 data files generated from thousands of their real games:
data/karpov/
├── karpov_opening_book.json # Opening repertoire (3500+ games)
├── karpov_prime_patterns.json # Tactical/positional patterns
├── karpov_prime_sequences.json # Move sequences
├── karpov_strategic_plans.json # 60,000+ strategic plans
├── karpov_causality.json # Theme cause-and-effect chains
├── karpov_style_rules.json # Style rules
├── karpov_db_style_rules.json # Database-derived style rules
├── karpov_comprehensive_style.json # Comprehensive style metrics
├── karpov_decision_formula.json # Decision triggers and weights
├── karpov_enhanced_formula.json # Position-adaptive thresholds
└── karpov.yaml # Personality definition
How the Data Was Generated
All personality data comes from the ChessMind Pipeline — a 13-stage automated analysis system:
- Data Ingestion — Parse PGN files of the player's games
- Opening Book — Extract opening repertoire with frequencies and win rates
- Bulk Analysis — Analyze every move with Stockfish + harmonics
- Pattern Extraction — Find recurring tactical and positional patterns
- Sequence Analysis — Discover multi-move sequences
- Style Discovery — Compare player moves to engine recommendations
- Decision Formula — Discover what makes the player deviate from the engine
- Enhanced Decision — Position-adaptive thresholds and rank tolerance
- Style Learning — Machine learning on style features
- Style Rules Export — Export as scoreable rules
- Strategic Plans — Backwards analysis discovering multi-move plans
- Causality Analysis — Discover theme cause-and-effect relationships
- YAML Generation — Create the personality definition file
How Different Players Feel Different
The same 12-layer pipeline produces very different results for different players because the data is different:
Karpov (The Boa Constrictor)
- Opening book: Heavy d4 preference, Queen's Gambit lines
- Strategic plans: Minority attacks, pawn structure exploitation
- Harmonics weights: High pawn_structure (0.18), high space_advantage (0.16)
- Move type preference: Quiet moves in openings, captures in middlegame
- Causality chains: Pawn structure weakness → minority attack → endgame advantage
Tal (The Magician)
- Opening book: Aggressive Sicilian lines, King's Indian Attack
- Strategic plans: Sacrificial attacks, king-side storms
- Harmonics weights: High initiative, high king_safety (opponent's)
- Move type preference: Checks and sacrifices across all phases
- Causality chains: Piece sacrifice → king exposure → mating attack
Fischer (The Machine)
- Opening book: 1.e4 dominant, Ruy Lopez, Sicilian Najdorf
- Strategic plans: Central control, piece activity
- Harmonics weights: Balanced across all metrics
- Move type preference: Precise, engine-like in all phases
- Causality chains: Central control → piece activity → tactical execution
Complete Move Flow: A Real Example
Position: Middlegame, roughly equal, Karpov to move.
Step 1: OPENING BOOK → No match (move 22, past book range)
Step 2: ASK ENGINE → 10 candidates returned:
#1: Nd5 +45cp
#2: Rc1 +40cp
#3: c4 +38cp
#4: Bxf7 +35cp
#5: Qe2 +32cp
... (5 more)
Step 3-8: EMERGENCY CHECKS
- No mate found ✓
- Not winning/losing heavily ✓
- Not an endgame ✓
→ Continue to personality scoring
Step 9: EXACT MATCH → No match in database
Step 10: STYLE SCORING (12 layers)
Nd5 (rank #1, +45cp):
Harmonics: 0.22 (decent)
Style rules: +0.08
Plans: +0.12 (matches outpost_occupation plan)
Piece placement: +0.15 (Karpov loved Nd5)
Total: 0.72
Rc1 (rank #2, +40cp):
Harmonics: 0.25 (good — improves piece activity)
Style rules: +0.10
Plans: +0.08
Total: 0.58
c4 (rank #3, +38cp):
Harmonics: 0.28 (excellent — space + pawn structure)
Style rules: +0.15 (quiet pawn advance!)
Plans: +0.25 (matches minority_attack plan!)
Plan continuity: +0.12 (continues active plan!)
Causality: +0.08 (enables future theme)
Total: 0.91 ← HIGHEST STYLE SCORE
Bxf7 (rank #4, +35cp):
Harmonics: 0.10 (poor — disrupts own structure)
Style rules: -0.05 (Karpov avoided wild sacrifices)
Total: 0.28 ← Below minimum style threshold
MOVE SELECTION:
Acceptable threshold: 25cp (middlegame)
c4 loses only 7cp vs best → WITHIN THRESHOLD ✓
Combined scores:
c4: 0.91 × 60% + 0.50 × 40% = 0.746
Nd5: 0.72 × 60% + 0.85 × 40% = 0.772
Rc1: 0.58 × 60% + 0.65 × 40% = 0.608
Nd5 wins! Even though c4 had the highest style score,
Nd5's rank #1 engine bonus (0.85) pushes it over the top.
But it's close — and sometimes c4 WOULD win with slight
random variation in rank tolerance.
VERIFICATION:
Push Nd5 → Check for mate → No mate found ✓
→ PLAY Nd5
Technical Reference
Key Source Files
| File | Lines | Purpose |
|---|---|---|
style_engine.py |
~2300 | Complete personality engine |
server.py |
~2100 | Web server, calls style_engine.get_move() |
harmonics.py |
~400 | Position harmony analysis |
plan_memory.py |
~200 | Active plan tracking |
causality_matcher.py |
~300 | Theme chain detection |
Key Classes
| Class | Method | What It Does |
|---|---|---|
StyleEngine |
get_move() |
Full 10-tier pipeline |
StyleEngine |
_get_candidates() |
UCI engine communication |
StyleEngine |
_score_candidates() |
12-layer style scoring |
StyleEngine |
_select_move() |
Formula-based selection |
StyleEngine |
_verify_move_safety() |
Post-selection mate check |
PlanMemory |
get_continuation_moves() |
Active plan tracking |
CausalityMatcher |
score_move_causality() |
Theme chain scoring |
Key Constants
# Style vs Engine balance
STYLE_WEIGHT = 0.60 # 60% style, 40% engine evaluation
# Safety thresholds (centipawns)
ACCEPTABLE_LOSS = 15-25 # Max loss before soft penalty
HARD_PENALTY = 35-50 # Max loss before severe penalty
BLUNDER = 80-120 # Max loss before rejection
# Post-verification
MATE_CHECK_DEPTH = 10 # Depth for mate detection
EVAL_OVERRIDE = 150 # cp loss to trigger eval fallback
MoveCandidate Object
Every candidate move carries this data through the pipeline:
class MoveCandidate:
move: chess.Move # The actual move
san: str # "Nd5", "c4", etc.
eval_cp: int # Engine evaluation in centipawns
sf_rank: int # Engine ranking (1 = best)
style_score: float # 0.0 to 1.0 (from 12 scoring layers)
style_reason: str # "plan_match, harmonics, outpost"
Summary
ChessGate's AI personality system is a multi-layer pipeline that:
- Gets strong candidate moves from a chess engine
- Scores them against 12 independent style metrics derived from real game data
- Selects the most characteristic move within strict safety bounds
- Verifies the selected move doesn't allow forced mate
The result: moves that are both tactically sound and stylistically authentic — each personality genuinely feels different because the underlying data comes from thousands of their real games.
The system never sacrifices safety for style. A personality will always prefer survival over character. But within the space of "good enough" moves, it consistently picks the one that the real player would have chosen.