C语言小游戏-贪吃蛇

贪吃蛇

ncurse的使用

需要包含头文件#include<curses.h>

ncurse的介绍

C语言的ncurses库是一个用于控制终端屏幕的库。它提供了一组函数和数据结构,可以用于在终端上创建文本界面和图形界面。

ncurses库允许你在终端上进行以下操作:

  • 控制光标的位置
  • 设置文本颜色和背景色
  • 在屏幕上绘制文本和图形
  • 处理键盘输入和鼠标事件
  • 创建菜单和对话框等用户界面元素

使用ncurses库,你可以编写交互式的终端应用程序,如文本编辑器、游戏、终端界面的图形化工具等。

ncurses库在Unix-like系统上广泛使用,包括Linux和macOS。它是一个非常强大和灵活的库,可以帮助你创建复杂的终端应用程序。

常用的ncurse的函数

initscr();

该函数可以调用ncurse的引擎

printw();

printw();使用方式和printf一样,原样输出

getch();

防止程序退出,应该和getchar()使用起来差不多

endwin();

程序退出后防止乱码

详细解释上面函数

在这里插入图片描述

练习使用上面的函数

代码:

#include<curses.h>

int main()
{
  char data;//一个字符变量
  initscr();//初始化界面
  printw("plase scanf of data\n");
  data=getch();//获取字符
  printw("\n you scanf of %c\n",data);
  getch();//防止程序退出
  endwin();//防止出现乱码
  return 0;
}

ncurses获取上下左右键

在C语言中,keypad函数是ncurses库中的一个函数,用于启用或禁用终端键盘的特殊键盘码。

keypad(stdscr, 1)是将ncurses库中的stdscr窗口启用特殊键盘码的函数调用。
stdscr是ncurses库中的一个全局变量,表示标准屏幕窗口。通过调用keypad(stdscr, 1),我们可以启用特殊键盘码,使得getch()函数可以返回特殊键盘码。

通过上面函数我们可以准确获得上下左右键的命令,用于控制贪吃蛇的行动
代码:

#include<curses.h>

上面的头文件对上下左右键进行了引用
上:KEY_UP
下:KEY_DOWN
左:KEY_LEFT
右:KEY_RIGHT

#include<curses.h>

int main() {
	int data;
	initscr();//界面初始化
	keypad(stdscr, 1); //使用界面的键盘输入
	while (1) {
		data = getch(); //获取data的值进行输入
		switch (data) {
			case KEY_DOWN:
				printw("DOWN\n");
				break;
			case KEY_UP:
				printw("UP\n");
				break;
			case KEY_LEFT:
				printw("LEFT\n");
				break;
			case KEY_RIGHT:
				printw("RIGHT\n");
				break;
		}

	}
	getch();//防止程序退出

	endwin();//防止乱码

	return 0;
}

运行结果:
在这里插入图片描述

贪吃蛇地图绘制

绘制一个20x20的地图

地图的第一行

代码:注意看注释

#include<curses.h>//需要包含这个头文件

void ncurses1() {
	initscr();//界面初始化的函数
	keypad(stdscr, 1); //使用特殊键盘
}
void gamemap() {
	//定义行和列
	int hang, lie;
	//地图是20行,建立循环20次
	for (hang = 0; hang < 20; hang++) {
		if (hang == 0) { //对第一行进行绘制
			for (lie = 0; lie < 20; lie++) {
				printw("--");//打印20次--
			}
			printw("\n");
			for (lie = 0; lie <= 20; lie++) {
				//第0列和第20列打印一个“|”
				if (lie == 0 || lie == 20) {
					printw("|");
				} else {
					//其他位置用空格代替
					printw("  ");
				}
			}
		}
	}
}

int main() {
	//定义一个函数用来初始化界面和使用特殊键盘
	ncurses1();
	//地图的第一行进行绘制
	gamemap();
	getch();//防止程序退出
	endwin();//防止乱码
	return 0;
}

运行结果:
在这里插入图片描述

完整地图绘制

代码:

#include<curses.h>//需要包含这个头文件

void ncurses1() {
	initscr();//界面初始化的函数
	keypad(stdscr, 1); //使用特殊键盘
}
void gamemap() {
	//定义行和列
	int hang, lie;
	//地图是20行,建立循环20次
	for (hang = 0; hang < 20; hang++) {
		if (hang == 0) { //第一行地图的绘制
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
		if (hang >= 0 && hang <= 19) { //中间0-19行地图绘制
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
				} else {
					printw("  ");
				}
			}
			printw("\n");
		}
		//最后一行地图的绘制 
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxx\n");
}

int main() {
	//定义一个函数用来初始化界面和使用特殊键盘
	ncurses1();
	//地图的第一行进行绘制
	gamemap();
	getch();//防止程序退出
	endwin();//防止乱码
	return 0;
}

运行结果:
在这里插入图片描述

贪吃蛇显示蛇的头

蛇的一个节点的显示

蛇头用“[ ]”来表示

贪吃蛇的节点是一个结构体,包含,行和列坐标以及下一个节点的指针
struct snake{
int hang;
int lie;
struct snake*next;
}

