IRF logo
Technical Documentation · Engine Design

Raumcapa: A Raumschach Engine

A Principle-Only Influence-Field Engine
Raumcapa (German: “space Capablanca”) is a browser-based engine for Raumschach, the 5×5×5 three-dimensional chess variant invented by Ferdinand Maack in 1907, available at raumschach.org. Unlike conventional chess engines, Raumcapa uses no minimax search tree and no neural network. All positional reasoning derives from φ(d) = 1/(1+d) Chebyshev-decayed piece influence fields projected across the 125-cell board. Candidate moves are selected by six positional principles — safe checks, threat response, king shelter, opening development, worst-piece improvement, and endgame king and pawn activity — and ranked by one-ply static evaluation against those fields. Difficulty is controlled by a calibrated noise parameter rather than search depth, yielding five named levels from random play to fully determined evaluation.

1. Introduction

Raumschach — literally “space chess” in German — was invented by Dr. Ferdinand Maack (1861–1930) of Hamburg and first published in 1907. Its mature version, referred to as “Normal Form,” is played on a 5×5×5 board composed of five stacked 5×5 levels and introduces the Unicorn, a rider confined to space-diagonal (three-axis) rays. The Queen combines all three rider types. Despite more than a century of theoretical attention — documented most authoritatively by Anthony Dickins (1914–1987) in A Guide to Fairy Chess (1969–71) — computer opposition for Raumschach has remained underdeveloped and underdocumented.

Raumcapa is a browser-based AI opponent for Raumschach, available in the raumschach.org game client. Its central technical contribution is its architecture: it uses no minimax search tree, no alpha-beta pruning, and no neural network. Instead, it projects a piece influence field — a Chebyshev-distance-decayed map of each color’s positional reach — across all 125 cells of the board, selects candidate moves by ten named positional principles, and ranks them by one-ply static evaluation. The approach places Raumcapa in a small and historically notable category: principle-only chess engines that reason about structure rather than searching a game tree.

The evaluation function is organized around the documented playing principles of José Raúl Capablanca (1888–1942), adapted to the geometry of three dimensions. The design premise is that Capablanca’s method — economy of means, structural patience, the relentless improvement of the worst-placed piece — was never fundamentally two-dimensional. It was an algorithmic instinct for what the position demands, a quality that translates intact into three dimensions and, in some respects, finds a more natural home there.

This article is organized as follows: §2 presents the historical and philosophical case for choosing Capablanca as design model; §3 situates Raumcapa among known Raumschach engines and discusses its novelty as a principle-only system; §4 and §5 describe board representation and move generation; §6 presents the complete algorithm; §7 documents the evaluation function; §8 records principal design decisions; §9 assesses advantages and disadvantages; §10 identifies Capablancan principles not yet encoded; and §11 concludes.

2. On the Choice of José Raúl Capablanca

The decision to ground Raumcapa’s evaluation in the principles of José Raúl Capablanca (1888–1942) rests on three independent grounds.

Historical Coincidence

Raumschach was invented in 1907 and had its peak cultural vitality in the 1920s, when it attracted serious attention from the German and Central European chess community. The greatest chess player of that precise decade was Capablanca. He won the world championship from Emanuel Lasker in 1921, dominated the great tournaments at London 1922 and New York 1924, and was considered so clearly the strongest player alive that his 1927 loss to Alexander Alekhine in Buenos Aires — a 34-game match he entered as a prohibitive favorite — remains one of the most shocking results in chess history. Capablanca is not merely a plausible model for a Raumschach engine; he is the world champion of Raumschach’s prime decade. The connection is a historical fact, not a design conceit.

Principle as Algorithm

Capablanca’s chess was governed by a principle that translates directly into algorithmic form: find the simplest path to the correct structure. He did not search for brilliant combinations; he searched for positions where the correct move was nearly forced, where the opponent’s resources were exhausted, where the conversion path was clear. This is precisely the kind of chess a principled evaluation function can embody: recognize structural advantages, improve the least-active piece, simplify when ahead. Each of these corresponds to an explicit term in Raumcapa’s evaluation and candidate-generation logic.

Importantly, Capablanca was not merely intuitive — he was explicit. His two instructional books, Chess Fundamentals (1921) and A Primer of Chess (1935), state his principles in language direct enough to serve as engineering specifications: “Improve the position of the piece that is worst placed.” “When ahead in material, exchange pieces but avoid pawn exchanges.” These sentences appear nearly verbatim in the candidate-generation logic of §6.3.

Endgame

By common consensus, Capablanca’s endgame technique was the finest of any player who ever lived. In Raumschach, where the endgame presents fundamentally new features — a 5×5×5 king march, no formal castling, the unicorn as an endgame piece — endgame understanding matters differently than in two-dimensional chess. Raumcapa’s king activation terms encode Capablanca’s documented endgame principle directly: when it is safe to do so, march the king toward the geometric center and toward the enemy king, and do so without hesitation.

Paul Morphy (1837–1884) is the other player one might consider — perhaps the purest first-principles player in the history of the game. A Morphy-derived engine would have been conceptually coherent. The reason Capablanca is preferred is documentary: his principles are self-articulated in writing with sufficient precision to be used as design specifications. Morphy’s principles are reconstructed by historians from his games and letters, not stated by Morphy himself with codeable specificity.

3. Raumcapa and the Raumschach Engine Landscape

Several browser-playable AI opponents for Raumschach have been made publicly available over the years. Most have been described as using minimax with alpha-beta pruning. To the best of the present author’s knowledge, none has published implementation details, evaluation functions, or pseudocode. Raumcapa is, to the best of the present author’s knowledge, the second fully documented Raumschach engine in the public record, after Maackesgeist, which established that record. The present article makes available a complete technical description including board representation, direction geometry, legal move generation, the evaluation function with all heuristic weights, and the complete algorithm in pseudocode.

A Principle-Only Engine

In his 1950 paper “Programming a Computer for Playing Chess,” Claude Shannon distinguished two approaches to machine chess. Type A engines examine every legal move to some fixed depth — the exhaustive minimax tradition that dominated the field for seventy years, from early programs through Deep Blue and beyond. Type B engines instead select a subset of “plausible” moves at each node, mimicking human selective attention. Shannon believed Type B was more promising; in practice, the field converged almost entirely on Type A once alpha-beta pruning made exhaustive search computationally tractable.

Neural network engines, beginning with AlphaZero (2017), revived selectivity through learned policy functions, but retained the game tree: Monte Carlo Tree Search still searches positions many plies deep, guided by a neural network rather than hand-written heuristics.

Raumcapa belongs to neither tradition. It is not Type A (no game tree is searched beyond one ply). It is not a neural network (no weights are learned from data). It is, in effect, a Type B system taken to a logical extreme: candidate moves are selected entirely by named positional principles, and the position is evaluated without any opponent-reply search at all. To the best of the present author’s knowledge, truly search-free chess engines of this kind are vanishingly rare in the literature. Botvinnik’s “Pioneer” program (1970s) used plan-based selective search, but still searched several plies ahead. Raumcapa does not.

Why Raumschach Is Suited to This Approach

The choice is not arbitrary. Raumschach has a substantially higher branching factor than flat chess: the Queen controls up to 26 ray directions, the Knight has 24 jump targets from an interior cell, and the 5×5×5 board produces an average legal-move count well above the flat-chess average of roughly 30. Minimax at useful depth is correspondingly more expensive and more prone to horizon-effect distortions. The influence-field approach side-steps these costs entirely, producing instant responses at any difficulty level.

There is also a structural argument. Raumschach’s theory is nascent; there is no deep opening book, no large body of annotated master games, and no well-established middlegame canon. In this environment, a principle-based engine is not a degraded version of a search engine — it is a reasonable first approximation of how strong human Raumschach players must think. The engine’s reasoning is also fully transparent: every move it makes traces to one of ten named principles and an explicit evaluation score.

4. Board Representation

The board is a JavaScript three-dimensional array board[l][r][f], where l ∈ {0–4} indexes the level (A through E), r ∈ {0–4} the rank (1–5), and f ∈ {0–4} the file (a–e). An occupied cell contains a plain object { type, color }; an empty cell contains null. Piece types are the single-character strings K Q R B U N P; colors are 'w' and 'b'.

4.1 Geometric Direction Sets

NameCountDescriptionPieces
Orthogonal (RD)6±1 along exactly one axisRook, Queen
Face-diagonal (BD)12±1 along exactly two axesBishop, Queen
Space-diagonal (UD)8±1 along all three axesUnicorn, Queen
All (QD)26Union of the aboveQueen, King

The Knight extends to three dimensions through all permutations of (±2, ±1, 0), yielding 24 distinct jump directions from an interior cell. All direction sets are precomputed as constant arrays at startup.

Two distance and centrality helpers are used throughout: cheby(al,ar,af,bl,br,bf), the Chebyshev distance between two cells; and cenDist(l,r,f) = max(|l−2|, |r−2|, |f−2|), the Chebyshev distance from any cell to the geometric center Cc3 = (2, 2, 2). The influence decay function is φ(d) = 1/(1+d), where d is the Chebyshev distance from a piece to a target cell.

The choice of Chebyshev distance rather than Manhattan distance is deliberate. Chebyshev distance reflects the number of King moves required to reach any cell, and correctly distributes centrality penalties: Manhattan distance over-penalizes edge cells along one axis while under-penalizing true corner cells.

5. Move Generation

pieceMoves(board, l, r, f) generates pseudo-legal moves for a single piece by ray-casting (sliding pieces) or single-step enumeration (King, Knight, Pawn). allPseudo(board, color) aggregates over all pieces of a given color. legalMoves(board, color) filters pseudo-legal moves by make/unmake: each candidate move is applied in place, the moving side’s King is tested for check via isAttacked, and the move is immediately undone — producing no heap allocations.

isAttacked(board, cell, byColor) performs reverse ray-casting from the target cell. Pawn promotion is handled inside pieceMoves: when a pawn reaches the promotion rank, the move is emitted five times with prom set to each of Q R B U N. The engine always selects the Queen promotion.

6. The Algorithm

The engine operates in four stages per move decision: (1) compute influence fields for both colors; (2) detect the current phase; (3) generate a candidate set by positional principles; (4) evaluate each candidate by one-ply static evaluation and return the highest-scoring move. No game tree is searched; the opponent’s reply is not considered.

6.1 Piece Influence Fields

For a given color, the influence field is a flat array of 125 values, one per cell of the board. Each own piece at cell (l, r, f) contributes φ(d) = 1/(1+d) to every cell (tl, tr, tf) reachable by a legal move, where d is the Chebyshev distance from the piece to the target. A Queen on a central square with 40 legal move targets radiates influence broadly; a Unicorn confined to a corner with three targets radiates almost none. The field thus encodes both quantity and quality of positional reach.

