FAILURE MAP
← Case archive

FA-82886 / Go territory scoring / Open access

Komi is omitted from the white territory score · case 01

White totals ignore compensation, so black wins close games it lost.

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

ROOT CAUSE

The white score is computed from territory and prisoners only.

VERIFIED REPAIR

Add komi_tenths to the white score in tenths.

Unsuccessful approach: Adding komi only after truncating it to whole points loses the half point.

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':
            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
    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']], [[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.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']]], [[[['WBW.', '.BWB', '.BW.', '.BW.', '.BW.'], [], 4, 2, 55], [0, 0, 4, 2, 'W+3.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', '.WBW', '.WBW', '..BW'], [[0, 3], [4, 2], [3, 0]], 0, 0, 60], [0, 0, 1, 1, 'W+6.0']]], [[[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.5']], [[['.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']]], [[[['.....BW', '....WBW', '.W...BW', '..W..BW', '...W.BW'], [[4, 5]], 0, 0, 65], [0, 0, 0, 1, 'W+7.5']], [[['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']], [[['...BWB.', '...BW..', '...BW..', '...BW..', '...BW..'], [[1, 4], [2, 4], [4, 3]], 2, 1, 5], [0, 0, 4, 2, 'B+1.5']]], [[[['....BW.', '....BW.', '.W..BW.', '...WBW.', '...WBWB', '....BW.'], [[5, 1]], 4, 3, 55], [0, 0, 4, 3, 'W+4.5']], [[['...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.B', '.B.'], [[0, 1], [2, 1], [0, 1]], 0, 1, 5], [7, 0, 0, 3, 'B+3.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, 'Jigo'][0, 0, 1, 1, 'W+5.5']Failed
territory score case 1[12, 0, 3, 2, 'B+13.0'][12, 0, 3, 2, 'B+12.5']Failed
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+1.0'][0, 0, 0, 1, 'W+7.5']Failed
territory score case 5[0, 0, 3, 3, 'Jigo'][0, 0, 3, 3, 'W+0.5']Failed
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, 'Jigo'][0, 0, 2, 2, 'W+5.5']Failed

SHA-256 / 5f1a746cc5dce089f3a2c98e31137ed50055e0fe3ec6820ea49393c6fdee3bb4

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
        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 + komi // 10) * 10
    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']], [[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.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']]], [[[['WBW.', '.BWB', '.BW.', '.BW.', '.BW.'], [], 4, 2, 55], [0, 0, 4, 2, 'W+3.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', '.WBW', '.WBW', '..BW'], [[0, 3], [4, 2], [3, 0]], 0, 0, 60], [0, 0, 1, 1, 'W+6.0']]], [[[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.5']], [[['.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']]], [[[['.....BW', '....WBW', '.W...BW', '..W..BW', '...W.BW'], [[4, 5]], 0, 0, 65], [0, 0, 0, 1, 'W+7.5']], [[['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']], [[['...BWB.', '...BW..', '...BW..', '...BW..', '...BW..'], [[1, 4], [2, 4], [4, 3]], 2, 1, 5], [0, 0, 4, 2, 'B+1.5']]], [[[['....BW.', '....BW.', '.W..BW.', '...WBW.', '...WBWB', '....BW.'], [[5, 1]], 4, 3, 55], [0, 0, 4, 3, 'W+4.5']], [[['...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.B', '.B.'], [[0, 1], [2, 1], [0, 1]], 0, 1, 5], [7, 0, 0, 3, 'B+3.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.0'][0, 0, 1, 1, 'W+5.5']Failed
territory score case 1[12, 0, 3, 2, 'B+13.0'][12, 0, 3, 2, 'B+12.5']Failed
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.0'][0, 0, 0, 1, 'W+7.5']Failed
territory score case 5[0, 0, 3, 3, 'Jigo'][0, 0, 3, 3, 'W+0.5']Failed
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.0'][0, 0, 2, 2, 'W+5.5']Failed

SHA-256 / fa626925d798a1a558551eafdcd49638af1b1f22f63b64eb9a150bbaca5efcfc

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']], [[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.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']]], [[[['WBW.', '.BWB', '.BW.', '.BW.', '.BW.'], [], 4, 2, 55], [0, 0, 4, 2, 'W+3.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', '.WBW', '.WBW', '..BW'], [[0, 3], [4, 2], [3, 0]], 0, 0, 60], [0, 0, 1, 1, 'W+6.0']]], [[[['..BW..', '..BW.B', '..BW..', '..BW..', '..BW..', '..BW..'], [[3, 3], [2, 3]], 1, 2, 5], [12, 0, 3, 2, 'B+12.5']], [[['.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']]], [[[['.....BW', '....WBW', '.W...BW', '..W..BW', '...W.BW'], [[4, 5]], 0, 0, 65], [0, 0, 0, 1, 'W+7.5']], [[['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']], [[['...BWB.', '...BW..', '...BW..', '...BW..', '...BW..'], [[1, 4], [2, 4], [4, 3]], 2, 1, 5], [0, 0, 4, 2, 'B+1.5']]], [[[['....BW.', '....BW.', '.W..BW.', '...WBW.', '...WBWB', '....BW.'], [[5, 1]], 4, 3, 55], [0, 0, 4, 3, 'W+4.5']], [[['...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.B', '.B.'], [[0, 1], [2, 1], [0, 1]], 0, 1, 5], [7, 0, 0, 3, 'B+3.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[12, 0, 3, 2, 'B+12.5'][12, 0, 3, 2, 'B+12.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 / aaf2323592049bc0d9d7de42143f9f15b557ca9ea912ede407f6b43156a240ac

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

Case digest / d5bda9db2ffb41a82f2691053df7aea245a47b34489ffef6880ee23636f63100