VeryChess 0.9.0

 ·  Release

Download

Overview

This release is about search quality and cost. Two changes add strength outright — aspiration windows (+27.9 Elo) and a transposition table in quiescence search (+9.5 Elo) — while three more make the engine do substantially less work for the same playing strength: staged move generation halves the moves generated per node, a negative extension cuts the tree by a further 16%, and a large internal cleanup drops per-thread memory by 92%. The engine also gains exact endgame knowledge (a KPK bitbase and a KBNvK mating technique) that needs no external files.

Strength

In the roundrobin tournament with older versions VeryChess 0.9.0 shows significant increasing strength:

namegameswinsdrawslossesscoreelo
1VeryChess 0.9.0200121745158.0325
2VeryChess 0.8.0200749333120.5217
3VeryChess 0.7.020053856295.5153
4VeryChess 0.6.020039867582.0115
5VeryChess 0.5.0200146012644.00

What's new

Aspiration windows

Until now every root iteration was searched with the full (-inf, +inf) window — the engine had never had this mechanism at all. Iteration d is now searched in a ±25 cp window centred on the score of the last completed iteration, widening ~1.5× on each fail-low or fail-high and falling back to the full window after three failures or as soon as the score reaches mate range. On 150 opening positions the engine reaches a fixed depth with ~12% fewer nodes.

Transposition table in quiescence search

Quiescence search is 57% of all nodes and never consulted the transposition table, so millions of positions were re-derived from scratch, stand-pat evaluation included. It now probes and stores in the shared table, with entries deliberately marked shallower than any main-search entry so they cannot masquerade as a deep result, and stored as bounds only (a quiescence value depends on the window it was searched with). Fixed along the way: a quiescence store could overwrite a deep main-search entry for the same position, because the table's "same key → refresh" path ran before its depth guard.

Staged move generation

The engine used to generate and score every legal move at a node before searching any of them. Measurement showed what that cost: 37 moves generated per node, of which only 3.5 were ever searched, and two thirds of beta cutoffs happened before a single quiet move was tried. Moves are now produced lazily in stages — transposition-table move, good captures, promotions, killers, quiet moves, losing captures — so 54% of nodes never generate quiet moves at all and the moves generated per node fall from 36.8 to 18.0. The engine reaches a fixed depth 10% faster.

Singular extensions: the second verdict

When the engine tests whether the transposition-table move is the only good move, that test produces two answers, and only one of them was ever used. If the test shows the move is not singular — there is at least one other move just as good — the engine now searches it one ply shallower in nodes where a cutoff is expected anyway. That removes 16% of all nodes and reaches a fixed depth 17.6% faster.

Embedded endgame knowledge

Exact answers where they can be had without external files: an exhaustively generated KPK bitbase (24 KB, ~11 ms at startup, verified against an independent reference over all 165,676 legal positions), a KBNvK mating technique that drives the weak king to a corner of the bishop's colour — 0.8.0 could not convert this ending at all, while 0.9.0 mates in 71 and 59 plies on the two test positions — and extended endgame scaling keyed on material counts rather than tuned thresholds. This package is strength-neutral by design: it trades no Elo, it removes wrong answers.

Memory

Per-thread memory drops from 771.9 KB to 56.9 KB. Nine search mechanisms that had been implemented, measured as neutral-to-negative and left disabled were removed outright, together with the tables they reserved — 715 KB per thread that was allocated and zeroed on every search. At `Threads=256` the engine's resident memory falls from 335 MB to 155 MB.

Separately, every UCI session used to allocate the hash table twice at startup, peaking at twice the configured `Hash` (131 MB instead of 67 MB with the default setting, and over 1 GB for a GUI configured with `Hash=1024`). Fixed.

Multi-threading: measured, not changed

The long-deferred scaling report was finally done. On a 4+4-core Apple M2, node throughput scales 3.83× at four threads but time-to-depth only 1.71×, and eight threads are no better than four — partly because a depth-limited search finishes when the main thread finishes, so an operating system that parks that thread on an efficiency core slows the whole search regardless of how many helpers are running. Practical advice: do not set `Threads` higher than the number of performance cores.

