snake

在linux 上模拟贪吃蛇小程序

采用三个线程完成,线程1负责打印,线程2负责接收键盘控制,线程3负责蛇的移动

1. wsad键控制蛇上下左右移动

2. 蛇不能碰撞自身

3. 不做墙壁碰撞检测

采用链表模拟蛇,蛇头节点唯一标识蛇,蛇移动只需要添加一个头结点,同时删除一个尾节点。蛇吃到食物之后添加头结点而不删除尾节点。

鸣谢:huawei实习面试官

/* 
	greedy snake console program
	linux c programmed
	luojiahu
	
	update: 2015.11.1
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>

#define LOG_FILE "snake.log"

#define CLEAR_SCR() printf("\033[2J")	/* clear the screen */
#define RESET_CURSOR() printf("\033[H") /* relocate the cursor */
#define MIN_X 0
#define MAX_X 14
#define MIN_Y 0
#define MAX_Y 29

typedef struct tagSnake
{
	int pos_x;
	int pos_y;
	struct tagSnake* next;
}SNAKE;

/* snake move direction */
typedef enum tagDirection
{
	dir_up = 1,
	dir_down = 2,
	dir_left = 3,
	dir_right = 4
}DIRECTION;

/* node type */
typedef enum graphType
{
	g_snake = 1,	/* snake - 1 - '*' */
	g_food = 2,		/* food - 2 - 'o' */
	g_null = 3		/* null - 3 - ' ' */
}G_TYPE;

char graph[MAX_X + 1][MAX_Y + 1];	/* graph to storage whether its null, snake or a food node */
int moveDirec = dir_right;	/* snake move direction */
int lastDirec = dir_right; 	/* snake last moved direction */

/* write log with variable parameters */
void writeLog(const char* format, ...)
{
	va_list argp;
	FILE* flog;

	va_start(argp, format);
	flog = fopen(LOG_FILE, "a");
	vfprintf(flog, format, argp);
	fclose(flog);
	va_end(argp);
}

/* update the graph in pos(x,y) with type */
void updateGraph(int x, int y, G_TYPE type)
{
	if(x <= MIN_X || x >= MAX_X || y <= MIN_Y || y >= MAX_Y)
		return;

	//writeLog("update graph type : %d\n", type);

	if(type == g_food)
		graph[x][y] = 'o';
	else if(type == g_snake)
		graph[x][y] = '*';
	else if(type == g_null)
		graph[x][y] = ' ';
}

/*	init the snake, give one node 
	return the head
	pos in the middle of array 'graph'
	graph[7][15]
*/
SNAKE* initSnake()
{
	SNAKE* head = (SNAKE*)malloc(sizeof(SNAKE));

	head->pos_x = (MAX_X+1)/2;
	head->pos_y = (MAX_Y+1)/2;
	head->next = NULL;

	updateGraph(head->pos_x, head->pos_y, g_snake);
	
	return head;
}

/* test whether the snake hit its body, 0-safe, 1-collision */ 
int collisionTest(SNAKE* head, int x, int y)
{
/* it's quite simple now, you can imagine when two snake in same room */
	if(graph[x][y] == '*')
		return 1;
	
	return 0;
}

