FA-83021 / Go territory scoring / Open access
SGF draw value 0 is not recognised · case 01
Records with RE[0] or RE[Jigo] are classified as unknown.
ROOT CAUSE
Only the literal word Draw is treated as a draw.
VERIFIED REPAIR
Accept "0", "Draw" and "Jigo".
Unsuccessful approach: Adding Jigo but not 0 still misses the most common SGF draw encoding.
Case contract
Input an SGF RE string. "0", "Draw", "Jigo" -> ["draw", 0, "score"]; "Void" -> ["void", None, "none"]; C+R/Resign, C+T/Time, C+F/Forfeit (case-insensitive) -> [C, None, reason]; "C+" -> [C, None, "unknown"]; C+digits with optional one-digit fraction -> [C, tenths, "score"]; else unknown.
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):
s = x.strip()
if s == 'Draw':
return ['draw', 0, 'score']
if s == 'Void':
return ['void', None, 'none']
if len(s) < 2 or s[0] not in 'BW' or s[1] != '+':
return ['unknown', None, 'none']
who = s[0]
rest = s[2:]
if rest == '':
return [who, None, 'unknown']
word = rest.lower()
for key, reason in (('r', 'resign'), ('t', 'time'), ('f', 'forfeit')):
if word in (key, reason):
return [who, None, reason]
if '.' in rest:
whole, _, frac = rest.partition('.')
else:
whole, frac = rest, '0'
if not whole.isdigit() or not frac.isdigit() or len(frac) != 1:
return ['unknown', None, 'none']
return [who, int(whole) * 10 + int(frac), 'score']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['B+3.5', ['B', 35, 'score']], ['W+0.5', ['W', 5, 'score']], ['W+12', ['W', 120, 'score']], ['B+2.3', ['B', 23, 'score']], ['W+8.2', ['W', 82, 'score']], ['B+1.1', ['B', 11, 'score']], ['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']]], [['W+', ['W', None, 'unknown']], ['0', ['draw', 0, 'score']], ['Draw', ['draw', 0, 'score']], ['Jigo', ['draw', 0, 'score']], ['Void', ['void', None, 'none']], ['?', ['unknown', None, 'none']], [' B+R ', ['B', None, 'resign']], ['B+3.5 ', ['B', 35, 'score']]], [['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']], [' W+7', ['W', 70, 'score']], ['X+3', ['unknown', None, 'none']], ['B-3', ['unknown', None, 'none']], ['B+3.25', ['unknown', None, 'none']], ['W+3.0', ['W', 30, 'score']], ['B+6.0', ['B', 60, 'score']]], [['0', ['draw', 0, 'score']], ['Jigo', ['draw', 0, 'score']], ['W+Timeout', ['unknown', None, 'none']], ['B+r', ['B', None, 'resign']], ['W+t', ['W', None, 'time']], ['B+100.5', ['B', 1005, 'score']], ['W+4.1', ['W', 41, 'score']], ['W+.5', ['unknown', None, 'none']]], [['B+R', ['B', None, 'resign']], ['W+Resign', ['W', None, 'resign']], ['B+resign', ['B', None, 'resign']], ['W+T', ['W', None, 'time']], ['B+Time', ['B', None, 'time']], ['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']], ['B+7.', ['unknown', None, 'none']]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("result 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| result case 0 | ['B', 35, 'score'] | ['B', 35, 'score'] | Passed |
| result case 1 | ['W', 5, 'score'] | ['W', 5, 'score'] | Passed |
| result case 2 | ['W', 120, 'score'] | ['W', 120, 'score'] | Passed |
| result case 3 | ['B', 23, 'score'] | ['B', 23, 'score'] | Passed |
| result case 4 | ['W', 82, 'score'] | ['W', 82, 'score'] | Passed |
| result case 5 | ['B', 11, 'score'] | ['B', 11, 'score'] | Passed |
| result case 6 | ['unknown', None, 'none'] | ['draw', 0, 'score'] | Failed |
| result case 7 | ['unknown', None, 'none'] | ['draw', 0, 'score'] | Failed |
SHA-256 / bd5b18d0d0d9ffefccf56e8608d9968115c0848242bc40e2bba074ee2ec52b11
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
s = x.strip()
if s in ('Draw', 'Jigo'):
return ['draw', 0, 'score']
if s == 'Void':
return ['void', None, 'none']
if len(s) < 2 or s[0] not in 'BW' or s[1] != '+':
return ['unknown', None, 'none']
who = s[0]
rest = s[2:]
if rest == '':
return [who, None, 'unknown']
word = rest.lower()
for key, reason in (('r', 'resign'), ('t', 'time'), ('f', 'forfeit')):
if word in (key, reason):
return [who, None, reason]
if '.' in rest:
whole, _, frac = rest.partition('.')
else:
whole, frac = rest, '0'
if not whole.isdigit() or not frac.isdigit() or len(frac) != 1:
return ['unknown', None, 'none']
return [who, int(whole) * 10 + int(frac), 'score']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['B+3.5', ['B', 35, 'score']], ['W+0.5', ['W', 5, 'score']], ['W+12', ['W', 120, 'score']], ['B+2.3', ['B', 23, 'score']], ['W+8.2', ['W', 82, 'score']], ['B+1.1', ['B', 11, 'score']], ['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']]], [['W+', ['W', None, 'unknown']], ['0', ['draw', 0, 'score']], ['Draw', ['draw', 0, 'score']], ['Jigo', ['draw', 0, 'score']], ['Void', ['void', None, 'none']], ['?', ['unknown', None, 'none']], [' B+R ', ['B', None, 'resign']], ['B+3.5 ', ['B', 35, 'score']]], [['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']], [' W+7', ['W', 70, 'score']], ['X+3', ['unknown', None, 'none']], ['B-3', ['unknown', None, 'none']], ['B+3.25', ['unknown', None, 'none']], ['W+3.0', ['W', 30, 'score']], ['B+6.0', ['B', 60, 'score']]], [['0', ['draw', 0, 'score']], ['Jigo', ['draw', 0, 'score']], ['W+Timeout', ['unknown', None, 'none']], ['B+r', ['B', None, 'resign']], ['W+t', ['W', None, 'time']], ['B+100.5', ['B', 1005, 'score']], ['W+4.1', ['W', 41, 'score']], ['W+.5', ['unknown', None, 'none']]], [['B+R', ['B', None, 'resign']], ['W+Resign', ['W', None, 'resign']], ['B+resign', ['B', None, 'resign']], ['W+T', ['W', None, 'time']], ['B+Time', ['B', None, 'time']], ['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']], ['B+7.', ['unknown', None, 'none']]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("result 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| result case 0 | ['B', 35, 'score'] | ['B', 35, 'score'] | Passed |
| result case 1 | ['W', 5, 'score'] | ['W', 5, 'score'] | Passed |
| result case 2 | ['W', 120, 'score'] | ['W', 120, 'score'] | Passed |
| result case 3 | ['B', 23, 'score'] | ['B', 23, 'score'] | Passed |
| result case 4 | ['W', 82, 'score'] | ['W', 82, 'score'] | Passed |
| result case 5 | ['B', 11, 'score'] | ['B', 11, 'score'] | Passed |
| result case 6 | ['unknown', None, 'none'] | ['draw', 0, 'score'] | Failed |
| result case 7 | ['unknown', None, 'none'] | ['draw', 0, 'score'] | Failed |
SHA-256 / fa55bdc75d080631a95a304451f62142f3360af26f9893fb1a6f7e51ccd9fe3a
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
s = x.strip()
if s in ('0', 'Draw', 'Jigo'):
return ['draw', 0, 'score']
if s == 'Void':
return ['void', None, 'none']
if len(s) < 2 or s[0] not in 'BW' or s[1] != '+':
return ['unknown', None, 'none']
who = s[0]
rest = s[2:]
if rest == '':
return [who, None, 'unknown']
word = rest.lower()
for key, reason in (('r', 'resign'), ('t', 'time'), ('f', 'forfeit')):
if word in (key, reason):
return [who, None, reason]
if '.' in rest:
whole, _, frac = rest.partition('.')
else:
whole, frac = rest, '0'
if not whole.isdigit() or not frac.isdigit() or len(frac) != 1:
return ['unknown', None, 'none']
return [who, int(whole) * 10 + int(frac), 'score']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['B+3.5', ['B', 35, 'score']], ['W+0.5', ['W', 5, 'score']], ['W+12', ['W', 120, 'score']], ['B+2.3', ['B', 23, 'score']], ['W+8.2', ['W', 82, 'score']], ['B+1.1', ['B', 11, 'score']], ['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']]], [['W+', ['W', None, 'unknown']], ['0', ['draw', 0, 'score']], ['Draw', ['draw', 0, 'score']], ['Jigo', ['draw', 0, 'score']], ['Void', ['void', None, 'none']], ['?', ['unknown', None, 'none']], [' B+R ', ['B', None, 'resign']], ['B+3.5 ', ['B', 35, 'score']]], [['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']], [' W+7', ['W', 70, 'score']], ['X+3', ['unknown', None, 'none']], ['B-3', ['unknown', None, 'none']], ['B+3.25', ['unknown', None, 'none']], ['W+3.0', ['W', 30, 'score']], ['B+6.0', ['B', 60, 'score']]], [['0', ['draw', 0, 'score']], ['Jigo', ['draw', 0, 'score']], ['W+Timeout', ['unknown', None, 'none']], ['B+r', ['B', None, 'resign']], ['W+t', ['W', None, 'time']], ['B+100.5', ['B', 1005, 'score']], ['W+4.1', ['W', 41, 'score']], ['W+.5', ['unknown', None, 'none']]], [['B+R', ['B', None, 'resign']], ['W+Resign', ['W', None, 'resign']], ['B+resign', ['B', None, 'resign']], ['W+T', ['W', None, 'time']], ['B+Time', ['B', None, 'time']], ['0', ['draw', 0, 'score']], ['0', ['draw', 0, 'score']], ['B+7.', ['unknown', None, 'none']]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("result 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| result case 0 | ['B', 35, 'score'] | ['B', 35, 'score'] | Passed |
| result case 1 | ['W', 5, 'score'] | ['W', 5, 'score'] | Passed |
| result case 2 | ['W', 120, 'score'] | ['W', 120, 'score'] | Passed |
| result case 3 | ['B', 23, 'score'] | ['B', 23, 'score'] | Passed |
| result case 4 | ['W', 82, 'score'] | ['W', 82, 'score'] | Passed |
| result case 5 | ['B', 11, 'score'] | ['B', 11, 'score'] | Passed |
| result case 6 | ['draw', 0, 'score'] | ['draw', 0, 'score'] | Passed |
| result case 7 | ['draw', 0, 'score'] | ['draw', 0, 'score'] | Passed |
SHA-256 / ab9b9547b15126355ca47f4aaddc6b3f0603875bd089c65f3dc0f2b00263a36b
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:17.463049+00:00.
Case digest / 601ee38b8a376a79b7c9fab83989d972d77bb4f37c641c5c6901b177811a54c0