免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: forest077
打印 上一主题 下一主题

[算法] [原创]贡献一个unix的贪吃蛇小游戏 [复制链接]

论坛徽章:
0
1 [报告]
发表于 2012-11-14 16:15 |显示全部楼层
/***snake.c***/
#include <stdio.h>
#include <malloc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <termio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define SNAKE_INITX 5
#define SNAKE_INITY 5
#define SNAKE_SHAPE '*'
#define SNAKE_INITLEN 8

#define WIN_X1 1
#define WIN_X2 80
#define WIN_Y1 1
#define WIN_Y2 24

#define MAX_LEVEL 20
#define MAX_INTER 200000
#define MIN_INTER 0
#define MAX_RICH 10
#define DEVRATE 5
#define OVER "Game Over!!!"

struct stNode {
        int x;
        int y;
        char shape;
        struct stNode *next;
};

struct stFood {
        int x;
        int y;
};

struct stNode *gpstHead, *gpstTail;
struct stFood gastFood[MAX_RICH];
int giLevel = 1;
int giRich = 1;
int giScore = 0;
int giLen = 0;

void settty(int iFlag)
{
        int fd;
        struct termio stTerm;

        if((fd = open(ttyname(1), O_RDWR)) == -1)        //ttyname()返回tty1的name  
                return;                                        //fd是打开tty1
        if(iFlag == 1)
        {
                ioctl(fd, TCGETA, &stTerm);        //获取tty1的状态
                stTerm.c_lflag &= ~ICANON;        //设置tty1的状态
                stTerm.c_lflag &= ~ECHO;
                stTerm.c_cc[4] = 1;
                stTerm.c_cc[5] = 0;
                stTerm.c_iflag &= ~ISTRIP;
                stTerm.c_cflag |= CS8;
                stTerm.c_cflag &= ~PARENB;
                ioctl(fd, TCSETA, &stTerm);
        } else
        {
                ioctl(fd, TCGETA, &stTerm);
                stTerm.c_lflag |= ICANON;
                stTerm.c_lflag |= ECHO;
                stTerm.c_cc[4] = 4;
                stTerm.c_cc[5] = 5;
                stTerm.c_iflag &= ~ISTRIP;
                stTerm.c_cflag |= CS8;
                stTerm.c_cflag &= ~PARENB;
                ioctl(fd, TCSETA, &stTerm);
        }
        close(fd);
}

//绘制蛇的节点
void vDrawOneNode(struct stNode *pstNode, int iFlag)
{
    if(iFlag == 1)
    {
            printf("\033[%dm\033[%d;%dm\033[%d;%dH%c",
                   iFlag,40, 33, pstNode->y, pstNode->x, pstNode->shape);
    }
    if(iFlag == 0)
    {
            printf("\033[%dm\033[%d;%dH%c",
                   iFlag, pstNode->y, pstNode->x, ' ');
    }
    //修改了蛇的运行轨迹BUG
        //printf("\033[%dm\033[%d;%dm\033[%d;%dH%c",
        //           iFlag,45 - iFlag * 5, 35 - iFlag * 2, pstNode->y, pstNode->x, pstNode->shape);
        fflush(stdout);
}

void vDrawOneFood(int x, int y)
{
        printf("\033[1m\033[40;36m\033[%d;%dH%c", y, x, '@');
        fflush(stdout);
}

