// 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; // 当前蛇的移动方向
unsigned char map[17][17] = {0}; // 游戏地图
unsigned char snake[50] = {133}; // 初始化蛇坐标,游戏开始的时候蛇在(8,5)这个位置,存储0~255的unsigned char
unsigned char food = 67; // 食物的坐标,游戏开始的时候在(4,3)这个位置
int len = 1; // 保存蛇的当前长度
// 获取键盘响应:上、下、左、右键
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;
}
}
// 将 数字 转化为坐标系
void num_to_xy(unsigned char num, unsigned char *x, unsigned char *y)
{
*y = num % 16;
*x = num / 16;
}
// 更新地图数据
void update_map()
{
//打印边界
unsigned char x, y;
int i, j;
for (i = 0; i < 17; i++)
{
map[0][i] = '#';
map[16][i] = '#';
map[i][0] = '#';
map[i][16] = '#';
}
//打印空白
for (i = 1; i < 16; i++)
{
for (j = 1; j < 16; j++)
{
map[i][j] = ' ';
}
}
//食物坐标
num_to_xy(food, &x, &y);
map[y][x] = '$';
//打印蛇
for (i = 0; i < len; i++)
{
num_to_xy(snake[i], &x, &y);
map[y][x] = '*';
}
}
// 打印地图
void print_map()
{
int i, j;
//打印数组二维地图
for (i = 0; i < 17; i++)
{
for (j = 0; j < 17; j++)
printf("%c", map[i][j]);
printf("\n");
}
//程序睡眠刷新
usleep(1000000/(len));
//清屏
system("clear");
}
// 生成食物
unsigned char generate_food()
{
srand((unsigned int)time(NULL)); //随机数种子
unsigned char food; //食物的1个无符号字节数
unsigned char x, y; //食物转换为高低字节坐标
food = rand() % 256;//产生8位的无符号随机数
num_to_xy(food, &x, &y);
//避免食物生成在边界
while(x == 0 || x == 16 || y == 0 || y == 16)
{
//重新生成随机数
food = rand() % 256;
num_to_xy(food, &x, &y);
}
//避免食物生成在蛇的身体上
int i = 0;
for (i = 0; i < len; i++)
{
if (snake[i] == food)
{
//重新生成随机数
food = rand() % 256;
num_to_xy(food, &x, &y);
}
}
return food;//返回食物坐标
}
// 移动蛇
void move_snake()
{
unsigned char x,y; // 坐标
num_to_xy(snake[0], &x, &y); // 获取蛇头的坐标
// 判断移动方向
switch (dir)
{
case KEYCODE_U: // 向上移动
y--;
break;
case KEYCODE_D: // 向下移动
y++;
break;
case KEYCODE_L: // 向左移动
x--;
break;
case KEYCODE_R: // 向右移动
x++;
break;
}
//交换a[i] 和 a[i - 1]的数据位置
unsigned char last = snake[0];//保存原蛇头位置
snake[0] = (unsigned char)(x * 16 + y);//生成头的位置
unsigned char tmp;
//移动蛇的身体
int i;
for (i = 1; i < len; i++)
{
tmp = snake[i]; //保留tmp作为下一节身体的数据
snake[i] = last;//将上一节身体的数据给本节
last = tmp; //将本节a[i]的数据保留给下一节
}
//如果蛇头和食物坐标重合,证明蛇吃到食物
//len是蛇的长度,0 -- len - 1
if (snake[0] == food)
{
snake[len] = last;
len++;
food = generate_food();
}
}
// 判断蛇是否应该存活,如果返回值是0代表应该存活,1代表不应该存活
int isalive()
{
unsigned char x, y;
num_to_xy(snake[0], &x, &y);//获取头部位置坐标
//判断有没有碰到边界
if (x == 0 || x == 16 || y == 0 || y == 16)
return 1;
//判断有没有咬到自己
//遍历数组比较蛇头有没有和身体重叠
int i;
for (i = 1; i < len; i++)
{
//咬到了自己
if (snake[0] == snake[i])
return 1;
}
//存活
return 0;
}
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;
}
编程题——贪吃蛇
猜你喜欢
转载自blog.csdn.net/qq_35599308/article/details/84612483
今日推荐
周排行