"""Deterministic Gomocup brain: exact-five tactics and open/closed line scores. Derived from the repository's starter protocol loop. Each hypothetical move is evaluated for both colours. A five-cell window is useful only when neither adjacent cell extends it into an overline. No opponent files are inspected. """ import sys N = 15 board = {} DIRECTIONS = ((1, 0), (0, 1), (1, 1), (1, -1)) def line_at(x, y, colour, dx, dy): cells = [] for offset in range(-5, 6): p = (x + offset * dx, y + offset * dy) if offset == 0: cells.append('X') elif not (0 <= p[0] < N and 0 <= p[1] < N): cells.append('#') else: cells.append('X' if board.get(p) == colour else '#' if p in board else '.') return ''.join(cells) def line_value(line): """Return (exact five, immediate winning offsets, positional score).""" left = right = 5 while left > 0 and line[left - 1] == 'X': left -= 1 while right < 10 and line[right + 1] == 'X': right += 1 length = right - left + 1 if length == 5: return True, set(), 0 if length > 5: return False, set(), 0 ends = int(left > 0 and line[left - 1] == '.') + int(right < 10 and line[right + 1] == '.') weights = {(4, 2): 100000, (4, 1): 10000, (3, 2): 5000, (3, 1): 400, (2, 2): 300, (2, 1): 40, (1, 2): 10, (1, 1): 2} positional = weights.get((length, ends), 0) wins = set() for start in range(1, 6): window = line[start:start + 5] if '#' in window or line[start - 1] == 'X' or line[start + 5] == 'X': continue count = window.count('X') if count == 4: wins.add(start + window.index('.') - 5) elif count == 3: positional = max(positional, 400) elif count == 2: positional = max(positional, 40) # Open broken threes can become a two-ended four in one move. for pattern in ('.XXX.', '.XX.X.', '.X.XX.'): for start in range(len(line) - len(pattern) + 1): if start <= 5 < start + len(pattern) and line[start:start + len(pattern)] == pattern: positional = max(positional, 5000) return False, wins, positional def evaluate(p, colour): win = False winning_points = set() score = 0 open_threes = 0 for dx, dy in DIRECTIONS: exact, offsets, value = line_value(line_at(*p, colour, dx, dy)) win |= exact winning_points.update((p[0] + i * dx, p[1] + i * dy) for i in offsets) score += value open_threes += value >= 5000 # Two distinct winning squares cannot both be blocked in one reply. if len(winning_points) >= 2: score += 1000000 elif winning_points: score += 60000 elif open_threes >= 2: score += 20000 return win, score def move(): if not board: return N // 2, N // 2 near = {(x + a, y + b) for x, y in board for a in range(-2, 3) for b in range(-2, 3)} candidates = sorted(p for p in near if 0 <= p[0] < N and 0 <= p[1] < N and p not in board) if not candidates: raise ValueError('No legal move') ranked = [] for p in candidates: own_win, attack = evaluate(p, 1) their_win, defence = evaluate(p, 2) centrality = -abs(p[0] - N // 2) - abs(p[1] - N // 2) ranked.append(((own_win, their_win, attack * 11 + defence * 10, centrality), p)) return max(ranked)[1] def play(): x, y = move() board[(x, y)] = 1 print(f'{x},{y}', flush=True) def main(): global N for raw in sys.stdin: cmd, _, arg = raw.strip().partition(' ') cmd = cmd.upper() if cmd == 'START': N = int(arg) board.clear() print('OK', flush=True) elif cmd == 'RESTART': board.clear() print('OK', flush=True) elif cmd == 'BEGIN': play() elif cmd == 'TURN': x, y = map(int, arg.split(',')) board[(x, y)] = 2 play() elif cmd == 'BOARD': board.clear() for line in sys.stdin: if line.strip().upper() == 'DONE': break x, y, colour = map(int, line.split(',')) board[(x, y)] = colour # 3 is a blocked cell, not an opponent stone. play() elif cmd == 'ABOUT': print('name="exact-line-threats", version="1", author="codex-golive"', flush=True) elif cmd == 'END': return elif cmd != 'INFO': print('UNKNOWN', flush=True) if __name__ == '__main__': main()