c語言實現貪吃蛇教程

效果圖如圖 

首先發現組成元素是“實心方塊”我們可以百度 也可以在我這裏直接複製 ▇ 進編譯環境 (這個方塊是兩個字節這個很重要)

完成這個小程序基本上我們分以下幾步

1.完成所有靜態的元素(四周的方塊界線)

2.繪製蛇

3.使蛇吃東西

 

下面分佈進行實現

首先完成第一步

/*****************************************************************頭文件**********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
/*****************************************************************函數聲明*********************************************************/
void muban();
void Pos(int x, int y);
/*****************************************************************自定義函數*******************************************************/
void Pos(int x, int y)//設置光標位置,從哪裏開始輸出
{
    COORD pos;//表示一個字符在控制檯屏幕上的座標,左上角(0,0)
    HANDLE hOutput;
    pos.X = x;
    pos.Y = y;
    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回標準的輸入、輸出或錯誤的設備的句柄,也就是獲得輸入、輸出/錯誤的屏幕緩衝區的句柄
    SetConsoleCursorPosition(hOutput, pos);
}

void muban()
{	
	int i;
	for(i=0;i<=60;i+=2)//方塊水平方向佔兩個字節
	{
       Pos(i,0);
	   printf("▇");//上行
	   Pos(i,26);
	   printf("▇");//下行
	}
	for(i=0;i<=25;i+=1)//方塊豎直方向佔1個字節
	{
       Pos(0,i);//左列
	   printf("▇");
	   Pos(60,i);//右列
	   printf("▇");
	}
}
/*******************************************************************主函數************************************************************/
int main()
{
    muban();
	return 0;
}

下面我們來描繪蛇,這裏要用到結構體指針和鏈表

主要思想是 一個▇是蛇的一段 然後用鏈表將它們鏈接起來

具體的鏈表和結構體指針可以看下面的文章:(只用掌握最基本的用法即可)

結構體https://blog.csdn.net/zhanghow/article/details/53463825

鏈表https://blog.csdn.net/govshell/article/details/63274614

指針https://blog.csdn.net/cyh183269855/article/details/52278941(這篇文章講的很棒,配合結構體食用)

接下來繼續擼代碼

