一、整体模型把游戏拆成4个东西贪吃蛇游戏本质上只有4个部分蛇– 一串连续的“格子”有头有尾能移动、能变长。食物– 一个独立的格子蛇头碰到它就会“吃掉”。地图– 一个有限的矩形区域四周有墙或边界。规则– 移动规则、成长规则、死亡规则撞墙/撞自己。你的任务写代码模拟这4个东西的交互。二、数据存储为什么必须用链表2.1 蛇身的数据特点每一节身体都有x坐标和y坐标。蛇的长度是动态变化的初始3节吃东西后1。移动时蛇的整体形状会改变头往前尾巴收缩。2.2 数组 vs 链表特性数组链表固定大小需要预先设定最大长度可以无限增长受内存限制插入/删除头部需要移动所有元素O(n)只需要改变指针O(1)删除尾部直接减少长度如果记录长度需要遍历找到倒数第二个节点O(n)但简单内存使用连续空间可能浪费按需分配无浪费结论贪吃蛇最自然的实现就是单向链表。每个节点存储自己的坐标 (x, y)指向下一节身体的指针 (next)蛇头就是链表的头节点。蛇尾就是链表的尾节点next NULL。2.3 还需要存储什么全局状态除了蛇身链表你还需要记住当前方向上/下/左/右分数或已吃食物数量游戏是否结束布尔值食物的坐标x, y这些信息通常打包成一个结构体比如叫 Game 或 Snake。三、移动逻辑头插法 是否吃食物最核心3.1 头插法详解头插法在链表的最前面插入一个新节点让它成为新的头。为什么用头插法因为蛇移动时新蛇头是原蛇头往前一步的位置。这个新位置需要成为新的起点而原蛇头变成第二节。这正是“在头部插入一个节点”的操作。3.2 移动的两种可能结果情况A新蛇头的位置没有食物→ 执行完整头插法加头 删尾蛇的长度不变。情况B新蛇头的位置有食物→ 执行只加头不删尾蛇的长度 1分数 1重新生成食物。3.3 如何删除尾部节点单向链表删除尾部需要找到倒数第二个节点然后把它的 next 设为 NULL再 free 掉原来的尾节点。小优化你可以用一个额外的指针 tail 始终指向蛇尾这样删除尾部就是 O(1) 操作。但初学可以先遍历查找。3.4 关键代码片段仅用于理解逻辑// 假设已经计算出 newX, newY Node* newHead createNode(newX, newY); newHead-next snake-head; snake-head newHead; if (newX foodX newY foodY) { // 吃到食物不删尾 score; generateFood(); } else { // 没吃到删除尾部 deleteTail(snake); }注意deleteTail需要遍历到倒数第二个节点。四、食物生成不能与蛇身重叠4.1 随机生成算法在地图的可行走区域内通常排除墙壁那一圈随机选取一个坐标 (randX, randY)。遍历整个蛇身链表检查这个坐标是否等于任何一节身体的坐标。如果有重叠重新随机重复步骤1直到找到一个空位。如果地图几乎被蛇填满这种“随机 重试”可能会很慢。此时可改用扫描空白格子列表的方法但对小游戏来说没必要。4.2 重要细节食物不能生成在墙上地图四周一般是墙壁比如 x0 或 x最大宽度-1。食物的随机范围应该限制在 [1, 宽度-2] 和 [1, 高度-2]。伪代码do { fx rand() % (width - 2) 1; fy rand() % (height - 2) 1; } while (isOnSnake(fx, fy));五、碰撞检测游戏结束的两种条件5.1 撞墙检测比较新蛇头的坐标与地图边界如果 x 0 或 x width-1 → 撞左墙或右墙如果 y 0 或 y height-1 → 撞上墙或下墙注意不同地图设计可能不同有的四周是墙有的允许穿墙。标准贪吃蛇是撞墙死亡。5.2 撞自身检测从蛇头的下一节开始遍历整个链表。如果发现任何一节的坐标等于新蛇头的坐标→ 撞到自己游戏结束。为什么从下一节开始因为新蛇头的位置可能就是原蛇尾的位置在正常移动中。如果蛇头移动到原来尾巴的位置那是允许的因为尾巴即将被删除。但如果我们先检测再移动就需要小心处理。标准做法在完成头插法、尚未删除尾部时进行自碰检测此时新蛇头可能与原蛇身除原蛇尾外重叠。更简单的做法移动后检查新蛇头是否与蛇身的其他任何节点坐标相同排除新蛇头本身。简化版移动完成后已加头、可能已删尾再检测蛇头是否与后面任何节点重叠。如果重叠游戏结束。六、键盘控制方向限制与实时响应6.1 如何获取按键WindowsGetAsyncKeyState(VK_UP) 可以检测按键是否被按下无需等待控制台输入。跨平台使用 kbhit() getch()conio.h或 curses 库。6.2 方向限制规则不能反向如果当前方向是“右”则按“左”键无效。同理“上”与“下”互斥。这是为了防止蛇瞬间掉头撞到自己因为蛇身还在掉头后新蛇头位置就是原第二节的位置导致立即死亡。6.3 方向更新时机通常在主循环的每次迭代开始时读取按键更新方向变量。然后移动时使用更新后的方向。细节在一次移动过程中不能再次改变方向否则可能在同一帧内转向多次导致穿模。因此方向变量只保存下次移动将要使用的方向。七、主循环7.1 标准主循环流程while (游戏未结束) { 处理输入更新方向 移动蛇头插法 食物判断 碰撞检测如果死亡跳出循环 重新绘制界面 等待一段时间控制速度 }7.2 为什么需要等待如果没有等待蛇会以CPU的最大速度移动快到你看不见。Sleep(毫秒) 让程序暂停一会儿速度 每步之间的时间间隔。时间越短蛇跑得越快。7.3 如何实现加速可以通过动态改变等待时间每吃一定数量的食物Sleep 的时间减少比如减少10毫秒但设一个下限比如50毫秒否则太快无法控制。八、绘制界面如何显示地图和蛇8.1 两种绘制方式全量重绘每次移动后清屏system(cls) 或 printf(\033[2J)然后重新打印整个地图、蛇身、食物。简单但会有闪烁。增量更新只擦除旧的蛇尾位置绘制新的蛇头位置食物不变。不闪烁但实现稍微复杂。对于初学全量重绘完全足够而且代码清晰。8.2 绘制逻辑伪代码遍历整个地图二维数组或根据坐标 如果坐标是墙壁 - 打印 # 否则如果坐标等于食物坐标 - 打印 $ 否则如果坐标属于蛇身链表 - 打印 O蛇头可用 否则 - 打印 空格注意蛇身需要快速查找某个坐标是否属于蛇。简单方法遍历链表。如果要优化可以用二维数组标记。8.3 光标移动不刷屏而是用 SetConsoleCursorPositionWindows或 \033[HLinux将光标移到左上角重绘也可以减少闪烁。九、程序结构一个典型的贪吃蛇项目组织main.c– 主循环、初始化、游戏流程控制snake.h / snake.c– 蛇的数据结构、移动、碰撞检测food.h / food.c– 食物生成map.h / map.c– 地图绘制input.h / input.c– 键盘输入处理好处各模块独立便于修改和调试。十、知识点知识点作用双缓冲绘制消除画面闪烁实现平滑移动状态机管理游戏开始、暂停、结束、计分等不同状态文件I/O保存最高分下次启动时读取计时器精确控制速度不依赖Sleep防止卡输入AI自动寻路让蛇自动走向食物需要搜索算法总结贪吃蛇的核心数据结构是单向链表每个节点存储身体坐标。蛇移动 头插法新蛇头插到链表头部。吃到食物只执行头插不删除尾部 → 长度1。没吃到食物头插后再删除尾部 → 长度不变平移。食物生成随机选空白位置不能与蛇身重叠。游戏结束蛇头撞墙 或 蛇头撞到自己身体。键盘控制不能180°掉头防反向。主循环输入 → 移动 → 碰撞 → 重绘 → 延时。速度控制延时越长蛇越慢延时越短蛇越快。绘制界面循环遍历地图坐标打印不同符号表示墙、蛇、食物。
如何用TS写一个贪吃蛇小游戏
一、整体模型把游戏拆成4个东西贪吃蛇游戏本质上只有4个部分蛇– 一串连续的“格子”有头有尾能移动、能变长。食物– 一个独立的格子蛇头碰到它就会“吃掉”。地图– 一个有限的矩形区域四周有墙或边界。规则– 移动规则、成长规则、死亡规则撞墙/撞自己。你的任务写代码模拟这4个东西的交互。二、数据存储为什么必须用链表2.1 蛇身的数据特点每一节身体都有x坐标和y坐标。蛇的长度是动态变化的初始3节吃东西后1。移动时蛇的整体形状会改变头往前尾巴收缩。2.2 数组 vs 链表特性数组链表固定大小需要预先设定最大长度可以无限增长受内存限制插入/删除头部需要移动所有元素O(n)只需要改变指针O(1)删除尾部直接减少长度如果记录长度需要遍历找到倒数第二个节点O(n)但简单内存使用连续空间可能浪费按需分配无浪费结论贪吃蛇最自然的实现就是单向链表。每个节点存储自己的坐标 (x, y)指向下一节身体的指针 (next)蛇头就是链表的头节点。蛇尾就是链表的尾节点next NULL。2.3 还需要存储什么全局状态除了蛇身链表你还需要记住当前方向上/下/左/右分数或已吃食物数量游戏是否结束布尔值食物的坐标x, y这些信息通常打包成一个结构体比如叫 Game 或 Snake。三、移动逻辑头插法 是否吃食物最核心3.1 头插法详解头插法在链表的最前面插入一个新节点让它成为新的头。为什么用头插法因为蛇移动时新蛇头是原蛇头往前一步的位置。这个新位置需要成为新的起点而原蛇头变成第二节。这正是“在头部插入一个节点”的操作。3.2 移动的两种可能结果情况A新蛇头的位置没有食物→ 执行完整头插法加头 删尾蛇的长度不变。情况B新蛇头的位置有食物→ 执行只加头不删尾蛇的长度 1分数 1重新生成食物。3.3 如何删除尾部节点单向链表删除尾部需要找到倒数第二个节点然后把它的 next 设为 NULL再 free 掉原来的尾节点。小优化你可以用一个额外的指针 tail 始终指向蛇尾这样删除尾部就是 O(1) 操作。但初学可以先遍历查找。3.4 关键代码片段仅用于理解逻辑// 假设已经计算出 newX, newY Node* newHead createNode(newX, newY); newHead-next snake-head; snake-head newHead; if (newX foodX newY foodY) { // 吃到食物不删尾 score; generateFood(); } else { // 没吃到删除尾部 deleteTail(snake); }注意deleteTail需要遍历到倒数第二个节点。四、食物生成不能与蛇身重叠4.1 随机生成算法在地图的可行走区域内通常排除墙壁那一圈随机选取一个坐标 (randX, randY)。遍历整个蛇身链表检查这个坐标是否等于任何一节身体的坐标。如果有重叠重新随机重复步骤1直到找到一个空位。如果地图几乎被蛇填满这种“随机 重试”可能会很慢。此时可改用扫描空白格子列表的方法但对小游戏来说没必要。4.2 重要细节食物不能生成在墙上地图四周一般是墙壁比如 x0 或 x最大宽度-1。食物的随机范围应该限制在 [1, 宽度-2] 和 [1, 高度-2]。伪代码do { fx rand() % (width - 2) 1; fy rand() % (height - 2) 1; } while (isOnSnake(fx, fy));五、碰撞检测游戏结束的两种条件5.1 撞墙检测比较新蛇头的坐标与地图边界如果 x 0 或 x width-1 → 撞左墙或右墙如果 y 0 或 y height-1 → 撞上墙或下墙注意不同地图设计可能不同有的四周是墙有的允许穿墙。标准贪吃蛇是撞墙死亡。5.2 撞自身检测从蛇头的下一节开始遍历整个链表。如果发现任何一节的坐标等于新蛇头的坐标→ 撞到自己游戏结束。为什么从下一节开始因为新蛇头的位置可能就是原蛇尾的位置在正常移动中。如果蛇头移动到原来尾巴的位置那是允许的因为尾巴即将被删除。但如果我们先检测再移动就需要小心处理。标准做法在完成头插法、尚未删除尾部时进行自碰检测此时新蛇头可能与原蛇身除原蛇尾外重叠。更简单的做法移动后检查新蛇头是否与蛇身的其他任何节点坐标相同排除新蛇头本身。简化版移动完成后已加头、可能已删尾再检测蛇头是否与后面任何节点重叠。如果重叠游戏结束。六、键盘控制方向限制与实时响应6.1 如何获取按键WindowsGetAsyncKeyState(VK_UP) 可以检测按键是否被按下无需等待控制台输入。跨平台使用 kbhit() getch()conio.h或 curses 库。6.2 方向限制规则不能反向如果当前方向是“右”则按“左”键无效。同理“上”与“下”互斥。这是为了防止蛇瞬间掉头撞到自己因为蛇身还在掉头后新蛇头位置就是原第二节的位置导致立即死亡。6.3 方向更新时机通常在主循环的每次迭代开始时读取按键更新方向变量。然后移动时使用更新后的方向。细节在一次移动过程中不能再次改变方向否则可能在同一帧内转向多次导致穿模。因此方向变量只保存下次移动将要使用的方向。七、主循环7.1 标准主循环流程while (游戏未结束) { 处理输入更新方向 移动蛇头插法 食物判断 碰撞检测如果死亡跳出循环 重新绘制界面 等待一段时间控制速度 }7.2 为什么需要等待如果没有等待蛇会以CPU的最大速度移动快到你看不见。Sleep(毫秒) 让程序暂停一会儿速度 每步之间的时间间隔。时间越短蛇跑得越快。7.3 如何实现加速可以通过动态改变等待时间每吃一定数量的食物Sleep 的时间减少比如减少10毫秒但设一个下限比如50毫秒否则太快无法控制。八、绘制界面如何显示地图和蛇8.1 两种绘制方式全量重绘每次移动后清屏system(cls) 或 printf(\033[2J)然后重新打印整个地图、蛇身、食物。简单但会有闪烁。增量更新只擦除旧的蛇尾位置绘制新的蛇头位置食物不变。不闪烁但实现稍微复杂。对于初学全量重绘完全足够而且代码清晰。8.2 绘制逻辑伪代码遍历整个地图二维数组或根据坐标 如果坐标是墙壁 - 打印 # 否则如果坐标等于食物坐标 - 打印 $ 否则如果坐标属于蛇身链表 - 打印 O蛇头可用 否则 - 打印 空格注意蛇身需要快速查找某个坐标是否属于蛇。简单方法遍历链表。如果要优化可以用二维数组标记。8.3 光标移动不刷屏而是用 SetConsoleCursorPositionWindows或 \033[HLinux将光标移到左上角重绘也可以减少闪烁。九、程序结构一个典型的贪吃蛇项目组织main.c– 主循环、初始化、游戏流程控制snake.h / snake.c– 蛇的数据结构、移动、碰撞检测food.h / food.c– 食物生成map.h / map.c– 地图绘制input.h / input.c– 键盘输入处理好处各模块独立便于修改和调试。十、知识点知识点作用双缓冲绘制消除画面闪烁实现平滑移动状态机管理游戏开始、暂停、结束、计分等不同状态文件I/O保存最高分下次启动时读取计时器精确控制速度不依赖Sleep防止卡输入AI自动寻路让蛇自动走向食物需要搜索算法总结贪吃蛇的核心数据结构是单向链表每个节点存储身体坐标。蛇移动 头插法新蛇头插到链表头部。吃到食物只执行头插不删除尾部 → 长度1。没吃到食物头插后再删除尾部 → 长度不变平移。食物生成随机选空白位置不能与蛇身重叠。游戏结束蛇头撞墙 或 蛇头撞到自己身体。键盘控制不能180°掉头防反向。主循环输入 → 移动 → 碰撞 → 重绘 → 延时。速度控制延时越长蛇越慢延时越短蛇越快。绘制界面循环遍历地图坐标打印不同符号表示墙、蛇、食物。