Linux学习篇——基于C语言使用结构体、链表实现贪吃蛇

导读:本篇文章讲解 Linux学习篇——基于C语言使用结构体、链表实现贪吃蛇,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、需要用的头文件以及Linux图形库函数、数据初始化

#include<stdio.h>
#include<curses.h>
#include<stdlib.h>

// 用于调整蛇移动方向,防止蛇移动不合理
#define UP    1
#define DOWN -1
#define LEFT -2
#define RIGHT 2
struct Snake{

	int hang; //行
	int lie;  //列
	struct Snake *next; //节点指针
	
};
struct Snake *head = NULL; 	//头节点
struct Snake *wei = NULL;  	//尾巴节点
int direction; 	// 方向
int key; 	// 接受方向键的指令
struct Snake food; 	//控制食物
int cnt =0; 	//进食分数
(1)关于curses.h图形库函数

1.初始化界面

void initcurses()
{
	initscr();
	keypad(stdscr,1);
	noecho();
}
  • initscr() :curses程式必备先呼叫函数,作用是系统将根据终端机的形态并启动 curses模式
  • keypad(stdscr,1) :第一个参数stdscr为标准输出荧幕,第二个参数为1(TRUE)/ 0(False),使用keypad目的是为了使用方向键指令
  • noecho() : 防止界面产生乱码,图形界面破坏的情况

2.相关函数使用

  1. getch() :用于从键盘读取一个字元,原理和getchar相似
  2. endwin() :** 用于结束程式,关闭上述的curses模式
  3. move(x,y) : 用于在贪吃蛇吃完食物 / 死亡 需要将地图重新生成并且覆盖旧地图,而不是换行重新打印一张新地图
  4. refresh():用于刷新界面函数,并输出到屏幕

3.使用到curses中的宏建

  • KEY_UP 0403 ↑
  • KEY_DOWN 0402 ↓
  • KEY_LEFT 0404 ←
  • KEY_RIGHT 0405 →

二、地图制作

//该地图是20行20列地图
void Map()
{
	int hang;
	int lie;
	move(0,0);	//刷新地图,覆盖旧地图
	for(hang=0 ; hang<20 ; hang++)
	{
		if(hang==0)
		{
			for(lie=0 ;lie<20;lie++)
			{
				printw("--");
			}
			printw("\n");
		}
		else if(hang>0 && hang <19)
		{
			for(lie=0 ;lie<=20;lie++)
			{
				if(lie ==0 || lie==20)
				{
					printw("|");
				}
				else if(calcSnake(hang,lie)) //生成蛇的身体
				{
					printw("[]");
				}
				else if(Eatfood(hang,lie) ) //生成蛇的食物
				{
					printw("$$");
				}
				else{
					printw("  ");
				}
			}
			printw("\n");	
		}
		else
		{
			for(lie=0 ; lie<20 ; lie++)
			{
				printw("--");
			}
			// 调试信息如下,可以设计添加其他
			printw("\n");
			printw("The Map is by LiuZheng sir make\n"); // 地图制作者
			printw("The food hang = %d,food lie = %d\n",food.hang,food.lie); //记录蛇食物坐标
			printw("Alway eat food score = %d\n",cnt); // 记录蛇吃了多少食物
		}

	}
}

三、蛇身链表的初始化生成

//用于在initsnake中指定方向生成贪吃蛇身体
int  calcSnake(int h,int l)
{
	struct Snake *p;
	p = head;
	while(p != NULL)
	{
		if(p->hang ==h &&p->lie == l)
		{
			return 1;
		}
		p = p->next;
	}
	return 0;

}
//贪吃蛇身体初始化
void initsnake()
{
	struct Snake *p;
		
	direction = RIGHT;	//规定刚开始蛇朝着右方向移动

	if(head !=NULL)  //用于清除蛇死亡后残留的数据,清理内存
	{
		p = head;
		head = head->next;
		free(p);
	}

	createfood(); //生成食物
	
	head = (struct Snake*)malloc(sizeof(struct Snake));
	
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
	wei = head;
	
	//生成蛇的身子
	AddNode();
	AddNode();
}

四、关于贪吃蛇移动,身体操作