代码:

#include<curses.h>//需要包含这个头文件
//定义出蛇的节点的结构体
typedef struct snakes{
   int hang;
   int lie;
   struct snakes*next;
}node;
//蛇的节点显示在
node t1={2,2,NULL};
void ncurses1() {
	initscr();//界面初始化的函数
	keypad(stdscr, 1); //使用特殊键盘
}
void gamemap() {
	//定义行和列
	int hang, lie;
	//地图是20行,建立循环20次
	for (hang = 0; hang < 20; hang++) {
		if (hang == 0) { //第一行地图的绘制
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
		if (hang >= 0 && hang <= 19) { //中间0-19行地图绘制
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
				}else if(t1.hang==hang&&t1.lie==lie)//满足条件打印
				{
				printw("[]");//打印蛇的节点
				} 
				else {
					printw("  ");
				}
			}
			printw("\n");
		}
		//最后一行地图的绘制 
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxx\n");
}

int main() {
	//定义一个函数用来初始化界面和使用特殊键盘
	ncurses1();
	//地图的第一行进行绘制
	gamemap();
	getch();//防止程序退出
	endwin();//防止乱码
	return 0;
}

运行结果:
在这里插入图片描述

分装一个函数显示3个蛇的节点(静态添加)

代码:

#include<curses.h>

typedef struct snakes{
int hang;//蛇的行坐标
int lie;//列坐标
struct snakes*next;//可以指向下一个节点的位置
}node;//蛇,节点结构体

node t1={2,2,NULL};
node t2={2,3,NULL};
node t3={2,4,NULL};
node t5={2,5,NULL};//设置四个节点的位置

int shejie(int hang,int lie)
{
   node*point=&t1;//保留蛇节点t1的地址
   while(point!=NULL)
   {  
      //行和列的值和上面4个节点的行和列的值是否相等,满足返回1不满足返回0
      if(point->hang==hang&&point->lie==lie)
      {
        return 1;
      }
      point=point->next;//访问下一个节点
   }
return 0;
}
void ncurses1()
{
 initscr();
 keypad(stdscr,1);//调用键盘
}
void gamemap()
{
   int hang,lie;//行和列
   for(hang=0;hang<20;hang++)//绘制20x20的地图需要的循环20次
   {
      //第一行地图的绘制
      if(hang==0)
      {
        for(lie=0;lie<20;lie++)
        {
         printw("--");//打印20次--
        }
        printw("\n");//进行换行
      }   
    //中间部分的绘制
    if(hang>=0&&hang<=19)
    {
        for(lie=0;lie<=20;lie++)
        {
            if(lie==0||lie==20)
            {
              printw("|");//第0列和第20列打印一个|
            }else if(shejie(hang,lie))//建立函数是否满足条件,满足打印,把hang和lie的值进行传递,
            {
             printw("[]");
            }
            else{
              printw("  ");//其余部分打印空格两个
                 }
          }
             printw("\n");
    }
     //最后一行地图的绘制
     if(hang==19)
     {
      for(lie=0;lie<20;lie++)
      {
      printw("--");//打印20次--
      }
      printw("\n");
     }
   }
}

int main()
{
ncurses1();//初始化界面和调用特殊键盘
//t1-t5之间节点形成链表
t1.next=&t2;
t2.next=&t3;
t3.next=&t4;
t4.next=&t5;
//绘制出贪吃蛇的地图
gamemap();
getch();//防止退出
endwin();//防止乱码
return 0;
}

运行结果:
在这里插入图片描述

贪吃蛇身子的改进,使用malloc来创造空间添加节点

代码:

#include<curses.h>
#include<stdlib.h>
//建立一个蛇节点的结构体
typedef struct node {
	int hang;
	int lie;//节点的行和列坐标
	struct node*next;//指向下一个节点的指针
} snakes;
//定义两个全局变量,头和尾
snakes*head = NULL; //链表头(蛇头)
snakes*tail = NULL; //链表尾(蛇尾)
void ncurses1() {
	initscr();//初始化界面
	keypad(stdscr,1);//使用特殊键盘
}
void addsnakes() {
//新节点开辟空间
	snakes*new = (snakes*)malloc(sizeof(snakes));
	new->hang = tail->hang;
	new->lie = tail->lie + 1; //地图第2行的位置往右添加节点
	new->next = NULL;
	tail->next = new; //尾巴指向新的节点
	tail = new; //新开辟的节点用永远是尾巴
}

void initsnakes() {
//1.给蛇头开辟一个空间,赋值
	head = (snakes*)malloc(sizeof(snakes));
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
//2.蛇尾赋值
	tail = head;
//3.建立一个函数进行新节点的添加
	int i = 3; //想添加3个节点
	while (i > 0) {
//新节点开辟的函数
		addsnakes();
		i--;
	}
}