Two fields are computed per move decision: ownField and oppField. They are used in evaluation for center control, king safety and shelter, piece coordination, and outpost detection. They are also used directly in candidate generation to distinguish safe squares (low oppField) from dangerous ones.

FUNCTION computeInfluenceField(board, color)  Float32Array[125]:
  field  zeros(125)
  FOR EACH own piece at (l, r, f):
    FOR EACH legal move target (tl, tr, tf):
      d  cheby(l, r, f, tl, tr, tf)
      field[tl × 25 + tr × 5 + tf] += φ(d)   // φ(d) = 1/(1+d)
  RETURN field

6.2 Phase Detection

Three phases are distinguished per color. Opening: hasUndevelopedPiece returns true, meaning at least one minor piece (N, B, U, Q) or the King still sits on its home square. Endgame: both queens are off the board and the opponent holds at most one non-pawn, non-king piece. Middlegame: neither opening nor endgame condition is met. The phase gate controls which candidate-generation principles are active.

On the choice of which pieces gate the opening. Rooks are intentionally excluded from hasUndevelopedPiece. A Rook develops not by moving early, but by occupying an open file or rank once the opening fight has resolved. Requiring a Rook to leave home before declaring the opening over is causally backwards — and in practice catastrophic: a Rook that never finds an open file in the first twenty moves holds the engine in opening mode indefinitely. Rooks are the one piece class that should emerge as a consequence of the opening resolving, not a precondition for it.

The King is included. Capablanca considered king safety — castling — a component of opening completion, not something to be deferred once the middlegame began. In Raumschach the equivalent is the tuck. An unmoved King on its starting square represents an incomplete opening objective. The King’s own tuck logic (Principle 4) reliably generates the corner approach as a candidate; the W_KING_FILE evaluation penalty (§7.6) ensures that candidate is actually chosen rather than passed over in favor of piece development.

Monotonic phase floor. determinePhase is backed by _phaseFloor, a persistent worker-scope object keyed by side, which makes the opening→middlegame boundary a one-way door. Without it, a piece returning to its home square after having left — the King tucking back, or a piece driven back in a skirmish — would re-trigger hasUndevelopedPiece and revert the phase to opening mid-game, reactivating all opening-only candidate-generation principles at the wrong moment. The floor advances from ‘opening’ to ‘middlegame’ the first time all qualifying pieces have left their home squares, and thereafter holds there regardless of subsequent piece movement. The only reset is when the board shows no qualifying piece off its home square at all — unambiguously a fresh position, covering the new-game case without requiring an explicit reset signal.

The middlegame↔endgame boundary is explicitly not guarded by the floor. A pawn promotion can restore a queen and legitimately push the position back from endgame to middlegame; that transition must remain live in both directions. Only the opening→middlegame boundary is a one-way door: once pieces have developed, they have developed.

6.3 Candidate Generation: Ten Positional Principles

The candidate set is assembled by up to six principle groups, applied in order. Duplicates are suppressed by a move-key map. If fewer than MIN_CAND = 6 candidates are collected after all principles, a safety-net fill ranks remaining legal moves by single-step SEE plus centrality gain and adds from the top until the minimum is reached, tagging each addition so it remains distinguishable from a principled pick. The six principles are as follows.

Principle 1 — Safe checks. Any move that gives check and leaves the checking piece either defended or unattacked. The safety condition is verified by make/unmake.

Principle 2 — Threat response. A piece qualifies for a response under either of two signals: diffuse enemy influence above a threshold (oppField[sq] > THREAT_THRESH = 0.40), or an exact attacker found by lva (least-valuable attacker) on the current board. The two signals are not redundant. oppField[sq] sums φ(d) = 1/(1+d) over every enemy piece that can reach the square, so it reliably flags accumulating pressure from several attackers at once, but a single attacker does not always clear 0.40 on its own: every Knight move in this geometry has Chebyshev distance exactly 2, so a lone Knight contributes exactly φ(2) = 0.33, and a lone slider two or more squares away contributes 0.33, 0.25, or less — all sub-threshold. The lva check closes that case directly: it asks only whether some opponent piece can capture this square right now, with no distance decay and no dependence on how many other attackers happen to be present, so a piece attacked by exactly one Knight, or by one slider that isn’t adjacent, still qualifies even where the diffuse signal alone would not notice. Once a piece qualifies under either signal, two responses are generated: escape moves to a safe destination (low oppField, verified safe by make/unmake), and captures of any board piece where see1() clears the contextual exchange threshold τ (see §7.13). see1() applies the capture, finds the opponent’s cheapest recapture, and returns the signed net gain. A result at or above τ indicates the exchange is appropriate given the current position’s material balance, king safety, and the piece’s own activity.

Principle 2b — Jump threats (Knight fork pre-emption). Principle 2 above responds only to threats that already exist on the current board, surfaced by oppField or an exact lva hit. A different class of danger is latent rather than current: an enemy Knight that has not yet moved, but whose next jump would land on a square attacking one or more own pieces. The Knight is the only leaping piece in Raumschach — sliding and stepping pieces are already covered by Principle 2’s current-board check and by veiledThreatCount — so this latent case is isolated and answered by a dedicated geometric scan, jumpThreats, rather than by search. For every enemy Knight, each of its jump destinations is examined; for each destination, every own non-pawn piece that landing there would attack is collected as a victim, with a destination covering two or more victims flagged as a fork. Pawns are excluded from victim-counting: a Knight can never profitably initiate a capture against a lone pawn, so pawn-only ‘threats’ would flood the candidate set with noise rather than addressing anything materially significant. For each qualifying (Knight, landing) pair, three classes of candidate are generated: escape — move a victim (other than the King, whose own escapes are Principle 2’s domain) to a destination safe by make/unmake; attack — capture or drive off the threatening Knight now, accepted whenever see1() is at least neutral, so the engine does not concede material merely to forestall a threat that has not yet materialized; and landing denial — when the landing square is currently empty, occupy it with any non-King piece via a move verified safe by make/unmake, removing the destination before the Knight can ever reach it. This is the engine’s one principled instance of opponent-move anticipation without an opponent-reply search: the threat is read directly off the current board’s static geometry, at the cost of two passes over the Knight’s jump directions, rather than by simulating a reply.

Principle 3 — King shelter. When enemy influence in the king’s immediate neighborhood exceeds friendly influence by a threshold, moves that place a friendly piece in the king’s neighborhood at a safe destination are included.

Principle 4 — Opening development. Active while hasUndevelopedPiece is true. For non-pawn, non-king pieces still on their home square: moves to a safe destination with strictly lower cenDist than the home square are added as development candidates, and devMoveFound is set true. When no such strictly-improving move exists — because the piece’s path toward the center is entirely blocked by own or enemy pieces — a lateral fallback fires: any safe move to a square with cenDist ≤ srcCen (same or better centrality) is accepted. This prevents geometrically cornered pieces, such as a Unicorn or Bishop on a back-level corner square with all diagonals blocked, from holding the engine in the opening phase indefinitely because they cannot satisfy the strict < criterion. The fallback fires only when no strictly-improving move was found in the primary pass, so it does not broaden the candidate set for well-placed pieces. For the King: moves that stay on the home level and bring the king’s file index strictly closer to the nearest corner (file 0 or 4) — Capablanca’s king-tuck, implemented as a progressive corner approach across as many moves as needed. A queen-development gate is enforced as a final filter: the Queen may not leave her home square before at least one own Knight has left its home square. If filtering would leave the candidate set empty, the gate is lifted.

Principle 4b — Pawn structural development. Capablanca’s opening play followed a strict priority: pieces come out before pawns advance beyond what the position requires. A pawn move is treated as primary opening development only when it serves one of two structural purposes — opening a line for a friendly slider, or claiming central space. linesOpenedByPawnMove tests whether the pawn is the first blocker along any direction vector of a friendly Bishop, Unicorn, Rook, or Queen; if its departure would immediately extend that piece’s reach, the pawn move qualifies. Central-space claims use the same inner 3×3×3 test as elsewhere in the engine. Quiet pawn advances meeting neither condition are not added by this principle — they are passed over in favor of genuine piece development, though they remain available as a last resort through the central volume contest or the safety-net fill if nothing better exists.

The development gate. Capablanca’s opening discipline included a further rule: do not move the same piece twice while other pieces remain undeveloped. developmentGateOK implements this directly from the current position, with no move-history tracking required. isHomeSq serves as the proxy for “has this piece already moved”: a piece still on its home square has not developed and may move freely. A piece already off its home square may move again only under one of three conditions — it is currently threatened (lva finds an opponent attacker), the move wins material (see1() on a capture clears the contextual exchange threshold τ), or no other undeveloped piece has any safe developing move available this turn, tracked via a devMoveFound flag set during Principle 4’s main loop and consulted lazily. This gate is applied to the central volume contest sub-principle and to Principle 5b below, the two principles that iterate over all legal moves without already restricting themselves to undeveloped pieces. Principle 1 (threat response) is deliberately left ungated: it fires only under genuine threat or a winning capture, which are already the gate’s own exceptions (a) and (b), so gating it would be redundant. Principle 4’s own loop is likewise already gate-compliant by construction, since it considers only home-square pieces in the first place.

Principle 5 — Worst-piece improvement. Active outside the opening. The worst piece is the own non-pawn, non-king piece with the lowest influence-weighted mobility: Σm φ(cheby(sq, m.to)) over all legal moves m. worstPiece skips pieces with zero legal moves (c > 0 && c < lowestC); a blocked Rook in the corner scores c = 0 and is excluded, so the function always returns the least active piece that can actually do something. If all non-king, non-pawn pieces are immobile the function returns null, and P5 generates nothing. The King is excluded from worstPiece throughout: its opening development is the exclusive domain of the dedicated tuck logic in Principle 4, and in the middlegame and endgame its movement is governed by Principle 6. Moving the King toward the center via P5 is precisely the suicidal behaviour the king-exclusion guards prevent. Moves from the worst piece to a safe destination closer to the center are added.

Principle 5b — Anti-development occupancy (prophylaxis). When the opponent is still in the opening phase, the engine identifies the opponent’s worst piece (lowest positive influence-weighted mobility, via worstPiece, subject to the same exclusions) and enumerates all squares that piece could reach that would improve its Chebyshev centrality. Any own move that lands safely on one of those squares is added as a candidate, subject also to the development gate during our own opening phase. The King is explicitly excluded from contributing to this principle: King moves into the central cube are precisely the suicidal moves the architecture works to prevent, and treating them as desirable “prophylactic” destinations would undermine the safety logic. This directly encodes Capablanca’s prophylactic principle: if they intend to develop their worst piece toward the center, occupy those squares first. The opponent’s pseudo-legal moves are used (no make/unmake), keeping the cost O(board).

