Martuni Part 2 — Evaluation, Pawn Structure, Endgames
If a chess engine only counts material, it plays like someone who learned chess three weeks ago. It knows a queen is worth more than a knight. But it doesn’t know that a knight is better placed in the center than on the edge. It doesn’t know the king needs to become active in the endgame. It doesn’t know that a passed pawn on the seventh rank decides the game.
On April 11th, I started changing that. It was a long day.
Piece-Square Tables: where pieces should stand
The concept is simple: every piece gets a bonus or penalty for every square. A knight on e4 is worth more than a knight on a1. A bishop on the long diagonal draws more strength from its position than one walled in behind its own pawns.
These tables — Piece-Square Tables, or PSTs — are the simplest form of positional knowledge. I wrote them by hand. Every number is a decision: how much do I value knight centralization? How harshly do I penalize a king standing in the middle during the middlegame?
On top of that comes Tapered Eval: the same position is evaluated differently depending on whether we’re in the middlegame or endgame. The king, for instance — in the middlegame it should hide, in the endgame it should fight. For each position I calculate a “phase” between 0 (full middlegame) and 1 (endgame), then interpolate between the middlegame and endgame PSTs.
This is small chess-computing craftsmanship — but once integrated, the engine started placing pieces on more sensible squares. It liked the center. Knights wanted to go to e4 and d5. The feel of the play changed.
King Safety: the king is not a pawn
The next step was king safety. This is the most complex part of the evaluation function I’ve built so far.
The idea: a 3×3 zone is laid around the king. Every enemy piece attacking this zone increments a danger counter. Different pieces are weighted differently — an enemy queen bearing down on the king’s position is more threatening than a distant rook. This counter feeds into a Safety Table, which translates it non-linearly into centipawn penalties: a little pressure is barely a penalty, a lot of pressure is catastrophic.
Added to that is Pawn Shield: a king sheltered behind a row of its own pawns (classically: kingside castling with pawns on f2/g2/h2) receives a bonus. Missing a pawn from that shield — penalized.
The first time I tested the new evaluation function, the engine was hesitant about early king attacks from the opponent. It castled. Reliably. That feels like real chess logic.
Endgame Technique by Hand
Endgames are the area where engines without specialized knowledge often fail. Without tablebases (massive databases of perfect endgame results), an engine has to have the knowledge built in.
I built it in. In three phases.
Phase A — Mop-up endgames: King and rook versus king, king and queen versus king. Every engine should be able to win these. But without guidance, an engine circles endlessly because checkmate is still 30 moves away. I added two evaluation terms: the weaker king should be driven into a corner, and the stronger king should come close. That’s enough — the search then finds the path on its own.
Phase B — King and pawn versus king: The Rule of the Square. If a passed pawn is running and the opposing king is outside the “square” formed by the pawn’s distance to the promotion rank, the pawn can no longer be caught. This is chess knowledge every amateur player knows. Now Martuni knows it too.
Phase C — King, bishop, and knight versus king: The dreaded KBNK endgame. Winnable if played correctly, but even strong players often stumble. The trick: checkmate is only possible in the corner matching the color of the bishop. A light-squared bishop can only checkmate in a8 or h1, not in a1 or h8. So there’s a dedicated corner gradient that actively steers the weaker king into the right corner.
I was surprised how much fun it was to implement these things. It isn’t magic — it’s chess knowledge, formalized as code.
Ponder: thinking while the opponent thinks
Later that same day: Ponder support. This means the engine thinks about its expected reply move while the opponent is still thinking. If the opponent plays the expected move (ponderhit), the engine already has a result — it’s effectively had twice the time.
The UCI protocol for this isn’t trivial. There’s a go ponder command, a ponderhit command, a stop command. The engine has to respond correctly in each of these states. I implemented it carefully — it was the first moment where Martuni felt like a real engine, with genuine protocol features.
Lichess
At this point Martuni had enough substance to play on Lichess. As BOT Martuni, it now plays real games automatically, via a systemd service running around the clock.
It was a strange moment: I started the engine, the first game came in — and I watched. Live. My engine against some stranger’s bot.
It played decently. Solid opening thanks to the book, sensible middlegame. And then — in the endgame — it missed a pawn it should have taken.
I cursed internally. Even though I knew it was still young.
Continued in Part 3: Blunder analysis, SEE, and the search for deeper vision.