int shejie(int hang, int lie) {
	snakes*point = head; //保留头的位置
	while (point != NULL) {
		if (point->hang == hang && point->lie == lie) {
			return 1;//找到节点返回1
		}
		point = point->next;
	}
	return 0;//没有节点返回0
}
void gamemap() {
	//行和列组成地图,20x20的地图行用--,列用|和空格
	int hang, lie;
	for (hang = 0; hang < 20; hang++) { //20行地图循环20次
		//绘制第1行的情况
		if (hang == 0) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
		//绘制中间0-19行
		if (hang >= 0 && hang <= 19) {
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
					//中间部分打印一下蛇的节点,判断条件是否满足,根据返回值
				} else if (shejie(hang, lie)) {
					printw("[]");
				} else {
					printw("  ");
				}
			}
			printw("\n");
		}
		//最后一行绘制
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxxxxx\n");
}
int main() {
//1.建立一个函数初始化界面和使用keypad函数
	ncurses1();
//2.动态开辟空间的函数,显示蛇的身体
	initsnakes();
//3.绘制地图,
	gamemap();
//4.防止程序退出
	getch();
//5.防止乱码
	endwin();
	return 0;
}

运行结果:
在这里插入图片描述

贪吃蛇的移动

向右移动,通过删除一个节点然后添加一个节点方式,通过free()函数释放空间

move(0, 0)是ncurses库中的一个函数调用,用于将光标移动到终端屏幕上的指定位置。
代码:

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


typedef struct node {
	int hang;
	int lie;
	struct node*next;
} snakes; //snake node

snakes*head = NULL; //snakes head;
snakes*tail = NULL; //snakes tail;


void ncurses1() {
	initscr();//chu shi hua
	keypad(stdscr, 1); // jianpan
}

void addsnakes() {
	snakes*new = (snakes*)malloc(sizeof(snakes));
	new->hang = tail->hang;
	new->lie = tail->lie + 1;
	new->next = NULL;
	tail->next = new;
	tail = new;
}

void initsnakes() {
	//head zhi xiang kaipikong jian
	head = (snakes*)malloc(sizeof(snakes));
	//xianshi2,2,NULL
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
	// weiba=head
	tail = head;
	//gai han shu zeng jia jie dian
	addsnakes();
	addsnakes();
	addsnakes();
}


int shejie(int hang, int lie) {
	snakes*point = head;
	while (point != NULL) {
		if (point->hang == hang && point->lie == lie) {
			return 1;
		}
		point = point->next;
	}
	return 0;
}

void gamemap() {
	move(0, 0);//使其光标固定 
	int hang, lie;
	for (hang = 0; hang < 20; hang++) {
		if (hang == 0) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
		if (hang >= 0 && hang <= 19) {
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
				} else if (shejie(hang, lie)) {
					printw("[]");
				} else {
					printw("  ");
				}
			}
			printw("\n");
		}
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxxxxx\n");
}

void deleatehead() {
	snakes*p = head;//保留头原来的节点 
	head = head->next;//改变头的位置 
	free(p);//释放多余头节点的位置 
}

void snakesright() {
	addsnakes();//尾巴添加一个新的节点 
	deleatehead();//删除头部的一个节点 

}

int main() {
	int con;
	ncurses1();//初始化界面 

	initsnakes();// 开辟空间,形成链表 

	gamemap();// 贪吃蛇的地图
	 
  //循环,使其蛇的身体向右方向移动 
	while (1) {
		con = getch();
		switch (con) {
			case KEY_RIGHT:
				snakesright();//该函数使其向右移动,通过添加新节点,然后删除一个节点的方式 
				gamemap();//刷新地图 
				break;
		}
	}

	getch();
	endwin();

	return 0;
}

运行结果:按下方向键“->”,使其进行移动
在这里插入图片描述

贪吃蛇移动-撞墙后重新回到原来的位置

1.判定是4个边界,触碰到边界然后释放空间,然后开辟一条新的蛇的身体

代码:

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

typedef struct node {
	int hang;
	int lie;
	struct node*next;
} snakes;
snakes*head = NULL; //蛇头
snakes*tail = NULL; //蛇尾

void ncurses1() {
	initscr();//初始化界面
	keypad(stdscr, 1); //使用键盘
}
void addsnakes() {
	//申请空间
	snakes*new = (snakes*)malloc(sizeof(snakes));
	//新节点的右边加节点,行的坐标不用变化,列的坐标+1
	new->hang = tail->hang;
	new->lie = tail->lie + 1;
	new->next = NULL;
	//尾巴的指针指向新的节点,
	tail->next = new;
	//始终让尾巴的位置在新的节点
	tail = new;
}

void initsnakes() {

	snakes*p;
	while (head != NULL) {
		p = head;
		head = head->next;
		free(p);
	}
	//给头节点申请空间
	head = (snakes*)malloc(sizeof(snakes));
	//蛇头节点行坐标和列坐标赋值,指针为空
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
	//尾部节点的指针赋值
	tail = head;
	//建立函数,在尾巴后面添加节点
	addsnakes();
	addsnakes();
	addsnakes();//三次调用,添加三个节点
}