/*	add a head at pos(x,y) to snake 
	assume pos is legal
	1-success, 0-fail
*/
int snakeAddHead(SNAKE** head, int x, int y)
{
	int collision = 0;

	if(head == NULL)
		return;

	SNAKE* newNode = (SNAKE*)malloc(sizeof(SNAKE));
	if(newNode == NULL)
		return;

	/*	
		add a head, if the new head pos exceed the bound,
		add in the other direction
	*/
	if(x <= MIN_X)
		newNode->pos_x = (MAX_X - 1) + x;
	else if(x >= MAX_X)
		newNode->pos_x = (MIN_X + 1) + (x - MAX_X);
	else
		newNode->pos_x = x;
	
	if(y <= MIN_Y)
		newNode->pos_y = (MAX_Y - 1) + y;
	else if(y >= MAX_Y)
		newNode->pos_y = (MIN_Y + 1) + (y - MAX_Y);
	else
		newNode->pos_y = y;

	/* before add the new head, you have to detect the collision */
	collision = collisionTest(*head, newNode->pos_x,newNode->pos_y);

	if(collision)
	{
		writeLog("collision!\n");
		return 0;
	}
	
	/* add the node to the head if there is no collison */
	if(!collision)
	newNode->next = *head;
	*head = newNode;
	
	//writeLog("head pos (%d, %d)\n", (*head)->pos_x, (*head)->pos_y);
	
	updateGraph(newNode->pos_x, newNode->pos_y, g_snake);

	return 1;
}

/* delete the tail node from the snake */
void deleteTail(SNAKE** head)
{
	writeLog("delete tail...\n");
	if(*head == NULL)
		return;

	SNAKE* newNode = *head;
	
	if(newNode->next == NULL)
	{
		updateGraph((*head)->pos_x, (*head)->pos_y, g_null);
		free(*head);
		*head = NULL;
		return;
	}

	while(newNode->next->next != NULL)
		newNode = newNode->next;

	SNAKE* tailNode = newNode->next;
	newNode->next = NULL;

	updateGraph(tailNode->pos_x, tailNode->pos_y, g_null);
	free(tailNode);
	tailNode = NULL;
}

void killSnake(SNAKE** head)
{
	//writeLog("killing snake hahahahahaha!\n");
	while(*head != NULL)
		deleteTail(head);
}

void failHint()
{
	//writeLog("do fail hint...\n");
	int i,j;

	/* this seems unnecessary, but for guarantee */
	for(i = 1; i<14; i++)
		for(j = 1; j<29; j++)
			graph[i][j] = ' ';

	/* a sad face is quite simple--:( */
	graph[7][14] = ':';
	graph[7][15] = '(';

}


/* 	move the snake 
	1-up
	2-down
	3-right
	4-left

	return: 1-success, 0-fail
*/
int moveSnake(SNAKE** head, DIRECTION direc)
{
	int eated = 0;
	int ret = 1;
	int moved = 0;
	
	if(*head == NULL)
		return;

	if(direc < dir_up || direc > dir_right)
		return ;
	writeLog("move snake in direction %d!\n", direc);
	/* move according to direction */
	switch(direc)
	{
		case dir_up:
			if(lastDirec != dir_down)	
			{	/* you can not change the current direction to the opposite */
				if(graph[(*head)->pos_x - 1][(*head)->pos_y] == 'o')
					eated = 1;
				ret = snakeAddHead(head, (*head)->pos_x-1, (*head)->pos_y);
				if(!ret)
					return ret;
				
				lastDirec = dir_up;
				moved =	1;
			}
			else
				moveDirec = lastDirec; 
			break;
		case dir_down:
			if(lastDirec != dir_up)
			{
				if(graph[(*head)->pos_x + 1][(*head)->pos_y] == 'o')
					eated = 1;
				ret = snakeAddHead(head, (*head)->pos_x+1, (*head)->pos_y);
				if(!ret)
					return ret;
				
				lastDirec = dir_down;
				moved = 1;
			}
			else
				moveDirec = lastDirec;
			break;
		case dir_right:
			if(lastDirec != dir_left)
			{
				if(graph[(*head)->pos_x][(*head)->pos_y + 1] == 'o')
					eated = 1;
				
				ret = snakeAddHead(head, (*head)->pos_x, (*head)->pos_y + 1);
				if(!ret)
					return ret;

				lastDirec = dir_right;
				moved = 1;
			}
			else
				moveDirec = lastDirec;
			break;
		case dir_left:
			if(lastDirec != dir_right)
			{
				writeLog("snake moving in direction %d!\n", direc);
				if(graph[(*head)->pos_x][(*head)->pos_y - 1] == 'o')
					eated = 1;
				ret = snakeAddHead(head, (*head)->pos_x, (*head)->pos_y - 1);
				if(!ret)
					return ret;
				writeLog("snake moved in direction %d!\n", direc);
				lastDirec = dir_left;					
				moved = 1;
			}
			else
				moveDirec = lastDirec;
			break;
	}
	
	//writeLog("snake moved in direction %d!\n", direc);
	/* snake moved without eat a food, you have to delete the tail */
	if(moved==1 && 0==eated)
		deleteTail(head);

	return ret;
}


