首页/新闻资讯/正文详情

C++控制台循环链表实现贪吃蛇

发布时间:2026/11/5 5:39:10 来源:云帆数科 栏目:资讯中心
C++控制台循环链表实现贪吃蛇
前言贪吃蛇的核心数据是蛇身一串按顺序排列的格子头部每次向一个方向增加一格尾部在没吃到食物时删除一格。这个「头插尾删」的模式与链表天然契合又因为蛇头撞到蛇身就游戏结束需要判断新头是否与身体重合把链表接成环形可以让遍历和判重更统一。常见的误解是「循环链表只是为了省一个尾指针」。实际上循环链表在这里的价值是tail-next永远指向头插入新头时不需要单独维护「尾指向空」删除尾时也能从任意节点绕一圈找到前驱。代价是遍历必须用「回到起点」作为终止条件而不能用nullptr一旦写成while (p ! nullptr)就会死循环。另一个误解是「吃到食物只需要不删尾」。不删尾确实让长度加一但新头已经插入尾指针仍指向原来的尾链表结构是对的真正容易错的是「没吃到食物时删尾」的前驱查找以及「新头撞到尾巴」的判定——尾巴马上要移走撞上去应当允许。本文以 C17 为准实现一个只含逻辑层的贪吃蛇循环链表、移动、增长、自撞检测、食物生成。控制台的实时按键属于平台相关部分单独说明。一、为什么用循环链表蛇身需要支持三种操作在头部插入新节点每次移动。在尾部删除节点没吃到食物时。判断某个坐标是否被身体占据自撞检测、食物生成。用std::vector也能做但头部插入是O(n)删除尾部虽然均摊O(1)整体不如链表直接。用单链表非循环时删除尾节点需要找前驱而只有头指针的话要遍历整条链循环链表让尾节点的next指回头配合一个尾指针插入和删除都只差一次前驱查找仍是O(n)但代码结构统一。结构头插尾删判重遍历终止条件std::vectorO(n)O(1)O(n)下标单链表O(1)O(n)找前驱O(n)nullptr循环链表O(1)O(n)找前驱O(n)回到头严格来说若想尾删也O(1)可以用双向循环链表或者额外维护尾节点的前驱。本文选择单向循环链表重点是把指针操作讲清楚对贪吃蛇这种规模O(n)的尾删完全可以接受。二、移动、增长与自撞设当前头节点为head、尾节点为tail新头坐标为(head-x dx, head-y dy)。移动步骤计算新头坐标先做自撞检测。newNode-next head; tail-next newNode; head newNode;把新头接在旧头前面。如果没吃到食物删除旧尾从head出发找到p-next oldTail的节点p令p-next headtail pdelete oldTail。自撞检测从head-next开始遍历回到head停止。若某个节点坐标与新头相同如果没吃到食物且该节点正是tail允许——尾巴这一步会移走新头占据它的位置是合法的。否则判定撞身。增长吃到食物时不执行第 3 步旧尾保留长度自然加一。这里有一个容易忽略的细节自撞检测必须在插入新头之前做。若先插入再检测新头自己也在链表里遍历时必然撞到自己。三、完整实现// 适用于 C17#include cstddef#include iostream#include random#include string#include vectorclass Snake {public:Snake(int x, int y, int length) {head_ new Node{x, y, nullptr};head_-next head_;tail_ head_;for (int i 1; i length; i) {Node* node new Node{x - i, y, head_};tail_-next node;tail_ node;}}~Snake() {if (head_ nullptr) return;Node* p head_-next;while (p ! head_) {Node* next p-next;delete p;p next;}delete head_;}Snake(const Snake) delete;Snake operator(const Snake) delete;bool move(int dx, int dy, bool eat) {const int nx head_-x dx;const int ny head_-y dy;if (hitsBody(nx, ny, eat)) return false;Node* node new Node{nx, ny, head_};tail_-next node;head_ node;if (!eat) {Node* oldTail tail_;Node* p head_;while (p-next ! oldTail) {p p-next;}p-next head_;tail_ p;delete oldTail;}return true;}bool occupies(int x, int y) const {const Node* p head_;do {if (p-x x p-y y) return true;p p-next;} while (p ! head_);return false;}int headX() const { return head_-x; }int headY() const { return head_-y; }private:struct Node {int x;int y;Node* next;};bool hitsBody(int x, int y, bool eat) const {const Node* p head_-next;while (p ! head_) {if (p-x x p-y y) {if (!eat p tail_) return false; // 尾巴会移走允许return true;}p p-next;}return false;}Node* head_ nullptr;Node* tail_ nullptr;};构造函数先建头再逐个把新节点接到尾部next指回head_最后tail_-next自然形成环。析构从head_-next开始删到回到head_最后删头避免边遍历边释放导致的悬垂。move中的顺序值得逐行核对。插入新头后链表是newHead - oldHead - ... - oldTail - newHead。删除旧尾时从newHead出发找p-next oldTail的p由于oldTail-next newHead这个查找一定能在环内完成。若长度为 1p就是newHead令p-next head_后形成自环仍是合法的单节点循环链表。四、游戏循环与渲染// 适用于 C17#include chrono#include threadvoid render(const Snake snake, int width, int height, int fx, int fy) {std::vectorstd::string canvas(height, std::string(width, ));for (int x 0; x width; x) {canvas[0][x] #;canvas[height - 1][x] #;}for (int y 0; y height; y) {canvas[y][0] #;canvas[y][width - 1] #;}canvas[fy][fx] ;canvas[snake.headY()][snake.headX()] O;for (int y 0; y height; y) {std::cout canvas[y] \n;}}int main() {const int width 20;const int height 12;Snake snake(width / 2, height / 2, 3);int fx 5;int fy 5;std::mt19937 rng(std::random_device{}());std::uniform_int_distributionint distX(1, width - 2);std::uniform_int_distributionint distY(1, height - 2);int dx 1;int dy 0;char cmd 0;while (std::cin cmd) {int ndx dx;int ndy dy;if (cmd w) { ndx 0; ndy -1; }else if (cmd s) { ndx 0; ndy 1; }else if (cmd a) { ndx -1; ndy 0; }else if (cmd d) { ndx 1; ndy 0; }else if (cmd q) break;else continue;if (ndx -dx ndy -dy) continue; // 禁止 180 度掉头const int nx snake.headX() ndx;const int ny snake.headY() ndy;if (nx 0 || nx width - 1 || ny 0 || ny height - 1) {std::cout 撞墙游戏结束\n;break;}const bool eat (nx fx ny fy);if (!snake.move(ndx, ndy, eat)) {std::cout 撞到自己游戏结束\n;break;}if (eat) {bool placed false;for (int attempt 0; attempt 200 !placed; attempt) {const int x distX(rng);const int y distY(rng);if (!snake.occupies(x, y)) {fx x;fy y;placed true;}}if (!placed) {std::cout 没有空位放食物胜利\n;break;}}dx ndx;dy ndy;render(snake, width, height, fx, fy);std::this_thread::sleep_for(std::chrono::milliseconds(100));}return 0;}std::mt19937与std::uniform_int_distribution来自random比rand() % n分布更均匀也不依赖全局种子。食物生成最多尝试 200 次若蛇几乎占满地图找不到空位就判定胜利。输入采用「每次读一个字符、回车确认」的模式因此不需要conio.h。若想要不按回车就响应必须使用平台 APIWindows 的_kbhit/_getch在conio.h中POSIX 需要 termios 修改终端模式。二者都不是标准 C移植时要单独封装输入层。常见坑点1. 遍历时用nullptr判断结束❌for (Node* p head_; p ! nullptr; p p-next)循环链表永远不返回空死循环。 ✅ 用p ! head_或do { ... } while (p ! head_)。2. 删尾时找不到前驱❌delete tail_; tail_ tail_-next;删完还访问已释放内存UB。 ✅ 先从新头遍历到p-next oldTail改p-next更新tail_再删。3. 吃到食物也删尾❌ 无条件删尾长度不增长蛇始终原地踏步。 ✅ 用eat分支控制吃到时保留旧尾。4. 自撞检测把尾巴算进去❌ 新头进入尾巴格直接判负但没吃到食物时尾巴本该移走。 ✅ 未吃食物且目标节点是tail_时放行。5. 允许 180 度掉头❌ 蛇向右时输入a新头直接撞上第二节身体。 ✅ 记录当前方向拒绝与当前方向完全相反的方向。6. 食物生成在蛇身上❌ 随机坐标不与蛇身判重食物被身体覆盖永远吃不到。 ✅ 生成后用occupies检查冲突则重试。7. 使用rand() % n生成坐标❌ 模运算带来分布偏差且全局种子影响其他模块。 ✅ 用std::mt19937std::uniform_int_distribution。8. 忘记释放链表❌ 只delete head_其余节点泄漏。 ✅ 析构沿环遍历逐个delete最后删头或改用std::unique_ptr管理。总结操作指针动作注意点构造逐个尾插next指回头保持环闭合移动新节点插在头前先判自撞再插入增长跳过删尾eat为真时保留尾巴缩短找尾前驱改链删尾不能用tail_-next直接前进遍历回到head_停止不要用nullptr析构从head_-next删到head_最后删头循环链表实现贪吃蛇关键是把「环的闭合」贯穿到插入、删除、遍历、析构四个动作里。插入时让tail-next指向新头删除时让前驱指回新头遍历时以回到头为终止析构时沿环走一圈。把这四条写对蛇身的增删就不会出现断链或悬垂剩下的方向控制、食物生成和渲染只是外围逻辑。

