当前位置:网站首页>c语言(循环链表)实现贪吃蛇的基本功能

c语言(循环链表)实现贪吃蛇的基本功能

2020-11-09 12:58:25 osc_0tqqardd

总体思想

利用循环链表将一条蛇的坐标进行存储,然后利用gotoxy()函数(可以将光标定位到指定的位置),此时根据蛇的坐标进行输出“@”,输出多几个既可以产生一条蛇。通过遍历循环链表进行蛇的移动,对循环链表的插入元素,产生蛇变长的效果。下面为各功能实现的函数

1.贪吃蛇地图函数map()
2.蛇的移动move(),up(),left()等函数
3.产生食物food()和吃到食物eat_food()
4.蛇吃到食物时产生的变长效果snake_link()函数
5.判断蛇的死亡,分别为撞墙hit_wall()和自杀suicide()




1.贪吃蛇地图函数map()

游戏地图采用的是应该封闭的区域,采用一个数组a[25][50],将此数组初始化为0,将游戏墙的边缘赋值为1,当数组为0,输出" ",数组为1,输出“#”,产生一个地图。
代码如下:

void map()   //创建蛇的地图
{
   
    
    int a[25][50] = {
   
    0};
    int i,j;
    for(i = 0; i < 50; i++)
    {
   
    
        a[0][i] = 1;
        a[24][i] =1;
    }
    for(i = 0; i < 25; i++)
    {
   
    
        a[i][0] = 1;
        a[i][49] =1;
    }
    for(i = 0; i < 25; i++)
        for(j = 0; j < 50; j++)
        {
   
    
            if(j%50 == 0)
                printf("\n");
            if(a[i][j] == 0)
            {
   
    
                printf(" ");
            }
            else
            {
   
    
                printf("#");
            }
        }
}

2.蛇的移动move(),up(),left()等函数

move()函数主要对蛇的上下左右进行更改在此采用switch函数进行解决(下面代码中ch为全局变量)
代码如下

void move(struct snake *p)   //蛇的移动函数
{
   
     
    while(1)
    {
   
     
        ch = getch();
        switch(ch)
        {
   
     
        case 'W':p = up(p);break;
        case 'A':p = left(p);break;
        case 'D':p = right(p);break;
        case 'S':p = down(p);break;
        }
    }
}


让蛇动起来的即我们主要对蛇的坐标进行更改,此时蛇头移动一次我们就利用gotoxy()函数进行输出“@”,然后在蛇尾输出“ ”,循环往复就可以产生蛇移动的效果,蛇的上下左右则只需在移动一个方向的时候对单一的坐标x或y进行更改,然后对更改的坐标保存进循环链表即可。移动函数则主要有up(),left()等,因为做法差不多,在此只对up()函数进行展示
代码如下

struct snake *up(struct snake *p) //向上移动
{
   
     

    int x;
    int y;
    x = p->pre->x;                 //将蛇头坐标赋值给x,y
    y = p->pre->y;
    while(p)                      //对循环链表的遍历,即蛇的遍历
    {
   
     
        Sleep(SNAKE_SPEED);        //蛇移动的速度
        y--;                       //向上移动则只需将纵坐标进行减,就可以实现蛇向上移动的效果
        gotoxy(p->x,p->y);         //定位到蛇尾,输出“ ”即蛇尾消失
        printf(" ");
        gotoxy(x, y);              //定位到蛇头输出,"@",结合上面的蛇尾消失又进行蛇头打印,产生蛇移动的效果
        printf("@");
        suicide(p,x,y);            //判断蛇头是否撞到蛇身
        p = p->next;               //将蛇头的坐标变为下一个
        p->pre->x = x;             //此时将前一个蛇头变成蛇尾,通过不断的遍历产生不断移动的效果
        p->pre->y = y;
        food();                    //产生食物
        eat_food(p,x,y);           //判断是否吃到食物
        hit_wall(y);               //判断是否撞墙
        if(kbhit()) break;        //判断是否有按键输入,有就进行蛇移动方向的改变
    }

    return p;
}


3.产生食物food()和吃到食物eat_food()

食物和吃到食物,产生食物则采用了产生随机数,产生一个食物的x,y坐标分别存放在全局变量food_xy[2]数组里面,最后利用gotoxy(food_xy[0],food_xy[1])随机产生食物
代码如下

