行业资讯

Python Pygame中国象棋开发:从规则实现到AI对战完整指南

发布时间:2026/8/8 0:49:43
Python Pygame中国象棋开发:从规则实现到AI对战完整指南 1. 项目概述与核心价值最近在整理自己的项目库翻到了几年前用Python和Pygame写的一个中国象棋游戏。这个项目当时花了不少心思不仅实现了完整的双人对战还集成了一个基于简单规则的AI对手。今天把它重新梳理了一遍代码也做了优化和注释打算完整地分享出来。对于想用Python做点有趣东西的朋友尤其是刚学完基础语法、想找个综合项目练手的同学这个项目会是一个非常好的选择。它不像“贪吃蛇”或“飞机大战”那么简单但也没有复杂到让人望而却步正好卡在“有挑战性但又能搞定”的甜区。这个项目能帮你做什么首先它能让你把Python的基础语法列表、字典、函数、类在一个具体的场景里用起来理解它们是如何协同工作的。其次你会接触到Pygame这个经典的2D游戏开发库学会如何绘制图形、处理鼠标事件、管理游戏状态循环。最重要的是你会亲手实现一套中国象棋的完整规则逻辑包括棋子的走法、吃子规则、将军和将死的判断。最后你还能窥探一下游戏AI的入门世界了解如何让电脑“思考”并走出一步棋。整个过程下来你对“如何把一个想法变成可运行的代码”会有非常深刻的理解。2. 环境准备与核心工具链解析工欲善其事必先利其器。在开始敲代码之前我们需要把开发环境搭建好。这个项目对Python版本要求不高Python 3.7及以上都可以。我个人的习惯是使用最新的稳定版目前是Python 3.11或3.12它们在性能和包管理上都有所优化。2.1 Python与Pygame安装详解安装Python本身很简单去官网下载对应操作系统的安装包一路“下一步”即可。这里有个关键细节务必在安装时勾选“Add Python to PATH”。这个选项会把Python和它的包管理工具pip添加到系统环境变量让你能在命令行CMD或终端的任何位置直接使用python和pip命令。很多新手卡在第一步就是因为没勾选这个导致后面各种“命令未找到”的错误。安装完Python后我们通过pip来安装Pygame。打开你的命令行工具Windows上是CMD或PowerShellmacOS/Linux上是终端输入以下命令pip install pygame如果网络顺畅这条命令会自动从Python官方的包索引PyPI下载并安装Pygame及其依赖。安装成功后你可以通过python -m pygame.examples.aliens命令运行Pygame自带的一个示例游戏来验证安装是否成功。如果看到一个外星人射击游戏窗口弹出来恭喜你环境搭建成功了。注意如果你遇到安装速度慢或失败的情况很可能是网络问题。可以尝试使用国内的镜像源来加速下载例如清华源pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple。另外确保你的pip版本是最新的可以通过pip install --upgrade pip来升级。2.2 代码编辑器与项目结构规划写代码需要一个顺手的编辑器。对于Python开发VSCode和PyCharm是两大主流选择。VSCode轻量、插件丰富配置起来稍微需要点时间PyCharm是专业的Python IDE开箱即用功能强大但相对重一些。我个人更偏爱VSCode因为它足够灵活。你需要在VSCode里安装Python扩展由Microsoft官方提供它会提供代码高亮、智能提示、调试等功能。在开始写代码前先规划好项目目录结构。一个好的结构能让代码更清晰也便于后期维护。我建议创建如下目录和文件chinese_chess/ ├── assets/ # 存放图片、字体等资源文件 │ ├── images/ # 棋子图片、棋盘图片 │ └── fonts/ # 中文字体文件 ├── src/ # 源代码目录 │ ├── game.py # 游戏主循环和界面绘制 │ ├── board.py # 棋盘逻辑和棋子管理 │ ├── pieces.py # 所有棋子类的定义 │ ├── ai.py # AI对手逻辑 │ └── constants.py # 游戏常量颜色、尺寸、状态等 └── main.py # 程序入口文件这种模块化的设计把不同的功能拆分到不同的文件中。constants.py集中管理所有不会变的参数比如棋盘格数、格子大小、颜色RGB值。这样当你需要调整棋盘大小时只需要修改这一个文件里的一个变量而不是在十几个地方找数字。pieces.py里定义“车”、“马”、“炮”等棋子类每个类负责自己的移动规则判断。board.py管理整个棋盘的状态比如某个位置有什么棋子、当前轮到谁走、是否处于将军状态等。game.py负责用Pygame把棋盘和棋子画出来并处理玩家的鼠标点击事件。ai.py则是电脑玩家的“大脑”。最后main.py作为入口很简单就是初始化游戏并启动主循环。3. 游戏核心逻辑与棋盘实现棋盘是中国象棋的舞台所有故事都发生在这9x10的网格上。实现棋盘逻辑是整个项目最核心、也最考验逻辑严谨性的部分。3.1 棋盘坐标系与状态管理在代码里我们如何表示棋盘最直观的方式是用一个二维列表list of lists。我们可以定义一个Board类其核心属性是一个10行9列的列表grid。列表的每个元素代表棋盘上的一个交叉点它可以存储一个棋子对象如果是空位则存储None。# 在board.py中 class Board: def __init__(self): self.grid [[None for _ in range(9)] for _ in range(10)] # 10行9列 self.current_player red # 红方先行 self.game_over False self.selected_piece None # 当前被选中的棋子 self.init_board() # 初始化棋子位置这里有一个关键点我们定义的grid其索引[row][col]对应棋盘上的位置。通常我们设定左上角为原点(0,0)向右为col增加向下为row增加。这和我们直观的棋盘坐标比如“炮二平五”是相反的需要在绘制和逻辑转换时特别注意。我习惯在constants.py里定义ROWS 10和COLS 9然后在代码里使用这些常量而不是直接写数字9和10这样代码可读性更好以后想改成“象棋变体”也方便。初始化棋子init_board方法就是按照中国象棋的开局布局在grid的相应位置创建棋子对象并放进去。例如红方的“车”在(9,0)和(9,8)棋盘最底部一行黑方的“车”在(0,0)和(0,8)。棋子对象来自pieces.py中定义的Rook、Knight等类。3.2 棋子类设计与移动规则封装棋子的实现是面向对象编程的绝佳练习。我们定义一个基类Piece包含所有棋子共有的属性颜色红/黑、名称、位置row, col。然后为每种棋子创建子类如Rook(车)、Knight(马)、Cannon(炮)等。每个子类必须实现一个核心方法get_valid_moves(board)。这个方法接收当前的棋盘状态board作为参数返回这个棋子在当前位置所有符合规则的可以走到的位置列表。实现移动规则是细节最多的地方必须严谨。以“马”为例它的走法是“日”字形但有“蹩马腿”的限制。在代码里我们需要先计算出“日”字形的8个潜在目标点然后对每一个点检查是否在棋盘内以及是否被己方棋子占据不能吃己方。最关键的一步是检查马从当前位置到目标点的路径上是否“别腿”。规则是马走“日”字时它第一步直走的方向上紧邻的那个点不能有棋子。例如马向上走“日”字即先向上走一格再向左或右走一格那么它正上方紧邻的那个点必须是空的。这个判断逻辑需要精确地根据目标点相对于当前位置的方位来编写。“炮”的规则更特殊移动时和“车”一样必须沿直线且路径上不能有任何棋子但吃子时目标和起点之间必须恰好有一个棋子充当“炮架”。这意味着Cannon类的get_valid_moves方法需要分两步先像“车”一样找出所有直线可达的空位然后再找出所有在直线上、隔着一个棋子可以吃掉的敌方棋子位置。实操心得在实现这些规则时最有效的调试方法是写单元测试。为每个棋子类创建测试用例模拟各种棋盘局面验证get_valid_moves返回的结果是否正确。例如测试“马”在棋盘角落、被别腿、不被别腿等多种情况。虽然前期写测试会多花一点时间但它能极大减少后期集成时难以追踪的Bug。4. Pygame可视化界面与交互实现逻辑层完成后我们需要一个窗口把这些棋子展示出来并让玩家能用鼠标操作。这就是Pygame的用武之地。4.1 游戏窗口初始化与资源加载首先在game.py中我们需要初始化Pygame创建游戏窗口并设置一个主循环。窗口的大小可以根据棋盘格子大小计算出来比如每个格子60像素棋盘就是540像素宽600像素高再加上一些边距。import pygame from constants import SCREEN_WIDTH, SCREEN_HEIGHT, SQUARE_SIZE, MARGIN pygame.init() screen pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption(中国象棋 - Python Pygame) clock pygame.time.Clock()接下来是资源加载。我们需要棋子和棋盘的图片。你可以在网上找到标准的中国象棋矢量图或图片将它们保存为PNG格式背景最好是透明的。通常需要两套棋子图片一套红色一套黑色。在代码里我们使用pygame.image.load()来加载这些图片并用pygame.transform.scale()调整到合适的尺寸使其正好能放入一个棋盘格。字体也很重要因为我们需要在界面上显示“红方走棋”、“将军”等文字信息。Pygame默认字体对中文支持不好所以我们需要指定一个中文字体文件.ttf。将字体文件放在assets/fonts/目录下然后加载font pygame.font.Font(assets/fonts/simhei.ttf, 24)4.2 绘制棋盘与棋子及事件处理绘制过程在主循环的每一帧中进行。基本顺序是先画背景再画棋盘网格和楚河汉界然后遍历棋盘grid在每个有棋子的位置绘制对应的棋子图片最后绘制UI文字如当前玩家、游戏状态。棋盘绘制就是画线。使用pygame.draw.line()或pygame.draw.rect()来绘制10条横线和9条竖线。楚河汉界可以用两条粗线或中间写上文字来表示。为了美观可以给棋盘一个木纹背景图片。绘制棋子的关键是将棋盘坐标(row, col)转换为屏幕上的像素坐标(x, y)。转换公式通常是x MARGIN_LEFT col * SQUARE_SIZE y MARGIN_TOP row * SQUARE_SIZE然后使用screen.blit(piece_image, (x - piece_image.get_width()//2, y - piece_image.get_height()//2))将棋子图片中心对齐到交叉点上。交互的核心是处理Pygame的事件。在pygame.event.get()循环中我们主要关心QUIT事件关闭窗口和MOUSEBUTTONDOWN事件鼠标点击。当玩家点击鼠标时我们获取鼠标位置(pos_x, pos_y)将其反算回棋盘坐标(clicked_row, clicked_col)。然后根据游戏状态决定这个点击意味着什么如果当前没有棋子被选中且点击位置有己方棋子则选中该棋子高亮显示其所有可走位置。如果当前有棋子被选中且点击位置是该棋子的合法走位之一则执行移动或吃子。如果当前有棋子被选中但点击了其他位置则取消选中。移动执行后要更新board.grid中的数据交换行棋方current_player从‘red’变为‘black’或反之并检查移动后是否造成了“将军”或“将死”。注意事项鼠标点击的坐标转换要精确。由于棋子是画在交叉点上的而鼠标点击可能落在交叉点附近的一个小范围内。我们可以定义一个“点击有效区域”比如以交叉点为中心、半径为SQUARE_SIZE//3的圆形区域鼠标落在这个区域内才被认为点击了该交叉点这样操作体验会更友好。5. AI对手逻辑设计与实现让电脑能下棋是这个项目的“点睛之笔”。我们这里实现一个基于规则的、入门级的AI它不涉及复杂的机器学习或深度学习但已经能提供一个像样的对手。5.1 评估函数与局面分析AI下棋的核心是“评估”和“选择”。首先我们需要一个函数来评价当前棋盘局面对谁更有利。这个函数就是评估函数Evaluation Function。一个最简单的评估函数只计算棋子价值每个棋子有一个固定的分值比如将/帅10000车500马350炮350士200象200兵/卒100过河后可以适当增加。然后用己方所有棋子价值总和减去对方所有棋子价值总和得到一个分数。分数为正表示己方优势为负表示劣势。但仅看子力价值太粗糙了。我们可以加入一些简单的局面评价比如棋子位置价值同样一个“马”在河界附近的活跃位置如河口比在原始位置价值更高。我们可以为每种棋子在棋盘的不同位置预定义一张“位置价值表”。兵/卒的过河奖励过河的兵威胁更大可以给其价值乘以一个系数如1.5。将军威胁如果某一步走完后对方处于被将军状态可以给予一个很高的奖励分。这样评估函数就会综合考虑子力、位置和威胁给出一个相对合理的局面分数。5.2 搜索算法与走法生成有了评估函数AI如何选择走哪一步呢最朴素的方法是生成当前玩家所有可能的走法然后模拟走完这一步后的新局面用评估函数计算新局面的分数选择能让自己分数最高或让对方分数最低的那一步走。这就是“一步前瞻”的贪婪算法。但贪婪算法很容易中陷阱因为它看不到对手的回应。更好的方法是进行“多步前瞻”也就是搜索。这里我们可以实现一个极小化极大算法Minimax的简化版。基本思想是AI我们希望最大化自己的分数而对手人类希望最小化AI的分数即最大化他们自己的分数。算法会递归地模拟双方交替走棋几步这个深度称为搜索深度形成一个树状结构然后在树的叶子节点用评估函数打分再将这些分数从叶子节点倒推回根节点AI的回合选最大值对手的回合选最小值最终选出根节点最好的那一步。对于中国象棋在搜索深度为2或3时分支因子每一步的可能走法数就已经非常大了计算会很慢。因此我们需要进行剪枝优化。最常用的是Alpha-Beta剪枝它能在不影响结果的前提下大幅减少需要评估的节点数。其原理是在搜索过程中如果发现某条路径已经不可能比已知的最佳选择更好就停止对该路径的深入搜索。在我们的ai.py中可以定义一个find_best_move(board, depth)函数。它首先调用board.get_all_moves(current_player)获取所有合法走法。然后对每一个走法创建一个棋盘的副本在副本上执行这个走法然后递归调用find_best_move深度减1玩家切换获取对手的最佳回应后的局面分数。最后AI选择能导致最终分数最高的那一步走法。在递归函数中需要实现Alpha-Beta剪枝逻辑来加速。实操心得Alpha-Beta剪枝的效率极度依赖于走法生成的顺序。如果能把“好”的走法比如吃子、将军优先进行评估就能更早地剪掉大量分支。可以在生成所有走法后用一个简单的启发式规则对走法列表进行排序例如吃子走法优先走到中心位置的走法优先等。这个小技巧能显著提升AI的“思考”速度。6. 游戏状态管理与功能集成现在我们已经有了棋盘逻辑、可视化界面和AI大脑。最后一步是把它们全部串联起来形成一个完整的、可玩的游戏。6.1 游戏主循环与模式切换游戏的主循环在game.py中它是一个while running:的无限循环直到玩家关闭窗口。每一帧循环中我们按顺序做以下几件事处理事件检查是否有退出事件、鼠标点击事件等。更新游戏状态根据事件和当前模式双人、AI对战、游戏结束更新棋盘数据、选中状态、当前玩家等。绘制清空屏幕绘制棋盘、棋子、UI文字。刷新显示调用pygame.display.flip()更新窗口。控制帧率clock.tick(60)将循环速度限制在每秒60帧避免占用过多CPU。我们需要支持至少两种游戏模式双人对战模式和人机对战模式。可以通过一个游戏状态变量game_mode来控制。在双人模式下双方轮流用鼠标操作。在人机对战模式下当轮到AI比如黑方走棋时主循环在更新状态阶段不是等待玩家点击而是调用ai.find_best_move()函数计算出AI的最佳走法然后自动执行这步走法并切换回人类玩家的回合。6.2 规则判定与游戏结束处理在每次走棋无论是玩家还是AI走之后都必须进行一系列规则判定这是保证游戏合规性的核心将军检测检查移动方的这步棋是否直接攻击到了对方的将/帅。如果是则要将对方标记为“被将军”状态并在界面上显示“将军”提示。将死判定在被将军的一方需要检查其是否还有任何一步合法的走法能够解除将军状态包括移动将/帅、垫将、吃掉攻击子等。如果所有可能的走法都无法解除将军则判定为“将死”游戏结束走棋方获胜。困毙判定即使没有被将军如果轮到走棋的一方没有任何合法的走法可走所有棋子都被堵死也判负对方获胜。长将禁止在实际象棋规则中不允许单方面长将。我们可以在Board类中记录最近的几步棋如果检测到某方连续多次走出造成将军的棋步而局面没有实质性变化子力未减少可以判长将方违规。这是一个高级规则初期可以不实现但值得了解。当游戏结束时将死、困毙或玩家认输应将game_over标志设为True并在屏幕上清晰显示获胜方信息同时停止接受进一步的走棋输入。7. 代码优化、打包与扩展思路一个能运行的项目是第一步一个健壮、高效、可扩展的项目才是我们的目标。7.1 性能优化与代码重构建议随着AI搜索深度的增加计算量会指数级增长。性能瓶颈主要出现在两个方面走法生成和局面评估。走法生成优化不要在每次评估时都重新生成所有走法。对于Minimax搜索我们是在模拟走棋棋盘状态变化是连续的。可以考虑使用“增量更新”的思路只计算因上一步棋而改变的那些棋子的走法但这实现起来较复杂。一个更简单有效的优化是使用“置换表”Transposition Table它是一个缓存存储已经评估过的棋盘局面及其分数。当再次遇到相同的局面时可能由不同的走棋顺序导致可以直接查表获取分数避免重复计算。实现置换表需要为棋盘生成一个唯一的“哈希键”Zobrist Hashing是专门用于棋类游戏的高效哈希方法。评估函数优化评估函数会被调用成千上万次必须非常高效。避免在评估函数中进行复杂的循环或函数调用。所有棋子价值、位置价值表都应预定义为常量或数组直接查表取值。将评估函数设计为“增量评估”即只计算因一步棋而导致的分数变化而不是每次都全盘重新计算这能极大提升速度。在代码结构上初期为了清晰我们可能把一些逻辑写在了主循环里。随着功能完善应该考虑重构。例如将事件处理、状态更新、绘制渲染分离成独立的函数或类。使用配置文件如JSON来管理棋子价值、位置价值表等参数这样调整AI强度时不需要修改代码。7.2 项目打包与功能扩展方向当项目开发完成后你可能会想分享给不会编程的朋友。这就需要将Python脚本打包成一个独立的可执行文件.exe。最常用的工具是PyInstaller。首先安装它pip install pyinstaller。然后在项目根目录下执行pyinstaller --onefile --windowed --add-data assets;assets main.py--onefile打包成单个exe文件。--windowed运行时不显示控制台窗口对于GUI程序。--add-data assets;assets将assets文件夹内的资源文件一起打包进去Windows用分号;分隔macOS/Linux用冒号:。打包后在dist目录下会生成main.exe你可以直接发送这个文件给别人运行。关于扩展这个项目还有很多可以玩的方向增强AI目前的AI基于规则和搜索。你可以尝试集成更强大的开源中国象棋引擎如“象眼”或“ElephantEye”通过UCI协议与你的Python程序通信让你的游戏拥有大师级的AI对手。网络对战使用socket编程或websocket实现两个玩家通过网络联机对战。这需要设计通信协议来处理走棋、聊天、认输等消息。游戏功能增加悔棋功能需要记录历史局面、保存和加载棋局、显示走棋历史记录棋谱、加入音效和更精美的动画如棋子移动平滑动画、吃子特效等。规则变体实现一些有趣的象棋变体如“暗棋”棋子反面放置随机翻开、“三国象棋”等。这个项目从零开始涵盖了从基础语法到面向对象设计从算法实现到用户交互的完整链条。最难的部分不是某一项具体技术而是如何将所有这些模块清晰、解耦地组织在一起并让它们正确无误地协同工作。我建议你在动手时严格遵循我们规划的项目结构一个模块一个模块地实现和测试。当看到棋子第一次在屏幕上随着你的点击而移动当AI第一次走出一步让你惊讶的棋时那种成就感是无与伦比的。希望这份详细的指南和代码框架能帮你顺利搭建起属于自己的中国象棋游戏。