• C语言实现贪吃蛇游戏


    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/times.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<ncurses.h>
    
    #define TBool        int
    #define True        1
    #define False        0
    #define    SHAPE_FOOD    '@'
    #define SHAPE_SNAKE    '#'
    #define    GAMEWIN_YLEN    20
    #define GAMEWIN_XLEN    60
    #define LOGWIN_YLEN    7
    #define LOGWIN_XLEN    (GAMEWIN_XLEN)
    #define LOGBUF_NUM    (LOGWIN_YLEN - 2)
    #define LOGBUF_LEN    (GAMEWIN_XLEN - 2)
    #define MAXLEVEL    12
    
    #define GetSnakeTail(s)    ((s)->head->front)
    
    WINDOW *logwin;        //declare a windows for displaying message
    #define INITRUNLOG()    logwin = newlogw()
    #define RUNLOG(str)        runlog(logwin, str)
    #define DESTROYRUNLOG()    delwin(logwin)
    
    int g_level;    //level of player
    
    enum TDirection
    {
        DIR_UP,
        DIR_DOWN,
        DIR_LEFT,
        DIR_RIGHT
    };
    
    struct TFood
    {
        int x;
        int y;
    };
    
    struct TSnakeNode
    {
        int y;
        int x;
        struct TSnakeNode *front;
    };
    
    struct TSnake
    {
        int length;
        struct TSnakeNode *head;
        enum TDirection dir;
    };
    
    int refreshgamew(WINDOW *win, struct TSnake *psnake);
    void movesnake(struct TSnake *psnake);
    int checksnake(struct TFood *pfood, struct TSnake *psnake);
    void snakegrowup(struct TFood *pfood, struct TSnake *psnake);
    void gameover(WINDOW *win, char *str);
    struct TSnakeNode *newsnakenode(struct TSnakeNode **ppsnode, int y, int x);
    struct TSnake *initsnake();
    void destroysnake(struct TSnake *psnake);
    void drawsnakew(WINDOW *win, struct TSnake *psnake);
    void drawfoodw(WINDOW *win, struct TFood *pfood, struct TSnake *psnake);
    TBool checkfood(struct TFood *pfood, struct TSnake *psnake);
    WINDOW*    newgamew();
    WINDOW* newlogw();
    void runlog(WINDOW* win, char *str);
    void cleanline(WINDOW *win, int y, int x);
    
    
    int main()
    {
        initscr();
        raw();
        noecho();
        keypad(stdscr, TRUE);
        curs_set(0);
        refresh();
    
        g_level = 1;
        INITRUNLOG();
    
        RUNLOG("    press 'q' or 'Q' to quit.");
        RUNLOG("    press 'w/s/a/d' or 'W/S/A/D' to move the snake.");
        RUNLOG("info:");
    
        WINDOW *gwin = newgamew();
    
        struct TSnake *psnake = initsnake();
        drawsnakew(gwin, psnake);
    
        while(refreshgamew(gwin, psnake) >= 0)
            ;
    
        getch();
        destroysnake(psnake);
        delwin(gwin);
        DESTROYRUNLOG();
        endwin();
    
        return 0;
    }
    
    WINDOW* newlogw()
    {
        WINDOW *win = newwin(LOGWIN_YLEN, LOGWIN_XLEN, GAMEWIN_YLEN + 2, 3);
        box(win, 0, 0);
        mvwprintw(win, 0, 2, " LOG ");
        wrefresh(win);
    
        return win;
    }
    
    WINDOW *newgamew()
    {
        WINDOW *win = newwin(GAMEWIN_YLEN, GAMEWIN_XLEN, 1, 3);
        box(win, 0, 0);
        mvwprintw(win, 0, 2, " GAME ");
        mvwprintw(win, GAMEWIN_YLEN - 1, 2, " Level: %d ", g_level);
        mvwprintw(win, GAMEWIN_YLEN - 1, 30, " Speed: %d ", (int)(g_level/3));
        wrefresh(win);
        return win;
    }    
    
    void runlog(WINDOW *win, char *str)
    {
        static char logbuf[LOGBUF_NUM][LOGBUF_LEN] = {0};
        static int index = 0;
        strcpy(logbuf[index], str);
    
        int i = 0;
    
        for (; i < LOGBUF_NUM; ++i)
        {
            cleanline(win, i+1, 1);
            mvwprintw(win, i+1, 1, logbuf[(index + i) % LOGBUF_NUM]);
            wrefresh(win);
        }
        index = (index + LOGBUF_NUM - 1) % LOGBUF_NUM;
    }
    
    //将窗口win的坐标(x,y)清空
    void cleanline(WINDOW *win, int y, int x)
    {
        char EMPTYLINE[LOGBUF_LEN] = {0}; //LOGBUF_LEN = 57
        
        //置空数组 0-56个位置
        memset(EMPTYLINE, ' ', LOGBUF_LEN-1);
        
        //将光标移动到窗口win(y,x)然后打印字符串EMPTYLINE
        mvwprintw(win, y, x, EMPTYLINE);
        
        //在指定窗口上显示内容
        wrefresh(win);    
    }
    int refreshgamew(WINDOW *win, struct TSnake *psnake)
    {
        static TBool ffood = False;
        struct TFood pfood;
    
        if (!ffood)
        {
            drawfoodw(win, &pfood, psnake);
            ffood = True;
        }
    
        int key = -1;
    
        fd_set set;
        FD_ZERO(&set);
        FD_SET(0, &set);
    
        struct timeval timeout;
        timeout.tv_sec = 0;
        timeout.tv_usec = (6 - (int)(g_level/3)) * 100 * 1000;
    
        if (select(1, &set, NULL, NULL, &timeout) < 0)
        {
            return -1;
        }
    
        if (FD_ISSET(0, &set))
        {
            while ((key = getch()) == -1);
            switch(key)
            {
                case 'w':
                case 'W':
                    (psnake->dir == DIR_DOWN) ? : (psnake->dir = DIR_UP);
                    break;
                case 's':
                case 'S':
                    (psnake->dir == DIR_UP) ? : (psnake->dir = DIR_DOWN);
                    break;
    
                case 'a':
                case 'A':
                    (psnake->dir == DIR_RIGHT) ? : (psnake->dir = DIR_LEFT);
                    break;
                    
                case 'd':
                case 'D':
                    (psnake->dir == DIR_LEFT) ? : (psnake->dir = DIR_RIGHT);
                    break;
    
                case 'q':
                case 'Q':
                    RUNLOG("Quit Game!");
                    gameover(win, "Quit Game!");
                    return -1;
    
                default:
                    break;
            }
        }
    
        movesnake(psnake);
        drawsnakew(win, psnake);
        switch(checksnake(&pfood, psnake))
        {
            case 0:
                break;
                
            case 1:
                ffood = False;
                if (++g_level > MAXLEVEL)
                {
                    RUNLOG("Win!!!");
                    gameover(win, "Win!!!");
                    return -1;
                }
    
                mvwprintw(win, GAMEWIN_YLEN-1, 2, " Level: %d ", g_level);
                mvwprintw(win, GAMEWIN_YLEN-1, 30, " Speed: %d ", (int)(g_level/3));
                wrefresh(win);
                RUNLOG("Level UP!");
                snakegrowup(&pfood, psnake);
                break;
    
            default:
                RUNLOG("Game over!");
                gameover(win, "Game over!");
                return -1;
        }
        return 1;
    }
    
    /**
     * stuct TSnake是一个倒置的首尾相连的链表,head->front指向snake的尾部
     * 如: [a]<-[b]<-[c]<-[d]    a为head
     *      |              ^     snake移动的时候,只用head指向d,
     *      `--------------'     并且修改d的(y,x)为snake头移动到的位置.
     */
    void movesnake(struct TSnake *psnake)
    {
        int hy = psnake->head->y;
        int hx = psnake->head->x;
    
        psnake->head = GetSnakeTail(psnake);
        switch(psnake->dir)
        {
            case DIR_UP:
                psnake->head->y = hy - 1;
                psnake->head->x = hx;
                break;
    
            case DIR_DOWN:
                psnake->head->y = hy + 1;
                psnake->head->x = hx;
                break;
    
            case DIR_LEFT:
                psnake->head->y = hy;
                psnake->head->x = hx - 1;
                break;
    
            case DIR_RIGHT:
                psnake->head->y = hy;
                psnake->head->x = hx + 1;
                break;
    
            default:
                break;
        }
    }
    
    int checksnake(struct TFood *pfood, struct TSnake *psnake)
    {
        int hy = psnake->head->y;
        int hx = psnake->head->x;
        if (hy <= 0 || hy >= GAMEWIN_YLEN || hx <= 0 || hx >= GAMEWIN_XLEN)
        {
            return -1;
        }
    
        struct TSnakeNode *pnode = GetSnakeTail(psnake);
        int i = 0;
        for (; i < psnake->length - 1; ++i, pnode = pnode->front)
        {
            if (hy == pnode->y && hx == pnode->x)
            {
                return -1;
            }
    
        }
        if (hy == pfood->y && hx == pfood->x)
        {
            return 1;
        }
        return 0;
    }
    
    void snakegrowup(struct TFood *pfood, struct TSnake *psnake)
    {
        struct TSnakeNode *pnode = (struct TSnakeNode *)malloc(sizeof(struct TSnakeNode));
    
        switch(psnake->dir)
        {
            case DIR_UP:
                pnode->y = psnake->head->y - 1;
                pnode->x = psnake->head->x;
                break;
                
            case DIR_DOWN:
                pnode->y = psnake->head->y + 1;
                pnode->x = psnake->head->x;
                break;
                
            case DIR_LEFT:
                pnode->y = psnake->head->y ;
                pnode->x = psnake->head->x - 1;
                break;
                
            case DIR_RIGHT:
                pnode->y = psnake->head->y;
                pnode->x = psnake->head->x + 1;
                break;
    
            default:
                break;
        }
    
        pnode->front = GetSnakeTail(psnake);
        psnake->head->front = pnode;
        psnake->head = pnode;
        ++psnake->length;
    }
    
    void gameover(WINDOW *win, char *str)
    {
        mvwprintw(win, (int)(GAMEWIN_YLEN/2), (GAMEWIN_XLEN/2 - strlen(str)/2), str);
        mvwprintw(win, (int)(GAMEWIN_YLEN/2 + 1), 20, "Please any key to quit...");
        wrefresh(win);
    }
    
    struct TSnakeNode *newsnakenode(struct TSnakeNode **ppsnode, int y, int x)
    {
        *ppsnode = (struct TSnakeNode *)malloc(sizeof(struct TSnakeNode));
        (*ppsnode)->y = y;
        (*ppsnode)->x = x;
        (*ppsnode)->front = NULL;
    
        return *ppsnode;
    }
    
    struct TSnake *initsnake()
    {
        struct TSnake *psnake = (struct TSnake *)malloc(sizeof(struct TSnake));
    
        psnake->dir = DIR_LEFT;
        psnake->length = 4;
    
        newsnakenode(&newsnakenode(&newsnakenode(&newsnakenode(&psnake->head, 4, 50)->front,
                        4, 53)->front, 4, 52)->front,
                            4, 51)->front = psnake->head;
    
        return psnake;
    }
    
    void destroysnake(struct TSnake *psnake)
    {
        struct TSnakeNode *psnode =    GetSnakeTail(psnake);
        struct TSnakeNode *ptmp = NULL;
    
        int i = 0;
        for (; i < psnake->length; ++i)
        {
            ptmp = psnode;
            psnode = psnode->front;
            free(ptmp);
        }
    
        free(psnake);
        psnake = NULL;
    }
    
    void drawsnakew(WINDOW *win, struct TSnake *psnake)
    {
        static int taily = 0;
        static int tailx = 0;
        if (taily != 0 && tailx != 0)
        {
            //将光标移动到指定窗口的指定位置(taily, tailx)然后输出字符
            mvwaddch(win, taily, tailx, ' ');
        }
    
        struct TSnakeNode *psnode = GetSnakeTail(psnake);
    
        int i = 0;
        for (; i < psnake->length; ++i)
        {
            mvwaddch(win, psnode->y, psnode->x, SHAPE_SNAKE);
            psnode = psnode->front;
        }
    
        taily = GetSnakeTail(psnake)->y;
        tailx = GetSnakeTail(psnake)->x;
    
        wrefresh(win);
    }
    
    
    void drawfoodw(WINDOW *win, struct TFood *pfood, struct TSnake *psnake)
    {
        do
        {
            pfood->y = random() % (GAMEWIN_YLEN - 2) + 1;
            pfood->x = random() % (GAMEWIN_XLEN - 2) + 1;
        }while(False == checkfood(pfood, psnake));
        
        checkfood(pfood, psnake);
        mvwaddch(win, pfood->y, pfood->x, SHAPE_FOOD);
        wrefresh(win);
    }
    
    //检查food出现的位置不能在snake上
    TBool checkfood(struct TFood *pfood, struct TSnake *psnake)
    {
        struct TSnakeNode *pnode = GetSnakeTail(psnake);
    
        int i = 0;
        for (; i < psnake->length; ++i, pnode = pnode->front)
        {
            if (pfood->y == pnode->y && pfood->x == pnode->x)
            {
                return false;
            }
        }
    
        return true;
    }

    两个重点:

    1.整个窗口的坐标以(0,0)为原点,x轴,y轴建立坐标点,x轴正向和y轴负向作为数值增大的方向,所以坐标移动时向下y轴坐标+1,反之-1

    2.蛇移动时,每次只移动一个单位距离,所以只需要将最后一个节点拿走,放到要移动的那个位置上就整体上有移动的感觉了,至于移动的位置会有函数检测是否合法。

    注意:

    程序用到了ncurses库,如果没有的话需要下载:

    # 更新软件源中的所有软件列表
    $ sudo apt-get update
    
    # 安装 `curses` 库
    $ sudo apt-get install libncurses5-dev

    编译时最后要加上-lncurses

  • 相关阅读:
    2017年年终总结
    7只老鼠测试100个瓶子
    jgs--多线程和synchronized
    springboot&&vue简单的景点信息管理系统
    springboot&&vue前后端分离入门案例
    npm安装教程
    springboot整合mybatisplus
    spring整合Mybatis-plus
    Springboot简单练手的记账本
    SpringBoot整合thymeleaf简单的CRUD
  • 原文地址:https://www.cnblogs.com/wanghao-boke/p/11996380.html
Copyright © 2020-2023  润新知