FAILURE MAP
← Case archive

FA-82866 / Go territory scoring / Open access

Dead stones are credited as prisoners to their own colour · case 01

Marking a white group dead raises white prisoner count instead of black.

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

ROOT CAUSE

The removal loop increments the prisoner counter of the stone colour rather than the opponent.

VERIFIED REPAIR

A dead black stone is a white prisoner and a dead white stone is a black prisoner.

Unsuccessful approach: Using a bare else for white stones credits black a prisoner for duplicate or empty dead marks.

Case contract

Input [board, dead_points, prisoners_held_by_black, prisoners_held_by_white, komi_tenths]. Each listed dead stone is removed once and becomes a prisoner of its opponent (duplicates and empty points are ignored). Territory = empty regions (after removal) bordered by one colour only. Return [terr_b, terr_w, pris_b, pris_w, result] with result "B+x.y", "W+x.y" or "Jigo".

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, dead, capb, capw, komi = x
    g = [list(row) for row in board]
    pb = capb
    pw = capw
    for r, c in dead:
        if g[r][c] == 'B':
            pb += 1
        elif g[r][c] == 'W':
            pw += 1
        g[r][c] = '.'
    n = len(g)
    m = len(g[0])
    tb = 0
    tw = 0
    seen = set()
    for r in range(n):
        for c in range(m):
            if g[r][c] != '.' or (r, c) in seen:
                continue
            stack = [(r, c)]
            seen.add((r, c))
            size = 0
            owners = set()
            while stack:
                i, j = stack.pop()
                size += 1
                for p, q in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
                    if 0 <= p < n and 0 <= q < m:
                        if g[p][q] == '.':
                            if (p, q) not in seen:
                                seen.add((p, q))
                                stack.append((p, q))
                        else:
                            owners.add(g[p][q])
            if owners == {'B'}:
                tb += size
            elif owners == {'W'}:
                tw += size
    sb = (tb + pb) * 10
    sw = (tw + pw) * 10 + komi
    if sb > sw:
        res = 'B+%d.%d' % ((sb - sw) // 10, (sb - sw) % 10)
    elif sw > sb:
        res = 'W+%d.%d' % ((sw - sb) // 10, (sw - sb) % 10)
    else:
        res = 'Jigo'
    return [tb, tw, pb, pw, res]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['W...BW', '....BW', '....BW', '....BW'], [[1, 4], [1, 4]], 1, 0, 55], [0, 0, 1, 1, 'W+5.5']], [[['..WBW', '...BW', '...BW', 'W..BW'], [[0, 3], [3, 4], [3, 0]], 3, 0, 65], [0, 0, 5, 1, 'W+2.5']], [[['..BW.', '..BW.', '..BW.', '..BWB', '..BW.', '..BW.'], [[3, 2], [3, 2]], 1, 1, 0], [0, 0, 1, 2, 'W+1.0']], [[['.BW.B..', '.BW....', '.BW....', '.BW.B..', '.BW....', '.BW.B..', '.BW....'], [[4, 2], [2, 1], [6, 1]], 3, 0, 0], [0, 0, 4, 2, 'B+2.0']], [[['.....BW', '....WBW', '.W...BW', '..W..BW', '...W.BW'], [[4, 5]], 0, 0, 65], [0, 0, 0, 1, 'W+7.5']], [[['W..BW.', '..WBW.', 'W..BW.', '...BW.', '...BW.', '..WBW.'], [[1, 2], [5, 2], [0, 4]], 0, 3, 5], [0, 0, 3, 3, 'W+0.5']], [[['W.BW..', 'W.BW..', '..BW..', '.WBW..', '..BW..', '..BW..', '..BW..'], [[3, 2]], 3, 3, 0], [0, 14, 3, 4, 'W+15.0']], [[['W.BW..', '..BW..', '..BW..', '.WBW.B', 'W.BW..', '..BW..', '.WBW..'], [[1, 2], [3, 2], [5, 0]], 2, 0, 55], [0, 0, 2, 2, 'W+5.5']]], [[[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.5']], [[['....BW.', '....BW.', '.W..BW.', '...WBW.', '...WBWB', '....BW.'], [[5, 1]], 4, 3, 55], [0, 0, 4, 3, 'W+4.5']], [[['...BW..', '...BWB.', '...BW..', '...BW..', '...BW..'], [[0, 3], [0, 3], [4, 2]], 0, 1, 5], [0, 0, 0, 2, 'W+2.5']], [[['..BW', '.WBW', '..BW', '..BW'], [[3, 2], [0, 3], [3, 2]], 0, 4, 60], [0, 0, 1, 5, 'W+10.0']], [[['....BWB', '....BW.', '....BW.', '....BWB'], [[2, 5]], 4, 1, 0], [16, 0, 5, 1, 'B+20.0']], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[3, 1]], 1, 1, 60], [0, 4, 1, 2, 'W+11.0']], [[['..BW', '..BW', '..BW', '..BW', '..BW', '..BW', '..BW'], [[0, 2], [6, 2], [5, 3]], 1, 1, 0], [0, 0, 2, 3, 'W+1.0']], [[['.BW.', '.BW.', '.BW.', '.BW.', '.BWB', '.BWB', 'WBW.'], [[4, 1], [4, 1]], 0, 2, 0], [0, 0, 0, 3, 'W+3.0']]], [[[['.BW.B..', '.BW....', '.BW....', '.BW.B..', '.BW....', '.BW.B..', '.BW....'], [[4, 2], [2, 1], [6, 1]], 3, 0, 0], [0, 0, 4, 2, 'B+2.0']], [[['.BW.', '.BW.', '.BW.', '.BW.', '.BWB', '.BWB', 'WBW.'], [[4, 1], [4, 1]], 0, 2, 0], [0, 0, 0, 3, 'W+3.0']], [[['W.BW...', '.WBW...', '..BW..B', '..BW...', '.WBWB..', '..BW...'], [[4, 4], [4, 0]], 3, 1, 55], [0, 0, 3, 2, 'W+4.5']], [[['WBW.B..', '.BW.B..', '.BWBB..', 'WBW....', '.BW.B..', '.BW.B..', 'WBW....'], [[2, 2], [6, 2], [2, 2]], 0, 1, 5], [0, 0, 2, 1, 'B+0.5']], [[['..BW', '.WBW', '..BW', '..BW', '..BW', '..BW'], [[1, 3], [3, 3], [1, 3]], 4, 1, 55], [0, 0, 6, 1, 'W+0.5']], [[['.....BW', '.....BW', '.....BW', '.W...BW', '.....BW'], [[3, 5], [1, 5], [3, 6], [3, 5]], 0, 3, 5], [0, 0, 1, 5, 'W+4.5']], [[['..BW..', '..BW.B', '..BW..', '..BW.B', 'W.BW..', '..BW..', '..BW..'], [[2, 2], [2, 2]], 2, 1, 0], [0, 0, 2, 2, 'Jigo']], [[['W..BWB', '...BW.', '...BW.', '...BWB'], [[0, 5]], 4, 0, 5], [0, 0, 4, 1, 'B+2.5']]], [[[['W.BW..', 'W.BW..', '..BW..', '.WBW..', '..BW..', '..BW..', '..BW..'], [[3, 2]], 3, 3, 0], [0, 14, 3, 4, 'W+15.0']], [[['W...BW', '....BW', '....BW', '....BW', '....BW'], [[0, 0], [0, 4]], 0, 0, 5], [0, 0, 1, 1, 'W+0.5']], [[['..BWB', '..BWB', 'W.BW.', '..BW.', '..BW.'], [], 2, 4, 55], [0, 0, 2, 4, 'W+7.5']], [[['.BW..', 'WBWB.', '.BW..', 'WBW..', '.BW..'], [[2, 1], [3, 1]], 2, 2, 0], [0, 0, 2, 4, 'W+2.0']], [[['..BW...', '..BWB..', '.WBW...', '..BW.B.', 'W.BW...', '..BW...', '..BWBB.'], [[6, 2]], 2, 0, 65], [0, 0, 2, 1, 'W+5.5']], [[['.BW..', '.BW..', '.BW..', '.BW..', '.BWB.', '.BW..', 'WBW..'], [[0, 2], [0, 2]], 0, 2, 0], [0, 0, 1, 2, 'W+1.0']], [[['W..BW', '...BW', '...BW', '...BW', '...BW', '...BW', '...BW'], [], 2, 1, 65], [0, 0, 2, 1, 'W+5.5']], [[['B..W', 'B..W', 'B..W', 'B..W'], [[2, 0], [2, 3], [0, 3], [2, 0]], 4, 2, 5], [0, 0, 6, 3, 'B+2.5']]], [[[['....BWB', '....BW.', '....BW.', '....BWB'], [[2, 5]], 4, 1, 0], [16, 0, 5, 1, 'B+20.0']], [[['...BWBB', 'W..BW..', '..WBW..', '...BW..', '...BW.B', '...BWB.'], [[3, 4]], 2, 4, 5], [1, 0, 3, 4, 'W+0.5']], [[['.BW...', '.BWB..', '.BW...', '.BW...', '.BW...', '.BW...', '.BW.BB'], [[1, 1], [1, 1], [2, 4]], 4, 1, 60], [0, 0, 4, 2, 'W+4.0']], [[['...BW', '...BW', 'W..BW', '...BW', '.W.BW', '...BW', 'W..BW'], [], 1, 1, 0], [0, 0, 1, 1, 'Jigo']], [[['....BW.', 'W..WBW.', 'W...BW.', '..W.BW.', '....BW.', '....BW.'], [[3, 4], [4, 4], [1, 0], [2, 3]], 1, 3, 65], [0, 6, 2, 5, 'W+15.5']], [[['....', '....', '....', '....'], [], 4, 0, 60], [0, 0, 4, 0, 'W+2.0']], [[['...', '...', '...'], [], 3, 2, 65], [0, 0, 3, 2, 'W+5.5']], [[['B.B...', 'WB...B', '......', '......', '.B.B.B', 'BB....'], [[5, 1], [5, 0], [5, 1]], 2, 3, 5], [1, 0, 2, 5, 'W+2.5']]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("territory score 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
territory score case 0[0, 0, 2, 0, 'W+3.5'][0, 0, 1, 1, 'W+5.5']Failed
territory score case 1[0, 0, 4, 2, 'W+4.5'][0, 0, 5, 1, 'W+2.5']Failed
territory score case 2[0, 0, 2, 1, 'B+1.0'][0, 0, 1, 2, 'W+1.0']Failed
territory score case 3[0, 0, 5, 1, 'B+4.0'][0, 0, 4, 2, 'B+2.0']Failed
territory score case 4[0, 0, 1, 0, 'W+5.5'][0, 0, 0, 1, 'W+7.5']Failed
territory score case 5[0, 0, 0, 6, 'W+6.5'][0, 0, 3, 3, 'W+0.5']Failed
territory score case 6[0, 14, 4, 3, 'W+13.0'][0, 14, 3, 4, 'W+15.0']Failed
territory score case 7[0, 0, 4, 0, 'W+1.5'][0, 0, 2, 2, 'W+5.5']Failed

SHA-256 / 5fe396ae4a71923b4e28df03dcf3f5228b4a051a6a5eb667408d5500c18f07c3

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    board, dead, capb, capw, komi = x
    g = [list(row) for row in board]
    pb = capb
    pw = capw
    for r, c in dead:
        if g[r][c] == 'B':
            pw += 1
        else:
            pb += 1
        g[r][c] = '.'
    n = len(g)
    m = len(g[0])
    tb = 0
    tw = 0
    seen = set()
    for r in range(n):
        for c in range(m):
            if g[r][c] != '.' or (r, c) in seen:
                continue
            stack = [(r, c)]
            seen.add((r, c))
            size = 0
            owners = set()
            while stack:
                i, j = stack.pop()
                size += 1
                for p, q in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
                    if 0 <= p < n and 0 <= q < m:
                        if g[p][q] == '.':
                            if (p, q) not in seen:
                                seen.add((p, q))
                                stack.append((p, q))
                        else:
                            owners.add(g[p][q])
            if owners == {'B'}:
                tb += size
            elif owners == {'W'}:
                tw += size
    sb = (tb + pb) * 10
    sw = (tw + pw) * 10 + komi
    if sb > sw:
        res = 'B+%d.%d' % ((sb - sw) // 10, (sb - sw) % 10)
    elif sw > sb:
        res = 'W+%d.%d' % ((sw - sb) // 10, (sw - sb) % 10)
    else:
        res = 'Jigo'
    return [tb, tw, pb, pw, res]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['W...BW', '....BW', '....BW', '....BW'], [[1, 4], [1, 4]], 1, 0, 55], [0, 0, 1, 1, 'W+5.5']], [[['..WBW', '...BW', '...BW', 'W..BW'], [[0, 3], [3, 4], [3, 0]], 3, 0, 65], [0, 0, 5, 1, 'W+2.5']], [[['..BW.', '..BW.', '..BW.', '..BWB', '..BW.', '..BW.'], [[3, 2], [3, 2]], 1, 1, 0], [0, 0, 1, 2, 'W+1.0']], [[['.BW.B..', '.BW....', '.BW....', '.BW.B..', '.BW....', '.BW.B..', '.BW....'], [[4, 2], [2, 1], [6, 1]], 3, 0, 0], [0, 0, 4, 2, 'B+2.0']], [[['.....BW', '....WBW', '.W...BW', '..W..BW', '...W.BW'], [[4, 5]], 0, 0, 65], [0, 0, 0, 1, 'W+7.5']], [[['W..BW.', '..WBW.', 'W..BW.', '...BW.', '...BW.', '..WBW.'], [[1, 2], [5, 2], [0, 4]], 0, 3, 5], [0, 0, 3, 3, 'W+0.5']], [[['W.BW..', 'W.BW..', '..BW..', '.WBW..', '..BW..', '..BW..', '..BW..'], [[3, 2]], 3, 3, 0], [0, 14, 3, 4, 'W+15.0']], [[['W.BW..', '..BW..', '..BW..', '.WBW.B', 'W.BW..', '..BW..', '.WBW..'], [[1, 2], [3, 2], [5, 0]], 2, 0, 55], [0, 0, 2, 2, 'W+5.5']]], [[[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.5']], [[['....BW.', '....BW.', '.W..BW.', '...WBW.', '...WBWB', '....BW.'], [[5, 1]], 4, 3, 55], [0, 0, 4, 3, 'W+4.5']], [[['...BW..', '...BWB.', '...BW..', '...BW..', '...BW..'], [[0, 3], [0, 3], [4, 2]], 0, 1, 5], [0, 0, 0, 2, 'W+2.5']], [[['..BW', '.WBW', '..BW', '..BW'], [[3, 2], [0, 3], [3, 2]], 0, 4, 60], [0, 0, 1, 5, 'W+10.0']], [[['....BWB', '....BW.', '....BW.', '....BWB'], [[2, 5]], 4, 1, 0], [16, 0, 5, 1, 'B+20.0']], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[3, 1]], 1, 1, 60], [0, 4, 1, 2, 'W+11.0']], [[['..BW', '..BW', '..BW', '..BW', '..BW', '..BW', '..BW'], [[0, 2], [6, 2], [5, 3]], 1, 1, 0], [0, 0, 2, 3, 'W+1.0']], [[['.BW.', '.BW.', '.BW.', '.BW.', '.BWB', '.BWB', 'WBW.'], [[4, 1], [4, 1]], 0, 2, 0], [0, 0, 0, 3, 'W+3.0']]], [[[['.BW.B..', '.BW....', '.BW....', '.BW.B..', '.BW....', '.BW.B..', '.BW....'], [[4, 2], [2, 1], [6, 1]], 3, 0, 0], [0, 0, 4, 2, 'B+2.0']], [[['.BW.', '.BW.', '.BW.', '.BW.', '.BWB', '.BWB', 'WBW.'], [[4, 1], [4, 1]], 0, 2, 0], [0, 0, 0, 3, 'W+3.0']], [[['W.BW...', '.WBW...', '..BW..B', '..BW...', '.WBWB..', '..BW...'], [[4, 4], [4, 0]], 3, 1, 55], [0, 0, 3, 2, 'W+4.5']], [[['WBW.B..', '.BW.B..', '.BWBB..', 'WBW....', '.BW.B..', '.BW.B..', 'WBW....'], [[2, 2], [6, 2], [2, 2]], 0, 1, 5], [0, 0, 2, 1, 'B+0.5']], [[['..BW', '.WBW', '..BW', '..BW', '..BW', '..BW'], [[1, 3], [3, 3], [1, 3]], 4, 1, 55], [0, 0, 6, 1, 'W+0.5']], [[['.....BW', '.....BW', '.....BW', '.W...BW', '.....BW'], [[3, 5], [1, 5], [3, 6], [3, 5]], 0, 3, 5], [0, 0, 1, 5, 'W+4.5']], [[['..BW..', '..BW.B', '..BW..', '..BW.B', 'W.BW..', '..BW..', '..BW..'], [[2, 2], [2, 2]], 2, 1, 0], [0, 0, 2, 2, 'Jigo']], [[['W..BWB', '...BW.', '...BW.', '...BWB'], [[0, 5]], 4, 0, 5], [0, 0, 4, 1, 'B+2.5']]], [[[['W.BW..', 'W.BW..', '..BW..', '.WBW..', '..BW..', '..BW..', '..BW..'], [[3, 2]], 3, 3, 0], [0, 14, 3, 4, 'W+15.0']], [[['W...BW', '....BW', '....BW', '....BW', '....BW'], [[0, 0], [0, 4]], 0, 0, 5], [0, 0, 1, 1, 'W+0.5']], [[['..BWB', '..BWB', 'W.BW.', '..BW.', '..BW.'], [], 2, 4, 55], [0, 0, 2, 4, 'W+7.5']], [[['.BW..', 'WBWB.', '.BW..', 'WBW..', '.BW..'], [[2, 1], [3, 1]], 2, 2, 0], [0, 0, 2, 4, 'W+2.0']], [[['..BW...', '..BWB..', '.WBW...', '..BW.B.', 'W.BW...', '..BW...', '..BWBB.'], [[6, 2]], 2, 0, 65], [0, 0, 2, 1, 'W+5.5']], [[['.BW..', '.BW..', '.BW..', '.BW..', '.BWB.', '.BW..', 'WBW..'], [[0, 2], [0, 2]], 0, 2, 0], [0, 0, 1, 2, 'W+1.0']], [[['W..BW', '...BW', '...BW', '...BW', '...BW', '...BW', '...BW'], [], 2, 1, 65], [0, 0, 2, 1, 'W+5.5']], [[['B..W', 'B..W', 'B..W', 'B..W'], [[2, 0], [2, 3], [0, 3], [2, 0]], 4, 2, 5], [0, 0, 6, 3, 'B+2.5']]], [[[['....BWB', '....BW.', '....BW.', '....BWB'], [[2, 5]], 4, 1, 0], [16, 0, 5, 1, 'B+20.0']], [[['...BWBB', 'W..BW..', '..WBW..', '...BW..', '...BW.B', '...BWB.'], [[3, 4]], 2, 4, 5], [1, 0, 3, 4, 'W+0.5']], [[['.BW...', '.BWB..', '.BW...', '.BW...', '.BW...', '.BW...', '.BW.BB'], [[1, 1], [1, 1], [2, 4]], 4, 1, 60], [0, 0, 4, 2, 'W+4.0']], [[['...BW', '...BW', 'W..BW', '...BW', '.W.BW', '...BW', 'W..BW'], [], 1, 1, 0], [0, 0, 1, 1, 'Jigo']], [[['....BW.', 'W..WBW.', 'W...BW.', '..W.BW.', '....BW.', '....BW.'], [[3, 4], [4, 4], [1, 0], [2, 3]], 1, 3, 65], [0, 6, 2, 5, 'W+15.5']], [[['....', '....', '....', '....'], [], 4, 0, 60], [0, 0, 4, 0, 'W+2.0']], [[['...', '...', '...'], [], 3, 2, 65], [0, 0, 3, 2, 'W+5.5']], [[['B.B...', 'WB...B', '......', '......', '.B.B.B', 'BB....'], [[5, 1], [5, 0], [5, 1]], 2, 3, 5], [1, 0, 2, 5, 'W+2.5']]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("territory score 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
territory score case 0[0, 0, 2, 1, 'W+4.5'][0, 0, 1, 1, 'W+5.5']Failed
territory score case 1[0, 0, 5, 1, 'W+2.5'][0, 0, 5, 1, 'W+2.5']Passed
territory score case 2[0, 0, 2, 2, 'Jigo'][0, 0, 1, 2, 'W+1.0']Failed
territory score case 3[0, 0, 4, 2, 'B+2.0'][0, 0, 4, 2, 'B+2.0']Passed
territory score case 4[0, 0, 0, 1, 'W+7.5'][0, 0, 0, 1, 'W+7.5']Passed
territory score case 5[0, 0, 3, 3, 'W+0.5'][0, 0, 3, 3, 'W+0.5']Passed
territory score case 6[0, 14, 3, 4, 'W+15.0'][0, 14, 3, 4, 'W+15.0']Passed
territory score case 7[0, 0, 3, 2, 'W+4.5'][0, 0, 2, 2, 'W+5.5']Failed

SHA-256 / 48ebb5cdabf96dd14e30e36acaaf5347e24a85e21f24d7948c3376f82da77544

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    board, dead, capb, capw, komi = x
    g = [list(row) for row in board]
    pb = capb
    pw = capw
    for r, c in dead:
        if g[r][c] == 'B':
            pw += 1
        elif g[r][c] == 'W':
            pb += 1
        g[r][c] = '.'
    n = len(g)
    m = len(g[0])
    tb = 0
    tw = 0
    seen = set()
    for r in range(n):
        for c in range(m):
            if g[r][c] != '.' or (r, c) in seen:
                continue
            stack = [(r, c)]
            seen.add((r, c))
            size = 0
            owners = set()
            while stack:
                i, j = stack.pop()
                size += 1
                for p, q in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
                    if 0 <= p < n and 0 <= q < m:
                        if g[p][q] == '.':
                            if (p, q) not in seen:
                                seen.add((p, q))
                                stack.append((p, q))
                        else:
                            owners.add(g[p][q])
            if owners == {'B'}:
                tb += size
            elif owners == {'W'}:
                tw += size
    sb = (tb + pb) * 10
    sw = (tw + pw) * 10 + komi
    if sb > sw:
        res = 'B+%d.%d' % ((sb - sw) // 10, (sb - sw) % 10)
    elif sw > sb:
        res = 'W+%d.%d' % ((sw - sb) // 10, (sw - sb) % 10)
    else:
        res = 'Jigo'
    return [tb, tw, pb, pw, res]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['W...BW', '....BW', '....BW', '....BW'], [[1, 4], [1, 4]], 1, 0, 55], [0, 0, 1, 1, 'W+5.5']], [[['..WBW', '...BW', '...BW', 'W..BW'], [[0, 3], [3, 4], [3, 0]], 3, 0, 65], [0, 0, 5, 1, 'W+2.5']], [[['..BW.', '..BW.', '..BW.', '..BWB', '..BW.', '..BW.'], [[3, 2], [3, 2]], 1, 1, 0], [0, 0, 1, 2, 'W+1.0']], [[['.BW.B..', '.BW....', '.BW....', '.BW.B..', '.BW....', '.BW.B..', '.BW....'], [[4, 2], [2, 1], [6, 1]], 3, 0, 0], [0, 0, 4, 2, 'B+2.0']], [[['.....BW', '....WBW', '.W...BW', '..W..BW', '...W.BW'], [[4, 5]], 0, 0, 65], [0, 0, 0, 1, 'W+7.5']], [[['W..BW.', '..WBW.', 'W..BW.', '...BW.', '...BW.', '..WBW.'], [[1, 2], [5, 2], [0, 4]], 0, 3, 5], [0, 0, 3, 3, 'W+0.5']], [[['W.BW..', 'W.BW..', '..BW..', '.WBW..', '..BW..', '..BW..', '..BW..'], [[3, 2]], 3, 3, 0], [0, 14, 3, 4, 'W+15.0']], [[['W.BW..', '..BW..', '..BW..', '.WBW.B', 'W.BW..', '..BW..', '.WBW..'], [[1, 2], [3, 2], [5, 0]], 2, 0, 55], [0, 0, 2, 2, 'W+5.5']]], [[[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.5']], [[['....BW.', '....BW.', '.W..BW.', '...WBW.', '...WBWB', '....BW.'], [[5, 1]], 4, 3, 55], [0, 0, 4, 3, 'W+4.5']], [[['...BW..', '...BWB.', '...BW..', '...BW..', '...BW..'], [[0, 3], [0, 3], [4, 2]], 0, 1, 5], [0, 0, 0, 2, 'W+2.5']], [[['..BW', '.WBW', '..BW', '..BW'], [[3, 2], [0, 3], [3, 2]], 0, 4, 60], [0, 0, 1, 5, 'W+10.0']], [[['....BWB', '....BW.', '....BW.', '....BWB'], [[2, 5]], 4, 1, 0], [16, 0, 5, 1, 'B+20.0']], [[['.BW.', '.BW.', '.BW.', '.BW.'], [[3, 1]], 1, 1, 60], [0, 4, 1, 2, 'W+11.0']], [[['..BW', '..BW', '..BW', '..BW', '..BW', '..BW', '..BW'], [[0, 2], [6, 2], [5, 3]], 1, 1, 0], [0, 0, 2, 3, 'W+1.0']], [[['.BW.', '.BW.', '.BW.', '.BW.', '.BWB', '.BWB', 'WBW.'], [[4, 1], [4, 1]], 0, 2, 0], [0, 0, 0, 3, 'W+3.0']]], [[[['.BW.B..', '.BW....', '.BW....', '.BW.B..', '.BW....', '.BW.B..', '.BW....'], [[4, 2], [2, 1], [6, 1]], 3, 0, 0], [0, 0, 4, 2, 'B+2.0']], [[['.BW.', '.BW.', '.BW.', '.BW.', '.BWB', '.BWB', 'WBW.'], [[4, 1], [4, 1]], 0, 2, 0], [0, 0, 0, 3, 'W+3.0']], [[['W.BW...', '.WBW...', '..BW..B', '..BW...', '.WBWB..', '..BW...'], [[4, 4], [4, 0]], 3, 1, 55], [0, 0, 3, 2, 'W+4.5']], [[['WBW.B..', '.BW.B..', '.BWBB..', 'WBW....', '.BW.B..', '.BW.B..', 'WBW....'], [[2, 2], [6, 2], [2, 2]], 0, 1, 5], [0, 0, 2, 1, 'B+0.5']], [[['..BW', '.WBW', '..BW', '..BW', '..BW', '..BW'], [[1, 3], [3, 3], [1, 3]], 4, 1, 55], [0, 0, 6, 1, 'W+0.5']], [[['.....BW', '.....BW', '.....BW', '.W...BW', '.....BW'], [[3, 5], [1, 5], [3, 6], [3, 5]], 0, 3, 5], [0, 0, 1, 5, 'W+4.5']], [[['..BW..', '..BW.B', '..BW..', '..BW.B', 'W.BW..', '..BW..', '..BW..'], [[2, 2], [2, 2]], 2, 1, 0], [0, 0, 2, 2, 'Jigo']], [[['W..BWB', '...BW.', '...BW.', '...BWB'], [[0, 5]], 4, 0, 5], [0, 0, 4, 1, 'B+2.5']]], [[[['W.BW..', 'W.BW..', '..BW..', '.WBW..', '..BW..', '..BW..', '..BW..'], [[3, 2]], 3, 3, 0], [0, 14, 3, 4, 'W+15.0']], [[['W...BW', '....BW', '....BW', '....BW', '....BW'], [[0, 0], [0, 4]], 0, 0, 5], [0, 0, 1, 1, 'W+0.5']], [[['..BWB', '..BWB', 'W.BW.', '..BW.', '..BW.'], [], 2, 4, 55], [0, 0, 2, 4, 'W+7.5']], [[['.BW..', 'WBWB.', '.BW..', 'WBW..', '.BW..'], [[2, 1], [3, 1]], 2, 2, 0], [0, 0, 2, 4, 'W+2.0']], [[['..BW...', '..BWB..', '.WBW...', '..BW.B.', 'W.BW...', '..BW...', '..BWBB.'], [[6, 2]], 2, 0, 65], [0, 0, 2, 1, 'W+5.5']], [[['.BW..', '.BW..', '.BW..', '.BW..', '.BWB.', '.BW..', 'WBW..'], [[0, 2], [0, 2]], 0, 2, 0], [0, 0, 1, 2, 'W+1.0']], [[['W..BW', '...BW', '...BW', '...BW', '...BW', '...BW', '...BW'], [], 2, 1, 65], [0, 0, 2, 1, 'W+5.5']], [[['B..W', 'B..W', 'B..W', 'B..W'], [[2, 0], [2, 3], [0, 3], [2, 0]], 4, 2, 5], [0, 0, 6, 3, 'B+2.5']]], [[[['....BWB', '....BW.', '....BW.', '....BWB'], [[2, 5]], 4, 1, 0], [16, 0, 5, 1, 'B+20.0']], [[['...BWBB', 'W..BW..', '..WBW..', '...BW..', '...BW.B', '...BWB.'], [[3, 4]], 2, 4, 5], [1, 0, 3, 4, 'W+0.5']], [[['.BW...', '.BWB..', '.BW...', '.BW...', '.BW...', '.BW...', '.BW.BB'], [[1, 1], [1, 1], [2, 4]], 4, 1, 60], [0, 0, 4, 2, 'W+4.0']], [[['...BW', '...BW', 'W..BW', '...BW', '.W.BW', '...BW', 'W..BW'], [], 1, 1, 0], [0, 0, 1, 1, 'Jigo']], [[['....BW.', 'W..WBW.', 'W...BW.', '..W.BW.', '....BW.', '....BW.'], [[3, 4], [4, 4], [1, 0], [2, 3]], 1, 3, 65], [0, 6, 2, 5, 'W+15.5']], [[['....', '....', '....', '....'], [], 4, 0, 60], [0, 0, 4, 0, 'W+2.0']], [[['...', '...', '...'], [], 3, 2, 65], [0, 0, 3, 2, 'W+5.5']], [[['B.B...', 'WB...B', '......', '......', '.B.B.B', 'BB....'], [[5, 1], [5, 0], [5, 1]], 2, 3, 5], [1, 0, 2, 5, 'W+2.5']]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("territory score 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
territory score case 0[0, 0, 1, 1, 'W+5.5'][0, 0, 1, 1, 'W+5.5']Passed
territory score case 1[0, 0, 5, 1, 'W+2.5'][0, 0, 5, 1, 'W+2.5']Passed
territory score case 2[0, 0, 1, 2, 'W+1.0'][0, 0, 1, 2, 'W+1.0']Passed
territory score case 3[0, 0, 4, 2, 'B+2.0'][0, 0, 4, 2, 'B+2.0']Passed
territory score case 4[0, 0, 0, 1, 'W+7.5'][0, 0, 0, 1, 'W+7.5']Passed
territory score case 5[0, 0, 3, 3, 'W+0.5'][0, 0, 3, 3, 'W+0.5']Passed
territory score case 6[0, 14, 3, 4, 'W+15.0'][0, 14, 3, 4, 'W+15.0']Passed
territory score case 7[0, 0, 2, 2, 'W+5.5'][0, 0, 2, 2, 'W+5.5']Passed

SHA-256 / 0365910bb99af659ecf629350e63a54b8cceba7fe105653806236b75b95e6a96

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

Case digest / 9071c2e0cfb2ab4206eaf5c3986f5938fc74a2d434564adef373e74e9773a57c