Principle 5c — Piece-safety under material deficit (prophylaxis). When the engine is materially behind by more than 50 cp, it uses lva (least-valuable attacker) to identify any own non-pawn, non-king piece attacked by a cheaper opponent piece — for example, a Rook (460 cp) threatened by a Unicorn (290 cp). All safe escape moves for such pieces are added as candidates. This shares its exact-attacker mechanism with Principle 2 above, narrowed to the specific case that matters most once the position has already turned unfavorable: a losing exchange the engine can least afford to allow. The material-deficit gate makes Principle 5c a targeted reinforcement of Principle 2’s coverage at exactly the moment standing pat is costliest, rather than an independent detection path.

Principle 6 — Endgame: king march and passed pawns. Active in the endgame phase. King moves to a cell closer to the board center are included. All moves advancing any own passed pawn are included.

A central volume contest sub-principle also fires when the opponent’s cumulative influence over the inner 3×3×3 cube exceeds the engine’s own: moves to any inner-cube cell with low enemy influence are added. King moves are explicitly excluded from this sub-principle, and during the opening phase every candidate move is additionally checked against the development gate above, so this sub-principle cannot be used to justify a second move of an already-developed piece. This operates throughout all phases.

King candidate channels. Following the exclusions in P5, P5b, the central volume contest, and the safety-net fill, the King may enter the candidate set through exactly four principled paths: Principle 1 (a King move that gives check), Principle 3 (king shelter in an emergency, where the King moves a piece into its own neighborhood), Principle 2b (the King capturing the threatening Knight itself, the one branch of the jump-threat response not restricted to non-King movers), and the dedicated tuck logic in Principle 4. No undirected King move can reach the candidate list by any other route.

FUNCTION generateCandidates(board, color, phase, ownField, oppField, legal)  moves:
  seen  empty Map (move key → move)
  devMoveFound  false   // set true once Principle 4 adds an undeveloped-piece move
  exchBaseline  exchangeBaseline(board, color, ownField, oppField)  // material + king safety; computed once
  // developmentGateOK: "don’t move the same piece twice in the opening"
  FUNCTION developmentGateOK(sq, type, m):
    IF phase ≠ ‘opening’ OR type ∈ {Pawn, King}: RETURN true
    IF isHomeSq(sq, type, color): RETURN true               // has not developed yet
    IF lva(board, sq, opponent) ≠ null: RETURN true        // (a) threatened
    IF m is capture AND see1(m) > exchangeThreshold(baseline, mover.type, m.from, m.to, ownField, oppField): RETURN true         // (b) wins material
    IF devMoveFound = false: RETURN true                     // (c) blockage
    RETURN false
  // Principle 1: safe checks
  FOR EACH m IN legal:
    IF m gives check AND checker is defended OR not attacked: add(m)
  // Principle 2: threat response (influence-threshold OR exact attacker)
  FOR EACH own piece at sq where oppField[sq] > THREAT_THRESH OR lva(board, sq, opponent) ≠ null:
    FOR EACH m IN legal FROM sq:
      IF oppField[m.to] < SAFE_THRESH AND trulySafe(m): add(m)  // escape
    FOR EACH m IN legal WITH capture AND see1(m) ≥ exchangeThreshold(baseline, mover.type, m.from, m.to, ownField, oppField): add(m)  // contextual threshold
  // Principle 2b: jump threats — latent Knight forks (pure geometry, no search)
  jThreats  jumpThreats(board, color)
  FOR EACH {knightPos, landPos, victims} IN jThreats:
    FOR EACH victim IN victims where victim ≠ King:
      FOR EACH m IN legal FROM victim where trulySafe(m): add(m)        // (a) escape
    FOR EACH m IN legal TO knightPos where see1(m) ≥ 0: add(m)              // (b) attack the Knight
    IF landPos is empty:
      FOR EACH m IN legal TO landPos where mover ≠ King AND trulySafe(m): add(m)  // (c) deny landing
  // Principle 3: king shelter
  IF enemy influence in king’s neighborhood > friendly + 0.5:
    FOR EACH m IN legal where m.to is near king AND trulySafe(m): add(m)
  // Principle 4: opening development
  IF phase = ‘opening’:
    FOR EACH undeveloped non-pawn, non-king piece at home sq:
      hadImproving  false
      FOR EACH m to safe sq with cenDist(m.to) < cenDist(home): add(m); devMoveFound  true; hadImproving  true
      IF !hadImproving:   // lateral fallback: piece geometrically blocked from improving
        FOR EACH m to safe sq with cenDist(m.to) ≤ cenDist(home): add(m); devMoveFound  true
    FOR King on home level: add file-toward-corner moves that are safe
    // Queen gate: suppress queen moves until a knight has left home (filter removed if set empties)
  // Principle 4b: pawn structural development (line-opening or central-claiming only)
  IF phase = ‘opening’:
    FOR EACH own quiet pawn advance m:
      claimsCenter  m.to IN inner cube
      opensLine     linesOpenedByPawnMove(m.from, color)  // pawn is first blocker on a friendly slider ray
      IF (claimsCenter OR opensLine) AND oppField[m.to] < SAFE_THRESH AND trulySafe(m): add(m)
      // other quiet pawn pushes are NOT added here — they may surface via central
      // volume contest or the safety net, but are not treated as primary development
  // Principle 5: worst-piece improvement (middlegame / endgame only)
  IF phase ≠ ‘opening’:
    wp  worstPiece(board, color)  // excludes King, Pawn; skips pieces with c=0
    FOR EACH m FROM wp to safe sq with lower cenDist: add(m)
  // Principle 5b: prophylaxis — anti-development occupancy (King excluded; dev-gated)
  IF determinePhase(board, opponent) = ‘opening’:
    oppWP  worstPiece(board, opponent)  // excludes King, Pawn; skips c=0
    oppTargets  {m.to : m  allPseudo(board, opp) FROM oppWP, cenDist(m.to) < cenDist(oppWP)}
    FOR EACH m IN legal where m.to  oppTargets
                  AND mover ≠ King              // King never blocks dev squares
                  AND developmentGateOK(m.from, mover.type, m)
                  AND oppField[m.to] < SAFE_THRESH AND trulySafe(m): add(m)
  // Principle 5c: prophylaxis — piece-safety under material deficit
  IF materialBalance(board, color) < −50:
    FOR EACH own non-pawn, non-king piece at (l, r, f):
      threat  lva(board, [l,r,f], opponent)
      IF threat exists AND PV[threat] < PV[own piece]:
        FOR EACH m IN legal FROM (l,r,f) where trulySafe(m): add(m)
  // Central volume contest (all phases) — King excluded; dev-gated in opening
  IF oppCenter > ownCenter:
    FOR EACH m to inner-cube sq where mover ≠ King
                  AND developmentGateOK(m.from, mover.type, m)
                  AND low oppField: add(m)
  // Principle 6: endgame king and passed pawns
  IF phase = ‘endgame’:
    FOR EACH king move to sq with lower cenDist, not attacked: add(m)
    FOR EACH passed pawn advance: add(m)
  // Safety net: fill to MIN_CAND; King moves excluded entirely
  IF |seen| < MIN_CAND:
    fill from remaining legal (excluding King moves), sorted by (see(m) + φ(cenDist(m.to)))
  RETURN [...seen.values()]

6.4 Evaluation

Each candidate is evaluated by applying it to the board, recomputing both influence fields from the resulting position, calling evalBoard, and unmaking the move. The evaluation function returns a score in centipawns from the mover’s perspective. Its components are documented in full in §7.

6.5 getBestMove and Difficulty

After all candidates are scored, the highest-scoring move is returned with its score attached as _score, alongside the position’s own _phase. Both accompany the move back to the main thread and drive two Capablanca-faithful policies that sit outside the worker’s search-free architecture entirely, since they are decisions about whether to continue playing rather than positional reasoning: the draw policy (§8.4) and the resignation policy (§8.10). If _score < DRAW_OFFER_THRESH = −80 cp, the engine proactively offers a draw; if the player offers a draw and _score < DRAW_ACCEPT_THRESH = +50 cp, the engine accepts.

Difficulty is controlled by a perturbation parameter: a uniformly distributed random value in [−perturbation, +perturbation] centipawns is added to each candidate’s score before comparison. A negative perturbation bypasses evaluation entirely and returns a random legal move. Five named levels correspond to five historical Capablanca phases:

LevelNamePerturbationCharacter
0Boyrandom moveCapablanca at four, imitating the pieces his father moved
1Prodigy±100 cpPure talent, high noise — Capablanca at thirteen, before formal study
2Challenger±35 cpRefined and principled — Capablanca in his tournament prime, 1909–1919
3Champion±8 cpNear-perfect positional play — Capablanca as world champion, 1921–1927
4The Machine0Fully determined evaluation — the moves arrived as if the position held one answer
FUNCTION getBestMove(board, color, perturbation)  move:
  legal  legalMoves(board, color)
  IF legal is empty: RETURN null
  IF perturbation < 0: RETURN randomChoice(legal)   // Boy level
  phase     determinePhase(board, color)
  ownField  computeInfluenceField(board, color)
  oppField  computeInfluenceField(board, opponent)
  cands     generateCandidates(board, color, phase, ownField, oppField, legal)
  bestMove  null;  bestScore  −∞
  FOR EACH m IN cands:
    undo   makeMove(board, m)
    IF move gives checkmate: unmakeMove; RETURN m   // immediate checkmate
    nOwn   computeInfluenceField(board, color)
    nOpp   computeInfluenceField(board, opponent)
    s      evalBoard(board, color, nOwn, nOpp)
    s     += uniform{−perturbation, …, +perturbation}
    unmakeMove(board, m, undo)
    IF s > bestScore: bestScore  s; bestMove  m
  IF bestMove is null: bestMove  randomChoice(legal)
  RETURN bestMove WITH _score  bestScore

7. Evaluation Function — The Capablanca Heuristics

The evaluation function returns a score in centipawns from the mover’s perspective. It receives both influence fields as parameters, avoiding recomputation. The thirteen heuristic components are documented below.

§7.1 — Material

Capablanca played chess with clinical material precision, almost never sacrificing without concrete compensation. The piece values used here are Raumschach-calibrated rather than imported from flat chess, where the standard hierarchy (Q:975, R:475, B:320, N:280) is badly wrong for a 5×5×5 board.

PieceValue (cp)Notes
Pawn100Reference unit; 7 move-types (see below)
Unicorn2908 triagonal rays; color-bound to 1/8 of the board regardless of mobility
Knight51024 jump targets from interior; board compactness makes each target count
Rook4606 orthogonal rays; open-line dependency limits value on a crowded 5×5×5 board
Bishop56012 face-diagonal rays; color-bound but to one half of the board (60 or 65 cells), not 1/8
Queen1620All 26 directions; substantially dominant in 3D, much more so than in flat chess

