用C语言实现贪吃蛇(基于Linux系统 ubuntu 环境,调用curses库实现)

前言

这是一个基于C语言链表开发的贪吃蛇游戏

其实贪吃蛇游戏要解决的主要问题就是

1、这个游戏的基本组成元素和数据结构

2、如何初始化贪吃蛇并正常行走

  1. 如何判断事件发生

游戏说明

  1. 按方向键上下左右,可以实现蛇移动方向的改变。

  1. 超出边界或者蛇头撞到蛇尾,游戏结束。

实现代码

#include<curses.h>
#include<stdlib.h>

#define UP     1
#define DOWN  -1
#define LEFT   2
#define RIGHT -2

typedef struct Snake
{
    int hang;//横坐标
    int lie;//纵坐标
    struct  Snake* next;//下一个节点地址
}Snake;

struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;

struct Snake food;


void initfood()
{
    int x = rand() % 20;//横坐标
    int y = rand() % 20;//纵坐标
    food.hang = x;
    food.lie = y;
}


int hasfood(int i,int j)
{
        if(food.hang == i && food.lie == j)
        {
            return 1;
        }
    return 0;
}


int hasSnake(int i,int j)
{
    struct Snake*p = head;
    while(p != NULL)
    {
        if(p->hang == i && p->lie == j)
        {
            return 1;
        }
        p = p->next;
    }
    return 0;
}


void initNcurses()
{
    initscr();
    keypad(stdscr,1);
    noecho();
}


void gamePic()
{
    int hang;//行
    int lie;//列
    move(0,0);
    //从第0行开始循环
    for(hang = 0;hang<20;hang++)
    {
        if(hang ==0)
        {
            for(lie = 0;lie<20;lie++)
            {
                printw("--");
        }
        printw("\n");
            }

//-----------------------------------------------------------------
    if(hang>0 &&hang<=19)
    {
        for(lie = 0;lie<=20;lie++)
        {
            if(lie == 0||lie == 20)
            {
                printw("|");
            }
            //打印蛇的位置
            else if(hasSnake(hang,lie))
                {
                    printw("[]");
                    //打印食物的位置
            }else if(hasfood(hang,lie))
                {
                printw("##");
                }
                else{
                printw("  ");
                }
               }
        printw("\n");
             }
//------------------------------------------------------

            if(hang ==19)
    {
        for(lie =0;lie<20;lie++)
        {
            printw("--");
        }
        printw("\n");
        printw("My name is ChenYi   2023.3.2,food,hang =%d food.lie =%d ,key = %d\n ",food.hang,food.lie,key);
            }
        }
}


void addnode()
{
    //创建新节点
     Snake *new = (Snake*)malloc(sizeof(Snake));
    new->next = NULL;
    
    switch(dir)
    {
        case UP:
            new->hang =tail->hang-1;
            new->lie = tail->lie;

            break;
        case DOWN:
            new->hang =tail->hang+1;
            new->lie = tail->lie;

            break;
        case LEFT:
            new->hang =tail->hang;
            new->lie = tail->lie-1;

            break;
        case RIGHT:
            new->hang =tail->hang;
            new->lie = tail->lie+1;

            break;
    }
    //尾巴指向新节点
    tail->next = new;
    tail = new;
}


//初始化头节点,并赋值
void initSnake()
{
    struct Snake *p;
    dir = RIGHT;     
    //删除节点
    while(head!=NULL)
    {
        //临时变量保存头节点位置
        p = head;
        head = head->next;
        //释放头节点
        free(head);
    }
    //初始化食物函数
    initfood();
    //初始化头节点
    head = (Snake *)malloc(sizeof(Snake));
    //横坐标为2
    head->hang = 2;
    //纵坐标为1
    head->lie  = 1;
    head->next = NULL;
    //让头和尾都指向一个节点
    tail = head;
    //申请3个节点,让蛇初始长度为三
    addnode();
    addnode();
    addnode();
}


void delnode()
{
    Snake *new;
    new = head;
    head = head->next;
    free(new);
}


int ifsnakedle()
{
    struct     Snake *p;
    p = head;
    //判断蛇有没有超出边界
    if(tail->hang < 0|| tail->lie == 0 ||tail->hang == 20|| tail->lie == 20 )
    {
        return 1;
    }
    //判断蛇头有没有咬住尾巴
    while(p->next != NULL)
    {
        if(p->hang == tail->hang && p->lie == tail->lie)
        {
            return 1;
        }
        p = p->next;
    }
    return 0;
}


void moveSnake()
{
    //添加新节点
    addnode();
    //如果蛇头坐标和食物坐标一样,说明食物被吃掉了
    if(hasfood(tail->hang,tail->lie))
    {
        //初始话食物,
        initfood();
    }else
    {
        //删除节点
        delnode();
    }
    //判断有没有咬到自己或超出边界
    if(ifsnakedle())
    {        
        //咬到自己或超出边界了,初始化
        initSnake();
    }
}

