FAILURE MAP
← Case archive

FA-83161 / Go territory scoring / Open access

Shared liberties marked as seki are ignored · case 01

A region containing a marked shared liberty is still scored.

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

ROOT CAUSE

Only listed stones freeze regions; listed empty points are never examined.

VERIFIED REPAIR

Freeze a region when any of its own points is listed.

Unsuccessful approach: Checking only the region seed point misses listed points deeper in the region.

Case contract

Input [board, seki_points]. Empty regions bordered by exactly one colour are territory, unless the region touches a stone listed as seki or contains a listed empty point. Return [terr_b, terr_w].

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, seki = x
    n = len(board)
    m = len(board[0])
    sk = {tuple(p) for p in seki}
    tb = 0
    tw = 0
    seen = set()
    for r in range(n):
        for c in range(m):
            if board[r][c] != '.' or (r, c) in seen:
                continue
            seen.add((r, c))
            stack = [(r, c)]
            size = 0
            cols = set()
            frozen = False
            while stack:
                i, j = stack.pop()
                size += 1
                for p, q in ((i, j + 1), (i + 1, j), (i, j - 1), (i - 1, j)):
                    if not (0 <= p < n and 0 <= q < m):
                        continue
                    if board[p][q] == '.':
                        if (p, q) not in seen:
                            seen.add((p, q))
                            stack.append((p, q))
                    else:
                        cols.add(board[p][q])
                        if (p, q) in sk:
                            frozen = True
            if frozen or len(cols) != 1:
                continue
            if 'B' in cols:
                tb += size
            else:
                tw += size
    return [tb, tw]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['..WBW.', '...BW.', 'W.WBW.', 'W..BW.', '...BW.', '..WBW.'], []], [0, 6]], [[['....BW', '....BW', '....BW', '....BW', '....BW', '...WBW', 'W...BW'], []], [0, 0]], [[['...BW..', '.W.BW..', '...BW..', '..WBW..'], [[1, 1]]], [0, 8]], [[['...BW.', 'W..BW.', '...BWB', '.W.BW.', '...BW.'], [[4, 3]]], [0, 0]], [[['....BW', '....BW', '.W..BW', '....BW', '....BW'], [[1, 5], [2, 1]]], [0, 0]], [[['WBW.', '.BW.', 'WBW.', '.BW.', '.BW.', '.BW.'], [[2, 0], [4, 3]]], [0, 0]], [[['WBW.', '.BW.', 'WBW.', '.BW.', '.BW.', '.BW.'], [[2, 0], [4, 3]]], [0, 0]], [[['.WBW', '..BW', '.WBW', '..BW'], [[2, 3], [2, 0]]], [0, 0]]], [[[['.....BW', '.....BW', '.....BW', '.....BW', '.....BW', '.W...BW'], [[4, 6], [2, 6]]], [0, 0]], [[['..BWB', 'W.BW.', '..BW.', '..BW.'], [[3, 2]]], [0, 0]], [[['.W.BWB', '...BW.', '...BW.', '...BW.'], [[2, 4], [0, 5]]], [0, 0]], [[['.BW.B.', '.BW...', '.BW...', '.BW...'], [[1, 1], [0, 4], [3, 3]]], [0, 0]], [[['.WBW.', '..BW.', '..BW.', '..BW.'], []], [0, 4]], [[['....BW', '.W.WBW', '....BW', 'WW.WBW', '..W.BW', '.W..BW', '....BW'], [[2, 5], [1, 2]]], [0, 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], [[1, 2], [2, 0]]], [11, 1]], [[['BBBB', 'B..B', 'BBBB'], [[1, 2]]], [0, 0]]], [[[['..BWB.', '..BW..', '..BW..', '..BW..', '..BW..'], [[2, 2]]], [0, 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[0, 2], [3, 1], [0, 0]]], [0, 0]], [[['WW.BW.', '...BWB', '...BW.', '.W.BW.'], [[1, 5]]], [0, 0]], [[['..BW.', '..BW.', '..BW.', 'W.BWB', '..BWB', '..BW.', 'W.BW.'], [[3, 3], [0, 1]]], [0, 0]], [[['..BW..', '..BW..', '..BW..', '..BW..', '..BW..', '.WBW..'], [[2, 3]]], [0, 0]], [[['..BW.', '..BW.', '..BW.', 'WWBW.', '.WBW.'], []], [0, 6]], [[['B.BWW', '.BW..', 'WBWWW', '.BBBW'], [[1, 3]]], [1, 0]], [[['.WW..', 'BB.WW', 'WBWBW', '.WWWW', 'WWBBW'], [[3, 3], [0, 4]]], [0, 1]]], [[[['WBW..', '.BW..', '.BW..', '.BW..', '.BW.B', '.BW..'], [[4, 1]]], [0, 0]], [[['..BW.', '..BW.', '..BWB', '..BW.', 'W.BW.', '..BW.', '..BW.'], [[2, 3], [2, 4], [6, 1]]], [0, 0]], [[['....', '....', '....', '....'], [[0, 0]]], [0, 0]], [[['...', '...', '...'], []], [0, 0]], [[['.B.', 'B.B', '.B.'], [[1, 1]]], [4, 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], [[2, 3], [0, 0], [3, 2]]], [0, 0]], [[['...BB', 'B.B..', '.B.BB', '.B..B', '..B.B', 'B.B..'], [[1, 4]]], [15, 0]], [[['BBBB', 'B..B', 'BBBB'], [[1, 1]]], [0, 0]]], [[[['.B.', 'B.B', '.B.'], [[1, 1]]], [4, 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], []], [3, 5]], [[['W....', '.....', '.....', '.....', '....B'], [[0, 4]]], [0, 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], [[1, 2], [2, 0]]], [11, 1]], [[['BBBB', 'B..B', 'B..B', 'BBBB'], []], [4, 0]], [[['WWW', 'W.W', 'WWW'], [[0, 2]]], [0, 1]], [[['B.W', '...', 'W.B'], []], [0, 0]], [[['BW.', 'BW.', 'BW.'], [[2, 2]]], [0, 0]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("seki territory 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
seki territory case 0[0, 6][0, 6]Passed
seki territory case 1[0, 0][0, 0]Passed
seki territory case 2[0, 8][0, 8]Passed
seki territory case 3[0, 0][0, 0]Passed
seki territory case 4[0, 0][0, 0]Passed
seki territory case 5[0, 6][0, 0]Failed
seki territory case 6[0, 6][0, 0]Failed
seki territory case 7[0, 0][0, 0]Passed

SHA-256 / 0d77d128c059da3fc621f07831529d3d7dac7e0458a2c902f35ffafeeb27b270

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    board, seki = x
    n = len(board)
    m = len(board[0])
    sk = {tuple(p) for p in seki}
    tb = 0
    tw = 0
    seen = set()
    for r in range(n):
        for c in range(m):
            if board[r][c] != '.' or (r, c) in seen:
                continue
            seen.add((r, c))
            stack = [(r, c)]
            size = 0
            cols = set()
            frozen = False
            while stack:
                i, j = stack.pop()
                size += 1
                if (r, c) in sk:
                    frozen = True
                for p, q in ((i, j + 1), (i + 1, j), (i, j - 1), (i - 1, j)):
                    if not (0 <= p < n and 0 <= q < m):
                        continue
                    if board[p][q] == '.':
                        if (p, q) not in seen:
                            seen.add((p, q))
                            stack.append((p, q))
                    else:
                        cols.add(board[p][q])
                        if (p, q) in sk:
                            frozen = True
            if frozen or len(cols) != 1:
                continue
            if 'B' in cols:
                tb += size
            else:
                tw += size
    return [tb, tw]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['..WBW.', '...BW.', 'W.WBW.', 'W..BW.', '...BW.', '..WBW.'], []], [0, 6]], [[['....BW', '....BW', '....BW', '....BW', '....BW', '...WBW', 'W...BW'], []], [0, 0]], [[['...BW..', '.W.BW..', '...BW..', '..WBW..'], [[1, 1]]], [0, 8]], [[['...BW.', 'W..BW.', '...BWB', '.W.BW.', '...BW.'], [[4, 3]]], [0, 0]], [[['....BW', '....BW', '.W..BW', '....BW', '....BW'], [[1, 5], [2, 1]]], [0, 0]], [[['WBW.', '.BW.', 'WBW.', '.BW.', '.BW.', '.BW.'], [[2, 0], [4, 3]]], [0, 0]], [[['WBW.', '.BW.', 'WBW.', '.BW.', '.BW.', '.BW.'], [[2, 0], [4, 3]]], [0, 0]], [[['.WBW', '..BW', '.WBW', '..BW'], [[2, 3], [2, 0]]], [0, 0]]], [[[['.....BW', '.....BW', '.....BW', '.....BW', '.....BW', '.W...BW'], [[4, 6], [2, 6]]], [0, 0]], [[['..BWB', 'W.BW.', '..BW.', '..BW.'], [[3, 2]]], [0, 0]], [[['.W.BWB', '...BW.', '...BW.', '...BW.'], [[2, 4], [0, 5]]], [0, 0]], [[['.BW.B.', '.BW...', '.BW...', '.BW...'], [[1, 1], [0, 4], [3, 3]]], [0, 0]], [[['.WBW.', '..BW.', '..BW.', '..BW.'], []], [0, 4]], [[['....BW', '.W.WBW', '....BW', 'WW.WBW', '..W.BW', '.W..BW', '....BW'], [[2, 5], [1, 2]]], [0, 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], [[1, 2], [2, 0]]], [11, 1]], [[['BBBB', 'B..B', 'BBBB'], [[1, 2]]], [0, 0]]], [[[['..BWB.', '..BW..', '..BW..', '..BW..', '..BW..'], [[2, 2]]], [0, 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[0, 2], [3, 1], [0, 0]]], [0, 0]], [[['WW.BW.', '...BWB', '...BW.', '.W.BW.'], [[1, 5]]], [0, 0]], [[['..BW.', '..BW.', '..BW.', 'W.BWB', '..BWB', '..BW.', 'W.BW.'], [[3, 3], [0, 1]]], [0, 0]], [[['..BW..', '..BW..', '..BW..', '..BW..', '..BW..', '.WBW..'], [[2, 3]]], [0, 0]], [[['..BW.', '..BW.', '..BW.', 'WWBW.', '.WBW.'], []], [0, 6]], [[['B.BWW', '.BW..', 'WBWWW', '.BBBW'], [[1, 3]]], [1, 0]], [[['.WW..', 'BB.WW', 'WBWBW', '.WWWW', 'WWBBW'], [[3, 3], [0, 4]]], [0, 1]]], [[[['WBW..', '.BW..', '.BW..', '.BW..', '.BW.B', '.BW..'], [[4, 1]]], [0, 0]], [[['..BW.', '..BW.', '..BWB', '..BW.', 'W.BW.', '..BW.', '..BW.'], [[2, 3], [2, 4], [6, 1]]], [0, 0]], [[['....', '....', '....', '....'], [[0, 0]]], [0, 0]], [[['...', '...', '...'], []], [0, 0]], [[['.B.', 'B.B', '.B.'], [[1, 1]]], [4, 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], [[2, 3], [0, 0], [3, 2]]], [0, 0]], [[['...BB', 'B.B..', '.B.BB', '.B..B', '..B.B', 'B.B..'], [[1, 4]]], [15, 0]], [[['BBBB', 'B..B', 'BBBB'], [[1, 1]]], [0, 0]]], [[[['.B.', 'B.B', '.B.'], [[1, 1]]], [4, 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], []], [3, 5]], [[['W....', '.....', '.....', '.....', '....B'], [[0, 4]]], [0, 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], [[1, 2], [2, 0]]], [11, 1]], [[['BBBB', 'B..B', 'B..B', 'BBBB'], []], [4, 0]], [[['WWW', 'W.W', 'WWW'], [[0, 2]]], [0, 1]], [[['B.W', '...', 'W.B'], []], [0, 0]], [[['BW.', 'BW.', 'BW.'], [[2, 2]]], [0, 0]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("seki territory 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
seki territory case 0[0, 6][0, 6]Passed
seki territory case 1[0, 0][0, 0]Passed
seki territory case 2[0, 8][0, 8]Passed
seki territory case 3[0, 0][0, 0]Passed
seki territory case 4[0, 0][0, 0]Passed
seki territory case 5[0, 6][0, 0]Failed
seki territory case 6[0, 6][0, 0]Failed
seki territory case 7[0, 0][0, 0]Passed

SHA-256 / 1530f5c8313aab43878121e1738c7772dccaf88a89e205e725f3a2740dfb0bb9

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    board, seki = x
    n = len(board)
    m = len(board[0])
    sk = {tuple(p) for p in seki}
    tb = 0
    tw = 0
    seen = set()
    for r in range(n):
        for c in range(m):
            if board[r][c] != '.' or (r, c) in seen:
                continue
            seen.add((r, c))
            stack = [(r, c)]
            size = 0
            cols = set()
            frozen = False
            while stack:
                i, j = stack.pop()
                size += 1
                if (i, j) in sk:
                    frozen = True
                for p, q in ((i, j + 1), (i + 1, j), (i, j - 1), (i - 1, j)):
                    if not (0 <= p < n and 0 <= q < m):
                        continue
                    if board[p][q] == '.':
                        if (p, q) not in seen:
                            seen.add((p, q))
                            stack.append((p, q))
                    else:
                        cols.add(board[p][q])
                        if (p, q) in sk:
                            frozen = True
            if frozen or len(cols) != 1:
                continue
            if 'B' in cols:
                tb += size
            else:
                tw += size
    return [tb, tw]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['..WBW.', '...BW.', 'W.WBW.', 'W..BW.', '...BW.', '..WBW.'], []], [0, 6]], [[['....BW', '....BW', '....BW', '....BW', '....BW', '...WBW', 'W...BW'], []], [0, 0]], [[['...BW..', '.W.BW..', '...BW..', '..WBW..'], [[1, 1]]], [0, 8]], [[['...BW.', 'W..BW.', '...BWB', '.W.BW.', '...BW.'], [[4, 3]]], [0, 0]], [[['....BW', '....BW', '.W..BW', '....BW', '....BW'], [[1, 5], [2, 1]]], [0, 0]], [[['WBW.', '.BW.', 'WBW.', '.BW.', '.BW.', '.BW.'], [[2, 0], [4, 3]]], [0, 0]], [[['WBW.', '.BW.', 'WBW.', '.BW.', '.BW.', '.BW.'], [[2, 0], [4, 3]]], [0, 0]], [[['.WBW', '..BW', '.WBW', '..BW'], [[2, 3], [2, 0]]], [0, 0]]], [[[['.....BW', '.....BW', '.....BW', '.....BW', '.....BW', '.W...BW'], [[4, 6], [2, 6]]], [0, 0]], [[['..BWB', 'W.BW.', '..BW.', '..BW.'], [[3, 2]]], [0, 0]], [[['.W.BWB', '...BW.', '...BW.', '...BW.'], [[2, 4], [0, 5]]], [0, 0]], [[['.BW.B.', '.BW...', '.BW...', '.BW...'], [[1, 1], [0, 4], [3, 3]]], [0, 0]], [[['.WBW.', '..BW.', '..BW.', '..BW.'], []], [0, 4]], [[['....BW', '.W.WBW', '....BW', 'WW.WBW', '..W.BW', '.W..BW', '....BW'], [[2, 5], [1, 2]]], [0, 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], [[1, 2], [2, 0]]], [11, 1]], [[['BBBB', 'B..B', 'BBBB'], [[1, 2]]], [0, 0]]], [[[['..BWB.', '..BW..', '..BW..', '..BW..', '..BW..'], [[2, 2]]], [0, 0]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[0, 2], [3, 1], [0, 0]]], [0, 0]], [[['WW.BW.', '...BWB', '...BW.', '.W.BW.'], [[1, 5]]], [0, 0]], [[['..BW.', '..BW.', '..BW.', 'W.BWB', '..BWB', '..BW.', 'W.BW.'], [[3, 3], [0, 1]]], [0, 0]], [[['..BW..', '..BW..', '..BW..', '..BW..', '..BW..', '.WBW..'], [[2, 3]]], [0, 0]], [[['..BW.', '..BW.', '..BW.', 'WWBW.', '.WBW.'], []], [0, 6]], [[['B.BWW', '.BW..', 'WBWWW', '.BBBW'], [[1, 3]]], [1, 0]], [[['.WW..', 'BB.WW', 'WBWBW', '.WWWW', 'WWBBW'], [[3, 3], [0, 4]]], [0, 1]]], [[[['WBW..', '.BW..', '.BW..', '.BW..', '.BW.B', '.BW..'], [[4, 1]]], [0, 0]], [[['..BW.', '..BW.', '..BWB', '..BW.', 'W.BW.', '..BW.', '..BW.'], [[2, 3], [2, 4], [6, 1]]], [0, 0]], [[['....', '....', '....', '....'], [[0, 0]]], [0, 0]], [[['...', '...', '...'], []], [0, 0]], [[['.B.', 'B.B', '.B.'], [[1, 1]]], [4, 0]], [[['B..W', 'B..W', 'B..W', 'B..W'], [[2, 3], [0, 0], [3, 2]]], [0, 0]], [[['...BB', 'B.B..', '.B.BB', '.B..B', '..B.B', 'B.B..'], [[1, 4]]], [15, 0]], [[['BBBB', 'B..B', 'BBBB'], [[1, 1]]], [0, 0]]], [[[['.B.', 'B.B', '.B.'], [[1, 1]]], [4, 0]], [[['B.B.W', '.B.W.', 'B.BW.', 'BBW.W', '..W..'], []], [3, 5]], [[['W....', '.....', '.....', '.....', '....B'], [[0, 4]]], [0, 0]], [[['.W.B.', 'W.WB.', '.WB..', 'WB...', 'B....'], [[1, 2], [2, 0]]], [11, 1]], [[['BBBB', 'B..B', 'B..B', 'BBBB'], []], [4, 0]], [[['WWW', 'W.W', 'WWW'], [[0, 2]]], [0, 1]], [[['B.W', '...', 'W.B'], []], [0, 0]], [[['BW.', 'BW.', 'BW.'], [[2, 2]]], [0, 0]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("seki territory 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
seki territory case 0[0, 6][0, 6]Passed
seki territory case 1[0, 0][0, 0]Passed
seki territory case 2[0, 8][0, 8]Passed
seki territory case 3[0, 0][0, 0]Passed
seki territory case 4[0, 0][0, 0]Passed
seki territory case 5[0, 0][0, 0]Passed
seki territory case 6[0, 0][0, 0]Passed
seki territory case 7[0, 0][0, 0]Passed

SHA-256 / f88071e2a227e488c821517987a05ac06cb80134ca8a15940beb8ce66d384890

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

Case digest / 092f01cb750cf557866680c378e6316d609e1bf2523daebd60ba1431520a0ef2