On the first principles. The Raumschach-calibrated values are grounded in 3D mobility. The standard method for piece valuation in flat chess is to compute each piece’s average number of reachable squares from typical positions, then scale so that a pawn equals 100. In three dimensions, every piece class reaches substantially more squares than its flat-chess counterpart — the Knight has 24 jump targets (versus 8), the Bishop has 12 face-diagonal directions (versus 4), the Queen moves in all 26 directions — and the relative ordering changes accordingly. The Unicorn is the critical case: by raw mobility it should rank with or above the Knight, but it can only ever visit a fixed eighth of all board cells, those reachable by triagonal steps from its starting parity. That color-binding constraint is strategic, not tactical, and no increase in mobility can overcome it. 290 cp is therefore held constant regardless of its ray count. The values likely received empirical adjustment beyond pure mobility averaging to account for coordination effects; texel tuning against a Raumschach game corpus would resolve remaining uncertainty.

On the pawn as reference unit. The Raumschach pawn has seven distinct move-types, not two: two non-capture advances (rank+ and level+); four file-shifted captures — (rank+, file±) and (level+, file±); and one apex-direction capture advancing simultaneously in rank and level on the same file (l+1, r+1, f). A derivation using only the two non-capture advances as the pawn’s measure gives inflated ratios for every other piece. A derivation using all seven move-types gives more conservative ratios. Neither reproduces the calibrated values exactly, which confirms they incorporate empirical adjustment rather than being calculated from a simple mobility ratio.

§7.2 — Pawn Structure

Capablanca regarded structural weaknesses as permanent liabilities and played entire games around exploiting a single weak pawn. Raumschach’s third dimension requires three distinct doubled-pawn categories, each reflecting a different degree of structural severity.

Advancement: +W_PAWN_ADV × adv per pawn, where adv = l+r for White and (8−l−r) for Black, ranging 0–8. Rewards territorial progress.

Pawn chains: +W_PAWN_CHN = +10 cp when an own pawn one level behind shares the same rank and file, rewarding structural support along the level axis.

Rank-doubled (−20 cp each): two own pawns on the same level and file, one rank apart. The front pawn can escape the doubling by advancing one level, so the weakness is escapable. Applied to both pawns in the pair. Fires only at non-apex levels; the apex case is handled separately below.

Level-doubled (−20 cp each): two own pawns on the same rank and file, one level apart — vertically stacked. The less advanced pawn can escape by advancing one rank, dissolving the structure. Applied to both pawns in the pair. Note that two level-doubled pawns that are simultaneously rank-doubled (i.e. both penalties apply) represent a more complex structural defect without additional special-casing: the two independent penalties combine additively.

Apex-doubled (−55 cp, blocked pawn only): the most severe variant, and the only permanent one. A pawn is apex-doubled when it sits on the apex level (level E, l = 4 for White; level A, l = 0 for Black) and its forward rank direction is also blocked by an own pawn. Both escape routes are gone simultaneously: level advance is impossible because the ceiling is reached; rank advance is blocked by the own piece ahead. The pawn is structurally immobile. The penalty applies only to the blocked pawn — the blocking pawn occupies its own square and is not itself stuck. Two pawns on levels C and D (l = 2 and l = 3) on the same rank and file receive only the level-doubled penalty of −20 cp each; the C-pawn can still escape via rank advance. Only when both pawns are on the apex level, with one blocking the other’s last available move, does the severe penalty apply.

Isolated pawn (−18 cp): no own pawn exists on file f−1 or f+1 at any level or rank. isIsolatedPawn scans all 25 cells on each adjacent file. Capablanca treated isolated pawns as chronic structural liabilities; in Raumschach, where the five-file board gives them fewer hiding places, the penalty is calibrated accordingly.

Passed-pawn bonus (+W_PASSED_QUAD × adv², W = 3): quadratic in advancement, active in all phases. A pawn at advancement 4 earns +48 cp; at advancement 8 (adjacent to the promotion apex) earns +192 cp. The quadratic scaling reflects that a nearly promoted passer is exponentially more dangerous than one still in midboard.

Rook behind passed pawn (+W_ROOK_BEHIND = +20 cp): an own Rook stands behind the passer on a clear level or rank ray — sliding from behind toward the pawn with no intervening piece. rookBehindPasser searches both axes. This encodes Capablanca’s instinctive application of the Tarrasch formation: the Rook supports the pawn’s advance and is rewarded by the open line the pawn will eventually vacate.

§7.3 — Hanging Pieces and Exchange Evaluation

Capablanca was rarely caught with a piece en prise — nor, characteristically, did he allow a piece to sit on a square that was safe only by accident. Three sub-cases are distinguished using lva (least-valuable attacker) and see1, a single-step exchange evaluator consistent with the engine’s search-free design. see1 applies the initiating capture, finds the opponent’s cheapest recapture via lva, and returns the signed net material result — positive for a winning exchange, zero for neutral, negative for a losing one. One half-move of opponent reply is considered; no further. Whether to enter a given exchange is then controlled by the contextual threshold τ described in §7.13.

Hanging pieces: An own piece that is attacked (lva finds a recapture) but not defended incurs −0.85 × PV[type]. The discount from the full piece value reflects that the opponent may not immediately capture.

Unfavorable exchanges: A defended piece attacked by a cheaper opponent piece incurs a penalty proportional to the expected material deficit: −0.65 × max(0, PV[own] − PV[cheapest attacker]). Where the attacker is more valuable than the defender (a potential gain for the engine), a soft signal proportional to the enemy influence on the square and the piece’s relative value is applied: −W_THREAT × oppField[sq] × (PV[type] / PV[Q]), registering that an exchange on that square, while nominally favorable, involves some positional risk.

Veiled threats: When no current attacker exists, the square is not necessarily safe. veiledThreatCount scans each of the three sliding-direction families (RD, BD, UD) outward from the piece. If the first piece encountered on a ray is an enemy piece, the scan continues past that blocker for a second piece; if the second piece is an enemy whose movement type matches the ray’s geometry — Rook or Queen on an orthogonal ray, Bishop or Queen on a face-diagonal ray, Unicorn or Queen on a space-diagonal ray — the ray is a genuine latent threat, since the blocker need only step aside for the square to come under attack. A friendly blocker terminates the scan immediately and counts for nothing (that case is the separate self-discovered-exposure scenario, already handled correctly by reactive post-move evaluation), as does a blocker followed by a piece geometrically incapable of attacking along that ray, such as a Knight or Pawn. Each qualifying ray applies a small proportional discount: −W_VEILED × count × (PV[type] / PV[Q])  (W_VEILED = 10). The penalty scales with the piece at risk: in a tested three-square configuration with an own piece shielded by an enemy blocker in turn backed by a second enemy piece on the same ray, a Bishop in the shielded position drew a discount of −3.46 cp, while a Queen in the identical geometry drew the full −10.00 cp — proportionally larger because more is at stake. The same test confirmed a Knight or Pawn standing where the attacking second piece had been produces zero discount, since it poses no future threat along that ray regardless of the blocker’s position, and a friendly piece in the blocking position likewise produces zero discount. The magnitude is deliberately kept small relative to the hanging-piece penalty — a fully hanging piece still loses 85% of its value, while the worst veiled threat tested here cost a Queen only 10 cp — reflecting that a veiled threat is a partial liability, not an immediate one.

§7.4 — Piece Coordination

“Place your pieces on good squares and keep them protected.” In the influence-field framework, a piece is well-coordinated when it stands on a square with high friendly influence — meaning other friendly pieces bear on that square and support it. For each own non-king piece at square sq:

+W_COORD × ownField[sq]    (W_COORD = 7)

A piece on a square with ownField[sq] = 1.0 earns a full 7 cp coordination bonus; a piece in a corner with near-zero own influence earns nothing. This rewards piece networks rather than isolated activity.

Batteries: Beyond the general coordination measured by the influence field, the engine recognises a more specific structural pattern: two own sliding pieces of compatible movement type aligned on the same ray, with nothing between them, so that the rear piece’s attack is stacked directly behind the front piece’s. batteryThreats scans, for every own sliding piece (Rook, Bishop, Unicorn, or Queen), each of its movement directions outward; the first piece found along the ray is the candidate front piece, and if it is own and its own movement type also covers that exact ray, the rear/front pair constitutes a battery — no third piece is required. Each rear piece identified earns a small bonus proportional to its own value: +W_BATTERY × count × (PV[type] / PV[Q])    (W_BATTERY = 12). The bonus is attributed to the rear piece, not the front one, because it is the rear piece’s value that is ‘hidden’ and amplified by the piece standing ahead of it; losing a Queen backing a Rook battery costs more than losing the Rook backing the Queen, and the reward tracks that same asymmetry. This captures, in structural terms, a genuinely Capablancan preference: doubled Rooks on an open file, or a Queen-and-Bishop battery prepared well in advance, not as an improvised tactical combination but as a standing positional asset built with time and cashed in with technique.

§7.5 — Center Control

Capablanca’s positional judgment always included central control as a prerequisite for piece activity. The three-dimensional center is the inner 3×3×3 cube of 27 cells, with Cc3 at its geometric heart.

The cumulative own influence over all 27 inner-cube cells, minus the opponent’s, gives net central dominance:

+W_CENTER × (Σinner ownField[i] − Σinner oppField[i])    (W_CENTER = 14)

This term naturally rewards pieces with many legal moves bearing on the center, including sliders on open central lines and knights planted near Cc3.

§7.6 — King Safety and Activation

Capablanca’s endgame king was legendary in its activity. The evaluation distinguishes safety mode (opening and middlegame) from activation mode (endgame).

Safety mode uses influence in the king’s immediate 3×3×3 neighborhood (all cells at Chebyshev distance ≤ 1). Enemy influence in the neighborhood is penalized; friendly influence is rewarded:

−W_KING_EXP × oppField[nb] + W_KING_SHL × ownField[nb]    per neighbor cell (W_KING_EXP = 18, W_KING_SHL = 10)

Activation mode applies when both queens are off the board and the opponent holds at most one non-pawn, non-king piece. In this mode:

(a) Centrality: +W_KING_ACT × (4 − cenDist(king))  (W_KING_ACT = 8), drawing the king toward Cc3.

(b) Enemy king proximity: When ahead in material, +W_KING_ACT × (4 − cheby(king, enemyKing)), encoding Capablanca’s endgame principle of marching toward the opponent’s king once activation is safe.

