FAILURE MAP
← Case archive

FA-82901 / Go territory scoring / Open access

Opponent stones are treated as liberties · case 01

Surrounded chains are never captured and some suicides are accepted.

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

ROOT CAUSE

The liberty test accepts any point not of the chain colour instead of only empty points.

VERIFIED REPAIR

Only empty orthogonal neighbours are liberties.

Unsuccessful approach: Counting the just-played point as a liberty for every chain still prevents the capture it causes.

Case contract

Input [board, colour, row, col]. Occupied point -> ["illegal", "occupied"]. Otherwise place the stone, remove every adjacent opponent chain left without liberties, then reject the move as ["illegal", "suicide"] only if the placed chain still has no liberty. Return [new_board, captured_stone_count].

Why this case matters

Go servers and scoring tools compute this value automatically; a wrong answer changes a game result.

1 / The failure

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

N = 1
observations = []
def solve(x):
    board, color, r, c = x
    g = [list(row) for row in board]
    n = len(g)
    m = len(g[0])
    if g[r][c] != '.':
        return ['illegal', 'occupied']
    opp = 'W' if color == 'B' else 'B'
    g[r][c] = color
    def group(i, j):
        col = g[i][j]
        stones = {(i, j)}
        libs = set()
        stack = [(i, j)]
        while stack:
            a, b = stack.pop()
            for p, q in ((a, b + 1), (a + 1, b), (a, b - 1), (a - 1, b)):
                if 0 <= p < n and 0 <= q < m:
                    if g[p][q] != col:
                        libs.add((p, q))
                    elif g[p][q] == col and (p, q) not in stones:
                        stones.add((p, q))
                        stack.append((p, q))
        return stones, libs
    captured = 0
    for p, q in ((r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)):
        if 0 <= p < n and 0 <= q < m and g[p][q] == opp:
            stones, libs = group(p, q)
            if not libs:
                for s, t in stones:
                    g[s][t] = '.'
                captured += len(stones)
    stones, libs = group(r, c)
    if not libs:
        return ['illegal', 'suicide']
    return [[''.join(row) for row in g], captured]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['...', '...', '...'], 'W', 2, 0], [['...', '...', 'W..'], 0]], [[['.B.', 'B.B', '.B.'], 'W', 2, 2], ['illegal', 'suicide']], [[['.B.', 'B.B', '.B.'], 'W', 2, 0], ['illegal', 'suicide']], [[['.B.', 'B.B', '.B.'], 'B', 0, 0], [['BB.', 'B.B', '.B.'], 0]], [[['.B.', 'B.B', '.B.'], 'B', 0, 2], [['.BB', 'B.B', '.B.'], 0]], [[['.B.', 'B.B', '.B.'], 'B', 0, 1], ['illegal', 'occupied']], [[['B..W', 'B..W', 'B..W', 'B..W'], 'W', 2, 2], [['B..W', 'B..W', 'B.WW', 'B..W'], 0]], [[['WBW.', '..W.', 'WB.W', '..BW'], 'B', 1, 0], [['.BW.', 'B.W.', 'WB.W', '..BW'], 1]]], [[[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 1, 1], [['B..W', 'BB.W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'W', 0, 2], [['B.WW', 'B..W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 0, 1], [['BB.W', 'B..W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 2, 0], ['illegal', 'occupied']], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'W', 2, 3], [['.BW.', '.BW.', '.BWW', '.BW.'], 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'W', 0, 0], [['WBW.', '.BW.', '.BW.', '.BW.'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 0, 1], ['illegal', 'suicide']], [[['.WB.', 'B.WB', 'W.BW'], 'B', 1, 1], [['.WB.', 'BB.B', 'W.BW'], 1]]], [[[['.BW.', '.BW.', '.BW.', '.BW.'], 'B', 2, 0], [['.BW.', '.BW.', 'BBW.', '.BW.'], 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'B', 0, 2], ['illegal', 'occupied']], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 0, 0], [['B.B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 1, 1], [['..B..', '.BB..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 4, 0], [['..B..', '..B..', 'BBBBB', 'WWWWW', 'B.W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 0, 1], [['.BB..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['...', '.BW', '..B', '.BW', 'BB.'], 'W', 4, 2], ['illegal', 'suicide']], [[['B.BW', 'WWBB', 'BB.W', '.BBB'], 'W', 0, 1], [['.WBW', 'WWBB', 'BB.W', '.BBB'], 1]]], [[[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 2, 1], ['illegal', 'suicide']], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 0, 1], ['illegal', 'suicide']], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 4, 3], [['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..WB.'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 2, 4], [['B.B.W', '.B.W.', 'B.BWB', 'BBW.W', '..W..'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 3, 2], ['illegal', 'occupied']], [[['W....', '.....', '.....', '.....', '....B'], 'W', 4, 1], [['W....', '.....', '.....', '.....', '.W..B'], 0]], [[['WB.BB', 'B.B.W', 'B.WBB', 'BWBW.'], 'W', 1, 3], ['illegal', 'suicide']], [[['.BW', 'BW.', 'W..'], 'B', 2, 1], [['.BW', 'BW.', '.B.'], 1]]], [[[['W....', '.....', '.....', '.....', '....B'], 'W', 3, 0], [['W....', '.....', '.....', 'W....', '....B'], 0]], [[['W....', '.....', '.....', '.....', '....B'], 'W', 4, 0], [['W....', '.....', '.....', '.....', 'W...B'], 0]], [[['W....', '.....', '.....', '.....', '....B'], 'B', 4, 4], ['illegal', 'occupied']], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'W', 0, 2], [['.WWB.', 'W.WB.', '.WB..', 'WB...', 'B....'], 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'W', 0, 0], [['WW.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'B', 2, 4], [['.W.B.', 'W.WB.', '.WB.B', 'WB...', 'B....'], 0]], [[['.B...', 'WWWBW', 'WW..B', '.W.WW', '..B..'], 'W', 2, 3], [['.B...', 'WWWBW', 'WW.W.', '.W.WW', '..B..'], 1]], [[['.W.', 'WBW', 'BWB'], 'B', 0, 2], [['.WB', 'WB.', 'BWB'], 1]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("move result case %d" % i, solve(args), 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
move result case 0[['...', '...', 'W..'], 0][['...', '...', 'W..'], 0]Passed
move result case 1[['.B.', 'B.B', '.BW'], 0]['illegal', 'suicide']Failed
move result case 2[['.B.', 'B.B', 'WB.'], 0]['illegal', 'suicide']Failed
move result case 3[['BB.', 'B.B', '.B.'], 0][['BB.', 'B.B', '.B.'], 0]Passed
move result case 4[['.BB', 'B.B', '.B.'], 0][['.BB', 'B.B', '.B.'], 0]Passed
move result case 5['illegal', 'occupied']['illegal', 'occupied']Passed
move result case 6[['B..W', 'B..W', 'B.WW', 'B..W'], 0][['B..W', 'B..W', 'B.WW', 'B..W'], 0]Passed
move result case 7[['WBW.', 'B.W.', 'WB.W', '..BW'], 0][['.BW.', 'B.W.', 'WB.W', '..BW'], 1]Failed

SHA-256 / 3d05ababccb3aacb017041852cc5f5d180b713df6be4ce8b97d967bb083a457e

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    board, color, r, c = x
    g = [list(row) for row in board]
    n = len(g)
    m = len(g[0])
    if g[r][c] != '.':
        return ['illegal', 'occupied']
    opp = 'W' if color == 'B' else 'B'
    g[r][c] = color
    def group(i, j):
        col = g[i][j]
        stones = {(i, j)}
        libs = set()
        stack = [(i, j)]
        while stack:
            a, b = stack.pop()
            for p, q in ((a, b + 1), (a + 1, b), (a, b - 1), (a - 1, b)):
                if 0 <= p < n and 0 <= q < m:
                    if g[p][q] == '.' or (p, q) == (r, c):
                        libs.add((p, q))
                    elif g[p][q] == col and (p, q) not in stones:
                        stones.add((p, q))
                        stack.append((p, q))
        return stones, libs
    captured = 0
    for p, q in ((r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)):
        if 0 <= p < n and 0 <= q < m and g[p][q] == opp:
            stones, libs = group(p, q)
            if not libs:
                for s, t in stones:
                    g[s][t] = '.'
                captured += len(stones)
    stones, libs = group(r, c)
    if not libs:
        return ['illegal', 'suicide']
    return [[''.join(row) for row in g], captured]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['...', '...', '...'], 'W', 2, 0], [['...', '...', 'W..'], 0]], [[['.B.', 'B.B', '.B.'], 'W', 2, 2], ['illegal', 'suicide']], [[['.B.', 'B.B', '.B.'], 'W', 2, 0], ['illegal', 'suicide']], [[['.B.', 'B.B', '.B.'], 'B', 0, 0], [['BB.', 'B.B', '.B.'], 0]], [[['.B.', 'B.B', '.B.'], 'B', 0, 2], [['.BB', 'B.B', '.B.'], 0]], [[['.B.', 'B.B', '.B.'], 'B', 0, 1], ['illegal', 'occupied']], [[['B..W', 'B..W', 'B..W', 'B..W'], 'W', 2, 2], [['B..W', 'B..W', 'B.WW', 'B..W'], 0]], [[['WBW.', '..W.', 'WB.W', '..BW'], 'B', 1, 0], [['.BW.', 'B.W.', 'WB.W', '..BW'], 1]]], [[[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 1, 1], [['B..W', 'BB.W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'W', 0, 2], [['B.WW', 'B..W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 0, 1], [['BB.W', 'B..W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 2, 0], ['illegal', 'occupied']], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'W', 2, 3], [['.BW.', '.BW.', '.BWW', '.BW.'], 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'W', 0, 0], [['WBW.', '.BW.', '.BW.', '.BW.'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 0, 1], ['illegal', 'suicide']], [[['.WB.', 'B.WB', 'W.BW'], 'B', 1, 1], [['.WB.', 'BB.B', 'W.BW'], 1]]], [[[['.BW.', '.BW.', '.BW.', '.BW.'], 'B', 2, 0], [['.BW.', '.BW.', 'BBW.', '.BW.'], 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'B', 0, 2], ['illegal', 'occupied']], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 0, 0], [['B.B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 1, 1], [['..B..', '.BB..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 4, 0], [['..B..', '..B..', 'BBBBB', 'WWWWW', 'B.W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 0, 1], [['.BB..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['...', '.BW', '..B', '.BW', 'BB.'], 'W', 4, 2], ['illegal', 'suicide']], [[['B.BW', 'WWBB', 'BB.W', '.BBB'], 'W', 0, 1], [['.WBW', 'WWBB', 'BB.W', '.BBB'], 1]]], [[[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 2, 1], ['illegal', 'suicide']], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 0, 1], ['illegal', 'suicide']], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 4, 3], [['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..WB.'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 2, 4], [['B.B.W', '.B.W.', 'B.BWB', 'BBW.W', '..W..'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 3, 2], ['illegal', 'occupied']], [[['W....', '.....', '.....', '.....', '....B'], 'W', 4, 1], [['W....', '.....', '.....', '.....', '.W..B'], 0]], [[['WB.BB', 'B.B.W', 'B.WBB', 'BWBW.'], 'W', 1, 3], ['illegal', 'suicide']], [[['.BW', 'BW.', 'W..'], 'B', 2, 1], [['.BW', 'BW.', '.B.'], 1]]], [[[['W....', '.....', '.....', '.....', '....B'], 'W', 3, 0], [['W....', '.....', '.....', 'W....', '....B'], 0]], [[['W....', '.....', '.....', '.....', '....B'], 'W', 4, 0], [['W....', '.....', '.....', '.....', 'W...B'], 0]], [[['W....', '.....', '.....', '.....', '....B'], 'B', 4, 4], ['illegal', 'occupied']], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'W', 0, 2], [['.WWB.', 'W.WB.', '.WB..', 'WB...', 'B....'], 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'W', 0, 0], [['WW.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'B', 2, 4], [['.W.B.', 'W.WB.', '.WB.B', 'WB...', 'B....'], 0]], [[['.B...', 'WWWBW', 'WW..B', '.W.WW', '..B..'], 'W', 2, 3], [['.B...', 'WWWBW', 'WW.W.', '.W.WW', '..B..'], 1]], [[['.W.', 'WBW', 'BWB'], 'B', 0, 2], [['.WB', 'WB.', 'BWB'], 1]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("move result case %d" % i, solve(args), 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
move result case 0[['...', '...', 'W..'], 0][['...', '...', 'W..'], 0]Passed
move result case 1['illegal', 'suicide']['illegal', 'suicide']Passed
move result case 2['illegal', 'suicide']['illegal', 'suicide']Passed
move result case 3[['BB.', 'B.B', '.B.'], 0][['BB.', 'B.B', '.B.'], 0]Passed
move result case 4[['.BB', 'B.B', '.B.'], 0][['.BB', 'B.B', '.B.'], 0]Passed
move result case 5['illegal', 'occupied']['illegal', 'occupied']Passed
move result case 6[['B..W', 'B..W', 'B.WW', 'B..W'], 0][['B..W', 'B..W', 'B.WW', 'B..W'], 0]Passed
move result case 7[['WBW.', 'B.W.', 'WB.W', '..BW'], 0][['.BW.', 'B.W.', 'WB.W', '..BW'], 1]Failed

SHA-256 / ae64f2869e212afd7716a7051e8db4ebec2501df93749a77420b1309077ea30d

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    board, color, r, c = x
    g = [list(row) for row in board]
    n = len(g)
    m = len(g[0])
    if g[r][c] != '.':
        return ['illegal', 'occupied']
    opp = 'W' if color == 'B' else 'B'
    g[r][c] = color
    def group(i, j):
        col = g[i][j]
        stones = {(i, j)}
        libs = set()
        stack = [(i, j)]
        while stack:
            a, b = stack.pop()
            for p, q in ((a, b + 1), (a + 1, b), (a, b - 1), (a - 1, b)):
                if 0 <= p < n and 0 <= q < m:
                    if g[p][q] == '.':
                        libs.add((p, q))
                    elif g[p][q] == col and (p, q) not in stones:
                        stones.add((p, q))
                        stack.append((p, q))
        return stones, libs
    captured = 0
    for p, q in ((r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)):
        if 0 <= p < n and 0 <= q < m and g[p][q] == opp:
            stones, libs = group(p, q)
            if not libs:
                for s, t in stones:
                    g[s][t] = '.'
                captured += len(stones)
    stones, libs = group(r, c)
    if not libs:
        return ['illegal', 'suicide']
    return [[''.join(row) for row in g], captured]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['...', '...', '...'], 'W', 2, 0], [['...', '...', 'W..'], 0]], [[['.B.', 'B.B', '.B.'], 'W', 2, 2], ['illegal', 'suicide']], [[['.B.', 'B.B', '.B.'], 'W', 2, 0], ['illegal', 'suicide']], [[['.B.', 'B.B', '.B.'], 'B', 0, 0], [['BB.', 'B.B', '.B.'], 0]], [[['.B.', 'B.B', '.B.'], 'B', 0, 2], [['.BB', 'B.B', '.B.'], 0]], [[['.B.', 'B.B', '.B.'], 'B', 0, 1], ['illegal', 'occupied']], [[['B..W', 'B..W', 'B..W', 'B..W'], 'W', 2, 2], [['B..W', 'B..W', 'B.WW', 'B..W'], 0]], [[['WBW.', '..W.', 'WB.W', '..BW'], 'B', 1, 0], [['.BW.', 'B.W.', 'WB.W', '..BW'], 1]]], [[[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 1, 1], [['B..W', 'BB.W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'W', 0, 2], [['B.WW', 'B..W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 0, 1], [['BB.W', 'B..W', 'B..W', 'B..W'], 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], 'B', 2, 0], ['illegal', 'occupied']], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'W', 2, 3], [['.BW.', '.BW.', '.BWW', '.BW.'], 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'W', 0, 0], [['WBW.', '.BW.', '.BW.', '.BW.'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 0, 1], ['illegal', 'suicide']], [[['.WB.', 'B.WB', 'W.BW'], 'B', 1, 1], [['.WB.', 'BB.B', 'W.BW'], 1]]], [[[['.BW.', '.BW.', '.BW.', '.BW.'], 'B', 2, 0], [['.BW.', '.BW.', 'BBW.', '.BW.'], 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], 'B', 0, 2], ['illegal', 'occupied']], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 0, 0], [['B.B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 1, 1], [['..B..', '.BB..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 4, 0], [['..B..', '..B..', 'BBBBB', 'WWWWW', 'B.W..'], 0]], [[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 'B', 0, 1], [['.BB..', '..B..', 'BBBBB', 'WWWWW', '..W..'], 0]], [[['...', '.BW', '..B', '.BW', 'BB.'], 'W', 4, 2], ['illegal', 'suicide']], [[['B.BW', 'WWBB', 'BB.W', '.BBB'], 'W', 0, 1], [['.WBW', 'WWBB', 'BB.W', '.BBB'], 1]]], [[[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 2, 1], ['illegal', 'suicide']], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'W', 0, 1], ['illegal', 'suicide']], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 4, 3], [['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..WB.'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 2, 4], [['B.B.W', '.B.W.', 'B.BWB', 'BBW.W', '..W..'], 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], 'B', 3, 2], ['illegal', 'occupied']], [[['W....', '.....', '.....', '.....', '....B'], 'W', 4, 1], [['W....', '.....', '.....', '.....', '.W..B'], 0]], [[['WB.BB', 'B.B.W', 'B.WBB', 'BWBW.'], 'W', 1, 3], ['illegal', 'suicide']], [[['.BW', 'BW.', 'W..'], 'B', 2, 1], [['.BW', 'BW.', '.B.'], 1]]], [[[['W....', '.....', '.....', '.....', '....B'], 'W', 3, 0], [['W....', '.....', '.....', 'W....', '....B'], 0]], [[['W....', '.....', '.....', '.....', '....B'], 'W', 4, 0], [['W....', '.....', '.....', '.....', 'W...B'], 0]], [[['W....', '.....', '.....', '.....', '....B'], 'B', 4, 4], ['illegal', 'occupied']], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'W', 0, 2], [['.WWB.', 'W.WB.', '.WB..', 'WB...', 'B....'], 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'W', 0, 0], [['WW.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], 'B', 2, 4], [['.W.B.', 'W.WB.', '.WB.B', 'WB...', 'B....'], 0]], [[['.B...', 'WWWBW', 'WW..B', '.W.WW', '..B..'], 'W', 2, 3], [['.B...', 'WWWBW', 'WW.W.', '.W.WW', '..B..'], 1]], [[['.W.', 'WBW', 'BWB'], 'B', 0, 2], [['.WB', 'WB.', 'BWB'], 1]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("move result case %d" % i, solve(args), 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
move result case 0[['...', '...', 'W..'], 0][['...', '...', 'W..'], 0]Passed
move result case 1['illegal', 'suicide']['illegal', 'suicide']Passed
move result case 2['illegal', 'suicide']['illegal', 'suicide']Passed
move result case 3[['BB.', 'B.B', '.B.'], 0][['BB.', 'B.B', '.B.'], 0]Passed
move result case 4[['.BB', 'B.B', '.B.'], 0][['.BB', 'B.B', '.B.'], 0]Passed
move result case 5['illegal', 'occupied']['illegal', 'occupied']Passed
move result case 6[['B..W', 'B..W', 'B.WW', 'B..W'], 0][['B..W', 'B..W', 'B.WW', 'B..W'], 0]Passed
move result case 7[['.BW.', 'B.W.', 'WB.W', '..BW'], 1][['.BW.', 'B.W.', 'WB.W', '..BW'], 1]Passed

SHA-256 / 98a5cdeb3098873642d054c2f94ff7daa42dc3efb4bbc050ebfb9a3d71acf11b

Verification & scope

Small rectangular toy boards given as strings of B, W and dot; a bounded teaching model of one scoring or bookkeeping rule, not a complete rules engine or server implementation. 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:16.575539+00:00.

Case digest / 92b42e039758d7c39c537993b07e7ac9f1ddf82a3ab366b9114b354606f6f5c5