在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;
}