基于控制台的贪吃蛇实现
这是本人大一下学期的期末大作业,灵感是system(“cls”)清屏,还有键盘监听函数,单链表数据结构,核心是单链表和system(“cls”)函数。灵感来源于网络,启发了我基于单链表设计。由于system(“cls”)刷屏自身的缺陷和不足,游戏运行起来会不可避免地有些“闪眼睛”。我记得当时还有一种方法不用system(“cls”)清屏,但我没使用那种方法,大家可以找找别的博客或视频学习学习。
代码完全由本人编写和设计,由于时间比较久远,很多实现细节我也记不清了。现在决定对以前写的小史山开源,以供后来者学习,以下内容仅供学习和参考。
#include <iostream>
#include <Windows.h>
#include <cstdio>
#include <iomanip>
#include <conio.h>
#include <cstdlib>
#include <ctime>
#include <cstring>
#define lim 1000
using namespace std;
typedef struct SNAKE {//贪吃蛇特征
int snakeX;//行
int snakeY;//列
struct SNAKE * next;
}SNAKE;
SNAKE * head = NULL;
SNAKE * tail = NULL;
SNAKE * newone = NULL;
char border[lim][lim];//地图
char point = 'D';//游标
int Rank = 13;
int Column = 13;//地图大小
int results;//分数
int ranx;
int rany;//随机数(食物)
int foodNum = 27;//总食物数
int len;//蛇的长度
int key = 0;//监听标准呼入
int tempX;
int tempY;//中间变量
bool getFood = false;
void mabor(char (*border)[lim], int, int);//设置边界
void prtbor(char (*border)[lim], int rank, int column);//打印边界(打印地图)
void turnUp();//向上位移
void turnDown();//向下位移
void turnLeft();//向左位移
void turnRight();//向右位移
int move(char);//蛇的移动
void prtlink();//打印蛇身(打印链表)
void apply();//申请一个节点(蛇身长度加一)
void setSnake();//初始化蛇的属性
void settle();//游戏结算
void exLocation(int *a, int *b);//交换变量
void handle();//操控方向
void instruction();//一级菜单
int main() {
system("cls");//清屏
system("color 09");//发现WindowAPI还可以调控制台背景颜色和字体颜色
instruction();//说明书
mabor(border, Rank, Column);//初始化地图
prtbor(border, Rank, Column);//打印地图
while (move(point)) {//当键入o或O时退出游戏
Sleep(40);
handle();//监听标准输入事件
system("cls");
handle();//监听标准输入事件
prtbor(border, Rank, Column);//打印地图
handle();//监听标准输入事件
}
}
void mabor(char (*border)[lim], int rank, int column) {//设置地图
for (int x = 0; x < rank; x++) {
for (int y = 0; y < column; y++) {
if (x != 0 && x != column - 1) {
y == 0 || y == column - 1 ? border[x][y] = '#' : border[x][y] = ' ';
} else {
border[x][y] = '#';
}
}
}
setSnake();
//食物随机坐标
for (int i = 0; i < foodNum; i++) {
srand(time(NULL) + i);
ranx = rand() % (Rank - 2) + 1;
rany = rand() % (Column - 2) + 1;
if (border[ranx][rany] != '@' && border[ranx][rany] != 'O') {
border[ranx][rany] = '@';
} else {
i--;
}
}
}
void prtbor(char (*border)[lim], int rank, int column) {//打印地图
for (int x = 0; x < rank; x++) {
for (int y = 0; y < column; y++) {
cout << setw(2) << border[x][y];
}
cout << endl;
}
handle();
cout << "Food: " << foodNum << endl << endl;
cout << "Scores: " << results << endl << endl;
cout << "Len: " << len << endl << endl;
handle();
}
int move(char point) {//操作台,接收point来控制蛇的位移方向
switch (point) {
case 'W':
turnUp();//向上位移
break;
case 'S':
turnDown();//向下位移
break;
case 'A':
turnLeft();//向左位移
break;
case 'D':
turnRight();//向右位移
break;
case 'L':
settle();//直接结束
break;
}
return 1;//返回1维系死循环
}
void turnUp() {
if ((head -> snakeX) - 1 == 0 || border[(head -> snakeX) - 1][head -> snakeY] == 'O') {
Sleep(1000);
settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
} else {
tail = head;
tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
tempY = head -> snakeY;
(head -> snakeX)--;//头节点发生变化
if (border[head -> snakeX][head -> snakeY] == '@') {
getFood = true;//吃到食物
}
border[head -> snakeX][head -> snakeY] = 'O';
tail = tail -> next;
while (tail != NULL) {//遍历链表
exLocation(&tempX, &(tail -> snakeX));
exLocation(&tempY, &(tail -> snakeY));
border[tail -> snakeX][tail -> snakeY] = 'O';
tail = tail -> next;
}
if (getFood == true) {//吃到食物
apply();
newone -> snakeX = tempX;
newone -> snakeY = tempY;
border[tempX][tempY] = 'O';
getFood = false;
} else {
border[tempX][tempY] = ' ';//没吃到食物
}
}
}
void turnDown() {
if ((head -> snakeX) + 1 == Rank - 1 || border[(head -> snakeX) + 1][head -> snakeY] == 'O') {
Sleep(1000);
settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
} else {
tail = head;
tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
tempY = head -> snakeY;
(head -> snakeX)++;//头节点发生变化
if (border[head -> snakeX][head -> snakeY] == '@') {
getFood = true;//吃到食物
}
border[head -> snakeX][head -> snakeY] = 'O';
tail = tail -> next;
while (tail != NULL) {//遍历到蛇尾
exLocation(&tempX, &(tail -> snakeX));
exLocation(&tempY, &(tail -> snakeY));
border[tail -> snakeX][tail -> snakeY] = 'O';
tail = tail -> next;
}
if (getFood == true) {//吃到食物
apply();
newone -> snakeX = tempX;
newone -> snakeY = tempY;
border[tempX][tempY] = 'O';
getFood = false;
} else {
border[tempX][tempY] = ' ';//没吃到食物
}
}
}
//标记
void turnRight() {
if ((head -> snakeY) + 1 == Column - 1 || border[head -> snakeX][(head -> snakeY) + 1] == 'O') {
Sleep(1000);
settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
} else {
tail = head;
tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
tempY = head -> snakeY;
(head -> snakeY)++;//头节点发生变化
if (border[head -> snakeX][head -> snakeY] == '@') {
getFood = true;//吃到食物
}
border[head -> snakeX][head -> snakeY] = 'O';
tail = tail -> next;
while (tail != NULL) {
exLocation(&tempX, &(tail -> snakeX));
exLocation(&tempY, &(tail -> snakeY));
border[tail -> snakeX][tail -> snakeY] = 'O';
tail = tail -> next;//遍历到蛇尾
}
if (getFood == true) {//吃到食物
apply();
newone -> snakeX = tempX;
newone -> snakeY = tempY;
border[tempX][tempY] = 'O';
getFood = false;
} else {
border[tempX][tempY] = ' ';//没吃到食物
}
}
}
void turnLeft() {
if ((head -> snakeY) - 1 == 0 || border[head -> snakeX][(head -> snakeY) - 1] == 'O') {
Sleep(1000);
settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
} else {
tail = head;
tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
tempY = head -> snakeY;
(head -> snakeY)--;//头节点发生变化
if (border[head -> snakeX][head -> snakeY] == '@') {
getFood = true;//吃到食物
}
border[head -> snakeX][head -> snakeY] = 'O';
tail = tail -> next;
while (tail != NULL) {//遍历到蛇尾
exLocation(&tempX, &(tail -> snakeX));
exLocation(&tempY, &(tail -> snakeY));
border[tail -> snakeX][tail -> snakeY] = 'O';
tail = tail -> next;
}
if (getFood == true) {//吃到食物
apply();
newone -> snakeX = tempX;
newone -> snakeY = tempY;
border[tempX][tempY] = 'O';
getFood = false;
} else {
border[tempX][tempY] = ' ';//没吃到食物
}
}
}
void apply() {//尾插法
len++;//蛇身长度加一
results += 10;//积分加10,一个食物10分
foodNum--;//食物数量减一
newone = (SNAKE *)malloc(sizeof(SNAKE));//申请新的节点
tail = head;
if (head == NULL) {
head = newone;
head -> next = NULL;
} else {
while (tail -> next != NULL) {
tail = tail -> next;
}
tail -> next = newone;
newone -> next = NULL;
}
if (foodNum == 0) {
Sleep(1000);
settle();//食物剩余为0,直接结束
}
}
void prtlink() {//打印蛇身(打印链表)
tail = head;
while (tail != NULL) {
cout << "Snake " << tail -> snakeX << ' ' << tail -> snakeY << endl;
tail = tail -> next;
}
}
void setSnake() {//初始化蛇的相关属性
tail = head;
for (int i = 1; i <= 3; i++) {
apply();//初始化蛇的长度,长度为3
len = i;
}
tail = head;
for (int i = len; tail != NULL; i--) {
tail -> snakeX = 1;
tail -> snakeY = i;//初始化坐标
border[1][i] = 'O';
tail = tail -> next;
}
}
void settle() {//游戏结算
system("cls");//进入游戏结算画面
for (int i = 3; i > 0; --i) {
cout << "Game Over!" << endl << endl;
cout << "Scores: " << results << endl << endl;
cout << "Len: " << len << endl << endl;
cout << "quit in " << i <<" seconds..." << endl << endl;
Sleep(1000);
system("cls");
}
system("pause");
exit(0);
}
void exLocation(int *a, int *b) {//交换两个变量的值
int mid = *a;
*a = *b;
*b = mid;
}
void handle() {//监听标准输入事件
key = GetKeyState('W');
if (key < 0 && point != 'S') {
point = 'W';
key = 0;
}
key = GetKeyState('S');
if (key < 0 && point != 'W') {
point = 'S';
key = 0;
}
key = GetKeyState('A');
if (key < 0 && point != 'D') {
point = 'A';
key = 0;
}
key = GetKeyState('D');
if (key < 0 && point != 'A') {
point = 'D';
key = 0;
}
key = GetKeyState('L');
if (key < 0) {
settle();
}
}
void instruction() {//说明书
cout << "Hungry Snake" << endl << endl;
cout << "Instruction" << endl;
cout << "w or W -> move up" << endl;//按下w键向上移动
cout << "s or S -> move down" << endl;//按下s键向下移动
cout << "a or A -> move left" << endl;//按下a键向左移动
cout << "d or D -> move right" << endl;//按下d键向右移动
cout << "l or L -> game over" << endl << endl;//按下l键退出游戏
cout << "Continue to play?(Please enter 'y' or 'n')" << endl;
char code = _getch();
if (code == 'y' || code == 'Y') {
system("cls");
} else {
settle();
}
}