字符界面的贪吃蛇--链表--C++

时间:2023-03-08 16:17:21
字符界面的贪吃蛇--链表--C++

前天看了下链表,由于平时对链表的使用不多,所以对链表的应用也没什么了解,所以想来想去,就想用链表实现一下贪吃蛇。

下面言归正传,先看效果图,再看代码,其他没有了!

图1:

字符界面的贪吃蛇--链表--C++

图2:

字符界面的贪吃蛇--链表--C++

代码:

#include<iostream.h>
//turbo c++ conio.h==控制控制台相关函数的头文件
#include<conio.h>
#include<time.h>
#include<stdlib.h> typedef char bool; //点
struct Point
{
int x;
int y;
}; //链表的一个节点
//蛇的一个身体
//每个节点包含位置信息,pos:当前位置,lastpos:上一个位置
struct Node
{
Point lastpos;
Point pos;
Node* next;
}; //围墙
struct Wall
{
int UP_BOND;
int RIGHT_BOND;
int DOWN_BOND;
int LEFT_BOND;
int length;
short offset;
}; //蛇
struct Snake
{
Node* head;//蛇的头
Node* tail;//蛇的尾
Node* atefood;//蛇吃下的食物信息
Node* lastfood;//食物中最先被吃下的一个
int length;//蛇的长度
short direction;//蛇移动的方向
int timeperstep;//蛇的移动速度
Wall wall;//围墙
}; //获取当前蛇的尾巴节点
Node* getSnakeTail(Snake* snake)
{
Node* node;
Node* lastnode; lastnode = snake->head;
node = snake->head; while(NULL != node)
{
lastnode = node;
node = node->next;
} return lastnode;
}; //初始化时添加蛇身
void addNode(Snake* snake,Node* node)
{
node->next = snake->head;
snake->head = node;
}; //随机产生食物时,检测食物是否在蛇的身上,即不合法
bool generateCheck(Point* pos,Snake* snake)
{
Node* node;
node = snake->head;
while(node!=NULL)
{
if(pos->x == node->pos.x&&pos->y == node->pos.y)
{
return 1;
}
node = node->next;
}
node = snake->atefood;
while(NULL!=node)
{
if(pos->x == node->pos.x&&pos->y == node->pos.y)
{
return 1;
}
node = node->next;
}
return 0;
}; //产生食物,位置随机
Point* generateFood(Snake* snake)
{
Point* pos;
pos = new Point; srand(time(NULL)); pos->x = rand()%snake->wall.length+snake->wall.offset+2;
pos->y = rand()%snake->wall.length+snake->wall.offset+2; while(generateCheck(pos,snake))
{
pos->x = rand()%50+2;
pos->y = rand()%50+2;
} return pos;
}; //检测蛇是否吃到食物
bool eateFood(Snake* snake,Point* food)
{
if(snake->head->pos.x == food->x && snake->head->pos.y == food->y)
{
return 1;
} return 0;
} //检测蛇是否撞到围墙或自己的身体
bool attackCheck(Snake* snake)
{
switch(snake->direction)
{
case 0://up
if(snake->head->pos.y == snake->wall.UP_BOND)
{
return 1;
}
break;
case 1://right
if(snake->head->pos.x == snake->wall.RIGHT_BOND)
{
return 1;
}
break;
case 2://down
if(snake->head->pos.y == snake->wall.DOWN_BOND)
{
return 1;
}
break;
case 3://left
if(snake->head->pos.x == snake->wall.LEFT_BOND)
{
return 1;
}
break;
} Node* node;
node = snake->head->next;
while(NULL != node)
{
if(snake->head->pos.x == node->pos.x &&
snake->head->pos.y == node->pos.y)
return 1;
node = node->next;
} return 0;
}; //向当前方向移动蛇
bool move(Snake* snake)
{
snake->head->lastpos.x = snake->head->pos.x;
snake->head->lastpos.y = snake->head->pos.y; switch(snake->direction)
{
case 0://up
snake->head->pos.y--;
break;
case 1://right
snake->head->pos.x++;
break;
case 2://down
snake->head->pos.y++;
break;
case 3://left
snake->head->pos.x--;
break;
} Node* nodelast;
Node* node;
node = snake->head; //headmove
gotoxy(node->pos.x,node->pos.y);
cout<<'#'; nodelast = node;
node = node->next; while(node!=NULL)
{
node->lastpos.x = node->pos.x;
node->lastpos.y = node->pos.y; node->pos.x = nodelast->lastpos.x;
node->pos.y = nodelast->lastpos.y; //move
gotoxy(node->pos.x,node->pos.y);
cout<<'*';
nodelast = node;
node = node->next;
} //lastpos
//gotoxy(nodelast->lastpos.x,nodelast->lastpos.y);
gotoxy(snake->tail->lastpos.x,snake->tail->lastpos.y);
cout<<' '; if(1 == attackCheck(snake))
return 0; return 1;
}; //初始化
Snake* init()
{ Snake* snake;
snake = new Snake;
snake->length = 0;
snake->head = NULL; Node* node;
int head_x;
int head_y; head_x = 5;
head_y = 5; for(int i=0;i<3;i++)
{
node = new Node;
node->pos.x = head_x;
node->pos.y = head_y;
node->next = NULL;
head_x++;
snake->length++;
addNode(snake,node);
} snake->tail = getSnakeTail(snake);
snake->lastfood = NULL;
snake->atefood = NULL; snake->direction = 1; snake->timeperstep = 5; snake->wall.length = 20;
snake->wall.offset = 0;
snake->wall.UP_BOND = snake->wall.offset+1;
snake->wall.RIGHT_BOND = snake->wall.length+snake->wall.offset+2;
snake->wall.DOWN_BOND = snake->wall.length+snake->wall.offset+2;
snake->wall.LEFT_BOND = snake->wall.offset+1; //draw wall
int temp = snake->wall.length+2;
for(i = 0;i < temp;i++)
{
//left wall
gotoxy(1,i+1);
cout<<'|';
//right wall
gotoxy(temp,i+1);
cout<<'|';
//up wall
gotoxy(i+1,1);
cout<<'-';
//down wall
gotoxy(i+1,temp);
cout<<'-';
} //draw snake
//head
gotoxy(snake->head->pos.x,snake->head->pos.y);
cout<<'#';
//body
node = snake->head->next;
while(node!=NULL)
{
gotoxy(node->pos.x,node->pos.y);
cout<<'*';
node = node->next;
} //state
gotoxy(snake->wall.RIGHT_BOND+10,snake->wall.UP_BOND+6);
cout<<"SCORES:0"; return snake;
}; //获取蛇吃下的倒数第二个食物
Node* getLastSecFood(Snake* snake)
{
Node* node;
Node* lastnode; lastnode = NULL;
node = snake->atefood; while(NULL != node->next)
{
lastnode = node;
node = node->next;
} return lastnode;
} //吃到一个食物
void addFood(Snake* snake,Node* node)
{
if(NULL == snake->atefood)
snake->lastfood =node; node->next = snake->atefood;
snake->atefood = node; }; //检测食物是否消化,以增长蛇的长度
//当蛇吃下一个食物或多个食物时,食物的位置信息会被存储
//当蛇的尾部移动到相对最先被吃下的食物的位置时,将食物转换为蛇的体长,及食物被消化。
void growSnake(Snake* snake)
{
if(NULL == snake->lastfood)
return; Node* temp = getLastSecFood(snake);
if(NULL == temp)
{
snake->lastfood = snake->atefood;
}
else
{
snake->lastfood = temp->next;
} if(snake->lastfood ->pos.x == snake->tail->lastpos.x &&
snake->lastfood->pos.y == snake->tail->lastpos.y)
{
snake->tail->next = snake->lastfood;
snake->tail = snake->lastfood; if(NULL == snake->atefood->next)
{
snake->lastfood = NULL;
snake->atefood = NULL;
}
else
{
snake->lastfood = getLastSecFood(snake);
snake->lastfood->next = NULL;
}
snake->length++;
} } //清除链表的内存
void clean(Snake* snake)
{
Node* node;
Node* temp;
node = snake->head;
while(NULL != node)
{
temp = node;
node = node->next;
delete temp;
} node = snake->atefood;
while(NULL!=node)
{
temp = node;
node = node->next;
delete temp;
} delete snake;
} //主函数
int main()
{
Snake* snake;
clock_t start;
bool flag;
Point* food;
Node* node;
int score;
short state; while(1)//外层:重新开始游戏
{
clrscr();//清屏 snake = init();
state = 0;
score = 0; //产生第一个食物
food = generateFood(snake);
gotoxy(food->x,food->y);
cout<<'@'; while(1)//第二层:刷新,移动蛇
{
flag = 1;
start = clock();
//while((flag = (clock()-start<=snake->timeperstep))&&!kbhit());
while(1)
{
//如果是时间间隔到达
if(clock() - start >= snake->timeperstep)
{
flag = 0;
break;
}
//如果是有按键按下
if(kbhit())
break;
};
//有按键按下时,根据按下的键改变方向
if(1==flag)
{
char temp = getch();
//cin.ignore(80);
switch(temp)
{
case 'w':
if(2!=snake->direction)
snake->direction = 0;
break;
case 'd':
if(3!=snake->direction)
snake->direction = 1;
break;
case 's':
if(0!=snake->direction)
snake->direction = 2;
break;
case 'a':
if(1!=snake->direction)
snake->direction = 3;
break;
}
}
//如果移动失败,碰到围墙或自身
if(0==move(snake))
{
gotoxy(snake->wall.RIGHT_BOND+10,snake->wall.UP_BOND+7);
cout<<"game over!";
gotoxy(snake->wall.RIGHT_BOND+10,snake->wall.UP_BOND+8);
cout<<"press 'q' to quit and others to continue!";
char temp;
temp = getch(); if('q' == temp)
{
state = 1;
}
else
{
state = 2;
}
break;
} //如果吃到了食物
if(1 == eateFood(snake,food))
{
node = new Node;
node->pos.x = food->x;
node->pos.y = food->y;
node->next = NULL;
addFood(snake,node);
food = generateFood(snake);
gotoxy(food->x,food->y);
cout<<'@';
score+=10;
}
//时刻检测是否增长身体
growSnake(snake);
gotoxy(snake->wall.RIGHT_BOND+10,snake->wall.UP_BOND+6);
cout<<"SCORES:"<<score;
}
if(state == 1)
break;
clean(snake);
} return 1;
};