(c) King cut-off: +18 cp for each own passed pawn P where the own king K lies on a Chebyshev shortest path between the enemy king E and P: cheby(E,K)+cheby(K,P) = cheby(E,P). This encodes Capablanca’s asymmetric endgame technique of interposing the king between the enemy king and own passers.

Opening King exposure penalty acts as a structural override for the opening phase. The W_CENTER term rewards pieces that project influence into the inner 3×3×3 cube; because the King has 26 directions it floods the central cube with ownField more effectively than any other piece, generating a spurious W_CENTER bonus of roughly 105 cp that would otherwise draw it toward Cc3. The penalty is a direct countermeasure: for each level the King has strayed from its home level (level A for Black, level E for White):

−W_KING_OPEN × levelDist   (W_KING_OPEN = 40)

And if the King is inside the central cube at all:

−W_KING_OPEN × 3  (additional flat penalty)

A King one level from home and inside the cube therefore incurs −40 cp − 120 cp = −160 cp — sufficient to override the W_CENTER incentive by a comfortable margin even if candidate-generation guards were somehow bypassed. The penalty fires only during the opening phase; once the middlegame begins, the King’s movement is governed by shelter terms and eventually by activation.

King file-centering penalty (tuck incentive). The tuck logic in Principle 4 generates a candidate move toward the corner file, but without an accompanying eval incentive that move earns no positive score and is perpetually outbid by genuine piece development. W_KING_FILE provides that incentive: while the King is on its home level, it pays a penalty proportional to its distance from the nearest corner file:

−W_KING_FILE × min(f, 4−f)   (W_KING_FILE = 25)

A King on the central file (f = 2) loses 50 cp; one file from the corner (f = 1 or f = 3) loses 25 cp; at the corner file itself the penalty is zero. Every step of the tuck therefore reduces the penalty by 25 cp, giving the King a concrete incentive to move rather than wait. This term applies only on the home level — the level-exposure penalty above already handles any King that has strayed forward.

§7.7 — Unicorn 3-Parity

A Unicorn move flips all three coordinate parities simultaneously: its (l mod 2, r mod 2, f mod 2) triple is invariant across moves, permanently confining it to 30 of the 125 cells. Two own Unicorns with identical parity triples duplicate each other’s coverage; different triples give complementary reach.

When both own Unicorns share the same 3-parity class: −W_UNI_PARITY / 2 = −11 cp. When they cover complementary classes: +W_UNI_PARITY = +22 cp. The net swing of 33 cp for correct Unicorn pairing correctly reflects the structural advantage of full triagonal coverage.

§7.8 — Bishop Color Pair

All face-diagonal moves preserve (l+r+f) mod 2, confining each Bishop permanently to one color complex of 60 or 65 cells. Two own Bishops sharing the same complex duplicate coverage. When both own Bishops share the same color class: −W_BISH_PAIR / 2 = −9 cp. When they cover opposite complexes: +W_BISH_PAIR = +18 cp. Capablanca understood color-complex exploitation better than any player of his era; this heuristic is its 3D formalization.

§7.9 — Open and Half-Open Lines

Capablanca consistently placed his sliders on open lines bearing on enemy weaknesses. rayQuality returns 2 for an open ray (no pawns), 1 for half-open (no own pawn, but an opponent pawn present), and 0 for closed. Type factors scale the bonus to reflect how many rays each piece type has and how much it benefits from open lines:

PieceDirsType factorOpen ray bonusHalf-open bonus
RookRD (6)1.0+15 cp+8 cp
UnicornUD (8)0.7+10.5 cp+5.6 cp
BishopBD (12)0.5+7.5 cp+4 cp
QueenQD (26)0.3+4.5 cp+2.4 cp

The Queen receives a lower per-ray factor because its 26 rays generate a large raw bonus even at modest weights; the factor is calibrated so that a centrally placed Queen on mostly open lines earns roughly the same total open-line bonus as a centrally placed Rook.

§7.10 — Outpost Squares

Capablanca routinely placed knights and bishops on squares enemy pawns could never advance to attack, giving them permanent occupation. A square is an outpost for the engine if no enemy pawn currently attacks it. Eligible pieces are non-queen minor pieces (N, B, R, U) in the opponent’s half of the board (level ≥ 2 for White, ≤ 2 for Black) that are not on their home square. Knights and Bishops receive the full bonus; Rooks and Unicorns receive half.

+W_OUTPOST × factor    (W_OUTPOST = 14; factor = 1.0 for N/B, 0.5 for R/U)
§7.11 — Material Simplification

“When ahead in material, exchange pieces to simplify to a won endgame; when behind, keep pieces on the board to create chances.” This Capablancan principle is encoded as a per-piece-count adjustment. pieceDeficit counts non-pawn, non-king pieces already traded from the initial 16 (8 per side). When materially ahead (> 100 cp): +W_SIMPLIFY × pieceDeficit. When materially behind (< −100 cp): −W_SIMPLIFY × pieceDeficit. This applies a gentle incentive rather than a hard constraint, allowing tactics to override simplification when warranted. W_SIMPLIFY = 6.

§7.12 — Opening Piece-Development Bonus

Active only during the opening phase: +W_DEVELOP = +80 cp for each own non-pawn, non-king piece standing off its home square.

This term exists to correct a structural scoring imbalance the central-cube dominance term (§7.5) creates between pawns and sliding pieces. A pawn’s entire field of influence is local and concentrated, so nearly all of it lands inside the 27-cell central window when the pawn enters it. A Knight, Bishop, or Unicorn’s longer rays sweep through the cube and beyond it, often leaving the cube-restricted influence sum roughly unchanged or even reduced relative to a more conservative square. Left uncorrected, this made central pawn pushes consistently outscore genuine piece development — at the opening position, the best newly-qualifying pawn push scored 639 cp against the best minor-piece development move’s 564 cp, the precise inversion of Capablancan opening priority.

The flat per-piece bonus, set to roughly a Knight’s worth, was calibrated empirically as the threshold at which genuine development reliably overtakes the best competing central pawn push at the opening position. It is a standard, independently justifiable evaluation term in its own right — development tempo is a recognized concept in conventional chess evaluation — rather than a narrow patch; its specific value may benefit from further calibration through play.

§7.13 — Contextual Exchange Threshold

The single-step exchange evaluator see1() answers whether a trade is materially sound given an immediate recapture. It cannot answer whether now is the right moment to enter it. A strong player does not calculate three moves ahead to decide whether an exchange is premature; they read the position’s character — material balance, king exposure, piece activity, line control, and what the trade itself will leave behind — and feel the right level of appetite for simplification. The first four of those signals are visible in the current position without any lookahead; the fifth requires looking one forced half-move past the trade itself — the same single step of resolution see1() already takes, here extended from a material question to a structural one. Rather than a fixed see1() ≥ 0 bar, captures are accepted when see1() ≥ τ, where τ is a contextual threshold that rises and falls with the position. Because see1() returns a signed value — positive for a winning trade, zero for neutral, negative for a losing one — a negative τ correctly accepts mildly unfavorable trades when the position warrants it (for instance, when materially ahead and seeking simplification), while a positive τ correctly demands a clearly winning outcome.

exchangeBaseline computes the position-wide component of τ once per generateCandidates call, since neither signal it encodes depends on which specific piece is trading. It is then passed as a precomputed argument into exchangeThreshold for each individual capture decision.

Signal 1 — Material trajectory (W_EXCH_MATERIAL = 40). When materially ahead by more than 100 cp, τ drops by 40: the engine actively welcomes even mildly unfavorable trades, because simplification itself has value when the enemy force is smaller. When behind by the same margin, τ rises by 40: only a strictly winning trade justifies entering an exchange, since an even trade helps whichever side has the larger army — never the side trying to complicate. This extends the existing W_SIMPLIFY incentive (§7.11) from a post-hoc evaluation bonus into an actual decision threshold at the moment of capture.

Signal 2 — King safety asymmetry (W_EXCH_KING = 8). The same exposure() sum used in §7.6 — total enemy influence accumulated over the king’s immediate 3×3×3 neighborhood — is computed for both kings. The difference is applied continuously: when the engine’s own king is the more exposed of the two, τ falls (welcome trades that thin the attacking force, even at a small material discount); when the opponent’s king is more exposed, τ rises (hold material on the board as firepower rather than simplifying while there is a target). The signal is proportional rather than binary, reflecting that king safety exists on a spectrum.

Signal 3 — Piece activity (W_EXCH_ACTIVITY = 12, clamped ±30). This is the most distinctly Capablancan of the four signals. Capablanca would not trade an actively placed piece for a passively placed one of equal nominal value, because the resulting position favors whoever keeps the better piece. The activity of the specific piece considering a trade is measured by its influence-field φ-sum over legal moves — the same measure used by worstPiece. Crucially, this is normalized as a relative deviation from the average activity of all other own non-pawn, non-king pieces: relDev = (moverActivity − avgOther) / max(1, avgOther). Absolute φ-sums cannot be compared across piece types — a Queen is always more mobile than a Knight regardless of placement quality, so raw comparison would measure piece type rather than placement quality. The relative deviation expresses instead “how much better or worse placed is this piece than its peers” in a type-blind, normalized way. A well-placed piece (high positive relDev) raises the bar; a poorly placed one (negative relDev) lowers it, since trading a bad piece for anything is rarely wrong.

Signal 4 — Open-line monopoly (W_EXCH_LINE = 25). A piece commanding a fully open ray in its own movement family — a Rook on an orthogonal with no pawns, a Bishop on a clear face-diagonal — is worth more than its point value while it holds that line. Trading it away for an unrelated tactical reason forfeits an asset the raw exchange value does not capture. If any ray in the piece’s direction family has rayQuality = 2 (fully open), τ rises by 25. Two guards make this signal precise. First, it is gated to non-opening phases: before pawns have moved, long-range pieces trivially have several open diagonals simply because the board is still full and empty; the bonus is only meaningful once open lines are a scarce, earned asset. Second, a capture that travels along the credited ray is exempt: the piece is not abandoning the line, it is spending it productively, and penalizing that would conflate “this piece is valuable because of its line” with “this piece should sit still and do nothing with it.” The direction of the proposed capture is checked against the direction that earned the bonus; if they match, the signal is suppressed for that move.

