From 0ac407d5165f9ce197cb13196ce77a05448f4b55 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Sat, 11 Apr 2026 22:52:15 -0700 Subject: [PATCH] [1d-chess] Make AI force a 6-move mate instead of a 5-move I saw this on HN and after flailing around trying to win it, got annoyed and wrote (by hand, out of some potentially misplaced pride) a solver to find the winning line, and discovered that the AI's response was suboptimal and led to a 5-move mate instead of the 6-move one the searcher found. I looked at the code and noticed that the search didn't include depth in the score, and so wouldn't try to prolong the game as long as possible. The game is saved from being trivial by its move-ordering: it searches captures before other moves, and prefers the last "optimal" move it finds. If it searched captures last, then it would respond to N3 with R3, followed by a mate in 2 more turns.. Fixes: - Include the depth in the computed score, so that black tries to prolong the game even in a losing position. - Remove early-returns and state dependent code that got in the way of maximizing length. - Fix the beta calculation for alpha-beta pruning. Not sure if this matters at all. Code changes are from Opus 4.6 but I've reviewed them and think they are legit. This message actually written by a human. --- 1dchess/chess.html | 2 +- 1dchess/js/chess-ai.js | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/1dchess/chess.html b/1dchess/chess.html index 1bf4fbd..68b3e53 100644 --- a/1dchess/chess.html +++ b/1dchess/chess.html @@ -70,7 +70,7 @@

1D-Chess

Play as white against the AI. You might initally find it more difficult than expected, but assming optimal play, is there a forced win for white?

- Mouse over to reveal answer: Try this line: N4 N5, N6 K7, R4 K6, R2 K7, R5++ + Mouse over to reveal answer: Try this line: N4 N5, N6 K7, R4 N3, K2 N5, N8 K8, R5++

Rules

diff --git a/1dchess/js/chess-ai.js b/1dchess/js/chess-ai.js index 1e405c8..0c4b965 100644 --- a/1dchess/js/chess-ai.js +++ b/1dchess/js/chess-ai.js @@ -62,15 +62,15 @@ function minimax(gameState, a=-1, b=1, depth=0) { switch (gameState["gameResult"]["winner"]){ case "white": //console.log("white win"); - return 1; + return 1 - depth * 0.001; case "black": //console.log("black win"); - return -1 + return -1 + depth * 0.001; case "draw": return 0; } } - if (canClaimDraw(gameState["pieceList"]) && gameState["bestMove"] != "None") { + if (canClaimDraw(gameState["pieceList"]) && depth > 0) { // console.log("1 knight draw"); return 0; } @@ -121,12 +121,8 @@ function minimax(gameState, a=-1, b=1, depth=0) { childGameState["depth"] = gameState["depth"] + 1; let childValue = minimax(childGameState, a, b, depth+1); - if(childValue == 1) { - gameState["bestMove"] = movePair; - return childValue - } - value = Math.max(value, childValue); - if (value == childValue) { + if (childValue > value) { + value = childValue; gameState["bestMove"] = movePair; } a = Math.max(a, value); @@ -173,15 +169,11 @@ function minimax(gameState, a=-1, b=1, depth=0) { childGameState["turn"] = otherColor(childGameState["turn"]); let childValue = minimax(childGameState, a, b, depth+1); - if(childValue == -1) { - gameState["bestMove"] = movePair; - return childValue - } - value = Math.min(value, childValue); - if (value == childValue) { + if (childValue < value) { + value = childValue; gameState["bestMove"] = movePair; } - b = Math.max(b, value); + b = Math.min(b, value); if (b <= a){ break; }