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

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

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

图1:


图2:



代码:

#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...
");
   Sleep(5000);
   ShowTheCursor();
   SetColorAndBackground(15, 12);
   ConPrint("This is also a test...
", 23);
   SetColorAndBackground(1, 7);
   ConPrintAt(22, 15, "This is also a test...
", 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);
   }
}


原文地址:https://www.cnblogs.com/zhanghang-BadCoder/p/6476472.html