Python Pygame五子棋游戏开发:从零实现事件循环与胜负判定

发布时间:2026/7/23 10:37:59
Python Pygame五子棋游戏开发:从零实现事件循环与胜负判定 1. 项目概述从零到一用Python和Pygame实现你的第一个五子棋游戏如果你对Python编程感兴趣并且想找一个既有趣又有挑战性的项目来练手那么用Pygame库开发一个五子棋小游戏绝对是个绝佳的选择。这不仅仅是一个简单的“Hello World”式的练习它融合了图形界面编程、事件处理、游戏逻辑判断和简单的数据结构应用堪称是检验你Python基础是否扎实的“试金石”。我自己在带新人入门时也常常把这个项目作为从脚本编程迈向应用开发的关键一步。这个教程的目标非常明确我们将从零开始一步步构建一个完整的、可运行的五子棋游戏。它支持双人对战拥有清晰的棋盘界面、落子动画、胜负判定等核心功能。整个过程我们将深度依赖Pygame这个强大的2D游戏开发库。Pygame的优势在于它封装了底层的SDL库让处理窗口、图像、声音和用户输入变得异常简单即使你之前没有任何游戏开发经验也能快速上手。通过这个项目你不仅能学会Pygame的基本使用更能深刻理解一个游戏程序是如何通过“事件循环”这个核心机制运转起来的。最终你将得到一个可以和朋友对弈的、完全由你亲手编写的游戏这种成就感是看多少教程都无法比拟的。2. 核心思路与架构设计理解游戏程序的“心跳”在动手写代码之前我们必须先想清楚整个游戏的运行逻辑。一个交互式游戏程序其核心是一个永不停止的“循环”我们称之为游戏主循环或事件循环。这个循环每秒钟会运行几十甚至上百次在每一次循环中它主要做三件事处理用户的输入比如鼠标点击、根据输入更新游戏内部的状态比如在棋盘数组的某个位置记录一颗黑子、将最新的游戏状态绘制到屏幕上。理解了这三步你就抓住了游戏开发的命脉。对于我们的五子棋游戏我们可以将整个程序分解为几个相对独立的模块这样代码结构会更清晰也便于调试和维护游戏初始化模块负责创建游戏窗口、加载资源如图片、字体、初始化游戏状态如空棋盘、当前执子方。棋盘与棋子数据模块这是游戏的大脑。我们需要一个数据结构比如一个15x15的二维列表来精确记录棋盘上每一个交叉点的状态空、黑子、白子。所有关于落子、判断胜负的逻辑都围绕这个数据结构展开。图形绘制模块负责将抽象的游戏数据那个二维列表变成屏幕上可视化的棋盘和棋子。这包括绘制背景、网格线、以及根据数据列表在正确位置绘制黑棋和白棋的图案。事件处理与游戏逻辑模块这是游戏的心脏。它监听鼠标点击事件将鼠标的像素坐标转换为棋盘的网格坐标然后判断该位置是否可落子。如果可以就更新数据模块并触发胜负判断。胜负判断的逻辑是核心算法需要检查刚落子的位置在横、竖、左斜、右斜四个方向上是否形成了连续的五子。胜负判定与状态管理模块专门处理游戏结束的逻辑。一旦检测到五子连珠就显示获胜信息并阻止继续落子直到玩家选择重新开始。这种模块化的设计思路使得我们可以分而治之逐个实现和测试每个部分最后将它们组装成一个完整的游戏。3. 环境准备与Pygame基础3.1 搭建你的Python开发环境工欲善其事必先利其器。首先确保你的电脑上安装了Python。我强烈推荐使用Python 3.7或以上的版本。你可以打开命令行Windows上是CMD或PowerShellMac/Linux上是Terminal输入python --version来检查。如果没有安装去Python官网下载安装包安装时务必勾选“Add Python to PATH”这个选项这能省去后续配置环境变量的麻烦。接下来是安装Pygame库。Pygame并非Python标准库所以我们需要用pip这个包管理工具来安装。在命令行中输入以下命令pip install pygame如果下载速度慢可以考虑使用国内的镜像源例如清华源pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple安装成功后可以写一个简单的测试脚本来验证import pygame pygame.init() print(“Pygame初始化成功”) screen pygame.display.set_mode((800, 600)) pygame.display.set_caption(“测试窗口”)运行这段代码如果弹出一个800x600的窗口说明环境配置成功。注意有些集成开发环境IDE如PyCharm、VSCode可能有自己独立的Python环境。如果你在命令行安装成功但在IDE中导入失败可能需要检查IDE的Python解释器设置确保它指向的是你刚刚安装Pygame的那个Python环境。3.2 Pygame核心概念速览在深入五子棋开发前快速理解几个Pygame的核心对象它们将贯穿我们整个项目pygame.display显示模块。我们通过它来创建游戏窗口 (set_mode)更新屏幕内容 (flip或update)。pygame.event事件模块。游戏的所有交互如键盘按下、鼠标点击、窗口关闭都通过事件队列来获取。我们的主循环会不断调用pygame.event.get()来获取和处理这些事件。Surface对象你可以把它理解成一张“画布”。我们的屏幕本身是一个大的Surface棋盘、棋子都是绘制在不同的Surface上最后再“贴”到主屏幕Surface上。加载的图片也会被转换成Surface对象。Rect对象矩形区域。它用来表示一个对象在屏幕上的位置和大小x, y, width, height常用于碰撞检测比如判断鼠标点击是否在棋盘格内和图像定位。Clock对象时钟。用于控制游戏的帧率FPS确保游戏在不同性能的电脑上都能以基本一致的速度运行避免循环跑得太快或太慢。理解了这些我们就可以开始构建游戏世界的第一块砖——棋盘了。4. 构建游戏世界棋盘绘制与数据结构4.1 初始化游戏窗口与参数定义一个好的开始是成功的一半。我们先定义一些全局常量这些数值将决定我们游戏的外观和逻辑。import pygame import sys # 初始化pygame pygame.init() # 定义颜色 (R, G, B) BACKGROUND (220, 179, 92) # 棋盘背景色仿木色 LINE_COLOR (0, 0, 0) # 网格线颜色黑色 BLACK (0, 0, 0) # 黑棋颜色 WHITE (255, 255, 255) # 白棋颜色 RED (255, 0, 0) # 用于高亮或提示 # 定义棋盘参数 BOARD_SIZE 15 # 15x15的标准五子棋盘 GRID_WIDTH 40 # 每个格子的像素宽度 MARGIN 50 # 棋盘边距 PIECE_RADIUS 18 # 棋子半径 # 计算窗口大小 WINDOW_WIDTH 2 * MARGIN (BOARD_SIZE - 1) * GRID_WIDTH WINDOW_HEIGHT WINDOW_WIDTH 100 # 下方留出空间显示状态信息 # 创建游戏窗口 screen pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption(“Python 五子棋”) clock pygame.time.Clock()这里BOARD_SIZE15定义了标准的15路棋盘。GRID_WIDTH和MARGIN共同决定了棋盘在窗口中的位置和大小。通过公式计算出窗口尺寸可以确保棋盘始终居中。4.2 设计棋盘的数据结构屏幕上的棋盘是给人看的程序内部需要一个高效的数据结构来记录棋局状态。一个二维列表是最直观的选择# 初始化棋盘状态 # 0: 空 1: 黑子 2: 白子 board [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]这个board列表就是游戏的“内存”。board[row][col] 1表示在第row行、第col列有一个黑子。所有后续的落子、判断都基于这个列表操作。4.3 绘制棋盘与棋子有了数据和参数我们就可以编写绘制函数了。绘制分为两部分静态的棋盘网格和动态的棋子。def draw_board(screen, board): “”“绘制棋盘网格和所有棋子”“” # 1. 填充背景色 screen.fill(BACKGROUND) # 2. 绘制网格线 for i in range(BOARD_SIZE): # 竖线 start_pos (MARGIN i * GRID_WIDTH, MARGIN) end_pos (MARGIN i * GRID_WIDTH, MARGIN (BOARD_SIZE - 1) * GRID_WIDTH) pygame.draw.line(screen, LINE_COLOR, start_pos, end_pos, 2) # 横线 start_pos (MARGIN, MARGIN i * GRID_WIDTH) end_pos (MARGIN (BOARD_SIZE - 1) * GRID_WIDTH, MARGIN i * GRID_WIDTH) pygame.draw.line(screen, LINE_COLOR, start_pos, end_pos, 2) # 3. 绘制棋盘上的五个小黑点天元和星位 dots [(3, 3), (3, 11), (7, 7), (11, 3), (11, 11)] for dot in dots: center_x MARGIN dot[0] * GRID_WIDTH center_y MARGIN dot[1] * GRID_WIDTH pygame.draw.circle(screen, LINE_COLOR, (center_x, center_y), 5) # 4. 根据board数据绘制棋子 for row in range(BOARD_SIZE): for col in range(BOARD_SIZE): if board[row][col] 1: # 黑子 center_x MARGIN col * GRID_WIDTH center_y MARGIN row * GRID_WIDTH pygame.draw.circle(screen, BLACK, (center_x, center_y), PIECE_RADIUS) # 给黑子加一个高光小白点增加立体感 pygame.draw.circle(screen, (100, 100, 100), (center_x - 4, center_y - 4), 4) elif board[row][col] 2: # 白子 center_x MARGIN col * GRID_WIDTH center_y MARGIN row * GRID_WIDTH pygame.draw.circle(screen, WHITE, (center_x, center_y), PIECE_RADIUS) pygame.draw.circle(screen, (200, 200, 200), (center_x - 4, center_y - 4), 4)这个函数会在每一帧被调用。它先清屏并画上固定的网格和点然后遍历整个board列表根据数值在对应坐标绘制棋子。这里我稍微美化了一下给棋子加了一个小高光让它们看起来更圆润立体。实操心得坐标转换是游戏开发中的高频操作。记住我们的board列表索引是[行][列]对应到屏幕坐标是(x, y) (MARGIN 列 * GRID_WIDTH, MARGIN 行 * GRID_WIDTH)。这个公式在鼠标事件处理时也会反向用到务必理解透彻。5. 实现游戏交互落子与胜负判定5.1 处理鼠标事件与坐标转换游戏的核心交互是鼠标点击落子。我们需要在事件循环中捕捉鼠标按下事件并将鼠标的像素坐标转换为棋盘的网格坐标。def get_board_pos(mouse_x, mouse_y): “”“将鼠标像素坐标转换为棋盘网格坐标 (row, col)”“” # 首先判断点击是否在棋盘区域内 if (mouse_x MARGIN or mouse_x WINDOW_WIDTH - MARGIN or mouse_y MARGIN or mouse_y WINDOW_HEIGHT - 100 - MARGIN): return None, None # 计算最近的交叉点 col round((mouse_x - MARGIN) / GRID_WIDTH) row round((mouse_y - MARGIN) / GRID_WIDTH) # 确保坐标在合法范围内 if 0 row BOARD_SIZE and 0 col BOARD_SIZE: return row, col else: return None, None这里使用了round()函数进行四舍五入这样玩家点击位置只要离某个交叉点足够近就会被吸附到该点上提升了操作体验。WINDOW_HEIGHT - 100是因为我们下方预留了100像素的状态栏区域。5.2 落子逻辑与轮流下棋在主循环中我们处理鼠标事件并更新游戏状态# 游戏状态变量 current_player 1 # 1 代表黑方 2 代表白方 game_over False winner None # 游戏主循环 running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.MOUSEBUTTONDOWN and not game_over: if event.button 1: # 左键点击 mouse_x, mouse_y event.pos row, col get_board_pos(mouse_x, mouse_y) if row is not None and col is not None and board[row][col] 0: # 该位置为空可以落子 board[row][col] current_player # 检查是否获胜 if check_win(board, row, col, current_player): game_over True winner current_player else: # 切换玩家 current_player 3 - current_player # 巧妙切换1和2current_player 3 - current_player是一个小技巧当当前玩家是1黑时3-12白当是2时3-21黑实现了轮流下棋。5.3 胜负判定算法详解这是五子棋游戏的灵魂。我们需要检查刚落子的位置在其横、竖、左斜/、右斜\四个方向上是否存在连续五个同色棋子。def check_win(board, row, col, player): “”“检查在(row, col)位置落子后玩家player是否获胜”“” # 四个方向向量: 横(0,1), 竖(1,0), 左斜(1,1), 右斜(1,-1) directions [(0, 1), (1, 0), (1, 1), (1, -1)] for d_row, d_col in directions: count 1 # 刚落下的这颗子 # 向正方向检查 r, c row d_row, col d_col while 0 r BOARD_SIZE and 0 c BOARD_SIZE and board[r][c] player: count 1 r d_row c d_col # 向反方向检查 r, c row - d_row, col - d_col while 0 r BOARD_SIZE and 0 c BOARD_SIZE and board[r][c] player: count 1 r - d_row c - d_col # 如果某个方向连续子数大于等于5则获胜 if count 5: return True return False这个算法效率很高它只检查了与最新落子相关的四条线。directions列表定义了四个方向。对于每个方向我们从落子点开始向两个相反方向延伸计数直到遇到边界、空位或异色棋子为止。如果累计数量达到5则判定获胜。注意事项这里判断条件是count 5而不是5。这是为了程序的健壮性理论上不会出现大于5的情况因为落子时会检查位置是否为空但这样写更安全。另外这个算法是“无禁手”规则标准的休闲五子棋。6. 打磨游戏体验状态显示、音效与重新开始6.1 绘制游戏状态信息为了让玩家清楚当前是谁的回合以及游戏结果我们需要在屏幕下方绘制状态栏。def draw_game_info(screen, current_player, game_over, winner): “”“在屏幕下方绘制当前玩家、获胜信息等”“” font pygame.font.SysFont(None, 36) info_area_y WINDOW_HEIGHT - 80 if game_over: if winner 1: text font.render(“游戏结束黑方获胜点击R键重新开始”, True, RED) else: text font.render(“游戏结束白方获胜点击R键重新开始”, True, RED) else: if current_player 1: text font.render(“当前回合黑方”, True, BLACK) else: text font.render(“当前回合白方”, True, WHITE) screen.blit(text, (WINDOW_WIDTH // 2 - text.get_width() // 2, info_area_y)) # 绘制简单的操作提示 hint_font pygame.font.SysFont(None, 24) hint hint_font.render(“按 R 键重新开始按 ESC 键退出”, True, (100, 100, 100)) screen.blit(hint, (WINDOW_WIDTH // 2 - hint.get_width() // 2, info_area_y 40))这里使用了pygame.font模块来渲染文字。SysFont(None, 36)表示使用系统默认字体大小为36。blit方法将渲染好的文字Surface绘制到屏幕的指定位置。6.2 添加音效与落子动画进阶一个带有音效和简单动画的游戏体验会好很多。我们可以为落子添加一个“咔哒”声并在棋子落下时增加一个由小变大的动画。 首先准备一个音效文件如place.wav放在项目目录下。# 在初始化部分加载音效 try: place_sound pygame.mixer.Sound(“place.wav”) except: print(“警告未找到音效文件游戏将静音运行。”) place_sound None # 修改落子逻辑添加音效和动画标记 animation_pieces [] # 列表用于存储正在动画中的棋子信息 (row, col, player, radius) # 在落子成功后 board[row][col] current_player animation_pieces.append([row, col, current_player, 5]) # 起始半径很小 if place_sound: place_sound.play() # 在主循环的绘制部分之前更新并绘制动画棋子 for ap in animation_pieces[:]: # 使用切片创建副本进行遍历因为可能要在循环中删除元素 ap[3] 1 # 半径增大 if ap[3] PIECE_RADIUS: animation_pieces.remove(ap) # 动画结束 else: # 绘制动画中的棋子半透明半径逐渐变大 center_x MARGIN ap[1] * GRID_WIDTH center_y MARGIN ap[0] * GRID_WIDTH color BLACK if ap[2] 1 else WHITE # 创建一个临时Surface来绘制带透明度的圆 s pygame.Surface((PIECE_RADIUS*2, PIECE_RADIUS*2), pygame.SRCALPHA) pygame.draw.circle(s, (*color, 150), (PIECE_RADIUS, PIECE_RADIUS), ap[3]) # 150是透明度 screen.blit(s, (center_x - PIECE_RADIUS, center_y - PIECE_RADIUS))动画的基本原理是在落子瞬间我们不仅更新board数据还把一个动画信息[row, col, player, radius]加入列表。在主循环中每帧都增大这个半径并绘制一个半透明的圆直到半径达到正常棋子大小再从列表中移除。这样视觉上就形成了一个棋子“长出来”的效果。6.3 实现重新开始与退出功能最后我们完善事件处理增加重新开始和退出的快捷键。# 在主循环的事件处理部分补充 for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: if event.key pygame.K_ESCAPE: running False elif event.key pygame.K_r: # 按R键重新开始 # 重置所有游戏状态 board [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)] current_player 1 game_over False winner None animation_pieces.clear() elif event.type pygame.MOUSEBUTTONDOWN and not game_over: # ... 原有的落子逻辑 ... # 在主循环中每帧都需要做的三件事 # 1. 绘制静态棋盘和固定棋子 draw_board(screen, board) # 2. 绘制动画棋子如果有 # ... (动画绘制代码如上所述) # 3. 绘制游戏信息 draw_game_info(screen, current_player, game_over, winner) # 4. 更新屏幕 pygame.display.flip() # 5. 控制帧率 clock.tick(60) # 每秒60帧按R键后我们将board列表重置为空并恢复所有游戏状态变量。pygame.display.flip()会将我们这一帧所有绘制的内容更新到屏幕上。clock.tick(60)确保了循环每秒最多运行60次既保证了动画流畅又不会过度消耗CPU资源。7. 项目总结与扩展思考至此一个功能完整的双人五子棋游戏就开发完成了。回顾整个过程我们经历了从环境搭建、Pygame基础学习到数据结构设计、核心算法实现再到交互优化和体验打磨的全流程。这个项目虽然不大但涵盖了小型游戏开发绝大多数核心概念事件驱动、状态管理、图形绘制、碰撞检测坐标转换、胜负逻辑算法。我个人在多次实现和教学这个项目后最大的体会是清晰的模块划分和数据结构设计是代码可读性和可维护性的基石。一开始就把board这个二维列表定义好后续的所有功能都围绕着它展开思路会非常清晰。另一个关键是理解并熟练运用“事件循环”模型这是所有交互式图形程序的通用范式。这个项目还有巨大的扩展空间你可以尝试挑战以下功能让游戏变得更强大实现人机对战这是最经典的扩展。你可以从最简单的随机落子AI开始然后实现基于“棋型”评估的规则AI比如优先堵对方活四、冲四自己创造活三等这需要你设计一个评估函数来给棋盘上的每个空位打分。增加游戏模式比如选择棋盘大小13路、19路、设置禁手规则针对黑棋的“三三禁手”、“四四禁手”、“长连禁手”这会让胜负判定逻辑变得复杂但更有挑战性。美化界面使用真实的木质棋盘和玉石棋子图片替代简单的几何图形绘制。为不同状态添加更丰富的音效如开始音效、获胜音效。甚至可以加入背景音乐。网络对战使用Socket编程将游戏改造成一个客户端-服务器架构支持两个人在不同的电脑上联机对战。这会将项目从一个单机程序升级为一个真正的网络应用。把基础版本跑通后选择其中一个方向深入下去你收获的将不仅仅是一个五子棋游戏更是解决复杂问题、架构软件系统的宝贵经验。编程的乐趣正是在于将想法一步步变为现实并不断打磨完善的过程。希望这个教程能成为你游戏开发之路上一块坚实的垫脚石。