news 2026/9/22 8:00:50

猫鼠游戏从零搭建:3步跑通完整示例,告别只会抄代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
猫鼠游戏从零搭建:3步跑通完整示例,告别只会抄代码

猫鼠游戏从零搭建:3步跑通完整示例,告别只会抄代码

是不是觉得看了一堆教程还是不会写项目?别急,很多人卡在“看懂了但手不动”的尴尬期。今天这篇猫鼠游戏完整示例,直接带你从0到1跑通,不讲虚的,只给能跑的代码和踩坑记录。

1. 项目目标与核心逻辑

我们不做那种花里胡哨的图形界面,先搞个命令行版猫鼠游戏。目标很明确:

  1. 地图生成:随机生成一个有墙壁的迷宫。
  2. 角色控制:玩家控制老鼠(用键盘或输入指令),电脑控制猫(AI寻路)。
  3. 胜负判定:老鼠吃到所有奶酪算赢,被猫抓到算输。

核心难点:猫的寻路算法。如果猫只是乱走,游戏没意思;如果猫太聪明,玩家赢不了。我们要用 BFS(广度优先搜索) 让猫走最短路径追老鼠,但给猫加个“反应延迟”,模拟人类反应,这样才有“猫鼠游戏”的博弈感。

为什么选这个? 因为它涵盖了数据结构(队列)、算法(BFS)、状态机(游戏循环)和IO处理。比“Hello World”有深度,比大型框架易上手。

2. 目录结构规划

工程化思维很重要,别把所有代码堆在 main.py 里。建议结构如下:

cat_mouse_game/
├── main.py          # 入口文件,初始化游戏
├── game_logic.py    # 核心逻辑:地图生成、碰撞检测
├── ai_agent.py      # 猫的AI行为:BFS寻路
├── player.py        # 玩家控制:输入处理
├── config.py        # 配置:地图大小、速度等
└── utils.py         # 工具函数:地图渲染、消息打印

为什么这么分? 以后想换算法(比如A*)只改 ai_agent.py,想改地图规则只改 game_logic.py。解耦是代码可维护性的基石。

3. 核心代码实现

3.1 地图生成与初始化 (game_logic.py)

地图用二维列表表示,0 是空地,1 是墙,2 是奶酪。

import randomdef generate_map(width=10, height=10, wall_density=0.2, cheese_count=3):"""生成随机地图:param width: 地图宽度:param height: 地图高度:param wall_density: 墙壁密度,0-1之间:param cheese_count: 奶酪数量:return: 二维列表表示的地图"""# 初始化全为空地map_grid = [[0 for _ in range(width)] for _ in range(height)]# 随机生成墙壁,避免生成在边缘,确保地图连通性(简化处理,实际需更复杂算法)for i in range(1, height - 1):for j in range(1, width - 1):if random.random() < wall_density:map_grid[i][j] = 1# 随机放置奶酪cheese_positions = []while len(cheese_positions) < cheese_count:x = random.randint(1, height - 2)y = random.randint(1, width - 2)if map_grid[x][y] == 0 and [x, y] not in cheese_positions:map_grid[x][y] = 2cheese_positions.append([x, y])return map_grid

逐行解析

  • random.random() < wall_density:概率生成墙,wall_density 越小地图越开阔。
  • 奶酪放置时检查 map_grid[x][y] == 0,避免奶酪生成在墙上。

3.2 猫的AI:BFS寻路 (ai_agent.py)

这是灵魂部分。猫要找到离老鼠最近的路径。

from collections import dequeclass Cat:def __init__(self, x, y):self.x = xself.y = ydef bfs_next_move(self, map_grid, mouse_x, mouse_y):"""使用BFS寻找猫移动到下一个位置的最短路径:param map_grid: 当前地图:param mouse_x: 老鼠x坐标:param mouse_y: 老鼠y坐标:return: 下一个移动的 (x, y) 坐标"""height = len(map_grid)width = len(map_grid[0])# BFS初始化queue = deque()queue.append((self.x, self.y))visited = [[False] * width for _ in range(height)]visited[self.x][self.y] = True# 方向:上、下、左、右directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]# 记录路径,以便回溯parent = {}parent[(self.x, self.y)] = Nonewhile queue:curr_x, curr_y = queue.popleft()# 如果当前格子是老鼠位置,找到路径if curr_x == mouse_x and curr_y == mouse_y:# 回溯找到第一步step = (curr_x, curr_y)while parent[step] is not None:step = parent[step]return stepfor dx, dy in directions:nx, ny = curr_x + dx, curr_y + dy# 边界检查和墙壁检查if 0 <= nx < height and 0 <= ny < width and map_grid[nx][ny] != 1:if not visited[nx][ny]:visited[nx][ny] = Trueparent[(nx, ny)] = (curr_x, curr_y)queue.append((nx, ny))# 如果没找到路径(老鼠被围堵),随机移动return self.random_move(map_grid)def random_move(self, map_grid):"""随机移动一步,避免死锁"""directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]valid_moves = []for dx, dy in directions:nx, ny = self.x + dx, self.y + dyif 0 <= nx < len(map_grid) and 0 <= ny < len(map_grid[0]) and map_grid[nx][ny] != 1:valid_moves.append((nx, ny))if valid_moves:next_x, next_y = random.choice(valid_moves)self.x, self.y = next_x, next_y# 如果没得走,原地不动

