#ifndef _LIST_H
#define _LIST_H

template <class T>
class Node
{
public:
    Node<T> *next;
    Node<T> *prev;
    T data;
};

template <class T>
class List
{
private:
    Node<T> *head; // 头
    Node<T> *tail; // 尾巴
    int length; // 长度

public:
    List();
    List(const List& ln);
    ~List();
    void push_back(T data); // 在最后面添加
    void push_front(T data); // 在最前面添加
    void pop_back(); // 删除最后一个
    void remove(T data); // 移除
    T front(); // 获取第一个
    T back(); // 获取最后一个
    bool isEmpty(); // 是否为空
    int size(); // 大小
    void clear(); // 清空
    T operator[](int e);
};

template<typename T>
List<T>::List()
{
    head = new Node<T>;
    tail = new Node<T>;
    head -> next = tail;
    head -> prev = NULL;
    tail -> next = NULL;
    tail -> prev = head;
    length = 0;
}

template<typename T>
List<T>::List(const List &ln)
{
    head = new Node<T>;
    tail = new Node<T>;
    head -> prev = NULL;
    head -> next = tail;
    tail -> prev = head;
    tail -> next = NULL;
    length = 0;

Node<T> *temp = ln.head;
    while (temp -> next != ln.tail)
    {
        temp = temp -> next;
        tail -> data = temp -> data;
        Node<T> *p = new Node<T>;
        p -> prev = tail;
        tail -> next = p;
        tail = p;
        length ++;
    }
    tail -> next = NULL;
}

template<typename T>
void List<T>::push_back(T e)
{
    // 创建一个新节点
    Node<T> *newNode = new Node<T>;
    newNode -> data = e; // 节点数据
    tail -> prev -> next = newNode; // 尾节点的上一个节点的下一个节点是这个新节点
    newNode -> next = tail; // 当前节点的下一个节点是尾节点
    newNode -> prev = tail -> prev; // 当前节点的上一个节点是尾节点的上一个节点
    tail -> prev = newNode; // 尾节点的上一个节点是当前节点
    length ++;
}

template<typename T>
void List<T>::push_front(T e)
{
    // 创建一个新节点
    Node<T> *newNode = new Node<T>;
    newNode -> data = e; // 节点数据
    head -> next -> prev = newNode; // 头节点的下一个节点的上一个节点是当前节点
    newNode -> next = head -> next; // 当前节点的下一个节点是头节点的下一个节点
    head -> next = newNode;
    newNode -> prev = head;
    length ++;
}

template<typename T>
void List<T>::remove(T data)
{
    if (length == 0)
    {
        std::cout << "列表为空...";
        return;
    }

Node<T> *p = head;
    while (p -> next != NULL)
    {
        p = p -> next;
        if (p -> data == data)
        {
            Node<T> *temp = p -> prev;
            temp -> next = p -> next;
            p -> next -> prev = temp;
            -- length;
            delete p;
            return;
        }
    }
}

template<typename T>
void List<T>::pop_back()
{
    if (length == 0)
    {
        std::cout << "列表为空...";
        return;
    }

Node<T> *p = tail; // 指向尾巴
    if (tail -> prev != NULL) // 尾巴的上一个不为空
    {
        p = p -> prev; // 获取尾巴的上一个节点
        p -> prev -> next = tail; // 尾巴的上一个的上一个节点指向尾巴
        tail -> prev = p -> prev;
        delete p;
        -- length;
        return;
    }
}

template<typename T>
void List<T>::clear()
{
    if (length == 0)
    {
        return;
    }
    Node<T> *p = head -> next;
    Node<T> *temp;
    while (p != tail)
    {
        temp = p;
        p = p -> next;
        delete temp;
    }
    head -> next = tail;
    tail -> prev = head;
    length = 0;
}