void refreshjiemian()
{
    while(1)
    {
            moveSnake();
            gamePic();
            refresh();
            //控制蛇的移动速度,
            usleep(100000);
    }
}


void trun(int diretction)
{
    if(abs(dir) != abs(diretction))
    {
        dir = diretction;
    }
}


void changgeDir()
{
    while(1)
    {
        key = getch();
        switch(key)
        {
            case KEY_DOWN:
                trun(DOWN);
                break;
            case KEY_UP:
                trun(UP);
                break;
            case KEY_LEFT:
                trun(LEFT);
                break;
            case KEY_RIGHT:
                trun(RIGHT);
                break;
        }
    }    
}


int main()
{
    pthread_t t1;
    pthread_t t2;
    
    initNcurses();

    initSnake();//初始化创建节点

    gamePic();//遍历

    pthread_create(&t1,NULL,refreshjiemian,NULL);

    pthread_create(&t2,NULL,changgeDir,NULL);
    while(1);

    getch();

    endwin();

    return 0;
}

代码讲解

1,定义一个结构体,结构体当中存储着该段蛇身的位置坐标,并且定义了两个结构体指针,指向蛇头和蛇尾。

初始化

1.初始化结构体,为蛇头(head)申请一个空间,并对其赋初始值。

2.(160-170)行,是用来释放上一轮创建的空间的,如果不释放上一轮创建的空间,那每一次从新开始都会去创建新的空间,造成空间浪费,所以这边加了释放空间的代码,实现思路如下,先创建一个节点p用来保存头节点(head)的地址,(注意,直接释放头节点会找不到下一个节点,所以需要一个临时变量来先保存),在释放头节点(head);

3.封装一个食物函数,使用rand取模求余,赋值给食物的横坐标和纵坐标,让食物随机出现在屏幕上。

4,如何添加节点和控制蛇头的方向?

1 ,我先申请一个了新的节点,通过控制节点里面的数据来实现控制蛇头方向,

这里我采用了swich函数,通过调用curses库来获取键盘上输入的信息。

如果现在的蛇头在{4,9}位置上,往上走的话,纵坐标不变,横坐标坚毅,就是在{3,9}处添加一个新节点。

遍历

1.第一部分(71-81)

横坐标从零开始循环,如果横坐标满足条件等于0,继续执行下一步,纵坐标从零开始循环,并且 打印虚线--(上框),下框同理。

第二部分(84-106)

1.先判断横坐标,有没有在规定范围内,如果在,纵坐标从零开始扫描,并且根 据 条件打印左右框。

2.打印蛇的位置,通过调用hasSnake函数,每次扫描的时候都会把,横纵坐标传进去与蛇的坐标相匹配,该函数返回一的时候打印蛇的位置。

3。打印食物的位置也是如此,每次遍历都会把横纵坐标传进去,与之匹配,返回一打印食物位置。

1.第三部分(109-119)

横坐标从零开始循环,如果横坐标满足条件等于19,继续执行下一步,纵坐标从零开始循环,并且 打印虚线--(上框),下框同理。

如何让蛇移动和刷新界面同时实现呢?

这里我使用pthread创建了两个线程一个运行蛇的移动,一个来运行界面的刷新。

蛇要怎么才能移动?

如果向右移动的话,就给蛇头添加一个节点,蛇尾删除一个节点,其他方向同理。

在第225行调用hasfood判断了一下,如果蛇头吃到食物(蛇头坐标和食物坐标一致),只添加节点不删除尾巴节点。

1,判断蛇有没有超出边界。和判断蛇有没有咬住自己的尾巴。

如何判断蛇有没有咬住自己的尾巴?这里我使用了while循环来遍历链表,如果当前这个node的下一个不等于空,就开始判断蛇头的坐标等不等于蛇尾的坐标,如果等于说明撞上了,直接返回一,初始化,游戏将重新开始。

记录第一次写博客,写的不好还望大家多指导。

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Ubuntu是一种基于Linux操作系统的开源操作系统,而C++是一种通用的编程语言贪吃蛇是一种经典的游戏,玩家通过控制蛇的移动来吃食物并不断增长身体长度。在Ubuntu使用C++编写贪吃蛇游戏可以通过使用相关的开发工具和实现。 在Ubuntu使用C++编写贪吃蛇游戏,你可以使用一些图形如SDL、SFML或者Qt来实现游戏界面的绘制和用户交互。你需要编写代码来处理蛇的移动、食物的生成、碰撞检测等逻辑。 以下是一个简单的示例代码,展示了如何使用C++和SDL实现一个基本的贪吃蛇游戏: ```cpp #include <SDL2/SDL.h> const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; const int SNAKE_SIZE = 20; int main() { SDL_Init(SDL_INIT_VIDEO); SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); bool running = true; while (running) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { running = false; } } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); // 绘制贪吃蛇和食物 SDL_RenderPresent(renderer); } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` 这只是一个简单的示例,你可以根据自己的需求进行扩展和完善。你可以添加蛇的移动逻辑、食物的生成和碰撞检测等功能,使游戏更加完整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

走下去-别回头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值