关键点

  • deque 用于BFS,比 list 效率高,因为 list.pop(0) 是 O(n),deque.popleft() 是 O(1)。
  • parent 字典记录路径,BFS本身只给距离,回溯才能拿到具体走法。
  • 避坑:一定要加 random_move 兜底。如果地图生成时老鼠被墙完全围死,BFS会跑不完,游戏卡死。

3.3 游戏主循环 (main.py)

把逻辑串起来。

import os
import time
from game_logic import generate_map
from ai_agent import Cat
from player import Mousedef clear_screen():os.system('cls' if os.name == 'nt' else 'clear')def print_map(map_grid, cat_pos, mouse_pos, cheese_left):"""渲染地图到终端"""height = len(map_grid)width = len(map_grid[0])print("=" * (width * 2 + 1))for i in range(height):row = ""for j in range(width):if map_grid[i][j] == 1:row += "██ "elif (i, j) == cat_pos:row += "🐱 "elif (i, j) == mouse_pos:row += "🐭 "elif map_grid[i][j] == 2:row += "🧀 "else:row += "  "print(row)print("=" * (width * 2 + 1))print(f"奶酪剩余: {cheese_left} | 猫的位置: {cat_pos} | 鼠的位置: {mouse_pos}")def main():# 初始化map_size = 15map_grid = generate_map(width=map_size, height=map_size, wall_density=0.25, cheese_count=5)# 随机初始位置(确保不在墙上)def get_random_empty_pos():while True:x = random.randint(0, map_size - 1)y = random.randint(0, map_size - 1)if map_grid[x][y] == 0:return x, ymouse = Mouse(*get_random_empty_pos())cat = Cat(*get_random_empty_pos())cheese_left = 5game_over = Falseprint("游戏开始!输入方向: w(上) a(左) s(下) d(右), q(退出)")while not game_over:clear_screen()print_map(map_grid, (cat.x, cat.y), (mouse.x, mouse.y), cheese_left)# 1. 玩家输入move = input("你的移动: ").lower()if move == 'q':break# 2. 老鼠移动if not mouse.move(move, map_grid):print("撞墙了!原地不动。")# 3. 检查奶酪if map_grid[mouse.x][mouse.y] == 2:map_grid[mouse.x][mouse.y] = 0cheese_left -= 1print("吃到奶酪!")# 4. 检查胜负if cheese_left == 0:print("🎉 你赢了!")breakif (cat.x, cat.y) == (mouse.x, mouse.y):print("💀 被猫抓到了!游戏结束。")break# 5. 猫移动 (AI)time.sleep(0.5) # 模拟反应延迟next_pos = cat.bfs_next_move(map_grid, mouse.x, mouse.y)if next_pos:cat.x, cat.y = next_pos# 6. 再次检查碰撞(猫移动后可能撞上)if (cat.x, cat.y) == (mouse.x, mouse.y):print("💀 被猫抓到了!游戏结束。")breakif __name__ == "__main__":main()

player.py 补充

class Mouse:def __init__(self, x, y):self.x = xself.y = ydef move(self, direction, map_grid):dx, dy = 0, 0if direction == 'w': dx = -1elif direction == 's': dx = 1elif direction == 'a': dy = -1elif direction == 'd': dy = 1else: return Falsenx, ny = self.x + dx, self.y + dyif 0 <= nx < len(map_grid) and 0 <= ny < len(map_grid[0]) and map_grid[nx][ny] != 1:self.x, self.y = nx, nyreturn Truereturn False

4. 运行与测试

4.1 环境准备

不需要复杂环境,Python 3.7+ 即可。所有代码只用标准库,零依赖

pip install -r requirements.txt # 其实这里没东西,纯标准库
python main.py

4.2 测试场景

  1. 正常游玩:地图生成后,观察猫是否总是朝老鼠方向移动。
  2. 边界测试:把 wall_density 调到 0.8,地图很堵。看猫是否会卡在角落(random_move 是否生效)。
  3. 性能测试:把地图改到 50x50。BFS 在 2500 格内搜索,deque 能保证毫秒级响应。如果用 list 做队列,可能会卡。

Stack Overflow 经验: 很多初学者用 list 做 BFS 队列,当地图变大时程序变慢。在 Stack Overflow 上,高赞回答指出 collections.deque 是双端队列,popleft() 是 O(1) 操作,而 list.pop(0) 是 O(n),因为要移动所有元素。这就是为什么我们在 ai_agent.py 里用 deque

5. 优化与扩展