void initSnake()
{
  snake *tail;//尾指針
  snake *head;//頭指針
  tail = (snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用tail指向這個結構體
  tail ->x=30;//因爲實心方塊寬度爲2個單位長度,所以必須爲偶數
  tail ->y=10;
  tail ->next=NULL;
  for(i = 0;i<4;i++)
  {
    head=(snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用head指向這個結構體
	head->next=tail;//將
	tail=head;//將尾指針傳向下一個頭指針
  }
}

比較難理解的是這段尾插法,如果不會的可以去看這篇https://blog.csdn.net/viafcccy/article/details/84502334

這裏下面的代碼

typedef struct Snake//相當於蛇一個節點
{
	int x;//橫座標
	int y;//縱座標
    struct Snake *next;
}snake;

等價於

struct Snake
{
  int x;//橫座標
  int y;//縱座標
  struct Snake *next;
};
struct Snake snake;

關於typedef看這篇https://blog.csdn.net/viafcccy/article/details/84204456

下面完成蛇身的打印

其中第一遍的代碼是這樣

void initSnake()
{
  int i;
  snake *tail;//尾指針
  snake *head;//頭指針
  tail = (snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用tail指向這個結構體
  tail ->x=30;//因爲實心方塊寬度爲2個單位長度,所以必須爲偶數
  tail ->y=10;
  tail ->next=NULL;
  for(i = 0;i<4;i++)
  {
    head=(snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用head指向這個結構體
	head->next=tail;//鏈接成鏈
	head->x=30+2*i;//下一節點的位置
	head->y=10;
	tail=head;//將尾指針傳向下一個頭指針
  }

 

結果是

我們錯在原來蛇是從右向左打印了8個單位 但是應爲固定輸出press any keyt continues將三個實心方塊擋住了,於是將代碼修改

/*****************************************************************頭文件**********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
/*****************************************************************函數聲明*********************************************************/
void muban();//打印四周的邊界
void Pos(int x, int y);//設置光標輸出位置
void initSnake();//初始化蛇身,將結構體中的座標讀取打印實心方塊
/*****************************************************************結構體***********************************************************/
typedef struct Snake//將蛇身的位置存入結構體,相當於蛇身上的一個實心方塊
{
  int x;//橫座標
  int y;//縱座標
  struct Snake *next;
}snake;
/*****************************************************************自定義函數*******************************************************/
void Pos(int x, int y)//設置光標位置,從哪裏開始輸出
{
    COORD pos;//表示一個字符在控制檯屏幕上的座標,左上角(0,0)
    HANDLE hOutput;
    pos.X = x;
    pos.Y = y;
    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回標準的輸入、輸出或錯誤的設備的句柄,也就是獲得輸入、輸出/錯誤的屏幕緩衝區的句柄
    SetConsoleCursorPosition(hOutput, pos);
}

void muban()
{	
	int i;
	for(i=0;i<=60;i+=2)//方塊水平方向佔兩個單位
	{
       Pos(i,0);
	   printf("▇");//上行
	   Pos(i,26);
	   printf("▇");//下行
	}
	for(i=0;i<=25;i+=1)//方塊垂直方向佔1個單位
	{
       Pos(0,i);//左列
	   printf("▇");
	   Pos(60,i);//右列
	   printf("▇");
	}
}

void initSnake()
{
  int i;
  snake *tail;//尾指針
  snake *head;//頭指針
  tail = (snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用tail指向這個結構體
  tail ->x=30;//因爲實心方塊寬度爲2個單位長度,所以必須爲偶數
  tail ->y=10;
  tail ->next=NULL;
  for(i = 1;i<=4;i++)
  {
    head=(snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用head指向這個結構體
	head->next=tail;//鏈接成鏈
	head->x=30-2*i;//下一節點的位置
	head->y=10;
	tail=head;//將尾指針傳向下一個頭指針
  }
  //遍歷打印出來
  while(tail->next!=NULL)
  {
  Pos(tail->x,tail->y);
  printf("▇");
  tail = tail->next;
  }
}

/*******************************************************************主函數************************************************************/
int main()
{
    muban();
	initSnake();
	return 0;
}

這樣我用四個永遠緊緊相連的方塊構成了蛇的身體,下面我們來生成食物

思路是隨機生成兩個隨機數使x,y分別滿足在邊界內,但是x有一個要求就是要是偶數這個和實心方塊兩個字節有關,我們要避免下圖的情況產生

void creatFood()//創建食物
{
	snake *food;//創造一個食物
	food=(snake*)malloc(sizeof(snake));
	srand((unsigned int)time(NULL));//隨着時間變化,產生不一樣種子,就會得到沒規律的食物
	while(food->x%2!=0)
	{
		food->x=rand()%56+2;
	}
	food->y=rand()%23+1;

	//上面雖然解決了食物不會出現在城牆裏,沒有考慮食物出現在蛇本身裏面
	p=head;//用p來遍歷
    while(p!=NULL)//解決食物出現在蛇本身
	{
	        if(food->x==p->x&&food->y==p->y)
			{
				free(food);
				creatFood();
			}
			p=p->next;
	}
	Pos(food->x,food->y);
	food1=food;//food1用來標記的作用
	printf("▇");
}

關於產生隨機數可以看這篇文章https://blog.csdn.net/viafcccy/article/details/84311336

於是我們下一步就是要做到我們按鍵蛇會動我們使用頭文件windows.h,具體看這篇https://blog.csdn.net/viafcccy/article/details/84262393

這裏介紹一下遍歷 就是將所有鏈表中的數據訪問一遍 通常是打印出來

蛇的移動主要通過獲取鍵值,然後如下操作

但是我們要將1的位置打印空格 否則實心方塊不會消失 但是這樣打印時會有光標始終跟着蛇尾 因此需要在外面打印一個東西讓光標絕大部分時間在外面 只有一個瞬間在蛇尾我們就不會看到了 

 void snakeMove()
 {
  snake *nexthead;
  nexthead=(snake*)malloc(sizeof(snake));
  if(status=='R')
  {
      nexthead->x=head->x+2;
	  nexthead->y=head->y;
	  if(nexthead->x==food1->x&&nexthead->y==food->y)
	  {
	      nexthead->next=head;
          head=nexthead;
		  p=head;//遍歷
          while(p!=NULL)
		  {
		      Pos(p->x,p->y);
			  printf("▇");
			  p=p->next;
		  }
	  }
	  else
	  {
	      nexthead->next=head;
          head=nexthead;
		  p=head;//遍歷
		  while(p->next->next!=NULL)
		  {
		      Pos(p->x,p->y);
			  printf("▇");
			  p=p->next;
		  }
		  Pos(p->next->x,p->next->y);
		  printf(" ");//會帶來一個光標閃爍
		  Pos(70,20);
		  printf("您的分數是:%d",score);
		  free(p->next);
		  p->next=NULL;
	  }
	  		if(status=='L')//向左走
	{
		nexthead->x=head->x-2;
		nexthead->y=head->y;
		if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
		{
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}//吃掉了食物得創造
			score=score+add;
			creatFood();

		}
		else//沒有食物
        {
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p->next->next!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}
			Pos(p->next->x,p->next->y);
			printf("  ");
			Pos(70,20);//解決辦法
			printf("您的分數是:%d",score);
			free(p->next);
			p->next=NULL;
		}
		}
		if(status=='U')//向上走
	{
		nexthead->x=head->x;
		nexthead->y=head->y-1;
		if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
		{
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}//吃掉了食物得創造
			score=score+add;
			creatFood();

		}
		else//沒有食物
        {
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p->next->next!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}
			Pos(p->next->x,p->next->y);
			printf("  ");
			Pos(70,20);//解決辦法
			printf("您的分數是:%d",score);
			free(p->next);
			p->next=NULL;
		}
		}
		if(status=='D')//向下走
	{
		nexthead->x=head->x;
		nexthead->y=head->y+1;
		if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
		{
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}//吃掉了食物得創造
			score=score+add;
			creatFood();

		}
		else//沒有食物
        {
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p->next->next!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}
			Pos(p->next->x,p->next->y);
			printf("  ");
			Pos(70,20);//解決辦法
			printf("您的分數是:%d",score);
			free(p->next);
			p->next=NULL;
		}
		}
	
	Sleep(sleepTime);//蛇移動的速度,裏面是毫秒,越大速度越慢
	status=reDirection();//判別下方向先
	if(crossWall()==1||eatSelf()==1)
		//exit(0);//直接把程序關閉了
       endleap=1;
	return endleap;
	
}

int crossWall()//判斷蛇有沒穿透牆
{
	if(head->x==0||head->y==0||head->x==60||head->y==25)
		leap=1;
	return leap;
}
int eatSelf()//判斷是否咬到了自己
{
	snake *q;//遍歷的
	q=head->next;
	while(q!=NULL)
	{
		if(q->x==head->x&&head->y==q->y)
			leap=1;
		q=q->next;
	}
	return leap;
}

下面我們要解決gameover的判斷

貪吃蛇結束就兩種情況

1)撞到牆壁

2)咬到自己

