1. 程式人生 > >字元介面的貪吃蛇--連結串列--C++

字元介面的貪吃蛇--連結串列--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...\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);
   }
}