不能运行主要是clrscr和goto函数,参考:

#include <windows.h>
#include <stdio.h> void ConPrint(char *CharBuffer, int len);
void ConPrintAt(int x, int y, char *CharBuffer, int len);
void gotoXY(int x, int y);
void ClearConsole(void);
void ClearConsoleToColors(int ForgC, int BackC);
void SetColorAndBackground(int ForgC, int BackC);
void SetColor(int ForgC);
void HideTheCursor(void);
void ShowTheCursor(void); int main(int argc, char* argv[])
{
HideTheCursor();
ClearConsoleToColors(15, 1);
ClearConsole();
gotoXY(1, 1);
SetColor(14);
printf("This is a test...\n");
Sleep(5000);
ShowTheCursor();
SetColorAndBackground(15, 12);
ConPrint("This is also a test...\n", 23);
SetColorAndBackground(1, 7);
ConPrintAt(22, 15, "This is also a test...\n", 23);
gotoXY(0, 24);
SetColorAndBackground(7, 1);
return 0;
} //This will clear the console while setting the forground and
//background colors.
void ClearConsoleToColors(int ForgC, int BackC)
{
WORD wColor = ((BackC & 0x0F) << 4) + (ForgC & 0x0F);
//Get the handle to the current output buffer...
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
//This is used to reset the carat/cursor to the top left.
COORD coord = {0, 0};
//A return value... indicating how many chars were written
//not used but we need to capture this since it will be
//written anyway (passing NULL causes an access violation).
DWORD count; //This is a structure containing all of the console info
// it is used here to find the size of the console.
CONSOLE_SCREEN_BUFFER_INFO csbi;
//Here we will set the current color
SetConsoleTextAttribute(hStdOut, wColor);
if(GetConsoleScreenBufferInfo(hStdOut, &csbi))
{
//This fills the buffer with a given character (in this case 32=space).
FillConsoleOutputCharacter(hStdOut, (TCHAR) 32, csbi.dwSize.X * csbi.dwSize.Y, coord, &count); FillConsoleOutputAttribute(hStdOut, csbi.wAttributes, csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
//This will set our cursor position for the next print statement.
SetConsoleCursorPosition(hStdOut, coord);
}
} //This will clear the console.
void ClearConsole()
{
//Get the handle to the current output buffer...
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
//This is used to reset the carat/cursor to the top left.
COORD coord = {0, 0};
//A return value... indicating how many chars were written
// not used but we need to capture this since it will be
// written anyway (passing NULL causes an access violation).
DWORD count;
//This is a structure containing all of the console info
// it is used here to find the size of the console.
CONSOLE_SCREEN_BUFFER_INFO csbi;
//Here we will set the current color
if(GetConsoleScreenBufferInfo(hStdOut, &csbi))
{
//This fills the buffer with a given character (in this case 32=space).
FillConsoleOutputCharacter(hStdOut, (TCHAR) 32, csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
FillConsoleOutputAttribute(hStdOut, csbi.wAttributes, csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
//This will set our cursor position for the next print statement.
SetConsoleCursorPosition(hStdOut, coord);
}
} //This will set the position of the cursor
void gotoXY(int x, int y)
{
//Initialize the coordinates
COORD coord = {x, y};
//Set the position
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
} //This will set the forground color for printing in a console window.
void SetColor(int ForgC)
{
WORD wColor;
//We will need this handle to get the current background attribute
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi; //We use csbi for the wAttributes word.
if(GetConsoleScreenBufferInfo(hStdOut, &csbi))
{
//Mask out all but the background attribute, and add in the forgournd color
wColor = (csbi.wAttributes & 0xF0) + (ForgC & 0x0F);
SetConsoleTextAttribute(hStdOut, wColor);
}
} //This will set the forground and background color for printing in a console window.
void SetColorAndBackground(int ForgC, int BackC)
{
WORD wColor = ((BackC & 0x0F) << 4) + (ForgC & 0x0F);;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), wColor);
} //Direct console output
void ConPrint(char *CharBuffer, int len)
{
DWORD count;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), CharBuffer, len, &count, NULL);
} //Direct Console output at a particular coordinate.
void ConPrintAt(int x, int y, char *CharBuffer, int len)
{
DWORD count;
COORD coord = {x, y};
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hStdOut, coord);
WriteConsole(hStdOut, CharBuffer, len, &count, NULL);
} //Hides the console cursor
void HideTheCursor()
{
CONSOLE_CURSOR_INFO cciCursor;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); if(GetConsoleCursorInfo(hStdOut, &cciCursor))
{
cciCursor.bVisible = FALSE;
SetConsoleCursorInfo(hStdOut, &cciCursor);
}
} //Shows the console cursor
void ShowTheCursor()
{
CONSOLE_CURSOR_INFO cciCursor;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); if(GetConsoleCursorInfo(hStdOut, &cciCursor))
{
cciCursor.bVisible = TRUE;
SetConsoleCursorInfo(hStdOut, &cciCursor);
}
}