C语言实战-贪吃蛇
1、ncurse
为什么要用?因为C语言常用键盘键入函数scanf、gets、getchar输入后必须要按下enter才结束,对于贪吃蛇方向控制无法实现。
2、curses库介绍
curses是一个在Linux/Unix下广泛应用的图形函数库,作用是可以在终端内绘制简单的图形用户界面。
所有curses程序必须以initscr函数开始,以endwin函数结束。
#include <curses.h>int main()
{int key;initscr();keypad(stdscr,1);while(1){key = getch();switch(key){case KEY_DOWN:printw("DOWN\n");break;case KEY_UP:printw("UP\n");break;case KEY_LEFT:printw("LEFT\n");break;case KEY_RIGHT:printw("RIGHT\n");break;}}endwin();return 0;
}
3、地图绘制
地图边框,for循环
#include <curses.h>void initNcurse()
{initscr();keypad(stdscr,1);}
void gamePic()
{int hang;int lie;for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|");}else{printw(" ");}}printw("\n");for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>0&&hang<19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else{printw(" ");}}printw("\n");}}
}int main()
{initNcurse();//初始化gamePic();//地图绘制getch();//不使程序退出,使用键盘等待endwin();return 0;
}
编译:gcc snake.c -lcurses
运行:./a.out
4、蛇身子制作
贪吃蛇身子:①行坐标②列坐标③下一个节点的位置(指针)
//蛇结构体
struct Snake
{int hang;int lie;struct Snake *next;
};
#include <curses.h>
struct Snake
{int hang;int lie;struct Snake *next;
};//3个节点
struct Snake node1 = {3,3,NULL};
struct Snake node2 = {3,4,NULL};
struct Snake node3 = {3,5,NULL};void initNcurse()
{initscr();keypad(stdscr,1);}
//判断节点的函数显示身子
int hasSnakeNode(int i,int j)
{struct Snake *p;p = &node1;while(p!=NULL){if(p->hang == i && p->lie ==j){return 1;}p = p->next;}return 0;
}
void gamePic()
{int hang;int lie;for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0||hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}
}int main()
{initNcurse();node1.next = &node2;node2.next = &node3;gamePic();getch();endwin();return 0;
}
5、链表动态添加节点
#include <curses.h>
#include <stdlib.h>
struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head;
struct Snake *tail;void initNcurse()
{initscr();keypad(stdscr,1);}int hasSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p!=NULL){if(p->hang == i && p->lie ==j){return 1;}p = p->next;}return 0;
}
void gamePic()
{int hang;int lie;for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0||hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}
}
//增加节点
void addNode()
{struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));new->hang=tail->hang;new->lie=tail->lie+1;new->next = NULL;tail->next = new; tail =new;
}
//初始化节点
void initSnake()
{head =(struct Snake *)malloc(sizeof(struct Snake) );head->hang = 2;head->lie =2;head->next = NULL;tail = head;addNode();addNode();addNode();
}int main()
{initNcurse();initSnake();gamePic();getch();endwin();return 0;
}
6、蛇身子向右移动
首节点删除,增加尾节点,中间的不变
#include <curses.h>
#include <stdlib.h>
struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head;
struct Snake *tail;void initNcurse()
{initscr();keypad(stdscr,1);}int hasSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p!=NULL){if(p->hang == i && p->lie ==j){return 1;}p = p->next;}return 0;
}
void gamePic()
{int hang;int lie;move(0,0);for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0||hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}
}void addNode()
{struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));new->hang = tail->hang;new->lie = tail->lie+1;new->next = NULL;tail->next = new; tail =new;
}
//删除节点,并释放内存
void deleteNode()
{struct Snake *p;p = head;head = head->next;free(p);
}
void initSnake()
{head =(struct Snake *)malloc(sizeof(struct Snake) );head->hang = 2;head->lie =2;head->next = NULL;tail = head;addNode();addNode();addNode();
}
//蛇的移动,前删后增
void moveSnake()
{addNode();deleteNode();
}int main()
{int con;initNcurse();initSnake();gamePic();while(1){con = getch();if(con == KEY_RIGHT){moveSnake();gamePic();}}getch();endwin();return 0;
}
7、撞墙
如果尾巴的行或列等于边框的行列值
#include <curses.h>
#include <stdlib.h>
struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;void initNcurse()
{initscr();keypad(stdscr,1);}int hasSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p!=NULL){if(p->hang == i && p->lie ==j){return 1;}p = p->next;}return 0;
}
void gamePic()
{int hang;int lie;move(0,0);for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0||hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}
}void addNode()
{struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));new->hang = tail->hang;new->lie = tail->lie+1;new->next = NULL;tail->next = new; tail =new;
}
void deleteNode()
{struct Snake *p;p = head;head = head->next;free(p);
}
//撞墙后初始化蛇,释放之前的内存
void initSnake()
{struct Snake *p;while(head!=NULL){p = head;head=head->next;free(p);}head =(struct Snake *)malloc(sizeof(struct Snake) );head->hang = 1;head->lie =1;head->next = NULL;tail = head;addNode();addNode();addNode();
}void moveSnake()
{addNode();deleteNode();//撞墙,坐标重合,初始化蛇if(tail->hang==0 || tail->hang==20 || tail->lie==0 || tail->lie==20){initSnake();}
}int main()
{int con;initNcurse();initSnake();gamePic();while(1){con = getch();if(con == KEY_RIGHT){moveSnake();gamePic();}}getch();endwin();return 0;
}
8、自动向右走,撞墙初始化再走
不使用键盘键入,一直moveSnake,刷新界面
int main()
{int con;initNcurse();initSnake();gamePic();while(1){moveSnake();gamePic();refresh();usleep(100000);}getch();endwin();return 0;
}
9、贪吃蛇方向移动
此处涉及到两个while循环的问题,一个是贪吃蛇一直向右走,另一个是不断检测键盘输入。无法同时进行,涉及Linux线程问题
创建线程
①包含头文件pthread.h
②主函数中pthread_t + 线程名(定义线程名)
③函数定义时,要定义为指针类型(例:void *func1)
③创建线程:pthread_create(&线程名,NULL,函数,NULL);
#include <stdio.h>
#include <pthread.h>void *func1()
{while(1){printf("this is func1\n");sleep(1);}
}
void *func2()
{while(1){printf("this is func2\n");sleep(1);}
}int main()
{pthread_t th1;pthread_t th2;pthread_create(&th1,NULL,func1,NULL);pthread_create(&th2,NULL,func2,NULL);while(1);return 0;
}
全部代码:
//导入所需包
#include <curses.h>
#include <stdlib.h>//定义四个方向
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4//蛇结构体,行坐标,列坐标
struct Snake
{int hang;int lie;struct Snake *next;
};//蛇头蛇尾
struct Snake *head = NULL;
struct Snake *tail = NULL;//key为键盘输入,dir为方向,全局变量
int key;
int dir;//初始化界面
void initNcurse()
{initscr();keypad(stdscr,1);}//显示蛇身,与界面内某一坐标一致则打印蛇身
int hasSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p!=NULL){if(p->hang == i && p->lie ==j){return 1;}p = p->next;}return 0;
}
//地图与蛇身绘制
void gamePic()
{int hang;int lie;//刷新界面move(0,0);for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0||hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else if(hasSnakeNode(hang,lie)){printw("[]");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}
}//增加节点
void addNode()
{//新建new结构体并为其开辟空间struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));//指针指空,避免野指针new->next = NULL;//判断方向,根据不同的方向来增加节点(前删后增)switch(dir){case UP: new->hang = tail->hang-1;new->lie = tail->lie;break;case DOWN: new->hang = tail->hang+1;new->lie = tail->lie;break;case LEFT: new->hang = tail->hang;new->lie = tail->lie-1;break;case RIGHT: new->hang = tail->hang;new->lie = tail->lie+1;break;}tail->next = new; tail =new;
}
//删除前节点
void deleteNode()
{//将头节点赋给p,释放p的内存struct Snake *p;p = head;head = head->next;free(p);
}//初始化蛇
void initSnake()
{//定义p结构体,为遍历蛇身struct Snake *p;//初始化方向为右dir = RIGHT; //遍历蛇身while(head!=NULL){p = head;head=head->next;free(p);}//蛇的初始位置head =(struct Snake *)malloc(sizeof(struct Snake) );head->hang = 1;head->lie =1;head->next = NULL;//第一个节点,头即是尾tail = head;//初始化蛇为4节addNode();addNode();addNode();
}//蛇的移动,前删后增
void moveSnake()
{addNode();deleteNode();//如果与边框重合,则撞墙,重新初始化蛇if(tail->hang==0 || tail->hang==20 || tail->lie==0 || tail->lie==20){initSnake();}
}//一直刷新界面
void refreshJieMian()
{while(1){moveSnake();gamePic();refresh();usleep(100000);}
}//通过键盘控制方向,将键盘键入的内容赋给方向dir,通过dir判断节点的增加方法
void changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:dir = DOWN;break;case KEY_UP:dir = UP;break;case KEY_LEFT:dir = LEFT;break;case KEY_RIGHT:dir = RIGHT;break;}}
}//主函数
int main()
{//两个进程,一个刷新界面,另一个持续接收键盘键入pthread_t t1;pthread_t t2;initNcurse();//初始化界面initSnake();//初始化蛇gamePic();//地图绘制pthread_create(&t1,NULL,refreshJieMian,NULL);//刷新界面pthread_create(&t2,NULL,changeDir,NULL);//键盘键入while(1);getch();//防止程序退出endwin();return 0;
}
10、小问题:上述代码中蛇不合理走位,左右来回走,上下来回走
首先更改方向值
//定义四个方向
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
写一个函数,判断方向绝对值是否相等,相等则不执行命令;按完上键下一个不能按下键,以此类推
void turn(int direction)
{if(abs(dir) != abs(direction)){dir = direction;}
}void changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:turn(DOWN);break;case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}
}
11、食物
食物随机 xrand()函数,对边界值取余
食物范围:0≤x≤19 0≤y≤19
完整代码:
#include <curses.h>
#include <stdlib.h>#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;//定义一个食物
struct Snake food;//初始化食物,随机
void initFood()
{int x = rand()%20;int y = rand()%20;food.hang = x;food.lie = y;
}//食物显示
int hasFood(int i,int j)
{if(food.hang == i && food.lie == j){return 1;}return 0;
}void initNcurse()
{initscr();keypad(stdscr,1);}int hasSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p!=NULL){if(p->hang == i && p->lie ==j){return 1;}p = p->next;}return 0;
}void gamePic()
{int hang;int lie;move(0,0);for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0||hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else if(hasSnakeNode(hang,lie)){printw("[]");}//食物显示else if(hasFood(hang,lie)){printw("##");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}
}void addNode()
{struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));new->next = NULL;switch(dir){case UP: new->hang = tail->hang-1;new->lie = tail->lie;break;case DOWN: new->hang = tail->hang+1;new->lie = tail->lie;break;case LEFT: new->hang = tail->hang;new->lie = tail->lie-1;break;case RIGHT: new->hang = tail->hang;new->lie = tail->lie+1;break;}tail->next = new; tail =new;
}
void deleteNode()
{struct Snake *p;p = head;head = head->next;free(p);
}
void initSnake()
{struct Snake *p;dir = RIGHT; while(head!=NULL){p = head;head=head->next;free(p);}head =(struct Snake *)malloc(sizeof(struct Snake) );head->hang = 1;head->lie =1;head->next = NULL;tail = head;addNode();addNode();addNode();
}void moveSnake()
{addNode();//如果尾部与食物重合,即吃到食物,则不进行删除操作,并把食物重新随机放置if(hasFood(tail->hang,tail->lie)){initFood();}else{deleteNode();}if(tail->hang<0 || tail->hang==20 || tail->lie==0 || tail->lie==20){initSnake();}
}void refreshJieMian()
{while(1){moveSnake();gamePic();refresh();usleep(100000);}
}void turn(int direction)
{if(abs(dir) != abs(direction)){dir = direction;}
}void changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:turn(DOWN);break;case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}
}int main()
{pthread_t t1;pthread_t t2;initNcurse();initSnake();initFood();gamePic();pthread_create(&t1,NULL,refreshJieMian,NULL);pthread_create(&t2,NULL,changeDir,NULL);while(1);getch();endwin();return 0;
}
12、吃到自己身子死亡
#include <curses.h>
#include <stdlib.h>#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2struct Snake
{int hang;int lie;struct Snake *next;
};struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;struct Snake food;void initFood()
{int x = rand()%20;int y = rand()%20;food.hang = x;food.lie = y;
}int hasFood(int i,int j)
{if(food.hang == i && food.lie == j){return 1;}return 0;
}void initNcurse()
{initscr();keypad(stdscr,1);}int hasSnakeNode(int i,int j)
{struct Snake *p;p = head;while(p!=NULL){if(p->hang == i && p->lie ==j){return 1;}p = p->next;}return 0;
}void gamePic()
{int hang;int lie;move(0,0);for(hang=0;hang<20;hang++){if(hang==0){for(lie=0;lie<20;lie++){printw("--");}printw("\n");}if(hang>=0||hang<=19){for(lie=0;lie<=20;lie++){if(lie==0||lie==20){printw("|"); }else if(hasSnakeNode(hang,lie)){printw("[]");}else if(hasFood(hang,lie)){printw("##");}else{printw(" ");}}printw("\n");}if(hang==19){ for(lie=0;lie<20;lie++){printw("--");}printw("\n");}}
}void addNode()
{struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));new->next = NULL;switch(dir){case UP: new->hang = tail->hang-1;new->lie = tail->lie;break;case DOWN: new->hang = tail->hang+1;new->lie = tail->lie;break;case LEFT: new->hang = tail->hang;new->lie = tail->lie-1;break;case RIGHT: new->hang = tail->hang;new->lie = tail->lie+1;break;}tail->next = new; tail =new;
}
void deleteNode()
{struct Snake *p;p = head;head = head->next;free(p);
}
void initSnake()
{struct Snake *p;dir = RIGHT; while(head!=NULL){p = head;head=head->next;free(p);}head =(struct Snake *)malloc(sizeof(struct Snake) );head->hang = 1;head->lie =1;head->next = NULL;tail = head;addNode();addNode();addNode();
}//死亡函数,尾巴坐标与身子坐标一致,或者撞墙
int ifSnakeDie()
{struct Snake *p;p = head;if(tail->hang<0 || tail->hang==20 || tail->lie==0 || tail->lie==20){return 1;}while(p->next!=NULL){if(p->hang == tail->hang && p->lie == tail->lie){return 1;}p = p->next;}return 0;
}void moveSnake()
{addNode();if(hasFood(tail->hang,tail->lie)){initFood();}else{deleteNode();}if(ifSnakeDie()){initSnake();}}void refreshJieMian()
{while(1){moveSnake();gamePic();refresh();usleep(100000);}
}void turn(int direction)
{if(abs(dir) != abs(direction)){dir = direction;}
}void changeDir()
{while(1){key = getch();switch(key){case KEY_DOWN:turn(DOWN);break;case KEY_UP:turn(UP);break;case KEY_LEFT:turn(LEFT);break;case KEY_RIGHT:turn(RIGHT);break;}}
}int main()
{pthread_t t1;pthread_t t2;initNcurse();initSnake();initFood();gamePic();pthread_create(&t1,NULL,refreshJieMian,NULL);pthread_create(&t2,NULL,changeDir,NULL);while(1);getch();endwin();return 0;
}