Skip to content

Facón 1.5 - Espiga

Choose a tag to compare

@CMCanavessi CMCanavessi released this 26 May 17:17
· 1 commit to main since this release

Approximately +220 Elo over 1.4 (Ordo ~2550, G3+G4 combined n=2080). Gauntlet 3 (low-Ordo field, avg ~2310): 800.5/1040 (77.0%, Ordo ~2550). Gauntlet 4 (high-Ordo field, avg ~2680): 345.5/1040 (33.2%, Ordo ~2545). Cross-validation across the two fields confirms the rating within ~5 Elo.

The bulk of the gain comes from fixing a game_phase() inversion that had silently disabled king-safety evaluation in the middlegame since version 1.1 -- the fix in isolation measured +224 Elo at 10+0.1 self-play. The rest of the release adds search refinements (LMP, SEE-aware ordering, IIR, countermove, razoring, TT aging), evaluation refinements (knight outpost two-tier, mopup guard extended to KNN and KBB same-color), and infrastructure improvements (bench rebalanced and deepened, eval debug command, CLI bench mode, observability output).


Search

  • Late Move Pruning (LMP): skip late quiet moves at shallow depths in non-PV nodes when enough alternatives have already been searched without raising alpha.
  • SEE-aware capture ordering: captures split into GOOD (SEE >= 0, before quiets) and BAD (SEE < 0, after quiets) tiers. Replaces the single MVV-LVA tier from 1.4.
  • Internal Iterative Reductions (IIR): non-PV nodes without a TT move at sufficient depth are reduced by 1 ply before searching; the next visit benefits from move ordering.
  • Countermove heuristic: third quiet-move ordering tier after killers; remembers the refutation move per opponent (piece, to-square) pair.
  • Razoring: at depth 1-2 in non-PV nodes, drop into qsearch if eval + 250cp <= alpha.

Evaluation

  • game_phase() inversion fix (carried over from 1.1): the function had been returning the inverse of its documented contract, silently zeroing king-safety in the middlegame and using the endgame king PST from move 1. +224 Elo measured in isolation. Single largest source of strength gain in 1.5.
  • Knight outpost refactor: previous outpost detection required only that the square not be attackable by enemy pawns. Refactored to two tiers: KNIGHT_OUTPOST_REACHABLE (10cp, no friendly pawn support) and KNIGHT_OUTPOST_SUPPORTED (25cp, supported by a friendly pawn).
  • Knight outpost forward_mask fix: the disqualifying forward mask incorrectly included the knight's own rank, causing false-negatives when an enemy pawn sat on the same rank in an adjacent file. Pawns capture diagonally forward, so such pawns cannot attack the knight. Mask now strictly ahead.
  • Mopup insufficient material guard extended: now covers K+N+N vs K and K+B+B same-color vs K, in addition to the K+B vs K and K+N vs K cases from 1.4. K+B+B with opposite-colored bishops continues to trigger mopup correctly.

Transposition Table

  • TT aging: global generation counter packed into the entry's bound byte. Replacement compares (age_in_generations * 4) + (stored_depth - new_depth). Smarter eviction keeps the TT relevant across moves without losing meaningful old data. Entry size unchanged at 16 bytes.

Infrastructure

  • bench default depth raised from 15 to 18: more meaningful per-position measurements. Bench signature: 299,881,540 nodes at depth 18 (deterministic, used as no-regression check).
  • bench position rebalancing: 6 of 10 positions replaced for better time distribution. Per-position share went from 0.01%-38.85% (1.4 set) to 2.7%-16.7% (much more uniform). Each position now carries a label describing the search feature it stresses.
  • bench verbose flag: when present, full search output is emitted. Default (quiet) mode prints only per-position summary and total.
  • CLI bench mode: the binary can now be invoked as ./facon-1.5 bench [verbose] [depth N] (or facon-1.5.exe bench ... on Windows) to run the benchmark once and exit, without UCI handshake. Useful for automation.
  • eval UCI command: new debug command that prints a per-component breakdown of the static evaluation for the current position. Calls evaluate_verbose() which reproduces evaluate() exactly but accumulates each term separately.
  • Final summary on aborted iteration: when the search is interrupted mid-iteration, the engine now emits a final UCI info depth line and a human-readable info string summary right before bestmove, capturing total nodes / time / nps that would otherwise be lost between the last heartbeat and the bestmove.
  • currmove output suppressed for first 2 seconds: UCI info currmove lines from the root are gated by elapsed time. At shallow depths each move at the root completes in well under a millisecond, producing hundreds of currmove lines per second; suppressing this volume reduces stdout pressure on GUI pipe readers without losing useful information.

Build

  • MSVC support removed from CMakeLists.txt: the previous MSVC branch was non-functional because the source uses GCC/Clang builtins directly without wrappers. CMake now rejects MSVC at configure time with FATAL_ERROR. Native Windows builds should use MinGW-w64.
  • -fomit-frame-pointer added to Release builds: frees %rbp as a general-purpose register in the hot search/eval paths. Measured ~+2.3% NPS on Ryzen 7 1700 (Zen 1).

Known limitations

  • Drawn pawnless endings evaluated as material: KB vs K, KN vs K, KNN vs K, and KBB same-color vs K are theoretically drawn but evaluate() returns the raw material count (~+330 to ~+660 cp) rather than 0. An override forcing 0 was attempted during 1.5 development but reverted after extended gauntlet testing measured ~30 Elo regression -- the override was technically correct but demotivated the engine from reaching positions whose drawn final endings propagated back through search. Proper resolution requires material-signature endgame recognition or tablebase probing, planned for a future version.

Checksums (SHA1)

f7627bc8d319610b9d607f8349b955201ca152b0  facon-1.5
705aa911390d15bb819d9b792620cf3456bb6c5e  facon-1.5.exe