#include
#include
#include
#include
#include
#include
#include
#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]
* | ^ 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.蛇移动时,每次只移动一个单位距离,所以只需要将最后一个节点拿走,放到要移动的那个位置上就整体上有移动的感觉了,至于移动的位置会有函数检测是否合法。