Linux c 贪吃蛇 无curses、多线程、信号的实现

这一版贪吃蛇功能还不完全,我将继续更新,十分希望各位大神帮助完善
/************************************
终端控制和键盘输入部分
keyboard.h
************************************/
#ifndef __KEYBOARD_H_
#define __KEYBOARD_H_

#include 
#include 
#include 
#include 
#include 

//非阻塞无回显输入
int readch(void);  
//光标移动至指定位置
void gotoxy(int x,int y);   
//移动至指定位置并输出字符
void gotoxy_putc(int x,int y,char c);   
//移动至指定位置并输出字符串
void gotoxy_puts(int x,int y,char* c);
//初始化键盘输入和终端
void initkey();
//恢复键盘输入和终端属性
void relaykey();
//清屏
void clrter();

void clrter()
{
    system("clear");
}
int readch(void) 
{ 
        struct termios tm, tm_old; 
        int fd = STDIN_FILENO, c=0; 
      fd_set rfds;
        struct timeval tv;
        int retval;

        if(tcgetattr(fd, &tm) < 0) 
                return -1; 

        tm_old = tm; 

        cfmakeraw(&tm);

        FD_ZERO(&rfds);
        FD_SET(0, &rfds);

        tv.tv_sec  = 0;
        tv.tv_usec = 10;

        if(tcsetattr(fd, TCSANOW, &tm) < 0) 
                c = 0; 
      retval = select(1, &rfds, NULL, NULL, &tv);

        if (retval == -1) {
                c = 0;
        } else if (retval)  
            c = fgetc(stdin); 

        if(tcsetattr(fd, TCSANOW, &tm_old) < 0) 
                c = 0; 

        return c; 
}
void gotoxy(int x,int y)
{
    printf("\033[%d;%df\n",y,x);
}
void gotoxy_putc(int x,int y,char c)
{
    printf("\033[0m");
    printf("\033[%d;%df%c\n",y,x,c);
    printf("\033[8m");
}
void gotoxy_puts(int x,int y,char* c)
{
    printf("\033[0m");
    printf("\033[%d;%df%s\n",y,x,c);
    printf("\033[8m");
}
void initkey()
{
    clrter();
    system("stty -echo");
    printf("\033[8m");
    printf("\033[?25l");    
}
void relaykey()
{
    printf("\033[0m");
    system("stty echo");
    printf("\033[?25h");
}
#endif
/************************************
双向循环链表的实现(套用Linux内核)
List.h
************************************/
#ifndef __QUEUE_H__
#define __QUEUE_H__
/////////////////////////////////////////////////////////////////////////////////////////////////
//
//                                          队    列
//
/////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(_WIN32) || defined(_WIN64)      //Windows
#define NETLIST_EXPORT  __declspec(dllexport)
#else
#define NETLIST_EXPORT 
#endif  //defined(_WIN32) || defined(_WIN64)
#ifndef LIST_HEAD_DEF

    #define LIST_HEAD_DEF
    typedef struct LIST_HEAD
    {
        struct LIST_HEAD *Next;
        struct LIST_HEAD *Prev;
    } LIST_HEAD;

#endif /*LIST_HEAD_DEF*/

    #define INIT_LIST_HEAD(ptr) do { \
        (ptr)->Next = ptr; (ptr)->Prev = ptr; \
    } while (0)

    #define EMPTY_LIST_HEAD(ptr) do { \
        (ptr)->Next = 0; (ptr)->Prev = 0; \
    } while (0)


#ifdef WIN32
    #define offset_of(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
    #define container_of(ptr, type, member) (type *)((char *)ptr - offset_of(type, member))
#else
    #ifdef __compiler_offsetof
        #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
    #else
        #define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
    #endif

    #define container_of(ptr, type, member)                         \
    (                                                               \
        {                                                           \
            typeof( ((type *)0)->member ) *__mptr = (ptr);  \
            (type *)( (char *)__mptr - offsetof(type,member) );     \
        }                                                           \
    )
#endif

#define list_entry(ptr, type, member)   container_of(ptr, type, member)

#define ListForEachEntry(type,pos, head, member)                \
    for (pos = list_entry((head)->Next, type, member);          \
         pos->member.Next, &(pos->member) != (head);                \
         pos = list_entry(pos->member.Next, type, member)    \
         )