int shejie(int hang, int lie) {
	snakes*point = head; //保留头节点的位置
	while (point != NULL) {
		//根据每个节点的行和列判断是否满足条件
		if (point->hang == hang && point->lie == lie) {
			return 1;//满足条件返回1,打印节点
		}
		point = point->next; //访问下一个节点
	}
	return 0;//没有相等的节点,返回0不打印
}
void gamemap() {
	move(0, 0); //固定光标
	//打印出一个正方形,20x20
	int hang, lie; //定义两个变量分别代表行和列
	for (hang = 0; hang < 20; hang++) { //20行的地图循环20次
		//第一行地图的绘制
		if (hang == 0) {
			for (lie = 0; lie < 20; lie++) {
				//打印20个--组成的横线
				printw("--");
			}
			printw("\n");//换行打印
		}
		//中间部分0-19行显示|   |;
		if (hang >= 0 && hang <= 19) {
			//|的长度不够,列的长度改为20
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					//第0列和第20列打印|
					printw("|");
				} else if (shejie(hang, lie)) {
					//判断是否显示蛇的节点
					//根据节点行和列坐标是否相等
					printw("[]");
				} else {
					printw("  ");
				}
			}
			printw("\n");
		}
		//第19行也打印一个20个--
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("xxxxx\n");
}

void deleatenode() {
	snakes*p = head; //保留一个头节点的位置
	head = head->next; //头节点的位置变化
	free(p);//删除头节点的位置
}

void rightsnakes() {
	//1.先添加一个节点
	addsnakes();
	//删除一个节点
	deleatenode();
	//判断是否撞墙,通过尾部节点是否和边界重合判断
	if (tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20) {
		//位置重合,重新显示蛇的节点
		initsnakes();
	}
}

int main() {
	int con;//控制蛇移动的变量,往右
	//1.初始化界面和使用特殊键盘的函数
	ncurses1();
	//2.绘制蛇的身体形成链表
	initsnakes();
	//绘制地图20x20
	gamemap();
	//使其向右移动
	while (1) {
		con = getch(); //获取con的值
		switch (con) {
			case KEY_RIGHT:
				rightsnakes();//向右移动蛇
				gamemap();//刷新地图
				break;
		}
	}

	getch();//防止退出
	endwin();//防止乱码
	return 0;
}

运行结果:往右撞到边线后会重新刷新
撞墙后会刷新

贪吃蛇自己向右移动

自己移动通过不停的刷新界面的函数refresh();和sleep()函数

refresh()是一个函数调用,用于刷新ncurses库创建的游戏界面。

在使用ncurses库创建游戏界面时,我们可以使用各种函数来绘制游戏界面的各个元素,比如蛇的身体、食物等。但是这些绘制的元素并不会立即显示在屏幕上,而是在调用refresh()函数之后才会显示出来。

refresh()函数的作用是将之前绘制的元素刷新到屏幕上,使得玩家可以看到最新的游戏界面。

通常,在绘制完游戏界面的所有元素之后,我们会调用refresh()函数来更新屏幕显示。这样,玩家就可以看到最新的游戏状态,并且可以根据最新的界面进行操作。

总结起来,refresh()函数的作用是刷新游戏界面,使得之前绘制的元素能够显示在屏幕上。

sleep()是一个函数调用,用于在程序执行过程中暂停一段时间。

在编程中,我们有时需要在程序执行过程中添加一些延迟,以便控制程序的执行速度或者等待一些操作完成。sleep()函数就是用来实现这个目的的。

sleep()函数接受一个整数参数,表示要暂停的时间长度,单位是秒。调用sleep()函数后,程序会暂停执行指定的时间,然后再继续执行后面的代码。

例如,如果我们调用sleep(2),那么程序会暂停执行2秒钟,然后再继续执行后面的代码。

sleep()函数在很多编程语言中都有相应的实现,用法也基本相同。它可以用于各种场景,比如在游戏中添加延迟效果、在多线程编程中控制线程的执行速度等。

需要注意的是,sleep()函数会阻塞当前线程的执行,也就是说,在调用sleep()函数期间,当前线程无法执行其他任务。如果需要在后台执行一些其他操作,可以考虑使用异步编程的方式,或者使用定时器等机制来实现延迟效果。

代码:

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

typedef struct node{
  int hang;
  int lie;
  struct node*next;
  }snakes;//贪吃蛇每个节点的模板
  //定义两个指针,头节点的指针和尾巴节点的指针
  snakes*head=NULL;//头
  snakes*tail=NULL;//尾巴

 void ncurses1()
 {
   initscr();//初始化界面
   keypad(stdscr,1);//使用键盘
 }

void addsnakes()
{
   //新节点开辟空间,赋值
   snakes*new=(snakes*)malloc(sizeof(snakes));
   new->hang=tail->hang;
   new->lie=tail->lie+1;//列数+1是为了在右边添加行数还是在同一行
   new->next=NULL;
   tail->next=new;//尾巴节点指向新的节点
   tail=new;//尾巴永远在新节点
}