int crossWall()//判斷蛇有沒撞牆
{
	if(head->x==0||head->y==0||head->x==60||head->y==25)
		leap=1;
	return leap;
}
int eatSelf()//判斷是否咬到了自己
{
	snake *q;//遍歷的
	q=head->next;
	while(q!=NULL)
	{
		if(q->x==head->x&&head->y==q->y)
			leap=1;
		q=q->next;
	}
	return leap;
}

最後經過優化的源代碼在這裏

/*********************************************頭文件************************************/
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
/*********************************************函數聲明**********************************/
void Pos(int x, int y);//光標位置設定
void muban();//打印模板
void initSnake();//蛇身的初始化
void creatFood();//創建食物
char reDirection();//識別方向
int snakeMove();//蛇移動
int crossWall();//不能穿牆
int eatSelf();//不能吃自己
/**********************************************結構體***********************************/
typedef struct Snake//相當於蛇一個節點
{
	int x;//橫座標
	int y;//縱座標
    struct Snake *next;
}snake;
/***********************************************全局變量********************************/
snake *head;//頭指針
snake *p;//用來遍歷
snake *food1;//用來標記的
char status='L';//初始方向的狀態,解決開始會動的問題
int score=0;//分數
int add=10;//一個食物的分
int leap=0;//用來標誌是否結束,0沒有,1代表蛇死了代表結束了
int endleap=0;//結束標誌 1就是結束
int sleepTime=500;
/***********************************************自定義函數******************************/
void initSnake()//蛇身初始化,給定一個長度,用結構體表示是蛇的骨架,真正要顯示出來是打印▇
{
	int i;
	snake *tail;//尾指針
	tail=(snake*)malloc(sizeof(snake));//第一個節點/頭結點
	tail->x=30;//2的倍數,因爲方塊的長是兩個單位
	tail->y=10;//1個單位
	tail->next=NULL;
	for(i=1;i<=4;i++)//尾插法
	{
		head=(snake*)malloc(sizeof(snake));//申請一個節點
		head->next=tail;//連接成鏈
		head->x=30-2*i;//下一個節點的位置
		head->y=10;
		tail=head;
	} 
	//遍歷打印出來
	while(tail!=NULL)
	{
		Pos(tail->x,tail->y);
		printf("▇");
		tail=tail->next;
	}


}

