FAILURE MAP
← Case archive

FA-82451 / Chess move legality / Open access

Castling with a knight in the rook corner · case 01

White castles kingside with a knight or an enemy rook on h1.

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

ROOT CAUSE

The corner is only checked for being occupied.

THE FAILURE

The corner is only checked for being occupied.

Unsuccessful approach: Hard-coding the white rook letter rejects every black castling move.

Case contract

Input: {"board", "side": "w" or "b", "wing": "K" or "Q", "rights": subset of "KQkq"}. Standard castling is legal when the right is held, the king is on e1/e8 and the matching own rook on h- or a-file, the squares between them are empty (f,g or b,c,d), and none of the king's squares e, f, g (kingside) or e, d, c (queenside) is attacked by the opponent.

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):
    def attacked(bd, sq, by_white):
        f,r=ord(sq[0])-97,int(sq[1])-1
        def at(ff,rr):
            return bd.get(chr(97+ff)+str(rr+1)) if 0<=ff<8 and 0<=rr<8 else None
        P,N,B,R,Q,K=('PNBRQK' if by_white else 'pnbrqk')
        pr=r-1 if by_white else r+1
        if at(f-1,pr)==P or at(f+1,pr)==P: return True
        for df,dr in ((1,2),(2,1),(-1,2),(-2,1),(1,-2),(2,-1),(-1,-2),(-2,-1)):
            if at(f+df,r+dr)==N: return True
        for df in (-1,0,1):
            for dr in (-1,0,1):
                if (df or dr) and at(f+df,r+dr)==K: return True
        for df,dr,kinds in ((1,0,(R,Q)),(-1,0,(R,Q)),(0,1,(R,Q)),(0,-1,(R,Q)),(1,1,(B,Q)),(1,-1,(B,Q)),(-1,1,(B,Q)),(-1,-1,(B,Q))):
            ff,rr=f+df,r+dr
            while 0<=ff<8 and 0<=rr<8:
                v=at(ff,rr)
                if v:
                    if v in kinds: return True
                    break
                ff+=df; rr+=dr
        return False
    
    b=x['board']; white=x['side']=='w'; wing=x['wing']
    rank='1' if white else '8'
    right=wing if white else wing.lower()
    if right not in x['rights']: return False
    if b.get('e'+rank)!=('K' if white else 'k'): return False
    rook_sq=('h' if wing=='K' else 'a')+rank
    if rook_sq not in b: return False
    between='fg' if wing=='K' else 'bcd'
    if any(f+rank in b for f in between): return False
    path='efg' if wing=='K' else 'edc'
    return not any(attacked(b, f+rank, not white) for f in path)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('white kingside clear',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'KQkq', 'side': 'w', 'wing': 'K'}, True),
  ('queenside b-file blocked',
   {'board': {'a1': 'R', 'b1': 'N', 'e1': 'K', 'e8': 'k'},
    'rights': 'KQ',
    'side': 'w',
    'wing': 'Q'},
   False),
  ('queenside b-file attacked',
   {'board': {'a1': 'R', 'b8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   True),
  ('in check',
   {'board': {'a8': 'k', 'e1': 'K', 'e8': 'r', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('f1 attacked',
   {'board': {'c4': 'b', 'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('black uses own right',
   {'board': {'e1': 'K', 'e8': 'k', 'h8': 'r'}, 'rights': 'K', 'side': 'b', 'wing': 'K'}, False),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False)],
 [('in check',
   {'board': {'a8': 'k', 'e1': 'K', 'e8': 'r', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('f1 attacked',
   {'board': {'c4': 'b', 'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('black uses own right',
   {'board': {'e1': 'K', 'e8': 'k', 'h8': 'r'}, 'rights': 'K', 'side': 'b', 'wing': 'K'}, False),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('enemy rook in corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'r'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('pawn attacks g1 from h2',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R', 'h2': 'p'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('white pawn on f2 harmless',
   {'board': {'e1': 'K', 'e8': 'k', 'g2': 'P', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   True)],
 [('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('enemy rook in corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'r'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('pawn attacks g1 from h2',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R', 'h2': 'p'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('white pawn on f2 harmless',
   {'board': {'e1': 'K', 'e8': 'k', 'g2': 'P', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   True),
  ('black pawn attacks f8 check',
   {'board': {'e1': 'K', 'e7': 'P', 'e8': 'k', 'h8': 'r'}, 'rights': 'k', 'side': 'b', 'wing': 'K'},
   False),
  ('shielded rook attack',
   {'board': {'a8': 'k', 'e1': 'K', 'f4': 'P', 'f8': 'r', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True),
  ('own piece shields g1',
   {'board': {'a8': 'k', 'e1': 'K', 'g3': 'N', 'g5': 'q', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True)],
 [('white kingside clear',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'KQkq', 'side': 'w', 'wing': 'K'}, True),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('black pawn attacks f8 check',
   {'board': {'e1': 'K', 'e7': 'P', 'e8': 'k', 'h8': 'r'}, 'rights': 'k', 'side': 'b', 'wing': 'K'},
   False),
  ('shielded rook attack',
   {'board': {'a8': 'k', 'e1': 'K', 'f4': 'P', 'f8': 'r', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True),
  ('own piece shields g1',
   {'board': {'a8': 'k', 'e1': 'K', 'g3': 'N', 'g5': 'q', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True),
  ('c1 attacked',
   {'board': {'a1': 'R', 'e1': 'K', 'e8': 'k', 'h6': 'b'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   False),
  ('d1 blocked',
   {'board': {'a1': 'R', 'd1': 'Q', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   False)],
 [('white kingside clear',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'KQkq', 'side': 'w', 'wing': 'K'}, True),
  ('queenside b-file blocked',
   {'board': {'a1': 'R', 'b1': 'N', 'e1': 'K', 'e8': 'k'},
    'rights': 'KQ',
    'side': 'w',
    'wing': 'Q'},
   False),
  ('queenside b-file attacked',
   {'board': {'a1': 'R', 'b8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   True),
  ('in check',
   {'board': {'a8': 'k', 'e1': 'K', 'e8': 'r', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('c1 attacked',
   {'board': {'a1': 'R', 'e1': 'K', 'e8': 'k', 'h6': 'b'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   False),
  ('d1 blocked',
   {'board': {'a1': 'R', 'd1': 'Q', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   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 fixtureActualExpectedOutcome
white kingside clearTrueTruePassed
queenside b-file blockedFalseFalsePassed
queenside b-file attackedTrueTruePassed
in checkFalseFalsePassed
f1 attackedFalseFalsePassed
black uses own rightFalseFalsePassed
black has rightTrueTruePassed
knight in rook cornerTrueFalseFailed

SHA-256 / 68c034d8520420783cc4328493d29f9878e39012f60ca629d5a4f963c7625e1f

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    def attacked(bd, sq, by_white):
        f,r=ord(sq[0])-97,int(sq[1])-1
        def at(ff,rr):
            return bd.get(chr(97+ff)+str(rr+1)) if 0<=ff<8 and 0<=rr<8 else None
        P,N,B,R,Q,K=('PNBRQK' if by_white else 'pnbrqk')
        pr=r-1 if by_white else r+1
        if at(f-1,pr)==P or at(f+1,pr)==P: return True
        for df,dr in ((1,2),(2,1),(-1,2),(-2,1),(1,-2),(2,-1),(-1,-2),(-2,-1)):
            if at(f+df,r+dr)==N: return True
        for df in (-1,0,1):
            for dr in (-1,0,1):
                if (df or dr) and at(f+df,r+dr)==K: return True
        for df,dr,kinds in ((1,0,(R,Q)),(-1,0,(R,Q)),(0,1,(R,Q)),(0,-1,(R,Q)),(1,1,(B,Q)),(1,-1,(B,Q)),(-1,1,(B,Q)),(-1,-1,(B,Q))):
            ff,rr=f+df,r+dr
            while 0<=ff<8 and 0<=rr<8:
                v=at(ff,rr)
                if v:
                    if v in kinds: return True
                    break
                ff+=df; rr+=dr
        return False
    
    b=x['board']; white=x['side']=='w'; wing=x['wing']
    rank='1' if white else '8'
    right=wing if white else wing.lower()
    if right not in x['rights']: return False
    if b.get('e'+rank)!=('K' if white else 'k'): return False
    rook_sq=('h' if wing=='K' else 'a')+rank
    if b.get(rook_sq)!='R': return False
    between='fg' if wing=='K' else 'bcd'
    if any(f+rank in b for f in between): return False
    path='efg' if wing=='K' else 'edc'
    return not any(attacked(b, f+rank, not white) for f in path)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('white kingside clear',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'KQkq', 'side': 'w', 'wing': 'K'}, True),
  ('queenside b-file blocked',
   {'board': {'a1': 'R', 'b1': 'N', 'e1': 'K', 'e8': 'k'},
    'rights': 'KQ',
    'side': 'w',
    'wing': 'Q'},
   False),
  ('queenside b-file attacked',
   {'board': {'a1': 'R', 'b8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   True),
  ('in check',
   {'board': {'a8': 'k', 'e1': 'K', 'e8': 'r', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('f1 attacked',
   {'board': {'c4': 'b', 'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('black uses own right',
   {'board': {'e1': 'K', 'e8': 'k', 'h8': 'r'}, 'rights': 'K', 'side': 'b', 'wing': 'K'}, False),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False)],
 [('in check',
   {'board': {'a8': 'k', 'e1': 'K', 'e8': 'r', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('f1 attacked',
   {'board': {'c4': 'b', 'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('black uses own right',
   {'board': {'e1': 'K', 'e8': 'k', 'h8': 'r'}, 'rights': 'K', 'side': 'b', 'wing': 'K'}, False),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('enemy rook in corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'r'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('pawn attacks g1 from h2',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R', 'h2': 'p'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('white pawn on f2 harmless',
   {'board': {'e1': 'K', 'e8': 'k', 'g2': 'P', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   True)],
 [('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('enemy rook in corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'r'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('pawn attacks g1 from h2',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R', 'h2': 'p'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('white pawn on f2 harmless',
   {'board': {'e1': 'K', 'e8': 'k', 'g2': 'P', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   True),
  ('black pawn attacks f8 check',
   {'board': {'e1': 'K', 'e7': 'P', 'e8': 'k', 'h8': 'r'}, 'rights': 'k', 'side': 'b', 'wing': 'K'},
   False),
  ('shielded rook attack',
   {'board': {'a8': 'k', 'e1': 'K', 'f4': 'P', 'f8': 'r', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True),
  ('own piece shields g1',
   {'board': {'a8': 'k', 'e1': 'K', 'g3': 'N', 'g5': 'q', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True)],
 [('white kingside clear',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'KQkq', 'side': 'w', 'wing': 'K'}, True),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('black pawn attacks f8 check',
   {'board': {'e1': 'K', 'e7': 'P', 'e8': 'k', 'h8': 'r'}, 'rights': 'k', 'side': 'b', 'wing': 'K'},
   False),
  ('shielded rook attack',
   {'board': {'a8': 'k', 'e1': 'K', 'f4': 'P', 'f8': 'r', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True),
  ('own piece shields g1',
   {'board': {'a8': 'k', 'e1': 'K', 'g3': 'N', 'g5': 'q', 'h1': 'R'},
    'rights': 'K',
    'side': 'w',
    'wing': 'K'},
   True),
  ('c1 attacked',
   {'board': {'a1': 'R', 'e1': 'K', 'e8': 'k', 'h6': 'b'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   False),
  ('d1 blocked',
   {'board': {'a1': 'R', 'd1': 'Q', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   False)],
 [('white kingside clear',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'R'}, 'rights': 'KQkq', 'side': 'w', 'wing': 'K'}, True),
  ('queenside b-file blocked',
   {'board': {'a1': 'R', 'b1': 'N', 'e1': 'K', 'e8': 'k'},
    'rights': 'KQ',
    'side': 'w',
    'wing': 'Q'},
   False),
  ('queenside b-file attacked',
   {'board': {'a1': 'R', 'b8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   True),
  ('in check',
   {'board': {'a8': 'k', 'e1': 'K', 'e8': 'r', 'h1': 'R'}, 'rights': 'K', 'side': 'w', 'wing': 'K'},
   False),
  ('black has right',
   {'board': {'a8': 'r', 'e1': 'K', 'e8': 'k'}, 'rights': 'q', 'side': 'b', 'wing': 'Q'}, True),
  ('knight in rook corner',
   {'board': {'e1': 'K', 'e8': 'k', 'h1': 'N'}, 'rights': 'K', 'side': 'w', 'wing': 'K'}, False),
  ('c1 attacked',
   {'board': {'a1': 'R', 'e1': 'K', 'e8': 'k', 'h6': 'b'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   False),
  ('d1 blocked',
   {'board': {'a1': 'R', 'd1': 'Q', 'e1': 'K', 'e8': 'k'}, 'rights': 'Q', 'side': 'w', 'wing': 'Q'},
   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 fixtureActualExpectedOutcome
white kingside clearTrueTruePassed
queenside b-file blockedFalseFalsePassed
queenside b-file attackedTrueTruePassed
in checkFalseFalsePassed
f1 attackedFalseFalsePassed
black uses own rightFalseFalsePassed
black has rightFalseTrueFailed
knight in rook cornerFalseFalsePassed

SHA-256 / 02bce4c23e67b69ab1672350748019c6d8804bea0940e13ec508c86f80eafb66

HELD IN THE MEMBER ARCHIVE

The verified repair and its recorded checks are member-only.

This mechanism has 8 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.

Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.

Member access is invitation-based. Sign in with your invited account to inspect the repair.

Sign in to the archive ↗

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

Case digest / cce8078844b9c27e21d63df4522dce3b9a7d28f8cc8f22bda931c0138010bb58