Under the hood

`Board::isPseudoLegal` was added so the transposition-table move can be played before any move generation happens; it is verified exhaustively against the generator (all 65,536 move encodings on 400 positions), and that test immediately found two unused move-encoding flags being accepted as captures.

VeryChess 0.8.0

 ·  Release

Download

Overview

This release makes the engine both stronger and — by a wide margin — faster. Three changes each add roughly twenty to thirty Elo: a clustered transposition table, internal iterative reduction, and a fix to how pinned pieces are evaluated. Separately, the search itself now runs about 2.7× faster than 0.7.0 did, mostly because sliding-piece attacks are now magic bitboards instead of a step-by-step ray walk, and because the macOS build was discovered to have been running emulated. Windows users now get two binaries to choose from.

Strength

In the roundrobin tournament with older versions VeryChess 0.8.0 shows significant increasing strength:

namegameswinsdrawslossesscoreelo
1VeryChess 0.8.0150726216103.0236
2VeryChess 0.7.015057613287.5187
3VeryChess 0.6.015038694372.5141
4VeryChess 0.5.015020349637.00

What's new

Clustered transposition table

The transposition table used to hold one entry per index, so two positions that hashed to the same slot simply evicted each other. Instrumentation showed the cost plainly: a 12.5% hit rate, with 32.5% of all stores overwriting a different position. Each index now addresses a cluster of four entries occupying exactly one cache line, so probing all four costs no extra memory latency, and a deep, valuable entry is no longer thrown away by a shallow one that happened to collide with it.

This one is worth a note for anyone comparing engines at fast time controls: the same change measures nothing at all (+2.9 ± 18.3) at 30+1. A larger table only helps once the search actually fills it, and blitz searches never get there. The gain appears as the time control lengthens.

Internal iterative reduction

When the transposition table holds no move for a node, the move ordering at that node is poor, and a full-depth search is largely wasted effort. The engine now searches such nodes one ply shallower, which is a cheaper way to obtain a good move for the table; later visits and re-searches then benefit from the improved ordering.

Pinned pieces no longer get imaginary mobility

Mobility evaluation counted moves that a pinned piece cannot legally make. A knight pinned against its own king was credited with all eight of its jumps, although every one of them would expose the king; a pinned bishop was credited with both diagonals rather than the one it is confined to. The engine now computes absolute pins and restricts a pinned piece's mobility to the squares along the pinning ray.

Sliding attacks: magic bitboards

Rook and bishop attacks were computed by walking each ray one square at a time, on every single call — which profiling showed to be 45% of all CPU time. They are now a single multiply-and-shift table lookup ("magic bitboards"), 26.7× faster in isolation and worth +87.8% to whole-engine speed. The attack tables are built at startup by the same ray-walking code they replace, so their contents are identical to the old implementation by construction.

macOS: the build was running emulated

On Apple Silicon the build was silently producing an x86-64 binary running under Rosetta, because the toolchain in that environment misreports the machine. Detection now goes through a Rosetta-proof check, and macOS ARM64 users get a native binary — +29% speed on its own. The release also uses profile-guided optimization (a further +11.6%), and a new pawn-structure cache (+5.5%).

Windows: two binaries

The Windows package now contains two builds. `verychess.exe` is the compatible one and runs on any x86-64 processor from roughly 2009 onwards — use it if unsure. `verychess-modern.exe` is about 3% faster but requires BMI1/BMI2, meaning Intel Haswell (2013) or AMD Zen (2017) and newer; if it refuses to start, the processor is too old. Both play identically and differ only in speed. Both are now built with profile-guided optimization, worth +7.8% and +11.1% respectively, measured on real hardware rather than under emulation.

Stability: clean shutdown when the GUI closes the connection

A crash is fixed. The engine stopped its search when it received `quit`, but not when its input stream was simply closed — which is what happens if a GUI disconnects or kills the engine instead of sending `quit`, and what happens whenever the engine is driven by a shell pipe. In that case the process began shutting down while its search threads were still running and still reading the transposition table, which had already been freed. On Linux this reliably crashed the engine; on macOS the same defect was present but usually went unnoticed because of timing. The search is now stopped and joined on both shutdown paths.

