贪吃蛇简单版本

 还没写完

//【例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;
}

你可能感兴趣的:(c语言)