- 论坛徽章:
- 0
|
/***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.游戏结束后会修改背景和字体的颜色 |
|