//贪吃蛇身子节点的添加
void AddNode()
{
	struct Snake *new;
	new = (struct Snake*)malloc(sizeof(struct Snake));

	switch(direction)//控制方向上移动
	{
		case DOWN:
			new->hang = wei->hang+1;
			new->lie  = wei->lie;
			break;
		case UP:
			new->hang = wei->hang-1;
			new->lie  = wei->lie;
			break;
		case LEFT:
			new->hang = wei->hang;
			new->lie  = wei->lie-1;
			break;
		case RIGHT:
			new->hang = wei->hang;
			new->lie  = wei->lie+1;
			break;
	}

	//让新的节点代替之前的尾巴,成为新的尾巴
	new->next = NULL;
	wei->next = new;
	wei = new; 
}
//删除贪吃蛇身体节点
void DeleteNode()
{
	struct Snake *p =head;
	head = head->next;
	free(p);
}
//控制贪吃蛇移动的函数
void moveSnake()
{
	AddNode();
	if(Eatfood(wei->hang,wei->lie) == 1) //如果吃到食物,就重新生成新的食物
	{
		
		createfood();
		cnt++;	//统计贪吃蛇进食分数情况
	}
	else
	{
		DeleteNode();
	}
	if(ifsnakedeath()) //检查移动中贪吃蛇是否撞墙或者自己吃自己,导致死亡
	{
		initsnake(); //死亡之后需要重新生成贪吃蛇
		cnt=0;
	}
}
//查看贪吃蛇是否撞墙死亡 / 自己吃自己
int ifsnakedeath()
{

	struct Snake *p =head;
	
	if(wei->hang == 0||wei->lie ==0||wei->hang ==19||wei->lie== 19) //检查撞墙
	{
			return 1;
	}
	
	while(p->next != NULL)
	{
		if(p->hang == wei->hang && p->lie ==wei->lie) // 检查是否自杀
		{
			return 1;
		}
		p = p->next;
	}
	return 0;
}

总结:

无论贪吃蛇朝着那个方向移动,都是头节点删除,尾巴节点添加,从而达到身体移动的目的

五、关于贪吃蛇的食物解析

//用于确定在随机生成的食物坐标中,在地图上打印该食物
int Eatfood(int h,int l)
{
	if(food.hang == h && food.lie ==l)
	{
		return 1;
	}
	return 0;
}
void createfood()
{
	int x = rand()%20; //使用rand()函数随机生成一个坐标值
	int y = rand()%20; //使用rand()函数随机生成一个坐标值
	
	if(x==0 || x==19 || y==0 || y==19) //防止食物生成到地图边缘或者离开地图
	{
		 x = rand()%20;
		 y = rand()%20;
	}

	food.hang = x;
	food.lie  = y;
}

六、控制贪吃蛇移动的方向位置

标注:

由于需要地图的刷新,贪吃蛇方向的移动和改变、食物的生成,系统一个线程是忙不过来的,因此我们需要引进线程的概念创建多个线程来分布执行这些命令操作

线程创建格式:

//创建线程 t1、t2

	pthread_t t1;
	pthread_t t2;
	
	pthread_create(&t1,NULL,refreshSnake,NULL); 	 //t1进程实现贪吃蛇移动
	pthread_create(&t2,NULL,Changedirection,NULL);	//t2进程实现贪吃蛇改变方向位置
//用该进程进行贪吃蛇移动
void refreshSnake()
{
	while(1)
	{
		moveSnake();
		Map();
		refresh(); //刷新界面
		usleep(40000); //控制贪吃蛇移动速度,这里是微秒单位
	}
}


//防止贪吃蛇位置移动不合理
void Turndirection(int dir)
{
	if(abs(direction) != abs(dir))
	{
		direction = dir;
	}
}
void Changedirection()
{
	while(1)
	{
		key = getch();//获取方向键控制贪吃蛇移动指令
		switch(key) //发送贪吃蛇方向移动指令
		{
			case KEY_DOWN: //向上
				Turndirection(DOWN);
				break;
			
			case KEY_UP: //向下
				Turndirection(UP);
				break;
			case KEY_LEFT: //向左
				Turndirection(LEFT);
				break;
			case KEY_RIGHT: //向右
				Turndirection(RIGHT);
				break;
		}

	}
}

七、主函数

int main()
{
	pthread_t t1; 	//建立线程t1
	pthread_t t2;	//建立线程t2

	initcurses();
	initsnake();
	Map();

	pthread_create(&t1,NULL,refreshSnake,NULL);
	pthread_create(&t2,NULL,Changedirection,NULL);

	while(1);		 //防止该进程不退出
	getch();
	endwin();
	return 0;
}

八、代码实现

#include<stdio.h>
#include<curses.h>
#include<stdlib.h>

#define UP    1
#define DOWN -1
#define LEFT -2
#define RIGHT 2

void initcurses()
{
	initscr();
	keypad(stdscr,1);
	noecho();

}

struct Snake{

	int hang;
	int lie;
	struct Snake *next;
	
};