#define ListForEachEntrySafe(type,pos, n, head, member)         \
    for (pos = list_entry((head)->Next, type, member),          \
         n = list_entry(pos->member.Next, type, member);        \
         &pos->member != (head);                                \
         pos = n, n = list_entry(n->member.Next, type, member))

NETLIST_EXPORT void ListAddHead(LIST_HEAD *New, LIST_HEAD *Head);
NETLIST_EXPORT void ListInsert(LIST_HEAD *New, LIST_HEAD *Pos);
NETLIST_EXPORT void ListAddTail(LIST_HEAD *New, LIST_HEAD *Head);
NETLIST_EXPORT void ListDel(LIST_HEAD *entry);
NETLIST_EXPORT int  ListIsEmpty(const LIST_HEAD *Head);
#endif /* __QUEUE_H__ */

static void ListAdd(LIST_HEAD *New,LIST_HEAD *Prev,LIST_HEAD *Next)
{
    Next->Prev = New;
    New->Next =  Next;
    New->Prev =  Prev;
    Prev->Next = New;
}

void ListAddHead(LIST_HEAD *New, LIST_HEAD *Head)
{
    ListAdd(New, (LIST_HEAD *)Head->Next, Head);
}

void ListAddTail(LIST_HEAD *New, LIST_HEAD *Head)
{
    ListAdd(New, (LIST_HEAD *)Head->Prev, Head);
}

void ListInsert(LIST_HEAD *New, LIST_HEAD *Pos)
{
    Pos->Prev->Next = New;
    New->Next = Pos;
    New->Prev = Pos->Prev;
    Pos->Prev = New;
}

static void __ListDel(LIST_HEAD * Prev, LIST_HEAD * Next)
{
    Next->Prev = Prev;
    Prev->Next = Next;
}

void ListDel(LIST_HEAD *entry)
{
    if (entry->Prev)
    {
        __ListDel((LIST_HEAD *)entry->Prev, (LIST_HEAD *)entry->Next);
        entry->Prev = 0;
        entry->Next = 0;
    }
}

int ListIsEmpty(const LIST_HEAD *Head)
{
    return (LIST_HEAD *)Head->Next == Head;
}

/************************************
贪吃蛇的实现
************************************/

#include "keyboard.h"
#include "List.h"

#define ENVLENGTH 40
#define ENVWIDTH  20

static int snack_head_x = 20; 
static int snack_head_y = 10;

static int snack_last_x = 0; 
static int snack_last_y = 0;

typedef enum{UP,LEFT,DOWN,RIGHT} DIRECTION;
typedef enum{YES,NO} ISFOOD;
typedef struct food{
    int x;
    int y;
    ISFOOD f;
}food;

static food sfood;

static DIRECTION AIM = RIGHT;
static DIRECTION oldAIM;

const char snacknode = '@';

LIST_HEAD SHEAD;

typedef struct snack{
    int x;
    int y;
    LIST_HEAD snode;
}snack;

int ifinsnack(int x,int y)
{
    snack *pos;
    int st = 0;
    ListForEachEntry(snack,pos,(SHEAD.Next), snode)
    {
        if(pos->x == x && pos->y == y)
        {
            st = 1;
            break;
        }
    }
    return st;
}

void add_food()
{
    snack *pos; 

    while(1)
    {
        srand((int)time(0));
        sfood.x=2+(int)(rand()%(ENVLENGTH-2));
        srand(sfood.x);
        sfood.y=2+(int)(rand()%(ENVWIDTH-2));

        if(ifinsnack(sfood.x,sfood.y) == 0)
            break;
    }

    gotoxy_putc(sfood.x,sfood.y,'$');
    sfood.f = YES;
}

void add_snode(int x,int y)
{
    snack *sn = (snack*)malloc(sizeof(snack));
    sn->x = x;
    sn->y = y;
    ListAddTail(&(sn->snode),&SHEAD);
}