VeryChess 0.7.0

 ·  Release

Download

Overview

This is primarily a search release. Its headline is singular extensions: the engine detects when the transposition-table move is clearly better than every alternative and searches that move one ply deeper, sharpening tactics and forced lines. It also retunes king safety for a small further gain, fixes the evaluation of a few known-drawn endgames, adds a go nodes search limit, and — importantly — fixes a search-robustness bug where some positions could ignore the move-time limit and never return a move.

Strength

About +34 Elo at a long time control.

What's new

Singular extensions

When the transposition table already holds a good move for a position, the engine runs a quick verification search of that node excluding the stored move, using a narrow window a little below its score. If every other move fails to reach that window — the stored move stands alone as the only good option — the move is judged singular and searched one ply deeper. This spends extra depth exactly where it matters: forced tactical sequences, single-defence positions, and critical endgame moves, where the nominal depth understates the real tactical depth. Measured at +34.3 ± 18.4 Elo at 120+2 versus the 0.6.0 search baseline.

Endgame scaling fixes

The endgame-scaling evaluation now recognises more dead-drawn material: two knights versus a lone king (KNNvK) is scored as a draw, and lone-minor endings (king and one knight or bishop versus a bare king) are again scored as draws after a 0.6.0 tuned-value change had briefly made a lone knight read as a winning edge.

King safety retune

King safety was left frozen during the 0.6 evaluation tuning. Its linearisable weights — the danger table indexed by the number of attackers, and the open-file penalty near a castled king — were now unfrozen and re-tuned with the same Texel pipeline. The tuner raised them: they had under-valued king danger. Worth +7.2 Elo in fixed-nodes self-play, and it is search-friendly — the sharper evaluation prunes a little more (~14% fewer nodes to a fixed depth).

Time limits are always honoured (search robustness)

A real robustness bug is fixed. On some positions with long forcing check sequences, the check extension can hold the search at the same nominal depth, so the search ply grows without bound; the quiescence search underneath had no depth cap and did not poll the clock, so it could spin without returning — the engine ignored its move-time and never moved (a lost-on-time in a real game). The search now has a hard ply cap in both the main and quiescence search, and the main thread emits a periodic info heartbeat during a timed search, so the clock is always honoured and long thinks stay visible to the GUI.

`go nodes` search limit

The engine now accepts `go nodes N`, stopping the search after N nodes. This gives a reproducible, speed-independent way to compare search or evaluation changes.

VeryChess 0.6.0

 ·  Release

Download

Overview

This is an evaluation and search-quality release.

The entire hand-crafted evaluation was automatically tuned with a Texel-style optimizer built for this release, and two pawn-evaluation terms were re-introduced as jointly-tuned features. Also it adds two standard selective-pruning techniques to the main search.

Strength

Together these are worth roughly +300 Elo in self-play at fast time control.

What's new

Automatic evaluation tuning (Texel)

Until now every evaluation weight — piece values, piece-square tables, mobility, bishop pair, rook bonuses — was set by hand. This release adds a complete, self-contained tuning pipeline and uses it to fit all of those weights to game results:

  • Self-play data generation: the engine plays itself from randomised openings at a fixed node budget, adjudicates the result, and writes out quiet, non-tactical positions labelled with the game outcome.
  • Feature export: for any position the engine emits the exact linear coefficient of every tunable weight, so an external optimizer can reconstruct the evaluation without running the engine.
  • Optimizer: a small NumPy Adam optimizer minimises the Texel error (game result vs. a sigmoid of the evaluation) over ~4 million positions, with a gauge-normalisation step that keeps the tables well-conditioned, and regenerates the C++ weight tables.

Passed pawns and pawn structure

With the tuning pipeline in place, two evaluation terms that were previously neutral as hand-weighted values were re-added as jointly-tuned features and kept: passed pawns (a bonus that grows as the pawn advances) and pawn structure (penalties for isolated and doubled pawns, a bonus for connected pawns).

