You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
8.0 KiB

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"