char reDirection()//識別用戶按下的鍵值  保留方向值
{
	if(GetAsyncKeyState(VK_F7))//熱鍵 
	{
		if(sleepTime>300)//最多減到300
		{
			sleepTime-=50;//每次減50
			add++;//每次食物加1分
		}
	}
	if(GetAsyncKeyState(VK_F8))
	{
		if(sleepTime<800)//最多加到800
		{
			sleepTime+=50;//每次加50
			add--;//每次食物減1分
		}
	}
	if(GetAsyncKeyState(VK_UP)&&status!='D')
		status='U';
	if(GetAsyncKeyState(VK_DOWN)&&status!='U')
		status='D';
	if(GetAsyncKeyState(VK_LEFT)&&status!='R')
		status='L';
	if(GetAsyncKeyState(VK_RIGHT)&&status!='L')
		status='R';
		return status;
}
void Pos(int x, int y)//設置光標位置,從哪裏開始輸出
{
    COORD pos;//表示一個字符在控制檯屏幕上的座標,左上角(0,0)
    HANDLE hOutput;
    pos.X = x;
    pos.Y = y;
    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回標準的輸入、輸出或錯誤的設備的句柄,也就是獲得輸入、輸出/錯誤的屏幕緩衝區的句柄
    SetConsoleCursorPosition(hOutput, pos);
}


void creatFood()//創建食物
{
	snake *food;//創造一個食物
	food=(snake*)malloc(sizeof(snake));
	srand((unsigned int)time(NULL));//隨着時間變化,產生不一樣種子,就會得到沒規律的食物
	while(food->x%2!=0)
	{
		food->x=rand()%56+2;
	}
	food->y=rand()%23+1;

	//上面雖然解決了食物不會出現在城牆裏,沒有考慮食物出現在蛇本身裏面
	p=head;//用p來遍歷
    while(p!=NULL)//解決食物出現在蛇本身
	{
	        if(food->x==p->x&&food->y==p->y)
			{
				free(food);
				creatFood();
			}
			p=p->next;
	}
	Pos(food->x,food->y);
	food1=food;//food1用來標記的作用
	printf("▇");
	Pos(70,20);//解決有光標閃爍的辦法
	printf("您的分數是:%d",score);
}
void muban()
{	
	int i;
	for(i=0;i<=60;i+=2)//方塊水平方向佔兩個單位
	{
       Pos(i,0);
	   printf("▇");//上行
	   Pos(i,26);
	   printf("▇");//下行
	}
	for(i=0;i<=25;i+=1)//方塊垂直方向佔1個單位
	{
       Pos(0,i);//左列
	   printf("▇");
	   Pos(60,i);//右列
	   printf("▇");
	}
}