Reverse futility pruning

In a shallow, non-principal-variation node where the side to move is not in check, if the static evaluation already exceeds the search window by a depth-scaled margin — so much that even conceding that margin still leaves the position winning — the node returns immediately without searching any moves. The margin grows with depth, so deep nodes only prune when the position is clearly won, while shallow nodes prune more readily.

Late move pruning

At shallow depth, once enough moves have already been examined in a node, the remaining late, quiet, non-checking moves are skipped entirely rather than searched at reduced depth. Because moves are ordered best-first, the moves reached late are low-priority quiet moves that rarely change the result — but skipping them cuts the search tree by roughly two thirds at fixed depth, which buys substantially deeper search under real time controls. Checks, captures, and promotions are never pruned this way.

VeryChess 0.4.0

 ·  Release

Download

Overview

This release covers two areas: a sharper quiescence search and a rebuilt static evaluation.

First, the quiescence search — the tactical search that runs at the leaves of the main search to resolve pending captures before the position is evaluated — was responsible for roughly 87% of all nodes searched, much of it spent on hopeless captures (a queen taking a defended pawn, a rook grabbing a piece and being lost immediately). Two standard filters now curb that waste, and a proper static exchange evaluation (SEE) underpins them.

Second, the hand-crafted evaluation was rebuilt from a flat, hard-switched function into a tapered, component-based one — smoothly interpolated between middlegame and endgame by game phase — and gained full-board mobility, rook file evaluation, and drawn-endgame scaling. Measured against the pre-rework build, the evaluation work alone is worth about +118 Elo at fast time control.

Strength

The whole evaluation rework was validated as an SPRT-gated series at 30s + 1s (search depth matters little for evaluation, so a fast control is appropriate), each stage against the one before it. Measured end-to-end against the pre-rework build, the cumulative result is +117.9 ± 31.2 Elo (LOS 100%, 372 games). As always this is self-play at a fast control and overestimates the gain against other engines and at longer controls; the individual accepted stages were tapered eval +80, full mobility +92, rook files +34, and endgame scaling +18. Move generation is unchanged (perft 23/23), and the evaluation symmetry tests pass.

What's new

Delta pruning in quiescence

A capture is skipped when, even in the most optimistic case — winning the captured piece for free plus a safety margin — the result still cannot reach the best score found so far. Promotions and positions where the side to move is in check are never pruned. This is exact at fixed depth (it only discards moves that provably cannot help) and removes about a quarter of quiescence nodes.

Static exchange evaluation (SEE)

A new SEE module evaluates the material outcome of a sequence of captures on one square without a full search — modelling each side recapturing with its least valuable attacker, including x-ray attackers revealed behind a moved piece, and en passant. It is used two ways: to skip materially losing captures in quiescence, and to order clearly losing captures after quiet moves in the main search. Together with delta pruning this cuts quiescence nodes by roughly 45%.

Tapered, component-based evaluation

The evaluation was previously flat — one set of piece-square tables, with a hard boolean switch to a handful of endgame terms once material dropped below a threshold. That switch caused an unnatural jump in the score across a single exchange. It has been rebuilt as a tapered evaluation: every component produces a middlegame and an endgame value, and the final score is interpolated between them by a game-phase measure (weighted by remaining non-pawn material). The endgame now blends in smoothly instead of snapping on.

On top of that framework, three positional terms were added:

  • Full mobility. Previously only knights had a mobility term. Now knights, bishops, rooks, and queens are all scored by how many safe squares they can reach — excluding squares occupied by their own pieces and squares attacked by enemy pawns, and counting sliders through their real blockers. This was the single largest gain of the rework.
  • Rooks on open files. A rook on a file with no pawns, or with no friendly pawns, now gets a bonus (larger for a fully open file, and larger in the middlegame).
  • Endgame scaling. Clearly drawn material configurations are scaled toward a draw so the engine stops playing for a win it cannot achieve: opposite-colored bishops with no other pieces are halved, and a pawnless side with no more than a minor-piece edge (K+B vs K, K+N vs K, and similar) is pulled close to zero.

