FA-82426 / Chess move legality / Open access
Any diagonal step accepted while an ep square exists · case 01
With ep on d6, a pawn capturing diagonally onto an empty f6 is treated as en passant.
ROOT CAUSE
The validator checks only that some en passant target exists, not that the move lands on it.
VERIFIED REPAIR
Require the destination to equal the en passant target square.
Unsuccessful approach: Matching the file alone accepts a target on the wrong rank and fails when there is no ep square.
Case contract
Input: {"board", "move": [from, to], "ep": target square or null}. Return true when the move is a legal en passant capture: a pawn on its fifth rank moves diagonally forward onto the ep square, an enemy pawn stands beside it on the origin rank, and after removing both pawns from their squares and placing the capturer, no enemy rook, bishop or queen attacks the mover's king along an open line.
Why this case matters
Move validators gate every move in engines, servers and arbiters; accepting an illegal move or rejecting a legal one corrupts the game record.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
b=dict(x['board']); fr,to=x['move']; p=b.get(fr)
if p not in ('P','p') or x['ep'] is None: return False
white=p=='P'
d=1 if white else -1
f0,r0=ord(fr[0])-97,int(fr[1])-1
f1,r1=ord(to[0])-97,int(to[1])-1
if r1-r0!=d or abs(f1-f0)!=1: return False
if r0!=(4 if white else 3): return False
victim=to[0]+fr[1]
if b.get(victim)!=('p' if white else 'P'): return False
del b[fr]; del b[victim]; b[to]=p
ks=[s for s,v in b.items() if v==('K' if white else 'k')][0]
kf,kr=ord(ks[0])-97,int(ks[1])-1
for df,dr in ((1,0),(-1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)):
cf,cr=kf+df,kr+dr
while 0<=cf<8 and 0<=cr<8:
v=b.get(chr(97+cf)+str(cr+1))
if v:
if v.isupper()!=white and v.lower() in (('r','q') if df==0 or dr==0 else ('b','q')): return False
break
cf+=df; cr+=dr
return True
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('rank pin both pawns',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
False),
('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)],
[('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('wrong rank for white',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'd5', 'move': ['e4', 'd5']},
False),
('black wrong rank',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'e4', 'move': ['d5', 'e4']},
False),
('file rook behind pawn',
{'board': {'d1': 'K', 'd5': 'p', 'd8': 'r', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)],
[('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('black wrong rank',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'e4', 'move': ['d5', 'e4']},
False),
('file rook behind pawn',
{'board': {'d1': 'K', 'd5': 'p', 'd8': 'r', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False),
('pin by own rook harmless',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'R'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('bishop on the rank',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'b'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on the diagonal',
{'board': {'c3': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'g7': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
True)],
[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False),
('bishop on the rank',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'b'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on the diagonal',
{'board': {'c3': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'g7': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on file of ep square',
{'board': {'a1': 'K', 'd1': 'r', 'd5': 'p', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
False)],
[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('rank pin both pawns',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
False),
('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)]]
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 |
|---|---|---|---|
| plain ep | True | True | Passed |
| black ep | True | True | Passed |
| rank pin both pawns | False | False | Passed |
| rank pin black side | False | False | Passed |
| bishop diagonal after ep | True | True | Passed |
| no ep square | False | False | Passed |
| wrong target | True | False | Failed |
| same file ep target | True | False | Failed |
SHA-256 / 466991438e70a5958f72cc7270c5f208d8151a70f71d00e59e94672872f7cf18
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
b=dict(x['board']); fr,to=x['move']; p=b.get(fr)
if p not in ('P','p') or to[0]!=x['ep'][0]: return False
white=p=='P'
d=1 if white else -1
f0,r0=ord(fr[0])-97,int(fr[1])-1
f1,r1=ord(to[0])-97,int(to[1])-1
if r1-r0!=d or abs(f1-f0)!=1: return False
if r0!=(4 if white else 3): return False
victim=to[0]+fr[1]
if b.get(victim)!=('p' if white else 'P'): return False
del b[fr]; del b[victim]; b[to]=p
ks=[s for s,v in b.items() if v==('K' if white else 'k')][0]
kf,kr=ord(ks[0])-97,int(ks[1])-1
for df,dr in ((1,0),(-1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)):
cf,cr=kf+df,kr+dr
while 0<=cf<8 and 0<=cr<8:
v=b.get(chr(97+cf)+str(cr+1))
if v:
if v.isupper()!=white and v.lower() in (('r','q') if df==0 or dr==0 else ('b','q')): return False
break
cf+=df; cr+=dr
return True
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('rank pin both pawns',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
False),
('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)],
[('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('wrong rank for white',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'd5', 'move': ['e4', 'd5']},
False),
('black wrong rank',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'e4', 'move': ['d5', 'e4']},
False),
('file rook behind pawn',
{'board': {'d1': 'K', 'd5': 'p', 'd8': 'r', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)],
[('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('black wrong rank',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'e4', 'move': ['d5', 'e4']},
False),
('file rook behind pawn',
{'board': {'d1': 'K', 'd5': 'p', 'd8': 'r', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False),
('pin by own rook harmless',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'R'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('bishop on the rank',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'b'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on the diagonal',
{'board': {'c3': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'g7': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
True)],
[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False),
('bishop on the rank',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'b'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on the diagonal',
{'board': {'c3': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'g7': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on file of ep square',
{'board': {'a1': 'K', 'd1': 'r', 'd5': 'p', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
False)],
[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('rank pin both pawns',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
False),
('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)]]
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 |
|---|---|---|---|
| plain ep | True | True | Passed |
| black ep | True | True | Passed |
| rank pin both pawns | False | False | Passed |
| rank pin black side | False | False | Passed |
| bishop diagonal after ep | True | True | Passed |
| no ep square | error:TypeError | False | Failed |
| wrong target | False | False | Passed |
| same file ep target | True | False | Failed |
SHA-256 / 40deeff6dbdaf645b363634e6acb08dfc506679d5396c39874715223f3f282b5
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
b=dict(x['board']); fr,to=x['move']; p=b.get(fr)
if p not in ('P','p') or to!=x['ep']: return False
white=p=='P'
d=1 if white else -1
f0,r0=ord(fr[0])-97,int(fr[1])-1
f1,r1=ord(to[0])-97,int(to[1])-1
if r1-r0!=d or abs(f1-f0)!=1: return False
if r0!=(4 if white else 3): return False
victim=to[0]+fr[1]
if b.get(victim)!=('p' if white else 'P'): return False
del b[fr]; del b[victim]; b[to]=p
ks=[s for s,v in b.items() if v==('K' if white else 'k')][0]
kf,kr=ord(ks[0])-97,int(ks[1])-1
for df,dr in ((1,0),(-1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)):
cf,cr=kf+df,kr+dr
while 0<=cf<8 and 0<=cr<8:
v=b.get(chr(97+cf)+str(cr+1))
if v:
if v.isupper()!=white and v.lower() in (('r','q') if df==0 or dr==0 else ('b','q')): return False
break
cf+=df; cr+=dr
return True
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('rank pin both pawns',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
False),
('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)],
[('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('wrong rank for white',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'd5', 'move': ['e4', 'd5']},
False),
('black wrong rank',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'e4', 'move': ['d5', 'e4']},
False),
('file rook behind pawn',
{'board': {'d1': 'K', 'd5': 'p', 'd8': 'r', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)],
[('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('black wrong rank',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'e4', 'move': ['d5', 'e4']},
False),
('file rook behind pawn',
{'board': {'d1': 'K', 'd5': 'p', 'd8': 'r', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False),
('pin by own rook harmless',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'R'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('bishop on the rank',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'b'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on the diagonal',
{'board': {'c3': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'g7': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
True)],
[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False),
('bishop on the rank',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'b'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on the diagonal',
{'board': {'c3': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'g7': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
True),
('rook on file of ep square',
{'board': {'a1': 'K', 'd1': 'r', 'd5': 'p', 'e5': 'P', 'e8': 'k'},
'ep': 'd6',
'move': ['e5', 'd6']},
False)],
[('plain ep',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd6', 'move': ['e5', 'd6']},
True),
('black ep',
{'board': {'d4': 'p', 'e1': 'K', 'e4': 'P', 'e8': 'k'}, 'ep': 'e3', 'move': ['d4', 'e3']},
True),
('rank pin both pawns',
{'board': {'a5': 'K', 'd5': 'p', 'e5': 'P', 'e8': 'k', 'h5': 'r'},
'ep': 'd6',
'move': ['e5', 'd6']},
False),
('rank pin black side',
{'board': {'a4': 'Q', 'd4': 'p', 'e1': 'K', 'e4': 'P', 'h4': 'k'},
'ep': 'e3',
'move': ['d4', 'e3']},
False),
('bishop diagonal after ep',
{'board': {'c3': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p', 'f6': 'n', 'h8': 'b'},
'ep': 'f6',
'move': ['e5', 'f6']},
True),
('no ep square',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': None, 'move': ['e5', 'd6']},
False),
('wrong target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k', 'f5': 'p'},
'ep': 'd6',
'move': ['e5', 'f6']},
False),
('same file ep target',
{'board': {'d5': 'p', 'e1': 'K', 'e5': 'P', 'e8': 'k'}, 'ep': 'd3', 'move': ['e5', 'd6']},
False)]]
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 |
|---|---|---|---|
| plain ep | True | True | Passed |
| black ep | True | True | Passed |
| rank pin both pawns | False | False | Passed |
| rank pin black side | False | False | Passed |
| bishop diagonal after ep | True | True | Passed |
| no ep square | False | False | Passed |
| wrong target | False | False | Passed |
| same file ep target | False | False | Passed |
SHA-256 / b90b89c64f64931e87c3805147c1abd6fba702e05ae7cdb5ed3b23888f61cbcd
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:12.054384+00:00.
Case digest / fb9e216d6c3fb86c6d684c05bc87745816e35e4ceea9935b9f1a3ceed66bf362