Python通过Pygame实现一个五子棋对弈游戏
作者:Wang ruoxi
项目概述
本文通过 Pygame 实现一个五子棋对弈游戏(Gomoku),支持双人对战和人机对战两种模式。
游戏在 15×15 标准棋盘上进行,鼠标点击交叉点落子,任意一方率先在横、纵、斜方向连成五子即获胜。AI 采用基于威胁评估的贪心搜索策略,兼顾进攻与防守,具有一定对弈强度。其中:
- 双模式切换:支持双人对战与人机对战,按 A 键一键切换,重置棋局即时生效。
- AI 对手:AI 执白,采用邻域扫描 + 四方向连子评分策略,兼顾己方进攻与对手防守,候选位置取最高分并随机打破平局。
- 悬停预览:鼠标移动时实时显示半透明落子预览,帮助玩家精确定位交叉点。
- 棋子渲染:阴影 + 主体 + 高光三层绘制,模拟立体质感;最后落子以红色圆点标记。
- 胜利高亮:连成五子后以半透明红色光圈逐一标记获胜连线,视觉冲击明确。
- 键盘操作:R 重置棋局、A 切换模式、ESC 退出;点击任意处亦可在结束后重开。
游戏实现

初始化与基础设置
游戏启动时初始化 Pygame,定义棋盘参数和窗口布局常量。
W, H = 620, 700 CELL = 38 SIZE = 15 BOARD_X = (W - (SIZE-1)*CELL) // 2 BOARD_Y = 90 FPS = 60
CELL = 38 定义相邻交叉点间距为 38px,在 620px 窗口宽度内恰好容纳 15 路棋盘(14 个间距 = 532px)并留出两侧边距。BOARD_X 和 BOARD_Y 通过居中计算使棋盘水平居中、垂直方向留出顶部 90px 给标题与按钮区域。窗口高度 700px 额外预留底部空间给状态栏和操作提示。
颜色定义
C_BG = (30, 20, 10) C_BOARD = (200, 155, 80) C_LINE = (160, 115, 45) C_BLACK = (15, 15, 20) C_WHITE = (248, 248, 242) C_LAST = (220, 60, 60) C_WIN_LINE = (255, 60, 60) C_TITLE = (255, 210, 100)
整体采用暖色木质风格:深褐色背景(C_BG)搭配黄褐色棋盘(C_BOARD),模拟实木棋盘的视觉质感。网格线颜色(C_LINE)比棋盘底色略深,形成内敛的刻线效果。黑白棋子分别取接近纯黑和暖白色,在木质背景上对比鲜明。红色系承担"焦点"语义——最后落子标记(C_LAST)和胜利连线(C_WIN_LINE)均为红色,引导玩家视线。
胜负判定
DIRS4 = [(1,0),(0,1),(1,1),(1,-1)]
def check_win(board, r, c, player):
for dr, dc in DIRS4:
count = 1
line = [(r, c)]
for sign in [1, -1]:
nr, nc = r + sign*dr, c + sign*dc
while in_bounds(nr, nc) and board[nr][nc] == player:
count += 1; line.append((nr, nc))
nr += sign*dr; nc += sign*dc
if count >= 5: return line
return None
对每次落子检查四个方向(横、纵、正斜、反斜),从落子点出发向正反两侧延伸计数同色棋子。sign 取 1 和 -1 实现双向扫描,避免重复遍历整盘。连续同色棋子数达到 5 即返回获胜连线坐标列表,供渲染层高亮显示;未达 5 则返回 None。
这种"从落子点出发"的局部检测相比全盘扫描效率极高——每次落子仅检查最多 4×(14+1)=60 个格子,而非遍历 225 格。
AI 评估系统
连子评分函数
def score_line(board, r, c, dr, dc, player):
opp = 3 - player
count, open_ends = 0, 0
# 向前
nr, nc = r+dr, c+dc
while in_bounds(nr, nc) and board[nr][nc] == player:
count += 1; nr += dr; nc += dc
if in_bounds(nr, nc) and board[nr][nc] == 0: open_ends += 1
# 向后
nr, nc = r-dr, c-dc
while in_bounds(nr, nc) and board[nr][nc] == player:
count += 1; nr -= dr; nc -= dc
if in_bounds(nr, nc) and board[nr][nc] == 0: open_ends += 1
if count >= 4: return 100000
if count == 3 and open_ends == 2: return 10000
if count == 3 and open_ends == 1: return 1000
if count == 2 and open_ends == 2: return 500
if count == 2 and open_ends == 1: return 100
if count == 1 and open_ends == 2: return 10
return 0
评分函数是 AI 的核心。对给定位置和方向,统计该方向上已有的同色连子数(count)和两端的开放端数(open_ends),然后按威胁等级映射为分数。评分梯度设计遵循五子棋的战术优先级:
- 活四(count≥4):10 万分,必胜局面,最高优先级。
- 活三(3 子 + 双开端):1 万分,对手若不堵即变活四,极高威胁。
- 眠三(3 子 + 单开端):1000 分,潜在威胁但可被封堵。
- 活二(2 子 + 双开端):500 分,发展潜力。
- 眠二 / 活一:100 / 10 分,早期布局参考。
分数梯度跨越多个数量级(10 vs 100000),确保高威胁局面不会被大量低分候选的累加淹没。
综合位置评估
def evaluate_pos(board, r, c, player):
if board[r][c] != 0: return -1
total = 0
for dr, dc in DIRS4:
total += score_line(board, r, c, dr, dc, player) * 2
total += score_line(board, r, c, dr, dc, 3 - player)
return total
对每个候选落子位置,在四个方向上同时评估己方和对手的连子价值。己方得分乘以 2 倍权重,体现"进攻优先于防守"的策略倾向——在同等威胁等级下,AI 会优先选择对己方更有利的进攻点,而非被动堵截。但由于评分梯度极大,对手的活四(10 万分)仍然会压过己方的活二(500×2=1000),确保 AI 不会在对手即将连五时忽视防守。
候选位置筛选
def ai_move(board, ai_player):
candidates = []
for r in range(SIZE):
for c in range(SIZE):
if board[r][c] == 0:
near = any(
in_bounds(r+dr, c+dc) and board[r+dr][c+dc] != 0
for dr in range(-2, 3) for dc in range(-2, 3) if (dr, dc) != (0, 0)
)
if near:
s = evaluate_pos(board, r, c, ai_player)
candidates.append((s, r, c))
candidates.sort(reverse=True)
AI 不会遍历全部 225 个交叉点,而是仅考虑已有棋子周围 2 格范围内的空位。这一邻域剪枝大幅减少评估数量——中局通常只有 40–60 个候选位置,相比全盘搜索减少 70% 以上的计算量。排序后取最高分,若有多个同分候选则随机选取,避免 AI 行为过于固定。
开局时若棋盘为空则直接落子天元(SIZE//2, SIZE//2),符合五子棋先手占 中的基本定式。
棋盘渲染
底板与网格
board_rect = pygame.Rect(BOARD_X-CELL//2-4, BOARD_Y-CELL//2-4,
(SIZE-1)*CELL+CELL+8, (SIZE-1)*CELL+CELL+8)
pygame.draw.rect(surf, C_BOARD, board_rect, border_radius=8)
pygame.draw.rect(surf, C_LINE, board_rect, 2, border_radius=8)
棋盘底板以 8px 圆角矩形绘制,向外扩展 CELL//2 + 4 像素以覆盖边缘交叉点的完整区域。先填充木色底板,再叠加 2px 深色描边,模拟棋盘边框的立体感。
星位点
for pr, pc in [(3,3), (3,11), (7,7), (11,3), (11,11)]:
pygame.draw.circle(surf, C_DOT, (BOARD_X+pc*CELL, BOARD_Y+pr*CELL), 4)
标准 15 路棋盘的 5 个星位点(四角星 + 天元),半径 4px 的深色圆点,帮助玩家快速定位棋盘区域。星位坐标采用 (3,3), (3,11), (11,3), (11,11) 四角对称加 (7,7) 中心的经典布局。
棋子立体渲染
# 阴影
pygame.draw.circle(surf, (0,0,0,80), (cx+2, cy+3), pr)
# 主体
pygame.draw.circle(surf, color, (cx, cy), pr)
# 渐变高光
hl = (70,70,70) if v==1 else (255,255,255)
pygame.draw.circle(surf, hl, (cx-pr//3, cy-pr//3), pr//3)
# 最后落子标记
if last_move and (r,c) == last_move:
pygame.draw.circle(surf, C_LAST, (cx, cy), 5)
每颗棋子由三层圆形构成立体效果:底层阴影向右下偏移 (+2, +3) 像素,模拟光源从左上方照射;中层为棋子主体;顶层在左上方 (-pr//3, -pr//3) 位置叠加小圆高光,黑子用暗灰色高光、白子用纯白色高光,模拟球面反光。最后落子以 5px 红色圆点标记在棋子中心,让双方都能迅速定位对手的最新一手。
悬停预览
if hover:
hr, hc = hover
if board[hr][hc] == 0:
s = pygame.Surface((CELL, CELL), pygame.SRCALPHA)
pygame.draw.circle(s, (100,200,100,100), (CELL//2, CELL//2), CELL//2-4)
surf.blit(s, (BOARD_X+hc*CELL-CELL//2, BOARD_Y+hr*CELL-CELL//2))
鼠标悬停时在目标交叉点绘制半透明绿色圆形预览(alpha=100),仅在该位置为空时显示。预览圆比实际棋子略大(CELL//2-4 vs 棋子半径 CELL//2-3),形成柔和的提示光晕。AI 回合时悬停预览自动禁用,避免误导玩家。
胜利连线高亮
if win_line:
for wr, wc in win_line:
cx = BOARD_X + wc*CELL; cy = BOARD_Y + wr*CELL
s = pygame.Surface((CELL//2*2, CELL//2*2), pygame.SRCALPHA)
pygame.draw.circle(s, (255,80,80,120), (CELL//2, CELL//2), CELL//2)
surf.blit(s, (cx-CELL//2, cy-CELL//2))
获胜的五子连线上每颗棋子额外叠加一层半透明红色光圈(alpha=120),通过独立 SRCALPHA Surface 实现透明混合。红色光圈大小与格子等宽,在视觉上将五子连成一条醒目的红色高亮带,即使在满盘棋子中也能一眼辨识胜负关键。
悬停格计算
gx = mx - BOARD_X; gy = my - BOARD_Y
hover = None
if -CELL//2 <= gx <= (SIZE-1)*CELL+CELL//2 and -CELL//2 <= gy <= (SIZE-1)*CELL+CELL//2:
hc = round(gx / CELL); hr = round(gy / CELL)
if in_bounds(hr, hc):
hover = (hr, hc)
将鼠标屏幕坐标转换为棋盘相对坐标后,用 round() 四舍五入取最近交叉点。相比 int() 截断,round() 使每个交叉点的有效点击区域恰好是以它为中心的 CELL×CELL 正方形,点击体验更自然。边界检查确保鼠标在棋盘外侧半格范围内仍能选中边缘交叉点,不会出现"明明看到棋盘边缘却点不到"的问题。
落子与状态流转
def place(r, c):
nonlocal board, current, last_move, win_line, game_over, pending_ai
if board[r][c] != 0 or game_over: return
board[r][c] = current
last_move = (r, c)
wl = check_win(board, r, c, current)
if wl:
win_line = wl; game_over = True
elif all(board[r][c] != 0 for r in range(SIZE) for c in range(SIZE)):
game_over = True
else:
current = 3 - current
if vs_ai and current == ai_player and not game_over:
pending_ai = True
place() 函数封装了落子的完整状态流转:写入棋盘 → 记录最后落子 → 检查胜负 → 检查平局(满盘) → 切换当前玩家。current = 3 - current 利用黑=1、白=2 的编码巧妙实现交替(3-1=2, 3-2=1)。人机模式下切换到 AI 回合时设置 pending_ai 标志,将 AI 计算延迟到下一帧主循环中执行,避免在事件处理中阻塞渲染。
绘制层次
绘制顺序为:深色背景填充 → 标题与按钮 → 棋盘底板 → 网格线 → 星位点 → 悬停预览 → 棋子(阴影 + 主体 + 高光 + 最后落子标记)→ 胜利连线 → 状态栏(当前回合 + 操作提示)→ 游戏结束遮罩弹窗。
棋子绘制在网格线之上确保不被线条切割,悬停预览在棋子之下避免遮挡已有棋子,胜利高亮在最顶层以叠加形式呈现。
状态栏
y_ui = BOARD_Y + (SIZE-1)*CELL + CELL//2 + 14
who = "黑方" if current==1 else ("AI(白)" if vs_ai and current==ai_player else "白方")
pygame.draw.circle(screen, C_BLACK if current==1 else C_WHITE, (24, y_ui+12), 12)
t = FONT_MD.render(f"轮到:{who}", True, col)
状态栏在棋盘下方显示当前回合方,左侧以 12px 实心圆直观表示黑/白色,文字标注"黑方"“白方"或"AI(白)”。人机模式下 AI 回合的文案区分于普通白方,让玩家清晰知道当前是否需要等待 AI 落子。
结算弹窗
if game_over:
ov = pygame.Surface((W, H), pygame.SRCALPHA)
ov.fill((0,0,0,140))
screen.blit(ov, (0, 0))
bc = sum(board[r][c]==1 for r in range(SIZE) for c in range(SIZE))
wc = sum(board[r][c]==2 for r in range(SIZE) for c in range(SIZE))
if win_line:
winner_id = board[win_line[0][0]][win_line[0][1]]
if vs_ai: wstr = "你赢了!🎉" if winner_id != ai_player else "AI获胜"
else: wstr = f"{'黑方' if winner_id==1 else '白方'}获胜!"
else:
wstr = "平局!"
半透明黑色遮罩(alpha=140)叠加在完整棋局之上,让玩家在结算界面仍能看到最终盘面和胜利连线。弹窗统计双方落子总数(bc / wc),并根据模式(双人/AI)和胜者输出不同文案:人机模式区分"你赢了"和"AI获胜",双人模式显示"黑方/白方获胜",满盘无五连则显示"平局"。
主循环
while True:
mx, my = pygame.mouse.get_pos()
# 悬停格计算 → 事件处理 → AI 落子 → 绘制
...
pygame.display.flip()
clock.tick(FPS)
主循环每帧顶部获取鼠标坐标并计算悬停格,保证预览与光标严格同步。AI 落子在事件处理之后、绘制之前执行,确保 AI 的落子结果在同一帧内即可渲染,玩家感知不到延迟。clock.tick(FPS) 锁定 60 帧,使悬停预览和按钮高亮的响应流畅。
全部代码
"""
五子棋(Gomoku)
模式:双人 或 vs AI(Minimax威胁评估,深度2)
操作:鼠标点击落子
连5子获胜
"""
import pygame
import sys
import copy
import math
import random
pygame.init()
W, H = 620, 700
CELL = 38
SIZE = 15
BOARD_X = (W - (SIZE-1)*CELL) // 2
BOARD_Y = 90
FPS = 60
C_BG = (30, 20, 10)
C_BOARD = (200, 155, 80)
C_LINE = (160, 115, 45)
C_DOT = (80, 50, 20)
C_BLACK = (15, 15, 20)
C_WHITE = (248, 248, 242)
C_LAST = (220, 60, 60)
C_HINT = (100, 200, 100)
C_TEXT = (240, 220, 180)
C_BTN = (80, 60, 30)
C_BTN_HL = (120, 90, 40)
C_WIN_LINE = (255, 60, 60)
C_TITLE = (255, 210, 100)
C_GREY = (150, 130, 100)
# 中文字体
CHINESE_FONT_PATH = r"C:/Windows/Fonts/simsun.ttc"
FONT_TL = pygame.font.Font(CHINESE_FONT_PATH, 28)
FONT_MD = pygame.font.Font(CHINESE_FONT_PATH, 20)
FONT_SM = pygame.font.Font(CHINESE_FONT_PATH, 15)
FONT_SC = pygame.font.Font(CHINESE_FONT_PATH, 40)
DIRS4 = [(1,0),(0,1),(1,1),(1,-1)]
def in_bounds(r,c): return 0<=r<SIZE and 0<=c<SIZE
def check_win(board, r, c, player):
for dr,dc in DIRS4:
count = 1
line = [(r,c)]
for sign in [1,-1]:
nr,nc = r+sign*dr, c+sign*dc
while in_bounds(nr,nc) and board[nr][nc]==player:
count+=1; line.append((nr,nc))
nr+=sign*dr; nc+=sign*dc
if count>=5: return line
return None
# ── AI评估 ────────────────────────────────────────────────────────────
def score_line(board, r, c, dr, dc, player):
"""评估从(r,c)出发某方向的连子价值"""
opp = 3-player
count, open_ends = 0, 0
# 向前
nr,nc = r+dr,c+dc
while in_bounds(nr,nc) and board[nr][nc]==player:
count+=1; nr+=dr; nc+=dc
if in_bounds(nr,nc) and board[nr][nc]==0: open_ends+=1
# 向后
nr,nc = r-dr,c-dc
while in_bounds(nr,nc) and board[nr][nc]==player:
count+=1; nr-=dr; nc-=dc
if in_bounds(nr,nc) and board[nr][nc]==0: open_ends+=1
if count>=4: return 100000
if count==3 and open_ends==2: return 10000
if count==3 and open_ends==1: return 1000
if count==2 and open_ends==2: return 500
if count==2 and open_ends==1: return 100
if count==1 and open_ends==2: return 10
return 0
def evaluate_pos(board, r, c, player):
if board[r][c]!=0: return -1
total = 0
for dr,dc in DIRS4:
total += score_line(board,r,c,dr,dc,player)*2
total += score_line(board,r,c,dr,dc,3-player)
return total
def ai_move(board, ai_player):
best_score, best_pos = -1, None
candidates = []
# 只考虑有棋子周围的位置
for r in range(SIZE):
for c in range(SIZE):
if board[r][c]==0:
near = any(
in_bounds(r+dr,c+dc) and board[r+dr][c+dc]!=0
for dr in range(-2,3) for dc in range(-2,3) if (dr,dc)!=(0,0)
)
if near:
s = evaluate_pos(board,r,c,ai_player)
candidates.append((s,r,c))
candidates.sort(reverse=True)
if not candidates:
r,c = SIZE//2, SIZE//2
if board[r][c]!=0:
r,c = random.randint(0,SIZE-1), random.randint(0,SIZE-1)
return r,c
# 取最高分(加随机打破平局)
top = [x for x in candidates if x[0]==candidates[0][0]]
_,r,c = random.choice(top)
return r,c
# ── 绘制 ─────────────────────────────────────────────────────────────
def draw_board(surf, board, last_move, win_line, hover):
# 棋盘底色
board_rect = pygame.Rect(BOARD_X-CELL//2-4, BOARD_Y-CELL//2-4,
(SIZE-1)*CELL+CELL+8, (SIZE-1)*CELL+CELL+8)
pygame.draw.rect(surf, C_BOARD, board_rect, border_radius=8)
pygame.draw.rect(surf, C_LINE, board_rect, 2, border_radius=8)
# 网格线
for i in range(SIZE):
pygame.draw.line(surf, C_LINE,
(BOARD_X, BOARD_Y+i*CELL), (BOARD_X+(SIZE-1)*CELL, BOARD_Y+i*CELL), 1)
pygame.draw.line(surf, C_LINE,
(BOARD_X+i*CELL, BOARD_Y), (BOARD_X+i*CELL, BOARD_Y+(SIZE-1)*CELL), 1)
# 星位点
for pr,pc in [(3,3),(3,11),(7,7),(11,3),(11,11)]:
pygame.draw.circle(surf, C_DOT,
(BOARD_X+pc*CELL, BOARD_Y+pr*CELL), 4)
# 悬停提示
if hover:
hr,hc = hover
if board[hr][hc]==0:
s = pygame.Surface((CELL,CELL), pygame.SRCALPHA)
pygame.draw.circle(s, (100,200,100,100), (CELL//2,CELL//2), CELL//2-4)
surf.blit(s, (BOARD_X+hc*CELL-CELL//2, BOARD_Y+hr*CELL-CELL//2))
# 棋子
for r in range(SIZE):
for c in range(SIZE):
v = board[r][c]
if v==0: continue
cx = BOARD_X+c*CELL
cy = BOARD_Y+r*CELL
pr = CELL//2-3
color = C_BLACK if v==1 else C_WHITE
# 阴影
pygame.draw.circle(surf, (0,0,0,80), (cx+2,cy+3), pr)
# 主体
pygame.draw.circle(surf, color, (cx,cy), pr)
# 渐变高光
hl = (70,70,70) if v==1 else (255,255,255)
pygame.draw.circle(surf, hl, (cx-pr//3, cy-pr//3), pr//3)
# 最后落子
if last_move and (r,c)==last_move:
pygame.draw.circle(surf, C_LAST, (cx,cy), 5)
# 胜利连线高亮
if win_line:
for wr,wc in win_line:
cx=BOARD_X+wc*CELL; cy=BOARD_Y+wr*CELL
s = pygame.Surface((CELL//2*2, CELL//2*2), pygame.SRCALPHA)
pygame.draw.circle(s, (255,80,80,120), (CELL//2,CELL//2), CELL//2)
surf.blit(s, (cx-CELL//2, cy-CELL//2))
def main():
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption("五子棋")
clock = pygame.time.Clock()
vs_ai = True
ai_player = 2 # AI执白
board = [[0]*SIZE for _ in range(SIZE)]
current = 1
last_move = None
win_line = None
game_over = False
hover = None
pending_ai = False
def reset():
nonlocal board, current, last_move, win_line, game_over, hover, pending_ai
board = [[0]*SIZE for _ in range(SIZE)]
current = 1; last_move = None; win_line = None
game_over = False; hover = None; pending_ai = False
def place(r, c):
nonlocal board, current, last_move, win_line, game_over, pending_ai
if board[r][c]!=0 or game_over: return
board[r][c] = current
last_move = (r,c)
wl = check_win(board, r, c, current)
if wl:
win_line = wl; game_over = True
elif all(board[r][c]!=0 for r in range(SIZE) for c in range(SIZE)):
game_over = True
else:
current = 3-current
if vs_ai and current==ai_player and not game_over:
pending_ai = True
while True:
mx, my = pygame.mouse.get_pos()
# 计算悬停格
gx = mx - BOARD_X; gy = my - BOARD_Y
hover = None
if -CELL//2 <= gx <= (SIZE-1)*CELL+CELL//2 and -CELL//2 <= gy <= (SIZE-1)*CELL+CELL//2:
hc = round(gx / CELL); hr = round(gy / CELL)
if in_bounds(hr, hc):
hover = (hr, hc)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit(); sys.exit()
if event.key == pygame.K_r: reset()
if event.key == pygame.K_a:
vs_ai = not vs_ai; reset()
if event.type == pygame.MOUSEBUTTONDOWN and event.button==1:
# 按钮
rst_btn = pygame.Rect(W-270, 10, 120, 32)
ai_btn = pygame.Rect(W-140, 10, 120, 32)
if rst_btn.collidepoint(mx,my): reset(); continue
if ai_btn.collidepoint(mx,my): vs_ai=not vs_ai; reset(); continue
if game_over: reset(); continue
if hover and (not vs_ai or current!=ai_player):
place(*hover)
# AI落子
if pending_ai and not game_over:
pending_ai = False
r,c = ai_move(board, ai_player)
place(r,c)
# ── 绘制 ──────────────────────────────────────────────────────
screen.fill(C_BG)
# 标题
tl = FONT_TL.render("五 子 棋", True, C_TITLE)
screen.blit(tl, tl.get_rect(x=14, y=14))
# 按钮
rst_btn = pygame.Rect(W-270, 10, 120, 32)
ai_btn = pygame.Rect(W-140, 10, 120, 32)
for btn, text in [(rst_btn,"R / 重新开始"), (ai_btn, f"模式:{'AI' if vs_ai else '双人'}")]:
pygame.draw.rect(screen, C_BTN, btn, border_radius=8)
screen.blit(FONT_SM.render(text, True, C_TEXT), btn.move(6,8))
draw_board(screen, board, last_move, win_line,
hover if not (vs_ai and current==ai_player) else None)
# 状态栏
y_ui = BOARD_Y + (SIZE-1)*CELL + CELL//2 + 14
if not game_over:
who = "黑方" if current==1 else ("AI(白)" if vs_ai and current==ai_player else "白方")
col = (200,200,200) if current==1 else (255,210,100)
pygame.draw.circle(screen, C_BLACK if current==1 else C_WHITE, (24,y_ui+12), 12)
t = FONT_MD.render(f"轮到:{who}", True, col)
screen.blit(t, (44, y_ui+2))
hint = FONT_SM.render("R 重置 | A 切换模式 | 点击落子", True, C_GREY)
screen.blit(hint, hint.get_rect(centerx=W//2, y=y_ui+36))
# 胜利弹窗
if game_over:
ov = pygame.Surface((W,H), pygame.SRCALPHA)
ov.fill((0,0,0,140))
screen.blit(ov,(0,0))
box = pygame.Rect(W//2-170, H//2-80, 340, 180)
pygame.draw.rect(screen, (25,15,5), box, border_radius=16)
pygame.draw.rect(screen, C_TITLE, box, 3, border_radius=16)
bc = sum(board[r][c]==1 for r in range(SIZE) for c in range(SIZE))
wc = sum(board[r][c]==2 for r in range(SIZE) for c in range(SIZE))
if win_line:
winner_id = board[win_line[0][0]][win_line[0][1]]
if vs_ai: wstr = "你赢了!🎉" if winner_id!=ai_player else "AI获胜"
else: wstr = f"{'黑方' if winner_id==1 else '白方'}获胜!"
else:
wstr = "平局!"
lines = [
(FONT_SC, wstr, C_TITLE, -40),
(FONT_MD, f"黑 {bc} 子 | 白 {wc} 子", C_TEXT, 15),
(FONT_SM, "点击任意处 或 按 R 重新开始", C_GREY, 60),
]
for font, text, color, dy in lines:
t = font.render(text, True, color)
screen.blit(t, t.get_rect(centerx=W//2, centery=H//2+dy))
pygame.display.flip()
clock.tick(FPS)
if __name__ == "__main__":
main()
以上就是Python通过Pygame实现一个五子棋对弈游戏的详细内容,更多关于Python Pygame五子棋游戏的资料请关注脚本之家其它相关文章!