VeryChess 0.3.0

 ·  Release

Download

Overview

This release covers two areas: search efficiency and time management.

The late move reduction (LMR) scheme — the heuristic that decides how much cheaper to search moves that are unlikely to be best — has been rebuilt from a crude fixed cap into a proper depth- and move-number-scaled formula, with history and principal-variation awareness. The engine now reaches noticeably greater depth in the same amount of time.

The second area is how the engine spends its clock. Previously it burned most of its time in the opening and middlegame and was left playing on the increment by around move 25–30 — occasionally losing games on time outright. Time allocation has been reworked so the clock now lasts the whole game, and the previously dead `Move Overhead` option is now actually honored.

There are no changes to board representation, move generation, or evaluation in this release.

Strength

Every change to the reduction scheme was gated by a sequential probability ratio test (SPRT) against the build immediately preceding it, played at 10s + 0.1s and 120s + 2s with paired openings, one thread, and a 64 MB hash. Measured directly against 0.2.0.

Approximately strength improvement 46 ± 5 ELO.

What's new

Rebuilt late move reductions

Previously, late quiet moves were reduced by a flat 1–2 plies regardless of how deep the search was or how late the move appeared. That cap wasted most of the available savings at higher depths. It has been replaced with a precomputed reduction table indexed by depth and move number. The reduction now grows smoothly with both depth and move number — reaching 4–6 plies deep in the tree, where the savings compound — instead of stopping at 2.

Two refinements sit on top of the table:

  • History-aware reductions. Quiet moves with a strong history score are reduced one ply less. Measurement showed these moves are systematically undervalued by reduction: moves that had to be re-searched carried a median history score roughly two orders of magnitude above a typical reduced move.
  • Principal-variation reductions. LMR now applies inside PV nodes as well, but cautiously: reductions there are one ply smaller than in non-PV nodes, and the first five legal moves of a PV node are never reduced at all. The first move of every PV node is still searched at full depth in a full window.

Reworked time management

The engine was exhausting its clock far too early. The root cause was that the "soft" time limit was never actually a spending cap — it only decided whether to start another search iteration. Because each iteration roughly doubles the time of the previous one, the real spend per move came out at one-and-a-half to four times the intended budget, draining the clock by the middlegame.

  • Iteration-completion prediction. The engine now starts a new deepening iteration only when it predicts it can finish it within the budget, rather than starting one whenever any time remains. This is the core fix: it turns the soft limit into a real per-move budget. On its own it eliminated the clock collapse — median time remaining at the engine's 40th move rose from under a second to over a minute (of a three-minute clock).
  • More realistic game-length assumption. When the GUI does not say how many moves remain until the next time control, the engine now assumes 50 rather than 30, matching measured game lengths and making early moves less expensive.
  • "Move Overhead" is now functional. The option was previously declared but never applied. The engine now subtracts the configured reserve (default 10 ms) from its available time so a move reliably reaches the GUI or arbiter before the flag falls.

A threading robustness fix

A latent race in the worker-thread startup could, in rare timing, cause the engine to accept a "go" command and then never reply — no "info", no "bestmove" — while the UCI loop itself stayed responsive. It required a "go" to arrive within microseconds of the engine process starting, so ordinary GUIs (which perform a full handshake first) never triggered it, but automated tooling that streams commands in one burst could. The worker now finishes parking before the constructor returns, closing the window.

Full principal variation in "info" output

"info" lines now report the complete principal variation rather than only the best move. The line is reconstructed from the transposition table between iterations on the main thread, off the search hot path, so it costs no search speed. Occasionally the reported line is shorter than the search depth, when transposition table entries have been overwritten — this is cosmetic and does not affect play.

VeryChess 0.2.0

 ·  Release

Download

VeryChess 0.2.0 introduces multi-threaded search based on the Lazy SMP approach, with support for 1 to 256 search threads and a shared lock-free transposition table.

