FAILURE MAP
← Case archive

FA-82671 / Chess move legality / Open access

Blocked rook counted as a competitor · case 01

Rh1-d1 with the a1 rook shielded by a bishop on c1 is written "Rhd1".

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Competitor reachability ignores pieces standing on the path.

VERIFIED REPAIR

A slider competes only if every square between it and the target is empty.

Unsuccessful approach: Checking only the first step still misses blockers further along the path.

Case contract

Input: {"board", "from", "to"} for a knight, bishop, rook, queen or king move. Return SAN (no check suffix): upper-case piece letter, then a disambiguator only if another same-colour piece of the same type could also reach the target (sliders need a clear path): the origin file if it is unique among those pieces, else the origin rank if unique, else the full origin square; then "x" if the target is occupied; then the target.

Why this case matters

Exported PGN must be re-readable; a wrong disambiguator makes the game unreplayable.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    b=x['board']; fr,to=x['from'],x['to']; p=b[fr]; white=p.isupper()
    def c(q): return ord(q[0])-97,int(q[1])-1
    def reach(q):
        f,r=c(q); tf,tr=c(to); df,dr=tf-f,tr-r
        t=p.upper()
        if t=='N': return (abs(df),abs(dr)) in ((1,2),(2,1))
        if t=='K': return max(abs(df),abs(dr))==1
        orth=(df==0)!=(dr==0); diag=abs(df)==abs(dr) and df!=0
        if not ((t=='R' and orth) or (t=='B' and diag) or (t=='Q' and (orth or diag))): return False
        sf=(df>0)-(df<0); sr=(dr>0)-(dr<0)
        return True
    others=[q for q,v in b.items() if v==p and q!=fr and reach(q)]
    dis=''
    if others:
        if all(q[0]!=fr[0] for q in others): dis=fr[0]
        elif all(q[1]!=fr[1] for q in others): dis=fr[1]
        else: dis=fr
    cap='x' if to in b else ''
    return p.upper()+dis+cap+to
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4'),
  ('capture with file',
   {'board': {'a1': 'K', 'a8': 'k', 'c3': 'N', 'e4': 'p', 'g3': 'N'}, 'from': 'c3', 'to': 'e4'},
   'Ncxe4'),
  ('simple bishop', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'from': 'c1', 'to': 'g5'}, 'Bg5'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4'),
  ('capture with file',
   {'board': {'a1': 'K', 'a8': 'k', 'c3': 'N', 'e4': 'p', 'g3': 'N'}, 'from': 'c3', 'to': 'e4'},
   'Ncxe4'),
  ('simple bishop', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'from': 'c1', 'to': 'g5'}, 'Bg5'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'error:' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
two knights different filesNbd2Nbd2Passed
two rooks same fileR1a3R1a3Passed
three queens need squareQh4e1Qh4e1Passed
blocked rook no disambiguationRhd1Rd1Failed
enemy knight irrelevantNd2Nd2Passed
black knightsNbd7Nbd7Passed
black rook captureRxa2Rxa2Passed
rook shielded by bishopRhd4Rd4Failed

SHA-256 / 8678787613e54dbe2738c5a988ff29b9f10de19243db6256b07cc089ddcdcd70

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    b=x['board']; fr,to=x['from'],x['to']; p=b[fr]; white=p.isupper()
    def c(q): return ord(q[0])-97,int(q[1])-1
    def reach(q):
        f,r=c(q); tf,tr=c(to); df,dr=tf-f,tr-r
        t=p.upper()
        if t=='N': return (abs(df),abs(dr)) in ((1,2),(2,1))
        if t=='K': return max(abs(df),abs(dr))==1
        orth=(df==0)!=(dr==0); diag=abs(df)==abs(dr) and df!=0
        if not ((t=='R' and orth) or (t=='B' and diag) or (t=='Q' and (orth or diag))): return False
        sf=(df>0)-(df<0); sr=(dr>0)-(dr<0)
        cf,cr=f+sf,r+sr
        if (cf,cr)!=(tf,tr) and chr(97+cf)+str(cr+1) in b: return False
        return True
    others=[q for q,v in b.items() if v==p and q!=fr and reach(q)]
    dis=''
    if others:
        if all(q[0]!=fr[0] for q in others): dis=fr[0]
        elif all(q[1]!=fr[1] for q in others): dis=fr[1]
        else: dis=fr
    cap='x' if to in b else ''
    return p.upper()+dis+cap+to
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4'),
  ('capture with file',
   {'board': {'a1': 'K', 'a8': 'k', 'c3': 'N', 'e4': 'p', 'g3': 'N'}, 'from': 'c3', 'to': 'e4'},
   'Ncxe4'),
  ('simple bishop', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'from': 'c1', 'to': 'g5'}, 'Bg5'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4'),
  ('capture with file',
   {'board': {'a1': 'K', 'a8': 'k', 'c3': 'N', 'e4': 'p', 'g3': 'N'}, 'from': 'c3', 'to': 'e4'},
   'Ncxe4'),
  ('simple bishop', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'from': 'c1', 'to': 'g5'}, 'Bg5'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'error:' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
