#include <iostream>
#include <windows.h>
#include <time.h>
#include <list>
#include <conio.h>
//#include<stdio.h>
//#include<string.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;
/*
list.assign() 给list赋值 list.back() 返回最后一个元素
list.begin() 返回指向第一个元素的迭代器 list.clear() 删除所有元素
list.empty() 如果list是空的则返回true list.end() 返回末尾的迭代器
list.erase() 删除一个元素 list.front() 返回第一个元素
list.get_allocator() 返回list的配置器 list.insert() 插入一个元素到list中
list.max_size() 返回list能容纳的最大元素数量 list.merge() 合并两个list
list.pop_back() 删除最后一个元素 list.pop_front() 删除第一个元素
list.push_back() 在list的末尾添加一个元素 list.push_front() 在list的头部添加一个元素
list.rbegin() 返回指向第一个元素的逆向迭代器 list.remove() 从list删除元素
list.remove_if() 按指定条件删除元素 list.rend() 指向list末尾的逆向迭代器
list.resize() 改变list的大小 list.reverse() 把list的元素倒转
list.size() 返回list中的元素个数 list.sort() 给list排序
list.splice() 合并两个list list.swap() 交换两个list
list.unique() 删除list中重复的元素*/
// 食物类
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);
// 打印蛇
list<Snake> :: iterator lt; // 声明一个贪吃蛇类型的集合迭代器lt
Snake sk;
for (lt = list_snake.begin(); lt != list_snake.end(); lt ++)
{
sk = (Snake)*lt;
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;
}
list<Snake> :: iterator lt; // 声明一个贪吃蛇类型的集合迭代器lt
Snake sk;
// 遍历蛇,保证生成的这个食物不在蛇身上
for (lt = list_snake.begin(); lt != list_snake.end(); lt ++)
{
sk = (Snake)*lt;
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;
}
list<Snake> :: iterator lt; // 声明一个贪吃蛇类型的集合迭代器lt
Snake sk;
// 遍历蛇,判断蛇头有没有跟其他部分重合
for (lt = ++ list_snake.begin(); lt != list_snake.end(); lt ++)
{
sk = (Snake)*lt;
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");
}