parent
d0f661cc04
commit
7825e7b438
@ -0,0 +1,237 @@
|
||||
|
||||
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"
|
Loading…
Reference in new issue