void initsnakes()
{
   //撞墙后清空节点
   snakes*p;
   while(head!=NULL)
   {
      p=head;
      head=head->next;
      free(p);
   }
  //给头节点申请空间赋值
  head=(snakes*)malloc(sizeof(snakes));
  head->hang=2;
  head->lie=2;
  head->next=NULL;
  //头节点的地址给到尾巴
  tail=head;
  //添加新节点的函数
  addsnakes();
  addsnakes();
  addsnakes();//添加三个节点
}
int snakesnode(int hang,int lie)
{
      snakes*point=head;
      while(point!=NULL)
      {
         if(point->hang==hang&&point->lie==lie)
         {
         return 1;
         }
         point=point->next;
      }
      return 0;
}
void snakesmap()
{
   move(0,0);//光标固定
   int hang,lie;//行和列
   for(hang=0;hang<20;hang++)//20x20的地图
   {
        //第一行绘制20个--
        if(hang==0)
        {
           for(lie=0;lie<20;lie++)
           {
           printw("--");
           }
           printw("\n");
        }
       //中间部分绘制
       if(hang>=0&&hang<=19)
       {
               for(lie=0;lie<=20;lie++)
                 {
                    if(lie==0||lie==20)
                        {
                          printw("|");
                        }else if(snakesnode(hang,lie)){
                              printw("[]");
                           }else{
                           printw("  ");
                           }
                 }
                 printw("\n");
       }
       if(hang==19)
         {
          for(lie=0;lie<20;lie++)
              {
                printw("--");
              }
              printw("\n");
         }
       }
       printw("by xxxxxx\n");
}

void deleatenode()
{
     snakes*p=head;
     head=head->next;
     free(p);
}


void snakesright()
{
   //1.添加一个节点,然后删除一个节点
   addsnakes();
   deleatenode();
   //判定撞墙
   if(tail->hang==0||tail->lie==0||tail->hang==20;tail->lie==20)
   {
     initsnakes();//重新刷新
   }
}

int main()
{
   //1.初始化界面和使用特殊键盘
   ncurses1();
   //2.开辟空间使其成为蛇的身体
   initsnakes();
   //3.绘制地图
   snakesmap();
   //4.通过循环自己向右移动
   while(1)
   {
        snakesright();//蛇向右移动
        snakesmap();
        refresh();//刷新界面
        usleep(100000);//暂停
   }
 getch();
 endwin();
return 0;
}

linux线程概念的引入及编程实现

线程的概念:
Linux线程是在Linux操作系统上创建和管理的执行单元。线程是进程的一部分,它与其他线程共享同一进程的资源,包括内存空间、文件描述符和其他系统资源。

Linux操作系统使用POSIX线程库(pthread)来支持多线程编程。通过使用pthread库,开发人员可以创建和管理多个线程,并在这些线程之间共享数据和执行任务。

在Linux中,线程的创建和管理是通过调用pthread库中的函数来实现的。一些常用的pthread函数包括:

  • pthread_create():用于创建一个新的线程。
  • pthread_join():用于等待一个线程的结束,并获取线程的返回值。
  • pthread_exit():用于线程的退出。
  • pthread_mutex_lock()pthread_mutex_unlock():用于线程之间的互斥访问共享资源。
  • pthread_cond_wait()pthread_cond_signal():用于线程之间的条件变量同步。

使用多线程编程可以提高程序的并发性和响应性。通过将任务分配给不同的线程并行执行,可以加快程序的执行速度。同时,多线程编程也需要注意线程之间的同步和互斥,以避免数据竞争和死锁等问题。

在Linux中,线程是一种轻量级的执行单元,相对于进程来说,线程的创建和切换开销较小。因此,多线程编程在Linux系统中得到了广泛的应用,包括服务器编程、并行计算、图形界面等领域。

代码:同时运行两个while(1)的循环

#include<stdio.h>
#include<pthread.h>//线程需要包含这个文件夹

void func()
{
   while(1)
   {
     printf("this is func 1\n");
     sleep(1);
   }
}
void func2()
{
   while(1)
   {
      printf("this is func 2\n");
      sleep(1);
   }
}

int main()
{
     pthread_t t1;//线程的标识符
     pthread_t t2;//线程的标识符
     //建立两个线程
     pthread_create(&t1,NULL,func,NULL);
     pthread_create(&t2,NULL,func2,NULL);
     while(1);
return 0;
}

运行结果:两个函数while会同时进行
在这里插入图片描述

通过多线程的方式实现两个循环同时进行(界面刷新)(键盘检测)

代码:

#include<curses.h>
#include<stdlib.h>
#include<pthread.h>//多线程的需要包含的头文件