two knights different filesNbd2Nbd2Passed
two rooks same fileR1a3R1a3Passed
three queens need squareQh4e1Qh4e1Passed
blocked rook no disambiguationRhd1Rd1Failed
enemy knight irrelevantNd2Nd2Passed
black knightsNbd7Nbd7Passed
black rook captureRxa2Rxa2Passed
rook shielded by bishopRhd4Rd4Failed

SHA-256 / d2e38537c6fa74489623994dc0bfdd0ea4762d99c10cee49abad79d253b1a396

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    b=x['board']; fr,to=x['from'],x['to']; p=b[fr]; white=p.isupper()
    def c(q): return ord(q[0])-97,int(q[1])-1
    def reach(q):
        f,r=c(q); tf,tr=c(to); df,dr=tf-f,tr-r
        t=p.upper()
        if t=='N': return (abs(df),abs(dr)) in ((1,2),(2,1))
        if t=='K': return max(abs(df),abs(dr))==1
        orth=(df==0)!=(dr==0); diag=abs(df)==abs(dr) and df!=0
        if not ((t=='R' and orth) or (t=='B' and diag) or (t=='Q' and (orth or diag))): return False
        sf=(df>0)-(df<0); sr=(dr>0)-(dr<0)
        cf,cr=f+sf,r+sr
        while (cf,cr)!=(tf,tr):
            if chr(97+cf)+str(cr+1) in b: return False
            cf+=sf; cr+=sr
        return True
    others=[q for q,v in b.items() if v==p and q!=fr and reach(q)]
    dis=''
    if others:
        if all(q[0]!=fr[0] for q in others): dis=fr[0]
        elif all(q[1]!=fr[1] for q in others): dis=fr[1]
        else: dis=fr
    cap='x' if to in b else ''
    return p.upper()+dis+cap+to
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4'),
  ('capture with file',
   {'board': {'a1': 'K', 'a8': 'k', 'c3': 'N', 'e4': 'p', 'g3': 'N'}, 'from': 'c3', 'to': 'e4'},
   'Ncxe4'),
  ('simple bishop', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'from': 'c1', 'to': 'g5'}, 'Bg5'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4'),
  ('capture with file',
   {'board': {'a1': 'K', 'a8': 'k', 'c3': 'N', 'e4': 'p', 'g3': 'N'}, 'from': 'c3', 'to': 'e4'},
   'Ncxe4'),
  ('simple bishop', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'from': 'c1', 'to': 'g5'}, 'Bg5'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')],
 [('two knights different files',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'from': 'b1', 'to': 'd2'}, 'Nbd2'),
  ('two rooks same file',
   {'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'from': 'a1', 'to': 'a3'}, 'R1a3'),
  ('three queens need square',
   {'board': {'a1': 'K', 'a8': 'k', 'e4': 'Q', 'h1': 'Q', 'h4': 'Q'}, 'from': 'h4', 'to': 'e1'},
   'Qh4e1'),
  ('blocked rook no disambiguation',
   {'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'from': 'h1', 'to': 'd1'},
   'Rd1'),
  ('enemy knight irrelevant',
   {'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'n'}, 'from': 'b1', 'to': 'd2'}, 'Nd2'),
  ('black knights',
   {'board': {'b8': 'n', 'e1': 'K', 'e8': 'k', 'f6': 'n'}, 'from': 'b8', 'to': 'd7'}, 'Nbd7'),
  ('black rook capture',
   {'board': {'a2': 'P', 'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'from': 'a8', 'to': 'a2'}, 'Rxa2'),
  ('rook shielded by bishop',
   {'board': {'a4': 'R', 'a8': 'k', 'c4': 'B', 'h1': 'K', 'h4': 'R'}, 'from': 'h4', 'to': 'd4'},
   'Rd4')]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'error:' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
two knights different filesNbd2Nbd2Passed
two rooks same fileR1a3R1a3Passed
three queens need squareQh4e1Qh4e1Passed
blocked rook no disambiguationRd1Rd1Passed
enemy knight irrelevantNd2Nd2Passed
black knightsNbd7Nbd7Passed
black rook captureRxa2Rxa2Passed
rook shielded by bishopRd4Rd4Passed

SHA-256 / 118f5803b4745a705d45d8374244dbb7904c74b6790ea6938c20766b0fcccdb4

Verification & scope

A deterministic toy model with an explicitly stated contract; boards are {square: piece} with white upper-case and black lower-case pieces; no claim of full FIDE Laws conformance. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:50:14.223404+00:00.

Case digest / 8e17e4223cccff6de268031eba52b2f7e2f84e29179d6c26ce12aba67979f325