import pygame import sys import json import os pygame.init() pygame.mixer.init() pygame.mixer.music.load("./bgm.mp3") pygame.mixer.music.play(-1) place_sound = pygame.mixer.Sound("./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"