Signal 5 — Post-exchange structural delta (W_EXCH_STRUCT = 100, clamp). Signals 1–4 all describe the position as it stands before the trade — material context, king-safety asymmetry, the mover’s own placement, its monopoly on a line. None of them ask what the board actually looks like once the exchange has finished: an even trade can still hand the opponent better central influence, a safer king, or a newly opened line, purely because of which square the recapturing piece ends up standing on. postExchangeDelta plays the initiating capture, finds the opponent’s cheapest reply on the same square via lvaSq — the same least-valuable-attacker scan as lva, returning the attacker’s square rather than only its type — and plays that reply too when one exists; when none exists, the comparison collapses to a one-ply post-capture delta, since a free capture has already reached its resting position after a single half-move. structuralScore is computed on both sides of that resolution and the difference is read off: it isolates the handful of evaluation terms that are genuinely about where pieces stand rather than what they’re worth — central-cube dominance (§7.5), king safety for both kings (§7.6, since a trade that exposes the opponent’s king is just as much a structural gain as one that shelters the engine’s own), piece coordination (§7.4), and open or half-open line status for the remaining sliders (§7.9). Material itself is deliberately excluded, since that is see1()’s own domain and folding it in again would double-count the same swing under a different name; the count-driven, phase-specific terms (simplification, the opening development bonus, Unicorn parity, the Bishop pair, the King tuck incentive) are excluded for the same reason — they shift by roughly the same amount no matter which specific square is involved, and so cannot distinguish a structurally good trade from a structurally bad one at equal material. The resulting delta is clamped to ±100 and subtracted from τ: wide enough that a genuinely catastrophic structural cost behaves almost like a veto, while still leaving room for a sufficiently large material gain — winning a Queen, say — to clear it. Unlike Signals 3 and 4, this one is not scoped away from Pawn and King movers: piece-activity and open-line-monopoly are the wrong lens for either of them, but the structural consequence of the contested square — who ends up standing there, and what that does to central influence, king safety, and coordination — is just as real no matter which piece initiates the capture.

These functions are wired into exactly the two places in generateCandidates where the engine genuinely decides to enter a trade: the relieve-capture branch of Principle 1 (threat response), and the “wins material” exception in the development gate. exchBaseline is computed once at the top of each candidate-generation pass and threaded, alongside the pre-trade influence fields postExchangeDelta needs for Signal 5, into exchangeThreshold; that function is then called per-piece for those two specific decisions, and the result is compared against see1().

8. Design Decisions

✓ Resolved
8.1 — No Minimax: A Design Choice, Not a Limitation

The governing choice behind Raumcapa is philosophical before it is practical. Computer game-playing has a well-known empiricist wing — deep search over enormous trees, neural networks trained on millions of self-play games, evaluation by learned weights no person can read — and a rationalist alternative: parsimony, named structure, a position understood rather than merely scored. Raumschach, with no deep opening book, no large body of annotated master games, and no established middlegame canon, is poorly served by the empiricist path; there is no corpus to be big about. It is well served by the rationalist one, where a position’s value follows from stated reasons rather than from data that does not yet exist. Raumcapa is built on the rationalist side of that line, and the choices below follow from it rather than standing beside it as separate concerns.

Two consequences follow directly. First, the choice of Capablanca as design model (§2): a rationalist engine needs principles articulated with engineering precision, and Capablanca’s instructional writing — unlike Morphy’s, reconstructed only after the fact by historians — supplies them in his own words. Second, the exclusion of minimax: search of any depth substitutes brute enumeration of future positions for understanding of the present one, which is precisely the empiricist move the rationalist framing rejects, independent of what it would cost to run. A position search-evaluated by exhaustive lookahead is scored, in the same sense a neural network’s position is scored; it is not understood by anything that could explain itself.

That philosophical ground is also, conveniently, the practical one. Raumschach’s branching factor makes useful minimax depth expensive in a browser context, and the horizon effect at shallow depth produces evaluations that can be worse than principled one-ply assessment. The influence-field approach produces instantly responsive moves at any difficulty level, a practical necessity for a browser-deployed game client. And a search engine optimizing a minimax score would not necessarily produce Capablancan style — methodical, structural, intelligibly reasoned — and might suppress it entirely in favor of short-range tactics. These three points support the decision; they are not the decision’s foundation.

The same standard excludes an endgame tablebase, even though one would be cheap to query at runtime. A tablebase is built by retrograde analysis: starting from terminal positions and working backward by exhaustive backward induction over every reachable arrangement of a small material set, classifying each as won, drawn, or lost. That construction is search — minimax carried to convergence over an entire state space rather than to a few plies down one branch — merely performed once, in advance, rather than live during play. Consulting the resulting table at move time costs nothing and looks, mechanically, like any other static lookup the engine already performs. But the architectural commitment defended above is to method, not only to runtime cost: a move that exists because exhaustive enumeration proved it is not a move any of Raumcapa’s ten named principles can claim, and folding it in regardless would quietly reintroduce, for a subset of positions, exactly the kind of engine Raumcapa was built not to be. A tablebase is accordingly excluded from Raumcapa on the same grounds as minimax itself. The idea is not wasted: exhaustively verified endgame outcomes for small material balances such as King+Unicorn vs. King are a natural and welcome contribution to the standalone Raumschach endgame-theory corpus, where they document settled fact rather than feed an engine’s runtime decision.

✓ Resolved
8.2 — Opening Book: Considered and Rejected

An opening book was considered and rejected. Raumschach’s game corpus is small and drawn from opponents of uncertain strength; a book derived from it would encode the biases and errors of those games without uncertainty quantification. More fundamentally, Capablanca’s most characteristic opening quality was not memorized preparation but principled development: he developed every piece before doing anything else, and was contemptuous of opening systems not grounded in positional logic. An engine modeled on Capablanca is poorly served by a library of rote first moves. The development principle of §6.3 implements exactly this — and does so through evaluation, not around it.

✓ Resolved
8.3 — Difficulty via Noise, Not Depth

Conventional engines control difficulty by reducing search depth. Since Raumcapa has no search depth to reduce, difficulty is controlled by adding uniform random noise to each candidate’s evaluation score. This has a desirable property: the qualitative character of the engine’s play remains Capablancan at all levels. At Boy level, moves are random; at Prodigy, the engine follows its principles but is frequently overridden by noise; at The Machine, evaluation is fully deterministic. The five named levels (Boy, Prodigy, Challenger, Champion, The Machine) correspond to Capablanca’s life phases, giving them descriptive weight beyond a mere integer scale.

✓ Resolved
8.4 — Draw Policy

The draw acceptance and offer policy follows Capablanca’s documented competitive practice. Capablanca accepted draws when he judged a position roughly equal or worse; he rarely offered draws when ahead, preferring to find the technical conversion. The policy: the engine proactively offers a draw when its static score falls below −80 cp (clearly losing by its own assessment); it accepts a draw offered by the player when its score is below +50 cp (not sufficiently ahead to decline). This policy is computed from _score on the main thread, not from search. The same _score channel also drives the resignation policy described in §8.10, for positions beyond what a draw offer would address.

✓ Resolved
8.5 — Evaluation Weights: First-Principles Values

All evaluation weights are set at first-principles values rather than calibrated by corpus. The reason is practical: calibration via self-play at one-ply depth produces a corpus that is strongly shaped by opening-phase behavior, where the influence-field terms have different relative importance than in the middlegame or endgame. A corpus-calibrated weight set would likely over-weight development-phase terms and under-weight endgame terms. The current weights are instead set by geometric argument (e.g., the open-line type factors reflect ray counts) and by qualitative testing against human play. Formal calibration is deferred to a future corpus with stratified phase sampling.

✓ Resolved
8.6 — The King Centralization Problem: a Structural Conflict

A structural conflict between the W_CENTER term and the King’s mobility required both a candidate-generation fix and an evaluation override. The conflict arose as follows. The central volume sub-principle (P5 contest) added any own move landing in the inner 3×3×3 cube to the candidate set when the engine was losing central influence. It did not filter King moves. Once the King was a candidate, evalBoard scored it dramatically higher than any development move: the King has 26 legal-move directions, projects influence along all of them, and from Cc3 floods the central cube with ownField values totaling roughly 105 cp of W_CENTER bonus — more than any other piece from that square. The engine thus reliably marched its King into the center in the opening phase, even at the fully deterministic difficulty level (The Machine), where randomness plays no role.

The fix required both layers of the architecture to act in concert. At the candidate-generation level, King moves are now excluded from P5 (central volume contest), P5b (anti-development occupancy), and the safety-net fill. The King may only enter the candidate set via Principle 1 (giving check), Principle 3 (king shelter in an emergency), Principle 2b (capturing a threatening Knight), and the dedicated tuck logic that steers it toward a corner of the home level. At the evaluation level, the opening King exposure penalty (W_KING_OPEN = 40 cp per level from home, plus 3× if inside the central cube) ensures that even if some future modification were to reintroduce a King candidate path, the evaluation itself would reject the move. The two layers are deliberately redundant: candidate generation prevents the King from reaching the candidate set in the first place; evaluation penalizes it if it somehow does.

The root cause is worth stating precisely: the W_CENTER term is correct for pieces that should develop to the center. The King is the single piece in Raumschach that should explicitly not centralize in the opening, yet it benefits most from the term by sheer geometric virtue of its 26 directions. Any influence-field engine applied to a game with a vulnerable king piece must address this conflict directly.

✓ Resolved
8.7 — The Pawn-versus-Piece Scoring Imbalance

Implementing the pawn structural development principle (4b) surfaced a second instance of the same underlying conflict identified in §8.6, this time between W_CENTER and pawn structure rather than the King. The central-cube dominance term rewards short-range and long-range pieces unevenly: a pawn’s entire field of relevance is concentrated nearby, so virtually all of it lands inside the 27-cell central window the instant the pawn enters it, while a Knight, Bishop, or Unicorn’s rays sweep through the cube and continue beyond it — sometimes leaving the cube-restricted influence sum roughly unchanged, or even reduced, despite genuine development. Measured at the opening position, the best newly-qualifying central pawn push scored 639 cp against the best available minor-piece development move’s 564 cp. Left uncorrected, Raumcapa would have played central pawn pushes almost every move — the precise inversion of the priority the new opening principles were meant to establish.

The fix is the opening piece-development bonus described in §7.12: a flat +80 cp for each own non-pawn, non-king piece off its home square during the opening phase, gated by phase === ‘opening’ so it has no effect once the opening concludes. The value of 80 was set empirically as the threshold at which genuine development scores reliably exceed the best competing central pawn push at the opening position, and is documented in the source as a candidate for further calibration.

Taken together, §8.6 and this entry illustrate a recurring property of the influence-field architecture: a term calibrated correctly for the general case (reward central influence) can become locally miscalibrated for a specific piece class whose geometry differs sharply from the pieces the term was designed around — the King’s 26-direction radius in one case, the pawn’s short concentrated range in the other. Each time this surfaces, the resolution has taken the same shape: a small, independently justifiable evaluation term added specifically to restore the intended priority, rather than a special-cased patch to W_CENTER itself.

✓ Resolved
8.8 — Opening Phase Duration: Calibrating hasUndevelopedPiece

