
import pygame
import sys
import json
import os

pygame.init()
pygame.mixer.init()

pygame.mixer.music.load(".\\fodder\\bgm.mp3")
pygame.mixer.music.play(-1)
place_sound = pygame.mixer.Sound("./fodder/sound_click_btn.mp3")

w, h = 900, 900
n = 15
s = w // n
black = (0, 0, 0)
white = (255, 255, 255)
bg = (200, 150, 100)
red = (200, 0, 0)

scr = pygame.display.set_mode((w, h))
pygame.display.set_caption("五子棋")

f_big = pygame.font.SysFont("fangsong", 80)
f_small = pygame.font.SysFont("fangsong", 60)
f_button = pygame.font.SysFont("fangsong", 40)

ranking_file = "rankings.json"

def load_rankings():
    if os.path.exists(ranking_file):
        with open(ranking_file, "r", encoding="utf-8") as f:
            return json.load(f)
    return {}

def save_rankings(data):
    with open(ranking_file, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

rankings = load_rankings()

def record_win(name):
    if name not in rankings:
        rankings[name] = {"wins": 0, "games": 0}
    rankings[name]["wins"] += 1
    rankings[name]["games"] += 1

def record_loss(name):
    if name not in rankings:
        rankings[name] = {"wins": 0, "games": 0}
    rankings[name]["games"] += 1

class TextInputBox:
    def __init__(self, x, y, w, h, label):
        self.rect = pygame.Rect(x, y, w, h)
        self.color = (200, 200, 200)
        self.text = ''
        self.label = label
        self.txt_surface = pygame.font.SysFont("fangsong", 40).render(self.text, True, black)
        self.active = False

    def handle_event(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN:
            self.active = self.rect.collidepoint(event.pos)
        elif event.type == pygame.KEYDOWN and self.active:
            if event.key == pygame.K_BACKSPACE:
                self.text = self.text[:-1]
            else:
                self.text += event.unicode
            self.txt_surface = pygame.font.SysFont("fangsong", 40).render(self.text, True, black)

    def draw(self, screen):
        label_surface = pygame.font.SysFont("fangsong", 40).render(self.label, True, black)
        screen.blit(label_surface, (self.rect.x, self.rect.y - 30))
        pygame.draw.rect(screen, self.color, self.rect, 2)
        screen.blit(self.txt_surface, (self.rect.x + 5, self.rect.y + 8))

game_state = "menu"
input_boxes = [
    TextInputBox(w//2 - 150, h//2 - 30, 300, 40, "黑棋玩家："),
    TextInputBox(w//2 - 150, h//2 + 60, 300, 40, "白棋玩家：")
]
player1, player2 = "", ""
p = 1
b = [[0 for _ in range(n)] for _ in range(n)]
over = False

def draw_board():
    scr.fill(bg)
    for i in range(n):
        pygame.draw.line(scr, black, (s // 2, s // 2 + i * s), (w - s // 2, s // 2 + i * s), 1)
        pygame.draw.line(scr, black, (s // 2 + i * s, s // 2), (s // 2 + i * s, h - s // 2), 1)
    pygame.draw.rect(scr, (230, 180, 150), (w - 160, 20, 140, 40))
    t = f_button.render("返回主菜单", True, black)
    scr.blit(t, (w - 150, 25))

def draw_chess():
    for y in range(n):
        for x in range(n):
            cx, cy = x * s + s // 2, y * s + s // 2
            r = s // 2 - 4
            if b[y][x] == 1:
                pygame.draw.circle(scr, (30, 30, 30), (cx, cy), r)
                pygame.draw.circle(scr, black, (cx - r // 3, cy - r // 3), r // 2)
            elif b[y][x] == 2:
                pygame.draw.circle(scr, white, (cx, cy), r)
                pygame.draw.circle(scr, (200, 200, 200), (cx - r // 3, cy - r // 3), r // 2)

def check_win(x, y, who):
    def count(dx, dy):
        c = 1
        for d in [1, -1]:
            nx, ny = x, y
            while True:
                nx += dx * d
                ny += dy * d
                if 0 <= nx < n and 0 <= ny < n and b[ny][nx] == who:
                    c += 1
                else:
                    break
        return c
    for dx, dy in [(1, 0), (0, 1), (1, 1), (1, -1)]:
        if count(dx, dy) >= 5:
            return True
    return False

def draw_text_center(text, font, color, y_offset=0):
    t = font.render(text, True, color)
    r = t.get_rect(center=(w // 2, h // 2 + y_offset))
    scr.blit(t, r)
    return r

def draw_menu():
    scr.fill((240, 220, 180))
    draw_text_center("五子棋小游戏", f_big, black, -150)
    start_btn = draw_text_center("开始游戏", f_small, black, -20)
    rank_btn = draw_text_center("查看排行榜", f_small, black, 50)
    quit_btn = draw_text_center("退出游戏", f_small, red, 120)
    return start_btn, rank_btn, quit_btn

def draw_name_input():
    scr.fill((220, 220, 200))
    draw_text_center("输入玩家姓名", f_big, black, -200)
    for box in input_boxes:
        box.draw(scr)
    draw_text_center("按 Enter 开始游戏", f_button, red, 150)

def draw_win_message():
    winner = player1 if p == 2 else player2
    draw_text_center(f"{winner} 获胜！点击任意处重新开始", f_small, black)

def draw_rankings():
    scr.fill((240, 240, 220))
    draw_text_center("排行榜", f_big, black, -300)
    y = 150
    for name, data in sorted(rankings.items(), key=lambda x: x[1]["wins"], reverse=True):
        win = data["wins"]
        games = data["games"]
        rate = (win / games * 100) if games else 0
        txt = f"{name}: 胜场 {win} / 对局 {games} - 胜率 {rate:.1f}%"
        t = f_small.render(txt, True, black)
        scr.blit(t, (100, y))
        y += 50
    draw_text_center("点击任意处返回菜单", f_small, red, 350)

while True:
    scr.fill(bg)

    if game_state == "menu":
        start_btn, rank_btn, quit_btn = draw_menu()
    elif game_state == "input_name":
        draw_name_input()
    elif game_state == "play":
        draw_board()
        draw_chess()
        if over:
            draw_win_message()
    elif game_state == "rank":
        draw_rankings()

    pygame.display.flip()

    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if game_state == "menu":
            if e.type == pygame.MOUSEBUTTONDOWN:
                mx, my = pygame.mouse.get_pos()
                if start_btn.collidepoint(mx, my):
                    game_state = "input_name"
                elif rank_btn.collidepoint(mx, my):
                    game_state = "rank"
                elif quit_btn.collidepoint(mx, my):
                    pygame.quit()
                    sys.exit()
        elif game_state == "input_name":
            for box in input_boxes:
                box.handle_event(e)
            if e.type == pygame.KEYDOWN and e.key == pygame.K_RETURN:
                player1 = input_boxes[0].text.strip() or "玩家1"
                player2 = input_boxes[1].text.strip() or "玩家2"
                b = [[0 for _ in range(n)] for _ in range(n)]
                p = 1
                over = False
                game_state = "play"
        elif game_state == "play":
            if e.type == pygame.MOUSEBUTTONDOWN:
                mx, my = pygame.mouse.get_pos()
                if w - 160 <= mx <= w - 20 and 20 <= my <= 60:
                    b = [[0 for _ in range(n)] for _ in range(n)]
                    p = 1
                    over = False
                    game_state = "menu"
                elif not over:
                    gx, gy = mx // s, my // s
                    if 0 <= gx < n and 0 <= gy < n and b[gy][gx] == 0:
                        b[gy][gx] = p
                        if place_sound:
                            place_sound.play()
                        if check_win(gx, gy, p):
                            winner = player1 if p == 1 else player2
                            loser = player2 if p == 1 else player1
                            record_win(winner)
                            record_loss(loser)
                            save_rankings(rankings)
                            over = True
                        else:
                            p = 2 if p == 1 else 1
                elif over:
                    b = [[0 for _ in range(n)] for _ in range(n)]
                    p = 1
                    over = False
        elif game_state == "rank":
            if e.type == pygame.MOUSEBUTTONDOWN:
                game_state = "menu"
