通过一个二维数组 来实现 边界用"#“表示,”*"代表snake " "@"代表食物,
数组来记录蛇的位置,通过不断的刷新来造成视觉上的不移动。
// gcc snake.c -lpthread
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#define KEYCODE_U 0x41 // 向上按键
#define KEYCODE_D 0x42 // 向下按键
#define KEYCODE_L 0x44 // 向左按键
#define KEYCODE_R 0x43 // 向右按键
int kfd = 0;
struct termios cooked, raw;
char dir = KEYCODE_U; // 当前蛇的移动方向
// 获取键盘响应:上、下、左、右键
void* get_dir(void *a)
{
while(1)
{
char c;
tcgetattr(kfd, &cooked); // 得到 termios 结构体保存,然后重新配置终端
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &=~ (ICANON | ECHO);
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
if(read(kfd, &c, 1) < 0)
{
perror("read():");
exit(-1);
}
tcsetattr(kfd, TCSANOW, &cooked);//在程序结束时在恢复原来的配置
dir = c;
}
}
unsigned char map[17][17] = {0}; // 游戏地图
unsigned char snake[50] = {133}; // 初始化蛇坐标,游戏开始的时候蛇在(8,5)这个位置
unsigned char food = 67; // 食物的坐标,游戏开始的时候在(4,3)这个位置
int len = 1; // 保存蛇的当前长度
// 将 数字 转化为坐标系
void num_to_xy(unsigned char num, unsigned char *x, unsigned char *y)
{
*x = num/16;
*y = num%16;
}
// 更新地图数据
void update_map()
{
int i,j,k;
//move_snake();
unsigned char x_snake; //记录蛇的x,y坐标
unsigned char y_snake;
unsigned char food_x,food_y; //记录食物的x,y坐标
num_to_xy(food,&food_x,&food_y);//将食物数值转化为坐标
for(i=0;i<17;i++)
{
for(j=0;j<17;j++)
{
if(0==i||16==i||0==j||16==j)//设置边界
{
map[i][j]='#';
}
else if(i==food_x&&j==food_y)//食物
{
map[i][j]='@';
}
else
{
map[i][j] = ' ';
}
}
}
for(i=0;i<17;i++) //打印蛇 因为蛇的长度不断变化所以需要重新来一遍遍历
{
for(j=0;j<17;j++)
{
for(k=0;k<len;k++)
{
num_to_xy(snake[k],&x_snake,&y_snake);
if(i==x_snake&&j==y_snake)
{
map[i][j]='*';
}
}
}
}
}
// 打印地图
void print_map()
{
system("clear");//清屏
int i,j;
for(i=0;i<17;i++)
{
for(j=0;j<17;j++)
{
printf("%c",map[i][j]);
}
printf("\n");
}
usleep(500000/(len/4+1)); //让蛇移动的速度随着蛇长度变化而变快
}
// 生成食物
unsigned char generate_food()
{
unsigned char food1,food_x,food_y;
int getfood = 1;//循环判断条件
int i;
srand((unsigned int)time(NULL));//随机函数
while(getfood)
{
food = rand()%255;
num_to_xy(food,&food_x,&food_y);
getfood = 0; //生成一个食物后置0
if(food_x == 0||food_x == 16 ||food_y==0||food_y==16)//如果食物生成在边界 重新生成
{
getfood = 1;//置1重新生成
}
for(i=0;i<len;i++)//如果食物被蛇吃了 再生成新的食物
{
if(snake[i] == food)
{
getfood = 1;
}
}
}
return food;
}
// 移动蛇
void move_snake()
{
unsigned char x,y; // 坐标
int newsna = snake[0];//先记录蛇头的位置
num_to_xy(snake[0], &x, &y); // 获取蛇头的坐标
int cz=0;
// 判断移动方向
switch (dir)
{
case KEYCODE_U: // 向上移动
x--;
break;
case KEYCODE_D: // 向下移动
x++;
break;
case KEYCODE_L: // 向左移动
y--;
break;
case KEYCODE_R: // 向右移动
y++;
break;
}
int i;
cz=snake[len-1];//记录蛇尾的位置
for(i = len-1;i>0;i--)
{
snake[i]=snake[i-1]; //将蛇头逆序覆盖
}
snake[0] = x*16+y;//重新记录蛇头位置
if(snake[0]==food)//当蛇头碰到食物时
{
len++;//长度+1
food = generate_food();//生成新的食物
snake[len-1]=cz;//将原先记录的蛇头位置赋值给蛇尾
}
}
// 判断蛇是否应该存活,如果返回值是0代表应该存活,1代表不应该存活
int isalive()
{
unsigned char x_snake;
unsigned char y_snake;
int life = 0;//活着为0
int i,j;
num_to_xy(snake[0],&x_snake,&y_snake);//将蛇头位置转换成x,y坐标
if(x_snake == 0|| x_snake ==16 || y_snake == 0||y_snake == 16)
{
life = 1;//如果蛇头碰到边界 置1使游戏结束
}
for(i=0;i<len;i++)//如果蛇头碰到蛇身 游戏结束
{
for(j=i+1;j<len;j++)
{
if(snake[i] == snake[j])
{
life = 1;
}
}
}
return life;
}
int main()
{
// 开启一个线程用于获取键盘的上下左右键响应
pthread_t tid1;
pthread_create(&tid1, NULL, get_dir, NULL);
while(1)
{
// 更新地图数据
update_map();
// 打印地图
print_map();
// 移动蛇
move_snake();
if (isalive() == 1)
{
break;
}
}
tcsetattr(kfd, TCSANOW, &cooked);//在程序结束时在恢复原来的配置
printf ("Game Over!\n");
return 0;
}