void food()                       //产生食物
{
   
     
    int i;
    if(!flag)                     //根据flag的值来判断地图上是否有食物
    {
   
     
        srand( (unsigned)time( NULL ) );
        for( i = 0; i < 2; i++ )  //对food_(x,y)来随机赋值
        {
   
     
            food_xy[i] = rand()%24+2;
            while(food_xy[0] == 1 || food_xy[0] == 25) //这两个while为了防止食物
                food_xy[0] = rand()%24+2;             //的坐标与地图的边缘重叠
            while(food_xy[1] >= 49 || food_xy[1] == 1)
                food_xy[1] =rand()%24+2;
        }
        gotoxy(food_xy[0],food_xy[1]);  //打印食物
        printf("*");
        flag = 1;
    }
}

吃到食物eat_food(),则我们只需判断蛇头是否和食物的坐标重叠,若重叠则表明蛇吃到了食物
代码如下

void eat_food(struct snake *p,int x, int y)           //蛇吃到食物,即主要是对蛇头的x,y坐标和
{
   
                                                          //food_xy的坐标进行匹配对比,若相同即调
    if(x == food_xy[0] && y == food_xy[1])            //snake_link函数即可
    {
   
     
        p = snake_link(p);
        flag = 0;                                     //表明食物被吃,准备重新产生食物
        printSnake(p);
        gotoxy(8,0);
        score = score + 1;                            //得分
        printf("%d",score);
    }
}

4.蛇吃到食物时产生的变长效果snake_link()函数

蛇的变长,当蛇吃到食物的时候,此时我们将食物的坐标变成蛇头,然后进行重新的打印蛇,即可以有蛇变成的效果产生,实质为对循环链表进行元素的插入。
在这里插入图片描述
即通过这样将食物的坐标插进去循环链表,达到蛇变成的效果
代码如下


struct snake *snake_link(struct snake *p)      //蛇的连接
{
   
     
    struct snake *q;
    q = (struct snake *)malloc(sizeof(struct snake)); //即主要是实现了对循环链表的插入元素,再
    q->x = food_xy[0];                                //进行打印蛇,即可有吃到食物蛇变长的结果
    q->y = food_xy[1];
    q->pre = p->pre;
    p->pre->next = q;
    p->pre = q;
    q->next = p;
    return p;
}

5.判断蛇的死亡,分别为撞墙hit_wall()和自杀suicide()

撞墙,则只需判断蛇头的单一坐标x轴或者y轴是否与墙壁的坐标是否相等,若相等则说明蛇撞墙了
代码如下

void hit_wall(int n)       //判断蛇是否撞墙,即对蛇的单一坐标x或者y进行判断,若等于墙壁的值,即游戏结束
{
   
     
    if(ch == 'W'|| ch == 'S' )
        if(n == 1 || n == 25)              //墙壁的坐标值
        {
   
     
            gotoxy(0,26);
            printf("游戏结束!");
            printf("你的得分:%d",score);
            exit(0);
        }
    if(ch == 'A'|| ch == 'D' )
        if(n == 0 || n == 49)
        {
   
     
            gotoxy(0,26);
            printf("游戏结束!");
            printf("你的得分:%d",score);
            exit(0);
        }
}

自杀suicide()即蛇头是否有撞到了蛇身,做法是把蛇头的坐标拿出来,与蛇身的坐标进行对比如果相等,说明蛇头撞到了蛇身,本质上是循环链表的值进行匹配,遍历
代码如下

void suicide(struct snake *p, int x, int y) //自杀,即撞到自己本身的时候游戏结束
{
   
     
    struct snake *q;                         //把蛇头坐标传递进来,然后与其自己身体坐标做对比若有相等则表明,蛇头撞到了蛇身
    q = p;
    while(q != p->next)                      //即对循环链表的遍历匹配
    {
   
     
        if(p->x == x && p->y == y)
        {
   
     
            gotoxy(0,26);
            printf("游戏结束!");
            printf("你的得分:%d",score);
            exit(0);
        }
        else
            p = p->next;
    }
}

到此蛇的基本功能已经讲完,以下是全部代码。


全部代码如下

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#define SNAKE_SPEED 200 //蛇移动的速度
int score = 0;         //成绩得分
int flag = 0;          //用于判断地图上是否存在食物,0为不存在食物
int food_xy[2];         //定位食物的位置
char ch;               //用来决定蛇的移动方向
struct snake   //定义一条循环链表的蛇
{
   
      
    int x;
    int y;
    struct snake *next;
    struct snake *pre;
};

