贪吃蛇游戏实现(链表)

#C语言实现贪吃蛇
##一、需求
###通过游戏的实现,学习C语言知识的综合应用,以及解决问题的方法。
###1、通过wasd操作控制蛇的运动方向;
###2、蛇运动时不能碰到墙壁和自己的身体;
###3、食物动态产生,存在时间不同,分值不同;
###4、游戏可以暂停;
###5、游戏可以保存进度,有不同的版本;
###6、启动游戏可以从保存的进度开始游戏,也可以重新开始游戏;
##二、原理
###01、预备知识
####printf函数特殊效果
```
#define BLUE "\033[0;32;24m"
#define RED  "\033[0;31;24m"
#define DEPBLUE "\033[0;34;24m"
#define NOCOLOR "\033[0m"
//清除显示内容
#define CLEAR() printf("\033[2J")
//开始反显
#define HIGHT_LIGHT() printf("\033[7m")
//反显结束
#define NHIGHT_LIGHT() printf("\033[27m")
//指定位置开始打印内容,x为命令行的字符位置,从左往右从1开始
#define  MOVETO(x,y) printf("\033[%d;%dH",(y),(x))
```
####不需要回车获取字符的处理
```
void RestoreConsole(void){
    tcsetattr(STDIN_FILENO,TCSANOW,&oldt);
    fcntl(STDIN_FILENO,F_SETFL,oldf);
}
void perpareConsole(void){
    struct termios oldt,newt;    
    tcgetattr(STDIN_FILENO,&oldt);
    newt = oldt;
    newt.c_lflag &= ~(ECHO | ICANON);
    oldf = fcntl(STDIN_FILENO,F_GETFL);
    int newf = oldf | O_NONBLOCK;
    if(tcsetattr(STDIN_FILENO,TCSANOW,&newt) == -1 ||  
        fcntl(STDIN_FILENO,F_SETFL,newt ) == -1){
        RestoreConsole();
        exit(-1);
    }
}
```
###02、蛇前进的原理
####链尾为蛇头,遍历链表实现蛇的移动
```
if(cur_dir == RIGHT || cur_dir == LEFT)
        ntail->date.x += cur_dir  - 3
else
        ntail->date.y += cur_dir ;

void draw_snake(struct snake_node *head)
draw_snake
{
    struct snake_node *snake,*nsnake,*tail;
    system("clear");
    snake = head;
    nsnake = head;
    while(snake != NULL)
    {
        MOVETO(snake->date.x,snake->date.y);
        printf("*");
        printf("\n");
        tail = snake;
        snake = snake->next;
    }
}
```
###03、蛇是否死亡的判定
####遍历坐标是否重合
```
while(nsnake->next != NULL)        //判断链尾是否撞墙撞身体
    {
        if(tail->date.y <= 5 || tail->date.y >= 17 || tail->date.x <= 11 || tail->date.x >= 59)
        {
            MOVETO(tail->date.x,tail->date.y);
            printf("X");
            flag = 1;
        }
        if(tail->date.x == nsnake->date.x && tail->date.y == nsnake->date.y)
        {
            flag = 1;
        }
        nsnake = nsnake->next;
    }
```
###04、如何产生食物
####随机生成食物坐标
```
food = (struct food_node*)malloc(sizeof(struct food_node));
    food->date.x = rand() % (width- 20) + 12;
    food->date.y =   rand() % (HEIGHT- 9) + 13;
    food->date.time = rand()%90 + 60;
    food->next = NULL;
    foodhead = food;
```
###05、如何判断蛇吃到食物
####提前拿到下一个点坐标与食物坐标比较
```
nsnake = head;
    while (nsnake != NULL && nsnake->next != NULL)
    {
        nsnake = nsnake->next;
        ntail = nsnake;                    
    }
    if(cur_dir == RIGHT || cur_dir == LEFT)
    {
    x = ntail->date.x + cur_dir  - 3;
    y = ntail->date.y;
    }
    else
    {
    y = ntail->date.y + cur_dir ;
    x = ntail->date.x;
    }
    head->date.dir = cur_dir;
    
```
###06、实现蛇身增长
####生成新节点赋值食物的坐标添加到链尾(蛇头)
```
if(x == foodhead->date.x && y == foodhead->date.y)
    {
        i++;
        snake_l++;
        node = (struct snake_node*)malloc(sizeof(struct snake_node));    
        node->date.x = x;
        node->date.y  = y;
        node->date.dir = cur_dir;
        sorce += foodhead->date.time;
        foodhead->date.x = rand() % (width- 20) + 12;      //食物初始化
        foodhead->date.y =   rand() % (HEIGHT- 9) + 13;
        foodhead->date.time = rand()%90 + 60;
        ntail->next = node;
        ntail = node;
    }
```
###07、游戏数据文件设计
####版本号、蛇长和数据、食物数据、分数、速度
#####实现数据保存
```
void save_date()
{
    FILE *fp;
    int i;
    char buf[6];
    struct snake_node *p;
    struct food_node *f;
    fp = fopen("snake.dat","w");
    if(NULL == fp)
    {  
        perror("open");
        return;   //退出程序块
    }
    fwrite("SNAKEA",6,1,fp);
    fwrite(&ver,4,1,fp);                                     
    fwrite(&snake_l,4,1,fp);
    p = head;
    for(i = 0; i < snake_l; i++)
    {
        fwrite(&p->date,sizeof(snake_date),1,fp);
        p = p->next;
    }
    fwrite(&food_l,4,1,fp);                                   
    f = foodhead;
    for(i = 0; i < food_l; i++)
    {
        fwrite(&f->date,sizeof(food_date),1,fp);
        f = f->next;
    }
    fwrite(&sorce,4,1,fp);
    fwrite(&k,4,1,fp);
    fwrite(&flag,4,1,fp);
    fclose(fp);
```
#####文件读取
```
if(ver == 1)
                {
                    //printf("1");                
                    int i;
                    snake_date date;
                    food_date  fdate;
                    struct snake_node *snake,*nsnake;
                    struct food_node *nfood;
                    fread(&snake_l,4,1,fp); //蛇身体
                    //printf("snake length:%d\n",snake_l);
                    //getchar();
                    fread(&date,sizeof(snake_date),1,fp);
                    head = (struct snake_node*)malloc(sizeof(struct snake_node));
                    head->date = date;
                    snake = head;
                    for (int i = 1; i < snake_l; ++i)
                    {
                        nsnake = (struct snake_node*)malloc(sizeof(struct snake_node));
                        fread(&date,sizeof(snake_date),1,fp);
                        nsnake->date = date;
                        snake->next = nsnake;
                        snake = snake->next;
                    }
                       snake->next = NULL;
                       printf("UP = -1,DOWN = 1,LEFT = 2,RIGHT = 4\n");
                       printf("now direct is %d\n",nsnake->date.dir);
                       getchar();
                       getchar();
                    fread(&food_l,4,1,fp); //food note
                    fread(&fdate,sizeof(food_date),1,fp);   
                    //printf("fdate:%d\n",fdate.x);
                    //getchar();                     
                    foodhead = (struct food_node*)malloc(sizeof(struct food_node));
                    foodhead->date = fdate;
                    food = foodhead;
                    for (int i = 1; i < food_l; ++i)
                    {
                        nfood = (struct food_node*)malloc(sizeof(struct food_node));
                        fread(&fdate,sizeof(food_date),1,fp);
                        nfood->date = fdate;
                        food->next = nfood;
                        food = food->next;
                    }
                    food->next = NULL;
                    fread(&sorce,4,1,fp);//int类型
                    fread(&k,4,1,fp);   //int类型
                    fread(&flag,4,1,fp); //int类型;
                }
```
##三、设计
###01、蛇的数据结构
```
typedef struct n_snake_date
{
    int x;
    int y;
    enum MOVE_DIR dir;
}snake_date;
struct snake_node{
    snake_date date;
    struct snake_node *next;
}*head;
```
###02、食物的表示
```
typedef struct n_food_date
{
    int x;
    int y;
    int time;
}food_date;

struct food_node
{
    food_date date;
    struct food_node *next;
}*food,*foodhead;
```
###03、小功能实现
```
if(head->date.dir == cur_dir)             //同向加速
    {
        k = 10;
    }else
    {
        k = 0;
    }
```
###04、界面搭建
```
void display()
{
    
    int i;
    //system("clear");
    MOVETO(10,4);
    printf("wasd控制方向默认方向向右");
    MOVETO(10,5);
    printf("┏");
    for ( i = 1; i < width - 1; i++)
    {
        printf("—");
    }
    printf("┓");

    for ( i = 1; i < HEIGHT; ++i)
    {
        MOVETO(10,5+i);
        printf("|");
        MOVETO(10 + width-2 ,5+i);
        printf("|");

    }

    MOVETO(10,5+HEIGHT);
    printf("┗");
    for ( i = 1; i < width - 1; i++)
    {
        printf("—");
    }
    printf("┛");
}
```
###05、主函数实现
```
int main()
{
    int count = 0,f = 0;
    char ch;
    init_game();
    ch = getchar();
    while(1)
    {
        /*
        perpareConsole();
        int c = EOF, ch = EOF;
        while((c = getchar()) != EOF)
        {
            ch = c;
        }*/
        switch(kbhit())
        {
            case 'w':
            case 'W':
                cur_dir = UP;
                break;
            case 's':
            case 'S':
                cur_dir = DOWN;
                break;
            case 'a':
            case 'A':
                cur_dir = LEFT;
                break;
            case 'd': 
            case 'D':
                cur_dir = RIGHT;
                 break;
             case 'p':
             case 'P':
                 MOVETO(20,1);
                 printf("PAUSE ENTER P TO CONTINUE");
                 MOVETO(10,4);
                 printf("                          ");
                 MOVETO(20,2);
                 while((ch = getchar()) != 'p' &&(ch = getchar()) != 'P' );
                 MOVETO(20,1);
                 printf("                          ");
                 break;
             case 'v':
             case 'V':
                 printf("write down you want to save as 1 or 2\n");
                 while(1)
                 {
                     ch = getchar();
                     if(ch == '1')
                     {
                         ver = 1;
                         break;
                     }
                     else if(ch == '2')
                     {
                         ver = 2;
                         break;
                     }
                 }
                 save_date();
            case 'q':
            case 'Q':
                return 0;        
        }
        display();

        if(count == 5)
        {
            if (sorce > 0 && ver == 2)
            {
                f++;
                sorce -= 5;
            }
            count = 0;
            snake_move(head,foodhead);
            
        }
        draw_food(foodhead,head);
        draw_snake(head);
        usleep(1000 * 100 - (500 * k )- (10 * sorce));
        
        if (foodhead->date.time == 0)
        {
            foodhead = init_food();
        }
        if(flag == 1 || sorce < 0)
        {
            if (ver == 2)
            {
                printf("you insist on %d\n",f);
            }
            printf("game over\n");
            break;
        }
        count++;
    }
    //save_date();
    //RestoreConsole();
    return 0;
}
```


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值