十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用 aider 以对话方式从零构建 Pygame Pong:SEARCH/REPLACE 编辑格式与迭代式游戏开发的完整实战

用 aider 以对话方式从零构建 Pygame Pong:SEARCH/REPLACE 编辑格式与迭代式游戏开发的完整实战 用 aider 以对话方式从零构建 Pygame PongSEARCH/REPLACE 编辑格式与迭代式游戏开发的完整实战【免费下载链接】Qwen3-CoderQwen3-Coder is the code version of Qwen3, the large language model series developed by Qwen team.项目地址: https://gitcode.com/GitHub_Trending/co/Qwen3-Coder在本文中我们基于 pong.md 这份真实聊天记录完整复现用自然语言驱动 aider 从零开发一个 Pygame Pong 游戏的全过程从创建pong_game.py基础骨架、加入 Paddle/Ball 类与游戏循环到逐步调整球速、拍子尺寸与颜色甚至实现每次反弹加速 10%的游戏机制。通过逐条拆解聊天中的SEARCH/REPLACE编辑块edit block并结合仓库内 editblock_coder.py 的解析与应用实现读者将掌握 aider 的核心编辑协议、自动 git 提交流程以及如何用纯对话完成从原型到可玩游戏的迭代开发。聊天记录背景aider 是怎么看懂编辑请求的在进入 Pong 案例之前先理解这份 transcript 的运行机制。根据 examples/README.md 的说明这些聊天记录展示了 aider 的实际工作方式每当 LLM 给出一个代码修改建议aider 会自动将其应用到源文件应用完成后aider 会使用描述性 commit message 自动提交到 gitLLM 只能看到并编辑已加入聊天会话的文件用户通过命令行参数或聊天内的/add命令添加文件当 LLM 主动要求查看某个文件时aider 会先征得用户许可transcript 中能看到文件被加入或移出会话的通知。transcript 的排版约定也很明确前缀是 aider 工具自身的输出####是用户输入的聊天消息蓝色字体的普通文本是 LLM 的回复其中常包含彩色标注的 edit block 来指明代码改动。edit block 的格式非常关键它在 editblock_prompts.py 的 system reminder 中有严格定义基本骨架如下文件路径独占一行 SEARCH 现有文件中的一段连续代码必须逐字符精确匹配 要替换进去的新代码 REPLACE关于这一格式源码给出的规则包括SEARCH部分必须与现有文件内容逐字符精确匹配包括所有注释、docstring、缩进SEARCH/REPLACE 块会替换所有匹配出现的位置块要保持简洁大改动应拆成一系列小块创建新文件时使用空 SEARCH 段加完整 REPLACE 段——这正是 Pong 案例中第一个 edit block 的做法。第一步从零创建 pong_game.py 基础骨架用户的第一条指令是can you make a simple game of pong with pygame?aider 先给出了开发步骤拆解安装 Pygame、初始化窗口、创建 Paddle 与 Ball 类、初始化对象、游戏循环、输入处理、碰撞与计分、绘制、记分、显示分数然后通过一个新文件类型的 edit block 创建了pong_game.py——注意 SEARCH 段为空这对应源码中新文件的约定pong_game.py ORIGINAL import pygame import sys # Initialize Pygame pygame.init() # Set up the game window screen_width 640 screen_height 480 screen pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption(Pong) # Game loop running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False # Update game objects # Draw game objects # Update the display pygame.display.flip() # Quit Pygame pygame.quit() sys.exit() UPDATED注transcript 原文中此处的分隔符写作ORIGINAL/UPDATED与规范中的SEARCH/REPLACE等价源码 editblock_coder.py 中定义了HEAD SEARCH、DIVIDER 、UPDATED REPLACE三个标记。随后 transcript 记录了 aider 的关键交互流程Allow creation of new file pong_game.py? y Add pong_game.py to git? y Applied edit to pong_game.py Commit 14799cb aider: Added basic Pygame initialization and game loop structure topong_game.py.这展示了一个重要事实创建新文件与加入 git 都需要用户显式确认输入y这与 README 中LLM 只能编辑已加入会话的文件的原则一致——aider 在此处请求了用户许可后才真正落盘并提交。第二步加入 Paddle / Ball 类与完整游戏循环用户说 go aheadaider 在同一个文件中继续追加代码。首先在初始化段之后插入类定义与random导入pong_game.py ORIGINAL import pygame import sys # Initialize Pygame pygame.init() # Set up the game window screen_width 640 screen_height 480 screen pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption(Pong) import pygame import sys import random # Initialize Pygame pygame.init() # Set up the game window screen_width 640 screen_height 480 screen pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption(Pong) # Paddle and Ball classes class Paddle(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.width 10 self.height 60 self.image pygame.Surface((self.width, self.height)) self.image.fill((255, 255, 255)) self.rect self.image.get_rect() self.rect.x x self.rect.y y def move(self, dy): if self.rect.y dy 0 and self.rect.y dy screen_height - self.height: self.rect.y dy class Ball(pygame.sprite.Sprite): def __init__(self): super().__init__() self.width 10 self.height 10 self.image pygame.Surface((self.width, self.height)) self.image.fill((255, 255, 255)) self.rect self.image.get_rect() self.rect.x screen_width // 2 - self.width // 2 self.rect.y screen_height // 2 - self.height // 2 self.dx random.choice([-1, 1]) * 4 self.dy random.choice([-1, 1]) * 4 def update(self): self.rect.x self.dx self.rect.y self.dy # Collision with top and bottom if self.rect.y 0 or self.rect.y screen_height - self.height: self.dy -self.dy # Scoring if self.rect.x 0 or self.rect.x screen_width - self.width: self.reset() def reset(self): self.rect.x screen_width // 2 - self.width // 2 self.rect.y screen_height // 2 - self.height // 2 self.dx random.choice([-1, 1]) * 4 self.dy random.choice([-1, 1]) * 4 def collide_with(self, paddle): if self.rect.colliderect(paddle.rect): self.dx -self.dx UPDATED注意这里只替换了文件的开头部分文件头与窗口初始化文件后半部分的游戏循环保持不动——这正是 SEARCH/REPLACE 机制的典型用法SEARCH 段精确锁定一小段上下文REPLACE 段将其替换为更长的内容。接着第二个 edit block 替换游戏循环占位符加入对象初始化、键盘输入W/S 控制左拍方向键控制右拍、球体更新与碰撞、绘制逻辑以及 16ms 帧延迟pong_game.py ORIGINAL # Game loop running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False # Update game objects # Draw game objects # Update the display pygame.display.flip() # Initialize paddles and ball left_paddle Paddle(10, screen_height // 2 - 30) right_paddle Paddle(screen_width - 20, screen_height // 2 - 30) ball Ball() # Game loop running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False # Handle user input keys pygame.key.get_pressed() if keys[pygame.K_w]: left_paddle.move(-5) if keys[pygame.K_s]: left_paddle.move(5) if keys[pygame.K_UP]: right_paddle.move(-5) if keys[pygame.K_DOWN]: right_paddle.move(5) # Update game objects ball.update() ball.collide_with(left_paddle) ball.collide_with(right_paddle) # Draw game objects screen.fill((0, 0, 0)) screen.blit(left_paddle.image, left_paddle.rect) screen.blit(right_paddle.image, right_paddle.rect) screen.blit(ball.image, ball.rect) # Update the display pygame.display.flip() pygame.time.delay(16) UPDATED两个 edit block 全部应用成功后aider 再次提交Applied edit to pong_game.py Applied edit to pong_game.py Commit b20bcee aider: Added Paddle and Ball classes, handled user input, updated ball position, drew objects, and handled scoring.到这一步一个可玩的基础 Pong 游戏已经诞生。从实现上看Ball.update()负责位移与上下边界反弹collide_with()用pygame.Rect.colliderect做 AABB 碰撞检测并反向水平速度Paddle.move()则通过边界检查把拍子限制在窗口内——这些细节全部由 LLM 一次生成用户无需手写任何代码。第三步微调游戏手感——降低球速用户反馈 the ball moves way too fastaider 通过一个极小的 edit block 把球的初速度从 4 降到 2pong_game.py ORIGINAL self.dx random.choice([-1, 1]) * 4 self.dy random.choice([-1, 1]) * 4 self.dx random.choice([-1, 1]) * 2 self.dy random.choice([-1, 1]) * 2 UPDATEDApplied edit to pong_game.py Commit f4ceed4 aider: Reduced ball speed by half.这个改动同时影响Ball.__init__与Ball.reset两处代码相同SEARCH/REPLACE 块会替换所有匹配出现的位置——这正是 editblock_prompts.py 中will replaceallmatching occurrences规则的实际体现。aider 也在回复中提示用户可自行微调数值以获得更慢的效果体现了它尊重用户控制权的设计。第四步视觉定制——加大拍子并改成红蓝配色用户提出 make the paddles a bit bigger? and make one paddle red and one blue?aider 分两次改动完成先修改Paddle类尺寸从 10x60 增大到 15x80并依据拍子所在半区x 坐标是否小于窗口中线设置默认颜色——右半区默认蓝色pong_game.py ORIGINAL self.width 10 self.height 60 self.image pygame.Surface((self.width, self.height)) self.image.fill((255, 255, 255)) self.width 15 self.height 80 self.image pygame.Surface((self.width, self.height)) self.color (255, 255, 255) if x screen_width // 2 else (0, 0, 255) self.image.fill(self.color) UPDATED再单独把左拍设为红色同时微调其初始 y 坐标以适配新尺寸pong_game.py ORIGINAL left_paddle Paddle(10, screen_height // 2 - 30) left_paddle Paddle(10, screen_height // 2 - 40) left_paddle.color (255, 0, 0) left_paddle.image.fill(left_paddle.color) UPDATEDApplied edit to pong_game.py Applied edit to pong_game.py Commit 2c645c9 aider: Changed paddle size and color, and set left paddle to red.这里有两个值得注意的细节一是对Paddle类的改动会自动作用于左右两个拍子因为它们共用同一个类二是 aider 用了两个独立的 edit block 来分别处理类定义与实例初始化两个位置验证了 prompts 中将大改动拆分为一系列小块的建议。第五步游戏机制增强——每次反弹加速 10%最后一个需求颇具游戏设计意味each time the ball bounces off the paddle, can you make it go 10% faster?aider 修改Ball.collide_with()把原本的纯反向改为反向并放大 1.1 倍pong_game.py ORIGINAL def collide_with(self, paddle): if self.rect.colliderect(paddle.rect): self.dx -self.dx def collide_with(self, paddle): if self.rect.colliderect(paddle.rect): self.dx -self.dx * 1.1 self.dy * 1.1 UPDATEDApplied edit to pong_game.py Commit 693faf4 aider: Increased ball speed by 10% in both x and y directions each time it bounces off a paddle.这一改动使水平方向在反转的同时放大垂直方向直接放大球体随拍击次数逐次加速为游戏引入了逐步上升的难度曲线——全部通过一句自然语言需求完成无需用户自己定位方法或推导数学表达式。源码解析edit block 是如何被解析与应用的看完整个案例我们来追一遍 edit block 在源码中的生命周期这能帮助读者理解为什么 SEARCH 必须精确匹配以及匹配失败时会发生什么。解析阶段。在 editblock_coder.py 的find_original_update_blocks()中回复文本按行扫描遇到 SEARCH标记后收集 SEARCH 段直至再收集 REPLACE 段直至 REPLACE并以(文件名, SEARCH 文本, REPLACE 文本)三元组产出。文件名通过find_filename()从 edit block 上方最多 3 行中回溯查找支持精确匹配、基于Path的 basename 匹配、以及difflib.get_close_matches的模糊匹配cutoff0.8。应用阶段。apply_edits()遍历解析出的 edit block对每个 block 调用do_replace()如果目标文件不存在且 SEARCH 为空则创建新文件对应案例第一步否则调用replace_most_similar_chunk()在文件中查找 SEARCH 文本并替换。查找按完美匹配 → 容忍统一缩进偏差 → 容忍首个空行 → 支持...省略号对应try_dotdotdots()的优先级逐级降级容错。如果 SEARCH 段在文件中找不到匹配aider 会返回详细的错误报告指出该 block 匹配失败并用find_similar_lines()基于SequenceMatcher相似度阈值 0.6给出你是否想匹配这些行的提示。测试验证。仓库中的 test_editblock.py 对这一整套协议做了系统测试例如test_find_original_update_blocks验证从带围栏文本中正确提取(foo.txt, Two\n, Tooooo\n)test_find_filename验证文件名可从# file1.py、file1_py等变体中正确解析test_find_original_update_blocks_missing_filename与test_find_original_update_blocks_unclosed则分别验证缺少文件名、缺少 REPLACE结束标记时抛出ValueError。这些测试从侧面印证了案例中每个 edit block 之所以能被Applied edit成功是因为 SEARCH 段与文件内容的精确匹配得到了严格保证。迭代成果最终的 pong_game.py将五个 edit block 依次应用到初始骨架后最终可运行的游戏代码如下累计了全部定制球速 2、拍子 15x80、左红右蓝、反弹加速 10%import pygame import sys import random # Initialize Pygame pygame.init() # Set up the game window screen_width 640 screen_height 480 screen pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption(Pong) # Paddle and Ball classes class Paddle(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.width 15 self.height 80 self.image pygame.Surface((self.width, self.height)) self.color (255, 255, 255) if x screen_width // 2 else (0, 0, 255) self.image.fill(self.color) self.rect self.image.get_rect() self.rect.x x self.rect.y y def move(self, dy): if self.rect.y dy 0 and self.rect.y dy screen_height - self.height: self.rect.y dy class Ball(pygame.sprite.Sprite): def __init__(self): super().__init__() self.width 10 self.height 10 self.image pygame.Surface((self.width, self.height)) self.image.fill((255, 255, 255)) self.rect self.image.get_rect() self.rect.x screen_width // 2 - self.width // 2 self.rect.y screen_height // 2 - self.height // 2 self.dx random.choice([-1, 1]) * 2 self.dy random.choice([-1, 1]) * 2 def update(self): self.rect.x self.dx self.rect.y self.dy # Collision with top and bottom if self.rect.y 0 or self.rect.y screen_height - self.height: self.dy -self.dy # Scoring if self.rect.x 0 or self.rect.x screen_width - self.width: self.reset() def reset(self): self.rect.x screen_width // 2 - self.width // 2 self.rect.y screen_height // 2 - self.height // 2 self.dx random.choice([-1, 1]) * 2 self.dy random.choice([-1, 1]) * 2 def collide_with(self, paddle): if self.rect.colliderect(paddle.rect): self.dx -self.dx * 1.1 self.dy * 1.1 # Initialize paddles and ball left_paddle Paddle(10, screen_height // 2 - 40) left_paddle.color (255, 0, 0) left_paddle.image.fill(left_paddle.color) right_paddle Paddle(screen_width - 20, screen_height // 2 - 30) ball Ball() # Game loop running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False # Handle user input keys pygame.key.get_pressed() if keys[pygame.K_w]: left_paddle.move(-5) if keys[pygame.K_s]: left_paddle.move(5) if keys[pygame.K_UP]: right_paddle.move(-5) if keys[pygame.K_DOWN]: right_paddle.move(5) # Update game objects ball.update() ball.collide_with(left_paddle) ball.collide_with(right_paddle) # Draw game objects screen.fill((0, 0, 0)) screen.blit(left_paddle.image, left_paddle.rect) screen.blit(right_paddle.image, right_paddle.rect) screen.blit(ball.image, ball.rect) # Update the display pygame.display.flip() pygame.time.delay(16) # Quit Pygame pygame.quit() sys.exit()运行方式为pip install pygame python pong_game.py案例总结这套工作流给开发者带来的启示回看整段对话从第一条需求到最终可玩的定制版 Pong共经历 5 轮交互、6 次文件编辑、5 次自动 git 提交14799cb、b20bcee、f4ceed4、2c645c9、693faf4每一轮都遵循用户提需求 → LLM 给出 SEARCH/REPLACE edit block → aider 精确应用 → 自动提交的闭环。这个案例至少说明三点edit block 是 aider 与模型之间的稳定协议精确匹配的 SEARCH 段保证了编辑的可预期性即使匹配失败aider 也会给出清晰的诊断与相似行提示见 editblock_coder.py 的错误处理逻辑粒度化提交天然形成开发历史每个需求对应一次语义清晰的 commit方便回滚与回溯这得益于 base_coder.py 中对 git 操作的内建支持自然语言可以承载设计意图而不只是代码操作从球太快到每次反弹加速 10%用户描述的是体验与目标模型负责将其翻译为具体数值与逻辑改动。如果你想继续深入了解 edit block 的完整语法规范、更多示例 transcript如 Flask 应用、多文件复杂改动、测试用例生成等可以继续阅读 examples/README.md 中列出的其他对话记录或直接查看 editblock_prompts.py 中写给模型的完整规则。【免费下载链接】Qwen3-CoderQwen3-Coder is the code version of Qwen3, the large language model series developed by Qwen team.项目地址: https://gitcode.com/GitHub_Trending/co/Qwen3-Coder创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表