FAILURE MAP
← Case archive

FA-82676 / Chess move legality / Open access

Black moves written with lower-case piece letters · case 01

A black rook capture is written "rxa2".

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

ROOT CAUSE

The board letter is copied verbatim, keeping black's lower case.

VERIFIED REPAIR

SAN piece letters are always upper case.

Unsuccessful approach: Upper-casing only knights leaves other black pieces lower case.

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)
        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+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'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4')],
 [('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'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1')],
 [('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'),
  ('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'),
  ('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'),
  ('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')],
 [('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'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4')]]
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 knightsnbd7Nbd7Failed
black rook capturerxa2Rxa2Failed
queen diagonal twinQad4Qad4Passed

SHA-256 / 3630d72a0d948b46aae950218cef7549f62813c98a6ddd9c99dd66bd283efe09

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
        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 ('N' if p in 'Nn' else p)+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'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4')],
 [('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'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1')],
 [('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'),
  ('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'),
  ('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'),
  ('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')],
 [('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'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4')]]
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 capturerxa2Rxa2Failed
queen diagonal twinQad4Qad4Passed

SHA-256 / 6ca78e64a8143a36fe066d910a9b3f44142e8d7b282a762827252b2224c21ad6

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'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4')],
 [('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'),
  ('rooks on same rank',
   {'board': {'a1': 'R', 'f1': 'R', 'h2': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd1'}, 'Rad1')],
 [('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'),
  ('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'),
  ('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'),
  ('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')],
 [('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'),
  ('queen diagonal twin',
   {'board': {'a1': 'Q', 'g1': 'Q', 'h1': 'K', 'h8': 'k'}, 'from': 'a1', 'to': 'd4'}, 'Qad4')]]
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
queen diagonal twinQad4Qad4Passed

SHA-256 / ca80c409e7960628087d0344a3268208d237aceea586ba253bd2c5e9c5ff0070

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.336547+00:00.

Case digest / 8cd07bc06a88ed736c8733e25fc0b6a41925aede2fd366adea8a3c13c3547d48