还没写完
//【例20-5】贪吃蛇游戏(源文件文件名snake2019.c)。
/**
* 项目名称:贪吃蛇游戏
* 开发人员:BBC
* 开发日期:2019年3月25日
* 编译方法:gcc snake2019.c -o snake.exe -static -lmycon -lwinmm -std=c99
* 游戏说明:
* 贪吃蛇自动移动,碰墙则死掉,共有3条生命,食物颜色随机产生。
* 配置文件可用命令行参数的形式指定,如果无效则采用默认的配置文件snake.txt,
* 如果默认配置文件也不存在或配置读取错误,则采用程序中默认的参数启动游戏
*/
#include
#include
#include
#include
#include
#include
#define DEBUG 0 //是否输出调试信息
#define CONFIGFILE "snake.txt" //默认配置文件名
#define WIDTH 80 //屏幕宽度
#define HEIGHT 24 //屏幕高度
#define INITLEN 1 //蛇身初始长度
#define STEP 1 //蛇每次移动距离
#define MAXNUM 50 //屏幕中最多食物数
#define NUM 5 //屏幕中食物数
#define LIFES 3 //蛇的生命条数
#define SPEED 100 //蛇的移动速度(延迟时间,越小越快)
#define UP 0 //贪吃蛇移动方向:向上
#define DOWN 1 //贪吃蛇移动方向:向下
#define LEFT 2 //贪吃蛇移动方向:向左
#define RIGHT 3 //贪吃蛇移动方向:向右
#define BACKCOLOR 0x3 //背景颜色
#define WALLCOLOR 0xa //墙的颜色
#define SNAKECOLOR 0xe //蛇的颜色
#define INFOCOLOR 0xf //提示信息颜色
struct node //蛇身结点坐标,双向链表存储
{
int x; //蛇身结点的x坐标
int y; //蛇身结点的y坐标
struct node *next; //蛇头到蛇尾方向
struct node *before; //蛇尾到蛇头方向
} *head, *tail, *p; //head:蛇头 tail:蛇尾 p:工作指针
struct game_info
{
int dx[MAXNUM], dy[MAXNUM]; //屏幕中食物的坐标
int score; //吃到的食物数,也即玩家得分数
int start, now, total_time; //计时用的初始时间、现在时间、总用时秒数
int direction; //贪吃蛇当前移动方向
int lifes; //蛇的当前生命条数
int oldx, oldy; //移动前原来的蛇尾坐标,供擦除蛇尾用
int len; //蛇身当前实际长度(每吃一个食物增长1)
int is_dead; //蛇是否碰墙死掉
int init_snake_length; //初始时蛇身的长度
int init_lifes; //初始时蛇的生命数
int init_speed; //初始时蛇的移动速度
int init_food_number; //初始时的食物数
int init_back_color; //初始时的背景色
int init_wall_color; //初始时边墙的颜色
int init_snake_color; //初始时蛇的颜色
int init_info_color; //游戏提示信息颜色
int init_direction; //初始时蛇的移动方向
};
//调试用函数
void debug(struct node *head)
{
struct node *p = head;
if (DEBUG)
{
gotoxy(0, 24);
set_color(0x3, 0xf);
while (p)
{
printf("(%d,%d) ", p->x, p->y);
p = p->next;
}
getkey();
}
}
//画出游戏边墙
void draw_wall(struct game_info *pinfo)
{
int i;
set_color(pinfo->init_back_color, pinfo->init_wall_color);
for (i = 0; i < WIDTH; i += 2)
{
gotoxy(i, 0);
printf("━"); //画出上边墙
gotoxy(i, HEIGHT - 1);
printf("━"); //画出下边墙
}
for (i = 0; i < HEIGHT; i++)
{
gotoxy(0, i);
printf("┃"); //画出左边墙
gotoxy(78, i);
printf("┃"); //画出右边墙
}
gotoxy(0, 0);
printf("┏"); //画出左上墙角
gotoxy(WIDTH - 2, 0);
printf("┓"); //画出右上墙角
gotoxy(0, HEIGHT - 1);
printf("┗"); //画出左下墙角
gotoxy(WIDTH - 2, HEIGHT - 1);
printf("┛"); //画出右下墙角
}
//显示相关信息
void show_info(struct game_info *pinfo)
{
gotoxy(22, HEIGHT);
set_color(pinfo->init_back_color, pinfo->init_info_color);
printf("得分:%d", pinfo->score);
gotoxy(37, HEIGHT);
set_color(pinfo->init_back_color, pinfo->init_info_color);
printf("用时:%d秒", pinfo->total_time);
gotoxy(52, HEIGHT);
set_color(pinfo->init_back_color, pinfo->init_info_color);
printf("生命:%2d", pinfo->lifes);
}
//初始化食物坐标
void init_food(struct game_info *pinfo)
{
int i;
//随机产生各个食物的坐标
for (i = 0; i < pinfo->init_food_number; i++)
{
pinfo->dx[i] = (rand() % (WIDTH - 4)) + 2;
pinfo->dy[i] = (rand() % (HEIGHT - 3)) + 1;
}
}
//随机产生不等于bcolor的颜色
//主要用于防止产生和背景色相同的食物
int get_color(int bcolor)
{
int color;
do
{
color = rand() % 16;
}
while (color == bcolor);
return color;
}
//画出食物
void draw_food(struct game_info *pinfo)
{
int i;
for (i = 0; i < pinfo->init_food_number; i++)
{
gotoxy(pinfo->dx[i], pinfo->dy[i]);
//食物颜色随机产生
set_color(pinfo->init_back_color, get_color(pinfo->init_back_color));
printf("O");
}
}
//初始化蛇身坐标
void init_snake(struct game_info *pinfo)
{
int i;
pinfo->len = pinfo->init_snake_length; //蛇身初始长度
head = NULL;
tail = NULL;
//蛇的初始位置在屏幕中间
for (i = 0; i < pinfo->len; i++)
{
p = (struct node *)malloc(sizeof(struct node));
p->next = NULL;
p->before = NULL;
p->x = (WIDTH - pinfo->len) / 2 + i;
p->y = HEIGHT / 2;
p->next = head;
if (i > 0)
{
head->before = p;
}
head = p;
if (i == 0)
{
tail = p;
}
}
head = p; //thead指向蛇头,ttail指向蛇尾
pinfo->direction = pinfo->init_direction;//初始化蛇的运行方向为向右
pinfo->oldx = tail->x; //保存蛇尾的x坐标位置
pinfo->oldy = tail->y; //保存蛇尾的y坐标位置
//debug(head);
}
//检测是否吃到食物:1 吃到 0 没吃到;tx, ty为新的蛇头坐标
int check_eat(struct game_info *pinfo, int tx, int ty)
{
int i, j;
//遍历每一个食物,检测食物是否和蛇头坐标一致
for (i = 0; i < pinfo->init_food_number; i++)
{
if ((pinfo->dx[i] == tx) && (pinfo->dy[i] == ty))
{
//已经吃到食物,得分加1
pinfo->score++;
//显示新的得分
show_info(pinfo);
//重新生成一个食物
pinfo->dx[i] = (rand() % (WIDTH - 4)) + 2;
pinfo->dy[i] = (rand() % (HEIGHT - 3)) + 1;
//绘制新生成的食物
gotoxy(pinfo->dx[i], pinfo->dy[i]);
//食物颜色随机产生
set_color(pinfo->init_back_color, rand() % 16);
printf("O");
return 1;
}
}
return 0;
}
//画出贪吃蛇
void draw_snake(struct game_info *pinfo)
{
int i;
//debug(head);
set_color(pinfo->init_back_color, pinfo->init_snake_color);
p = head;
while (p)
{
gotoxy(p->x, p->y);
printf("h");
p = p->next;
}
gotoxy(pinfo->oldx, pinfo->oldy);
set_color(pinfo->init_back_color, pinfo->init_back_color);
printf(" ");
//debug();
}
//擦除贪吃蛇
void erase_snake(struct game_info *pinfo)
{
int i;
p = head;
set_color(pinfo->init_back_color, pinfo->init_back_color);
while (p)
{
gotoxy(p->x, p->y);
p = p->next;
printf(" ");
}
gotoxy(pinfo->oldx, pinfo->oldy);
printf(" ");
//释放蛇对应的数据结构
p = head;
while (head)
{
p = head->next;
free(head);
head = p;
}
head = NULL;
tail = NULL;
//debug();
draw_wall(pinfo); //重画边框墙面
}
//获取游戏启动时的时间
void get_start_time(struct game_info *pinfo)
{
SYSTEMTIME system_time; //计时用的数据结构
//获取本地时间
GetLocalTime(&system_time);
pinfo->start = system_time.wMinute * 60 + system_time.wSecond;
}
//获取现在的时间,计算游戏已用时秒数,使用了Windows的API函数
void get_now_time(struct game_info *pinfo)
{
SYSTEMTIME system_time; //计时用的数据结构
GetLocalTime(&system_time);
pinfo->now = system_time.wMinute * 60 + system_time.wSecond;
pinfo->total_time = pinfo->now - pinfo->start;
}
//结束游戏,显示成绩
void game_over(struct game_info *pinfo)
{
erase_snake(pinfo);
show_info(pinfo);
gotoxy(WIDTH / 2 - 11, HEIGHT / 2);
set_color(pinfo->init_back_color, pinfo->init_info_color);
printf("游戏结束!你总计得到%d分!", pinfo->score);
getkey(); //按任意键退出游戏
exit(0); //退出游戏
}
//贪吃蛇死掉一次后重启
void restart(struct game_info *pinfo)
{
void start_game(struct game_info *);
erase_snake(pinfo);
init_snake(pinfo);
draw_snake(pinfo);
show_info(pinfo);
start_game(pinfo);
}
//贪吃蛇撞墙死掉一次
void die_once(struct game_info *pinfo)
{
//debug();
pinfo->lifes--;
pinfo->is_dead = 0;
if (!pinfo->lifes) //是否还有生命
{
game_over(pinfo); //无生命则结束
}
else
{
restart(pinfo); //否则重启游戏
}
}
//根据方向键移动贪吃蛇;贪吃蛇移动规则:擦除蛇尾,添加蛇头
//如果玩家没有按键则按蛇的当前移动方向自动移动
void move_snake(struct game_info *pinfo)
{
int i, key; //key:玩家按键
int tx, ty; //临时变量
int directkey[4] = {72, 80, 75, 77}; //依次为上下左右光标键键值
pinfo->is_dead = 0; //是否撞墙
if (!kbhit())
{
delay(pinfo->init_speed); //自动移动时每次延迟指定的时间
key = directkey[pinfo->direction];
}
else
{ //玩家按键则不延时
key = getkey();
}
switch (key) //根据按键更改新的蛇头结点
{
case 75: //向左的光标键
pinfo->direction = LEFT;
tx = head->x - STEP;
ty = head->y;
if (tx < 2) //撞左边墙
{
tx = 2;
pinfo->is_dead = 1;
}
break;
case 77: //向右的光标键
pinfo->direction = RIGHT;
tx = head->x + STEP;
ty = head->y;
if (tx > WIDTH - 3)
{ //撞右边墙
tx = WIDTH - 3;
pinfo->is_dead = 1;
}
break;
case 80: //向下的光标键
pinfo->direction = DOWN;
ty = head->y + STEP;
tx = head->x;
if (ty > HEIGHT - 2)
{ //撞下边墙
ty = HEIGHT - 2;
pinfo->is_dead = 1;
}
break;
case 72: //向上的光标键
pinfo->direction = UP;
ty = head->y - STEP;
tx = head->x;
if (ty < 1)
{ //撞上边墙
ty = 1;
pinfo->is_dead = 1;
}
break;
default: //按其它键无效返回
return;
}
//debug();
//改变结点(tx, ty)为新的蛇头
p = (struct node *)malloc(sizeof(struct node));
p->x = tx;
p->y = ty;
p->next = head;
head->before = p;
head = p;
pinfo->oldx = tail->x; //保存原来的蛇尾坐标
pinfo->oldy = tail->y;
if (pinfo->is_dead)
{
die_once(pinfo); //蛇碰墙生命数减1,长度归为初始值
}
//检测是否吃到食物,没吃到则去掉原来的蛇尾,如果吃到
//则不删除蛇尾,从而使蛇身长度加1
//贪吃蛇移动原则为:删除蛇尾,添加蛇头
if (!check_eat(pinfo, tx, ty))
{
p = tail->before;
p->next = NULL;
free(tail);
tail = p;
p = NULL;
}
//debug();
//绘制移动后的贪吃蛇
draw_snake(pinfo);
}
//游戏默认参数配置
void default_config(struct game_info *pinfo)
{
pinfo->init_snake_length = INITLEN;
pinfo->init_lifes = LIFES;
pinfo->init_speed =SPEED;
pinfo->init_food_number = NUM;
pinfo->init_back_color = BACKCOLOR;
pinfo->init_wall_color = WALLCOLOR;
pinfo->init_snake_color = SNAKECOLOR;
pinfo->init_info_color = INFOCOLOR;
pinfo->init_direction = RIGHT;
}
//初始化配置信息,如果读取配置文件有误则采用默认配置信息
void init_config(struct game_info *pinfo, int argc, char *argv[])
{
FILE *fp;
if (argc == 2) //有命令行参数则处理,argv[1]应为指定的配置文件名
{
if ((fp = fopen(argv[1], "r")) == NULL) //指定的配置文件不存在
{
if ((fp = fopen(CONFIGFILE, "r")) == NULL) //读默认的配置文件
{
default_config(pinfo); //没有或配置文件出错则采用默认配置信息
}
else if (fscanf(fp, "%d%d%d%d%d%d%d%d%d%d", \
&pinfo->init_snake_length, &pinfo->init_lifes, &pinfo->init_speed, \
&pinfo->init_back_color, &pinfo->init_wall_color, \
&pinfo->init_snake_color, &pinfo->init_info_color, \
&pinfo->init_food_number, &pinfo->init_direction) != 9)
{
default_config(pinfo); //配置文件读取错误则采用默认配置信息
}
}
else
{
if (fscanf(fp, "%d%d%d%d%d%d%d%d%d%d", &pinfo->init_snake_length,
&pinfo->init_lifes, &pinfo->init_speed, &pinfo->init_back_color, \
&pinfo->init_wall_color,&pinfo->init_snake_color, \
&pinfo->init_info_color, &pinfo->init_food_number, \
&pinfo->init_direction) != 9)
{
default_config(pinfo); //配置文件读取错误则采用默认配置信息
}
}
}
else //无命令行参数则采用默认的配置文件
{
if ((fp = fopen(CONFIGFILE, "r")) == NULL)
{
default_config(pinfo); //没有或配置文件出错则采用默认配置信息
}
else if (fscanf(fp, "%d%d%d%d%d%d%d%d%d%d", &pinfo->init_snake_length,
&pinfo->init_lifes, &pinfo->init_speed, &pinfo->init_back_color, \
&pinfo->init_wall_color, &pinfo->init_snake_color, \
&pinfo->init_info_color, &pinfo->init_food_number, \
&pinfo->init_direction) != 9)
{
default_config(pinfo); //配置文件读取错误则采用默认配置信息
}
}
//程序中以下三个参数会发生变化
pinfo->len = pinfo->init_snake_length;
pinfo->direction = pinfo->init_direction;
pinfo->lifes = pinfo->init_lifes;
fclose(fp);
}
//游戏初始化
void init_game(struct game_info *pinfo, int argc, char *argv[])
{
//初始化配置信息(配置文件为snake.txt,无或错则默认配置)
init_config(pinfo, argc, argv);
//初始化游戏计时时间
pinfo->total_time = 0;
//初始化玩家得分
pinfo->score = 0;
//初始化窗口大小为80行25列
set_size(80, 25);
//利用背景色清屏
set_color(pinfo->init_back_color, pinfo->init_back_color);
cls();
//更改控制台窗口的标题
set_title("贪吃蛇游戏!");
//初始化随机数种子
srand((unsigned)time(NULL));
//隐藏光标
hide_cursor();
//画出边墙
draw_wall(pinfo);
//初始化蛇身坐标
init_snake(pinfo);
//debug(head);
//画出贪吃蛇
draw_snake(pinfo);
//初始化食物坐标
init_food(pinfo);
//画出食物
draw_food(pinfo);
//获取游戏启动时间
get_start_time(pinfo);
//显示相关信息
show_info(pinfo);
}
//启动贪吃蛇游戏
void start_game(struct game_info *pinfo)
{
while (1)
{
move_snake(pinfo); //移动一次贪吃蛇
get_now_time(pinfo); //获取现在的时间
show_info(pinfo); //更新游戏相关信息
}
}
int main(int argc, char *argv[])
{
struct game_info info; //封装所有游戏相关信息
init_game(&info, argc, argv); //游戏初始化
start_game(&info); //启动游戏
return 0;
}