FA-82636 / Chess move legality / Open access
Rank disambiguator compared with the file letter · case 01
"R1a3" is reported illegal although the a1 rook can play it.
ROOT CAUSE
Every disambiguation character is matched against the origin file.
VERIFIED REPAIR
Letters filter by file and digits filter by rank.
Unsuccessful approach: Ignoring file letters makes "Nbd2" ambiguous.
Case contract
Input: {"board", "white": side to move, "san"} for a non-castling move. Check suffixes + and # are ignored. Resolve the SAN to the unique origin square of a pseudo-legal move (sliders need a clear path; pawns push one or, from their start rank through an empty square, two; pawns capture one step diagonally forward). "x" requires an enemy piece on the target and its absence requires an empty target. File/rank disambiguators filter candidates. Return the origin, "ambiguous" or "illegal".
Why this case matters
PGN import and move entry depend on resolving SAN to exactly one move.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
b=x['board']; white=x['white']; s=x['san'].rstrip('+#')
def c(q): return ord(q[0])-97,int(q[1])-1
def n(f,r): return chr(97+f)+str(r+1)
piece=s[0] if s[0] in 'NBRQK' else 'P'
body=s[1:] if piece!='P' else s
capture='x' in body
body=body.replace('x','')
to=body[-2:]; dis=body[:-2]
tf,tr=c(to)
occ=b.get(to)
if capture and (occ is None or occ.isupper()==white): return 'illegal'
if not capture and occ is not None: return 'illegal'
letter=piece if white else piece.lower()
cands=[]
for q,v in b.items():
if v!=letter: continue
f,r=c(q)
if piece=='P':
d=1 if white else -1
if capture:
ok=abs(tf-f)==1 and tr-r==d
else:
ok=tf==f and (tr-r==d or (tr-r==2*d and r==(1 if white else 6) and n(f,r+d) not in b))
elif piece=='N':
ok=(abs(tf-f),abs(tr-r)) in ((1,2),(2,1))
elif piece=='K':
ok=max(abs(tf-f),abs(tr-r))==1
else:
df,dr=tf-f,tr-r
orth=(df==0)!=(dr==0); diag=abs(df)==abs(dr) and df!=0
ok=(piece=='R' and orth) or (piece=='B' and diag) or (piece=='Q' and (orth or diag))
if ok:
sf=(df>0)-(df<0); sr=(dr>0)-(dr<0)
cf,cr=f+sf,r+sr
while (cf,cr)!=(tf,tr):
if n(cf,cr) in b: ok=False
cf+=sf; cr+=sr
if ok and all(ch==q[0] for ch in dis):
cands.append(q)
if len(cands)==1: return cands[0]
return 'ambiguous' if cands else 'illegal'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('mate suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qf7#', 'white': True}, 'h5'),
('check suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qe5+', 'white': True},
'h5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('quiet move onto own',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'P', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('capture with x',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nxf3', 'white': True}, 'g1'),
('black double push', {'board': {'d7': 'p', 'e1': 'K', 'e8': 'k'}, 'san': 'd5', 'white': False},
'd7'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('black double push', {'board': {'d7': 'p', 'e1': 'K', 'e8': 'k'}, 'san': 'd5', 'white': False},
'd7'),
('white double push', {'board': {'e1': 'K', 'e2': 'P', 'e8': 'k'}, 'san': 'e4', 'white': True},
'e2'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4'),
('backward pawn capture',
{'board': {'d3': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd3', 'white': True},
'illegal'),
('black pawn capture',
{'board': {'d4': 'P', 'e1': 'K', 'e5': 'p', 'e8': 'k'}, 'san': 'exd4', 'white': False}, 'e5')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4'),
('black pawn capture',
{'board': {'d4': 'P', 'e1': 'K', 'e5': 'p', 'e8': 'k'}, 'san': 'exd4', 'white': False}, 'e5'),
('rook blocked candidate',
{'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'san': 'Rd1', 'white': True},
'h1'),
('bishop simple', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'san': 'Bg5', 'white': True},
'c1')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('mate suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qf7#', 'white': True}, 'h5'),
('check suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qe5+', 'white': True},
'h5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| knight file disambiguation | b1 | b1 | Passed |
| knight ambiguous | ambiguous | ambiguous | Passed |
| rook rank disambiguation | illegal | a1 | Failed |
| rook rank disambiguation upper | illegal | a5 | Failed |
| mate suffix | h5 | h5 | Passed |
| check suffix | h5 | h5 | Passed |
| quiet move onto enemy | illegal | illegal | Passed |
| pawn capture file | e4 | e4 | Passed |
SHA-256 / bcb3142755d67e62557f1d2d9901e5b7e2a6dfe6adb9d084493e1b3a38f761cc
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']; white=x['white']; s=x['san'].rstrip('+#')
def c(q): return ord(q[0])-97,int(q[1])-1
def n(f,r): return chr(97+f)+str(r+1)
piece=s[0] if s[0] in 'NBRQK' else 'P'
body=s[1:] if piece!='P' else s
capture='x' in body
body=body.replace('x','')
to=body[-2:]; dis=body[:-2]
tf,tr=c(to)
occ=b.get(to)
if capture and (occ is None or occ.isupper()==white): return 'illegal'
if not capture and occ is not None: return 'illegal'
letter=piece if white else piece.lower()
cands=[]
for q,v in b.items():
if v!=letter: continue
f,r=c(q)
if piece=='P':
d=1 if white else -1
if capture:
ok=abs(tf-f)==1 and tr-r==d
else:
ok=tf==f and (tr-r==d or (tr-r==2*d and r==(1 if white else 6) and n(f,r+d) not in b))
elif piece=='N':
ok=(abs(tf-f),abs(tr-r)) in ((1,2),(2,1))
elif piece=='K':
ok=max(abs(tf-f),abs(tr-r))==1
else:
df,dr=tf-f,tr-r
orth=(df==0)!=(dr==0); diag=abs(df)==abs(dr) and df!=0
ok=(piece=='R' and orth) or (piece=='B' and diag) or (piece=='Q' and (orth or diag))
if ok:
sf=(df>0)-(df<0); sr=(dr>0)-(dr<0)
cf,cr=f+sf,r+sr
while (cf,cr)!=(tf,tr):
if n(cf,cr) in b: ok=False
cf+=sf; cr+=sr
if ok and all(ch==q[1] if ch.isdigit() else True for ch in dis):
cands.append(q)
if len(cands)==1: return cands[0]
return 'ambiguous' if cands else 'illegal'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('mate suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qf7#', 'white': True}, 'h5'),
('check suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qe5+', 'white': True},
'h5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('quiet move onto own',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'P', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('capture with x',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nxf3', 'white': True}, 'g1'),
('black double push', {'board': {'d7': 'p', 'e1': 'K', 'e8': 'k'}, 'san': 'd5', 'white': False},
'd7'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('black double push', {'board': {'d7': 'p', 'e1': 'K', 'e8': 'k'}, 'san': 'd5', 'white': False},
'd7'),
('white double push', {'board': {'e1': 'K', 'e2': 'P', 'e8': 'k'}, 'san': 'e4', 'white': True},
'e2'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4'),
('backward pawn capture',
{'board': {'d3': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd3', 'white': True},
'illegal'),
('black pawn capture',
{'board': {'d4': 'P', 'e1': 'K', 'e5': 'p', 'e8': 'k'}, 'san': 'exd4', 'white': False}, 'e5')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4'),
('black pawn capture',
{'board': {'d4': 'P', 'e1': 'K', 'e5': 'p', 'e8': 'k'}, 'san': 'exd4', 'white': False}, 'e5'),
('rook blocked candidate',
{'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'san': 'Rd1', 'white': True},
'h1'),
('bishop simple', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'san': 'Bg5', 'white': True},
'c1')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('mate suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qf7#', 'white': True}, 'h5'),
('check suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qe5+', 'white': True},
'h5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| knight file disambiguation | ambiguous | b1 | Failed |
| knight ambiguous | ambiguous | ambiguous | Passed |
| rook rank disambiguation | a1 | a1 | Passed |
| rook rank disambiguation upper | a5 | a5 | Passed |
| mate suffix | h5 | h5 | Passed |
| check suffix | h5 | h5 | Passed |
| quiet move onto enemy | illegal | illegal | Passed |
| pawn capture file | ambiguous | e4 | Failed |
SHA-256 / 60bc2c1cd5d27b1956ca7c022b93131c0a6915080131ef08a9a2114e7b22d4d5
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']; white=x['white']; s=x['san'].rstrip('+#')
def c(q): return ord(q[0])-97,int(q[1])-1
def n(f,r): return chr(97+f)+str(r+1)
piece=s[0] if s[0] in 'NBRQK' else 'P'
body=s[1:] if piece!='P' else s
capture='x' in body
body=body.replace('x','')
to=body[-2:]; dis=body[:-2]
tf,tr=c(to)
occ=b.get(to)
if capture and (occ is None or occ.isupper()==white): return 'illegal'
if not capture and occ is not None: return 'illegal'
letter=piece if white else piece.lower()
cands=[]
for q,v in b.items():
if v!=letter: continue
f,r=c(q)
if piece=='P':
d=1 if white else -1
if capture:
ok=abs(tf-f)==1 and tr-r==d
else:
ok=tf==f and (tr-r==d or (tr-r==2*d and r==(1 if white else 6) and n(f,r+d) not in b))
elif piece=='N':
ok=(abs(tf-f),abs(tr-r)) in ((1,2),(2,1))
elif piece=='K':
ok=max(abs(tf-f),abs(tr-r))==1
else:
df,dr=tf-f,tr-r
orth=(df==0)!=(dr==0); diag=abs(df)==abs(dr) and df!=0
ok=(piece=='R' and orth) or (piece=='B' and diag) or (piece=='Q' and (orth or diag))
if ok:
sf=(df>0)-(df<0); sr=(dr>0)-(dr<0)
cf,cr=f+sf,r+sr
while (cf,cr)!=(tf,tr):
if n(cf,cr) in b: ok=False
cf+=sf; cr+=sr
if ok and all((ch==q[0]) if ch.isalpha() else (ch==q[1]) for ch in dis):
cands.append(q)
if len(cands)==1: return cands[0]
return 'ambiguous' if cands else 'illegal'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('mate suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qf7#', 'white': True}, 'h5'),
('check suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qe5+', 'white': True},
'h5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('quiet move onto own',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'P', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('capture with x',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nxf3', 'white': True}, 'g1'),
('black double push', {'board': {'d7': 'p', 'e1': 'K', 'e8': 'k'}, 'san': 'd5', 'white': False},
'd7'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('black double push', {'board': {'d7': 'p', 'e1': 'K', 'e8': 'k'}, 'san': 'd5', 'white': False},
'd7'),
('white double push', {'board': {'e1': 'K', 'e2': 'P', 'e8': 'k'}, 'san': 'e4', 'white': True},
'e2'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4'),
('backward pawn capture',
{'board': {'d3': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd3', 'white': True},
'illegal'),
('black pawn capture',
{'board': {'d4': 'P', 'e1': 'K', 'e5': 'p', 'e8': 'k'}, 'san': 'exd4', 'white': False}, 'e5')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4'),
('black pawn capture',
{'board': {'d4': 'P', 'e1': 'K', 'e5': 'p', 'e8': 'k'}, 'san': 'exd4', 'white': False}, 'e5'),
('rook blocked candidate',
{'board': {'a1': 'R', 'c1': 'B', 'e2': 'K', 'e8': 'k', 'h1': 'R'}, 'san': 'Rd1', 'white': True},
'h1'),
('bishop simple', {'board': {'c1': 'B', 'e1': 'K', 'e8': 'k'}, 'san': 'Bg5', 'white': True},
'c1')],
[('knight file disambiguation',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nbd2', 'white': True}, 'b1'),
('knight ambiguous',
{'board': {'b1': 'N', 'e1': 'K', 'e8': 'k', 'f3': 'N'}, 'san': 'Nd2', 'white': True},
'ambiguous'),
('rook rank disambiguation',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R1a3', 'white': True}, 'a1'),
('rook rank disambiguation upper',
{'board': {'a1': 'R', 'a5': 'R', 'e1': 'K', 'e8': 'k'}, 'san': 'R5a3', 'white': True}, 'a5'),
('mate suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qf7#', 'white': True}, 'h5'),
('check suffix', {'board': {'e1': 'K', 'e8': 'k', 'h5': 'Q'}, 'san': 'Qe5+', 'white': True},
'h5'),
('quiet move onto enemy',
{'board': {'e1': 'K', 'e8': 'k', 'f3': 'p', 'g1': 'N'}, 'san': 'Nf3', 'white': True},
'illegal'),
('pawn capture file',
{'board': {'c4': 'P', 'd5': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'san': 'exd5', 'white': True},
'e4')]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| knight file disambiguation | b1 | b1 | Passed |
| knight ambiguous | ambiguous | ambiguous | Passed |
| rook rank disambiguation | a1 | a1 | Passed |
| rook rank disambiguation upper | a5 | a5 | Passed |
| mate suffix | h5 | h5 | Passed |
| check suffix | h5 | h5 | Passed |
| quiet move onto enemy | illegal | illegal | Passed |
| pawn capture file | e4 | e4 | Passed |
SHA-256 / dfb6424c8d9c1fd49f0b5ed8b2ebeec25c93be7a077b57b8dbbbed1ac39dd2ec
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:13.995630+00:00.
Case digest / b9aa5a088e239247a082596bb0cf43331d5e8c900f4edc9f3613f7a9b65ce2ab