# VeryChess 0.4.0

**Released:** 4 August 2026
**Previous release:** 0.3.0

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.

There are no changes to board representation, move generation, time management, or the UCI interface in this release.

## 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.

The king-safety term, which used to switch off abruptly in the endgame, now fades out smoothly with the phase instead. A new `evaltrace "<FEN>"` command prints the per-component middlegame/endgame breakdown for a position, and a set of symmetry unit tests (`tests/eval_tests.cpp`) guards the evaluation against sign and mirror errors.

Several further terms were implemented, tested, and **rejected** because they did not help — a tempo bonus and hanging-piece threats both *hurt* (they duplicate what the search already sees and distort its pruning margins), while passed pawns and basic pawn structure came out neutral-to-marginal and were dropped to keep the evaluation simple. Each rejection is recorded in the git history with its numbers so it is not re-attempted blindly. The full kept-vs-rejected table and the design rationale live in `Docs/architecture/evaluation.md`.

**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.

---

# VeryChess 0.3.0

**Released:** 29 July 2026
**Previous release:** 0.2.0

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.

---

## 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.

Move searching was restructured into a three-level re-search pattern — reduced null-window, then full-depth null-window, then a full-window search in PV nodes — so that a reduced move which looks promising is re-verified cheaply before the engine pays for an exact score. Behavior in non-PV nodes is unchanged by the PV work.

### 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.

Three changes fix this:

- **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. The reserve is deducted once from the remaining time, not per move, so even a large setting costs almost no thinking depth.

### 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.

### New `benchsearch` command

A new subcommand, available both from the command line and as a UCI debug command: "verychess benchsearch 11"

It runs a deterministic, single-threaded, fixed-depth search over a fixed set of positions, clearing the transposition table before each one, and prints `bestmove`, `score`, `nodes`, `qnodes`, `time`, `nps`, and `pv` per position plus a totals line. Because it is fully deterministic, its `bestmove + score + nodes` signature is a cheap way to compare two builds of the engine and catch unintended changes in search behavior.

This is distinct from the existing `bench` command, which measures perft (move generation) speed only.

---

## 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.


---

## Downloads and license

Native binaries are provided for macOS (Apple Silicon and Intel), Linux x86_64,
and Windows x86_64. No installer, no runtime dependencies — a single executable.

VeryChess is **freeware**: free to use and free to share unmodified, but it is
not open source. See `LICENSE.txt` in the release package for the binding terms,
and `readme.txt` for a user-facing summary.

Website: https://verychess.org · Contact: terrytet.chess@gmail.com
