Since you’re here...

We hope you will consider supporting us today. We need your support to continue to exist, because good entries are more and more work time. Every reader contribution, however big or small, is so valuable. Support "Chess Engines Diary" even a small amount– and it only takes a minute. Thank you.
============================== My email: jotes@go2.pl



Facón 1.5 Espiga, new version chess engine


Facón is a chess engine built from scratch in C++17, designed as a learning project and long-term development platform. The name comes from the facón — a traditional Argentine gaucho knife, forged by hand, raw and functional.
Each version carries a codename that follows the knife-making process: from rough rusty iron to a sharp, precise blade.
Author: Carlos M. Canavessi

Facón 1.5 - what's new?

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.



Comments