Self-play revealed that the engine could remain in the opening phase for 46 or more moves. The root cause was that hasUndevelopedPiece gated the transition on all non-pawn pieces, including Rooks and the King. A Rook that never found an open file held the entire position in opening mode indefinitely; the opening phase ended only when that Rook happened to move for some tactical reason, if ever.

The correct resolution required understanding why each piece class was originally included, then deciding which inclusions were wrong and which were right for the wrong reason. Rooks are excluded permanently: they are major pieces whose development is a consequence of open files appearing, not a precondition for leaving the opening. Including them in the gate conflates two different things — completing the opening and completing piece activity — that do not coincide for major pieces. The King, by contrast, was initially excluded on a defensive guess (worry that it, too, would hold the opening open indefinitely), but this was wrong in principle. Capablanca treated king safety as an opening objective, not a middlegame afterthought. The King belongs in the gate.

With the King restored to the gate, two further conditions were necessary to make the tuck actually fire. The W_KING_FILE penalty (§7.6) was needed because the tuck was already generated as a Principle 4 candidate but never chosen: sitting on the central file of the home level carried no penalty, so the tuck outbid nothing. The 25 cp-per-file-step penalty gives every corner-approach move a concrete positive evaluation value. And the lateral fallback in Principle 4 was needed because some geometrically cornered minor pieces — a Unicorn on a back-level corner square, for instance — had no cenDist-improving move available at all, so the strict < criterion produced zero candidates for them and they held the opening open regardless. The fallback accepts any safe off-home-square move as development for pieces in that situation, firing only when the primary criterion yields nothing.

With all three changes in place, both sides leave the opening phase around move 18–19 in self-play at The Machine difficulty — still longer than Capablanca’s human games, but defensible for a 5×5×5 board with seven minor pieces per side plus the King to activate.

✓ Resolved
8.9 — Phase Reversion: Making the Opening→Middlegame Boundary a One-Way Door

determinePhase was originally stateless — it recomputed the phase from scratch on every call. This produced a phase-reversion bug: a piece returning to its home square after having left it (the King tucking back from a slightly advanced position, or a piece retreating under pressure) would re-trigger hasUndevelopedPiece, pushing the phase back to ‘opening’ mid-game and reactivating all opening-only candidate-generation principles at an entirely wrong moment.

The fix introduces _phaseFloor, a persistent worker-scope object keyed by side, which acts as a ratchet. The floor starts at ‘opening’ and advances to ‘middlegame’ the first time no qualifying piece remains on a home square; it never retreats. Subsequent calls to determinePhase consult the floor: if it has advanced, the function returns ‘middlegame’ (or ‘endgame’ if applicable) regardless of what hasUndevelopedPiece currently reports. The floor resets to ‘opening’ only when every qualifying piece is still at home — unambiguously a fresh position — which handles the new-game case without requiring an explicit reset signal from the main thread.

The middlegame↔endgame boundary is deliberately not protected by the floor. A pawn promotion during the endgame restores a queen and legitimately re-opens the middlegame; suppressing that transition would produce a different kind of error. The asymmetry is principled: opening development is a process that, once completed, cannot be meaningfully undone by piece movement; material imbalance is a live quantity that can change in either direction at any moment.

✓ Resolved
8.10 — Resignation Policy

Capablanca’s competitive record makes him an unusually well-characterized model for resignation behavior. His eight-year unbeaten streak (1916–1924) meant he rarely faced lost positions at all, but when he did, he resigned cleanly and without delay. His most documented resignation, in the adjourned 34th game of the 1927 World Championship match against Alekhine, was a short note sent rather than a continued struggle: terse, gracious, and immediate once the conclusion was certain. He resigned from certainty, never from complexity or from being merely worse, and he never played on hoping for a blunder.

The policy translates this character into three conditions, none requiring lookahead, evaluated on the main thread after the worker returns its chosen move:

(1) Phase gate. The policy never fires during the opening. Capablanca would find a way through opening complexity; a single bad-looking opening evaluation is not the kind of certainty that warranted his resignations.

(2) Phase-scaled evaluation threshold. The worker returns _phase alongside _score (§6.5). If _score falls below −1100 cp in the middlegame (roughly a Queen’s worth) or below −700 cp in the endgame (roughly a Rook plus), the position is treated as objectively lost. The endgame bar is set lower than the middlegame bar because Capablanca’s own endgame calculation was precise enough that he saw no reason to prolong a technically determined loss, whereas a middlegame evaluation of the same magnitude still carries more practical uncertainty.

(3) Persistence check. A single qualifying evaluation is not sufficient; G.resignStreak must reach 3 consecutive engine turns below the threshold before resignation fires, and resets to zero the moment any turn falls back above it. This filters out transient tactical blips that produce a momentarily bad score but resolve favorably one or two moves later, preserving the “I have calculated this; there is no resource” quality of an authentic resignation rather than a reflexive one.

When all three conditions are met, the engine resigns instead of making its chosen move. The resignation is presented as a styled letter on the game-over panel, addressed to the winning side and signed by Raumcapa, deliberately echoing the tone of Capablanca’s 1927 note — brief, gracious, and without complaint:

Dear [opponent],

The position no longer offers any resource. I give up the game. Congratulations on your success, and my compliments on your play.

Sincerely yours,
Raumcapa

The salutation and signature are set in upright type; the body remains italic — notepaper styling against the dark overlay, distinct from the plain-text draw and game-over messages elsewhere in the interface.

✓ Resolved
8.11 — Approximate Zugzwang (Tier 1)

§10.3 originally listed zugzwang recognition as beyond one-ply evaluation, full stop. That framing collapsed two different problems into one. Recognizing that an opponent is currently obligated — that even their best available move makes their position worse than standing pat would — is a comparison between two static evaluations of the same position, and needs no tree. Engineering obligation through triangulation, opposition, or corresponding squares requires reasoning about a sequence of future positions, and does need one. The two were never going to get the same answer.

True recognition, even in its cheapest form, is a stand-pat-vs-best-reply comparison: evaluate the opponent’s position as it stands, then run the engine’s own candidate-generation and evaluation pipeline once more from the opponent’s side to find their best reply, and compare the two. That is a real ply of lookahead — bounded, not a tree, but lookahead nonetheless — and it was deliberately not built. Implementing it broadly would mean paying generateCandidates and evalBoard a second time for every one of Raumcapa’s own candidates, at exactly the stage of the game (the middlegame, with Raumschach’s high branching factor still in full effect) where that second pass is least affordable.

What ships instead is a mobility-restriction proxy, not recognition: approxZugzwang(oppLegalCount, phase) returns max(0, ZZ_MOBILITY_BASELINE − oppLegalCount) × W_ZUGZWANG, active only when phase==='endgame'. The premise is structural rather than evaluative — an opponent with very few legal replies is more likely to find that all of them concede something, simply because there is so little room left to maneuver. It costs nothing beyond a lookup: oppLegal is already computed in getBestMove’s candidate loop to detect stalemate and checkmate, so the proxy reads a value the engine was already holding. No new influence-field pass, no recursive candidate generation, no second call into evalBoard.

The endgame gate is doing real work here, not just scoping the feature. isEndgame requires both queens off the board and the opponent reduced to at most one minor piece — precisely the material range in which mobility counts are naturally small and a low count is informative, and precisely where Capablanca’s own zugzwang sense actually operated (king-and-pawn and king-and-minor endings, not queen-and-rook middlegames where a dozen safe moves is the normal state of affairs).

The initial constants, ZZ_MOBILITY_BASELINE=6 and W_ZUGZWANG=6, are calibration candidates rather than derived values, set to put a single-digit-mobility position on the same order of magnitude as the engine’s other endgame terms (W_KING_ACT=8, §7.6) without letting the proxy dominate material or king-activity considerations on its own. The honest caveat: this term sometimes rewards a cramped opponent who in fact has a perfectly fine move available. It is a heuristic correlate of obligation, not a certificate of it, and is named and documented as approximate for exactly that reason.

✓ Resolved
8.12 — Repetition Contempt

The worker carries the game’s position history into its own move-selection loop, not only into the player-facing draw-claim button. For each candidate, repAdjustment hashes the position that would result — the board exactly as it would stand after that move, paired with the side to move next — and checks it against posHistory for prior occurrences. A candidate that would create the position’s second occurrence receives an adjustment of REP_APPROACH_ADJ = 20 cp; one that would create the third, the point at which either side may simply claim the draw on the next move, receives REP_CLAIM_ADJ = 65 cp. A position with no prior occurrence receives no adjustment.

The sign follows REP_AHEAD_THRESH = 50 cp, the same bar the draw-acceptance policy uses (§8.4): below it, the engine has no advantage worth protecting, and the adjustment is added — repetition is treated as an acceptable, even welcome, outcome, consistent with Capablanca’s own readiness to take a fair half-point rather than press a position with nothing left in it. At or above the bar, the adjustment is subtracted: repeating the position would mean surrendering a real advantage for no reason, and the engine is steered toward one of its other candidates instead.

This is a contempt term, not a search. It reads the same posHistory array the main thread already maintains for the human player’s draw claim, costs a single string-equality scan per candidate, and requires no lookahead into the opponent’s reply. It sits alongside approxZugzwang (§8.11) as a positional-context adjustment computed inside getBestMove’s candidate loop, applied before perturbation.

✓ Resolved
8.13 — Etiology: Surfacing the Engine’s Reasoning in Studio

§9 claims that Raumcapa is transparent: every move traces to a named principle and a named evaluation score. Until this addition, that claim was true of the architecture but not of anything a player could actually see. generateCandidates computed which principle admitted each candidate and then discarded the information the moment a single move object survived the comparison in getBestMove; evalBoard summed fifteen named terms into one score variable and returned only the sum. The reasoning existed for the duration of a function call and then vanished. Etiology, a panel in Raumschach Studio, is the change that stops discarding it.

The implementation is instrumentation, not new analysis. add(), the function every candidate-generation principle calls to register a move, now also accepts a tag naming the principle that called it — and, where a single principle has more than one qualifying path, which path: 'P2-escape' and 'P2-capture' are both Principle 2; 'P6-king' and 'P6-pawn' are both Principle 6; and so on through all ten. The safety-net fill (§6.3) is tagged the same way, under a dedicated 'SAFETY-NET' label, since a pick that satisfied none of the ten principles is exactly the case this panel exists to say plainly rather than leave silent. A move’s tag set can hold more than one entry, since a move can satisfy more than one principle at once. evalBoard gained a small accumulator, T(key, value), that performs the identical score += value as before while optionally also recording the contribution under its name; it is a no-op on the great majority of calls — legality probes, SEE, the White’s-perspective comparison score — and active only for the handful of candidates getBestMove retains for explanation. getBestMove itself now keeps, for the chosen move and its five closest-scoring rivals, the tags and the term breakdown that were already being computed and thrown away. None of this changes which move is selected: bestMove, bestScore, and bestWhiteScore are chosen by exactly the comparisons documented in §6.5. It only stops deleting the record of why.