struct Snake *head = NULL;
struct Snake *wei = NULL;
int direction;
int key;
struct Snake food;
int cnt =0;

int  calcSnake(int h,int l)
{
	struct Snake *p;
	p = head;
	while(p != NULL)
	{
		if(p->hang ==h &&p->lie == l)
		{
			return 1;
		}
		p = p->next;
	}
	return 0;

}

int Eatfood(int h,int l)
{
	if(food.hang == h && food.lie ==l)
	{
		return 1;
	}
	return 0;
}

void createfood()
{
	int x = rand()%20;
	int y = rand()%20;
	
	if(x==0 || x==19 || y==0 || y==19)
	{
		 x = rand()%20;
		 y = rand()%20;
	}

	food.hang = x;
	food.lie  = y;
}

void Map()
{
	int hang;
	int lie;
	move(0,0);
	for(hang=0 ; hang<20 ; hang++)
	{
		if(hang==0)
		{
			for(lie=0 ;lie<20;lie++)
			{
				printw("--");
			}
			printw("\n");
		}
		else if(hang>0 && hang <19)
		{
			for(lie=0 ;lie<=20;lie++)
			{
				if(lie ==0 || lie==20)
				{
					printw("|");
				}
				else if(calcSnake(hang,lie))
				{
					printw("[]");
				}
				else if(Eatfood(hang,lie) )
				{
					printw("$$");
				}
				else{
					printw("  ");
				}
			}
			printw("\n");	
		}
		else
		{
			for(lie=0 ; lie<20 ; lie++)
			{
				printw("--");
			}
			printw("\n");
			printw("The Map is by LiuZheng sir make\n");
			printw("The food hang = %d,food lie = %d\n",food.hang,food.lie);
			printw("Alway eat food score = %d\n",cnt);
		}

	}
}

void AddNode()
{
	struct Snake *new;
	new = (struct Snake*)malloc(sizeof(struct Snake));

	switch(direction)
	{
		case DOWN:
			new->hang = wei->hang+1;
			new->lie  = wei->lie;
			break;
		case UP:
			new->hang = wei->hang-1;
			new->lie  = wei->lie;
			break;
		case LEFT:
			new->hang = wei->hang;
			new->lie  = wei->lie-1;
			break;
		case RIGHT:
			new->hang = wei->hang;
			new->lie  = wei->lie+1;
			break;
	}

	
	new->next = NULL;
	wei->next = new;
	wei = new;
}

void DeleteNode()
{
	struct Snake *p =head;
	head = head->next;
	free(p);
}


void initsnake()
{
	struct Snake *p;
		
	direction = RIGHT;	

	if(head !=NULL)
	{
		p = head;
		head = head->next;
		free(p);
	}

	createfood();
	head = (struct Snake*)malloc(sizeof(struct Snake));
	
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
	wei = head;
	
	AddNode();
	AddNode();
}

void moveSnake()
{
	AddNode();
	if(Eatfood(wei->hang,wei->lie) == 1)
	{
		
		createfood();
		cnt++;	
	}
	else
	{
		DeleteNode();
	}
	if(ifsnakedeath())
	{
		initsnake();
		cnt=0;
	}
}

int ifsnakedeath()
{

	struct Snake *p =head;
	
	if(wei->hang == 0||wei->lie ==0||wei->hang ==19||wei->lie== 19)
	{
			return 1;
	}
	
	while(p->next != NULL)
	{
		if(p->hang == wei->hang && p->lie ==wei->lie)
		{
			return 1;
		}
		p = p->next;
	}
	return 0;
}

void refreshSnake()
{
	while(1)
	{
		moveSnake();
		Map();
		refresh();
		usleep(40000);
	}
}
void Turndirection(int dir)
{
	if(abs(direction) != abs(dir))
	{
		direction = dir;
	}
}
void Changedirection()
{
	while(1)
	{
		key = getch();
		switch(key)
		{
			case KEY_DOWN:
				Turndirection(DOWN);
				break;
			
			case KEY_UP:
				Turndirection(UP);
				break;
			case KEY_LEFT:
				Turndirection(LEFT);
				break;
			case KEY_RIGHT:
				Turndirection(RIGHT);
				break;
		}

	}
}
int main()
{
	pthread_t t1;
	pthread_t t2;

	initcurses();
	initsnake();
	Map();

	pthread_create(&t1,NULL,refreshSnake,NULL);
	pthread_create(&t2,NULL,Changedirection,NULL);

	while(1);		
	getch();
	endwin();
	return 0;
}

哪里不好,有待提高的地方还望大佬指出,共同进步!感谢你的留言!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68486.html

(0)
newbe的头像newbebm

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!