5.1 让猫更“狡猾”

目前猫每步都重新 BFS,计算量稍大。可以优化:

  • 预计算:老鼠不动时,缓存猫的路径,直到老鼠移动才重新计算。
  • 威胁区域:计算老鼠周围 3 步内的区域,如果老鼠进入,猫加速(减少 time.sleep 时间)。

5.2 图形化界面

命令行版太素?用 pygametkinter 替换 print_map

  • tkinter:Python 自带,轻量,适合快速原型。
  • pygame:功能强大,适合做真正的游戏,支持音效、动画。

5.3 多猫模式

生成 2-3 只猫,各自独立 BFS。这时候老鼠需要判断哪只猫最近,选择安全路径。这引入了博弈论的雏形。

5.4 持久化

json 保存最高分和地图布局。每次启动读取 config.json

6. 小结

从 0 到 1 搭完这个猫鼠游戏,你掌握了:

  1. BFS 算法:最短路径搜索的经典应用。
  2. 模块化设计:逻辑、AI、UI 分离,方便维护。
  3. 状态机:游戏循环中的输入-处理-渲染流程。
  4. 边界处理:墙、地图边缘、死锁情况。

常见报错排查

  • IndexError:检查坐标是否越界,BFS 中 nx, ny 是否加了边界检查。
  • 游戏卡死:检查 BFS 是否无限循环,visited 数组是否正确标记。
  • 猫不动:检查 random_move 是否被触发,地图是否全被墙围死。

这个完整示例不是终点,而是起点。你可以加音效、加皮肤、加多人模式。编程的乐趣在于折腾,不在于完美。

互动时间: 你在写类似的小游戏时,卡在哪个环节最多?是地图生成不连通,还是 AI 太笨/太聪明?或者你有更好的寻路算法建议?评论区留言,我挨个回。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/22 8:00:47

一二三四五六七从零搭建:避开3个高频面试题坑的实战指南

一二三四五六七从零搭建:避开3个高频面试题坑的实战指南 别翻那几百页的官方文档了,直接看这里。 官方文档太长抓不住重点,这是很多转岗开发者的通病。尤其是面对一二三四五六七这种底层逻辑复杂的模块,看文档像看天书,面试时一问细节就卡壳。其实,一二三四五六七的核心逻辑并不深奥,难的是在实战中如何稳定落地,…

作者头像 李华
网站建设 2026/9/22 8:00:45

避坑指南:一文搞懂高中知识点配置,告别环境卡壳

避坑指南:一文搞懂高中知识点配置,告别环境卡壳 配置环境就卡半天?别急,这不仅是你的问题,更是很多老手都会踩的深坑。 做开发这么多年,我见过太多人在“高中知识点”相关的学习框架或模拟系统搭建时,因为依赖版本冲突、路径配置错误或权限问题,在终端里敲了半小时命令,最后只能对着报错日志发呆。这种体验极其糟…

作者头像 李华
网站建设 2026/9/22 8:00:36

雪倪性能调优:一文搞懂3步让慢代码飞起来

雪倪性能调优:一文搞懂3步让慢代码飞起来 代码从网上复制下来,本地一跑直接报错?别急,这往往不是代码烂,而是环境依赖、版本冲突或者你根本不知道哪里卡住了。很多刚入行的学员,或者在培训机构里跟着敲代码的朋友,最头疼的就是这种“看着能跑,一上项目就崩”的局面。今天咱们不聊虚的,直接切入正题,结合 雪倪…

作者头像 李华
网站建设 2026/9/22 8:00:21

网易有钱安全吗?后端视角拆解资金流,新手避坑指南

网易有钱安全吗?后端视角拆解资金流,新手避坑指南 你刚把教程里的支付接口代码复制到本地,运行报错 Connection Refused ,盯着屏幕发呆,不知道是网络问题还是密钥没填对?这种“代码跑不通、报错看不懂”的绝望感,是每个后端新手在接触金融类项目时的噩梦。别慌,今天咱们不聊虚的,直接切入正题…

作者头像 李华
网站建设 2026/9/22 7:59:55

别再瞎选框架了,breeze356避坑指南助你搞定项目

别再瞎选框架了,breeze356避坑指南助你搞定项目 看了一堆教程还是不会写项目?别急着骂教程,可能是你选错了工具。很多新手卡在“Demo能跑,业务写不动”的坑里,根源往往不是代码能力,而是架构选型混乱。今天这篇 breeze356避坑指南…

作者头像 李华
网站建设 2026/9/22 7:59:40

何亨建全栈开发避坑指南含完整示例

何亨建全栈开发避坑指南含完整示例 配置环境就卡半天,是不是你也经历过?很多刚接触何亨建相关技术栈的朋友,一上手就被各种依赖冲突和版本报错搞得焦头烂额,甚至怀疑自己是不是不适合写代码。别急,今天这篇何亨建全栈开发实战教程,专门为你准备了 完整示例…

作者头像 李华