博客搬家:最爱午后红茶
游戏规则:
初始时两条蛇会在中间,一条蛇头是'@',食物是‘$’;另一条蛇头是'+',食物是'*',蛇身都是'#'。WASD控制'+'蛇方向,方向键控制'@'蛇方向,空格键暂停游戏,再按一次继续游戏。每条蛇只能吃自己的食物。撞墙,碰到自己或另一条蛇的身体,吃到另一条蛇的食物都会结束游戏。
运行环境:Linux操作系统,请确认已安装curses库,没有的请在终端输入sudo apt-get libncurses5-dev 安装
编译指令:例如保存为snake.c 就在终端键入:g++ -o snake snake.c -lcurses
执行指令:在终端键入:./snake
思路:
双链表,每个结点记录的是当前蛇身的坐标跟前驱后继指针。这样在蛇移动时在头结点前添加一个结点,用move()移动到蛇头坐标,打印蛇头;接着如果没有吃到食物的话,把尾结点删掉,用move()移动到蛇尾,打印空值。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <curses.h>
#include <time.h>
int dir_y[] = {-1, 1, 0, 0};
int dir_x[] = {0, 0, -1, 1};
int vis[100][100];
int scord;
typedef struct body
{
int y, x;
struct body *pre, *sub;
} Body;
typedef struct
{
Body *head, *tail;
int DIRECTION;
int HEAD_X;
int HEAD_Y;
int FOOD_X;
int FOOD_Y;
char food;
char H;
} Snake;
//随机函数
int Get_Rand_Number(int k)
{
static int first_time = 0;
if(!first_time){
first_time = 1;
srand((unsigned int)(time(NULL)));
}
return rand()%k + 1;
}
bool kbhit()
{
if(ERR != halfdelay(0)) return 1;
else return 0;
}
void Add(Snake *snake)
{
Body *s;
s = (Body *)malloc(sizeof(Body));
s->y = snake->HEAD_Y;
s->x = snake->HEAD_X;
s->pre = NULL;
s->sub = snake->head;
snake->head->pre = s;
snake->head = s;
}
void Free(Body *head)
{
if(NULL == head) return;
Free(head->sub);
free(head);
}
int Judge_Kill(Snake *snake)
{
if(snake->HEAD_Y == LINES - 1 || snake->HEAD_X == COLS - 1 || snake->HEAD_Y == 0 || snake->HEAD_X == 0) return 1;
if(1 == vis[snake->HEAD_Y][snake->HEAD_X]) return 1;
return 0;
}
void Initialization(Snake *snake, int y, int x)
{
snake->DIRECTION = 0;
int &FOOD_Y = snake->FOOD_Y;
int &FOOD_X = snake->FOOD_X;
snake->HEAD_Y = y;
snake->HEAD_X = x;
while(1)
{
FOOD_Y = Get_Rand_Number(LINES - 3);
FOOD_X = Get_Rand_Number(COLS - 3);
if(!vis[FOOD_Y][FOOD_X]){
vis[FOOD_Y][FOOD_X] = 2;
break;
}
}
move(FOOD_Y, FOOD_X);
printw("%c", snake->food);
snake->head = (Body *)malloc(sizeof(Body));
snake->head->y = y;
snake->head->x = x;
snake->head->pre = snake->head->sub = NULL;
snake->tail = snake->head;
move(y, x);
printw("%c", snake->H);
refresh();
}
void New_Food(Snake *snake)
{
scord++;
int &FOOD_Y = snake->FOOD_Y;
int &FOOD_X = snake->FOOD_X;
while(1){
FOOD_Y = Get_Rand_Number(LINES - 3);
FOOD_X = Get_Rand_Number(COLS - 3);
if(!vis[FOOD_Y][FOOD_X]){
vis[FOOD_Y][FOOD_X] = 2;
break;
}
}
move(FOOD_Y, FOOD_X);
printw("%c", snake->food);
}
int Reverse_Direction(Snake *snake, int fy, int fx)
{
int &HEAD_Y = snake->HEAD_Y;
int &HEAD_X = snake->HEAD_X;
Body *&head = snake->head;
HEAD_Y += dir_y[snake->DIRECTION];
HEAD_X += dir_x[snake->DIRECTION];
if(HEAD_Y == fy && HEAD_X == fx) return 1;
//在蛇长不为1时,下一个移动跟蛇运动方向相反
if(head->sub && HEAD_Y == head->sub->y && HEAD_X == head->sub->x){
if(HEAD_Y == head->y){
if(HEAD_X > head->x) HEAD_X = head->x - 1;
else HEAD_X = head->x + 1;
}
else{
if(HEAD_Y > head->y) HEAD_Y = head->y - 1;
else HEAD_Y = head->y + 1;
}
}
return 0;
}
void Move(Snake *snake)
{
int &FOOD_Y = snake->FOOD_Y;
int &FOOD_X = snake->FOOD_X;
int &HEAD_Y = snake->HEAD_Y;
int &HEAD_X = snake->HEAD_X;
Body *&head = snake->head;
Body *&tail = snake->tail;
//输出蛇头
move(HEAD_Y, HEAD_X);
printw("%c", snake->H);
vis[HEAD_Y][HEAD_X] = 1;
//如果有蛇身,输出之
if(head->sub){
move(head->sub->y, head->sub->x);
printw("#");
}
if(HEAD_Y == FOOD_Y && HEAD_X == FOOD_X){
New_Food(snake);//吃到食物
}
else{//什么都没有吃到
move(tail->y, tail->x);
printw(" ");
vis[tail->y][tail->x] = 0;
if(tail->pre){
Body *tmp = tail;
tail = tail->pre;
tail->sub = NULL;
free(tmp);
}
}
}
int main()
{
initscr();
curs_set(0);//隐藏物理指针
noecho();//不回显输入
box(stdscr, '|', '-');//画框
Snake snake1, snake2;
snake1.food = '$';
snake2.food = '*';
snake1.H = '@';
snake2.H = '+';
//两条蛇的初始化位置(小蛇放中间比较舒服)
int init1_y = LINES / 2;
int init1_x = COLS / 2 - 1;
int init2_y = LINES / 2;
int init2_x = COLS / 2 + 1;
vis[init1_y][init1_x] = 1;
//初始化两条蛇位置跟食物位置
Initialization(&snake1, init1_y, init1_x);
Initialization(&snake2, init2_y, init2_x);
keypad(stdscr, true);//开启键盘功能键
int key = 0;
key = getch();
while(1)
{
#if 0
if(kbhit() != 0) //检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
{
while(kbhit() != 0) //可能存在多个按键,要全部取完,以最后一个为主
key = getch(); //将按键从控制台中取出并保存到key中
}
#endif
if(KEY_UP == key) snake1.DIRECTION = 0;
else if(KEY_DOWN == key) snake1.DIRECTION = 1;
else if(KEY_LEFT == key) snake1.DIRECTION = 2;
else if(KEY_RIGHT == key) snake1.DIRECTION = 3;
else if('w' == key) snake2.DIRECTION = 0;
else if('s'== key) snake2.DIRECTION = 1;
else if('a' == key) snake2.DIRECTION = 2;
else if('d' == key) snake2.DIRECTION = 3;
else if(' ' == key){
while((key = getch()) != ' ');
key = 0;
}
//else if('p' == key) getch();
//getch();
//获取向前移动的坐标及判断是否跟蛇运动方向相反(一条长度不为1的运动着的蛇
//是有一个方向是不能走的)
int over1 = 0, over2 = 0;
over1 = Reverse_Direction(&snake1, snake2.FOOD_Y, snake2.FOOD_X);
over2 = Reverse_Direction(&snake2, snake1.FOOD_Y, snake1.FOOD_X);
//判断游戏是否结束
if(Judge_Kill(&snake1) || Judge_Kill(&snake2) || over1 || over2){
//释放内存,很重要
Free(snake1.head);
Free(snake2.head);
break;
}
//往各自的链表添加蛇身
Add(&snake1);
Add(&snake2);
//移动蛇及判断是否吃到另一条蛇的食物
Move(&snake1);
Move(&snake2);
refresh();
usleep(150*1000);//停留150毫秒
if(ERR != halfdelay(1)) key = getch();//在100毫秒内等待输入,如无输入,往下执行
//halfdelay(1);
//key = getch();
}
sleep(3);
endwin();
exit(0);
}