typedef struct node {
	int hang;
	int lie;
	struct node*next;
} snakes; //蛇每个节点的结构体
snakes*head = NULL; //蛇头的地址
snakes*tail = NULL; //蛇尾巴的结构体
int con;//键盘输入
void ncurses1() {
	initscr();//界面初始化
	keypad(stdscr, 1); //使用特殊键盘
}
//增加蛇节点的函数
void addsnakes() {
	//定义新节点的指针和开辟空间
	snakes*new = (snakes*)malloc(sizeof(snakes));
	//头节点的右边增加节点,行不变,列+1
	new->hang = tail->hang;
	new->lie = tail->lie+1;
	new->next = NULL;
	//尾巴的指针指向新节点的位置
	tail->next = new;
	//始终让新节点的位置为尾巴
	tail = new;
}
void initsnakes() {
	//蛇碰到边界重新刷新
	snakes*p = NULL;
	while (head != NULL) {
		p = head;
		head = head->next;
		free(p);
	}
	//蛇头的指针指向开辟的空间
	head = (snakes*)malloc(sizeof(snakes));
	//蛇在第2行第2列的位置开始打印
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
	//尾巴的位置赋值
	tail = head;
	//增加节点的函数
	addsnakes();
	addsnakes();
	addsnakes();//增加三个节点
}

int snakesx(int hang,int lie) {
	snakes*point = head;
	while (point != NULL) {
		if (point->hang == hang && point->lie == lie) {
			return 1;
		}
		point = point->next;
	}
	return 0;
}

//地图打印函数
void snakesmap() {
	move(0, 0); //固定光标的位置
	int hang, lie; //行和列
	for (hang = 0; hang < 20; hang++) { //20行地图的打印
		//第一行打印
		if (hang == 0) {
			//每一列打印20个--
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");//换行
		}
		//中间部分进行打印
		if (hang >= 0 && hang <= 20) {
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
				} else if (snakesx(hang, lie)) {
					printw("[]");
				} else {
					printw("  ");
				}
			}
			printw("\n");
		}
		//最后一行的部分进行打印
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxxxxx\n");
}

void deleatenode() { //删除头节点
	snakes*p = head;
	head = head->next;
	free(p);//释放节点
}

void rightsnakes() { //向右移动蛇
	addsnakes();
	deleatenode();
	//移动过程碰到边界重新刷新蛇的位置
	if (tail->hang == 0 || tail->hang == 20 || tail->lie == 0 || tail->lie == 20) {
		//重新调用生成蛇的函数
		initsnakes();
	}
}
void* initmap() { //界面刷新的函数
	while (1) {
		rightsnakes();//蛇自己向右移动
		snakesmap();//调用地图
		refresh();//界面刷新
		usleep(100000);//暂停
	}
}

void* keyscanf() { //键盘输入
	while (1) {
		con = getch(); //
		switch (con) {
			case KEY_UP:
				printw("UP\n");
				break;
			case KEY_DOWN:
				printw("DOWN\n");
				break;
			case KEY_LEFT:
				printw("LEFT\n");
				break;
			case KEY_RIGHT:
				printw("RIGHT\n");
				break;
		}
	}
}

int main() {
	pthread_t t1;
	pthread_t t2;//两个线程的标识符

	//界面初始化的函数
	ncurses1();
	//蛇的节点形成链表
	initsnakes();
	//绘制地图,打印蛇的身体
	snakesmap();
	//通过线程实现两个循环,
	pthread_create(&t1, NULL, initmap, NULL);
	pthread_create(&t2, NULL, keyscanf, NULL);
	while (1);
	getch();
	endwin();
	return 0;
}

贪吃蛇改变方向自己移动

代码:

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

#define UP    1
#define DOWN  2
#define LEFT  3
#define RIGHT 4//按键设置蛇的四个方向

typedef struct node {
	int hang;
	int lie;
	struct node*next;
} snakes; //蛇的每个节点的结构体
snakes*head = NULL;
snakes*tail = NULL;
int con;//检测按键
int dir;//改变方向

void ncurses1() { //初始化界面,调用键盘
	initscr();
	keypad(stdscr, 1);
}
void addsnakes() {
	//增加节点要开辟新的空间
	snakes*new = (snakes*)malloc(sizeof(snakes));
	new->next = NULL;
	switch (dir) {
		case UP:
			new->hang = tail->hang - 1;
			new->lie = tail->lie;
			break;
		case DOWN:
			new->hang = tail->hang + 1;
			new->lie = tail->lie;
			break;
		case LEFT:
			new->hang = tail->hang;
			new - lie = new->lie - 1;
			break;
		case RIGHT:
			new->hang = tail->hang;
			new -> lie = tail->lie + 1;
			break;
	}

	tail->next = new; //尾巴指向新的节点
	tail = new; //新节点的位置始终在尾巴
}
void initsnakes() {
	dir = RIGHT; //现在右边增加三个节点
	//触碰到边界释放节点
	snakes*p = NULL;
	while (head != NULL) {
		p = head;
		head = head->next;
		free(p);
	}
//给蛇的头开辟空间赋值
	head = (snakes*)malloc(sizeof(snakes));
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
//蛇头的空间给到尾巴
	tail = head;
//在尾巴后面增加三个节点
	addsnakes();//增加节点的函数
	addsnakes();
	addsnakes();
}
int snakesx(int hang, int lie) {
	//通过每个节点行坐标和列坐标来判断是否返回1
	snakes*point = head;
	while (point != NULL) {
		if (point->hang == hang && point->lie == lie) {
			return 1;
		}
		point = point->next;
	}
	return 0;
}
//绘制地图的函数
void gamemap() {
	move(0, 0); //固定光标
	int hang, lie; //行和列
	for (hang = 0; hang < 20; hang++) {
		//第一行地图
		if (hang == 0) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
		//中间部分绘制
		if (hang >= 0 && hang <= 19) {
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
				} else if (snakesx(hang, lie)) {
					printw("[]");
				} else {
					printw("  ");
				}
			}
			printw("\n");
		}
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxxxxx\n");
}
void deleatenode() {
	snakes*p = head;
	head = head->next;
	free(p);
}