Each worker thread performs its own iterative deepening search from the same root position. Threads cooperate exclusively through the shared transposition table, without split points or locking in performance-critical search paths.

Every worker maintains its own board copy, history table, and node counters. To improve search diversity, helper threads selectively skip certain depths and explore the position using slightly different search trajectories.

Only the main thread is responsible for time management and UCI output, including info and bestmove. The final move is selected through a voting mechanism across all worker threads. Priority is given to the move from the deepest completed iteration, with the evaluation score used as a tie-breaker.

Internal testing indicates that VeryChess 0.2.0 running with four threads is approximately 100 Elo stronger than VeryChess 0.1.0.

VeryChess 0.1.0

 ·  Beta

Initial public development release of VeryChess. This version establishes the core engine architecture, UCI compatibility, and a basic but functional search and evaluation. Strength is experimental and will improve in future versions.

VeryChess is a UCI chess engine written in C++20, targeting native compiled binaries for macOS (ARM64 primary), Linux, and Windows. It's a classical alpha-beta engine with hand-crafted evaluation — no neural networks, no third-party dependencies, standard library only.

Architecture / Board representation

  • Bitboards - one 64-bit integer per [color][piece-type], plus per-color and total occupancy.
  • Incremental Zobrist hashing updated on every make/unmake.
  • Compact 32-bit move encoding: from | to<<6 | flag<<12, with flags for quiet/capture/double-push/en passant/castling/promotion.
  • Attack tables precomputed at startup: knight/king/pawn lookups; sliding pieces (rook/bishop/queen) use a classical fill algorithm over rank/file/diagonal masks — not magic bitboards.

Move generation

  • Pseudo-legal generation with separate capture-only generation for quiescence.
  • Legality enforced lazily: moves are made, then rejected if they leave the own king in check.
  • Validated via perft — exposed both as a CLI subcommand and a UCI debug command, with Kiwipete and other standard positions in the bench set.

Search algorithms

  • PVS (Principal Variation Search): full window on first move, null-window scout on the rest.
  • Transposition table: probe for cutoffs (non-PV), best-move ordering; depth+age replacement.
  • Null-move pruning: R=2–3, disabled in PV / in check / zugzwang-risk (no non-pawn material).
  • Late move reductions: reduces late quiet moves, re-searches if they beat alpha.
  • Quiescence search: captures-only with stand-pat, avoids the horizon effect.
  • Check extensions: +1 ply when a move gives check.
  • Mate-distance pruning: tightens the alpha/beta window.
  • Move ordering: TT move → MVV-LVA captures → 2 killer moves → history heuristic.
  • Draw detection in search: 50-move rule + repetition via the hash history.

Evaluation

Hand-crafted, integer (centipawn), symmetric, returned from the side-to-move's perspective (eval.cpp):

  • Material + piece-square tables.
  • Phase awareness — separate king PST for middlegame vs endgame, switched on non-pawn material; endgame adds king-centralization and a king-driving term for K+piece vs K mates.
  • Bishop pair bonus, simple knight mobility.
  • King safety — open-file penalties near a castled king plus a non-linear attacker-weight table over inner/outer king zones.

UCI / functionality

  • Commands: uci, isready, ucinewgame, position (startpos/fen + moves), go (depth/movetime/wtime-btime-inc/movestogo/infinite), stop, quit, plus debug perft.
  • Time management with soft/hard limits derived from the clock; time checked every 2048 nodes for low overhead.
  • Hash option: configurable TT size, default 64 MB, 1 MB–64 GB.
  • Standard UCI info output (depth, seldepth, score cp/mate, nodes, nps, time, pv).

Characteristics summary

  • Style: classical alpha-beta engine, correctness-first, performance-conscious.
  • Strengths: complete modern search-pruning toolkit, clean cache-friendly bitboard design, zero dependencies, fully cross-platform, interruptible threaded search.
  • Current limitations / growth areas: single-threaded search (no SMP), classical (non-magic) sliding attacks, untapered hand-crafted eval, no opening book or endgame tablebases, no NNUE.