void HideCursor()//把蛇移动的时候产生的光标进行隐藏,隐藏光标函数
{
   
      
	CONSOLE_CURSOR_INFO cursor;
	cursor.bVisible = FALSE;
	cursor.dwSize = sizeof(cursor);
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorInfo(handle, &cursor);
}

void gotoxy(int x, int y)   //定位光标函数,用来实现蛇的移动,和食物的出现(传送x,y可以将光标定位到x,y)
{
   
      
    HideCursor();
    COORD coord = {
   
      x,y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void map()   //创建蛇的地图
{
   
      
    int a[25][50] = {
   
      0};
    int i,j;
    for(i = 0; i < 50; i++)
    {
   
      
        a[0][i] = 1;
        a[24][i] =1;
    }
    for(i = 0; i < 25; i++)
    {
   
      
        a[i][0] = 1;
        a[i][49] =1;
    }
    for(i = 0; i < 25; i++)
        for(j = 0; j < 50; j++)
        {
   
      
            if(j%50 == 0)
                printf("\n");
            if(a[i][j] == 0)
            {
   
      
                printf(" ");
            }
            else
            {
   
      
                printf("#");
            }
        }
}

struct snake *createSnake()   //给蛇进行初始化,构建一条蛇,本质为循环链表
{
   
      
    int i;
    struct snake *head,*p,*q;
    p = q = (struct snake *)malloc(sizeof(struct snake));
    head = NULL;
    head = p;
    head->pre = NULL;
    for( i = 0; i < 5; i++)
    {
   
      
        p->x = 25 - i;
        p->y = 13;
        p->pre = head->pre;
        head->pre = p;
        q->next = p;
        q = p;
        p = (struct snake *)malloc(sizeof(struct snake));
    }
    q->next = head;
    return head;

}

void printSnake(struct snake *p)   //打印蛇,利用gotoxy()来对蛇进行打印,只需遍历一次循环链表即可将坐标可视化为一条蛇
{
   
      
    struct snake *q;
    q = p;
    while(q != p->next)             //循环链表的遍历
    {
   
      
        gotoxy(p->x,p->y);          //根据坐标和定位光标函数来打@,实现输出蛇
        printf("@");
        p = p->next;
    }
    gotoxy(p->x,p->y);
    printf("@");
    gotoxy(0,0);
    printf("你的得分:");          //初始化得分
}

void food()                       //产生食物
{
   
      
    int i;
    if(!flag)                     //根据flag的值来判断地图上是否有食物
    {
   
      
        srand( (unsigned)time( NULL ) );
        for( i = 0; i < 2; i++ )  //对food_(x,y)来随机赋值
        {
   
      
            food_xy[i] = rand()%24+2;
            while(food_xy[0] == 1 || food_xy[0] == 25) //这两个while为了防止食物的坐标与地图的边缘重叠
                food_xy[0] = rand()%24+2;
            while(food_xy[1] >= 49 || food_xy[1] == 1)
                food_xy[1] =rand()%24+2;
        }
        gotoxy(food_xy[0],food_xy[1]);  //打印食物
        printf("*");
        flag = 1;
    }
}

struct snake *snake_link(struct snake *p)      //蛇的连接
{
   
      
    struct snake *q;
    q = (struct snake *)malloc(sizeof(struct snake)); //即主要是实现了对循环链表的插入元素,再进行打印蛇,即可有吃到食物蛇变长的结果
    q->x = food_xy[0];
    q->y = food_xy[1];
    q->pre = p->pre;
    p->pre->next = q;
    p->pre = q;
    q->next = p;
    return p;
}

void eat_food(struct snake *p,int x, int y)           //蛇吃到食物,即主要是对蛇头的x,y坐标和food_xy的坐标进行匹配对比,若相同即调用snake_link函数即可
{
   
      
    if(x == food_xy[0] && y == food_xy[1])
    {
   
      
        p = snake_link(p);
        flag = 0;
        printSnake(p);
        gotoxy(8,0);
        score = score + 1;
        printf("%d",score);
    }
}

void hit_wall(int n)       //判断蛇是否撞墙,即对蛇的单一坐标x或者y进行判断,若等于墙壁的值,即游戏结束
{
   
      
    if(ch == 'W'|| ch == 'S' )
        if(n == 1 || n == 25)
        {
   
      
            gotoxy(0,26);
            printf("游戏结束!");
            printf("你的得分:%d",score);
            exit(0);
        }
    if(ch == 'A'|| ch == 'D' )
        if(n == 0 || n == 49)
        {
   
      
            gotoxy(0,26);
            printf("游戏结束!");
            printf("你的得分:%d",score);
            exit(0);
        }
}


void suicide(struct snake *p, int x, int y) //自杀,即撞到自己本身的时候游戏结束
{
   
      
    struct snake *q;                         //把蛇头坐标传递进来,然后与其自己身体坐标做对比若有相等则表明,蛇头撞到了蛇身
    q = p;
    while(q != p->next)                      //即对循环链表的遍历匹配
    {
   
      
        if(p->x == x && p->y == y)
        {
   
      
            gotoxy(0,26);
            printf("游戏结束!");
            printf("你的得分:%d",score);
            exit(0);
        }

        else
            p = p->next;
    }
}

struct snake *up(struct snake *p) //向上移动
{
   
      

    int x;
    int y;
    x = p->pre->x;                 //将蛇头坐标赋值给x,y
    y = p->pre->y;
    while(p)                      //对循环链表的遍历,即蛇的遍历
    {
   
      

        Sleep(SNAKE_SPEED);        //蛇移动的速度
        y--;                       //向上移动则只需将纵坐标进行减,就可以实现蛇向上移动的效果
        gotoxy(p->x,p->y);         //定位到蛇尾,输出“ ”即蛇尾消失
        printf(" ");
        gotoxy(x, y);              //定位到蛇头输出,"@",结合上面的蛇尾消失又进行蛇头打印,产生蛇移动的效果
        printf("@");
        suicide(p,x,y);            //判断蛇头是否撞到蛇身
        p = p->next;               //将蛇头的坐标变为下一个
        p->pre->x = x;             //此时将前一个蛇头变成蛇尾,通过不断的遍历产生不断移动的效果
        p->pre->y = y;
        food();                    //产生食物
        eat_food(p,x,y);           //判断是否吃到食物
        hit_wall(y);               //判断是否撞墙
        if(kbhit()) break;        //判断是否有按键输入,有就进行蛇移动方向的改变
    }

    return p;


}

struct snake *left(struct snake *p) //向左移动
{
   
      
    int x;
    int y;
    x = p->pre->x;
    y = p->pre->y;
    while(p)
    {
   
      

        Sleep(SNAKE_SPEED);
        x--;
        gotoxy(p->x,p->y);
        printf(" ");
        gotoxy(x, y);
        printf("@");
        suicide(p,x,y);
        p = p->next;
        p->pre->x = x;
        p->pre->y = y;
        food();
        eat_food(p,x,y);
        hit_wall(x);
        if(kbhit()) break;
    }
    return p;
}

struct snake *down(struct snake *p)  //向下移动
{
   
      
    int x;
    int y;
    x = p->pre->x;
    y = p->pre->y;
    while(p)
    {
   
      

        Sleep(SNAKE_SPEED);
        y++;
        gotoxy(p->x,p->y);
        printf(" ");
        gotoxy(x, y);
        printf("@");
        suicide(p,x,y);
        p = p->next;
        p->pre->x = x;
        p->pre->y = y;
        food();
        eat_food(p,x,y);
        hit_wall(y);
        if(kbhit()) break;
    }
    return p;
}

struct snake *right(struct snake *p)   //向右移动
{
   
      
    int x;
    int y;
    x = p->pre->x;
    y = p->pre->y;
    while(p)
    {
   
      

        Sleep(SNAKE_SPEED);
        x++;
        gotoxy(p->x,p->y);
        printf(" ");
        gotoxy(x, y);
        printf("@");
        suicide(p,x,y);
        p = p->next;
        p->pre->x = x;
        p->pre->y = y;
        food();
        eat_food(p,x,y);
        hit_wall(x);
        if(kbhit()) break;
    }
    return p;


}
void move(struct snake *p)   //蛇的移动函数
{
   
      
    while(1)
    {
   
      
        ch = getch();
        switch(ch)
        {
   
      
        case 'W':p = up(p);break;
        case 'A':p = left(p);break;
        case 'D':p = right(p);break;
        case 'S':p = down(p);break;
        }
    }
}
int main()
{
   
      
    struct snake *p;
    map();      //产生地图
    p = createSnake(); // 初始化蛇
    printSnake(p);   // 打印蛇
    move(p);        //移动蛇

    return 0;
}

版权声明
本文为[osc_0tqqardd]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/4263437/blog/4709418