上一篇我用 Seed Evolving 做了一个金价监控 Agent,这次想换一个轻松一点的方向:做一个 Python 版贪吃蛇小游戏。
因为它不只是“让模型写一段代码”,而是能完整体现 Seed Evolving 在一个小项目里的作用:拆需求、定规则、规划代码结构、补齐游戏循环、处理边界情况,最后生成一个能直接运行的小游戏。

一、我想做什么
我一开始给 Seed Evolving 的需求很简单:
我想用 Python 做一个贪吃蛇小游戏。
要求能控制蛇移动、吃食物加分、撞墙或撞到自己就结束。
代码最好适合新手阅读,能直接运行。
Seed Evolving 没有直接给我一大坨代码,而是先把需求拆成了几个模块:
- 游戏窗口:用 Pygame 创建窗口和主循环。
- 蛇的数据结构:用列表保存蛇头和蛇身坐标。
- 方向控制:监听方向键或 WASD。
- 食物生成:随机生成,不能出现在蛇身上。
- 碰撞检测:撞墙或撞到自己就 Game Over。
- 分数和重开:吃到食物加分,结束后按 R 重新开始。
这一步很有用,因为小游戏最容易写着写着变成“所有逻辑都塞进 while True”。Seed Evolving 先把结构拆出来,后面写代码就顺很多。
二、整体方案
这次的工具结构可以理解成这样:
Pygame 窗口
↓
事件监听:方向键 / WASD / 退出 / 重开
↓
游戏状态更新:蛇移动、吃食物、碰撞检测
↓
画面渲染:网格、蛇、食物、分数、结束提示
Seed Evolving 在这里主要做了两类事。
第一类是开发指导:
- 建议用 Pygame 做 2D 小游戏。
- 建议用“格子坐标”管理蛇和食物,而不是直接用像素坐标。
- 建议把随机食物、绘制方块、重置游戏拆成函数。
- 提醒我处理“反向移动”这个经典 bug。
第二类是代码生成:
- 生成基础游戏循环。
- 生成键盘控制逻辑。
- 生成蛇移动和吃食物逻辑。
- 生成撞墙、撞身体、重开游戏逻辑。