int snakeMove()
{
	snake *nexthead;
	nexthead=(snake*)malloc(sizeof(snake));
	if(status=='R')//向右走
	{
		nexthead->x=head->x+2;
		nexthead->y=head->y;
		if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
		{
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}//吃掉了食物得創造
			score=score+add;
			creatFood();

		}
		else//沒有食物
        {
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p->next->next!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}
			Pos(p->next->x,p->next->y);
			printf("  ");//會帶來一個光標閃爍
			Pos(70,20);//解決辦法
			printf("您的分數是:%d",score);
			free(p->next);
			p->next=NULL;
		}
	}
		if(status=='L')//向左走
	{
		nexthead->x=head->x-2;
		nexthead->y=head->y;
		if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
		{
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}//吃掉了食物得創造
			score=score+add;
			creatFood();

		}
		else//沒有食物
        {
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p->next->next!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}
			Pos(p->next->x,p->next->y);
			printf("  ");
			Pos(70,20);//解決辦法
			printf("您的分數是:%d",score);
			free(p->next);
			p->next=NULL;
		}
		}
		if(status=='U')//向上走
	{
		nexthead->x=head->x;
		nexthead->y=head->y-1;
		if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
		{
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}//吃掉了食物得創造
			score=score+add;
			creatFood();

		}
		else//沒有食物
        {
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p->next->next!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}
			Pos(p->next->x,p->next->y);
			printf("  ");
			Pos(70,20);//解決辦法
			printf("您的分數是:%d",score);
			free(p->next);
			p->next=NULL;
		}
		}
		if(status=='D')//向下走
	{
		nexthead->x=head->x;
		nexthead->y=head->y+1;
		if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
		{
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}//吃掉了食物得創造
			score=score+add;
			creatFood();

		}
		else//沒有食物
        {
			nexthead->next=head;
			head=nexthead;
            p=head;//p用來從頭遍歷,打印方塊
			while(p->next->next!=NULL)
			{
				Pos(p->x,p->y);
				printf("▇");
				p=p->next;
			}
			Pos(p->next->x,p->next->y);
			printf("  ");
			Pos(70,20);//解決辦法
			printf("您的分數是:%d",score);
			free(p->next);
			p->next=NULL;
		}
		}
	
	Sleep(sleepTime);//蛇移動的速度,裏面是毫秒,越大速度越慢
	status=reDirection();//判別下方向先
	if(crossWall()==1||eatSelf()==1)
		//exit(0);//直接把程序關閉了
       endleap=1;
	return endleap;
	
}

int crossWall()//判斷蛇有沒穿透牆
{
	if(head->x==0||head->y==0||head->x==60||head->y==25)
		leap=1;
	return leap;
}
int eatSelf()//判斷是否咬到了自己
{
	snake *q;//遍歷的
	q=head->next;
	while(q!=NULL)
	{
		if(q->x==head->x&&head->y==q->y)
			leap=1;
		q=q->next;
	}
	return leap;
}
//打印食物的時候會出現光標,解決辦法就是引開它
/*********************************************主函數***********************************/
int main()
{
	muban();//打印模板
	initSnake();//初始化蛇
	creatFood();//創建食物
	while(1)//死循環,讓蛇一直動起來,直到蛇死了
	{
		if(snakeMove()==1)//判斷是否結束
		{
			Pos(70,23);
			printf("蛇死了");
            system("pause");//用來暫停
			Pos(70,24);//解決press any key  to continue   在該地點打印  大家試下
			break;
		}
		
	}
	printf("是否繼續遊戲,y or n:");//y 繼續
	if(getch()=='y')//重新遊戲
	{
		//蛇一開始就死了,因爲全局變量沒有恢復原值,仍然保留上一局的值
		status='L';//初始方向的狀態,解決開始會動的問題
		score=0;//分數
		add=10;//一個食物的分
		leap=0;//用來標誌是否結束,0沒有,1代表蛇死了代表結束了
		endleap=0;//結束標誌 1就是結束
		sleepTime=500;
		system("cls");//清理屏幕
		main();//自己調用自己 看不一樣的編譯器,vc6.0允許調用自己
	}
	if(getch()=='n')
	{
		Pos(70,25);//定一個位置,再打印press 
		exit(0);//退出程序
	}
	
	return 0;
}

//蛇的速度變化,每個食物的分數增加
//是否繼續遊戲
//按鍵的作用

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章