相关推荐

页面置换算法实验:FIFO、LRU、OPT与Belady异常解析
页面置换算法实验:FIFO、LRU、OPT与Belady异常解析

简介:这份资源面向操作系统课程中学习虚拟存储与页面置换的学生,围绕实验七的要求,提供可运行的模拟程序与实验文档,帮助理解不同置换策略的执行过程与性能差异。压缩包共5个文件,约31KB,包含cpp与h源文件&… · 2026/11/5 5:38:10

XP系统IO Libraries 15.5安装与VISA仪器连接配置指南
XP系统IO Libraries 15.5安装与VISA仪器连接配置指南

简介:Agilent IO Libraries Suite 15.5 是面向 Windows XP 环境、用于连接与控制安捷伦仪器的通信工具套件,适合仍在使用老旧测试设备的实验室工程师、科研人员及仪器驱动开发者,可解决 GPIB、USB、串口与以太网等接口下的仪器识别、配置和程… · 2026/11/5 5:38:10

MFC界面美化实战:纯GDI+重绘标题栏、菜单与列表控件
MFC界面美化实战:纯GDI+重绘标题栏、菜单与列表控件

简介:本资源是一套面向MFC桌面开发者的界面美化完整工程实践,专为希望摆脱原生Windows风格、提升软件专业感与用户体验的中高级C开发者设计。项目基于VS2022可直接编译运行,系统性实现了菜单栏、标题栏、自定义按钮(含启动/停止&a… · 2026/11/5 5:38:10

高效会议互动设计:10个实战方案提升40%会议效率
高效会议互动设计:10个实战方案提升40%会议效率

1. 会议互动环节设计的核心价值会议冷场和跑题是职场中最常见的两大痛点。根据我过去五年为37家企业设计会议流程的经验,传统会议模式中平均每小时的无效时间高达18分钟。而精心设计的互动环节能将会效提升40%以上,这个数据来自我们对实施新会议模式的23… · 2026/11/5 8:32:13

工业防爆无线AP选型指南:IIB与IIC标准对比
工业防爆无线AP选型指南:IIB与IIC标准对比

1. 防爆无线AP选型背景解析 在石油化工、煤矿井下、危化品仓储等特殊工业场景中,常规无线网络设备存在严重安全隐患。一个电火花就可能引发灾难性事故——这正是防爆无线接入点(AP)存在的核心价值。这类设备通过特殊设计确保在易燃易爆环境中… · 2026/11/5 8:32:13

楼宇微网虚拟储能系统设计与优化实践
楼宇微网虚拟储能系统设计与优化实践

1. 项目概述与背景解析 楼宇微网作为分布式能源系统的重要应用形式,正在成为建筑领域实现"双碳"目标的关键技术路径。传统楼宇微网主要依赖物理储能设备(如锂电池)来平抑可再生能源的波动性,但这种方案面临着投资成本高… · 2026/11/5 8:32:13

数学建模国赛C题实战指南:数据预处理、模型选择与结果检验全解析
数学建模国赛C题实战指南:数据预处理、模型选择与结果检验全解析

简介:这份资源是2022年全国大学生数学建模竞赛C题的MATLAB实现代码,面向正在备战数学建模国赛、希望参考获奖方案实现细节的高校学生与建模爱好者。压缩包共24个文件,约405KB,以13个.m脚本为主体,配合6个.mat数据文件和… · 2026/11/5 8:32:13

预期功能安全SOTIF:零故障为何仍会撞车?
预期功能安全SOTIF:零故障为何仍会撞车?

简介:面向智能网联汽车功能安全从业者、系统与软件工程师及高校相关专业学习者,这份资料提供预期功能安全(SOTIF)领域的核心标准文本,重点解决自动驾驶与辅助驾驶系统在无故障情形下因性能局限、感知偏差和算法约束所引… · 2026/11/5 8:32:13

OpenClaw智能体集群:工业级部署与优化实践
OpenClaw智能体集群:工业级部署与优化实践

1. 项目概述:智能体集群的工业级实践 在自动化运维和分布式计算领域,智能体集群正成为企业基础设施的新标配。OpenClaw作为轻量级智能体管理框架,其核心价值在于将复杂的集群管理简化为可编程的标准化操作。我在金融行业的基础架构升级项目中… · 2026/11/5 8:31:12

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

多智能体协作系统架构设计与实践优化
多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39

大模型系统性入门实战指南:从原理到部署微调的全链路路线
大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39

鸿蒙文件系统沙箱机制解析与开发实践
鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码