The leading agent on Go, 9 by 9, written by a language model.
"""
Gen1065 Master Strategist
Employs advanced local tactical evaluation including true-eye detection,
liberty analysis, cutting/connecting logic, and prioritized stone salvation
to outperform aggressive territory deniers.
"""
import random
from collections import deque
NEIGHBORS = [(-1, 0), (1, 0), (0, -1), (0, 1)]
DIAGONALS = [(-1, -1), (-1, 1), (1, -1), (1, 1)]
def _in_bounds(r, c, size=9):
return 0 <= r < size and 0 <= c < size
def _get_group_info(board, row, col, size=9):
color = board[row][col]
if color == 0:
return set(), set()
visited = set()
liberties = set()
queue = deque([(row, col)])
visited.add((row, col))
while queue:
r, c = queue.popleft()
for dr, dc in NEIGHBORS:
nr, nc = r + dr, c + dc
if not _in_bounds(nr, nc, size):
continue
if (nr, nc) in visited:
continue
if board[nr][nc] == 0:
liberties.add((nr, nc))
elif board[nr][nc] == color:
visited.add((nr, nc))
queue.append((nr, nc))
return visited, liberties
def _is_true_eye(board, r, c, color, size):
# An empty point must be surrounded orthogonally by friendly stones
for dr, dc in NEIGHBORS:
nr, nc = r+dr, c+dc
if _in_bounds(nr, nc, size) and board[nr][nc] != color:
return False
# Check diagonals for opponent stones
opponent_count = 0
for dr, dc in DIAGONALS:
nr, nc = r+dr, c+dc
if _in_bounds(nr, nc, size) and board[nr][nc] == -color:
opponent_count += 1
is_edge = (r == 0 or r == size-1 or c == 0 or c == size-1)
if is_edge:
# On edge/corner, all valid diagonals must be friendly/empty
return opponent_count == 0
else:
# In center, at most 1 opponent stone on diagonal is allowed
return opponent_count <= 1
def _simulate_move(board, row, col, my_color, size=9):
b = [r[:] for r in board]
b[row][col] = my_color
opponent = -my_color
total_captured = 0
# Identify captures
captured_groups = set()
for dr, dc in NEIGHBORS:
nr, nc = row + dr, col + dc
if _in_bounds(nr, nc, size) and b[nr][nc] == opponent:
group, libs = _get_group_info(b, nr, nc, size)
if len(libs) == 0:
captured_groups.add(frozenset(group))
# Remove captured stones
for grp in captured_groups:
total_captured += len(grp)
for gr, gc in grp:
b[gr][gc] = 0
# Own group stats
own_group, own_libs = _get_group_info(b, row, col, size)
# Opponent stats after captures are removed
opp_atari_stones = 0
opp_libs_score = 0
opp_groups_seen = set()
for dr, dc in NEIGHBORS:
nr, nc = row + dr, col + dc
if _in_bounds(nr, nc, size) and b[nr][nc] == opponent:
group, libs = _get_group_info(b, nr, nc, size)
grp_id = frozenset(group)
if grp_id not in opp_groups_seen:
opp_groups_seen.add(grp_id)
num_libs = len(libs)
if num_libs == 1:
opp_atari_stones += len(group)
elif num_libs > 0:
opp_libs_score += 15.0 / num_libs
return b, total_captured, len(own_libs), len(own_group), opp_atari_stones, opp_libs_score
def get_move(state):
board = state['board']
legal_moves = state['legal_moves']
my_color = state['my_color']
turn = state['turn']
opponent_color = -my_color
size = len(board)
if not legal_moves: return None
best_score = -999999
best_moves = []
for row, col in legal_moves:
# Never fill our own true eyes
if _is_true_eye(board, row, col, my_color, size):
continue
friendly_stones_in_atari = 0
opp_adj = 0
friendly_adj = 0
atari_groups_seen = set()
for dr, dc in NEIGHBORS:
nr, nc = row + dr, col + dc
if _in_bounds(nr, nc, size):
if board[nr][nc] == my_color:
friendly_adj += 1
group, libs = _get_group_info(board, nr, nc, size)
if len(libs) == 1:
grp_id = frozenset(group)
if grp_id not in atari_groups_seen:
atari_groups_seen.add(grp_id)
friendly_stones_in_atari += len(group)
elif board[nr][nc] == opponent_color:
opp_adj += 1
new_b, captures, own_libs, own_group_size, opp_atari_stones, opp_libs_score = _simulate_move(board, row, col, my_color, size)
# Base scoring
score = captures * 150
score += min(own_libs, 5) * 15 + own_libs * 2
score += opp_atari_stones * 35
score += opp_libs_score
# Self-atari and suicide penalties
if own_libs == 0:
score -= 10000
elif own_libs == 1:
if captures == 0:
score -= 300
else:
score -= 50
else:
# We only get the bonus for saving stones if they end up with >1 liberties!
if friendly_stones_in_atari > 0:
score += friendly_stones_in_atari * 120
# Cutting and contact bonuses
if friendly_adj > 0 and opp_adj > 0:
score += 25
elif opp_adj > 0:
score += 10 # approach
# Territory filling penalty (avoid playing in our own safe space unnecessarily)
if opp_adj == 0 and captures == 0:
opp_nearby = False
for r in range(max(0, row-2), min(size, row+3)):
for c in range(max(0, col-2), min(size, col+3)):
if board[r][c] == opponent_color:
opp_nearby = True
break
if opp_nearby: break
if not opp_nearby:
score -= 40
# Positional heuristics: center early, edges late
dist_from_center = abs(row - size//2) + abs(col - size//2)
if turn < 20:
score -= dist_from_center * 4
elif turn < 45:
score -= dist_from_center * 1
else:
score += dist_from_center * 2
if score > best_score:
best_score = score
best_moves = [(row, col)]
elif score == best_score:
best_moves.append((row, col))
if turn > 75 and best_score < 25:
return None
if not best_moves:
return None
return random.choice(best_moves)