Two adjustments computed outside evalBoardapproxZugzwang (§8.11) and repAdjustment (§8.12) — are recorded the same way, under their own names, rather than folded silently into the principled total. Difficulty-level perturbation (§6.5, §8.3) is recorded likewise: below The Machine, noise can be the actual reason a non-top-scoring candidate was played, and Etiology says so rather than presenting randomness as positional judgment.

One honest edge case is worth recording here rather than leaving it to be discovered. In a small number of positions — an absolute pin against the engine’s own King with no interposing or capturing resource, for instance — every legal move belongs to the King, no King move satisfies Principle 1, 2b, or 3, or the tuck, and the safety net cannot help because it excludes King moves by construction (§6.3). cands is then empty, and getBestMove falls back to a uniformly random legal move exactly as it always has. Etiology reports this case as what it is — no principle nominated a candidate — rather than inventing a reason after the fact.

The distinction worth drawing explicitly is with a large language model’s narrated reasoning. An LLM’s stated chain of thought is text generated by the same opaque, learned weights that produced its answer, and the literature on chain-of-thought faithfulness documents real cases where the narration does not track the computation it claims to describe. Etiology has no such gap to worry about, because there is no second system narrating a first: the tags are which principle’s condition matched, and the breakdown is the arithmetic evalBoard already performed. This is only possible because of the architectural choice defended in §8.1 — a search-free, principle-only engine has nothing to summarize that it did not already compute in a form a person can read.

9. Advantages and Disadvantages

The influence-field architecture is not a minimax engine with features removed; it is a different kind of engine. Its strengths and weaknesses differ accordingly.

Advantages

Speed and responsiveness. Raumcapa returns a move instantly at any difficulty level. No search tree is constructed; the per-candidate cost is two influence-field computations and one evaluation call, each O(125). The browser is never blocked.

Transparency. Every move Raumcapa makes traces to one of ten named principles and an explicit numeric score. This is not true of minimax engines (where the chosen move emerges from a tree that cannot be easily summarized) and certainly not true of neural network engines. Players can understand, and disagree with, what Raumcapa is doing. Raumschach Studio’s Etiology panel (§8.13) puts this directly in front of the player, rather than leaving it as a property only the source code reveals.

Smooth difficulty gradient. Noise-controlled difficulty produces a more natural gradient than depth-controlled difficulty. At depth 2, a minimax engine plays very differently from depth 3; the change is discontinuous and qualitative. At perturbation 35 vs. 8 (Challenger vs. Champion), Raumcapa plays the same structural chess, with more or fewer random deviations.

Appropriate branching factor. The influence-field approach scales well to Raumschach’s high branching factor. Raumschach’s Queen controls up to 26 ray directions; the Knight has 24 jump targets from an interior cell. The candidate-generation step limits the work to a small principled subset of legal moves without paying the exponential cost of a game tree.

Educational style. A principle-based engine plays in a way that illustrates Raumschach strategy. Players can study its moves and extract something useful. A highly optimized minimax engine would be a stronger opponent but a less instructive one.

Disadvantages

Tactical blindness. The engine does not see two-move combinations. It cannot find a discovered check two plies away, a fork that requires a quiet preparatory move, or a mating sequence beyond an immediate check. In sharp tactical positions, it will be outplayed by any engine performing even shallow minimax.

No opponent modeling. Because the engine does not search the opponent’s reply, it cannot anticipate or prevent threats that do not already exist on the board. It will sometimes develop a piece into a position that the opponent can immediately exploit. The threat-response principle (Principle 2) mitigates this for existing threats but does not prevent future ones — with one narrow exception: the jump-threat principle (2b) reads a Knight’s latent next-move fork directly off the board’s static geometry, since the Knight’s leap makes that one case computable without search. Threats from sliding or stepping pieces, and anything requiring more than a single move to materialize, remain entirely outside the engine’s anticipation.

Candidate incompleteness. The safety-net fill at MIN_CAND = 6 guarantees a minimum candidate set, but there exist positions where the correct move is not generated by any of the ten principles and does not rank in the top-6 safety-net fill. Principles 5b and 5c substantially reduce the incidence of the most costly oversights — missed development blockades and unaddressed exchange threats — but the engine remains capable of overlooking defensive resources that require a non-obvious quiet move outside the principle categories.

Endgame imprecision. Capablanca’s endgame technique depended on calculation — precise mating sequences, zugzwang recognition, opposition play — that is beyond one-ply evaluation. The king activation and cut-off heuristics encode the general direction of correct endgame play but cannot execute technical conversions with the precision the historical Capablanca brought to them.

Strength ceiling. The engine’s strength is bounded by the quality of its positional principles and evaluation weights, with no computational path to improvement. A minimax engine can be made stronger simply by running deeper; Raumcapa’s ceiling is determined by the quality of its heuristics. That ceiling may be sufficient for recreational human Raumschach — but it is a ceiling.

10. Capablanca Principles Not Yet Encoded

The heuristics in §7 encode Capablanca’s most transferable positional principles. Several dimensions of his mastery are not yet encoded, falling into three categories.

10.1 — Inapplicable in Three Dimensions

Castling and king shelter. Capablanca castled quickly and consistently, regarding early king safety as a prerequisite for middlegame operations. Raumschach has no castling rule. The king-shelter heuristic (§7.6) and the opening-phase king-tuck (§6.3, Principle 4) capture the spirit, but the precise two-move castled architecture has no implementation target.

Classical king opposition. Capablanca’s endgame technique included precise understanding of king opposition — the rule that two kings standing two squares apart with the side to move lose the opposition. In Raumschach, kings can approach from 26 directions; opposition is not formally defined in the same way. The king-proximity term (§7.6) captures the king-approach incentive but not the formal opposition concept.

Minority attack. Capablanca was precise about pawn-majority dynamics and the use of a minority pawn attack to create structural weaknesses. In Raumschach, the pawn topology makes the classical minority attack inapplicable as a defined strategy.

10.2 — Absent but Codeable

Backward pawns. A backward pawn — one that cannot advance without moving into a square controlled by an enemy pawn, and behind which no friendly pawn supports — is a distinct weakness in flat chess. Raumschach’s three-dimensional pawn advance geometry does not provide a clean analogue: the forward directions include both level and rank advance, and a pawn blocked in one direction may still have the other open. A defensible 3D backward-pawn definition could be constructed and weighted, but has not yet been.

10.3 — Beyond One-Ply Evaluation

Combination finding. Capablanca’s play was not combinationally oriented, but he calculated precisely when he needed to. One-ply evaluation cannot find any combination requiring a quiet preparatory move followed by a tactic.

Zugzwang recognition and engineering. Capablanca’s endgame technique included acute recognition of zugzwang — not a merely cramped position, but the specific condition that every move available, including the best one, makes things worse than standing pat would. A structural proxy for the precursor condition is implemented (§8.11, approxZugzwang): in the endgame, an opponent with very few legal replies is scored as more likely to be obligated, at no extra cost beyond a value getBestMove already computes for its stalemate check. This is restriction, not recognition — it rewards cramped positions whether or not every move in them actually loses, and proves nothing about any specific position. Genuine recognition would mean comparing the opponent’s stand-pat evaluation against their actual best reply, which requires running the engine’s own move-selection pipeline a second time from the opponent’s side — one real ply of lookahead, deliberately not built outside that single narrow, documented exception. Zugzwang engineering — triangulation, opposition, corresponding squares, deliberately losing a tempo to hand the obligation to the opponent — is a different problem again: it requires reasoning about sequences of future positions, not just the next one, and remains entirely out of scope. Both the deeper recognition and the engineering are problems that require either a perfect evaluation function or sufficient search depth.

Prophylaxis. Capablanca routinely anticipated and pre-empted the opponent’s plans. Principles 5b and the development gate implement the most directly formalizable prophylactic behaviors; deeper plan-anticipation — recognizing that the opponent intends a maneuver three moves from now and pre-empting it quietly — remains beyond one-ply evaluation.

11. Conclusion

Raumcapa is, to the best of the present author’s knowledge, among the first chess engines of any kind to rely entirely on positional principles and influence fields, with no minimax search tree and no neural network. It operates by projecting φ(d) = 1/(1+d) Chebyshev-decayed influence across the 5×5×5 board, selecting candidates by ten named principles — including a Capablancan development gate that prevents moving the same piece twice while others remain undeveloped, and a structural test that admits pawn moves into the opening repertoire only when they open a line or claim central space — and evaluating by one-ply static assessment of fifteen Capablancan heuristics: material; pawn structure (with rank-doubled, level-doubled, and apex-doubled penalties, isolated pawn, quadratic passed-pawn bonus, and Tarrasch rook-behind-passer); hanging pieces, unfavorable exchanges, and veiled threats; a contextual exchange threshold encoding material trajectory, king safety asymmetry, piece activity, and open-line monopoly; piece coordination; center control; king safety and activation with cut-off technique; unicorn 3-parity; bishop color pair; open and half-open lines; outpost squares; material simplification; an opening piece-development bonus; an endgame mobility-restriction proxy approximating zugzwang; and a repetition-contempt adjustment that discourages repeating a position when ahead and welcomes it otherwise.

Its advantages are speed, transparency, and a recognizable positional style. Its disadvantages are tactical blindness and a bounded strength ceiling. For Raumschach specifically — a game with a high branching factor, nascent theory, and no large corpus of master games — these trade-offs are defensible. The engine plays chess that can be understood, discussed, and learned from. That is a different goal from playing chess that wins, and it is a goal Capablanca himself would have recognized.

He was, after all, the world champion of Raumschach’s own prime decade.

12. References

Capablanca, J. R. (1921). Chess Fundamentals. New York: Harcourt, Brace and Company.

Capablanca, J. R. (1935). A Primer of Chess. New York: Harcourt, Brace and Company.

Dickins, A. S. M. (1971). A Guide to Fairy Chess. New York: Dover Publications.

Maack, F. (1907). Das Schachraumspiel: Dreidimensionales Schachspiel. Hamburg.

Shannon, C. E. (1950). Programming a computer for playing chess. Philosophical Magazine, 41(314), 256–275.

Silver, D., et al. (2017). Mastering chess and shogi by self-play with a general reinforcement learning algorithm. arXiv:1712.01815.

International Raumschach Federation. (2026). Maackesgeist: A Raumschach Engine (Version 2). maackesgeist-v2.html

Source code: raumschach.html (2026). International Raumschach Federation. raumschach.org/raumschach.html

Source code: studio.html (2026). International Raumschach Federation. raumschach.org/studio.html