此段代码是在Linux系统下实现的贪吃蛇项目,用到的工具为Ncurse,非常古老的一门技术,比他先进的有很多,目的旨在于强化个人的cyy基础,有兴趣的胖友可以参考实现以下。
首先我来介绍一下这个思路的实现:
1、首先贪吃蛇身体的实现是由许多个[]符号来实现的,这里用到了C语言的链表,灵活的连接每一个节点的前后关系。
2、贪吃蛇的身体位置定位是由行和列组成的,共有20行,20列;
3、大多数人认为贪吃蛇的移动实现,必然是“头”开始移动去吃食物,但这里我转变了思路:使用“尾节点”来带头,每移动一步,尾节点+1,这样必然会导致贪吃蛇越来越长,所以与此同时头节点后移(也就是删除)一位,并且释放空间(能够防止空间爆炸)。
#include <curses.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <math.h>
#define UP 1 //进行宏定义,用于操控贪吃蛇的方向
#define DOWN -1
#define LEFT 2
#define RIGHT -2
typedef struct Snake{
int row;
int col;
struct Snake *next;
}snake; //此处,因为使用了typedef,所以snake可以直接表示 struct Student
snake *head=NULL;
snake *tail=NULL;
int key;
int dir;
snake food;
void initNcurse(){ //初始化ncurse界面的函数
initscr();
keypad(stdscr,1);
}
void initFood(){ //初始化食物函数
int x=rand()%20,y=rand()%20; //使用随机函数,初始化食物位置
food.row=x;
food.col=y;
}
void initSnake(){ //初始化贪吃蛇的身体
dir=RIGHT; //起始移动方向,向右前进
snake* p;
while(head!=NULL){ //当头节点不为空时,向后遍历每一个身体节点
p=head;
head=head->next;
free(p); //最后释放空闲空间
}
initFood(); //在初始化贪吃蛇时,也同时初始化食物的位置
head=(snake*)malloc(sizeof(snake));
head->row=0;
head->col=1;
head->next=NULL;
tail=head;
addNode();
addNode();
addNode();
addNode();
}
int hasSnakeNode(int row,int col){ //
snake *p=head;
while(p!=NULL){
if(p->row==row && p->col==col){
return 1;
}
p=p->next;
}
return 0;
}
int hasFood(int i,int j){
if(food.row==i && food.col==j) return 1;
return 0;
}
void printTheBox(){ //打印贪吃蛇边框
int row,col;
move(0,0); //调用移动函数,让贪吃蛇从0,0位置开始移动
for(row=0;row<20;row++){
if(row==0){
for(col=0;col<20;col++){
printw("--");
}
printw("\n");
}
if(row>=0 && row<=19){
for(col=0;col<=20;col++){
if(col==0 ||col==20){
printw("|");
}else if(hasSnakeNode(row,col)){
//用于打印贪吃蛇的身体,在对应的位置,将空格替代
printw("[]");
}else if(hasFood(row,col)){
//打印食物的位置
printw("**");
}else{
printw(" ");
}
}
printw("\n");
}
if(row==19){
for(col=0;col<20;col++){
printw("--");
}
printw("\n");
printw("Bye Bye!!!key=%d\n",key);
}
}
}
void addNode(){ //添加贪吃蛇身体节点函数,每执行一次,贪吃蛇增加1节
snake *new=(snake*)malloc(sizeof(snake));
new ->next=NULL;
switch(dir){ //判断指令,下一步想着什么方向移动
case UP:
new->row=tail->row-1;
new->col=tail->col;
break;
case DOWN:
new->row=tail->row+1;
new->col=tail->col;
break;
case LEFT:
new->row=tail->row;
new->col=tail->col-1;
break;
case RIGHT:
new->row=tail->row;
new->col=tail->col+1;
break;
}
tail->next=new;
tail=new;//update the tail's address
}
void deleteNode(){ //贪吃移动的实现,靠的是尾节点+1,头节点-1,此处实现删除头节点
snake *p;
p=head;
head=head->next;//change the head
free(p);
}
void moveSnake(){ //实现贪吃蛇的移动
addNode();
if(hasFood(tail->row,tail->col)){//如果吃到食物,刷新食物位置,仅此时不删除旧的头节点
initFood();
}else{
deleteNode(); //没有吃到食物时,移动时头节点-1,尾节点+1
}
if(ifSnakeDie()){ //如果吃到了自己,或者碰壁,贪吃蛇重置刷新
initSnake();
}
}
int ifSnakeDie(){ //判断贪吃蛇是否死亡
snake *p=head;
if(tail->row<0 ||tail->col==0 ||tail->row==20 ||tail->col==20){
return 1;
}
while(p->next!=NULL){
if(p->row==tail->row && p->col==tail->col) return 1;
p=p->next;
}
return 0;
}
void* refreshTheWin(){ //为实现贪吃蛇动态移动,要实现实时刷新地图
while(1){
moveSnake();
printTheBox();
refresh();
usleep(300000);
}
}
void turn(int direction){//防止贪吃蛇上下、左右冲突撞到自己
if(abs(dir)!=abs(direction)){
dir=direction;
}
}
void* changeTheDir(){ //贪吃蛇转向函数
while(1){
key=getch();
switch(key){
case KEY_UP:
turn(UP);
break;
case KEY_DOWN:
turn(DOWN);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_RIGHT:
turn(RIGHT);
break;
}
}
}
int main(){
//create the thread;
pthread_t th1;
pthread_t th2;
initNcurse();
initSnake();
printTheBox();
pthread_create(&th1,NULL,refreshTheWin,NULL);
pthread_create(&th1,NULL,changeTheDir,NULL);
while(1);
getch();
endwin();//exit the exe
return 0;
}