FAILURE MAP
← Case archive

FA-83151 / Go territory scoring / Open access

Seki contact is forgotten while flooding a region · case 01

Regions touching seki stones are still counted if the last processed point does not touch them.

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

ROOT CAUSE

The frozen flag is reset for every popped point.

VERIFIED REPAIR

Initialise the flag once per region and only ever set it.

Unsuccessful approach: Detecting seki contact only from the region seed misses contacts elsewhere 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()
                frozen = False
                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 = [[[[['..BW..', '..BW..', '..BW..', '..BW..'], [[1, 2], [3, 2], [3, 0]]], [0, 8]], [[['....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'], [[2, 3], [2, 0]]], [0, 0]], [[['..B.', '.B..', '.BB.'], [[0, 2]]], [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]], [[['..BW.', '..BW.', '..BW.', '..BW.', '.WBW.'], [[0, 0]]], [0, 5]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[2, 2], [1, 0]]], [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]], [[['.BW.', '.BWB', '.BW.', '.BWB', '.BW.'], [[4, 3]]], [5, 0]], [[['WBW..', '.BW..', '.BW..', '.BW..', '.BW.B', '.BW..'], [[4, 1]]], [0, 0]]], [[[['.BW..', '.BW..', '.BW..', '.BW..'], [[1, 1], [0, 2], [3, 3]]], [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]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[2, 2], [1, 0]]], [0, 0]], [[['.....', '.BBB.', '.B.B.', '.BBB.', '.....'], [[1, 2], [3, 2], [4, 0]]], [0, 0]]], [[[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], [[3, 3]]], [8, 2]], [[['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]], [[['..W', '.W.', 'W..'], [[0, 2]]], [0, 0]], [[['B.BWW', '.BW..', 'WBWWW', '.BBBW'], [[1, 3]]], [1, 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[8, 8][0, 8]Failed
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, 0][0, 0]Passed
seki territory case 7[4, 0][0, 0]Failed

SHA-256 / 8fc0b2aba1081560eb86118b08caaa7ecd7f4100fb8f16d1d8d51601cc2a56dd

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 (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 and (i, j) == (r, c):
                            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 = [[[[['..BW..', '..BW..', '..BW..', '..BW..'], [[1, 2], [3, 2], [3, 0]]], [0, 8]], [[['....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'], [[2, 3], [2, 0]]], [0, 0]], [[['..B.', '.B..', '.BB.'], [[0, 2]]], [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]], [[['..BW.', '..BW.', '..BW.', '..BW.', '.WBW.'], [[0, 0]]], [0, 5]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[2, 2], [1, 0]]], [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]], [[['.BW.', '.BWB', '.BW.', '.BWB', '.BW.'], [[4, 3]]], [5, 0]], [[['WBW..', '.BW..', '.BW..', '.BW..', '.BW.B', '.BW..'], [[4, 1]]], [0, 0]]], [[[['.BW..', '.BW..', '.BW..', '.BW..'], [[1, 1], [0, 2], [3, 3]]], [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]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[2, 2], [1, 0]]], [0, 0]], [[['.....', '.BBB.', '.B.B.', '.BBB.', '.....'], [[1, 2], [3, 2], [4, 0]]], [0, 0]]], [[[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], [[3, 3]]], [8, 2]], [[['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]], [[['..W', '.W.', 'W..'], [[0, 2]]], [0, 0]], [[['B.BWW', '.BW..', 'WBWWW', '.BBBW'], [[1, 3]]], [1, 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, 8][0, 8]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[4, 0][0, 0]Failed

SHA-256 / f349e6209c4d18acf3f37657545b2e877520c797da4c838dabb8fbcbfb92f964

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 = [[[[['..BW..', '..BW..', '..BW..', '..BW..'], [[1, 2], [3, 2], [3, 0]]], [0, 8]], [[['....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'], [[2, 3], [2, 0]]], [0, 0]], [[['..B.', '.B..', '.BB.'], [[0, 2]]], [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]], [[['..BW.', '..BW.', '..BW.', '..BW.', '.WBW.'], [[0, 0]]], [0, 5]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[2, 2], [1, 0]]], [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]], [[['.BW.', '.BWB', '.BW.', '.BWB', '.BW.'], [[4, 3]]], [5, 0]], [[['WBW..', '.BW..', '.BW..', '.BW..', '.BW.B', '.BW..'], [[4, 1]]], [0, 0]]], [[[['.BW..', '.BW..', '.BW..', '.BW..'], [[1, 1], [0, 2], [3, 3]]], [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]], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[2, 2], [1, 0]]], [0, 0]], [[['.....', '.BBB.', '.B.B.', '.BBB.', '.....'], [[1, 2], [3, 2], [4, 0]]], [0, 0]]], [[[['..B..', '..B..', 'BBBBB', 'WWWWW', '..W..'], [[3, 3]]], [8, 2]], [[['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]], [[['..W', '.W.', 'W..'], [[0, 2]]], [0, 0]], [[['B.BWW', '.BW..', 'WBWWW', '.BBBW'], [[1, 3]]], [1, 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, 8][0, 8]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 / 1272bde1b9e00ab361300f9bf0700ee806284462273544310e99467779961fa0

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

Case digest / 95f98bb00d6b87707d2c46471361d2be860663a6fb14283a6db7799725f2102b