int iGetDir(int iOriDir)
{
        fd_set rset;
        struct timeval hTmo;
        int iRet, iFlag = 0;
        char cCh;

        FD_ZERO(&rset);
        FD_SET(0, &rset);
        hTmo.tv_sec = 0;
        hTmo.tv_usec = MAX_INTER - (MAX_INTER - MIN_INTER) / MAX_LEVEL * giLevel;

        iRet = select(1, &rset, NULL, NULL, &hTmo);
        if(iRet <= 0)
        {
                return (iOriDir);
        }
        for (;
        {
                cCh = getchar();
                if(cCh != -1)
                {
                        switch (cCh)
                        {
                        case 27:
                        case 91:
                                iFlag++;
                                break;
                        case 65:                        //UP
                        case 66:                        //DOWN
                        case 67:                        //RIGHT
                        case 68:                        //LEFT
                                if(iFlag == 2)
                                        return ((!((cCh - 0x41) ^ iOriDir ^ 1)) ^ (cCh - 0x41));
                        default:
                                return (iOriDir);
                        }
                }
        }
}

void vInitScreen()
{
        settty(1);
        printf("\033[?25l\033[2J";        //隐藏光标和清屏
}

void vRestoreScreen()
{
    //修正了游戏结束时,屏幕显示的BUG
        //printf("\033[25;1H\033[0m\033[46;37m\033[?25h";
        printf("\033[25;1H\033[0m\033[?25h";
        settty(0);
}

void vDrawScope()
{
        int i, j;

        for (j = WIN_Y1; j <= WIN_Y2; j += WIN_Y2 - WIN_Y1)
        {
                printf("\033[%d;%dH+", j, WIN_X1);        //光标定位和答应每行开头的“+”号
                for (i = WIN_X1 + 1; i < WIN_X2; i++)
                        printf("-";                //打印每行中间的“-”
                printf("+";                        //打印每行末尾的“+”
        }
        for (i = WIN_Y1 + 1; i < WIN_Y2; i++)
                //将第二行到倒数第二行中间设置为空,同时开头和末尾设置为“|”
                //不明白为什么只有三个占位符,却又四个参数?
                printf("\033[%d;%dH|%*c|\n", i, WIN_X1, WIN_X2 - WIN_X1 - 1, ' ');
}

void vCreateSnake()
{
        struct stNode *pstNew;
        int i;

        gpstHead = (struct stNode *) malloc(sizeof(struct stNode));        //为蛇头开辟空间,同时初始化蛇头位置
        gpstHead->x = SNAKE_INITX;
        gpstHead->y = SNAKE_INITY;
        gpstHead->shape = SNAKE_SHAPE;
        gpstHead->next = NULL;
        vDrawOneNode(gpstHead, 1);        //绘制蛇头节点
        gpstTail = gpstHead;
        for (i = 1; i < SNAKE_INITLEN; i++)        //绘制其他蛇身节点
        {
                pstNew = (struct stNode *) malloc(sizeof(struct stNode));
                pstNew->x = gpstHead->x + 1;
                pstNew->y = gpstHead->y;
                pstNew->shape = SNAKE_SHAPE;
                pstNew->next = NULL;
                vDrawOneNode(pstNew, 1);
                gpstHead->next = pstNew;
                gpstHead = pstNew;
        }
        return;
}

void vKillSnake()
{
        struct stNode *pstNode;

        for (pstNode = gpstTail; gpstTail != NULL
        {
                gpstTail = pstNode->next;
                free(pstNode);
                pstNode = gpstTail;
        }
}

void vGenFood(int iIdx)
{
        struct stNode *pstNode;
        int i, iFound = 0;

        for (; !iFound
        {
                iFound = 1;
                gastFood[iIdx].x = rand() % (WIN_X2 - WIN_X1 - 1) + WIN_X1 + 1;
                gastFood[iIdx].y = rand() % (WIN_Y2 - WIN_Y1 - 1) + WIN_Y1 + 1;
                for (i = 0; i < giRich; i++)
                {
                        if(i != iIdx && gastFood[iIdx].x == gastFood.x &&
                           gastFood[iIdx].y == gastFood.y)
                        {
                                iFound = 0;
                                break;
                        }
                }
                if(!iFound)
                        continue;
                for (pstNode = gpstTail; pstNode != NULL; pstNode = pstNode->next)
                {
                        if(gastFood[iIdx].x == pstNode->x && gastFood[iIdx].y == pstNode->y)
                        {
                                iFound = 0;
                                break;
                        }
                }
                if(!iFound)
                        continue;
        }
        vDrawOneFood(gastFood[iIdx].x, gastFood[iIdx].y);
}

void vInitFood()
{
        int i;

        srand(getpid());
        for (i = 0; i < giRich; i++)
                vGenFood(i);
}

int iIsValid(int x, int y)
{
        struct stNode *pstNode;

        if(x <= WIN_X1 || x >= WIN_X2 || y <= WIN_Y1 || y >= WIN_Y2)
                return (0);
        pstNode = gpstTail;
        for (; pstNode != NULL
        {
                if(x == pstNode->x && y == pstNode->y)
                        return (0);
                pstNode = pstNode->next;
        }
        return (1);
}

int iEat(int x, int y)
{
        int i;

        for (i = 0; i < giRich; i++)
        {
                if(x == gastFood.x && y == gastFood.y)
                {
                        vGenFood(i);
                        giScore += giLevel * 10;
                        giLen++;
                        if(giLevel < MAX_LEVEL)
                                if(giLen % DEVRATE == 0)
                                        giLevel++;
                        return (1);
                }
        }
        return (0);
}

int main(void)
{
        int iDir = 2, iNextX, iNextY;
        struct stNode *pstNew;
        char sPrompt[80];

        vInitScreen();                                //初始化tty1和清屏
        vDrawScope();                                //绘制外围边框
        vCreateSnake();                                //创建蛇的原型
        vInitFood();                                //初始化食物
        for (;
        {
                iDir = iGetDir(iDir);
                iNextX = gpstHead->x + (iDir >> 1) * (5 - (iDir << 1));
                iNextY = gpstHead->y - (!(iDir >> 1)) * (1 - (iDir << 1));
                if(!iIsValid(iNextX, iNextY))
                {
                        printf("\033[%d;%dH\033[1m\033[40;34m%s\033[0m",
                                   WIN_Y2 - 1, (WIN_X1 + WIN_X2) / 2 - strlen(OVER) / 2, OVER);
                        break;
                }
                pstNew = (struct stNode *) malloc(sizeof(struct stNode));
                pstNew->x = iNextX;
                pstNew->y = iNextY;
                pstNew->shape = SNAKE_SHAPE;
                pstNew->next = NULL;
                gpstHead->next = pstNew;
                gpstHead = pstNew;
                vDrawOneNode(gpstHead, 1);
                if(!iEat(iNextX, iNextY))
                {
                        //vDrawOneNode(gpstHead, 1);
                        vDrawOneNode(gpstTail, 0);
                        pstNew = gpstTail;
                        gpstTail = pstNew->next;
                        free(pstNew);
                }
                sprintf(sPrompt, "Score:%7d Level:%2d", giScore, giLevel);
                printf("\033[%d;%dH\033[1m\033[40;34m%s\033[0m",
                           WIN_Y2, (WIN_X1 + WIN_X2) / 2 - strlen(sPrompt) / 2, sPrompt);
        }
        vKillSnake();
        vRestoreScreen();
        return 0;
}

我把楼主的代码带了一些注释,另外修改了两个小问题,
1.蛇路过的地方,会留下轨迹。2.游戏结束后会修改背景和字体的颜色

论坛徽章:
0
2 [报告]
发表于 2012-11-14 16:17 |显示全部楼层
/***snake.c***/
#include <stdio.h>
#include <malloc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <termio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define SNAKE_INITX 5
#define SNAKE_INITY 5
#define SNAKE_SHAPE '*'
#define SNAKE_INITLEN 8

#define WIN_X1 1
#define WIN_X2 80
#define WIN_Y1 1
#define WIN_Y2 24

#define MAX_LEVEL 20
#define MAX_INTER 200000
#define MIN_INTER 0
#define MAX_RICH 10
#define DEVRATE 5
#define OVER "Game Over!!!"

struct stNode {
        int x;
        int y;
        char shape;
        struct stNode *next;
};

struct stFood {
        int x;
        int y;
};

struct stNode *gpstHead, *gpstTail;
struct stFood gastFood[MAX_RICH];
int giLevel = 1;
int giRich = 1;
int giScore = 0;
int giLen = 0;

void settty(int iFlag)
{
        int fd;
        struct termio stTerm;

        if((fd = open(ttyname(1), O_RDWR)) == -1)        //ttyname()返回tty1的name  
                return;                                        //fd是打开tty1
        if(iFlag == 1)
        {
                ioctl(fd, TCGETA, &stTerm);        //获取tty1的状态
                stTerm.c_lflag &= ~ICANON;        //设置tty1的状态
                stTerm.c_lflag &= ~ECHO;
                stTerm.c_cc[4] = 1;
                stTerm.c_cc[5] = 0;
                stTerm.c_iflag &= ~ISTRIP;
                stTerm.c_cflag |= CS8;
                stTerm.c_cflag &= ~PARENB;
                ioctl(fd, TCSETA, &stTerm);
        } else
        {
                ioctl(fd, TCGETA, &stTerm);
                stTerm.c_lflag |= ICANON;
                stTerm.c_lflag |= ECHO;
                stTerm.c_cc[4] = 4;
                stTerm.c_cc[5] = 5;
                stTerm.c_iflag &= ~ISTRIP;
                stTerm.c_cflag |= CS8;
                stTerm.c_cflag &= ~PARENB;
                ioctl(fd, TCSETA, &stTerm);
        }
        close(fd);
}

//绘制蛇的节点
void vDrawOneNode(struct stNode *pstNode, int iFlag)
{
    if(iFlag == 1)
    {
            printf("\033[%dm\033[%d;%dm\033[%d;%dH%c",
                   iFlag,40, 33, pstNode->y, pstNode->x, pstNode->shape);
    }
    if(iFlag == 0)
    {
            printf("\033[%dm\033[%d;%dH%c",
                   iFlag, pstNode->y, pstNode->x, ' ');
    }
    //修改了蛇的运行轨迹BUG
        //printf("\033[%dm\033[%d;%dm\033[%d;%dH%c",
        //           iFlag,45 - iFlag * 5, 35 - iFlag * 2, pstNode->y, pstNode->x, pstNode->shape);
        fflush(stdout);
}

void vDrawOneFood(int x, int y)
{
        printf("\033[1m\033[40;36m\033[%d;%dH%c", y, x, '@');
        fflush(stdout);
}

int iGetDir(int iOriDir)
{
        fd_set rset;
        struct timeval hTmo;
        int iRet, iFlag = 0;
        char cCh;

        FD_ZERO(&rset);
        FD_SET(0, &rset);
        hTmo.tv_sec = 0;
        hTmo.tv_usec = MAX_INTER - (MAX_INTER - MIN_INTER) / MAX_LEVEL * giLevel;

        iRet = select(1, &rset, NULL, NULL, &hTmo);
        if(iRet <= 0)
        {
                return (iOriDir);
        }
        for (;;)
        {
                cCh = getchar();
                if(cCh != -1)
                {
                        switch (cCh)
                        {
                        case 27:
                        case 91:
                                iFlag++;
                                break;
                        case 65:                        //UP
                        case 66:                        //DOWN
                        case 67:                        //RIGHT
                        case 68:                        //LEFT
                                if(iFlag == 2)
                                        return ((!((cCh - 0x41) ^ iOriDir ^ 1)) ^ (cCh - 0x41));
                        default:
                                return (iOriDir);
                        }
                }
        }
}

void vInitScreen()
{
        settty(1);
        printf("\033[?25l\033[2J");        //隐藏光标和清屏
}

void vRestoreScreen()
{
    //修正了游戏结束时,屏幕显示的BUG
        //printf("\033[25;1H\033[0m\033[46;37m\033[?25h");
        printf("\033[25;1H\033[0m\033[?25h");
        settty(0);
}

void vDrawScope()
{
        int i, j;

        for (j = WIN_Y1; j <= WIN_Y2; j += WIN_Y2 - WIN_Y1)
        {
                printf("\033[%d;%dH+", j, WIN_X1);        //光标定位和答应每行开头的“+”号
                for (i = WIN_X1 + 1; i < WIN_X2; i++)
                        printf("-");                //打印每行中间的“-”
                printf("+");                        //打印每行末尾的“+”
        }
        for (i = WIN_Y1 + 1; i < WIN_Y2; i++)
                //将第二行到倒数第二行中间设置为空,同时开头和末尾设置为“|”
                //不明白为什么只有三个占位符,却又四个参数?
                printf("\033[%d;%dH|%*c|\n", i, WIN_X1, WIN_X2 - WIN_X1 - 1, ' ');
}

void vCreateSnake()
{
        struct stNode *pstNew;
        int i;

        gpstHead = (struct stNode *) malloc(sizeof(struct stNode));        //为蛇头开辟空间,同时初始化蛇头位置
        gpstHead->x = SNAKE_INITX;
        gpstHead->y = SNAKE_INITY;
        gpstHead->shape = SNAKE_SHAPE;
        gpstHead->next = NULL;
        vDrawOneNode(gpstHead, 1);        //绘制蛇头节点
        gpstTail = gpstHead;
        for (i = 1; i < SNAKE_INITLEN; i++)        //绘制其他蛇身节点
        {
                pstNew = (struct stNode *) malloc(sizeof(struct stNode));
                pstNew->x = gpstHead->x + 1;
                pstNew->y = gpstHead->y;
                pstNew->shape = SNAKE_SHAPE;
                pstNew->next = NULL;
                vDrawOneNode(pstNew, 1);
                gpstHead->next = pstNew;
                gpstHead = pstNew;
        }
        return;
}

void vKillSnake()
{
        struct stNode *pstNode;

        for (pstNode = gpstTail; gpstTail != NULL;)
        {
                gpstTail = pstNode->next;
                free(pstNode);
                pstNode = gpstTail;
        }
}

void vGenFood(int iIdx)
{
        struct stNode *pstNode;
        int i, iFound = 0;

        for (; !iFound;)
        {
                iFound = 1;
                gastFood[iIdx].x = rand() % (WIN_X2 - WIN_X1 - 1) + WIN_X1 + 1;
                gastFood[iIdx].y = rand() % (WIN_Y2 - WIN_Y1 - 1) + WIN_Y1 + 1;
                for (i = 0; i < giRich; i++)
                {
                        if(i != iIdx && gastFood[iIdx].x == gastFood[i].x &&
                           gastFood[iIdx].y == gastFood[i].y)
                        {
                                iFound = 0;
                                break;
                        }
                }
                if(!iFound)
                        continue;
                for (pstNode = gpstTail; pstNode != NULL; pstNode = pstNode->next)
                {
                        if(gastFood[iIdx].x == pstNode->x && gastFood[iIdx].y == pstNode->y)
                        {
                                iFound = 0;
                                break;
                        }
                }
                if(!iFound)
                        continue;
        }
        vDrawOneFood(gastFood[iIdx].x, gastFood[iIdx].y);
}

void vInitFood()
{
        int i;

        srand(getpid());
        for (i = 0; i < giRich; i++)
                vGenFood(i);
}

int iIsValid(int x, int y)
{
        struct stNode *pstNode;

        if(x <= WIN_X1 || x >= WIN_X2 || y <= WIN_Y1 || y >= WIN_Y2)
                return (0);
        pstNode = gpstTail;
        for (; pstNode != NULL;)
        {
                if(x == pstNode->x && y == pstNode->y)
                        return (0);
                pstNode = pstNode->next;
        }
        return (1);
}

int iEat(int x, int y)
{
        int i;

        for (i = 0; i < giRich; i++)
        {
                if(x == gastFood[i].x && y == gastFood[i].y)
                {
                        vGenFood(i);
                        giScore += giLevel * 10;
                        giLen++;
                        if(giLevel < MAX_LEVEL)
                                if(giLen % DEVRATE == 0)
                                        giLevel++;
                        return (1);
                }
        }
        return (0);
}

int main(void)
{
        int iDir = 2, iNextX, iNextY;
        struct stNode *pstNew;
        char sPrompt[80];

        vInitScreen();                                //初始化tty1和清屏
        vDrawScope();                                //绘制外围边框
        vCreateSnake();                                //创建蛇的原型
        vInitFood();                                //初始化食物
        for (;;)
        {
                iDir = iGetDir(iDir);
                iNextX = gpstHead->x + (iDir >> 1) * (5 - (iDir << 1));
                iNextY = gpstHead->y - (!(iDir >> 1)) * (1 - (iDir << 1));
                if(!iIsValid(iNextX, iNextY))
                {
                        printf("\033[%d;%dH\033[1m\033[40;34m%s\033[0m",
                                   WIN_Y2 - 1, (WIN_X1 + WIN_X2) / 2 - strlen(OVER) / 2, OVER);
                        break;
                }
                pstNew = (struct stNode *) malloc(sizeof(struct stNode));
                pstNew->x = iNextX;
                pstNew->y = iNextY;
                pstNew->shape = SNAKE_SHAPE;
                pstNew->next = NULL;
                gpstHead->next = pstNew;
                gpstHead = pstNew;
                vDrawOneNode(gpstHead, 1);
                if(!iEat(iNextX, iNextY))
                {
                        //vDrawOneNode(gpstHead, 1);
                        vDrawOneNode(gpstTail, 0);
                        pstNew = gpstTail;
                        gpstTail = pstNew->next;
                        free(pstNew);
                }
                sprintf(sPrompt, "Score:%7d Level:%2d", giScore, giLevel);
                printf("\033[%d;%dH\033[1m\033[40;34m%s\033[0m",
                           WIN_Y2, (WIN_X1 + WIN_X2) / 2 - strlen(sPrompt) / 2, sPrompt);
        }
        vKillSnake();
        vRestoreScreen();
        return 0;
}

上面不知道为什么有表情
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP