python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python Pygame五子棋游戏

Python通过Pygame实现一个五子棋对弈游戏

作者:Wang ruoxi

本文用Pygame实现双人对战和人机对战,AI基于威胁评估的贪心搜索,兼顾进攻防守,教你如何设计评分函数、优化候选位置筛选,还有悬停预览和胜利高亮等细节,适合想学习游戏开发或AI策略的你,需要的朋友可以参考下

项目概述

本文通过 Pygame 实现一个五子棋对弈游戏(Gomoku),支持双人对战和人机对战两种模式。

游戏在 15×15 标准棋盘上进行,鼠标点击交叉点落子,任意一方率先在横、纵、斜方向连成五子即获胜。AI 采用基于威胁评估的贪心搜索策略,兼顾进攻与防守,具有一定对弈强度。其中:

游戏实现

初始化与基础设置

游戏启动时初始化 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),然后按威胁等级映射为分数。评分梯度设计遵循五子棋的战术优先级:

分数梯度跨越多个数量级(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五子棋游戏的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文