三、准备环境
这次只需要 Python 和 Pygame。
pip install pygame
运行方式:
python seed_evolving_snake.py
如果是在 VS Code 里,直接打开文件运行也可以。
四、核心实现思路
1. 用格子坐标管理游戏
Seed Evolving 建议不要直接用像素坐标写蛇的位置,而是先定义“每个格子多大”,再让蛇在格子里移动。
CELL_SIZE = 24
COLS = 24
ROWS = 20
WIDTH = CELL_SIZE * COLS
HEIGHT = CELL_SIZE * ROWS
这样蛇头 (10, 8) 表示第 10 列、第 8 行,而不是屏幕上的具体像素点。绘制时再乘以 CELL_SIZE 就行。
2. 用列表保存蛇身
蛇的身体本质上就是一串坐标。列表第一个元素是蛇头,后面的元素是蛇身。
snake = [
(COLS // 2, ROWS // 2),
(COLS // 2 - 1, ROWS // 2),
(COLS // 2 - 2, ROWS // 2),
]
direction = (1, 0)
每次移动时,把新的蛇头插到列表最前面;如果没有吃到食物,就删掉最后一节身体。
3. 随机生成食物
食物不能出现在蛇身上,所以生成后要检查一下。
def random_food(snake):
while True:
pos = (random.randint(0, COLS - 1), random.randint(0, ROWS - 1))
if pos not in snake:
return pos
这段是 Seed Evolving 特别提醒我的地方:如果不判断 pos not in snake,食物可能直接刷在蛇身下面,看起来就像“凭空消失”。
4. 处理方向控制
方向控制除了监听按键,还要避免蛇直接反向。例如蛇正在向右走时,不能马上向左,否则会直接撞到自己。
if event.key in (pygame.K_UP, pygame.K_w) and direction != (0, 1):
direction = (0, -1)
elif event.key in (pygame.K_DOWN, pygame.K_s) and direction != (0, -1):
direction = (0, 1)
elif event.key in (pygame.K_LEFT, pygame.K_a) and direction != (1, 0):
direction = (-1, 0)
elif event.key in (pygame.K_RIGHT, pygame.K_d) and direction != (-1, 0):
direction = (1, 0)
这个小判断看起来不起眼,但它是贪吃蛇里非常典型的边界问题。
5. 移动、吃食物和碰撞
游戏每一帧都会计算新的蛇头位置,然后判断是否撞墙、撞自己、吃到食物。
head_x, head_y = snake[0]
dx, dy = direction
new_head = (head_x + dx, head_y + dy)
hit_wall = new_head[0] < 0 or new_head[0] >= COLS or new_head[1] < 0 or new_head[1] >= ROWS
hit_self = new_head in snake
if hit_wall or hit_self:
game_over = True
else:
snake.insert(0, new_head)
if new_head == food:
score += 1
food = random_food(snake)
else:
snake.pop()
Seed Evolving 给我的建议是:先把“死亡判断”放在前面,只有没死才更新蛇身。这样逻辑更清楚。
五、实测效果
运行后会出现一个深色背景的贪吃蛇窗口:
- 方向键或 WASD 控制移动。
- 吃到红色食物后加分。
- 撞墙或撞到自己后显示 Game Over。
- 游戏结束后按 R 可以重新开始。


六、Seed Evolving 具体帮了什么
这次我感觉 Seed Evolving 对小游戏开发的帮助主要在 4 个地方。
1. 把需求拆成可实现模块
一开始只是“做个贪吃蛇”,但 Seed Evolving 把它拆成窗口、蛇、食物、方向、碰撞、分数、重开几个模块。这个拆解让代码不会乱。
2. 提醒我处理经典边界
比如:
- 食物不能生成在蛇身上。
- 不能直接反向移动。
- 撞墙和撞自己都要 Game Over。
- 游戏结束后要能重开。
这些都是小游戏里很容易漏掉的小细节。
3. 让代码适合新手阅读
我让 Seed Evolving 不要把代码写得太炫技,而是拆成几个函数:
random_food()
draw_grid()
draw_block()
reset_game()
main()
这样每个函数只做一件事,后面想改速度、颜色、窗口大小都比较方便。
4. 帮我保留后续扩展空间
这个版本是基础版,但后续可以继续扩展:
- 加开始菜单。
- 加难度选择。
- 加最高分记录。
- 加音效。
- 加障碍物模式。
- 加暂停功能。
这也是我喜欢用 Seed Evolving 做小项目的原因:它不只是补全代码,还会帮你把下一步能怎么迭代想清楚。
七、踩坑记录
1. Pygame 坐标和格子坐标不要混用
一开始最容易混的是像素坐标和格子坐标。后来我按 Seed Evolving 的建议统一用格子坐标保存状态,绘制时再转换成像素,代码就清楚很多。
2. 反向移动一定要限制
如果不限制反向移动,蛇向右走时按左键会直接撞到自己,游戏体验很怪。
3. 食物不能刷在蛇身上
这个问题肉眼不一定马上发现,但玩几轮就会遇到。所以 random_food() 里必须判断食物位置不在蛇身列表里。
4. Game Over 后不要继续更新蛇身
游戏结束后只负责等待退出或重开,不继续移动蛇,否则画面状态会越来越乱。
总结
这次用 Seed Evolving 做贪吃蛇,最大的感受是:它很适合帮我把一个小想法变成一个完整项目。
它不是只生成一段代码,而是参与了整个过程:
- 拆需求。
- 设计数据结构。
- 提醒边界情况。
- 生成核心代码。
- 保留后续扩展方向。
贪吃蛇本身不复杂,但很适合练习“如何把一个小游戏做完整”。从这个角度看,Seed Evolving 像是一个会陪你把小项目一点点搭起来的编程助手。
下一期如果继续做小游戏,我想试试用 Seed Evolving 做一个带关卡和道具的小游戏。
附:完整代码
import random
import sys
import pygame
CELL_SIZE = 24
COLS = 24
ROWS = 20
WIDTH = CELL_SIZE * COLS
HEIGHT = CELL_SIZE * ROWS
FPS = 10
BG_COLOR = (18, 24, 38)
GRID_COLOR = (30, 39, 58)
SNAKE_COLOR = (68, 199, 123)
SNAKE_HEAD_COLOR = (110, 231, 183)
FOOD_COLOR = (248, 113, 113)
TEXT_COLOR = (226, 232, 240)
def random_food(snake):
while True:
pos = (random.randint(0, COLS - 1), random.randint(0, ROWS - 1))
if pos not in snake:
return pos
def draw_grid(screen):
for x in range(0, WIDTH, CELL_SIZE):
pygame.draw.line(screen, GRID_COLOR, (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, CELL_SIZE):
pygame.draw.line(screen, GRID_COLOR, (0, y), (WIDTH, y))
def draw_block(screen, pos, color):
x, y = pos
rect = pygame.Rect(x * CELL_SIZE + 2, y * CELL_SIZE + 2, CELL_SIZE - 4, CELL_SIZE - 4)
pygame.draw.rect(screen, color, rect, border_radius=6)
def draw_text(screen, font, text, x, y):
image = font.render(text, True, TEXT_COLOR)
screen.blit(image, (x, y))
def reset_game():
snake = [(COLS // 2, ROWS // 2), (COLS // 2 - 1, ROWS // 2), (COLS // 2 - 2, ROWS // 2)]
direction = (1, 0)
food = random_food(snake)
score = 0
game_over = False
return snake, direction, food, score, game_over
def main():
pygame.init()
pygame.display.set_caption("Seed Evolving Snake")
screen = pygame.display.set_mode((WIDTH, HEIGHT + 56))
clock = pygame.time.Clock()
font = pygame.font.SysFont("Microsoft YaHei", 22)
small_font = pygame.font.SysFont("Microsoft YaHei", 18)
snake, direction, food, score, game_over = reset_game()
while True:
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_r and game_over:
snake, direction, food, score, game_over = reset_game()
elif event.key in (pygame.K_UP, pygame.K_w) and direction != (0, 1):
direction = (0, -1)
elif event.key in (pygame.K_DOWN, pygame.K_s) and direction != (0, -1):
direction = (0, 1)
elif event.key in (pygame.K_LEFT, pygame.K_a) and direction != (1, 0):
direction = (-1, 0)
elif event.key in (pygame.K_RIGHT, pygame.K_d) and direction != (-1, 0):
direction = (1, 0)
if not game_over:
head_x, head_y = snake[0]
dx, dy = direction
new_head = (head_x + dx, head_y + dy)
hit_wall = new_head[0] < 0 or new_head[0] >= COLS or new_head[1] < 0 or new_head[1] >= ROWS
hit_self = new_head in snake
if hit_wall or hit_self:
game_over = True
else:
snake.insert(0, new_head)
if new_head == food:
score += 1
food = random_food(snake)
else:
snake.pop()
screen.fill(BG_COLOR)
draw_grid(screen)
draw_block(screen, food, FOOD_COLOR)
for index, part in enumerate(snake):
draw_block(screen, part, SNAKE_HEAD_COLOR if index == 0 else SNAKE_COLOR)
pygame.draw.rect(screen, (15, 23, 42), (0, HEIGHT, WIDTH, 56))
draw_text(screen, font, f"Score: {score}", 16, HEIGHT + 14)
draw_text(screen, small_font, "Arrow/WASD move | R restart", WIDTH - 250, HEIGHT + 18)
if game_over:
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 150))
screen.blit(overlay, (0, 0))
draw_text(screen, font, "Game Over", WIDTH // 2 - 58, HEIGHT // 2 - 28)
draw_text(screen, small_font, "Press R to restart", WIDTH // 2 - 72, HEIGHT // 2 + 8)
pygame.display.flip()
clock.tick(FPS)
if __name__ == "__main__":
main()
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_44273429/article/details/163474985



