import pygame
import sys
import math
import random
import time
import threading

# Initialize pygame
pygame.init()

# Define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (185, 185, 185)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)

# Set screen size
BOARD_SIZE = 13
CELL_SIZE = 40
MARGIN = 20
WIDTH = CELL_SIZE * (BOARD_SIZE - 1) + 2 * MARGIN
HEIGHT = WIDTH + 100  # Reserved area below for display

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Gomoku')

FONT = pygame.font.SysFont(None, 36)
SMALL_FONT = pygame.font.SysFont(None, 24)

# Board data structure
board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]

# Game state
mode = None  # None, 'PVP', 'PVC'
player_color = None  # 1: Black, 2: White
current_player = 1  # 1: Black, 2: White
game_over = False
winner = None

# AI variables
ai_thinking = False
ai_move_result = None
ai_start_time = None
ai_time_limit = 5  # seconds
ai_countdown = ai_time_limit
ai_lock = threading.Lock()

def draw_board():
    screen.fill(GRAY)
    # Draw grid
    for i in range(BOARD_SIZE):
        # Draw horizontal lines
        pygame.draw.line(screen, BLACK, (MARGIN, MARGIN + i * CELL_SIZE),
                         (WIDTH - MARGIN, MARGIN + i * CELL_SIZE), 1)
        # Draw vertical lines
        pygame.draw.line(screen, BLACK, (MARGIN + i * CELL_SIZE, MARGIN),
                         (MARGIN + i * CELL_SIZE, WIDTH - MARGIN), 1)
    # Draw star points
    star_points = [3, 6, 9]
    for i in star_points:
        for j in star_points:
            pygame.draw.circle(screen, BLACK,
                               (MARGIN + i * CELL_SIZE, MARGIN + j * CELL_SIZE), 5)

    # Draw pieces
    for row in range(BOARD_SIZE):
        for col in range(BOARD_SIZE):
            if board[row][col] != 0:
                center = (MARGIN + col * CELL_SIZE, MARGIN + row * CELL_SIZE)
                if board[row][col] == 1:
                    pygame.draw.circle(screen, BLACK, center, CELL_SIZE//2 - 2)
                else:
                    pygame.draw.circle(screen, WHITE, center, CELL_SIZE//2 - 2)
                    pygame.draw.circle(screen, BLACK, center, CELL_SIZE//2 - 2, 1)

def get_cell(pos):
    x, y = pos
    if x < MARGIN - CELL_SIZE//2 or x > WIDTH - MARGIN + CELL_SIZE//2 or \
    y < MARGIN - CELL_SIZE//2 or y > WIDTH - MARGIN + CELL_SIZE//2:
        return None
    col = int(round((x - MARGIN) / CELL_SIZE))
    row = int(round((y - MARGIN) / CELL_SIZE))
    if 0 <= row < BOARD_SIZE and 0 <= col < BOARD_SIZE:
        return row, col
    return None

def is_winner(r, c):
    color = board[r][c]
    directions = [(1,0), (0,1), (1,1), (1,-1)]
    for dr, dc in directions:
        count = 1
        # Count in the positive direction
        i = 1
        while True:
            nr = r + dr * i
            nc = c + dc * i
            if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board[nr][nc] == color:
                count += 1
                i += 1
            else:
                break
        # Count in the negative direction
        i = 1
        while True:
            nr = r - dr * i
            nc = c - dc * i
            if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board[nr][nc] == color:
                count += 1
                i += 1
            else:
                break
        if count >= 5:
            return color
    return None

def draw_info(text, countdown=None):
    pygame.draw.rect(screen, GRAY, (0, WIDTH, WIDTH, HEIGHT - WIDTH))
    img = FONT.render(text, True, RED)
    screen.blit(img, (MARGIN, WIDTH + 30))
    if countdown is not None:
        countdown_img = SMALL_FONT.render(f"AI thinking time left: {countdown} sec", True, BLUE)
        screen.blit(countdown_img, (WIDTH - 300, WIDTH + 60))

def reset_game():
    global board, current_player, game_over, winner, ai_move_result, ai_thinking, ai_countdown
    board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
    current_player = 1
    game_over = False
    winner = None
    ai_move_result = None
    ai_thinking = False
    ai_countdown = ai_time_limit

# Enhanced Evaluation Function
def evaluate(board_state):
    # Simple heuristic: number of open sequences
    patterns = {
        'five': 100000,
        'open_four': 10000,
        'four': 1000,
        'open_three': 1000,
        'three': 100,
        'open_two': 100,
        'two': 10
    }
    score = 0
    for r in range(BOARD_SIZE):
        for c in range(BOARD_SIZE):
            if board_state[r][c] != 0:
                color = board_state[r][c]
                for dr, dc in [(1,0), (0,1), (1,1), (1,-1)]:
                    cnt, open_cnt = count_sequence(board_state, r, c, dr, dc, color)
                    if cnt >=5:
                        score += patterns['five']
                    elif cnt ==4 and open_cnt ==2:
                        score += patterns['open_four']
                    elif cnt ==4 and open_cnt ==1:
                        score += patterns['four']
                    elif cnt ==3 and open_cnt ==2:
                        score += patterns['open_three']
                    elif cnt ==3 and open_cnt ==1:
                        score += patterns['three']
                    elif cnt ==2 and open_cnt ==2:
                        score += patterns['open_two']
                    elif cnt ==2 and open_cnt ==1:
                        score += patterns['two']
    return score

def count_sequence(board_state, r, c, dr, dc, color):
    count = 1
    open_ends = 0

    # Forward direction
    i = 1
    while True:
        nr = r + dr * i
        nc = c + dc * i
        if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board_state[nr][nc] == color:
            count += 1
            i += 1
        else:
            if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board_state[nr][nc] == 0:
                open_ends += 1
            break

    # Backward direction
    i = 1
    while True:
        nr = r - dr * i
        nc = c - dc * i
        if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board_state[nr][nc] == color:
            count += 1
            i += 1
        else:
            if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board_state[nr][nc] == 0:
                open_ends += 1
            break
    return count, open_ends

def minimax(board_state, depth, maximizing, alpha, beta, start_time, time_limit):
    if depth == 0 or check_terminal(board_state):
        return evaluate(board_state), None

    if time.time() - start_time > time_limit:
        return evaluate(board_state), None

    best_move = None

    possible_moves = get_possible_moves(board_state)
    # Move ordering: prioritize center and recent moves
    possible_moves = sorted(possible_moves, key=lambda move: -distance_from_center(move))

    if maximizing:
        max_eval = -math.inf
        for move in possible_moves:
            new_board = [row[:] for row in board_state]
            new_board[move[0]][move[1]] = 2
            eval, _ = minimax(new_board, depth -1, False, alpha, beta, start_time, time_limit)
            if eval > max_eval:
                max_eval = eval
                best_move = move
            alpha = max(alpha, eval)
            if beta <= alpha:
                break
        return max_eval, best_move
    else:
        min_eval = math.inf
        for move in possible_moves:
            new_board = [row[:] for row in board_state]
            new_board[move[0]][move[1]] = 1
            eval, _ = minimax(new_board, depth -1, True, alpha, beta, start_time, time_limit)
            if eval < min_eval:
                min_eval = eval
                best_move = move
            beta = min(beta, eval)
            if beta <= alpha:
                break
        return min_eval, best_move

def distance_from_center(move):
    center = BOARD_SIZE // 2
    return math.hypot(move[0] - center, move[1] - center)

def check_terminal(board_state):
    for r in range(BOARD_SIZE):
        for c in range(BOARD_SIZE):
            if board_state[r][c] != 0:
                if evaluate_win(board_state, r, c):
                    return True
    # Draw
    for row in board_state:
        if 0 in row:
            return False
    return True

def evaluate_win(board_state, r, c):
    color = board_state[r][c]
    directions = [(1,0), (0,1), (1,1), (1,-1)]
    for dr, dc in directions:
        count = 1
        i = 1
        while True:
            nr = r + dr * i
            nc = c + dc * i
            if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board_state[nr][nc] == color:
                count +=1
                i +=1
            else:
                break
        i =1
        while True:
            nr = r - dr * i
            nc = c - dc * i
            if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board_state[nr][nc] == color:
                count +=1
                i +=1
            else:
                break
        if count >=5:
            return True
    return False

def get_possible_moves(board_state):
    # To optimize, consider only moves near existing pieces
    moves = set()
    for r in range(BOARD_SIZE):
        for c in range(BOARD_SIZE):
            if board_state[r][c] !=0:
                for dr in range(-2,3):
                    for dc in range(-2,3):
                        nr = r + dr
                        nc = c + dc
                        if 0 <= nr < BOARD_SIZE and 0 <= nc < BOARD_SIZE and board_state[nr][nc] ==0:
                            moves.add((nr, nc))
    if not moves:
        # If board is empty, choose center
        return [(BOARD_SIZE//2, BOARD_SIZE//2)]
    return list(moves)

def ai_move_minimax():
    global ai_move_result, ai_thinking, ai_countdown
    start_time = time.time()
    best_move = None
    depth = 1
    best_score = -math.inf
    while True:
        if time.time() - start_time >= ai_time_limit:
            break
        score, move = minimax(board, depth, True, -math.inf, math.inf, start_time, ai_time_limit)
        if move is not None:
            best_move = move
            best_score = score
        else:
            break
        depth +=1
    with ai_lock:
        ai_move_result = best_move
    ai_thinking = False

def start_ai_thread():
    global ai_thinking, ai_move_result, ai_start_time, ai_countdown
    ai_thinking = True
    ai_move_result = None
    ai_start_time = time.time()
    ai_countdown = ai_time_limit
    threading.Thread(target=ai_move_minimax, daemon=True).start()

def main_menu():
    menu = True
    while menu:
        screen.fill(GRAY)
        title = FONT.render('Gomoku Game', True, BLUE)
        screen.blit(title, (WIDTH//2 - title.get_width()//2, HEIGHT//2 - 150))

        pvp_text = FONT.render('Player vs Player', True, BLACK)
        pvc_text = FONT.render('Player vs AI', True, BLACK)
        screen.blit(pvp_text, (WIDTH//2 - pvp_text.get_width()//2, HEIGHT//2 - 50))
        screen.blit(pvc_text, (WIDTH//2 - pvc_text.get_width()//2, HEIGHT//2 + 10))

        pygame.display.flip()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mx, my = pygame.mouse.get_pos()
                if HEIGHT//2 -50 <= my <= HEIGHT//2 -50 + pvp_text.get_height():
                    return 'PVP'
                elif HEIGHT//2 +10 <= my <= HEIGHT//2 +10 + pvc_text.get_height():
                    return 'PVC'

def choose_color():
    choosing = True
    while choosing:
        screen.fill(GRAY)
        prompt = FONT.render('Choose Your Color', True, BLUE)
        screen.blit(prompt, (WIDTH//2 - prompt.get_width()//2, HEIGHT//2 - 100))

        black_text = FONT.render('Black', True, BLACK)
        white_text = FONT.render('White', True, WHITE)
        pygame.draw.rect(screen, BLACK, (WIDTH//2 - 100, HEIGHT//2 - 40, 80, 40))
        pygame.draw.rect(screen, WHITE, (WIDTH//2 +20, HEIGHT//2 -40, 80, 40))

        screen.blit(black_text, (WIDTH//2 - 90, HEIGHT//2 -35))
        screen.blit(white_text, (WIDTH//2 +30, HEIGHT//2 -35))

        pygame.display.flip()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mx, my = pygame.mouse.get_pos()
                if WIDTH//2 -100 <= mx <= WIDTH//2 -20 and HEIGHT//2 -40 <= my <= HEIGHT//2:
                    return 1  # Black
                elif WIDTH//2 +20 <= mx <= WIDTH//2 +100 and HEIGHT//2 -40 <= my <= HEIGHT//2:
                    return 2  # White

def main():
    global mode, player_color, current_player, game_over, winner, ai_thinking, ai_move_result, ai_countdown
    clock = pygame.time.Clock()

    mode = main_menu()

    if mode == 'PVC':
        player_color = choose_color()
        ai_player = 2 if player_color ==1 else 1

    draw_board()
    if mode == 'PVC' and player_color ==2:
        # AI starts first
        start_ai_thread()

    while True:
        draw_board()
        countdown_display = None
        if game_over:
            if winner ==1:
                msg = "Black Wins!"
            elif winner ==2:
                msg = "White Wins!"
            else:
                msg = "Draw!"
            draw_info(msg + " Click to Restart")
        else:
            if mode == 'PVP':
                turn = 'Black' if current_player ==1 else 'White'
                draw_info(f"{turn}'s Turn")
            else:
                if current_player == player_color:
                    turn = 'Your Turn'
                else:
                    if ai_thinking:
                        elapsed = time.time() - ai_start_time
                        remaining = max(0, ai_time_limit - int(elapsed))
                        countdown_display = remaining
                        # Check if time exceeded
                        if elapsed >= ai_time_limit and ai_thinking:
                            # Time out, make best available move
                            if ai_move_result is None:
                                # If AI hasn't returned any move, make random move
                                empty = [(r, c) for r in range(BOARD_SIZE) for c in range(BOARD_SIZE) if board[r][c] == 0]
                                if empty:
                                    move = random.choice(empty)
                                    board[move[0]][move[1]] = ai_player
                                    winner = is_winner(move[0], move[1])
                                    if winner:
                                        game_over = True
                                    current_player = player_color
                            else:
                                # Apply AI move
                                move = ai_move_result
                                if move:
                                    board[move[0]][move[1]] = ai_player
                                    winner = is_winner(move[0], move[1])
                                    if winner:
                                        game_over = True
                                    current_player = player_color
                            ai_thinking = False
                    elif not ai_thinking and ai_move_result is not None:
                        # Apply AI move
                        move = ai_move_result
                        if move:
                            board[move[0]][move[1]] = ai_player
                            winner = is_winner(move[0], move[1])
                            if winner:
                                game_over = True
                            current_player = player_color
                        ai_move_result = None
                    turn = 'AI is Thinking...'
                draw_info(turn, countdown_display)

        pygame.display.flip()

        if game_over:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    reset_game()
                    mode = main_menu()
                    if mode == 'PVC':
                        player_color = choose_color()
                        ai_player = 2 if player_color ==1 else 1
                    draw_board()
                    if mode == 'PVC' and player_color ==2:
                        # AI starts first
                        start_ai_thread()
                    continue
        else:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    pos = pygame.mouse.get_pos()
                    cell = get_cell(pos)
                    if cell and board[cell[0]][cell[1]] ==0:
                        if mode == 'PVP':
                            board[cell[0]][cell[1]] = current_player
                            winner = is_winner(cell[0], cell[1])
                            if winner:
                                game_over = True
                            current_player = 2 if current_player ==1 else 1
                        else:
                            if current_player == player_color:
                                board[cell[0]][cell[1]] = current_player
                                winner = is_winner(cell[0], cell[1])
                                if winner:
                                    game_over = True
                                else:
                                    current_player = ai_player
                                    # AI's turn
                                    start_ai_thread()

        # Update AI countdown and handle AI move
        if mode == 'PVC' and current_player == ai_player and not game_over:
            if not ai_thinking and ai_move_result is None:
                # Start AI thinking
                start_ai_thread()

        clock.tick(30)

if __name__ == "__main__":
    main()

1.写一个五子棋游戏，13*13，双人对弈，用python写，代码生产在一起，可以一次性运行bug free，用鼠标点击交互，黑白子UI，pygame里显示中文乱码全部改成英文
2.有双人对弈模式，和人机对战，选择模式 不用键盘 弹出方框用鼠标选择
3.人机对战，玩家可选黑白，完成人机对战五子棋AI，人机对战AI要厉害，最大化胜率赢过玩家，思考最长时间5秒并显示倒计时