/* print function, screen flush per 10ms */
void print()
{
	struct timespec t;
	t.tv_sec = 0;
	t.tv_nsec = 10000000;	/* 10 ms */

	//printf("\r");
	while(1)
	{
		/* clear the screen and reset the cursor */
		CLEAR_SCR();	
		RESET_CURSOR();

		int i,j;
		for(i = 0; i<15; i++)
		{
			for(j = 0; j<30; j++)
			{
				printf("%c", graph[i][j]);
			}
			printf("\n");
		}
		nanosleep(&t, NULL);	/* sleep  10ms */
	}
}

/* initialize the array, form a box */
void init()
{
	int i,j;
	for(i = 0; i<30; i++)
	{
		graph[0][i] = '*';
		graph[14][i] = '*';
	}

	for(i = 1; i<14; i++)
	{
		graph[i][0] = '*';

		for(j = 1; j<29; j++)
		{	graph[i][j] = ' ';	}

		graph[i][29] = '*';
	}
}

/* function to get the keyboard input,
up, down, left, right sort of thing */
void getInput()
{
	while(1)
	{
		char tmp;
		//writeLog("geting input...\n");
		scanf("%c", &tmp);
	
		//writeLog("key board input is: %c\n", tmp);
		switch(tmp)
		{
			case 'w':	moveDirec = dir_up;	break;
			case 's':	moveDirec = dir_down;	break;
			case 'a':	moveDirec = dir_left;	break;
			case 'd':	moveDirec = dir_right;	break;
		}
	}
}

void doMove(SNAKE* head)
{
	sleep(1);
	moveSnake(&head, moveDirec);
}

int main(void)
{
	pthread_t th_print, th_getInput;
	int ret, i, j;
	moveDirec = dir_right;
	SNAKE* mySnake = initSnake();
	
	init();

	ret = pthread_create(&th_print, NULL, &print, NULL);
	if(ret != 0)
	{
		writeLog("create print thread failed!\n");
		exit(EXIT_FAILURE);
	}
	
	ret = pthread_detach(th_print);	
	if(ret != 0)
	{
		writeLog("print thread detach failed!\n");
		exit(EXIT_FAILURE);
	}	

	ret = pthread_create(&th_getInput, NULL, &getInput, NULL);
	if(ret != 0)
	{
		writeLog("create get input thread failed!\n");
		exit(EXIT_FAILURE);
	}
	
	ret = pthread_detach(th_getInput);	
	if(ret != 0)
	{
		writeLog("get input thread detach failed!\n");
		exit(EXIT_FAILURE);
	}

	i = 1;
	j = 0;

	graph[7][20] = 'o';	
	graph[6][15] = 'o';
	graph[8][22] = 'o';
	graph[2][3]  = 'o';
	graph[12][4] = 'o';
	graph[10][15] = 'o';

	struct timespec t;
	t.tv_sec = 0;
	t.tv_nsec = 500000000;	/* 500 ms */

	while(mySnake != NULL) /* while snake is still alive */
	{	
		if(!moveSnake(&mySnake, moveDirec))
		{			
			killSnake(&mySnake);
			failHint();
		}
		writeLog("present direction : %d\n", moveDirec);
		nanosleep(&t, NULL);
//		moveSnake(&mySnake, dir_up);
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值