void initsnack()
{
    int x,y;
    INIT_LIST_HEAD(&SHEAD);
    add_snode(snack_head_x,snack_head_y);
    add_snode(snack_head_x-1,snack_head_y);
    add_snode(snack_head_x-2,snack_head_y);

    sfood.f = NO;
    for(x=1;x<=ENVLENGTH;x++)
    {
        gotoxy_putc(x,1,'*');
        gotoxy_putc(x,ENVWIDTH,'*');
    }
    for(y=1;y<=ENVWIDTH;y++)
    {
        gotoxy_putc(1,y,'*');
        gotoxy_putc(ENVLENGTH,y,'*');
    }
    gotoxy_puts(ENVLENGTH+2,3,"欢迎来到魔蛇峡谷");
    gotoxy_puts(ENVLENGTH+2,5,"w(W) 向上移动");
    gotoxy_puts(ENVLENGTH+2,7,"a(A) 向左移动");
    gotoxy_puts(ENVLENGTH+2,9,"s(S) 向下移动");
    gotoxy_puts(ENVLENGTH+2,11,"d(D) 向右移动");
    gotoxy_puts(ENVLENGTH+2,11,"q(Q) 退出游戏");
    gotoxy_puts(ENVLENGTH+2,13,"预祝您玩的愉快");
    gotoxy_puts(ENVLENGTH+2,15,"后续游戏功能会持续更新");
    gotoxy_puts(ENVLENGTH+2,17,"也欢迎各位大神优化一下");
}

void showsnack()
{
    snack *pos; 
    ListForEachEntry(snack,pos,&SHEAD, snode)
        gotoxy_putc(pos->x,pos->y,snacknode);
    gotoxy_putc(snack_last_x,snack_last_y,' ');
}

void movesnack()
{
    snack *pos,*posr;
    LIST_HEAD *p = SHEAD.Prev;
    pos = list_entry((p),snack,snode);
    snack_last_x = pos->x;
    snack_last_y = pos->y;
    while(p->Prev != &SHEAD)
    {
        pos = list_entry((p),snack,snode);
        posr = list_entry((p->Prev),snack,snode);
        pos->x = posr->x;
        pos->y = posr->y;
        p = p->Prev;
    }
    pos = list_entry((p),snack,snode);
    pos->x = snack_head_x;
    pos->y = snack_head_y;
}

void ifremove()
{

    if(AIM == UP)
    {
        if(oldAIM == DOWN)
            AIM = oldAIM;
    }
    if(AIM == DOWN)
    {
        if(oldAIM == UP)
            AIM = oldAIM;
    }
    if(AIM == LEFT)
    {
        if(oldAIM == RIGHT)
            AIM = oldAIM;
    }
    if(AIM == RIGHT)
    {
        if(oldAIM == LEFT)
            AIM = oldAIM;
    }
}

int ifout()
{   
    if(snack_head_x > 1 && snack_head_x <  ENVLENGTH &&
       snack_head_y > 1 && snack_head_y <  ENVWIDTH   )
    {
        return 0;
    }
    else
        return 1;
}



void run()
{
    int st = 1;
    char c = 0;

    for(;;)
    {
        usleep(400*1000);
        if(sfood.f == NO)
            add_food();
        c = 0;
        c = readch();
        oldAIM = AIM;
        if(c)
        {
            if(c == 'q'){
                return ;
            }
            if(c == 'w' || c == 'W')
                AIM = UP;
            if(c == 'a' || c == 'A')
                AIM = LEFT;
            if(c == 's' || c == 'S')
                AIM = DOWN;
            if(c == 'd' || c == 'D')
                AIM = RIGHT;
        }
        ifremove();
        gotoxy_putc(snack_head_x,snack_head_y,' ');
        if(AIM == UP)
            snack_head_y -= 1;
        if(AIM == LEFT)
            snack_head_x -= 1;
        if(AIM == DOWN)
            snack_head_y += 1;
        if(AIM == RIGHT)
            snack_head_x += 1;
        if(snack_head_x == sfood.x && snack_head_y == sfood.y)
        {
            add_snode(0,0);
            sfood.f = NO;
        }
        movesnack();
        showsnack();

        if(ifout())  break;
        if(ifinsnack(snack_head_x,snack_head_y))  break;
    }
    gotoxy_puts(20,10,"宝宝还需努力哦");
    gotoxy_puts(20,11,"Enter键推出");
    getchar();
}

int main()
{
    initkey();
    initsnack();
    run();
    relaykey();
    clrter("clear");
    return 0;
}

效果图如下:
Linux c 贪吃蛇 无curses、多线程、信号的实现_第1张图片

各位大神快来优化吧!

你可能感兴趣的:(C)