void movesnakes() {
//增加节点
	addsnakes();
//删除一个节点
	deleatenode();
	if (tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20) {
		initsnakes();
	}
}
void* initmap() {
	while (1) {
		movesnakes();//蛇自己移动
		gamemap();//地图
		refresh();//刷新
		usleep(100000);
	}
}
void* keyscanf() {
	while (1) {
		con = getch();
		switch (con) {
			case KEY_UP:
				dir = UP;
				break;
            case KEY_DOWN:
				dir = DOWN;
				break;
			case KEY_LEFT:
				dir = LEFT;
				break;
			case KEY_RIGHT:
				dir = RIGHT;
				break;
		}
	}
}
int main() {
	pthread_t t1;
	pthread_t t2;//两个线程的标识

	ncurses1();//初始化界面的函数
	initsnakes();//绘制蛇的节点
	gamemap();//绘制地图
	//建立两个线程,一个刷新界面,一个不停使用键盘
	pthread_create(&t1, NULL, initmap, NULL); //界面
	pthread_create(&t2, NULL, keyscanf, NULL); //键盘
	while (1);
	getch();
	endwin();
	return 0;
}

绝对值方式解决走位的问题

通过函数ads();查看绝对值

代码:

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

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

typedef struct node {
	int hang;
	int lie;
	struct node*next;
} snakes;

snakes*head = NULL;
snakes*tail = NULL;
int con;
int dir;

void ncurses1() {
	initscr();
	keypad(stdscr, 1);
	noecho();
}

void addsnakes() {
	snakes*new = (snakes*)malloc(sizeof(snakes));
	new->next = NULL;
	switch (dir) {
		case UP:
			new->hang = tail->hang - 1;
			new->lie = tail->lie;
			break;
		case DOWN:
			new->hang = tail->hang + 1;
			new->lie = tail->lie;
			break;
		case LEFT:
			new->hang = tail->hang;
			new->lie = tail->lie - 1;
			break;
		case RIGHT:
			new->hang = tail->hang;
			new->lie = tail->lie + 1;
			break;
	}
	tail->next = new;
	tail = new;
}

void initsnakes() {
	snakes*p = NULL;
	while (head != NULL) {
		p = head;
		head = head->next;
		free(p);
	}
	dir = RIGHT;
	head = (snakes*)malloc(sizeof(snakes));
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
	tail = head;
	addsnakes();
	addsnakes();
	addsnakes();
}

int snakesx(int hang, int lie) {
	snakes*point = head;
	while (point != NULL) {
		if (point->hang == hang && point->lie == lie) {
			return 1;
		}
		point = point->next;
	}
	return 0;
}

void gamemap() {
	move(0, 0);
	int hang, lie;
	for (hang = 0; hang < 20; hang++) {
		if (hang == 0) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
		if (hang >= 0 && hang <= 19) {
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
				} else if (snakesx(hang, lie)) {
					printw("[]");
				} else {
					printw("  ");
				}
			}

			printw("\n");
		}
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxxxx\n");
}

void deleatenode() {
	snakes*p = head;
	head = head->next;
	free(p);
}

void movesnakes() {
	addsnakes();
	deleatenode();
	if (tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20) {
		initsnakes();
	}
}

void* func() {

	while (1) {
		movesnakes();
		gamemap();
		refresh();
		usleep(100000);
	}

}

void turn(int dirch) {
	if (abs(dir) != abs(dirch)) {
		dir = dirch;
	}
}

void* func2() {
	while (1) {
		con = getch();
		switch (con) {
			case KEY_UP:
				turn(UP);
				break;
			case KEY_DOWN:
				turn(DOWN);
				break;
			case KEY_LEFT:
				turn(LEFT);
				break;
			case KEY_RIGHT:
				turn(RIGHT);
				break;
		}
	}
}

int main() {

	pthread_t t1;
	pthread_t t2;


	ncurses1();

	initsnakes();

	gamemap();

	pthread_create(&t1, NULL, func, NULL);
	pthread_create(&t2, NULL, func2, NULL);


	while (1);

	getch();

	endwin();

	return 0;
}

贪吃蛇吃食物和咬到自己

代码:

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

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

typedef struct node {
	int hang;
	int lie;
	struct node*next;
} snakes; //蛇的节点结构体

