FAILURE MAP
← Case archive

FA-83336 / Go territory scoring / Open access

Dead stones are also credited as prisoners under area scoring · case 01

Removing a dead stone gives the capturer an extra point on top of the recovered area.

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

ROOT CAUSE

Territory-scoring prisoner credit is mixed into the area total.

THE FAILURE

Territory-scoring prisoner credit is mixed into the area total.

Unsuccessful approach: Subtracting dead stones from their owner as well still double counts the swing.

Case contract

Input [board, dead_points, komi_tenths]. Remove dead stones (no prisoners in area scoring), then area = remaining stones + regions bordered by one colour. Return [black, white, margin_tenths].

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, komi = x
    dd = {tuple(p) for p in dead}
    bonus = {'B': 0, 'W': 0}
    for r, c in dd:
        if board[r][c] != '.':
            bonus['W' if board[r][c] == 'B' else 'B'] += 1
    g = [[('.' if (r, c) in dd else ch) for c, ch in enumerate(row)] for r, row in enumerate(board)]
    n = len(g)
    m = len(g[0])
    area = {'B': 0, 'W': 0}
    for row in g:
        for ch in row:
            if ch != '.':
                area[ch] += 1
    seen = set()
    for r in range(n):
        for c in range(m):
            if g[r][c] != '.' or (r, c) in seen:
                continue
            seen.add((r, c))
            todo = [(r, c)]
            cnt = 0
            edge = set()
            while todo:
                i, j = todo.pop()
                cnt += 1
                for p, q in ((i, j + 1), (i, j - 1), (i + 1, j), (i - 1, j)):
                    if 0 <= p < n and 0 <= q < m:
                        if g[p][q] == '.':
                            if (p, q) not in seen:
                                seen.add((p, q))
                                todo.append((p, q))
                        else:
                            edge.add(g[p][q])
            if len(edge) == 1:
                area[edge.pop()] += cnt
    area['B'] += bonus['B']
    area['W'] += bonus['W']
    return [area['B'], area['W'], (area['B'] - area['W']) * 10 - komi]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['.BW..', 'WBW..', 'WBW..', '.BW.B', 'WBW..', 'WBW.B'], [[2, 1]], 0], [7, 10, -30]], [[['.BW...', '.BW...', '.BW...', 'WBW...', 'WBW...'], [[4, 2]], 0], [5, 6, -10]], [[['...W.BW', '.....BW', 'W....BW', '.....BW', '..W..BW'], [[1, 6], [2, 6]], 0], [5, 6, -10]], [[['.BWB', '.BW.', '.BWB', '.BW.', '.BWB'], [], 0], [13, 5, 80]], [[['.W.BW..', '...BW..', '...BWB.', '...BW..'], [[1, 4], [3, 4]], 0], [5, 3, 20]], [[['.BWBB.', '.BW...', '.BW..B', '.BW...', '.BW...', '.BW..B', '.BW.B.'], [[3, 1]], 5], [12, 7, 45]], [[['..W..BW', '.....BW', '..W..BW', '.....BW', '.W...BW', '...W.BW'], [], 0], [6, 10, -40]], [[['.BW..', '.BW..', '.BWB.', '.BWB.', 'WBW..'], [[4, 2]], 75], [7, 5, -55]]], [[[['.BW....', '.BWB...', '.BW.B..', 'WBW.B..', '.BW.B..', '.BW....'], [[5, 2]], 0], [10, 6, 40]], [[['..BWB', '..BW.', '..BWB', 'W.BW.', '..BW.'], [[4, 2], [3, 3]], 75], [6, 5, -65]], [[['.BW.', '.BW.', '.BW.', '.BW.', 'WBW.', '.BW.', 'WBW.'], [[2, 2], [6, 2]], 75], [7, 7, -75]], [[['..BW', '..BW', 'W.BW', '..BW'], [[0, 2], [2, 3]], 75], [3, 4, -85]], [[['.W.BW.', '...BW.', '...BW.', '...BW.', '.W.BW.', '...BW.'], [[4, 3], [0, 4]], 0], [5, 7, -20]], [[['W..BW.', '...BW.', '...BWB', '...BW.'], [[3, 4], [0, 3]], 75], [4, 4, -75]], [[['.WBW...', '..BW...', '..BW...', 'W.BW...', '..BW...'], [[0, 3], [4, 2]], 75], [4, 6, -95]], [[['..BW', 'W.BW', '..BW', 'W.BW', '..BW'], [], 75], [5, 7, -95]]], [[[['...W.BW', '.....BW', 'W....BW', '.....BW', '..W..BW'], [[1, 6], [2, 6]], 0], [5, 6, -10]], [[['..BW...', '..BW...', '..BW...', '..BW...', '..BWB..', '.WBW...'], [[5, 3]], 5], [7, 6, 5]], [[['...BW', '.W.BW', 'W..BW', '..WBW'], [[3, 4], [0, 4]], 5], [4, 5, -15]], [[['..BW..B', '..BW...', '..BW...', '..BW...'], [], 75], [13, 4, 15]], [[['..BW', '..BW', '.WBW', '..BW', '..BW', '..BW'], [[1, 2]], 5], [5, 7, -25]], [[['W.BW.', '..BW.', 'W.BW.', '..BW.', '..BW.', '..BW.', '.WBW.'], [[6, 1]], 5], [7, 16, -95]], [[['..BW.', '..BW.', '..BW.', '..BW.', '..BW.', '..BW.'], [], 5], [18, 12, 55]], [[['..BW.', '.WBW.', '..BW.', '.WBW.'], [[2, 2], [3, 2]], 5], [2, 10, -85]]], [[[['.BW..', '.BW..', '.BWB.', '.BWB.', 'WBW..'], [[4, 2]], 75], [7, 5, -55]], [[['.WBWB', '..BW.', '..BW.', '..BW.', '..BW.'], [], 0], [6, 6, 0]], [[['...BW.B', '...BWBB', '...BWBB', '...BW..', '...BW..'], [[3, 3], [0, 4]], 5], [9, 4, 45]], [[['.W..BW.', '..W.BW.', '....BW.', '..W.BW.', '...WBW.', '....BW.', '...WBW.'], [[2, 4], [3, 5]], 75], [6, 11, -125]], [[['...BW', '..WBW', '...BW', '..WBW', '...BW'], [[2, 3]], 75], [4, 7, -105]], [[['.WBW', '..BW', '..BW', '..BW', '..BW', '..BW'], [[2, 2], [4, 2]], 5], [4, 7, -35]], [[['WBW..', '.BW..', '.BW..', '.BWBB'], [[1, 2]], 0], [6, 4, 20]], [[['...WBW.', '....BW.', '....BW.', 'W...BW.', '....BW.', '..W.BW.'], [[1, 4]], 5], [5, 15, -105]]], [[[['..BW', '..BW', 'W.BW', '..BW'], [[0, 2], [2, 3]], 75], [3, 4, -85]], [[['...WBW.', '....BW.', '....BW.', 'W...BW.', '....BW.', '..W.BW.'], [[1, 4]], 5], [5, 15, -105]], [[['...BW', 'W..BW', '..WBW', '...BW', '..WBW', '...BW'], [[5, 3]], 0], [5, 9, -40]], [[['..BW.', '..BW.', '..BW.', '..BW.', 'W.BW.', '..BW.', '..BW.'], [], 5], [7, 15, -85]], [[['.BWB', '.BW.', '.BW.', '.BW.'], [], 0], [9, 4, 50]], [[['....', '....', '....', '....'], [], 75], [0, 0, -75]], [[['...', '...', '...'], [], 5], [0, 0, -5]], [[['.B.', 'B.B', '.B.'], [[2, 1], [1, 2]], 0], [9, 0, 90]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("area with dead 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
area with dead case 0[7, 11, -40][7, 10, -30]Failed
area with dead case 1[6, 6, 0][5, 6, -10]Failed
area with dead case 2[7, 6, 10][5, 6, -10]Failed
area with dead case 3[13, 5, 80][13, 5, 80]Passed
area with dead case 4[7, 3, 40][5, 3, 20]Failed
area with dead case 5[12, 8, 35][12, 7, 45]Failed
area with dead case 6[6, 10, -40][6, 10, -40]Passed
area with dead case 7[8, 5, -45][7, 5, -55]Failed

SHA-256 / 17ed8ea7a723c2cb5418530bafe37fb7fcfa298f2240ba31d7ecfeae463ba8f8

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    board, dead, komi = x
    dd = {tuple(p) for p in dead}
    g = [[('.' if (r, c) in dd else ch) for c, ch in enumerate(row)] for r, row in enumerate(board)]
    n = len(g)
    m = len(g[0])
    area = {'B': 0, 'W': 0}
    for row in g:
        for ch in row:
            if ch != '.':
                area[ch] += 1
    seen = set()
    for r in range(n):
        for c in range(m):
            if g[r][c] != '.' or (r, c) in seen:
                continue
            seen.add((r, c))
            todo = [(r, c)]
            cnt = 0
            edge = set()
            while todo:
                i, j = todo.pop()
                cnt += 1
                for p, q in ((i, j + 1), (i, j - 1), (i + 1, j), (i - 1, j)):
                    if 0 <= p < n and 0 <= q < m:
                        if g[p][q] == '.':
                            if (p, q) not in seen:
                                seen.add((p, q))
                                todo.append((p, q))
                        else:
                            edge.add(g[p][q])
            if len(edge) == 1:
                area[edge.pop()] += cnt
    for r, c in dd:
        if board[r][c] != '.':
            area[board[r][c]] -= 1
    return [area['B'], area['W'], (area['B'] - area['W']) * 10 - komi]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['.BW..', 'WBW..', 'WBW..', '.BW.B', 'WBW..', 'WBW.B'], [[2, 1]], 0], [7, 10, -30]], [[['.BW...', '.BW...', '.BW...', 'WBW...', 'WBW...'], [[4, 2]], 0], [5, 6, -10]], [[['...W.BW', '.....BW', 'W....BW', '.....BW', '..W..BW'], [[1, 6], [2, 6]], 0], [5, 6, -10]], [[['.BWB', '.BW.', '.BWB', '.BW.', '.BWB'], [], 0], [13, 5, 80]], [[['.W.BW..', '...BW..', '...BWB.', '...BW..'], [[1, 4], [3, 4]], 0], [5, 3, 20]], [[['.BWBB.', '.BW...', '.BW..B', '.BW...', '.BW...', '.BW..B', '.BW.B.'], [[3, 1]], 5], [12, 7, 45]], [[['..W..BW', '.....BW', '..W..BW', '.....BW', '.W...BW', '...W.BW'], [], 0], [6, 10, -40]], [[['.BW..', '.BW..', '.BWB.', '.BWB.', 'WBW..'], [[4, 2]], 75], [7, 5, -55]]], [[[['.BW....', '.BWB...', '.BW.B..', 'WBW.B..', '.BW.B..', '.BW....'], [[5, 2]], 0], [10, 6, 40]], [[['..BWB', '..BW.', '..BWB', 'W.BW.', '..BW.'], [[4, 2], [3, 3]], 75], [6, 5, -65]], [[['.BW.', '.BW.', '.BW.', '.BW.', 'WBW.', '.BW.', 'WBW.'], [[2, 2], [6, 2]], 75], [7, 7, -75]], [[['..BW', '..BW', 'W.BW', '..BW'], [[0, 2], [2, 3]], 75], [3, 4, -85]], [[['.W.BW.', '...BW.', '...BW.', '...BW.', '.W.BW.', '...BW.'], [[4, 3], [0, 4]], 0], [5, 7, -20]], [[['W..BW.', '...BW.', '...BWB', '...BW.'], [[3, 4], [0, 3]], 75], [4, 4, -75]], [[['.WBW...', '..BW...', '..BW...', 'W.BW...', '..BW...'], [[0, 3], [4, 2]], 75], [4, 6, -95]], [[['..BW', 'W.BW', '..BW', 'W.BW', '..BW'], [], 75], [5, 7, -95]]], [[[['...W.BW', '.....BW', 'W....BW', '.....BW', '..W..BW'], [[1, 6], [2, 6]], 0], [5, 6, -10]], [[['..BW...', '..BW...', '..BW...', '..BW...', '..BWB..', '.WBW...'], [[5, 3]], 5], [7, 6, 5]], [[['...BW', '.W.BW', 'W..BW', '..WBW'], [[3, 4], [0, 4]], 5], [4, 5, -15]], [[['..BW..B', '..BW...', '..BW...', '..BW...'], [], 75], [13, 4, 15]], [[['..BW', '..BW', '.WBW', '..BW', '..BW', '..BW'], [[1, 2]], 5], [5, 7, -25]], [[['W.BW.', '..BW.', 'W.BW.', '..BW.', '..BW.', '..BW.', '.WBW.'], [[6, 1]], 5], [7, 16, -95]], [[['..BW.', '..BW.', '..BW.', '..BW.', '..BW.', '..BW.'], [], 5], [18, 12, 55]], [[['..BW.', '.WBW.', '..BW.', '.WBW.'], [[2, 2], [3, 2]], 5], [2, 10, -85]]], [[[['.BW..', '.BW..', '.BWB.', '.BWB.', 'WBW..'], [[4, 2]], 75], [7, 5, -55]], [[['.WBWB', '..BW.', '..BW.', '..BW.', '..BW.'], [], 0], [6, 6, 0]], [[['...BW.B', '...BWBB', '...BWBB', '...BW..', '...BW..'], [[3, 3], [0, 4]], 5], [9, 4, 45]], [[['.W..BW.', '..W.BW.', '....BW.', '..W.BW.', '...WBW.', '....BW.', '...WBW.'], [[2, 4], [3, 5]], 75], [6, 11, -125]], [[['...BW', '..WBW', '...BW', '..WBW', '...BW'], [[2, 3]], 75], [4, 7, -105]], [[['.WBW', '..BW', '..BW', '..BW', '..BW', '..BW'], [[2, 2], [4, 2]], 5], [4, 7, -35]], [[['WBW..', '.BW..', '.BW..', '.BWBB'], [[1, 2]], 0], [6, 4, 20]], [[['...WBW.', '....BW.', '....BW.', 'W...BW.', '....BW.', '..W.BW.'], [[1, 4]], 5], [5, 15, -105]]], [[[['..BW', '..BW', 'W.BW', '..BW'], [[0, 2], [2, 3]], 75], [3, 4, -85]], [[['...WBW.', '....BW.', '....BW.', 'W...BW.', '....BW.', '..W.BW.'], [[1, 4]], 5], [5, 15, -105]], [[['...BW', 'W..BW', '..WBW', '...BW', '..WBW', '...BW'], [[5, 3]], 0], [5, 9, -40]], [[['..BW.', '..BW.', '..BW.', '..BW.', 'W.BW.', '..BW.', '..BW.'], [], 5], [7, 15, -85]], [[['.BWB', '.BW.', '.BW.', '.BW.'], [], 0], [9, 4, 50]], [[['....', '....', '....', '....'], [], 75], [0, 0, -75]], [[['...', '...', '...'], [], 5], [0, 0, -5]], [[['.B.', 'B.B', '.B.'], [[2, 1], [1, 2]], 0], [9, 0, 90]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("area with dead 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
area with dead case 0[6, 10, -40][7, 10, -30]Failed
area with dead case 1[5, 5, 0][5, 6, -10]Failed
area with dead case 2[5, 4, 10][5, 6, -10]Failed
area with dead case 3[13, 5, 80][13, 5, 80]Passed
area with dead case 4[5, 1, 40][5, 3, 20]Failed
area with dead case 5[11, 7, 35][12, 7, 45]Failed
area with dead case 6[6, 10, -40][6, 10, -40]Passed
area with dead case 7[7, 4, -45][7, 5, -55]Failed

SHA-256 / 1869c234364d7135bf9296a7a65ec450d87e31bc1d4b82d29df2f1d0bff3eae2

HELD IN THE MEMBER ARCHIVE

The verified repair and its recorded checks are member-only.

This mechanism has 8 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.

Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.

Member access is invitation-based. Sign in with your invited account to inspect the repair.

Sign in to the archive ↗

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

Case digest / 21a85f92675f99ac0e85fab488f5c4dfafc1a09d7be31f828bc784a8fab3e15c