import os, json, numpy as np
os.makedirs("/work", exist_ok=True)
source = r'''import numpy as np
DEFAULTS = dict( capture=9.0, rescue=6.0, attack=1.15, self_atari=7.5, eye=11.0, connection=0.9, contact=1.4, own_territory=1.6, enemy_territory=1.0, territory_threshold=3.0, pass_base=-9.0, pass_per_stone=0.10, pass_white=0.35, own_adj=0.25, own_diag=0.65, own_two=0.40, own_knight=0.12, enemy_adj=0.75, enemy_diag=0.25, enemy_two=0.12, temperature_scale=1.0,)
def build(save, overrides=None): cfg = dict(DEFAULTS) if overrides: cfg.update(overrides)
W = [np.zeros((243 if k == 0 else 512, 512), np.float32) for k in range(6)] B = [np.zeros(512, np.float32) for _ in range(6)] OW = np.zeros((512, 82), np.float32) OB = np.zeros(82, np.float32)
def u(ch, p): return 81*ch+p
adj = [] diag = [] rings = [] for p in range(81): r, c = divmod(p, 9) adj.append([rr*9+cc for rr, cc in [(r-1,c), (r+1,c), (r,c-1), (r,c+1)] if 0 <= rr < 9 and 0 <= cc < 9]) diag.append([rr*9+cc for rr, cc in [(r-1,c-1), (r-1,c+1), (r+1,c-1), (r+1,c+1)] if 0 <= rr < 9 and 0 <= cc < 9]) rings.append([(q, abs(r-q//9), abs(c-q%9)) for q in range(81) if q != p])
# Layer 1: board, exact local liberties, and excess liberties. for p in range(81): W[0][3*p, u(0,p)] = 1 W[0][3*p+1, u(1,p)] = 1 for colour in range(2): for ch, excess in [(2+colour, 0), (4+colour, 1)]: j = u(ch,p) B[0][j] = len(adj[p])-4-excess W[0][3*p+colour,j] += 4 for q in adj[p]: W[0][3*q,j] -= 1 W[0][3*q+1,j] -= 1
# Global counts and colour are carried separately. for p in range(81): W[0][3*p,486] = 1 W[0][3*p+1,487] = 1 W[0][2,488] = 1
for k in range(1,6): for g in [486,487,488]: W[k][g,g] = 1
def carry_board(k): for ch in [0,1]: for p in range(81): W[k][u(ch,p),u(ch,p)] = 1
# Layer 2: liberty evidence along monochromatic connections. # Orthogonally adjacent stones cannot share an immediate liberty. carry_board(1) for p in range(81): for colour in range(2): j = u(2+colour,p) B[1][j] = -21 W[1][u(colour,p),j] = 20 for q in [p]+adj[p]: W[1][u(2+colour,q),j] += 1 W[1][u(4+colour,p),u(4+colour,p)] = 1
# Layer 3: spread evidence of safety through connected stones. # The other channels estimate liberties available after extension. carry_board(2) for p in range(81): for colour in range(2): j = u(2+colour,p) B[2][j] = -100 W[2][u(colour,p),j] = 100 for q in [p]+adj[p]: W[2][u(2+colour,q),j] += 1
j = u(4+colour,p) B[2][j] = len(adj[p]) for q in adj[p]: W[2][u(0,q),j] -= 1 W[2][u(1,q),j] -= 1 W[2][u(2+colour,q),j] += 1
# Layer 4: approximate whole-group atari, self-atari, weak enemies. carry_board(3) for p in range(81): for colour in range(2): j = u(2+colour,p) W[3][u(colour,p),j] = 1 for q in [p]+adj[p]: W[3][u(2+colour,q),j] -= 1
B[3][u(4,p)] = 2 W[3][u(4,p),u(4,p)] = -1
j = u(5,p) W[3][u(1,p),j] = 3 W[3][u(3,p),j] = -1 for q in adj[p]: W[3][u(3,q),j] -= 0.5
# Layer 5: spatial prior, captures, attack, safe rescue, and # capture-aware self-atari suppression. carry_board(4) line_prior = [-1.5, 0.25, 1.2, 0.9, 1.0] for p in range(81): r,c = divmod(p,9) j = u(2,p) B[4][j] = (100 + line_prior[min(r,8-r)] + line_prior[min(c,8-c)]) for q,dr,dc in rings[p]: if dr+dc == 1: a,b = cfg["own_adj"],cfg["enemy_adj"] elif dr == 1 and dc == 1: a,b = cfg["own_diag"],cfg["enemy_diag"] elif dr+dc == 2: a,b = cfg["own_two"],cfg["enemy_two"] elif sorted([dr,dc]) == [1,2]: a,b = cfg["own_knight"],0.0 else: continue W[4][u(0,q),j] += a W[4][u(1,q),j] += b for q in adj[p]: W[4][u(3,q),j] += cfg["capture"] W[4][u(5,q),j] += cfg["attack"]
j = u(3,p) W[4][u(4,p),j] = 1 for q in adj[p]: W[4][u(3,q),j] -= 2
j = u(4,p) W[4][u(4,p),j] = -4 for q in adj[p]: W[4][u(2,q),j] += 1
# Layer 6: combine tactics with connection, contact, territory, # and true-eye features. for p in range(81): j = u(0,p) W[5][u(2,p),j] = 1 W[5][u(3,p),j] = -cfg["self_atari"] W[5][u(4,p),j] = cfg["rescue"]
# Connect stones without repeatedly rewarding filled triangles. j = u(1,p) B[5][j] = -1 for q in adj[p]: W[5][u(0,q),j] = 1 for q in diag[p]: W[5][u(0,q),j] = -0.65
# Avoid unsupported contact with multiple enemy stones. j = u(2,p) B[5][j] = -1 for q in adj[p]: W[5][u(1,q),j] = 1 W[5][u(0,q),j] = -0.75 for q in diag[p]: W[5][u(0,q),j] -= 0.20
# Discourage both filling established territory and hopeless # invasions; urgent tactical bonuses can override either. for colour,ch in [(0,3),(1,4)]: j = u(ch,p) B[5][j] = -cfg["territory_threshold"] for q,dr,dc in rings[p]: if dr+dc == 1: weight = 1.0 elif dr == 1 and dc == 1: weight = 0.65 elif dr+dc == 2: weight = 0.45 elif sorted([dr,dc]) == [1,2]: weight = 0.20 else: continue W[5][u(colour,q),j] += weight W[5][u(1-colour,q),j] -= 1.2*weight
# A fully friendly orthogonal surround, adjusted for false eyes. j = u(5,p) B[5][j] = 1-len(adj[p]) for q in adj[p]: W[5][u(0,q),j] += 1 diagonal_penalty = 1.0 if len(adj[p]) < 4 else 0.5 for q in diag[p]: W[5][u(1,q),j] -= diagonal_penalty
for ch,coefficient in [ (0,1.0), (1,cfg["connection"]), (2,-cfg["contact"]), (3,-cfg["own_territory"]), (4,-cfg["enemy_territory"]), (5,-cfg["eye"]), ]: OW[u(ch,p),p] = coefficient OB[p] = -100
OB[81] = cfg["pass_base"] OW[486,81] = cfg["pass_per_stone"] OW[487,81] = cfg["pass_per_stone"] OW[488,81] = cfg["pass_white"]
OW *= cfg["temperature_scale"] OB *= cfg["temperature_scale"]
tensors = {} for k in range(6): tensors["L%d.W"%(k+1)] = W[k] tensors["L%d.b"%(k+1)] = B[k] tensors["out.W"] = OW tensors["out.b"] = OB for name,array in tensors.items(): assert np.isfinite(array).all() save(name,array) return cfg, tensors'''
with open("/work/policy_design.py", "w") as f: f.write(source)
namespace = {}exec(compile(source, "/work/policy_design.py", "exec"), namespace)config, tensors = namespace["build"](save)
with open("/work/policy_config.json", "w") as f: json.dump(config, f, indent=2)np.savez("/work/policy_initial.npz", **tensors)
print("Saved all 14 tensors.")print("Nonzero parameters:", sum(np.count_nonzero(a) for a in tensors.values()))print("Design: analytic liberty propagation, captures, safe rescues,")print("self-atari avoidance, connection, territory, eyes, and phase-aware passing.")print(json.dumps(config, indent=2))Saved all 14 tensors.Nonzero parameters: 21080Design: analytic liberty propagation, captures, safe rescues,self-atari avoidance, connection, territory, eyes, and phase-aware passing.{ "capture": 9.0, "rescue": 6.0, "attack": 1.15, "self_atari": 7.5, "eye": 11.0, "connection": 0.9, "contact": 1.4, "own_territory": 1.6, "enemy_territory": 1.0, "territory_threshold": 3.0, "pass_base": -9.0, "pass_per_stone": 0.1, "pass_white": 0.35, "own_adj": 0.25, "own_diag": 0.65, "own_two": 0.4, "own_knight": 0.12, "enemy_adj": 0.75, "enemy_diag": 0.25, "enemy_two": 0.12, "temperature_scale": 1.0}{"games":{"strong":64,"weak":32,"random":32}}{"ok": true, "pass": 1, "kind": "games", "per_opponent": {"random": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 42.96875, "mean_length": 96.8125, "stones_captured_per_game": 32.09375, "stones_lost_per_game": 0.53125, "passes_per_game": 1.25, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}, "strong": {"games": 64, "score": 0.578125, "wins": 37, "losses": 27, "score_as_black": 0.375, "score_as_white": 0.78125, "mean_margin": 13.09375, "mean_length": 101.0625, "stones_captured_per_game": 19.609375, "stones_lost_per_game": 16.46875, "passes_per_game": 2.9375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 62, "end_repetition": 2, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.90625, "wins": 29, "losses": 3, "score_as_black": 0.8125, "score_as_white": 1.0, "mean_margin": 22.4375, "mean_length": 96.9375, "stones_captured_per_game": 20.6875, "stones_lost_per_game": 9.5, "passes_per_game": 3.75, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_1/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import os, json, numpy as nppath = "/work/pass_1"games = json.load(open(path+"/games.json"))print(type(games), len(games))if isinstance(games, dict): print(games.keys())else: print(json.dumps(games[0], indent=2)[:6500])boards = np.load(path+"/boards.npy")valid = np.load(path+"/valid.npy")print("board shapes", boards.shape, valid.shape)print("files", os.listdir(path))if isinstance(games, list): for i,g in enumerate(games): if g.get("opponent") == "strong" and g.get("result") == -1: print("LOSS",i,{k:v for k,v in g.items() if k!="moves"}) b = boards[i, np.flatnonzero(valid[i])[-1]].reshape(9,9) print("\n".join("".join({0:".",1:"X",-1:"O"}[int(x)] for x in row) for row in b)) if i > 45: break<class 'list'> 128{ "game": 0, "opponent": "random", "net_color": "black", "result": 1, "margin": 73.5, "end": "two passes", "stones_captured_by_net": 71, "stones_lost_by_net": 0, "moves": [ { "ply": 0, "by": "net", "move": 67, "top5": [ [ 20, 0.0369 ], [ 24, 0.0369 ], [ 56, 0.0369 ], [ 60, 0.0369 ], [ 22, 0.0302 ] ] }, { "ply": 1, "by": "opponent", "move": 65 }, { "ply": 2, "by": "net", "move": 57, "top5": [ [ 56, 0.0752 ], [ 57, 0.0574 ], [ 59, 0.0447 ], [ 60, 0.0355 ], [ 58, 0.0331 ] ] }, { "ply": 3, "by": "opponent", "move": 3 }, { "ply": 4, "by": "net", "move": 24, "top5": [ [ 58, 0.0817 ], [ 56, 0.0754 ], [ 66, 0.0605 ], [ 59, 0.0521 ], [ 12, 0.0472 ] ] }, { "ply": 5, "by": "opponent", "move": 59 }, { "ply": 6, "by": "net", "move": 29, "top5": [ [ 58, 0.1474 ], [ 56, 0.0643 ], [ 66, 0.0516 ], [ 60, 0.0501 ], [ 49, 0.0467 ] ] }, { "ply": 7, "by": "opponent", "move": 63 }, { "ply": 8, "by": "net", "move": 20, "top5": [ [ 58, 0.1331 ], [ 56, 0.058 ], [ 66, 0.0466 ], [ 60, 0.0452 ], [ 47, 0.0452 ] ] }, { "ply": 9, "by": "opponent", "move": 4 }, { "ply": 10, "by": "net", "move": 41, "top5": [ [ 58, 0.1269 ], [ 56, 0.0553 ], [ 66, 0.0444 ], [ 60, 0.0431 ], [ 47, 0.0431 ] ] }, { "ply": 11, "by": "opponent", "move": 72 }, { "ply": 12, "by": "net", "move": 39, "top5": [ [ 58, 0.1245 ], [ 49, 0.067 ], [ 56, 0.0481 ], [ 39, 0.0458 ], [ 60, 0.0423 ] ] }, { "ply": 13, "by": "opponent", "move": 0 }, { "ply": 14, "by": "net", "move": 50, "top5": [ [ 58, 0.1071 ], [ 49, 0.0979 ], [ 38, 0.067 ], [ 47, 0.0548 ], [ 31, 0.0506 ] ] }, { "ply": 15, "by": "opponent", "move": 17 }, { "ply": 16, "by": "net", "move": 60, "top5": [ [ 58, 0.2323 ], [ 60, 0.1256 ], [ 49, 0.0809 ], [ 38, 0.0431 ], [ 48, 0.0367 ] ] }, { "ply": 17, "by": "opponent", "move": 42 }, { "ply": 18, "by": "net", "move": 58, "top5": [ [ 58, 0.4931 ], [ 51, 0.0956 ], [ 68, 0.087 ], [ 33, 0.0477 ], [ 49, 0.0411 ] ] }, { "ply": 19, "by": "opponent", "move": 34 }, { "ply": 20, "by": "net", "move": 68, "top5": [ [ 68, 0.9999 ], [ 51, 0.0 ], [ 33, 0.0 ], [ 40, 0.0 ], [ 49, 0.0 ] ] }, { "ply": 21, "by": "opponent", "move": 44 }, { "ply": 22, "by": "net", "move": 47, "top5": [ [ 51, 0.2161 ], [ 33, 0.215 ], [ 40, 0.0492 ], [ 38, 0.0443 ], [ 47, 0.0409 ] ] }, { "ply": 23, "by": "opponent", "move": 10 }, { "ply": 24, "by": "net", "move": 79, "top5": [ [ 51, 0.2029 ], [ 33, 0.2019 ], [ 56, 0.1212 ], [ 38, 0.0464 ], [ 40, 0.0403 ] ] }, { "ply": 25, "by": "opponent", "move": 16 }, { "ply": 26, "by": "net", "move": 22, "top5": [ [ 51, 0.2063 ], [ 33, 0.2053 ], [ 56, 0.1233 ], [ 38, 0.0472 ], [ 40, 0.041 ] ] }, { "ply": 27, "by": "opponent", "move": 18 }, { "ply": 28, "by": "net", "move": 15, "top5": [ [ 33, 0.2001 ], [ 51, 0.1783 ], [ 56, 0.1066 ], [ 19, 0.0822 ], [ 32, 0.0412 ] ] }, { "ply": 29, "by": "opponent", "move": 13 }, { "ply": 30, board shapes (128, 162, 81) (128, 162)files ['boards.npy', 'summary.json', 'README.txt', 'games.json', 'valid.npy']LOSS 33 {'game': 33, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -8.5, 'end': 'two passes', 'stones_captured_by_net': 9, 'stones_lost_by_net': 8}.X.XXXOO.XXX.XOOOOXXXXXOOO.X.XXXOOOOXX.XOOOOO.XXXOO.OOX.XXOOOOO.XXOO.O.OX.XXOOOO.LOSS 35 {'game': 35, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -14.5, 'end': 'two passes', 'stones_captured_by_net': 3, 'stones_lost_by_net': 10}XX.X.XX.XXXXXX.XX.XOOOXXXXXXOOOXXXXXOO.OOXXXOOOOOOOXOOOOO.OOOOOOOOOOOOOO.OO.XOOX.LOSS 36 {'game': 36, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -9.5, 'end': 'two passes', 'stones_captured_by_net': 6, 'stones_lost_by_net': 18}X.XOOOOOO.XXXOOOXOXXXXXOO.O.XXXXO.OOX.XXOOOOO.XXOOOOOOX.XOO.XX..XXXOOOOOX.XXOOO..LOSS 38 {'game': 38, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': 9.5, 'end': 'repetition', 'stones_captured_by_net': 15, 'stones_lost_by_net': 35}.X.X..OO.OOXXO.OOOOOO.O.OXOOOOOOOOXXXOOOOOXX.XXXOXXX.XX.XOXX.X..XXXXXX.XX.X.X.XX.LOSS 40 {'game': 40, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -21.5, 'end': 'two passes', 'stones_captured_by_net': 4, 'stones_lost_by_net': 25}.X.X.XXO.XXX.X.XOO.XXXXXXOOXXXXXXOOOOOOXXXOOOOOOOOOOOOOO.OO.XOOO.XOOOOOO.OOOOOOO.LOSS 41 {'game': 41, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -8.5, 'end': 'two passes', 'stones_captured_by_net': 10, 'stones_lost_by_net': 18}X.XXXXOO.XX.XXOOOO.XXXXOOO.XX.XXO..O.XXXOOOOOXXXXOOOOOXXOOO.XXX.XOO.OOO.XXXOOOOOOLOSS 42 {'game': 42, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -17.5, 'end': 'two passes', 'stones_captured_by_net': 7, 'stones_lost_by_net': 32}.XXOO.OO.XXOOOXOOOXXOOO.OOO.XXXOO.OOXXXXOOOOOX.XXOO.OO.XXOOOOOOXXXXOOOOX.XXXXXOO.LOSS 44 {'game': 44, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -12.5, 'end': 'two passes', 'stones_captured_by_net': 21, 'stones_lost_by_net': 37}OXXXX.X.XOOOXXX.XXOOOOXXX.XOX.OOXXXXOOOOOOXXX.O.OXXX.XOOOOOXXXXOOO.OOOOX.OOOO.OO.LOSS 45 {'game': 45, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -27.5, 'end': 'two passes', 'stones_captured_by_net': 11, 'stones_lost_by_net': 54}OOOOOOXXX.OOOOXXX.OOOOOOXXX.OOOOXXXXOOOOOXXX.OXXOOOXXXOOX.OXXXXOOOOOXOOXOOOOOOOOOLOSS 47 {'game': 47, 'opponent': 'strong', 'net_color': 'black', 'result': -1, 'margin': -9.5, 'end': 'two passes', 'stones_captured_by_net': 5, 'stones_lost_by_net': 7}XXX.X.X.XX.XXXXXX.XX.XX.XXXOXXXXXXOOOOXXOOOOOOOOOOO.O.OO.O.OXOX.OOOXOOOO.OOOOOOO.import json, numpy as np, collectionsgames=json.load(open("/work/pass_1/games.json"))boards=np.load("/work/pass_1/boards.npy")N=[[] for _ in range(81)]for p in range(81): r,c=divmod(p,9) N[p]=[rr*9+cc for rr,cc in [(r-1,c),(r+1,c),(r,c-1),(r,c+1)] if 0<=rr<9 and 0<=cc<9]def groups(b): seen=set(); out=[] for p in range(81): if not b[p] or p in seen: continue stones={p}; stack=[p]; seen.add(p); libs=set() while stack: q=stack.pop() for z in N[q]: if b[z]==0: libs.add(z) elif b[z]==b[p] and z not in seen: seen.add(z); stones.add(z); stack.append(z) out.append((int(b[p]),stones,libs)) return outdef show(b): print("\n".join("".join(".XO"[{0:0,1:1,-1:2}[int(x)]] for x in row) for row in b.reshape(9,9)))stats=collections.Counter()examples=[]for gi,g in enumerate(games): if g["opponent"]!="strong": continue me=1 if g["net_color"]=="black" else -1 for t,m in enumerate(g["moves"]): before=boards[gi,t-1] if t else np.zeros(81,dtype=np.int8) after=boards[gi,t] if m["by"]=="net": caps=collections.Counter() for col,st,libs in groups(before): if col==-me and len(libs)==1: caps[next(iter(libs))]+=len(st) if caps: stats["capture_available"]+=1 if m["move"] in caps: stats["capture_taken"]+=1 elif not any(a in caps for a,p in m["top5"]): stats["capture_absent_top5"]+=1 else: lost=int(np.sum((before==me)&(after!=me))) if lost>=3: stats["loss_events_3plus"]+=1 prev=g["moves"][t-1] if t else None preprev=boards[gi,t-2] if t>=2 else np.zeros(81,dtype=np.int8) threatened=[(len(st),sorted(libs)) for col,st,libs in groups(preprev) if col==me and len(libs)==1] examples.append((lost,gi,t,prev,threatened,preprev.copy()))print(dict(stats))for lost,gi,t,prev,threatened,b in sorted(examples,reverse=True,key=lambda x:x[0])[:8]: print("\nCAPTURED",lost,"game",gi,"ply",t,"our previous move",prev, "prior own ataris",threatened) show(b){'capture_available': 875, 'capture_taken': 679, 'loss_events_3plus': 85, 'capture_absent_top5': 161}
CAPTURED 17 game 51 ply 81 our previous move {'ply': 80, 'by': 'net', 'move': 9, 'top5': [[9, 0.7707], [28, 0.1547], [0, 0.0437], [81, 0.0302], [79, 0.0003]]} prior own ataris [(1, [1]), (4, [5]), (17, [52])]..XOX.O.O.OOOXXOOXXXOOXOOXXX.XOOOOOX.XXXOXXXXX.XOOXX.XXXXOXXXXOXOOOOXXOOXO.O.OO..
CAPTURED 15 game 83 ply 82 our previous move {'ply': 81, 'by': 'net', 'move': 37, 'top5': [[37, 0.652], [81, 0.3356], [71, 0.007], [75, 0.003], [76, 0.002]]} prior own ataris [(1, [71])]XXXXXOXX.XOOXXOXXXOOOXOOOOO.OXXXOO.OO.OXXOOOOOOOXXO.O.OXOXOOOOOXXOXXXXO..XX..X.XO
CAPTURED 14 game 45 ply 89 our previous move {'ply': 88, 'by': 'net', 'move': 81, 'top5': [[81, 0.6572], [33, 0.3428], [44, 0.0], [72, 0.0], [80, 0.0]]} prior own ataris [(2, [0]), (14, [36])].OOOOOXXXXXOOOXXX.OOOOOOXXXXXXXOX.XX.XXOOXXX.XXXOOOXXXOXXXOXXXXOOXOOXOOX.OXOOO.O.
CAPTURED 14 game 62 ply 81 our previous move {'ply': 80, 'by': 'net', 'move': 79, 'top5': [[79, 0.998], [75, 0.0019], [81, 0.0], [76, 0.0], [25, 0.0]]} prior own ataris [(1, [79])].XOOO.OOOXXXXOOO.O.XXXXOO.OX.XOOXOOOXXXXOXXOX.XXOOOXXXX.XXOOXXX.XXOOOOX.X.X..OX.X
CAPTURED 13 game 91 ply 86 our previous move {'ply': 85, 'by': 'net', 'move': 81, 'top5': [[81, 1.0], [61, 0.0], [79, 0.0], [67, 0.0], [0, 0.0]]} prior own ataris [(13, [12])]XOOOOOOXXXXO.OOXXXXXXOOXXOOX.XXOOXXOXXXXXXXXOXXXOOOOOOXXOOOOO.OXXXO.OOOO.XOOOOO.O
CAPTURED 12 game 48 ply 83 our previous move {'ply': 82, 'by': 'net', 'move': 45, 'top5': [[45, 1.0], [81, 0.0], [56, 0.0], [63, 0.0], [72, 0.0]]} prior own ataris [(12, [0]), (1, [8])].XXXXXOX.OXXXOXOOOOOXXOOOXOOOOXOOXXXOOOOOXXX..XXOXXX.XOO.OOX.X..OOOXXX.X.OOXXXXX.
CAPTURED 11 game 38 ply 81 our previous move {'ply': 80, 'by': 'net', 'move': 0, 'top5': [[0, 1.0], [81, 0.0], [55, 0.0], [52, 0.0], [44, 0.0]]} prior own ataris [].X.XXXOO.OXXXOXOOOOOOXOXOXOOOOOOOOXXXOOOOOXX.XXXOXXX.XX.XOXX.X..XXXXXX.XX.X.X.XX.
CAPTURED 11 game 54 ply 73 our previous move {'ply': 72, 'by': 'net', 'move': 44, 'top5': [[44, 0.5784], [2, 0.42], [10, 0.0011], [18, 0.0002], [9, 0.0001]]} prior own ataris [(2, [2]), (1, [44])].X.XXOOOX..XOOOOOX.X.XXOXXXX.XXOOX.X.X.XOOXX.X.XOOOOOXXXXXO.OOO.X.XO..OOX.XOOO.O.{"positions":[{"board":[".XOOO.OOO","XXXXOOO.O",".XXXXOO.O","X.XOOXOOO","XXXXOXXOX",".XXOOOXXX","X.XXOOXXX",".XXOOOOX.","X.X..OX.X"],"to_move":"black"},{"board":["XXXXXOXX.","XOOXXOXXX","OOOXOOOOO",".OXXXOO.O","O.OXXOOOO","OOOXXO.O.","OXOXOOOOO","XXOXXXXO.",".XX..X.XO"],"to_move":"white"},{"board":[".X.XXXOO.","OXXXOXOOO","OOOXOXOXO","OOOOOOOXX","XOOOOOXX.","XXXOXXX.X","X.XOXX.X.","XXXXXXX.X","X.X.X.XX."],"to_move":"black"}],"activations":true}{"ok": true, "pass": 2, "kind": "positions", "positions": 3, "top3_per_position": [[["(8,7)", 0.998], ["(8,3)", 0.0019], ["pass", 0.0]], [["(8,6)", 1.0], ["(4,1)", 0.0], ["pass", 0.0]], [["(0,0)", 1.0], ["pass", 0.0], ["(5,7)", 0.0]]], "files": "/work/pass_2/", "readme": "inputs.npy (P, 243) the network inputs; legal.npy (P, 82) the legal moves (for 'board' positions: the empty points and pass; suicide and ko are not checked); logits.npy, probs.npy (P, 82); act_L1.npy .. act_L6.npy (P, 512) the hidden activations after ReLU."}import numpy as nppath="/work/pass_2/"X=np.load(path+"inputs.npy").reshape(-1,81,3)logits=np.load(path+"logits.npy")acts=[np.load(path+"act_L%d.npy"%k) for k in range(1,7)]for n, candidates in enumerate([[79,75,76],[37,78,71],[0,55,52]]): print("\nPOSITION",n) for p in candidates: print("move",divmod(p,9),"logit",float(logits[n,p])) for k in range(6): print(" L",k+1, np.round(acts[k][n,:486].reshape(6,81)[:,p],3).tolist()) r,c=divmod(p,9) nn=[rr*9+cc for rr,cc in [(r-1,c),(r+1,c),(r,c-1),(r,c+1)] if 0<=rr<9 and 0<=cc<9] print("adjacent stone features:", [(q,X[n,q,:2].astype(int).tolist(), np.round(acts[3][n,:486].reshape(6,81)[:,q],2).tolist()) for q in nn])
POSITION 0move (8, 7) logit 8.310005187988281 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 2.0, 0.0] L 4 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 5 [0.0, 0.0, 100.51000213623047, 0.0, 1.0, 0.0] L 6 [106.51000213623047, 2.0, 0.0, 0.0, 0.0, 0.0]adjacent stone features: [(70, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (78, [1, 0], [1.0, 0.0, 1.0, 0.0, 1.0, 0.0]), (80, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0])]move (8, 3) logit 2.05999755859375 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 2.0, 2.0] L 4 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 5 [0.0, 0.0, 102.05999755859375, 0.0, 0.0, 0.0] L 6 [102.05999755859375, 0.0, 0.0, 0.0, 0.0, 0.0]adjacent stone features: [(66, [0, 1], [0.0, 1.0, 0.0, 0.0, 0.0, 0.0]), (74, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (76, [0, 0], [0.0, 0.0, 0.0, 0.0, 1.0, 0.0])]move (8, 4) logit -5.090003967285156 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 1.0, 2.0] L 4 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0] L 5 [0.0, 0.0, 103.80999755859375, 1.0, 0.0, 0.0] L 6 [96.30999755859375, 0.0, 1.0, 0.0, 0.0, 0.0]adjacent stone features: [(67, [0, 1], [0.0, 1.0, 0.0, 0.0, 1.0, 0.0]), (75, [0, 0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (77, [0, 1], [0.0, 1.0, 0.0, 0.0, 1.0, 1.0])]
POSITION 1move (4, 1) logit -0.9860000610351562 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 2.0, 0.0] L 4 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 5 [0.0, 0.0, 104.91999816894531, 0.0, 0.0, 0.0] L 6 [104.91999816894531, 1.7000000476837158, 0.0, 1.2100000381469727, 0.0, 0.5]adjacent stone features: [(28, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (46, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (36, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (38, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0])]move (8, 6) logit 13.819999694824219 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 0.0, 1.0] L 4 [0.0, 0.0, 0.0, 0.0, 2.0, 0.0] L 5 [0.0, 0.0, 116.33999633789062, 0.0, 0.0, 0.0] L 6 [116.33999633789062, 0.0, 1.7999999523162842, 0.0, 0.0, 0.0]adjacent stone features: [(69, [0, 1], [0.0, 1.0, 0.0, 0.0, 1.0, 0.0]), (77, [0, 1], [0.0, 1.0, 0.0, 0.0, 0.0, 0.0]), (79, [0, 1], [0.0, 1.0, 0.0, 1.0, 1.0, 3.0])]move (7, 8) logit -5.525001525878906 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0] L 4 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0] L 5 [0.0, 0.0, 100.76000213623047, 1.0, 0.0, 0.0] L 6 [93.26000213623047, 1.350000023841858, 0.0, 0.0, 0.0, 0.0]adjacent stone features: [(62, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (80, [1, 0], [1.0, 0.0, 1.0, 0.0, 1.0, 0.0]), (70, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0])]
POSITION 2move (0, 0) logit 11.339996337890625 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0] L 4 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0] L 5 [0.0, 0.0, 111.33999633789062, 0.0, 0.0, 0.0] L 6 [111.33999633789062, 0.0, 0.0, 0.0, 0.0, 0.0]adjacent stone features: [(9, [0, 1], [0.0, 1.0, 0.0, 1.0, 0.0, 3.0]), (1, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0])]move (6, 1) logit -16.91400146484375 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0] L 4 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0] L 5 [0.0, 0.0, 105.7699966430664, 1.0, 0.0, 0.0] L 6 [98.2699966430664, 0.4000000059604645, 0.0, 2.8399999141693115, 0.0, 1.0]adjacent stone features: [(46, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (64, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (54, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (56, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0])]move (5, 7) logit -9.397003173828125 L 1 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 2 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 3 [0.0, 0.0, 0.0, 0.0, 7.0, 0.0] L 4 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] L 5 [0.0, 0.0, 104.08000183105469, 0.0, 0.0, 0.0] L 6 [104.08000183105469, 2.3499999046325684, 0.0, 2.869999885559082, 0.0, 1.0]adjacent stone features: [(43, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (61, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (51, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0]), (53, [1, 0], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0])]import json, numpy as npp="/work/policy_design.py"s=open(p).read()s=s.replace('temperature_scale=1.0,','temperature_scale=1.8,\n surrounded_penalty=10.0,')s=s.replace( ' # Layer 6: combine tactics', ''' # Empty points surrounded by friendly stones are normally already # owned. Preserve them even when diagonal enemies make them false eyes. for p in range(81): j = u(5,p) B[4][j] = 1-len(adj[p]) for q in adj[p]: W[4][u(0,q),j] = 1
# Layer 6: combine tactics''')s=s.replace( 'W[5][u(4,p),j] = cfg["rescue"]', 'W[5][u(4,p),j] = cfg["rescue"]\n W[5][u(5,p),j] = -cfg["surrounded_penalty"]')open(p,"w").write(s)ns={}exec(compile(s,p,"exec"),ns)cfg,tensors=ns["build"](save)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v2.npz",**tensors)print("Saved v2: protect all friendly surrounds and sharpen the analytic policy.")Saved v2: protect all friendly surrounds and sharpen the analytic policy.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 3, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.6145833134651184, "wins": 59, "losses": 37, "score_as_black": 0.4583333432674408, "score_as_white": 0.7708333134651184, "mean_margin": 14.770833015441895, "mean_length": 99.73958333333333, "stones_captured_per_game": 20.020833333333332, "stones_lost_per_game": 15.770833333333334, "passes_per_game": 2.4479166666666665, "games_with_a_pass_before_move_60": 0, "end_two_passes": 88, "end_repetition": 8, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.9375, "wins": 30, "losses": 2, "score_as_black": 0.9375, "score_as_white": 0.9375, "mean_margin": 34.1875, "mean_length": 97.34375, "stones_captured_per_game": 26.46875, "stones_lost_per_game": 3.125, "passes_per_game": 3.4375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_3/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import os, json, numpy as npos.system("cp /work/policy_design.py /work/policy_design_v2.py")source = r'''import numpy as np
DEFAULTS = dict( capture=24.0, rescue=8.0, attack=1.15, self_atari=7.5, eye=16.0, connection=0.9, contact=1.4, own_territory=1.6, territory_threshold=3.0, pass_base=-9.0, pass_per_stone=0.10, pass_white=0.35, own_adj=0.25, own_diag=0.65, own_two=0.40, own_knight=0.12, enemy_adj=0.75, enemy_diag=0.25, enemy_two=0.12, temperature_scale=1.8,)def build(save, overrides=None): c=dict(DEFAULTS) if overrides: c.update(overrides) W=[np.zeros((243 if k==0 else 512,512),np.float32) for k in range(6)] B=[np.zeros(512,np.float32) for k in range(6)] OW=np.zeros((512,82),np.float32); OB=np.zeros(82,np.float32) def u(ch,p): return 81*ch+p adj=[]; diag=[]; rings=[] for p in range(81): r,s=divmod(p,9) adj.append([a*9+b for a,b in [(r-1,s),(r+1,s),(r,s-1),(r,s+1)] if 0<=a<9 and 0<=b<9]) diag.append([a*9+b for a,b in [(r-1,s-1),(r-1,s+1),(r+1,s-1),(r+1,s+1)] if 0<=a<9 and 0<=b<9]) rings.append([(q,abs(r-q//9),abs(s-q%9)) for q in range(81) if q!=p]) for p in range(81): W[0][3*p,u(0,p)]=1; W[0][3*p+1,u(1,p)]=1 for colour in [0,1]: j=u(2+colour,p) B[0][j]=len(adj[p])-4 W[0][3*p+colour,j]=4 for q in adj[p]: W[0][3*q,j]-=1; W[0][3*q+1,j]-=1 # Contact and connection features, directly from the board. B[0][u(4,p)]=-1; B[0][u(5,p)]=-1 for q in adj[p]: W[0][3*q+1,u(4,p)]+=1 W[0][3*q,u(4,p)]-=0.75 W[0][3*q,u(5,p)]+=1 for q in diag[p]: W[0][3*q,u(4,p)]-=0.20 W[0][3*q,u(5,p)]-=0.65 W[0][3*p,486]=1; W[0][3*p+1,487]=1 W[0][2,488]=1 for k in range(1,6): for g in [486,487,488]: W[k][g,g]=1 def board(k): for col in [0,1]: for p in range(81): W[k][u(col,p),u(col,p)]=1
board(1) line=[-1.5,0.25,1.2,0.9,1.0] for p in range(81): for colour in [0,1]: j=u(2+colour,p) B[1][j]=-21; W[1][u(colour,p),j]=20 for q in [p]+adj[p]: W[1][u(2+colour,q),j]+=1 j=u(4,p); B[1][j]=-c["territory_threshold"] for q,dr,dc in rings[p]: if dr+dc==1: a=1.0 elif dr==dc==1: a=0.65 elif dr+dc==2: a=0.45 elif sorted([dr,dc])==[1,2]: a=0.20 else: continue W[1][u(0,q),j]+=a; W[1][u(1,q),j]-=1.2*a j=u(5,p); r,s=divmod(p,9) B[1][j]=100+line[min(r,8-r)]+line[min(s,8-s)] W[1][u(4,p),j]=-c["contact"] W[1][u(5,p),j]=c["connection"] for q,dr,dc in rings[p]: if dr+dc==1: a,b=c["own_adj"],c["enemy_adj"] elif dr==dc==1: a,b=c["own_diag"],c["enemy_diag"] elif dr+dc==2: a,b=c["own_two"],c["enemy_two"] elif sorted([dr,dc])==[1,2]: a,b=c["own_knight"],0 else: continue W[1][u(0,q),j]+=a; W[1][u(1,q),j]+=b
# Propagate liberty evidence for three more layers. # Conservative analytic upper bounds make every colour gate exact. for k,gate in [(2,100),(3,500)]: board(k) for p in range(81): for colour in [0,1]: j=u(2+colour,p) B[k][j]=-gate; W[k][u(colour,p),j]=gate for q in [p]+adj[p]: W[k][u(2+colour,q),j]+=1 if k==2: j=u(4,p); B[k][j]=2-len(adj[p]) for q in adj[p]: W[k][u(0,q),j]+=1 W[k][u(1,q),j]+=1 W[k][u(2,q),j]-=1 W[k][u(5,p),u(5,p)]=1 W[k][u(4,p),u(5,p)]=-c["own_territory"] else: W[k][u(4,p),u(4,p)]=1 W[k][u(5,p),u(5,p)]=1
# Friendly danger now reaches four links; enemy safety reaches four. board(4) for p in range(81): j=u(2,p); W[4][u(0,p),j]=1 for q in [p]+adj[p]: W[4][u(2,q),j]-=1 j=u(3,p); B[4][j]=-2500; W[4][u(1,p),j]=2500 for q in [p]+adj[p]: W[4][u(3,q),j]+=1 W[4][u(4,p),u(4,p)]=1 W[4][u(5,p),u(5,p)]=1
for p in range(81): W[5][u(5,p),u(0,p)]=1 j=u(1,p) W[5][u(4,p),j]=-4 for q in adj[p]: W[5][u(2,q),j]+=1 j=u(2,p); W[5][u(1,p),j]=1 for q in [p]+adj[p]: W[5][u(3,q),j]-=1 W[5][u(4,p),u(3,p)]=1 j=u(4,p); B[5][j]=1-len(adj[p]) for q in adj[p]: W[5][u(0,q),j]+=1 j=u(5,p); W[5][u(1,p),j]=3 W[5][u(3,p),j]=-1 for q in adj[p]: W[5][u(3,q),j]-=0.5
OW[u(0,p),p]=1 OW[u(1,p),p]=c["rescue"] OW[u(3,p),p]=-c["self_atari"] OW[u(4,p),p]=-c["eye"] for q in adj[p]: OW[u(2,q),p]+=c["capture"] OW[u(5,q),p]+=c["attack"] OB[p]=-100
OB[81]=c["pass_base"] OW[486,81]=OW[487,81]=c["pass_per_stone"] OW[488,81]=c["pass_white"] OW*=c["temperature_scale"]; OB*=c["temperature_scale"] ts={} for k in range(6): ts["L%d.W"%(k+1)]=W[k]; ts["L%d.b"%(k+1)]=B[k] ts["out.W"]=OW; ts["out.b"]=OB for n,a in ts.items(): assert np.isfinite(a).all() save(n,a) return c,ts'''open("/work/policy_design_v3.py","w").write(source)open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v3.npz",**tensors)print("Saved v3: longer-range liberty propagation and conservative eye preservation.")Saved v3: longer-range liberty propagation and conservative eye preservation.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 4, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.5520833134651184, "wins": 53, "losses": 43, "score_as_black": 0.4583333432674408, "score_as_white": 0.6458333134651184, "mean_margin": 12.989583015441895, "mean_length": 102.98958333333333, "stones_captured_per_game": 21.489583333333332, "stones_lost_per_game": 16.979166666666668, "passes_per_game": 3.5625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 88, "end_repetition": 8, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.75, "wins": 24, "losses": 8, "score_as_black": 0.625, "score_as_white": 0.875, "mean_margin": 27.65625, "mean_length": 101.15625, "stones_captured_per_game": 24.6875, "stones_lost_per_game": 7.34375, "passes_per_game": 5.15625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 29, "end_repetition": 3, "end_move_limit": 0}}, "files": "/work/pass_4/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v2.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)config=dict( own_territory=2.5, territory_threshold=1.5, own_adj=0.20, own_diag=0.50, own_two=0.70, own_knight=0.30, enemy_adj=0.70, enemy_diag=0.40, enemy_two=0.25,)cfg,tensors=ns["build"](save,config)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v4.npz",**tensors)print("Saved v4: prioritize outward extension and contested boundaries over interior consolidation.")print(json.dumps(cfg,indent=2))Saved v4: prioritize outward extension and contested boundaries over interior consolidation.{ "capture": 9.0, "rescue": 6.0, "attack": 1.15, "self_atari": 7.5, "eye": 11.0, "connection": 0.9, "contact": 1.4, "own_territory": 2.5, "enemy_territory": 1.0, "territory_threshold": 1.5, "pass_base": -9.0, "pass_per_stone": 0.1, "pass_white": 0.35, "own_adj": 0.2, "own_diag": 0.5, "own_two": 0.7, "own_knight": 0.3, "enemy_adj": 0.7, "enemy_diag": 0.4, "enemy_two": 0.25, "temperature_scale": 1.8, "surrounded_penalty": 10.0}{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 5, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.7395833134651184, "wins": 71, "losses": 25, "score_as_black": 0.6666666865348816, "score_as_white": 0.8125, "mean_margin": 24.82291603088379, "mean_length": 102.85416666666667, "stones_captured_per_game": 26.958333333333332, "stones_lost_per_game": 14.479166666666666, "passes_per_game": 2.5208333333333335, "games_with_a_pass_before_move_60": 0, "end_two_passes": 85, "end_repetition": 10, "end_move_limit": 1}, "weak": {"games": 32, "score": 0.9375, "wins": 30, "losses": 2, "score_as_black": 0.9375, "score_as_white": 0.9375, "mean_margin": 31.90625, "mean_length": 93.96875, "stones_captured_per_game": 23.90625, "stones_lost_per_game": 4.875, "passes_per_game": 3.28125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_5/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v2.py").read()source=source.replace( " # Global counts and colour are carried separately.", ''' # A zero-liberty indicator lets the next layer distinguish a # duplicated single liberty around a bent chain from independent ones. for p in range(81): for colour in range(2): j=u(4+colour,p) W[0][:,j]=0 B[0][j]=-len(adj[p]) W[0][3*p+colour,j]=1 for q in adj[p]: W[0][3*q,j]+=1 W[0][3*q+1,j]+=1
# Global counts and colour are carried separately.''')source=source.replace( ' W[1][u(4+colour,p),u(4+colour,p)] = 1', ''' W[1][u(4+colour,p),j] -= 1
# Independent supplementary safety certificate. A shared # liberty of two neighbours must lie diagonally from p. j=u(4+colour,p) B[1][j]=-21-len(diag[p]) W[1][u(colour,p),j]=20 for q in [p]+adj[p]: W[1][u(2+colour,q),j]+=1 for q in diag[p]: W[1][u(0,q),j]+=1 W[1][u(1,q),j]+=1''')source=source.replace( ' W[2][u(2+colour,q),j] += 1', ''' W[2][u(2+colour,q),j] += 1 W[2][u(4+colour,q),j] += 1''')open("/work/policy_design_v5.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v5.npz",**tensors)print("Saved v5: safety certificates that do not mistake duplicated liberties for two eyes.")Saved v5: safety certificates that do not mistake duplicated liberties for two eyes.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 6, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.7291666865348816, "wins": 70, "losses": 26, "score_as_black": 0.75, "score_as_white": 0.7083333134651184, "mean_margin": 46.80208206176758, "mean_length": 112.75, "stones_captured_per_game": 55.979166666666664, "stones_lost_per_game": 12.447916666666666, "passes_per_game": 1.8645833333333333, "games_with_a_pass_before_move_60": 0, "end_two_passes": 84, "end_repetition": 12, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.90625, "wins": 29, "losses": 3, "score_as_black": 0.9375, "score_as_white": 0.875, "mean_margin": 69.3125, "mean_length": 115.78125, "stones_captured_per_game": 71.78125, "stones_lost_per_game": 3.875, "passes_per_game": 2.125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 29, "end_repetition": 3, "end_move_limit": 0}}, "files": "/work/pass_6/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as np, collectionsfor k in [1,3,4,5,6]: gs=json.load(open("/work/pass_%d/games.json"%k)) print("\nPASS",k) for op in sorted(set(g["opponent"] for g in gs)): a=[g for g in gs if g["opponent"]==op] print(op,dict(collections.Counter((g["end"],g["result"]) for g in a)), "loss margins",[g["margin"] for g in a if g["result"]<0])gs=json.load(open("/work/pass_6/games.json"))bs=np.load("/work/pass_6/boards.npy")for i,g in enumerate(gs): if g["end"]=="repetition" and g["result"]<0: print("\nREPETITION",i,"colour",g["net_color"],"margin",g["margin"]) print(json.dumps(g["moves"][-16:])) t=len(g["moves"])-1 b=bs[i,t-1] print("\n".join("".join({0:".",1:"X",-1:"O"}[int(v)] for v in row) for row in b.reshape(9,9))) if i>40: break
PASS 1random {('two passes', 1): 32} loss margins []strong {('two passes', 1): 36, ('two passes', -1): 26, ('repetition', -1): 1, ('repetition', 1): 1} loss margins [-8.5, -14.5, -9.5, 9.5, -21.5, -8.5, -17.5, -12.5, -27.5, -9.5, -20.5, -22.5, -12.5, -20.5, -19.5, -17.5, -6.5, -3.5, -21.5, -1.5, -24.5, -1.5, -9.5, -1.5, -4.5, -0.5, -2.5]weak {('two passes', 1): 29, ('two passes', -1): 3} loss margins [-0.5, -7.5, -11.5]
PASS 3strong {('two passes', 1): 58, ('two passes', -1): 30, ('repetition', -1): 7, ('repetition', 1): 1} loss margins [-13.5, -5.5, -0.5, -18.5, -9.5, 5.5, 0.5, -25.5, -15.5, -20.5, -15.5, -3.5, -2.5, -5.5, -6.5, -2.5, -7.5, -13.5, -4.5, 10.5, -11.5, -1.5, -29.5, 13.5, -11.5, -10.5, -7.5, -10.5, -0.5, 10.5, -8.5, -0.5, -2.5, -9.5, -6.5, -9.5, 45.5]weak {('two passes', 1): 30, ('two passes', -1): 1, ('repetition', -1): 1} loss margins [-3.5, 18.5]
PASS 4strong {('two passes', 1): 52, ('two passes', -1): 36, ('repetition', -1): 7, ('repetition', 1): 1} loss margins [-13.5, -24.5, -4.5, -22.5, -2.5, -23.5, -10.5, -16.5, -8.5, -13.5, -30.5, 12.5, -12.5, -10.5, -0.5, 15.5, -8.5, -11.5, -9.5, -20.5, -25.5, -31.5, -9.5, 13.5, -33.5, -17.5, -1.5, -9.5, -1.5, 20.5, -18.5, -4.5, -2.5, -4.5, -7.5, -7.5, -6.5, -1.5, -8.5, -11.5, -2.5, 9.5, -2.5]weak {('repetition', -1): 3, ('two passes', -1): 5, ('two passes', 1): 24} loss margins [-15.5, -1.5, -32.5, -1.5, 16.5, -3.5, -1.5, 30.5]
PASS 5strong {('two passes', 1): 70, ('repetition', -1): 9, ('two passes', -1): 15, ('move limit', -1): 1, ('repetition', 1): 1} loss margins [9.5, 37.5, -9.5, -5.5, -50.5, -16.5, -1.5, -11.5, -4.5, -5.5, 11.5, -4.5, -11.5, -7.5, -4.5, -3.5, -4.5, 20.5, -0.5, -4.5, 5.5, -7.5, -7.5, 14.5, 33.5]weak {('two passes', 1): 30, ('two passes', -1): 1, ('repetition', -1): 1} loss margins [-17.5, 19.5]
PASS 6strong {('two passes', 1): 68, ('two passes', -1): 16, ('repetition', 1): 2, ('repetition', -1): 10} loss margins [-2.5, -10.5, -5.5, -0.5, -13.5, -5.5, -2.5, -14.5, -17.5, -12.5, -11.5, -5.5, -15.5, 26.5, -2.5, -1.5, -8.5, 2.5, 11.5, 52.5, -5.5, -0.5, -6.5, 10.5, -2.5, -1.5]weak {('two passes', 1): 29, ('repetition', -1): 3} loss margins [25.5, 27.5, 30.5]
REPETITION 7 colour black margin -5.5[{"ply": 55, "by": "opponent", "move": 2}, {"ply": 56, "by": "net", "move": 76, "top5": [[78, 0.4573], [76, 0.3751], [18, 0.0691], [62, 0.0527], [16, 0.0257]]}, {"ply": 57, "by": "opponent", "move": 16}, {"ply": 58, "by": "net", "move": 78, "top5": [[78, 1.0], [6, 0.0], [18, 0.0], [62, 0.0], [9, 0.0]]}, {"ply": 59, "by": "opponent", "move": 5}, {"ply": 60, "by": "net", "move": 18, "top5": [[18, 0.5104], [62, 0.3896], [9, 0.0492], [63, 0.0133], [73, 0.0133]]}, {"ply": 61, "by": "opponent", "move": 7}, {"ply": 62, "by": "net", "move": 9, "top5": [[9, 0.7841], [62, 0.1943], [63, 0.0066], [73, 0.0066], [71, 0.0049]]}, {"ply": 63, "by": "opponent", "move": 1}, {"ply": 64, "by": "net", "move": 62, "top5": [[62, 0.9011], [63, 0.0308], [73, 0.0308], [71, 0.0225], [64, 0.0076]]}, {"ply": 65, "by": "opponent", "move": 79}, {"ply": 66, "by": "net", "move": 80, "top5": [[80, 1.0], [53, 0.0], [71, 0.0], [63, 0.0], [73, 0.0]]}, {"ply": 67, "by": "opponent", "move": 3}, {"ply": 68, "by": "net", "move": 53, "top5": [[53, 0.848], [63, 0.0565], [73, 0.0565], [13, 0.015], [64, 0.014]]}, {"ply": 69, "by": "opponent", "move": 71}, {"ply": 70, "by": "net", "move": 62, "top5": [[62, 1.0], [63, 0.0], [73, 0.0], [13, 0.0], [64, 0.0]]}].OOOOO.O.XO...OXO.XOOO.OOOOXOXOOO.O..XXXOXOOOX.XXOXXO..X.XOXOO...X.XXXXO...XX.X.X
REPETITION 9 colour black margin -0.5[{"ply": 41, "by": "opponent", "move": 2}, {"ply": 42, "by": "net", "move": 28, "top5": [[28, 0.3706], [1, 0.1466], [25, 0.1171], [10, 0.115], [44, 0.0935]]}, {"ply": 43, "by": "opponent", "move": 63}, {"ply": 44, "by": "net", "move": 44, "top5": [[1, 0.2094], [10, 0.1718], [25, 0.1672], [44, 0.1335], [41, 0.1001]]}, {"ply": 45, "by": "opponent", "move": 46}, {"ply": 46, "by": "net", "move": 42, "top5": [[42, 1.0], [1, 0.0], [10, 0.0], [25, 0.0], [41, 0.0]]}, {"ply": 47, "by": "opponent", "move": 54}, {"ply": 48, "by": "net", "move": 41, "top5": [[41, 0.9996], [50, 0.0002], [1, 0.0001], [10, 0.0001], [3, 0.0]]}, {"ply": 49, "by": "opponent", "move": 50}, {"ply": 50, "by": "net", "move": 66, "top5": [[1, 0.4281], [10, 0.3512], [3, 0.0996], [18, 0.0263], [36, 0.0183]]}, {"ply": 51, "by": "opponent", "move": 43}, {"ply": 52, "by": "net", "move": 42, "top5": [[42, 1.0], [1, 0.0], [10, 0.0], [25, 0.0], [56, 0.0]]}, {"ply": 53, "by": "opponent", "move": 18}, {"ply": 54, "by": "net", "move": 41, "top5": [[41, 1.0], [10, 0.0], [1, 0.0], [56, 0.0], [3, 0.0]]}, {"ply": 55, "by": "opponent", "move": 43}, {"ply": 56, "by": "net", "move": 42, "top5": [[42, 1.0], [10, 0.0], [1, 0.0], [25, 0.0], [56, 0.0]]}]..O.X.X..O.XX.X.X.OXX.XXX...XXXOOOX..OOOO..OX.O.OOOOXXO...O..O.O..X.OOO..........
REPETITION 19 colour black margin -2.5[{"ply": 37, "by": "opponent", "move": 26}, {"ply": 38, "by": "net", "move": 38, "top5": [[38, 0.9654], [17, 0.0346], [44, 0.0], [11, 0.0], [19, 0.0]]}, {"ply": 39, "by": "opponent", "move": 53}, {"ply": 40, "by": "net", "move": 17, "top5": [[17, 0.994], [44, 0.006], [61, 0.0], [62, 0.0], [66, 0.0]]}, {"ply": 41, "by": "opponent", "move": 16}, {"ply": 42, "by": "net", "move": 61, "top5": [[44, 0.5232], [61, 0.2266], [62, 0.1008], [66, 0.0537], [11, 0.0469]]}, {"ply": 43, "by": "opponent", "move": 70}, {"ply": 44, "by": "net", "move": 44, "top5": [[44, 1.0], [66, 0.0], [11, 0.0], [65, 0.0], [56, 0.0]]}, {"ply": 45, "by": "opponent", "move": 26}, {"ply": 46, "by": "net", "move": 62, "top5": [[62, 0.6726], [25, 0.3274], [66, 0.0], [11, 0.0], [65, 0.0]]}, {"ply": 47, "by": "opponent", "move": 71}, {"ply": 48, "by": "net", "move": 25, "top5": [[25, 1.0], [66, 0.0], [11, 0.0], [65, 0.0], [56, 0.0]]}, {"ply": 49, "by": "opponent", "move": 64}, {"ply": 50, "by": "net", "move": 24, "top5": [[24, 0.8907], [56, 0.0572], [65, 0.0389], [66, 0.0077], [11, 0.0043]]}, {"ply": 51, "by": "opponent", "move": 26}, {"ply": 52, "by": "net", "move": 25, "top5": [[25, 1.0], [56, 0.0], [65, 0.0], [66, 0.0], [11, 0.0]]}].......O....XXOOOX..XOOO..O.X.XXOOOX.XXXOXOXX...XOXXX..X.XOOOXX.O..OO.OO.........
REPETITION 44 colour black margin -5.5[{"ply": 75, "by": "opponent", "move": 63}, {"ply": 76, "by": "net", "move": 54, "top5": [[54, 1.0], [5, 0.0], [3, 0.0], [73, 0.0], [10, 0.0]]}, {"ply": 77, "by": "opponent", "move": 19}, {"ply": 78, "by": "net", "move": 10, "top5": [[5, 0.3382], [10, 0.2825], [18, 0.2359], [3, 0.1148], [11, 0.0272]]}, {"ply": 79, "by": "opponent", "move": 5}, {"ply": 80, "by": "net", "move": 3, "top5": [[18, 0.6525], [3, 0.3475], [2, 0.0], [11, 0.0], [12, 0.0]]}, {"ply": 81, "by": "opponent", "move": 73}, {"ply": 82, "by": "net", "move": 18, "top5": [[18, 0.6192], [74, 0.3808], [43, 0.0], [2, 0.0], [11, 0.0]]}, {"ply": 83, "by": "opponent", "move": 43}, {"ply": 84, "by": "net", "move": 74, "top5": [[74, 1.0], [2, 0.0], [53, 0.0], [1, 0.0], [12, 0.0]]}, {"ply": 85, "by": "opponent", "move": 12}, {"ply": 86, "by": "net", "move": 11, "top5": [[11, 1.0], [2, 0.0], [75, 0.0], [1, 0.0], [53, 0.0]]}, {"ply": 87, "by": "opponent", "move": 53}, {"ply": 88, "by": "net", "move": 75, "top5": [[75, 0.9768], [2, 0.0106], [0, 0.006], [1, 0.0038], [9, 0.0023]]}, {"ply": 89, "by": "opponent", "move": 73}, {"ply": 90, "by": "net", "move": 74, "top5": [[74, 1.0], [2, 0.0], [0, 0.0], [1, 0.0], [9, 0.0]]}]...XOOOO..XX.XXXOOX.XXXOOO..X.XOXOOOXXXXOX.O.X.XXOOOOOXXXOOXXO..XOOOO.OOXO..O.OO.import json, numpy as npsource=open("/work/policy_design_v5.py").read()source=source.replace( " # Layer 4: approximate whole-group atari, self-atari, weak enemies.", ''' # The unused opponent-extension channel instead detects exactly # an enemy singleton with one liberty. for p in range(81): j=u(5,p) W[2][:,j]=0 B[2][j]=-len(adj[p]) W[2][u(1,p),j]=2 for q in adj[p]: W[2][u(0,q),j]+=1 W[2][u(1,q),j]-=1
# Layer 4: approximate whole-group atari, self-atari, weak enemies.''')source=source.replace( " # Layer 5: spatial prior, captures, attack, safe rescue, and", ''' # Avoid isolated singleton recaptures with at most one other # liberty. These are common ko/snapback repetition traps. for p in range(81): j=u(5,p) W[3][:,j]=0 B[3][j]=-len(adj[p]) for q in adj[p]: W[3][u(5,q),j]+=2 W[3][u(0,q),j]-=3 W[3][u(1,q),j]+=1
# Layer 5: spatial prior, captures, attack, safe rescue, and''')source=source.replace( ' W[4][u(5,q),j] += cfg["attack"]', ' W[4][u(5,p),j] -= cfg.get("repeat_risk",18.0)')# The replacement above puts a single risk term after the adjacency loop.source=source.replace("temperature_scale=1.8,", "temperature_scale=1.8,\n repeat_risk=18.0,")cfg=json.load(open("/work/policy_config.json"))cfg.update(capture=12.5, repeat_risk=18.0)open("/work/policy_design_v6.py","w").write(source)open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v6.npz",**tensors)print("Saved v6: explicit singleton ko/snapback risk avoidance.")Saved v6: explicit singleton ko/snapback risk avoidance.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 7, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.6145833134651184, "wins": 59, "losses": 37, "score_as_black": 0.5416666865348816, "score_as_white": 0.6875, "mean_margin": 33.47916793823242, "mean_length": 111.19791666666667, "stones_captured_per_game": 48.625, "stones_lost_per_game": 14.03125, "passes_per_game": 2.0, "games_with_a_pass_before_move_60": 0, "end_two_passes": 92, "end_repetition": 4, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.9375, "wins": 30, "losses": 2, "score_as_black": 0.9375, "score_as_white": 0.9375, "mean_margin": 65.65625, "mean_length": 120.1875, "stones_captured_per_game": 75.625, "stones_lost_per_game": 4.4375, "passes_per_game": 2.5, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_7/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v5.py").read()source=source.replace( " # Layer 4: approximate whole-group atari, self-atari, weak enemies.", ''' # Exact one-stone capture detector in a previously unused channel. for p in range(81): j=u(5,p) W[2][:,j]=0 B[2][j]=-len(adj[p]) W[2][u(1,p),j]=2 for q in adj[p]: W[2][u(0,q),j]+=1 W[2][u(1,q),j]-=1
# Layer 4: approximate whole-group atari, self-atari, weak enemies.''')patch=r''' # At layer 4, encode own/empty/enemy as 1, 1/2, 0. This frees # a full channel for repetition-risk detection without losing attack. for p in range(81): old_weak=W[3][:,u(5,p)].copy() old_bias=B[3][u(5,p)] W[3][:,u(1,p)]=old_weak B[3][u(1,p)]=old_bias W[3][:,u(0,p)]=0 B[3][u(0,p)]=0.5 W[3][u(0,p),u(0,p)]=0.5 W[3][u(1,p),u(0,p)]=-0.5
j=u(5,p) W[3][:,j]=0 B[3][j]=-len(adj[p]) for q in adj[p]: W[3][u(5,q),j]+=2 W[3][u(0,q),j]-=3 W[3][u(1,q),j]+=1
# Decode the board for the final shape layer. for ch,sign,bias in [(0,2,-1),(1,-2,1)]: j=u(ch,p) W[4][:,j]=0 B[4][j]=bias W[4][u(0,p),j]=sign
# Tactical score only here; move the spatial prior to layer 6. j=u(2,p) W[4][:,j]=0 B[4][j]=100 for q in adj[p]: W[4][u(3,q),j]+=cfg["capture"] W[4][u(1,q),j]+=cfg["attack"] W[4][u(5,p),j]-=cfg.get("repeat_risk",18.0)
j=u(5,p) W[4][:,j]=0 B[4][j]=1-2*len(adj[p]) for q in adj[p]: W[4][u(0,q),j]=2
j=u(0,p) r,c=divmod(p,9) B[5][j]=line_prior[min(r,8-r)]+line_prior[min(c,8-c)] for q,dr,dc in rings[p]: if dr+dc==1: a,b=cfg["own_adj"],cfg["enemy_adj"] elif dr==dc==1: a,b=cfg["own_diag"],cfg["enemy_diag"] elif dr+dc==2: a,b=cfg["own_two"],cfg["enemy_two"] elif sorted([dr,dc])==[1,2]: a,b=cfg["own_knight"],0.0 else: continue W[5][u(0,q),j]+=a W[5][u(1,q),j]+=b'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v7.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(capture=9.0,repeat_risk=18.0)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v7.npz",**tensors)print("Saved v7: packed colour channel preserves weak-group attacks while avoiding ko/snapback traps.")Saved v7: packed colour channel preserves weak-group attacks while avoiding ko/snapback traps.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 8, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.7916666865348816, "wins": 76, "losses": 20, "score_as_black": 0.6666666865348816, "score_as_white": 0.9166666865348816, "mean_margin": 45.61458206176758, "mean_length": 114.44791666666667, "stones_captured_per_game": 60.135416666666664, "stones_lost_per_game": 11.947916666666666, "passes_per_game": 2.15625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 91, "end_repetition": 3, "end_move_limit": 2}, "weak": {"games": 32, "score": 0.96875, "wins": 31, "losses": 1, "score_as_black": 0.9375, "score_as_white": 1.0, "mean_margin": 59.21875, "mean_length": 111.71875, "stones_captured_per_game": 64.09375, "stones_lost_per_game": 3.03125, "passes_per_game": 2.5, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_8/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v7.py").read()patch=r''' # Komi-aware territorial ambition: Black needs more than half the # board, while White can consolidate a more modest share. for p in range(81): B[5][u(3,p)]=-cfg.get("black_territory_threshold",0.75) W[5][488,u(3,p)]=( cfg.get("black_territory_threshold",0.75) -cfg.get("white_territory_threshold",1.5))
# Passing is safer when materially ahead, riskier when behind. lead=cfg.get("pass_lead",0.10) OW[486,81]+=lead OW[487,81]-=lead OB[81]-=7.5*lead OW[488,81]+=15.0*lead'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v8.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(black_territory_threshold=0.75,white_territory_threshold=1.5,pass_lead=0.10)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v8.npz",**tensors)print("Saved v8: komi-aware expansion and material-aware passing.")Saved v8: komi-aware expansion and material-aware passing.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 9, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.7291666865348816, "wins": 70, "losses": 26, "score_as_black": 0.6875, "score_as_white": 0.7708333134651184, "mean_margin": 42.88541793823242, "mean_length": 116.72916666666667, "stones_captured_per_game": 57.739583333333336, "stones_lost_per_game": 15.84375, "passes_per_game": 3.0729166666666665, "games_with_a_pass_before_move_60": 0, "end_two_passes": 93, "end_repetition": 1, "end_move_limit": 2}, "weak": {"games": 32, "score": 0.90625, "wins": 29, "losses": 3, "score_as_black": 0.875, "score_as_white": 0.9375, "mean_margin": 66.59375, "mean_length": 119.78125, "stones_captured_per_game": 79.625, "stones_lost_per_game": 5.0625, "passes_per_game": 2.875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_9/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as np, collectionsg=json.load(open("/work/pass_9/games.json"))b=np.load("/work/pass_9/boards.npy")print("Loss breakdown",dict(collections.Counter((x["net_color"],x["end"]) for x in g if x["opponent"]=="strong" and x["result"]<0)))for i,x in enumerate(g): if x["opponent"]!="strong" or x["result"]>0 or x["net_color"]!="black": continue print("\nGAME",i,"margin",x["margin"],"lost",x["stones_lost_by_net"], "captured",x["stones_captured_by_net"],"end",x["end"]) for t in [19,39,59,len(x["moves"])-1]: if t>=len(x["moves"]): continue print("after",t+1) bb=b[i,t].reshape(9,9) print("\n".join("".join({0:".",1:"X",-1:"O"}[int(z)] for z in row) for row in bb)) print("last net moves",[m for m in x["moves"][-10:] if m["by"]=="net"]) if i>=10: breakLoss breakdown {('black', 'two passes'): 13, ('black', 'move limit'): 1, ('black', 'repetition'): 1, ('white', 'two passes'): 11}
GAME 6 margin -4.5 lost 22 captured 16 end two passesafter 20...............O.....OXXX....XXOO.....XOO......XOO......XOXX.......O.............after 40....X.......X.XOX...X.XXXO...XXOOOO...XOOXOX...XOOXX....XOXXX....OOOOOO..........after 60..X.X.X.X...X.X.XOO.X.XXXOO.OXXOOOOO.XXOO.O.O..XOO.....XXO...O..XOOOOOOO..X...O..after 109X.X.X.XOOXX.X.XOOO.XX.XXXOOX.XXOOOOOXXXOO.O.OX.XOOOXOOXXXO.XXOO.XOOOOOOOXXX.OOO..last net moves [{'ply': 100, 'by': 'net', 'move': 74, 'top5': [[74, 1.0], [81, 0.0], [75, 0.0], [79, 0.0], [80, 0.0]]}, {'ply': 102, 'by': 'net', 'move': 73, 'top5': [[73, 0.5314], [75, 0.4685], [81, 0.0], [72, 0.0], [79, 0.0]]}, {'ply': 104, 'by': 'net', 'move': 72, 'top5': [[72, 1.0], [6, 0.0], [75, 0.0], [81, 0.0], [79, 0.0]]}, {'ply': 106, 'by': 'net', 'move': 6, 'top5': [[6, 1.0], [81, 0.0], [79, 0.0], [80, 0.0], [75, 0.0]]}, {'ply': 108, 'by': 'net', 'move': 81, 'top5': [[81, 0.7941], [5, 0.2059], [79, 0.0], [80, 0.0], [75, 0.0]]}]
GAME 9 margin -0.5 lost 11 captured 9 end two passesafter 20...........X........XOX......OOOO.....X.OX......OOX.....XXOXX......O.............after 40...X......XXOX....X.XOXOOO..XOOOOXO..OXXOXXX...XOOX.....XXOXX....OOOO............after 60..O..O...OXXO..O..X.XO.OOO..XOOOOXOXX.XXOXXXO.XXOOX.X.XXXXOXXO.OXOOOOXO..........after 91XXOO.O.X..XXOXOOO.X.XOXOOOO.XOOOOXOOX.XXOXXXO.XXOOX.XXXXXXOXXXXOXOOOOX.X.OOOOOOX.last net moves [{'ply': 82, 'by': 'net', 'move': 4, 'top5': [[4, 1.0], [14, 0.0], [7, 0.0], [8, 0.0], [81, 0.0]]}, {'ply': 84, 'by': 'net', 'move': 7, 'top5': [[7, 0.8762], [8, 0.0924], [81, 0.0314], [6, 0.0001], [17, 0.0]]}, {'ply': 86, 'by': 'net', 'move': 13, 'top5': [[13, 0.9869], [8, 0.0114], [81, 0.0013], [22, 0.0004], [17, 0.0]]}, {'ply': 88, 'by': 'net', 'move': 22, 'top5': [[22, 1.0], [4, 0.0], [8, 0.0], [81, 0.0], [6, 0.0]]}, {'ply': 90, 'by': 'net', 'move': 81, 'top5': [[8, 0.8073], [81, 0.1913], [17, 0.001], [6, 0.0004], [72, 0.0]]}]
GAME 10 margin -10.5 lost 28 captured 12 end two passesafter 20............X..........X......XXOO.....XOX....XXXOX.....OOOO.....O...O...........after 40......X.....XOOOX....OXXOO....XXOOX....XOXOX..XXXOXX.X.XOOOOXX...O..OOO..........after 60....O..O....XOOOXX..X.XXOOX...XXOOXX.XOXOOOX..XXXOO.OXOXOOOO..O.XO..OOO..........after 102.X.XOOOOOX.XXOOOO..XX.XXOOOX.XXXOO..XX.XOOOOO.XXXOOOOOXXOOOO.OO.XOXOOOOOXXXXOXX..last net moves [{'ply': 92, 'by': 'net', 'move': 11, 'top5': [[1, 0.8061], [11, 0.1826], [34, 0.008], [26, 0.0028], [17, 0.0005]]}, {'ply': 94, 'by': 'net', 'move': 1, 'top5': [[1, 1.0], [34, 0.0], [26, 0.0], [80, 0.0], [0, 0.0]]}, {'ply': 96, 'by': 'net', 'move': 34, 'top5': [[34, 0.7598], [26, 0.2401], [81, 0.0001], [79, 0.0], [17, 0.0]]}, {'ply': 98, 'by': 'net', 'move': 81, 'top5': [[81, 0.9899], [79, 0.0101], [80, 0.0001], [21, 0.0], [63, 0.0]]}, {'ply': 100, 'by': 'net', 'move': 81, 'top5': [[81, 0.9794], [79, 0.0204], [80, 0.0001], [35, 0.0], [34, 0.0]]}]import json, numpy as npsource=open("/work/policy_design_v7.py").read()patch=r''' # On an empty 9x9 board, take the central star point. This feature # switches off exactly after either player places the first stone. W[0][:,489]=0 B[0][489]=1 for p in range(81): W[0][3*p,489]=-1 W[0][3*p+1,489]=-1 for k in range(1,6): W[k][489,489]=1 OW[489,40]+=cfg.get("opening_center",8.0)'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v9.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(opening_center=8.0,pass_lead=0.0, black_territory_threshold=1.5,white_territory_threshold=1.5)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v9.npz",**tensors)print("Saved v9: claim the center on the first move; retain v7's middlegame policy.")Saved v9: claim the center on the first move; retain v7's middlegame policy.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 10, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.6458333134651184, "wins": 62, "losses": 34, "score_as_black": 0.5416666865348816, "score_as_white": 0.75, "mean_margin": 36.19791793823242, "mean_length": 113.26041666666667, "stones_captured_per_game": 52.96875, "stones_lost_per_game": 16.416666666666668, "passes_per_game": 2.09375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 93, "end_repetition": 2, "end_move_limit": 1}, "weak": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 64.625, "mean_length": 117.75, "stones_captured_per_game": 74.875, "stones_lost_per_game": 4.25, "passes_per_game": 3.53125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_10/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v7.py").read()patch=r''' # Defend vulnerable groups before their final liberty is taken. # The feature is graded: strongest in atari, weaker with nearby # evidence of additional liberties. for p in range(81): j=u(2,p) W[3][:,j]=0 B[3][j]=0 W[3][u(0,p),j]=3 W[3][u(2,p),j]=-1 for q in adj[p]: W[3][u(2,q),j]-=0.5 W[4][u(4,p),u(4,p)]=-12'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v10.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(rescue=2.2,opening_center=0.0)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v10.npz",**tensors)print("Saved v10: graded preventive defense, with suicidal rescues still suppressed.")Saved v10: graded preventive defense, with suicidal rescues still suppressed.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 11, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.6875, "wins": 66, "losses": 30, "score_as_black": 0.625, "score_as_white": 0.75, "mean_margin": 36.65625, "mean_length": 110.58333333333333, "stones_captured_per_game": 51.34375, "stones_lost_per_game": 13.3125, "passes_per_game": 2.2083333333333335, "games_with_a_pass_before_move_60": 0, "end_two_passes": 90, "end_repetition": 4, "end_move_limit": 2}, "weak": {"games": 32, "score": 0.96875, "wins": 31, "losses": 1, "score_as_black": 1.0, "score_as_white": 0.9375, "mean_margin": 66.25, "mean_length": 115.75, "stones_captured_per_game": 75.59375, "stones_lost_per_game": 1.71875, "passes_per_game": 2.8125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_11/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v7.py").read()patch=r''' # Topological pressure: an empty point between enemy stones that # lack diagonal support can separate their groups. for p in range(81): j=u(5,p) W[5][:,j]=0 B[5][j]=-1 for q in adj[p]: W[5][u(1,q),j]+=1 for q in diag[p]: W[5][u(1,q),j]-=1 OW[j,p]=cfg.get("cut",1.5)'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v11.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(rescue=6.0,connection=1.8,surrounded_penalty=17.0,cut=1.5)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v11.npz",**tensors)print("Saved v11: stronger own connections and pressure on unsupported enemy cuts.")Saved v11: stronger own connections and pressure on unsupported enemy cuts.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 12, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.6770833134651184, "wins": 65, "losses": 31, "score_as_black": 0.6041666865348816, "score_as_white": 0.75, "mean_margin": 40.5625, "mean_length": 112.9375, "stones_captured_per_game": 55.0, "stones_lost_per_game": 15.510416666666666, "passes_per_game": 1.9791666666666667, "games_with_a_pass_before_move_60": 0, "end_two_passes": 94, "end_repetition": 1, "end_move_limit": 1}, "weak": {"games": 32, "score": 0.9375, "wins": 30, "losses": 2, "score_as_black": 0.875, "score_as_white": 1.0, "mean_margin": 63.0, "mean_length": 117.25, "stones_captured_per_game": 77.15625, "stones_lost_per_game": 4.15625, "passes_per_game": 3.4375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_12/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=r'''import numpy as npDEFAULTS=dict( capture=6.45,rescue=6.0,attack=2.0,self_atari=7.5, surrounded_penalty=17.0,connection=0.9,contact=1.4, own_territory=2.5,enemy_territory=1.0,territory_threshold=1.5, pass_base=-9.0,pass_per_stone=0.10,pass_white=0.35, own_adj=0.20,own_diag=0.50,own_two=0.70,own_knight=0.30, enemy_adj=0.70,enemy_diag=0.40,enemy_two=0.25, repeat_risk=18.0,temperature_scale=1.8,)def build(save,overrides=None): c=dict(DEFAULTS) if overrides: c.update(overrides) W=[np.zeros((243 if k==0 else 512,512),np.float32) for k in range(6)] B=[np.zeros(512,np.float32) for k in range(6)] OW=np.zeros((512,82),np.float32); OB=np.zeros(82,np.float32) def u(ch,p): return 81*ch+p adj=[]; diag=[]; rings=[] for p in range(81): r,s=divmod(p,9) adj.append([a*9+b for a,b in [(r-1,s),(r+1,s),(r,s-1),(r,s+1)] if 0<=a<9 and 0<=b<9]) diag.append([a*9+b for a,b in [(r-1,s-1),(r-1,s+1),(r+1,s-1),(r+1,s+1)] if 0<=a<9 and 0<=b<9]) rings.append([(q,abs(r-q//9),abs(s-q%9)) for q in range(81) if q!=p]) for p in range(81): W[0][3*p,u(0,p)]=1; W[0][3*p+1,u(1,p)]=1 for col in [0,1]: j=u(2+col,p); B[0][j]=len(adj[p])-4 W[0][3*p+col,j]=4 for q in adj[p]: W[0][3*q,j]-=1; W[0][3*q+1,j]-=1 B[0][u(4,p)]=-1; B[0][u(5,p)]=-1 for q in adj[p]: W[0][3*q+1,u(4,p)]+=1 W[0][3*q,u(4,p)]-=0.75 W[0][3*q,u(5,p)]+=1 for q in diag[p]: W[0][3*q,u(4,p)]-=0.20 W[0][3*q,u(5,p)]-=0.65 W[0][3*p,486]=1; W[0][3*p+1,487]=1 W[0][2,488]=1 for k in range(1,6): for g in [486,487,488]: W[k][g,g]=1
line=[-1.5,0.25,1.2,0.9,1.0] for p in range(81): for col in [0,1]: W[1][u(col,p),u(col,p)]=1 j=u(2+col,p) B[1][j]=-22; W[1][u(col,p),j]=20 W[1][u(2+col,p),j]=2 for q in adj[p]: W[1][u(2+col,q),j]+=1 # Excess direct liberties, used to correct the doubled centre. W[1][u(2,p),u(4,p)]=1 W[1][u(0,p),u(4,p)]=-1 j=u(5,p); r,s=divmod(p,9) B[1][j]=100+line[min(r,8-r)]+line[min(s,8-s)] W[1][u(4,p),j]=-c["contact"] W[1][u(5,p),j]=c["connection"] for q,dr,dc in rings[p]: if dr+dc==1: a,b=c["own_adj"],c["enemy_adj"] elif dr==dc==1: a,b=c["own_diag"],c["enemy_diag"] elif dr+dc==2: a,b=c["own_two"],c["enemy_two"] elif sorted([dr,dc])==[1,2]: a,b=c["own_knight"],0 else: continue W[1][u(0,q),j]+=a; W[1][u(1,q),j]+=b
# Layer 3: packed colour, two safety fields, extension liberties, # exact singleton-atari detector, and accumulated shape score. for p in range(81): B[2][u(0,p)]=0.5 W[2][u(0,p),u(0,p)]=0.5 W[2][u(1,p),u(0,p)]=-0.5 for col in [0,1]: j=u(1+col,p); B[2][j]=-100 W[2][u(col,p),j]=100 for q in [p]+adj[p]: W[2][u(2+col,q),j]+=1 j=u(3,p); B[2][j]=len(adj[p]) for q in adj[p]: W[2][u(0,q),j]-=1; W[2][u(1,q),j]-=1 W[2][u(2,q),j]+=1; W[2][u(4,q),j]-=1 j=u(4,p); B[2][j]=-len(adj[p]) W[2][u(1,p),j]=2 for q in adj[p]: W[2][u(0,q),j]+=1; W[2][u(1,q),j]-=1 W[2][u(5,p),u(5,p)]=1
# Layer 4 adds another full graph-propagation step. for p in range(81): W[3][u(0,p),u(0,p)]=1 for col in [0,1]: j=u(1+col,p) if col==0: B[3][j]=-1000 W[3][u(0,p),j]=1000 else: W[3][u(0,p),j]=-1000 for q in [p]+adj[p]: W[3][u(1+col,q),j]+=1 j=u(3,p); B[3][j]=2 W[3][u(3,p),j]=-1 j=u(4,p) for q in adj[p]: W[3][u(4,q),j]+=2 W[3][u(0,q),j]-=2 W[3][u(5,p),u(5,p)]=1
# Layer 5: atari reaches four links, with separate graded attack. for p in range(81): W[4][u(0,p),u(0,p)]=1 j=u(1,p); B[4][j]=-19 W[4][u(0,p),j]=20 for q in [p]+adj[p]: W[4][u(1,q),j]-=1
j=u(2,p); B[4][j]=1 W[4][u(0,p),j]=-20 for q in [p]+adj[p]: W[4][u(2,q),j]-=1
j=u(3,p); B[4][j]=3 W[4][u(0,p),j]=-20 W[4][u(2,p),j]=-1 for q in adj[p]: W[4][u(2,q),j]-=0.5
W[4][u(3,p),u(4,p)]=1 W[4][u(5,p),u(5,p)]=1 W[4][u(4,p),u(5,p)]=-c["repeat_risk"]
for p in range(81): j=u(0,p); W[5][u(5,p),j]=1 for q in adj[p]: W[5][u(2,q),j]+=c["capture"] W[5][u(3,q),j]+=c["attack"]
j=u(1,p); W[5][u(4,p),j]=-4 for q in adj[p]: W[5][u(1,q),j]+=1 j=u(2,p); W[5][u(4,p),j]=1 for q in adj[p]: W[5][u(2,q),j]-=2
for ch,sign in [(3,1),(5,-1)]: j=u(ch,p); B[5][j]=-c["territory_threshold"] for q,dr,dc in rings[p]: if dr+dc==1: a=1.0 elif dr==dc==1: a=0.65 elif dr+dc==2: a=0.45 elif sorted([dr,dc])==[1,2]: a=0.20 else: continue W[5][u(0,q),j]+=2*sign*a B[5][j]-=sign*a
j=u(4,p); B[5][j]=1-2*len(adj[p]) for q in adj[p]: W[5][u(0,q),j]+=2 for ch,a in [(0,1),(1,c["rescue"]),(2,-c["self_atari"]), (3,-c["own_territory"]),(4,-c["surrounded_penalty"]), (5,-c["enemy_territory"])]: OW[u(ch,p),p]=a OB[p]=-100 OB[81]=c["pass_base"] OW[486,81]=OW[487,81]=c["pass_per_stone"] OW[488,81]=c["pass_white"] OW*=c["temperature_scale"]; OB*=c["temperature_scale"] ts={} for k in range(6): ts["L%d.W"%(k+1)]=W[k]; ts["L%d.b"%(k+1)]=B[k] ts["out.W"]=OW; ts["out.b"]=OB for n,a in ts.items(): assert np.isfinite(a).all() save(n,a) return c,ts'''open("/work/policy_design_v12.py","w").write(source)open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v12.npz",**tensors)print("Saved v12: packed board representation permits deeper tactical propagation.")Saved v12: packed board representation permits deeper tactical propagation.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 13, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.6458333134651184, "wins": 62, "losses": 34, "score_as_black": 0.7083333134651184, "score_as_white": 0.5833333134651184, "mean_margin": 35.48958206176758, "mean_length": 111.41666666666667, "stones_captured_per_game": 51.458333333333336, "stones_lost_per_game": 14.520833333333334, "passes_per_game": 2.5833333333333335, "games_with_a_pass_before_move_60": 0, "end_two_passes": 92, "end_repetition": 4, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.9375, "wins": 30, "losses": 2, "score_as_black": 0.9375, "score_as_white": 0.9375, "mean_margin": 57.8125, "mean_length": 115.625, "stones_captured_per_game": 67.5625, "stones_lost_per_game": 5.96875, "passes_per_game": 3.53125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_13/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v7.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg=dict( capture=9.0,rescue=6.0,attack=1.15,self_atari=7.5, eye=11.0,connection=0.9,contact=1.4, own_territory=2.5,enemy_territory=1.0,territory_threshold=1.5, pass_base=-12.0,pass_per_stone=0.10,pass_white=3.35, own_adj=0.20,own_diag=0.50,own_two=0.70,own_knight=0.30, enemy_adj=0.70,enemy_diag=0.40,enemy_two=0.25, temperature_scale=2.5,surrounded_penalty=10.0,repeat_risk=18.0,)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v13.npz",**tensors)print("Saved v13: more decisive shape choices and greater endgame persistence as Black.")Saved v13: more decisive shape choices and greater endgame persistence as Black.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 14, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.8020833134651184, "wins": 77, "losses": 19, "score_as_black": 0.7916666865348816, "score_as_white": 0.8125, "mean_margin": 47.16666793823242, "mean_length": 113.95833333333333, "stones_captured_per_game": 58.541666666666664, "stones_lost_per_game": 11.625, "passes_per_game": 2.0208333333333335, "games_with_a_pass_before_move_60": 0, "end_two_passes": 95, "end_repetition": 0, "end_move_limit": 1}, "weak": {"games": 32, "score": 0.96875, "wins": 31, "losses": 1, "score_as_black": 1.0, "score_as_white": 0.9375, "mean_margin": 62.84375, "mean_length": 114.875, "stones_captured_per_game": 73.53125, "stones_lost_per_game": 3.625, "passes_per_game": 2.78125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_14/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 15, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.7578125, "wins": 97, "losses": 31, "score_as_black": 0.765625, "score_as_white": 0.75, "mean_margin": 44.671875, "mean_length": 113.25, "stones_captured_per_game": 59.890625, "stones_lost_per_game": 12.796875, "passes_per_game": 2.0546875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 124, "end_repetition": 3, "end_move_limit": 1}}, "files": "/work/pass_15/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v7.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg=json.load(open("/work/policy_config.json"))cfg.update(enemy_adj=1.3,enemy_diag=0.7,enemy_two=0.4, own_adj=0.4,attack=1.4)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v14.npz",**tensors)print("Saved v14: contest the opponent's boundary earlier instead of conceding central space.")Saved v14: contest the opponent's boundary earlier instead of conceding central space.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 16, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.8020833134651184, "wins": 77, "losses": 19, "score_as_black": 0.7708333134651184, "score_as_white": 0.8333333134651184, "mean_margin": 46.1875, "mean_length": 113.73958333333333, "stones_captured_per_game": 57.416666666666664, "stones_lost_per_game": 11.645833333333334, "passes_per_game": 1.5520833333333333, "games_with_a_pass_before_move_60": 0, "end_two_passes": 92, "end_repetition": 4, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.96875, "wins": 31, "losses": 1, "score_as_black": 1.0, "score_as_white": 0.9375, "mean_margin": 64.5, "mean_length": 113.1875, "stones_captured_per_game": 69.0625, "stones_lost_per_game": 3.34375, "passes_per_game": 2.40625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_16/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v7.py").read()patch=r''' # Opening line preferences should not make first-line endgame # points artificially unattractive. Flatten them as the board fills. start=cfg.get("endgame_start",12.0) finish=cfg.get("endgame_finish",52.0) for g,threshold in [(489,start),(490,finish)]: W[0][:,g]=0 B[0][g]=-threshold/(finish-start) for p in range(81): W[0][3*p,g]=1/(finish-start) W[0][3*p+1,g]=1/(finish-start) for k in range(1,6): W[k][g,g]=1 for p in range(81): r,c=divmod(p,9) prior=line_prior[min(r,8-r)]+line_prior[min(c,8-c)] delta=cfg.get("endgame_prior",2.0)-prior OW[489,p]+=delta OW[490,p]-=delta'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v15.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(endgame_start=12.0,endgame_finish=52.0,endgame_prior=2.0)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v15.npz",**tensors)print("Saved v15: opening geometry transitions to uniform point value in the endgame.")Saved v15: opening geometry transitions to uniform point value in the endgame.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 17, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.78125, "wins": 75, "losses": 21, "score_as_black": 0.7291666865348816, "score_as_white": 0.8333333134651184, "mean_margin": 48.85416793823242, "mean_length": 117.70833333333333, "stones_captured_per_game": 60.072916666666664, "stones_lost_per_game": 14.34375, "passes_per_game": 1.59375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 93, "end_repetition": 3, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.90625, "wins": 29, "losses": 3, "score_as_black": 0.875, "score_as_white": 0.9375, "mean_margin": 64.53125, "mean_length": 112.65625, "stones_captured_per_game": 65.875, "stones_lost_per_game": 3.75, "passes_per_game": 2.0625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 30, "end_repetition": 2, "end_move_limit": 0}}, "files": "/work/pass_17/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v7.py").read()patch=r''' # An endangered stone with several friendly connections is more # likely to represent a substantial group than an isolated stone. for p in range(81): j=u(2,p) W[3][:,j]=0 B[3][j]=-4 W[3][u(0,p),j]=5 for q in adj[p]: W[3][u(0,q),j]+=cfg.get("rescue_size",0.4) for q in [p]+adj[p]: W[3][u(2,q),j]-=10 W[4][u(4,p),u(4,p)]=-12'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v16.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(rescue=8.0,rescue_size=0.4)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v16.npz",**tensors)print("Saved v16: prioritize saving connected groups over harvesting isolated enemy stones.")Saved v16: prioritize saving connected groups over harvesting isolated enemy stones.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 18, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.78125, "wins": 75, "losses": 21, "score_as_black": 0.7916666865348816, "score_as_white": 0.7708333134651184, "mean_margin": 48.33333206176758, "mean_length": 113.16666666666667, "stones_captured_per_game": 55.385416666666664, "stones_lost_per_game": 11.6875, "passes_per_game": 1.5, "games_with_a_pass_before_move_60": 0, "end_two_passes": 92, "end_repetition": 4, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.96875, "wins": 31, "losses": 1, "score_as_black": 0.9375, "score_as_white": 1.0, "mean_margin": 66.75, "mean_length": 117.03125, "stones_captured_per_game": 74.8125, "stones_lost_per_game": 3.375, "passes_per_game": 2.40625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_18/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v16.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg=json.load(open("/work/policy_config.json"))cfg.update(own_adj=0.8,contact=3.0)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v17.npz",**tensors)print("Saved v17: prefer supported fighting and avoid unsupported multi-stone contact.")Saved v17: prefer supported fighting and avoid unsupported multi-stone contact.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 19, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.7708333134651184, "wins": 74, "losses": 22, "score_as_black": 0.75, "score_as_white": 0.7916666865348816, "mean_margin": 49.08333206176758, "mean_length": 115.5, "stones_captured_per_game": 64.15625, "stones_lost_per_game": 13.135416666666666, "passes_per_game": 1.8229166666666667, "games_with_a_pass_before_move_60": 0, "end_two_passes": 93, "end_repetition": 3, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.96875, "wins": 31, "losses": 1, "score_as_black": 0.9375, "score_as_white": 1.0, "mean_margin": 68.46875, "mean_length": 115.96875, "stones_captured_per_game": 73.6875, "stones_lost_per_game": 1.96875, "passes_per_game": 2.15625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_19/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v16.py").read()patch=r''' # Pack colour one layer earlier, freeing a channel for short-range # weakness. Capture certainty still uses the longer safety field. for p in range(81): j=u(0,p) W[2][:,j]=0 B[2][j]=0.5 W[2][u(0,p),j]=0.5 W[2][u(1,p),j]=-0.5
j=u(1,p) W[2][:,j]=0 B[2][j]=0 W[2][u(3,p),j]=1
j=u(0,p) W[3][:,j]=0 B[3][j]=0 W[3][u(0,p),j]=1
j=u(1,p) W[3][:,j]=0 B[3][j]=3 W[3][u(0,p),j]=-20 W[3][u(1,p),j]=-1 for q in adj[p]: W[3][u(1,q),j]-=0.5
j=u(2,p) W[3][:,j]=0 size=cfg.get("rescue_size",0.4) B[3][j]=1-0.5*size-20 W[3][u(0,p),j]=20 for q in adj[p]: W[3][u(0,q),j]+=size for q in [p]+adj[p]: W[3][u(2,q),j]-=10
j=u(3,p) W[3][:,j]=0 B[3][j]=1 W[3][u(0,p),j]=-20 for q in [p]+adj[p]: W[3][u(3,q),j]-=1
j=u(5,p) W[3][:,j]=0 B[3][j]=0 for q in adj[p]: W[3][u(5,q),j]+=2 W[3][u(0,q),j]-=2'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v18.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v18.npz",**tensors)print("Saved v18: separate short-range attack pressure from longer-range capture certification.")Saved v18: separate short-range attack pressure from longer-range capture certification.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 20, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.7708333134651184, "wins": 74, "losses": 22, "score_as_black": 0.7291666865348816, "score_as_white": 0.8125, "mean_margin": 49.55208206176758, "mean_length": 115.35416666666667, "stones_captured_per_game": 59.197916666666664, "stones_lost_per_game": 10.885416666666666, "passes_per_game": 1.4895833333333333, "games_with_a_pass_before_move_60": 0, "end_two_passes": 96, "end_repetition": 0, "end_move_limit": 0}, "weak": {"games": 32, "score": 0.96875, "wins": 31, "losses": 1, "score_as_black": 0.9375, "score_as_white": 1.0, "mean_margin": 66.96875, "mean_length": 115.53125, "stones_captured_per_game": 74.21875, "stones_lost_per_game": 1.90625, "passes_per_game": 1.9375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_20/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v18.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg=json.load(open("/work/policy_config.json"))cfg["temperature_scale"]=4.0cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v19.npz",**tensors)print("Saved v19: sharpen the policy while retaining symmetry among equally valued moves.")Saved v19: sharpen the policy while retaining symmetry among equally valued moves.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 21, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.71875, "wins": 69, "losses": 27, "score_as_black": 0.7291666865348816, "score_as_white": 0.7083333134651184, "mean_margin": 46.125, "mean_length": 115.10416666666667, "stones_captured_per_game": 56.385416666666664, "stones_lost_per_game": 13.177083333333334, "passes_per_game": 1.6875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 93, "end_repetition": 3, "end_move_limit": 0}, "weak": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 71.0625, "mean_length": 118.53125, "stones_captured_per_game": 81.0625, "stones_lost_per_game": 2.59375, "passes_per_game": 2.0625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_21/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v18.py").read()patch=r''' # Smooth influence beyond immediate contact distinguishes contested # boundaries from the interiors of larger frameworks. for p in range(81): for colour,ch in [(0,3),(1,4)]: j=u(ch,p) W[5][:,j]=0 B[5][j]=-cfg["territory_threshold"] for q,dr,dc in rings[p]: weight=float(np.exp(-(dr*dr+dc*dc-1)/ cfg.get("influence_width",3.5))) W[5][u(colour,q),j]+=weight W[5][u(1-colour,q),j]-=1.2*weight'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v20.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(temperature_scale=2.5,influence_width=3.5)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v20.npz",**tensors)print("Saved v20: broader territorial influence discourages redundant interior moves.")Saved v20: broader territorial influence discourages redundant interior moves.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 22, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.78125, "wins": 75, "losses": 21, "score_as_black": 0.75, "score_as_white": 0.8125, "mean_margin": 44.70833206176758, "mean_length": 114.55208333333333, "stones_captured_per_game": 56.916666666666664, "stones_lost_per_game": 13.447916666666666, "passes_per_game": 1.8541666666666667, "games_with_a_pass_before_move_60": 0, "end_two_passes": 93, "end_repetition": 2, "end_move_limit": 1}, "weak": {"games": 32, "score": 0.9375, "wins": 30, "losses": 2, "score_as_black": 0.9375, "score_as_white": 0.9375, "mean_margin": 67.90625, "mean_length": 114.1875, "stones_captured_per_game": 74.9375, "stones_lost_per_game": 2.3125, "passes_per_game": 1.84375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 31, "end_repetition": 1, "end_move_limit": 0}}, "files": "/work/pass_22/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, collections, numpy as npfor k in [14,15,16,18,19,20,21,22]: gs=json.load(open("/work/pass_%d/games.json"%k)) gs=[g for g in gs if g["opponent"]=="strong"] print("pass",k) for col in ["black","white"]: a=[g for g in gs if g["net_color"]==col] first=collections.Counter(next(m["move"] for m in g["moves"] if m["by"]=="opponent") for g in a) margins=[g["margin"] for g in a if g["result"]<0] print(col,"opponent first",first.most_common(6),"loss margins",margins)pass 14black opponent first [(40, 45), (49, 2), (61, 1)] loss margins [-6.5, -20.5, -13.5, -3.5, -9.5, -43.5, -9.5, -22.5, -23.5, -0.5]white opponent first [(40, 45), (48, 3)] loss margins [-0.5, -11.5, -9.5, -4.5, -2.5, -3.5, -14.5, -18.5, -33.5]pass 15black opponent first [(40, 53), (49, 8), (48, 1), (66, 1), (58, 1)] loss margins [-1.5, -11.5, -12.5, -2.5, -7.5, -13.5, -14.5, -8.5, -23.5, -18.5, -7.5, -6.5, -6.5, -18.5, -3.5]white opponent first [(40, 55), (48, 6), (49, 1), (31, 1), (50, 1)] loss margins [-10.5, -18.5, -4.5, -4.5, -5.5, -8.5, -5.5, -0.5, -18.5, -16.5, -6.5, -3.5, -5.5, -2.5, -0.5, -9.5]pass 16black opponent first [(40, 41), (49, 4), (58, 1), (50, 1), (11, 1)] loss margins [-23.5, -21.5, -8.5, -2.5, -16.5, 1.5, -9.5, -10.5, -28.5, -3.5, -1.5]white opponent first [(40, 45), (48, 2), (31, 1)] loss margins [-7.5, -8.5, -4.5, -32.5, -9.5, -21.5, 39.5, -3.5]pass 18black opponent first [(40, 42), (49, 5), (77, 1)] loss margins [-19.5, -9.5, -15.5, -0.5, -15.5, -17.5, -1.5, -11.5, -2.5, -15.5]white opponent first [(40, 42), (48, 4), (31, 2)] loss margins [-9.5, -7.5, -12.5, -17.5, -3.5, -8.5, -0.5, -4.5, -0.5, -5.5, -3.5]pass 19black opponent first [(40, 42), (49, 6)] loss margins [-1.5, -20.5, -5.5, -5.5, -8.5, -15.5, 11.5, -9.5, -4.5, -13.5, -9.5, -17.5]white opponent first [(40, 42), (48, 4), (31, 2)] loss margins [-4.5, -11.5, -9.5, -4.5, -0.5, -15.5, -17.5, -9.5, -4.5, -4.5]pass 20black opponent first [(40, 44), (49, 2), (58, 1), (43, 1)] loss margins [-10.5, -13.5, -7.5, -24.5, -19.5, -6.5, -31.5, -4.5, -12.5, -17.5, -8.5, -0.5, -4.5]white opponent first [(40, 42), (48, 5), (51, 1)] loss margins [-10.5, -5.5, -0.5, -8.5, -2.5, -43.5, -2.5, -11.5, -9.5]pass 21black opponent first [(40, 39), (49, 5), (58, 2), (48, 2)] loss margins [-17.5, 36.5, -24.5, -1.5, -22.5, -16.5, -2.5, -3.5, -5.5, -8.5, -15.5, -21.5, -0.5]white opponent first [(40, 46), (48, 2)] loss margins [-28.5, -11.5, -14.5, -5.5, -9.5, -3.5, -9.5, -2.5, -9.5, -4.5, -17.5, -13.5, -22.5, -4.5]pass 22black opponent first [(40, 45), (49, 3)] loss margins [22.5, -5.5, -4.5, -7.5, -11.5, -3.5, -19.5, -8.5, -3.5, -14.5, -6.5, -22.5]white opponent first [(40, 46), (50, 1), (48, 1)] loss margins [-64.5, -7.5, -10.5, -7.5, -12.5, -9.5, -5.5, -5.5, -7.5]import json, numpy as npsource=open("/work/policy_design_v18.py").read()patch=r''' # Four-four openings balance central influence with access to a # corner, without committing to direct contact at the centre. W[0][:,489]=0 B[0][489]=1 for p in range(81): W[0][3*p,489]=-1 W[0][3*p+1,489]=-1 for k in range(1,6): W[k][489,489]=1 for p in [30,32,48,50]: OW[489,p]+=cfg.get("opening_four_four",6.0)'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v21.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(opening_four_four=6.0,temperature_scale=2.5)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v21.npz",**tensors)print("Saved v21: symmetric four-four opening choices.")Saved v21: symmetric four-four opening choices.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 23, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.75, "wins": 72, "losses": 24, "score_as_black": 0.7083333134651184, "score_as_white": 0.7916666865348816, "mean_margin": 47.69791793823242, "mean_length": 115.57291666666667, "stones_captured_per_game": 64.64583333333333, "stones_lost_per_game": 12.427083333333334, "passes_per_game": 1.8125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 94, "end_repetition": 2, "end_move_limit": 0}, "weak": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 71.15625, "mean_length": 115.65625, "stones_captured_per_game": 77.59375, "stones_lost_per_game": 2.75, "passes_per_game": 1.8125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_23/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v18.py").read()patch=r''' # Value additional territory at the contested boundary more than # safe consolidation inside an existing sphere of influence. for p in range(81): B[5][u(3,p)]=-cfg.get("own_territory_threshold",0.5)'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v22.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(own_territory=3.0,own_territory_threshold=0.5)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v22.npz",**tensors)print("Saved v22: greater territorial ambition at contested boundaries.")Saved v22: greater territorial ambition at contested boundaries.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 24, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.84375, "wins": 81, "losses": 15, "score_as_black": 0.8333333134651184, "score_as_white": 0.8541666865348816, "mean_margin": 52.375, "mean_length": 116.34375, "stones_captured_per_game": 62.114583333333336, "stones_lost_per_game": 10.927083333333334, "passes_per_game": 1.9895833333333333, "games_with_a_pass_before_move_60": 0, "end_two_passes": 91, "end_repetition": 3, "end_move_limit": 2}, "weak": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 69.65625, "mean_length": 117.0625, "stones_captured_per_game": 74.875, "stones_lost_per_game": 3.28125, "passes_per_game": 1.75, "games_with_a_pass_before_move_60": 0, "end_two_passes": 30, "end_repetition": 1, "end_move_limit": 1}}, "files": "/work/pass_24/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 25, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.78125, "wins": 100, "losses": 28, "score_as_black": 0.796875, "score_as_white": 0.765625, "mean_margin": 46.8359375, "mean_length": 117.5234375, "stones_captured_per_game": 59.65625, "stones_lost_per_game": 15.7265625, "passes_per_game": 1.8203125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 122, "end_repetition": 5, "end_move_limit": 1}}, "files": "/work/pass_25/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v22.py").read()patch=r''' # A third-line framework is economical on 9x9: it can limit a # central group while retaining room for eyes on the outside. for p in range(81): r,c=divmod(p,9) if max(abs(r-4),abs(c-4))==2: OB[p]+=cfg.get("framework_bonus",2.0)'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v23.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg["framework_bonus"]=2.0ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v23.npz",**tensors)print("Saved v23: a symmetric third-line framework prior.")Saved v23: a symmetric third-line framework prior.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 26, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.84375, "wins": 81, "losses": 15, "score_as_black": 0.8333333134651184, "score_as_white": 0.8541666865348816, "mean_margin": 55.76041793823242, "mean_length": 117.5, "stones_captured_per_game": 64.08333333333333, "stones_lost_per_game": 10.739583333333334, "passes_per_game": 1.5416666666666667, "games_with_a_pass_before_move_60": 0, "end_two_passes": 96, "end_repetition": 0, "end_move_limit": 0}, "weak": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 70.5, "mean_length": 116.84375, "stones_captured_per_game": 71.28125, "stones_lost_per_game": 2.78125, "passes_per_game": 1.5, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_26/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 27, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.796875, "wins": 102, "losses": 26, "score_as_black": 0.75, "score_as_white": 0.84375, "mean_margin": 53.0703125, "mean_length": 115.65625, "stones_captured_per_game": 60.9921875, "stones_lost_per_game": 9.9609375, "passes_per_game": 1.6015625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 119, "end_repetition": 8, "end_move_limit": 1}}, "files": "/work/pass_27/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as np, collectionsg=json.load(open("/work/pass_27/games.json"))b=np.load("/work/pass_27/boards.npy")N=[]for p in range(81): r,c=divmod(p,9) N.append([rr*9+cc for rr,cc in [(r-1,c),(r+1,c),(r,c-1),(r,c+1)] if 0<=rr<9 and 0<=cc<9])def groups(board): out=[]; seen=set() for p in range(81): if board[p]==0 or p in seen: continue st={p}; lib=set(); seen.add(p); todo=[p] while todo: q=todo.pop() for a in N[q]: if board[a]==0: lib.add(a) elif board[a]==board[p] and a not in seen: st.add(a);seen.add(a);todo.append(a) out.append((int(board[p]),st,lib)) return outfor i,x in enumerate(g): if x["end"]!="repetition": continue t=len(x["moves"])-1 eq=[j for j in range(t) if np.array_equal(b[i,j],b[i,t])] print("\nREPEAT",i,x["net_color"],"result",x["result"],"margin",x["margin"], "ply",t,"earlier",eq) print(json.dumps(x["moves"][-8:])) pre=b[i,t-1] mover=1 if t%2==0 else -1 p=x["moves"][-1]["move"] print("move",divmod(p,9),"adjacent",[int(pre[q]) for q in N[p]], "capturable",[(len(s),sorted(s),list(l)) for co,s,l in groups(pre) if co==-mover and l=={p}]) print("\n".join("".join({0:".",1:"X",-1:"O"}[int(z)] for z in row) for row in pre.reshape(9,9)))
REPEAT 65 white result -1 margin 32.5 ply 81 earlier [78][{"ply": 74, "by": "opponent", "move": 7}, {"ply": 75, "by": "net", "move": 0, "top5": [[0, 1.0], [5, 0.0], [8, 0.0], [79, 0.0], [4, 0.0]]}, {"ply": 76, "by": "opponent", "move": 79}, {"ply": 77, "by": "net", "move": 5, "top5": [[5, 0.6106], [8, 0.3893], [4, 0.0001], [81, 0.0], [6, 0.0]]}, {"ply": 78, "by": "opponent", "move": 6}, {"ply": 79, "by": "net", "move": 8, "top5": [[8, 1.0], [81, 0.0], [26, 0.0], [30, 0.0], [65, 0.0]]}, {"ply": 80, "by": "opponent", "move": 26}, {"ply": 81, "by": "net", "move": 17, "top5": [[17, 0.8699], [81, 0.1301], [8, 0.0], [30, 0.0], [65, 0.0]]}]move (1, 8) adjacent [0, 1, 1] capturable [(1, [26], [17])]OOXX.OXX..OXXXXXX.OOXOOOXOXO.O.O.OOO.OOO.OO.OO.O.OOOOOOOOOXXOXX.O.OXXXXXO.OX.X.X.
REPEAT 73 white result 1 margin 15.5 ply 56 earlier [53][{"ply": 49, "by": "net", "move": 16, "top5": [[16, 0.8155], [68, 0.0669], [6, 0.0591], [27, 0.0358], [60, 0.0153]]}, {"ply": 50, "by": "opponent", "move": 69}, {"ply": 51, "by": "net", "move": 6, "top5": [[6, 1.0], [60, 0.0], [68, 0.0], [27, 0.0], [18, 0.0]]}, {"ply": 52, "by": "opponent", "move": 17}, {"ply": 53, "by": "net", "move": 60, "top5": [[60, 0.9975], [68, 0.0019], [27, 0.0003], [26, 0.0002], [18, 0.0]]}, {"ply": 54, "by": "opponent", "move": 54}, {"ply": 55, "by": "net", "move": 72, "top5": [[72, 1.0], [68, 0.0], [27, 0.0], [26, 0.0], [18, 0.0]]}, {"ply": 56, "by": "opponent", "move": 63}]move (7, 0) adjacent [0, -1, -1] capturable [(1, [72], [63])]...X..O...XOXXO.OX.XOOOOO...XXXO.OO.OOOXXOO..O.OXXO....OOOXOO...OXXX.X..OX.......
REPEAT 83 white result -1 margin 5.5 ply 107 earlier [102, 103][{"ply": 100, "by": "opponent", "move": 72}, {"ply": 101, "by": "net", "move": 4, "top5": [[4, 1.0], [2, 0.0], [81, 0.0], [1, 0.0], [0, 0.0]]}, {"ply": 102, "by": "opponent", "move": 2}, {"ply": 103, "by": "net", "move": 81, "top5": [[81, 0.6457], [5, 0.3543], [32, 0.0], [52, 0.0], [44, 0.0]]}, {"ply": 104, "by": "opponent", "move": 5}, {"ply": 105, "by": "net", "move": 4, "top5": [[4, 0.9964], [81, 0.0036], [3, 0.0], [32, 0.0], [15, 0.0]]}, {"ply": 106, "by": "opponent", "move": 81}, {"ply": 107, "by": "net", "move": 3, "top5": [[3, 1.0], [81, 0.0], [5, 0.0], [32, 0.0], [0, 0.0]]}]move (0, 3) adjacent [1, 1, -1] capturable []X.X.O.OO.XXXXXO.OOXXXOOOOO.XXOOO.O.OXXOOXOOO.XXOXXXO.OXXXXXXOOOXXXO.XXXOXXXXXXXXO
REPEAT 86 white result 1 margin 44.5 ply 102 earlier [99][{"ply": 95, "by": "net", "move": 7, "top5": [[7, 0.9804], [78, 0.0196], [81, 0.0], [16, 0.0], [26, 0.0]]}, {"ply": 96, "by": "opponent", "move": 35}, {"ply": 97, "by": "net", "move": 16, "top5": [[16, 1.0], [78, 0.0], [8, 0.0], [81, 0.0], [36, 0.0]]}, {"ply": 98, "by": "opponent", "move": 5}, {"ply": 99, "by": "net", "move": 6, "top5": [[6, 1.0], [15, 0.0], [8, 0.0], [78, 0.0], [81, 0.0]]}, {"ply": 100, "by": "opponent", "move": 8}, {"ply": 101, "by": "net", "move": 26, "top5": [[26, 1.0], [78, 0.0], [81, 0.0], [36, 0.0], [80, 0.0]]}, {"ply": 102, "by": "opponent", "move": 17}]move (1, 8) adjacent [0, -1, -1] capturable [(1, [26], [17])]O.OOO.OO..OOO.O.O.OO.OOOOXOOOOXXXXXX.XOXXOOXXXOOXOOOX.OOOOOO.OX.O.OO.OOOO.OO.O.X.
REPEAT 91 white result -1 margin 22.5 ply 95 earlier [92][{"ply": 88, "by": "opponent", "move": 8}, {"ply": 89, "by": "net", "move": 26, "top5": [[26, 0.7455], [3, 0.1838], [73, 0.0693], [72, 0.0008], [81, 0.0005]]}, {"ply": 90, "by": "opponent", "move": 17}, {"ply": 91, "by": "net", "move": 3, "top5": [[3, 0.7219], [73, 0.2723], [72, 0.003], [81, 0.0027], [74, 0.0002]]}, {"ply": 92, "by": "opponent", "move": 63}, {"ply": 93, "by": "net", "move": 4, "top5": [[4, 1.0], [73, 0.0], [81, 0.0], [74, 0.0], [72, 0.0]]}, {"ply": 94, "by": "opponent", "move": 2}, {"ply": 95, "by": "net", "move": 3, "top5": [[3, 0.696], [73, 0.2975], [81, 0.0033], [74, 0.0031], [4, 0.0]]}]move (0, 3) adjacent [1, 1, 0] capturable [(1, [2], [3])]OOX..X.OX.OOXXXXXXOOOXOOOX.O.OXXXOXXOOOOXOOOOOXOXOO.OOXXXXXOOO.XXXXO.O.O...XOOOOO
REPEAT 108 white result -1 margin 37.5 ply 93 earlier [90][{"ply": 86, "by": "opponent", "move": 44}, {"ply": 87, "by": "net", "move": 34, "top5": [[34, 0.9892], [3, 0.0073], [53, 0.0035], [1, 0.0], [10, 0.0]]}, {"ply": 88, "by": "opponent", "move": 77}, {"ply": 89, "by": "net", "move": 53, "top5": [[53, 1.0], [3, 0.0], [1, 0.0], [81, 0.0], [10, 0.0]]}, {"ply": 90, "by": "opponent", "move": 0}, {"ply": 91, "by": "net", "move": 3, "top5": [[3, 1.0], [10, 0.0], [81, 0.0], [1, 0.0], [5, 0.0]]}, {"ply": 92, "by": "opponent", "move": 5}, {"ply": 93, "by": "net", "move": 4, "top5": [[15, 0.7621], [4, 0.2353], [10, 0.0019], [81, 0.0007], [3, 0.0]]}]move (0, 4) adjacent [1, 0, 1] capturable [(1, [5], [4])]X.X..XOX.X.XXXO.OXXXXOOOOOXXOO.O.OOXO.OO.O.OXOO.OO.OOOO.OO.OO.O.OOOOXOOXO.OOXXXX.
REPEAT 119 white result 1 margin 36.5 ply 96 earlier [90][{"ply": 89, "by": "net", "move": 50, "top5": [[50, 1.0], [81, 0.0], [42, 0.0], [41, 0.0], [23, 0.0]]}, {"ply": 90, "by": "opponent", "move": 36}, {"ply": 91, "by": "net", "move": 27, "top5": [[18, 0.5245], [27, 0.4746], [81, 0.0009], [42, 0.0], [41, 0.0]]}, {"ply": 92, "by": "opponent", "move": 0}, {"ply": 93, "by": "net", "move": 18, "top5": [[18, 1.0], [0, 0.0], [1, 0.0], [2, 0.0], [3, 0.0]]}, {"ply": 94, "by": "opponent", "move": 1}, {"ply": 95, "by": "net", "move": 81, "top5": [[81, 0.9901], [0, 0.0097], [9, 0.0002], [42, 0.0], [41, 0.0]]}, {"ply": 96, "by": "opponent", "move": 9}]move (1, 0) adjacent [0, -1, -1] capturable [(2, [18, 27], [9])].XO.OX.XO.OOO.OXOOOXO.O.OO.OXOO.OO.OXXO.O..OOXXOOOOO.OXXOOO.OOOXXXXOOO.O.X.XXO.OO
REPEAT 123 white result -1 margin 37.5 ply 105 earlier [102][{"ply": 98, "by": "opponent", "move": 4}, {"ply": 99, "by": "net", "move": 16, "top5": [[16, 1.0], [8, 0.0], [62, 0.0], [81, 0.0], [63, 0.0]]}, {"ply": 100, "by": "opponent", "move": 62}, {"ply": 101, "by": "net", "move": 53, "top5": [[53, 0.7493], [44, 0.2494], [81, 0.0013], [70, 0.0], [63, 0.0]]}, {"ply": 102, "by": "opponent", "move": 8}, {"ply": 103, "by": "net", "move": 44, "top5": [[44, 1.0], [81, 0.0], [17, 0.0], [16, 0.0], [62, 0.0]]}, {"ply": 104, "by": "opponent", "move": 62}, {"ply": 105, "by": "net", "move": 53, "top5": [[53, 0.9987], [81, 0.0013], [44, 0.0], [17, 0.0], [16, 0.0]]}]move (5, 8) adjacent [0, 1, 1] capturable [(1, [62], [53])]XXXXXXXXXXOOXX.X..OOOOOXXXXOO.OOXOXX.OO.OOOX.OOOO.OOX.O.OOOOOOX.O.OO.O.OO.OO.OOOOimport json, numpy as npsource=open("/work/policy_design_v23.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg=json.load(open("/work/policy_config.json"))cfg["repeat_risk"]=80.0cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v24.npz",**tensors)print("Saved v24: isolated singleton recapture risk now dominates all local tactical bonuses.")print("This avoids treating several nearby weak stones as justification for a repetition-prone recapture.")Saved v24: isolated singleton recapture risk now dominates all local tactical bonuses.This avoids treating several nearby weak stones as justification for a repetition-prone recapture.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 28, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.8541666865348816, "wins": 82, "losses": 14, "score_as_black": 0.8333333134651184, "score_as_white": 0.875, "mean_margin": 53.98958206176758, "mean_length": 114.01041666666667, "stones_captured_per_game": 62.083333333333336, "stones_lost_per_game": 7.666666666666667, "passes_per_game": 1.3854166666666667, "games_with_a_pass_before_move_60": 0, "end_two_passes": 92, "end_repetition": 4, "end_move_limit": 0}, "weak": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 72.71875, "mean_length": 118.5625, "stones_captured_per_game": 70.1875, "stones_lost_per_game": 1.25, "passes_per_game": 1.65625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_28/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, collectionsg=json.load(open("/work/pass_28/games.json"))print(collections.Counter((x["opponent"],x["end"],x["result"]) for x in g))for i,x in enumerate(g): if x["end"]=="repetition": print(i,x["net_color"],x["result"],x["margin"],x["moves"][-4:])Counter({('strong', 'two passes', 1): 80, ('weak', 'two passes', 1): 32, ('strong', 'two passes', -1): 12, ('strong', 'repetition', 1): 2, ('strong', 'repetition', -1): 2})26 black 1 53.5 [{'ply': 104, 'by': 'net', 'move': 67, 'top5': [[67, 1.0], [77, 0.0], [72, 0.0], [81, 0.0], [55, 0.0]]}, {'ply': 105, 'by': 'opponent', 'move': 77}, {'ply': 106, 'by': 'net', 'move': 75, 'top5': [[75, 1.0], [72, 0.0], [69, 0.0], [81, 0.0], [0, 0.0]]}, {'ply': 107, 'by': 'opponent', 'move': 76}]44 black -1 -5.5 [{'ply': 87, 'by': 'opponent', 'move': 81}, {'ply': 88, 'by': 'net', 'move': 0, 'top5': [[0, 1.0], [5, 0.0], [81, 0.0], [6, 0.0], [75, 0.0]]}, {'ply': 89, 'by': 'opponent', 'move': 27}, {'ply': 90, 'by': 'net', 'move': 9, 'top5': [[9, 1.0], [5, 0.0], [81, 0.0], [0, 0.0], [6, 0.0]]}]55 white 1 20.5 [{'ply': 69, 'by': 'net', 'move': 62, 'top5': [[26, 0.4909], [54, 0.2762], [62, 0.1655], [1, 0.0326], [9, 0.0241]]}, {'ply': 70, 'by': 'opponent', 'move': 54}, {'ply': 71, 'by': 'net', 'move': 72, 'top5': [[72, 1.0], [26, 0.0], [1, 0.0], [71, 0.0], [9, 0.0]]}, {'ply': 72, 'by': 'opponent', 'move': 63}]84 white -1 -6.5 [{'ply': 108, 'by': 'opponent', 'move': 81}, {'ply': 109, 'by': 'net', 'move': 6, 'top5': [[6, 1.0], [81, 0.0], [46, 0.0], [60, 0.0], [8, 0.0]]}, {'ply': 110, 'by': 'opponent', 'move': 8}, {'ply': 111, 'by': 'net', 'move': 7, 'top5': [[7, 0.9999], [16, 0.0001], [81, 0.0], [46, 0.0], [17, 0.0]]}]import json, numpy as npg=json.load(open("/work/pass_28/games.json"))b=np.load("/work/pass_28/boards.npy")for i in [44,84]: x=g[i]; t=len(x["moves"])-1 print("\nGAME",i,"colour",x["net_color"],"last moves",x["moves"][-10:]) for j in range(t-4,t+1): print("ply",j,"action",x["moves"][j]["move"]) print("\n".join("".join({0:".",1:"X",-1:"O"}[int(z)] for z in row) for row in b[i,j].reshape(9,9)))
GAME 44 colour black last moves [{'ply': 81, 'by': 'opponent', 'move': 63}, {'ply': 82, 'by': 'net', 'move': 54, 'top5': [[54, 0.9406], [9, 0.0594], [7, 0.0], [81, 0.0], [0, 0.0]]}, {'ply': 83, 'by': 'opponent', 'move': 35}, {'ply': 84, 'by': 'net', 'move': 9, 'top5': [[9, 1.0], [7, 0.0], [81, 0.0], [0, 0.0], [8, 0.0]]}, {'ply': 85, 'by': 'opponent', 'move': 7}, {'ply': 86, 'by': 'net', 'move': 18, 'top5': [[18, 1.0], [0, 0.0], [1, 0.0], [2, 0.0], [3, 0.0]]}, {'ply': 87, 'by': 'opponent', 'move': 81}, {'ply': 88, 'by': 'net', 'move': 0, 'top5': [[0, 1.0], [5, 0.0], [81, 0.0], [6, 0.0], [75, 0.0]]}, {'ply': 89, 'by': 'opponent', 'move': 27}, {'ply': 90, 'by': 'net', 'move': 9, 'top5': [[9, 1.0], [5, 0.0], [81, 0.0], [0, 0.0], [6, 0.0]]}]ply 86 action 18.OOOO..O.XOOOOOOOXXOXXXXO.O.XXOOOOOOXXXXOXOXOX.XOOXXXXXX.XOXOX.OXXXOOOXX.OX.XXX.Xply 87 action 81.OOOO..O.XOOOOOOOXXOXXXXO.O.XXOOOOOOXXXXOXOXOX.XOOXXXXXX.XOXOX.OXXXOOOXX.OX.XXX.Xply 88 action 0XOOOO..O.XOOOOOOOXXOXXXXO.O.XXOOOOOOXXXXOXOXOX.XOOXXXXXX.XOXOX.OXXXOOOXX.OX.XXX.Xply 89 action 27.OOOO..O..OOOOOOOX.OXXXXO.OOXXOOOOOOXXXXOXOXOX.XOOXXXXXX.XOXOX.OXXXOOOXX.OX.XXX.Xply 90 action 9.OOOO..O.XOOOOOOOX.OXXXXO.OOXXOOOOOOXXXXOXOXOX.XOOXXXXXX.XOXOX.OXXXOOOXX.OX.XXX.X
GAME 84 colour white last moves [{'ply': 102, 'by': 'opponent', 'move': 8}, {'ply': 103, 'by': 'net', 'move': 7, 'top5': [[7, 0.9999], [16, 0.0001], [81, 0.0], [46, 0.0], [17, 0.0]]}, {'ply': 104, 'by': 'opponent', 'move': 0}, {'ply': 105, 'by': 'net', 'move': 16, 'top5': [[16, 1.0], [81, 0.0], [6, 0.0], [46, 0.0], [60, 0.0]]}, {'ply': 106, 'by': 'opponent', 'move': 81}, {'ply': 107, 'by': 'net', 'move': 17, 'top5': [[17, 1.0], [81, 0.0], [6, 0.0], [46, 0.0], [60, 0.0]]}, {'ply': 108, 'by': 'opponent', 'move': 81}, {'ply': 109, 'by': 'net', 'move': 6, 'top5': [[6, 1.0], [81, 0.0], [46, 0.0], [60, 0.0], [8, 0.0]]}, {'ply': 110, 'by': 'opponent', 'move': 8}, {'ply': 111, 'by': 'net', 'move': 7, 'top5': [[7, 0.9999], [16, 0.0001], [81, 0.0], [46, 0.0], [17, 0.0]]}]ply 107 action 17XXXXXX.O.XXXX.XXOOXOXXX.XXXOOOXXXXXX.OOOXOXOOO.OXXOOO.XOOOXO.OOXXXXXXO.OXXXXXXOOOply 108 action 81XXXXXX.O.XXXX.XXOOXOXXX.XXXOOOXXXXXX.OOOXOXOOO.OXXOOO.XOOOXO.OOXXXXXXO.OXXXXXXOOOply 109 action 6XXXXXXOO.XXXX.XXOOXOXXX.XXXOOOXXXXXX.OOOXOXOOO.OXXOOO.XOOOXO.OOXXXXXXO.OXXXXXXOOOply 110 action 8XXXXXX..XXXXX.XX..XOXXX.XXXOOOXXXXXX.OOOXOXOOO.OXXOOO.XOOOXO.OOXXXXXXO.OXXXXXXOOOply 111 action 7XXXXXX.OXXXXX.XX..XOXXX.XXXOOOXXXXXX.OOOXOXOOO.OXXOOO.XOOOXO.OOXXXXXXO.OXXXXXXOOOimport json, numpy as npsource=open("/work/policy_design_v23.py").read()patch=r''' # Compress the two material counts into one total, leaving 24 # auxiliary units for stronger edge self-atari certification. for k in range(6): W[k][:,486:]=0 B[k][486:]=0 if k: W[k][486:,:]=0 OW[486:,:]=0 for p in range(81): W[0][3*p,486]=1 W[0][3*p+1,486]=1 W[0][2,487]=1 for k in range(1,6): W[k][486,486]=1 W[k][487,487]=1 OW[486,81]=cfg["pass_per_stone"] OW[487,81]=cfg["pass_white"]
edge_points=[] for p in range(81): r,c=divmod(p,9) if r in [0,8] and c in [0,1,2,4,6,7,8]: edge_points.append(p) elif c in [0,8] and r in [1,2,4,6,7]: edge_points.append(p) assert len(edge_points)==24 for i,p in enumerate(edge_points): g=488+i # Exact singleton captures, rather than approximate group danger. for q in adj[p]: W[3][u(5,q),g]+=1
# Multiple threatened contacts can justify a larger capture. B[4][g]=-1 for q in adj[p]: W[4][u(3,q),g]+=1
j=u(3,p) W[4][:,j]=0 B[4][j]=0 W[4][u(4,p),j]=1 W[4][g,j]=-2
W[5][u(3,p),g]=1 W[5][g,g]=-2 W[5][u(3,p),u(0,p)]=0 OW[g,p]=-cfg.get("edge_self_atari",32.0)'''source=source.replace(' OW *= cfg["temperature_scale"]', patch+'\n OW *= cfg["temperature_scale"]')open("/work/policy_design_v25.py","w").write(source)open("/work/policy_design.py","w").write(source)cfg=json.load(open("/work/policy_config.json"))cfg.update(repeat_risk=80.0,edge_self_atari=32.0)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v25.npz",**tensors)print("Saved v25: exact small-capture evidence guards against false edge captures and self-atari cycles.")Saved v25: exact small-capture evidence guards against false edge captures and self-atari cycles.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 29, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.7604166865348816, "wins": 73, "losses": 23, "score_as_black": 0.6875, "score_as_white": 0.8333333134651184, "mean_margin": 45.82291793823242, "mean_length": 116.82291666666667, "stones_captured_per_game": 58.46875, "stones_lost_per_game": 13.416666666666666, "passes_per_game": 1.9791666666666667, "games_with_a_pass_before_move_60": 0, "end_two_passes": 94, "end_repetition": 1, "end_move_limit": 1}, "weak": {"games": 32, "score": 0.9375, "wins": 30, "losses": 2, "score_as_black": 0.875, "score_as_white": 1.0, "mean_margin": 64.21875, "mean_length": 117.0, "stones_captured_per_game": 72.96875, "stones_lost_per_game": 2.78125, "passes_per_game": 2.46875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_29/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v23.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg=dict( capture=9.0,rescue=8.0,attack=1.4,self_atari=7.5, eye=11.0,connection=0.9,contact=3.0, own_territory=3.0,own_territory_threshold=0.5, enemy_territory=1.0,territory_threshold=1.5, pass_base=-12.0,pass_per_stone=0.10,pass_white=3.35, own_adj=0.8,own_diag=0.5,own_two=0.7,own_knight=0.3, enemy_adj=1.3,enemy_diag=0.7,enemy_two=0.4, temperature_scale=2.5,surrounded_penalty=10.0, repeat_risk=80.0,rescue_size=0.4,framework_bonus=4.0,)cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)np.savez("/work/policy_v26.npz",**tensors)print("Saved v26: restore capture responsiveness and reinforce the third-line framework.")Saved v26: restore capture responsiveness and reinforce the third-line framework.{"games":{"strong":96,"weak":32}}{"ok": true, "pass": 30, "kind": "games", "per_opponent": {"strong": {"games": 96, "score": 0.8125, "wins": 78, "losses": 18, "score_as_black": 0.8333333134651184, "score_as_white": 0.7916666865348816, "mean_margin": 48.76041793823242, "mean_length": 116.85416666666667, "stones_captured_per_game": 60.927083333333336, "stones_lost_per_game": 12.3125, "passes_per_game": 1.8541666666666667, "games_with_a_pass_before_move_60": 0, "end_two_passes": 91, "end_repetition": 4, "end_move_limit": 1}, "weak": {"games": 32, "score": 1.0, "wins": 32, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 71.875, "mean_length": 119.71875, "stones_captured_per_game": 71.875, "stones_lost_per_game": 1.84375, "passes_per_game": 1.46875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 32, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_30/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, numpy as npsource=open("/work/policy_design_v23.py").read()open("/work/policy_design.py","w").write(source)ns={}exec(compile(source,"/work/policy_design.py","exec"),ns)cfg=json.load(open("/work/policy_config.json"))cfg["framework_bonus"]=2.0cfg,tensors=ns["build"](save,cfg)json.dump(cfg,open("/work/policy_config.json","w"),indent=2)json.dump(cfg,open("/work/candidate_A_config.json","w"),indent=2)np.savez("/work/candidate_A.npz",**tensors)print("Restored candidate A for independent validation.")print("Parameters:",sum(a.size for a in tensors.values()))print("Finite tensors:",all(np.isfinite(a).all() for a in tensors.values()))Restored candidate A for independent validation.Parameters: 1480274Finite tensors: True{"games":{"strong":128}}{"ok": true, "pass": 31, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.84375, "wins": 108, "losses": 20, "score_as_black": 0.78125, "score_as_white": 0.90625, "mean_margin": 55.4296875, "mean_length": 117.1875, "stones_captured_per_game": 65.8046875, "stones_lost_per_game": 10.2109375, "passes_per_game": 1.609375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 124, "end_repetition": 4, "end_move_limit": 0}}, "files": "/work/pass_31/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 32, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.84375, "wins": 108, "losses": 20, "score_as_black": 0.796875, "score_as_white": 0.890625, "mean_margin": 52.609375, "mean_length": 118.875, "stones_captured_per_game": 63.484375, "stones_lost_per_game": 10.703125, "passes_per_game": 2.09375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 127, "end_repetition": 0, "end_move_limit": 1}}, "files": "/work/pass_32/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 33, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.8359375, "wins": 107, "losses": 21, "score_as_black": 0.8125, "score_as_white": 0.859375, "mean_margin": 52.765625, "mean_length": 115.8203125, "stones_captured_per_game": 60.625, "stones_lost_per_game": 9.171875, "passes_per_game": 1.640625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 126, "end_repetition": 2, "end_move_limit": 0}}, "files": "/work/pass_33/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 34, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.8359375, "wins": 107, "losses": 21, "score_as_black": 0.796875, "score_as_white": 0.875, "mean_margin": 55.453125, "mean_length": 118.625, "stones_captured_per_game": 66.2109375, "stones_lost_per_game": 9.4375, "passes_per_game": 1.6953125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 125, "end_repetition": 2, "end_move_limit": 1}}, "files": "/work/pass_34/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"weak":64,"random":64}}{"ok": true, "pass": 35, "kind": "games", "per_opponent": {"random": {"games": 64, "score": 1.0, "wins": 64, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 65.1875, "mean_length": 109.25, "stones_captured_per_game": 63.625, "stones_lost_per_game": 0.34375, "passes_per_game": 1.109375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 63, "end_repetition": 1, "end_move_limit": 0}, "weak": {"games": 64, "score": 1.0, "wins": 64, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 70.015625, "mean_length": 117.359375, "stones_captured_per_game": 69.671875, "stones_lost_per_game": 2.078125, "passes_per_game": 1.640625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 62, "end_repetition": 2, "end_move_limit": 0}}, "files": "/work/pass_35/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"positions":[{"board":[".........",".........",".........","....X....","...XOX...",".........",".........",".........","........."],"to_move":"black"},{"board":[".........",".........",".........","....O....","...OXO...",".........",".........",".........","........."],"to_move":"black"},{"board":[".........",".........","...XX....","..XOOX...","..XO.....","...X.....",".........",".........","........."],"to_move":"black"},{"board":[".........",".........","...OO....","..OXXO...","..OX.....","...O.....",".........",".........","........."],"to_move":"black"},{"board":["XXXXXXXXX","XXXXXXXXX","XXXXXXXXX","XXXXXXXXX","XXXX.X.XX","XXXXXXXXX","XXXXXXXXX","XXXXXXXXX","XXXXXXXXX"],"to_move":"black"},{"board":[".........",".........",".........","......OX.",".......OX","......OX.",".........",".........","........."],"to_move":"black"},{"board":[".........",".........",".........","......OXX",".......OO","......OXX",".........",".........","........."],"to_move":"black"},{"board":[".........",".........",".........","...OXO...","...X.X...","...OXO...",".........",".........","........."],"to_move":"black"},{"board":[".........",".........",".........",".........",".........",".........",".........",".........","........."],"to_move":"black"}],"activations":true}{"ok": true, "pass": 36, "kind": "positions", "positions": 9, "top3_per_position": [[["(5,4)", 1.0], ["(4,2)", 0.0], ["(4,6)", 0.0]], [["(5,4)", 1.0], ["(2,4)", 0.0], ["(4,2)", 0.0]], [["(4,4)", 1.0], ["(2,2)", 0.0], ["(5,2)", 0.0]], [["(4,4)", 1.0], ["(3,6)", 0.0], ["(6,3)", 0.0]], [["pass", 1.0], ["(4,6)", 0.0], ["(4,4)", 0.0]], [["(2,6)", 0.4972], ["(6,6)", 0.4972], ["(3,5)", 0.0012]], [["(4,6)", 1.0], ["(2,6)", 0.0], ["(6,6)", 0.0]], [["(2,3)", 0.1248], ["(3,2)", 0.1248], ["(2,5)", 0.1248]], [["(2,6)", 0.0977], ["(2,2)", 0.0977], ["(6,6)", 0.0977]]], "files": "/work/pass_36/", "readme": "inputs.npy (P, 243) the network inputs; legal.npy (P, 82) the legal moves (for 'board' positions: the empty points and pass; suicide and ko are not checked); logits.npy, probs.npy (P, 82); act_L1.npy .. act_L6.npy (P, 512) the hidden activations after ReLU."}{"positions":[{"board":[".........",".........",".........","....O....","...OXO...",".........",".........",".........","........."],"to_move":"white"},{"board":[".........",".........",".........","....X....","...XOX...",".........",".........",".........","........."],"to_move":"white"},{"board":[".........",".........","...OO....","..OXXO...","..OX.....","...O.....",".........",".........","........."],"to_move":"white"},{"board":[".........",".........","...XX....","..XOOX...","..XO.....","...X.....",".........",".........","........."],"to_move":"white"},{"board":["OOOOOOOOO","OOOOOOOOO","OOOOOOOOO","OOOOOOOOO","OOOO.O.OO","OOOOOOOOO","OOOOOOOOO","OOOOOOOOO","OOOOOOOOO"],"to_move":"white"},{"board":[".........",".........",".........","......XO.",".......XO","......XO.",".........",".........","........."],"to_move":"white"},{"board":[".........",".........",".........","......XOO",".......XX","......XOO",".........",".........","........."],"to_move":"white"},{"board":[".........",".........",".........","...XOX...","...O.O...","...XOX...",".........",".........","........."],"to_move":"white"},{"board":[".........",".........",".........",".........",".........",".........",".........",".........","........."],"to_move":"white"}],"activations":false}{"ok": true, "pass": 37, "kind": "positions", "positions": 9, "top3_per_position": [[["(5,4)", 1.0], ["(4,2)", 0.0], ["(4,6)", 0.0]], [["(5,4)", 1.0], ["(2,4)", 0.0], ["(4,2)", 0.0]], [["(4,4)", 1.0], ["(2,2)", 0.0], ["(5,2)", 0.0]], [["(4,4)", 1.0], ["(3,6)", 0.0], ["(6,3)", 0.0]], [["pass", 1.0], ["(4,6)", 0.0], ["(4,4)", 0.0]], [["(2,6)", 0.4972], ["(6,6)", 0.4972], ["(3,5)", 0.0012]], [["(4,6)", 1.0], ["(2,6)", 0.0], ["(6,6)", 0.0]], [["(2,3)", 0.1248], ["(3,2)", 0.1248], ["(2,5)", 0.1248]], [["(2,6)", 0.0977], ["(2,2)", 0.0977], ["(6,6)", 0.0977]]], "files": "/work/pass_37/", "readme": "inputs.npy (P, 243) the network inputs; legal.npy (P, 82) the legal moves (for 'board' positions: the empty points and pass; suicide and ko are not checked); logits.npy, probs.npy (P, 82); no activations (ask with \"activations\": true)."}import json, hashlib, numpy as np
names = ["L%d.%s"%(k,t) for k in range(1,7) for t in ["W","b"]]names += ["out.W","out.b"]tensors = {n:load(n) for n in names}reference = np.load("/work/candidate_A.npz")assert all(np.array_equal(tensors[n],reference[n]) for n in names)assert all(np.isfinite(a).all() for a in tensors.values())
# Static symmetry checks on weights only; no network evaluation.errors=[]for reflect in [False,True]: for turns in range(4): perm=[] for p in range(81): r,c=divmod(p,9) if reflect: c=8-c for _ in range(turns): r,c=c,8-r perm.append(9*r+c) perm=np.array(perm) ip=np.array([3*perm[p]+ch for p in range(81) for ch in range(3)]) hp=np.concatenate([81*ch+perm for ch in range(6)]+[np.arange(486,512)]) op=np.r_[perm,81] err=0.0 for k in range(1,7): w=tensors["L%d.W"%k]; bb=tensors["L%d.b"%k] rp=ip if k==1 else hp err=max(err,float(np.max(np.abs(w[np.ix_(rp,hp)]-w)))) err=max(err,float(np.max(np.abs(bb[hp]-bb)))) err=max(err,float(np.max(np.abs(tensors["out.W"][np.ix_(hp,op)]-tensors["out.W"])))) err=max(err,float(np.max(np.abs(tensors["out.b"][op]-tensors["out.b"])))) errors.append(err)
black=np.load("/work/pass_36/logits.npy")white=np.load("/work/pass_37/logits.npy")print("Checkpoint unchanged:", True)print("Eight board-symmetry weight errors:", errors)print("Colour-swapped placement-logit error:",float(np.max(np.abs(black[:,:81]-white[:,:81]))))print("White pass-logit increments:",np.round(white[:,81]-black[:,81],6).tolist())print("Nonzero weights/biases:",sum(np.count_nonzero(a) for a in tensors.values()))digest=hashlib.sha256()for n in names: digest.update(n.encode()) digest.update(tensors[n].tobytes())print("Checkpoint SHA256:",digest.hexdigest())json.dump({"checkpoint":digest.hexdigest(),"validation_passes":[28,31,32,33,34,35]}, open("/work/final_validation_manifest.json","w"),indent=2)Checkpoint unchanged: TrueEight board-symmetry weight errors: [0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0]Colour-swapped placement-logit error: 0.0White pass-logit increments: [8.375, 8.375, 8.375, 8.375, 8.375, 8.375, 8.375, 8.375, 8.375]Nonzero weights/biases: 25947Checkpoint SHA256: dc92ab2b220254b8a377493bfb892d5c03c6e6fa69c8c370053a8328a076ea54import numpy as np# The colour plane is broadcast over all 81 points. Reading it at one# fixed point is functionally invariant, although its raw weight row is not.errors=[]for reflect in [False,True]: for turns in range(4): perm=[] for p in range(81): r,c=divmod(p,9) if reflect: c=8-c for _ in range(turns): r,c=c,8-r perm.append(9*r+c) perm=np.asarray(perm) hp=np.concatenate([81*ch+perm for ch in range(6)]+[np.arange(486,512)]) op=np.r_[perm,81] w=load("L1.W") stone_rows=np.array([3*p+ch for p in range(81) for ch in [0,1]]) rotated_rows=np.array([3*perm[p]+ch for p in range(81) for ch in [0,1]]) err=float(np.max(np.abs(w[np.ix_(rotated_rows,hp)]-w[stone_rows]))) colour_weights=w[2::3].sum(axis=0) err=max(err,float(np.max(np.abs(colour_weights[hp]-colour_weights)))) for k in range(1,7): bb=load("L%d.b"%k) err=max(err,float(np.max(np.abs(bb[hp]-bb)))) if k>1: ww=load("L%d.W"%k) err=max(err,float(np.max(np.abs(ww[np.ix_(hp,hp)]-ww)))) ww=load("out.W"); bb=load("out.b") err=max(err,float(np.max(np.abs(ww[np.ix_(hp,op)]-ww))), float(np.max(np.abs(bb[op]-bb)))) errors.append(err)assert max(errors)==0print("Exact symmetry on all valid broadcast-colour inputs:",errors)print("No weights changed.")Exact symmetry on all valid broadcast-colour inputs: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]No weights changed.{"games":{"strong":128}}{"ok": true, "pass": 38, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.828125, "wins": 106, "losses": 22, "score_as_black": 0.765625, "score_as_white": 0.890625, "mean_margin": 53.046875, "mean_length": 117.9609375, "stones_captured_per_game": 63.6171875, "stones_lost_per_game": 11.0390625, "passes_per_game": 1.7421875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 125, "end_repetition": 3, "end_move_limit": 0}}, "files": "/work/pass_38/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 39, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.8203125, "wins": 105, "losses": 23, "score_as_black": 0.734375, "score_as_white": 0.90625, "mean_margin": 50.578125, "mean_length": 115.75, "stones_captured_per_game": 59.9765625, "stones_lost_per_game": 9.84375, "passes_per_game": 1.8828125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 123, "end_repetition": 5, "end_move_limit": 0}}, "files": "/work/pass_39/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 40, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.7890625, "wins": 101, "losses": 27, "score_as_black": 0.78125, "score_as_white": 0.796875, "mean_margin": 51.0078125, "mean_length": 117.125, "stones_captured_per_game": 61.2109375, "stones_lost_per_game": 11.8984375, "passes_per_game": 1.6328125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 125, "end_repetition": 3, "end_move_limit": 0}}, "files": "/work/pass_40/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 41, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.859375, "wins": 110, "losses": 18, "score_as_black": 0.8125, "score_as_white": 0.90625, "mean_margin": 52.8125, "mean_length": 117.734375, "stones_captured_per_game": 64.5625, "stones_lost_per_game": 10.5703125, "passes_per_game": 1.7734375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 125, "end_repetition": 2, "end_move_limit": 1}}, "files": "/work/pass_41/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 42, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.828125, "wins": 106, "losses": 22, "score_as_black": 0.78125, "score_as_white": 0.875, "mean_margin": 49.703125, "mean_length": 116.703125, "stones_captured_per_game": 60.75, "stones_lost_per_game": 11.921875, "passes_per_game": 1.671875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 123, "end_repetition": 3, "end_move_limit": 2}}, "files": "/work/pass_42/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 43, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.8203125, "wins": 105, "losses": 23, "score_as_black": 0.765625, "score_as_white": 0.875, "mean_margin": 51.5234375, "mean_length": 114.515625, "stones_captured_per_game": 58.7109375, "stones_lost_per_game": 10.59375, "passes_per_game": 1.46875, "games_with_a_pass_before_move_60": 0, "end_two_passes": 125, "end_repetition": 3, "end_move_limit": 0}}, "files": "/work/pass_43/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 44, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.8359375, "wins": 107, "losses": 21, "score_as_black": 0.75, "score_as_white": 0.921875, "mean_margin": 52.0234375, "mean_length": 116.25, "stones_captured_per_game": 60.5625, "stones_lost_per_game": 10.125, "passes_per_game": 1.515625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 126, "end_repetition": 1, "end_move_limit": 1}}, "files": "/work/pass_44/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 45, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.8125, "wins": 104, "losses": 24, "score_as_black": 0.8125, "score_as_white": 0.8125, "mean_margin": 51.640625, "mean_length": 118.078125, "stones_captured_per_game": 63.6328125, "stones_lost_per_game": 11.6640625, "passes_per_game": 2.0, "games_with_a_pass_before_move_60": 0, "end_two_passes": 124, "end_repetition": 2, "end_move_limit": 2}}, "files": "/work/pass_45/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 46, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.875, "wins": 112, "losses": 16, "score_as_black": 0.828125, "score_as_white": 0.921875, "mean_margin": 57.5859375, "mean_length": 118.9296875, "stones_captured_per_game": 68.171875, "stones_lost_per_game": 9.1796875, "passes_per_game": 1.6484375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 128, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_46/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":128}}{"ok": true, "pass": 47, "kind": "games", "per_opponent": {"strong": {"games": 128, "score": 0.8203125, "wins": 105, "losses": 23, "score_as_black": 0.765625, "score_as_white": 0.875, "mean_margin": 52.0078125, "mean_length": 115.8671875, "stones_captured_per_game": 60.3828125, "stones_lost_per_game": 10.1796875, "passes_per_game": 1.7265625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 125, "end_repetition": 2, "end_move_limit": 1}}, "files": "/work/pass_47/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"strong":112,"weak":16}}{"ok": true, "pass": 48, "kind": "games", "per_opponent": {"strong": {"games": 112, "score": 0.8392857313156128, "wins": 94, "losses": 18, "score_as_black": 0.7857142686843872, "score_as_white": 0.8928571343421936, "mean_margin": 53.13393020629883, "mean_length": 115.11607142857143, "stones_captured_per_game": 58.964285714285715, "stones_lost_per_game": 8.330357142857142, "passes_per_game": 1.6964285714285714, "games_with_a_pass_before_move_60": 0, "end_two_passes": 109, "end_repetition": 2, "end_move_limit": 1}, "weak": {"games": 16, "score": 1.0, "wins": 16, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 66.1875, "mean_length": 115.6875, "stones_captured_per_game": 67.3125, "stones_lost_per_game": 1.375, "passes_per_game": 1.9375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 16, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_48/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}{"games":{"weak":64,"random":64}}{"ok": true, "pass": 49, "kind": "games", "per_opponent": {"random": {"games": 64, "score": 1.0, "wins": 64, "losses": 0, "score_as_black": 1.0, "score_as_white": 1.0, "mean_margin": 66.515625, "mean_length": 111.9375, "stones_captured_per_game": 59.8125, "stones_lost_per_game": 0.375, "passes_per_game": 1.109375, "games_with_a_pass_before_move_60": 0, "end_two_passes": 64, "end_repetition": 0, "end_move_limit": 0}, "weak": {"games": 64, "score": 0.984375, "wins": 63, "losses": 1, "score_as_black": 0.96875, "score_as_white": 1.0, "mean_margin": 68.515625, "mean_length": 115.578125, "stones_captured_per_game": 69.90625, "stones_lost_per_game": 1.828125, "passes_per_game": 1.65625, "games_with_a_pass_before_move_60": 0, "end_two_passes": 62, "end_repetition": 2, "end_move_limit": 0}}, "files": "/work/pass_49/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}import json, hashlib, numpy as np
passes=[28,31,32,33,34,35]+list(range(38,50))all_games=[]for k in passes: all_games.extend(json.load(open("/work/pass_%d/games.json"%k)))
summary={}for opponent in ["strong","weak","random"]: games=[g for g in all_games if g["opponent"]==opponent] wins=sum(g["result"]>0 for g in games) n=len(games) p=wins/n z=1.959963984540054 den=1+z*z/n center=(p+z*z/(2*n))/den half=z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/den summary[opponent]={ "games":n, "wins":wins, "win_rate":p, "mean_margin":float(np.mean([g["margin"] for g in games])), "wilson_95_percent":[center-half,center+half], "by_colour":{ colour:{ "games":sum(g["net_color"]==colour for g in games), "wins":sum(g["net_color"]==colour and g["result"]>0 for g in games) } for colour in ["black","white"] } }
names=["L%d.%s"%(k,t) for k in range(1,7) for t in ["W","b"]]+["out.W","out.b"]reference=np.load("/work/candidate_A.npz")tensors={name:load(name).astype(np.float32,copy=False) for name in names}assert sum(a.size for a in tensors.values())==1480274assert all(np.isfinite(a).all() for a in tensors.values())assert all(np.array_equal(tensors[name],reference[name]) for name in names)
# Re-save the already validated checkpoint without changing any values.for name,array in tensors.items(): save(name,array)np.savez("/work/final_policy.npz",**tensors)
cfg=json.load(open("/work/candidate_A_config.json"))json.dump(cfg,open("/work/final_policy_config.json","w"),indent=2)open("/work/final_policy_design.py","w").write( open("/work/policy_design_v23.py").read())
digest=hashlib.sha256()for name in names: digest.update(name.encode()) digest.update(tensors[name].tobytes())manifest={ "sha256":digest.hexdigest(), "completed_validation_passes":passes, "pending_final_pass":50, "summary_before_final_pass":summary, "training_or_search_used":False}json.dump(manifest,open("/work/final_validation_manifest.json","w"),indent=2)print(json.dumps(summary,indent=2))print("All 14 final tensors saved, unchanged from candidate A.")print("SHA256:",digest.hexdigest())print("Final remaining pass will validate another 128 games against weak."){ "strong": { "games": 2000, "wins": 1667, "win_rate": 0.8335, "mean_margin": 52.811, "wilson_95_percent": [ 0.8165372993335623, 0.849184030129842 ], "by_colour": { "black": { "games": 1000, "wins": 787 }, "white": { "games": 1000, "wins": 880 } } }, "weak": { "games": 176, "wins": 175, "win_rate": 0.9943181818181818, "mean_margin": 69.61363636363636, "wilson_95_percent": [ 0.9685225276336739, 0.9989963144675896 ], "by_colour": { "black": { "games": 88, "wins": 87 }, "white": { "games": 88, "wins": 88 } } }, "random": { "games": 128, "wins": 128, "win_rate": 1.0, "mean_margin": 65.8515625, "wilson_95_percent": [ 0.9708630437264916, 1.0 ], "by_colour": { "black": { "games": 64, "wins": 64 }, "white": { "games": 64, "wins": 64 } } }}All 14 final tensors saved, unchanged from candidate A.SHA256: dc92ab2b220254b8a377493bfb892d5c03c6e6fa69c8c370053a8328a076ea54Final remaining pass will validate another 128 games against weak.{"games":{"weak":128}}{"ok": true, "pass": 50, "kind": "games", "per_opponent": {"weak": {"games": 128, "score": 0.9765625, "wins": 125, "losses": 3, "score_as_black": 0.953125, "score_as_white": 1.0, "mean_margin": 69.8984375, "mean_length": 117.6171875, "stones_captured_per_game": 71.84375, "stones_lost_per_game": 2.125, "passes_per_game": 1.78125, "games_with_a_pass_before_move_60": 0, "end_two_passes": 128, "end_repetition": 0, "end_move_limit": 0}}, "files": "/work/pass_50/", "readme": "games.json: one entry per game (opponent, net_color, result +1/-1, margin = net's area score minus the opponent's incl. komi, end, stones captured / lost, moves: ply, by, move (row*9+col, 81 = pass), and for the network's moves its top-5 [move, probability]). boards.npy: (games, 162, 81) int8, the board AFTER each ply (1 Black, -1 White, 0 empty; index row*9+col); valid.npy: (games, 162) which plies were played."}