snakes*head = NULL; //蛇头的位置
snakes*tail = NULL; //蛇尾巴的位置
int dir;//改变方向
int con;//键盘输入
snakes food;//食物的结构体
void initfood() { //初始化食物坐标的函数
	int x = rand() % 20;
	int y = rand() % 20;
	food.hang = x;
	food.lie = y;
}
void ncurses1() {
	initscr();//初始化界面
	keypad(stdscr, 1); //键盘调用
	noecho();//防止乱码
}

void addsnakes() {
	//开辟新的空间
	snakes*new = (snakes*)malloc(sizeof(snakes));
	new->next = NULL;
	//增加节点,根据方向
	switch (dir) {
		case UP://往上hang-1,列不会变化
			new->hang = tail->hang - 1;
			new->lie = tail->lie;
			break;
		case DOWN://往下hang+1,列不变
			new->hang = tail->hang + 1;
			new->lie = tail->lie;
			break;
		case LEFT://往左lie-1,行不变
			new->hang = tail->hang;
			new->lie = tail->lie - 1;
			break;
		case RIGHT://往右lie+1,行不变
			new->hang = tail->hang;
			new->lie = tail->lie + 1;
			break;
	}

	tail->next = new;
	tail = new;
}
void initsnakes() {

      snakes*p=NULL;
      while(head!=NULL)
        {
            p=head;
            head=head->next;
            free(p);
        }

	initfood();//调用函数显示食物的位置
	dir = RIGHT; //刚进来蛇往右移动
	//给蛇头的节点开辟空间并给行和列坐标赋值
	head = (snakes*)malloc(sizeof(snakes));
	head->hang = 2;
	head->lie = 2;
	head->next = NULL;
	//蛇尾的位置赋值
	tail = head; //要在蛇的尾巴后面添加节点
	//添加节点的函数
	addsnakes();
	addsnakes();
	addsnakes();//添加三个节点
}

int snakesx(int hang, int lie) {
	snakes*point = head; //局部变量保存头节点的地址
	while (point != NULL) {
		if (point->hang == hang && point->lie == lie) {
			return 1;
		}
		point = point->next;
	}
	return 0;
}
int havefood(int hang, int lie) { //根据该函数返回值是否显示食物
	if (food.hang == hang && food.lie == lie) {
		return 1;
	}
	return 0;
}
void gamemap() {
	move(0, 0); //光标固定
	int hang, lie; //行和列
	for (hang = 0; hang < 20; hang++) {
		//第一行地图绘制
		if (hang == 0) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
		//中间20行地图绘制
		if (hang >= 0 && hang <= 19) {
			for (lie = 0; lie <= 20; lie++) {
				if (lie == 0 || lie == 20) {
					printw("|");
				} else if (snakesx(hang, lie)) { //根据该函数返回值来判断是否显示蛇的节点
					printw("[]");
				} else if (havefood(hang, lie)) {
					printw("##");
				} else {
					printw("  ");
				}
			}
			printw("\n");
		}
		if (hang == 19) {
			for (lie = 0; lie < 20; lie++) {
				printw("--");
			}
			printw("\n");
		}
	}
	printw("by xxxx");
}
void deleatenode() {
	snakes*p = head;
	head = head->next;
	free(p);//释放空间
}

int ifdese() {
	snakes*p = head;
	//移动时候新增节是否和边界重合
	if (tail->hang < 0 || tail->hang == 20 || tail->lie == 0 || tail->lie == 20) {
		return 1;
	}
	while (p->next != NULL) {
		//头节点是一直变化的,如果变化的过程中和尾巴重合返回1
		if (p->hang == tail->hang && p->lie == tail->lie) {
			return 1;
		}
            p=p->next;
	}
	return 0;
}
void movesnakes() {
	//蛇移动要增加节点和删除节点,
	addsnakes();//增加节点
	if (havefood(tail->hang, tail->lie)) {
		initfood();
	} else {
		deleatenode();//删除节点
	}
	if (ifdese()) { //判断是否撞墙和咬到自己
		initsnakes();
	}
}
void* func() {
	while (1) {
		movesnakes();//蛇的移动
		gamemap();//再次调用地图
		refresh();//地图刷新
		usleep(100000);//暂停
	}
}
void turn(int dirses) {
	//根据绝对值判断dir
	if (abs(dir) != abs(dirses)) {
		dir = dirses;
	}
}
void* func2() {
	while (1) {
		con = getch();
		switch (con) {
			case KEY_UP:
				turn(UP);
				break;
			case KEY_DOWN:
				turn(DOWN);
				break;
			case KEY_LEFT:
				turn(LEFT);
				break;
			case KEY_RIGHT:
				turn(RIGHT);
				break;
		}
	}
}

int main() {
	pthread_t t1;//界面刷新的线程标识符
	pthread_t t2;//键盘输入的线程标识符

//1.初始化界面和使用键盘的函数
	ncurses1();
//2.初始化蛇的身体
	initsnakes();
//3.地图
	gamemap();
//4.建立两个线程刷新地图和键盘输入
	pthread_create(&t1, NULL, func, NULL);
	pthread_create(&t2, NULL, func2, NULL);
	while (1); //主函数循环
	getch();//防止退出
	endwin();//防止出现乱码
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值