import pygame
import sys
import random

# --- 游戏常量配置 ---
BOARD_SIZE = 9          # 棋盘大小 (9x9)
CELL_SIZE = 50          # 每个格子的像素大小
MARGIN = 40             # 棋盘边缘留白
PIECE_RADIUS = 20       # 棋子半径
WINDOW_SIZE = CELL_SIZE * (BOARD_SIZE - 1) + 2 * MARGIN

# 颜色定义
BG_COLOR = (220, 179, 92)       # 棋盘背景色
LINE_COLOR = (0, 0, 0)          # 线条颜色
BLACK_PIECE = (20, 20, 20)      # 黑子
WHITE_PIECE = (240, 240, 240)   # 白子

# 棋子状态
EMPTY = 0
BLACK = 1
WHITE = 2

class GoGame:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WINDOW_SIZE, WINDOW_SIZE))
        pygame.display.set_caption("Python 围棋小游戏 (带AI)")
        self.font = pygame.font.Font(None, 30)
        
        # 初始化棋盘状态 (二维数组)
        self.board = [[EMPTY for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
        self.current_player = BLACK  # 黑棋先手（玩家）
        self.history = []            # 记录历史，用于悔棋
        self.game_over = False
        
        # AI 配置：玩家执黑，AI执白
        self.ai_color = WHITE
        self.player_color = BLACK

    def get_board_coord(self, mouse_pos):
        """将鼠标像素坐标转换为棋盘网格坐标"""
        x, y = mouse_pos
        col = round((x - MARGIN) / CELL_SIZE)
        row = round((y - MARGIN) / CELL_SIZE)
        if 0 <= row < BOARD_SIZE and 0 <= col < BOARD_SIZE:
            return row, col
        return None

    def get_neighbors(self, row, col):
        """获取当前坐标的上下左右相邻坐标"""
        neighbors = []
        for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            nr, nc = row + dr, col + dc
            if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE:
                neighbors.append((nr, nc))
        return neighbors

    def get_group_liberties(self, row, col, board_state):
        """使用 BFS 查找相连同色棋子的整块“气”"""
        color = board_state[row][col]
        visited = set()
        liberties = set()
        queue = [(row, col)]
        visited.add((row, col))

        while queue:
            r, c = queue.pop(0)
            for nr, nc in self.get_neighbors(r, c):
                if (nr, nc) not in visited:
                    if board_state[nr][nc] == EMPTY:
                        liberties.add((nr, nc))
                    elif board_state[nr][nc] == color:
                        visited.add((nr, nc))
                        queue.append((nr, nc))
        return visited, liberties

    def remove_dead_stones(self, row, col, board_state, color):
        """检查并移除对方没有气的棋子（提子）"""
        captured = []
        for nr, nc in self.get_neighbors(row, col):
            if board_state[nr][nc] == color:
                group, liberties = self.get_group_liberties(nr, nc, board_state)
                if len(liberties) == 0:
                    for gr, gc in group:
                        board_state[gr][gc] = EMPTY
                        captured.append((gr, gc))
        return captured

    def is_valid_move(self, row, col, board_state, player):
        """判断落子是否合法（不能下在已有棋子的地方，不能自杀）"""
        if board_state[row][col] != EMPTY:
            return False
        
        # 模拟落子
        opponent = WHITE if player == BLACK else BLACK
        temp_board = [r[:] for r in board_state]
        temp_board[row][col] = player
        
        # 检查是否能吃掉对方
        if self.remove_dead_stones(row, col, temp_board, opponent):
            return True
        
        # 检查自己是否有气（防止自杀）
        _, liberties = self.get_group_liberties(row, col, temp_board)
        return len(liberties) > 0

    def place_piece(self, row, col):
        """执行落子逻辑"""
        if self.game_over or not self.is_valid_move(row, col, self.board, self.current_player):
            return False

        # 保存历史以便悔棋
        self.history.append([r[:] for r in self.board])
        
        self.board[row][col] = self.current_player
        opponent = WHITE if self.current_player == BLACK else BLACK
        
        # 提子
        self.remove_dead_stones(row, col, self.board, opponent)
        
        # 切换玩家
        self.current_player = opponent
        return True

    def undo(self):
        """悔棋功能（玩家悔棋时，同时撤销AI的上一步）"""
        if len(self.history) >= 2:
            # 撤回AI的一步
            self.history.pop()
            # 撤回玩家的一步
            self.board = self.history.pop()
            self.current_player = self.player_color
        elif len(self.history) == 1:
            # 如果只有AI的一步（比如玩家刚开局就悔棋），直接退回初始状态
            self.board = [[EMPTY for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
            self.history = []
            self.current_player = self.player_color

    # ================= AI 核心逻辑 =================
    def evaluate_move(self, row, col, player):
        """
        评估某一步棋的价值，分数越高 AI 越倾向于下在这里
        """
        opponent = WHITE if player == BLACK else BLACK
        score = 0
        
        # 1. 吃子奖励：如果能吃掉对方棋子，给极高分
        temp_board = [r[:] for r in self.board]
        temp_board[row][col] = player
        captured = self.remove_dead_stones(row, col, temp_board, opponent)
        score += len(captured) * 100
        
        # 2. 逃跑奖励：如果自己相邻的棋子只有1口气，落子后能增加气，给高分
        for nr, nc in self.get_neighbors(row, col):
            if self.board[nr][nc] == player:
                _, liberties_before = self.get_group_liberties(nr, nc, self.board)
                if len(liberties_before) == 1:
                    score += 50
                    
        # 3. 基础位置评分：越靠近棋盘中心，分数越高
        center = BOARD_SIZE // 2
        distance = abs(row - center) + abs(col - center)
        score += max(0, 10 - distance)
        
        # 4. 避免贴边：尽量不下在边缘（除非为了吃子或逃跑）
        if row == 0 or row == BOARD_SIZE - 1 or col == 0 or col == BOARD_SIZE - 1:
            score -= 5
            
        return score

    def ai_turn(self):
        """AI 的回合：遍历所有合法空位，选择得分最高的点"""
        best_score = -1
        best_moves = []
        
        for r in range(BOARD_SIZE):
            for c in range(BOARD_SIZE):
                if self.is_valid_move(r, c, self.board, self.ai_color):
                    score = self.evaluate_move(r, c, self.ai_color)
                    if score > best_score:
                        best_score = score
                        best_moves = [(r, c)]
                    elif score == best_score:
                        best_moves.append((r, c))
        
        # 如果有多个得分相同的点，随机选一个，增加多样性
        if best_moves:
            move = random.choice(best_moves)
            self.place_piece(move[0], move[1])
    # ================================================

    def draw_board(self):
        """绘制棋盘网格"""
        self.screen.fill(BG_COLOR)
        for i in range(BOARD_SIZE):
            start_pos = MARGIN + i * CELL_SIZE
            pygame.draw.line(self.screen, LINE_COLOR, (MARGIN, start_pos), (WINDOW_SIZE - MARGIN, start_pos), 2)
            pygame.draw.line(self.screen, LINE_COLOR, (start_pos, MARGIN), (start_pos, WINDOW_SIZE - MARGIN), 2)

    def draw_pieces(self):
        """绘制棋子"""
        for r in range(BOARD_SIZE):
            for c in range(BOARD_SIZE):
                if self.board[r][c] != EMPTY:
                    color = BLACK_PIECE if self.board[r][c] == BLACK else WHITE_PIECE
                    center = (MARGIN + c * CELL_SIZE, MARGIN + r * CELL_SIZE)
                    pygame.draw.circle(self.screen, color, center, PIECE_RADIUS)
                    if self.board[r][c] == WHITE:
                        pygame.draw.circle(self.screen, LINE_COLOR, center, PIECE_RADIUS, 1)

    def draw_info(self):
        """绘制界面提示"""
        player_name = "黑棋(你)" if self.current_player == BLACK else "白棋(AI)"
        text = self.font.render(f"当前: {player_name} | 右键:悔棋 | ESC:退出", True, (0, 0, 0))
        self.screen.blit(text, (10, 10))

    def run(self):
        """游戏主循环"""
        clock = pygame.time.Clock()
        ai_pending = False  # 标记 AI 是否需要行动
        
        while True:
            clock.tick(60)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1 and self.current_player == self.player_color:
                        pos = self.get_board_coord(event.pos)
                        if pos:
                            if self.place_piece(pos[0], pos[1]):
                                ai_pending = True  # 玩家落子成功，触发 AI
                    elif event.button == 3:
                        self.undo()

                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        pygame.quit()
                        sys.exit()

            # 在主循环中处理 AI 落子，避免阻塞事件监听
            if ai_pending and self.current_player == self.ai_color:
                self.ai_turn()
                ai_pending = False

            self.draw_board()
            self.draw_pieces()
            self.draw_info()
            pygame.display.flip()

if __name__ == "__main__":
    game = GoGame()
    game.run()