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;
}