implementación basada en maldiciones de serpiente en el sistema Linux, comentarios ultra detallado

la implementación del código

#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

//蛇身的节点,用双向链表来表示. 信息有横坐标 x, 纵坐标 y, 前一个节点 pre, 后一个节点 next.
struct node{
	int x;
	int y;
	struct node * next;
	struct node * pre;
}; 
typedef struct node snakebody;	//把该结构体重命名

//表示一条蛇,只需要存储头节点和尾节点即可,在蛇的移动过程中,只需要把当前头节点指向新创建的一个头节点,尾节点指向它的前一个即可。
struct snake{
	snakebody * head;
	snakebody * tail;
};

//蛇要吃的点
struct point{
	int x;
	int y;
	int ifcreate;	//因为使用了多线程,所以用一个标志位来控制,每当蛇吃了一个点,则主线程修改 ifcreate 的值,创建得分点的线程新建一个点
};

//同样,使用一个线程来判断蛇是否死亡,为了传递参数,要创建一个结构体
struct dead {
	int flag;	//0 代表游戏没有开始,1 代表开始,2代表结束
	struct node * head;
};

//得到用户的控制,为了实现非阻塞,使用了 select
int mygetchar(void);
//在头节点前面加一个节点
snakebody * addnode(snakebody * head, int hori, int ver);
//创建蛇身
struct snake mksnake(void);
//创建得分点, 该函数运行起来是独立的线程
void * createpoint(void *);
//判断是否死亡, 该函数运行起来是独立的线程
void * ifdead(void *);

int main(void)
{
	int hori, ver;	//横、纵坐标
	char dir;	//用户输入的方向
	int begin;	//判断是否开始
	struct snake snake;
	snakebody *head, *tail, *current;
	struct point * p =  (struct point *) malloc(sizeof(struct point));	//用于向 createpoing 函数传递的参数
	struct dead * dead = (struct dead *) malloc(sizeof(struct dead));	//用于向 ifdead 函数传递的参数

	WINDOW * pt = initscr();	//初始化窗口
	snake = mksnake();	//创建蛇身
	head = snake.head;
	tail = snake.tail;
	//绘制初始图形
	current = head;
	while(current != NULL)
	{
		mvaddch(current->y, current->x, '*');
		current = current->next;
	}
	refresh();
	usleep(300000);
	//控制移动
	hori = ver = 0;
	begin = 0;

	p->x = p->y = -1;	//把初始的得分点设置成 (-1, -1)
	p->ifcreate = 1;	//开始创建的分店
	pthread_t t1;
	pthread_create(&t1, NULL, createpoint, (void *) p);	//启动创建得分点的线程

	dead->head = head;	
	dead->flag = 0;	
	pthread_t t2;
	pthread_create(&t2, NULL, ifdead, (void *) dead);	//启动判断死亡的线程

	while(dead->flag != 2)	//当蛇没死就一直运行
	{
		//获取输入
		switch(dir = mygetchar())
		{
			//在水平方向上不能按与运动方向相反的方向键
			case 'd': if (hori != -1) hori = 1;
					  begin = 1;	//用户按了方向键,游戏开始
				  ver = 0;
				  break;
			case 'a': if (hori != 1) hori = -1;
					  begin = 1;
				  ver = 0;
				  break;
			case 'w': if (ver != 1) ver = -1;
					  begin = 1;
				  hori = 0;
				  break;
			case 's': if (ver != -1) ver = 1;
					  begin = 1;
				  hori = 0;
				  break;
			default: break;
		}

		if(begin)
		{
			head = addnode(head, hori, ver);	//运动起来就像运动方向添加头节点
			mvaddch(tail->y, tail->x, ' ');		//把尾节点设置成空格
			mvaddch(head->y, head->x, '*');		//屏幕上新加一个头节点
			//如果得分
			if(head->y == p->y && head->x == p->x)	
			{
				head = addnode(head, hori, ver);
				mvaddch(head->y, head->x, '*');
				p->ifcreate = 1;	//再创建一个得分点
			}

			free(tail);	//把尾节点占的空空关键释放
			tail = tail->pre;	//尾节点指向它的前一个
			tail->next = NULL;

			dead->head = head;
			dead->flag = 1;		//判断是否死亡

			refresh();
			usleep(100000);
		}
	}
	
	sleep(2);
	endwin();
	return 0;
}

//使用 select 来使输入非阻塞,直接复制的 man select 中的代码
int mygetchar(void)
{
	fd_set rfds;
	struct timeval tv;
	int retval;
	char ch;

	/* Watch stdin (fd 0) to see when it has input. */
	FD_ZERO(&rfds);
	FD_SET(0, &rfds);

	/* Wait up to five seconds. */
	tv.tv_sec = 0;
	tv.tv_usec = 10000;

	retval = select(1, &rfds, NULL, NULL, &tv);
	/* Don't rely on the value of tv now! */

	ch = 0;
	if (retval == -1)
		perror("select()");
	else if (retval)
	{
		ch = getchar();
		setbuf(stdin,NULL);
	}

	/* FD_ISSET(0, &rfds) will be true. */
	else{
	}
	return ch;
}

//创建蛇身
struct snake mksnake(void)
{

	snakebody *head, *tail, *current;
	int i, j;


	head = (snakebody*) malloc(sizeof(snakebody));
	//初始化成 5 个长度的,头节点的坐标就是 5
	head->x = 5;
	head->y = 0;
	head->next = NULL;
	tail = head;
	i = 5;
	j = 0;
	while(i-- >  0)
	{
		current = (snakebody *) malloc(sizeof(snakebody));
		current->x = i;
		current->y = j;
		current->pre = tail;
		current->next = NULL;
		tail->next = current;
		tail = current;
	}
	struct snake s = {head, tail};

	return s;
}

//创建得分点, 注意,该函数运行起来时作为一个独立的线程
void * createpoint(void * point)
{
	struct point *p = (struct point *) point;
	while(1)
	{
		while(p->ifcreate == 0);	//如果玩家没有吃了当前得分点,则在此处一直空转等待
		
		//创建得分点
		p->x = rand() % 15 + 5;
		p->y = rand() % 15 + 5;
		mvaddch(p->y, p->x, '*');
		p->ifcreate = 0;
	}
	return (void *) p;
}

//添加节点,没什么好说的,就是链表添加节点
snakebody * addnode(snakebody * head, int hori, int ver)
{
	snakebody * current = (snakebody *) malloc(sizeof(snakebody));
	current->x = head->x + hori;
	current->y = head->y + ver;
	current->next = head;
	current->pre= NULL;
	head->pre = current;
	return current;
}

//判断是否死亡,注意,该函数运行起来时一个独立的线程
void * ifdead(void * d)
{
	struct dead * dead = (struct dead *) d;

	snakebody *head, *current;
	while(dead->flag != 2)
	{
		while(dead->flag == 0);	// 0 代表蛇没有运动,就不用判断,所以在此处空转等待
		head = dead->head;
		current = head;
		while((current = current->next) != NULL)
		{
			//判断蛇头是否指向了蛇身
			if(current->x == head->x && current->y == head->y){
				clear();
				mvprintw(0, 0, "Game Over!");
				refresh();
				dead->flag = 2;
				break;
			}
			else dead->flag = 0;
		}
	}		

	return NULL;
}

Publicado 42 artículos originales · ganado elogios 3 · Vistas 2080

Supongo que te gusta

Origin blog.csdn.net/stable_zl/article/details/104333901
Recomendado
Clasificación