template <typename T>
bool List<T>::isEmpty()
{
    if (length == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

template <typename T>
int List<T> ::size()
{
    return length;
}

template <typename T>
List<T>::~List()
{
    if (length == 0)
    {
        delete head;
        delete tail;
        head = NULL;
        tail = NULL;
        return;
    }
    while (head->next != NULL)
    {
        Node<T> *temp = head;
        head = head -> next;
        delete temp;
    }
    delete head;
    head = NULL;
}

template <typename T>
T List<T>::front()
{
    if (length <= 0)
    {
        std::cout << "列表为空...";
        return head -> data;
    }
    return head -> next -> data;
}

template <typename T>
T List<T>::back()
{
    if (length <= 0)
    {
        std::cout << "列表为空...";
        return tail -> data;
    }
    return tail -> prev -> data;
}

template <typename T>
T List<T>::operator[](int index)
{
    if (index < 0)
    {
        std::cout << "索引下标越界...";
        return head -> data;
    }
    int i = 0;
    Node<T> *p = head;
    while(p -> next != tail)
    {
        p = p -> next;
        if (i == index)
        {
            return p -> data;
        }
        i ++;
    }
    return tail -> data;
}
#endif

#include <iostream>
#include <windows.h>
#include <time.h>
#include "list.h"
#include <conio.h>

using namespace std;

#define up 'w'
#define down 's'
#define left 'a'
#define right 'd'

char name[20];  // 保存用户名 有兴趣可以制作登录系统
int score = 0;  // 分数
char click = 1; // 记录敲下的键盘按键
int speed = 10;      // 速度 其实是延迟的毫秒数

class Snake
{
public:
    int x;
    int y;

public:
    Snake() {}
    Snake(int x, int y)
    {
        this -> x = x;
        this -> y = y;
    }
    ~Snake(){}
};

int footX, footY; // 存放蛇尾

// 声明存放贪吃蛇的集合列表
List<Snake> list_snake;

// 食物类
class Food
{
public:
    int x;
    int y;
} food;

class Controller
{
public:
    void showCopy(); // 显示版权
    void welcome(); // 欢迎界面
    void gotoxy(int x, int y); // 光标跳到指定位置的方法
    void createGraph(); // 创建地图的方法
    void createFood(); // 创建食物
    bool clickControl(); // 鼠标控制
    void movingBody(); // 移动蛇的方法
    void eating();
    bool judge(); // 判断游戏是否结束
    void finish(int x); // 游戏结束的方法

// 输出墙砖
    void gotoPrint(int x, int y)
    {
        gotoxy(x, y);
        cout << "■";
    }

// 删除指定位置的内容
    void gotoDelete(int x, int y)
    {
        gotoxy(x, y);
        cout <<"  ";
    }
} controller;

int main()
{
    system("color 0B"); // 设置控制台字体颜色
    controller.welcome();          // 欢迎界面

controller.createGraph();       // 创建地图
    controller.createFood();        // 新建食物

// 捕获鼠标按键 ClickControl
    if (controller.clickControl()) return 0;

return 0;
}

void Controller::showCopy(){
    system("cls");
    cout << "\n\n\t*************************欢迎进入游戏世界***************************" << endl;
    cout << "\n\n\t**************************源辰信息版权所有**************************" << endl;
}

// 欢迎界面
void Controller::welcome()
{
    showCopy();
    gotoxy(26, 10);
    cout << "欢迎来到贪吃蛇游戏";
    gotoxy(14, 14);
    cout << "[请在英文输入法中操作,反向移动等同于吃到自己,wasd控制p暂停]";
    gotoxy(26, 18);
    cout << "请输入您的昵称:";

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursor; // 获取光标
    cursor.bVisible = TRUE;  // 隐藏光标
    cursor.dwSize = sizeof(cursor);

SetConsoleCursorInfo(hOutput, &cursor); // 显示光标
    cin >> name; // 获取用户输入的昵称
    system("cls");
}

void Controller :: gotoxy(int x, int y) // 移动光标到指定位置
{
    COORD pos; // 更新光标位置
    // GetStdHandle()返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄。
    // STD_INPUT_HANDLE   标准输入的句柄
    // STD_OUTPUT_HANDLE  标准输出的句柄
    // STD_ERROR_HANDLE   标准错误的句柄
    HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    pos.X = x;
    pos.Y = y;

SetConsoleCursorPosition(hOutput, pos); // 设置控制台(cmd)光标位置

CONSOLE_CURSOR_INFO cursor; // 获取光标
    cursor.bVisible = FALSE;  // 隐藏光标
    cursor.dwSize = sizeof(cursor);
    SetConsoleCursorInfo(hOutput, &cursor); // 隐藏光标
}

// 围墙打印
void Controller::createGraph() {
    int i;
    //  注意这里横坐标是每次+2 因为控制台字符宽高比为1:2
    for (i = 0; i < 58; i += 2)
    {
        gotoPrint(i, 0); // 顶部围墙
        gotoPrint(i, 26); // 底部围墙
    }

for (i = 1; i < 26; i++)
    {
        gotoPrint(0, i); // 左边围墙
        gotoPrint(56, i); // 右边围墙
    }

gotoxy(63, 8);
    cout << "你好" << name << ", 欢迎进入游戏世界";
    gotoxy(63, 13);
    cout << "当前分数:" << score;
    gotoxy(63, 18);
    cout << "源辰信息科技提供";

// 初始化蛇
    Snake head = Snake(16, 15); // 初始化蛇头
    Snake body = Snake(16, 16); // 第一节蛇身
    Snake foot = Snake(16, 17); // 蛇尾

list_snake.push_back(head); // 在列表的末尾添加一个元素
    list_snake.push_back(body);
    list_snake.push_back(foot);

// 打印蛇
    Snake sk;
    for (i = 0; i < list_snake.size(); i ++)
    {
        sk = list_snake[i];
        gotoPrint(sk.x, sk.y);
    }
}

// 创建食物
void Controller :: createFood()
{
    // 随机产生一个食物
    bool flag = false; // 标记食物是否已经吃了
    while (!flag) // 如果食物已经吃了
    {
        flag = true;
        // srand函数是随机数发生器的初始化函数
        // time(t) 是指返回自 Unix 纪元(January 1 1970 00:00:00 GMT)起的当前时间的秒数的函数,主要用来获取当前的系统时间,返回的结果是一个time_t类型。
        // 如果t是空指针,直接返回当前时间。如果t不是空指针,返回当前时间的同时,将返回值赋予t指向的内存空间。
        srand((int)time(NULL));

// 随机生成食物的坐标
        food.y = rand() % 25 + 1;
        food.x= rand() % (54 - 1) + 2;

if (food.x % 2 != 0)
        {
            food.x = food.x + 1;
        }

Snake sk;
        // 遍历蛇,保证生成的这个食物不在蛇身上
        for (int i = 0; i < list_snake.size(); i ++)
        {
            sk = list_snake[i];
            if (food.x == sk.x && food.y == sk.y) // 说明生成的食物在蛇身上,则需要重新生成食物
            {
                flag = false;
                break;
            }
        }
    }
    gotoxy(food.x, food.y); // 移动光标到食物位置
    cout << "⊙"; // 输出食物
}

// 捕获鼠标 游戏主循环
bool Controller :: clickControl()
{
    while (true)
    {
        if (judge()) return true; // 如果游戏已经结束

if (kbhit()) // kbhit()是一个C和C++函数,用于非阻塞地响应键盘输入事件 ,检查当前是否有键盘输入,若有则返回一个非0值,否则返回0。
        {
            click = getch(); // 获取键盘输入
        }

movingBody(); // 移动蛇
        eating(); // 吃食物
    }

return false;
}

// 移动蛇
void Controller :: movingBody() {
    // 获取蛇头的位置
    Snake head = list_snake.front();
    int x = head.x, y = head.y;

// 获取新蛇头的位置
    switch (click)
    {
    case up:
        y -= 1;
        break;
    case down:
        y += 1;
        break;
    case left:
        x -= 2;
        break;
    case right:
        x += 2;
        break;
    default:
        break;
    }

if (x != head.x || y != head.y) {
        // 先删除蛇尾
        Snake foot = list_snake.back();
        footX = foot.x; // 将蛇尾保存起来,如果吃到食物需要加一截尾巴
        footY = foot.y;
        gotoDelete(foot.x, foot.y); // 消除尾节点

list_snake.pop_back(); // 删除最后一个元素

Snake new_head = Snake(x, y); // 创建新的蛇头
        list_snake.push_front(new_head);
        gotoPrint(new_head.x, new_head.y); // 打印新蛇头
    }

// 蛇速度控制,分数越高,速度越快
    int count = score / 10;
    if (count <= 10) speed = 150;
    else if (count > 10 && count <= 20) speed = 100;
    else if (count > 20 && count <= 40) speed = 50;
    else speed = 10;
    Sleep(speed);
}

// 吃到食物处理 添加一个尾巴
void Controller :: eating()
{
     // 获取蛇头的位置
    Snake head = list_snake.front();

if (head.x == food.x && head.y == food.y) // 如果舌头跟食物的位置重合
    {
        createFood(); // 创建一个新的食物

Snake new_head = Snake(footX, footY); // 申请一个蛇身空间
        list_snake.push_back(new_head);
        score += 10; // 得分 +10
        gotoxy(72, 13); // 修改得分
        cout << score;
    }
}

// 判断是否游戏结束
bool Controller :: judge()
{
    // 获取蛇头的位置
    Snake head = list_snake.front();

// 如果撞墙了
    if (head.x == 0 || head.x == 56 || head.y == 0 || head.y == 26)
    {
        finish(1);
        return true;
    }

Snake sk;
    // 遍历蛇,判断蛇头有没有跟其他部分重合
    for (int i = 1; i < list_snake.size(); i ++)
    {
        sk = list_snake[i];
        if (head.x == sk.x && head.y == sk.y)
        {
            finish(2);
            return true;
        }
    }

return false;
}

void Controller :: finish(int x)
{
    switch(x)
    {
        case 1: MessageBox(NULL, "撞墙啦,游戏结束...", "结束通知", MB_OK | MB_ICONSTOP); break;
        case 2: MessageBox(NULL, "咬到自己啦,游戏结束...", "结束通知", MB_OK | MB_ICONSTOP); break;
        default: MessageBox(NULL, "程序运行出错啦...", "结束通知", MB_OK | MB_ICONSTOP); break;
    }
    system("cls");
    showCopy();
    gotoxy(20, 10);
    cout << "欢迎来到贪吃蛇游戏";
    gotoxy(20, 14);
    cout << "游戏结束...";
    gotoxy(20, 18);
    cout << "最终得分为: " << score;
    gotoxy(20, 22);
    system("pause");
}

C++自定义列表实现贪吃蛇相关推荐

  1. Pygame实战项目:用300行代码写出贪吃蛇小游戏

    贪吃蛇是一款逻辑清晰.操作简单.老少咸宜.备受欢迎的休闲小游戏. 下面就给大家介绍一下贪吃蛇游戏的基本原理,以及实现贪吃蛇所需要的相关方法. 一.主要思路 我们的贪吃蛇游戏将主要包括三个核心模块,分别 ...

  2. 用 Python 写个贪吃蛇,保姆级教程

    本文基于 Windows 环境开发,适合 Python 新手 下面我们就一起用 Python 实现一个简单有趣的命令行贪吃蛇小游戏,启动命令: .私信小编01即可获取大量Python学习教程 git ...

  3. 记事本贪吃蛇游戏代码_如何用Python10分钟绘制贪吃蛇小游戏?

    贪吃蛇是一款经典的益智游戏,有PC和手机等多种版本,既简单又耐玩.玩家通过上下左右键控制蛇的方向,寻找食物,每吃到一次食物,就能得到一定的积分,而且蛇的身体会越来越长.随着蛇的身体变长,游戏的难度就会 ...

  4. 【机器视觉案例】(12) 自制AI视觉小游戏--贪吃蛇,附python完整代码

    各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 自制贪吃蛇小游戏.先放张图看效果. 规则:食指指尖控制蛇头,指尖每接触到黄色方块,计数加一,蛇身变长,方块随机切换位置.如果指 ...

  5. 【课件】使用Python写贪吃蛇游戏(pygame)

    使用Python写贪吃蛇游戏(pygame) 课件地址:https://blog.csdn.net/a772304419/article/details/130087202 本地路径: cd /D/W ...

  6. 用 Python 写个贪吃蛇,保姆级教程!

    本文基于 Windows 环境开发,适合 Python 新手 本文作者:HelloGitHub-Anthony HelloGitHub 推出的<讲解开源项目>系列,本期介绍 Python ...

  7. 闲来无聊编的贪吃蛇最终版本

    以下是代码 #include <iostream> #include <ctime> #include <conio.h> #include <unistd. ...

  8. 【贪吃蛇小游戏】宝塔面板快速搭建贪吃蛇小游戏Web网站 - 无需云服务器

    文章目录 前言 视频教程 1. 环境安装 2. 安装cpolar内网穿透 3. 内网穿透 4. 固定http地址 5. 配置二级子域名 6. 创建一个测试页面 转载自远程内网穿透的文章:Linux使用 ...

  9. python简易贪吃蛇小游戏任务书含代码

    目  录 第一章 绪论 1.1 开发的背景 1.2 开发的目的 1.3 开发的意义 1.4 开发工具简介 第二章 需求分析 (1) 利用方向键来改变蛇的运行方向. (2) 在随机的地方产生食物. (3 ...

  10. 100行代码,使用 Pygame 制作一个贪吃蛇小游戏!

    作者 | 周萝卜 来源 | 萝卜大杂烩 相信我们大家都玩过贪吃蛇游戏,今天我们就从头一起来写一个贪吃蛇小游戏,只需要100多行的代码就完成了. 用到的 Pygame 函数 贪吃蛇小游戏用到的函数 功能 ...

最新文章

  1. matlab bp结果,Matlab如何处理BP网络每次运行结果不一样这个问题
  2. 你不懂的JS学习笔记(作用域和闭包)
  3. Docker私有仓库的搭建
  4. 安卓手机指纹解锁linux电脑,【果核干货应用篇-06】使用手机指纹解锁电脑
  5. 时序预测:从两篇高影响力的论文谈起
  6. Shell 变量的操作方法
  7. Simulink工作区无法保存To workspace模块的数据解决办法
  8. LeetCode 278. 第一个错误的版本(二分查找)
  9. npm安装vue_vue搭建脚手架的方式
  10. 如何在CentOS 7上使用HAproxy Loadbalancer设置Percona XtraDB集群(负载均衡)
  11. servlet api.jar是干什么的?
  12. Swift基本运算符详解
  13. 期货基础知识 第四节 期货交易流程
  14. 随机效应估算与固定效应估算_固定效应还是随机效应——Hausman检验.PPT
  15. 2021年中国A2P(应用程序对个人)消息传递市场趋势报告、技术动态创新及2027年市场预测
  16. php+ajax上传文件
  17. ps纯色、渐变填充图层只能是灰色
  18. IBM ServeRAID Manager 9.30
  19. 【BPM架构】BPM 平台:独立还是微服务实现
  20. 最全面的Java核心技术开发手册

热门文章

  1. 张帅帅学Java之注释
  2. RocksDB 笔记
  3. wps怎么做文档分享
  4. 阿里云SSL证书免费申请和部署方法((DigiCert 免费版 SSL-图文教程)
  5. Power BI集成Power Apps,轻松实现用户在报告中任意输入信息
  6. 原生 android 手机,三部具有原生安卓系统的旗舰手机,一部比一部漂亮
  7. html页面旋转图标或标签
  8. 删除word中自己插入的单行、双行线
  9. linux系统改键盘系统,Linux 修改键盘设置
  10. SpringMVC i18n国际化资源文件路径配置