Linux下C语言实现贪吃蛇
简单记录下贪吃蛇小游戏。
以下是源码:
1 include <curses.h> 2 include <stdlib.h> 3 include <pthread.h> 4 define UP 1 5 define DOWN -1 6 define LEFT 2 7 define RIGHT -2 8 9 /*蛇和食物的结构体*/ 10 struct Snake 11 { 12 int hang; 13 int lie; 14 struct Snake *next; 15 }; 16 17 struct Snake *head = NULL; //链表头 18 struct Snake *tail = NULL; //链表尾 19 struct Snake food; //食物 20 int key; //记录键盘输入值 21 int dir; //记录输入的方向键 22 23 /*输入横坐标和纵坐标值,判断是否存在蛇的链表节点*/ 24 int hasSnakeNode(int i,int j) 25 { 26 struct Snake *p; //临时变量,用于记录蛇的链表表头 27 p = head; 28 29 while(p != NULL) //当链表头不为空时,进入循环 30 { 31 if(p->hang == i && p->lie == j) 32 { 33 return 1; //输入的行纵坐标存在于蛇的链表中时返回1 34 } 35 p = p->next; //链表头指向下一个节点 36 } 37 return 0; //输入的横纵坐标不存在蛇的链表中时返回0 38 } 39 40 /*食物初始化*/ 41 void initFoodnode() 42 { 43 int x; //定义横坐标临时变量 44 int y; //定义纵坐标临时变量 45 x = rand()%20; //在[0-20]范围内随机获取横坐标值 46 y = rand()%20; //在[0-20]范围内随机获取纵坐标值 47 48 if(y == 0) 49 { 50 y = rand()%20; //当纵坐标取值为0时,重新取值 51 } 52 53 food.hang = x; //将前面获取到的横坐标赋值为食物的横坐标 54 food.lie = y; //将前面获取到的纵坐标复制为食物的纵坐标 55 } 56 57 /*输入横纵坐标判断是否存在食物,用于地图刷新*/ 58 int hasFoodnode(int i,int j) 59 { 60 if((food.hang == i)&&(food.lie == j)) 61 { 62 return 1; //当输入横纵坐标为食物的横纵坐标时,返回1 63 } 64 return 0; //当输入横纵坐标不是食物的横纵坐标时,返回0 65 } 66 67 /*地图绘制*/ 68 void gamePic() 69 { 70 int hang,lie; //行列临时变量 71 move(0,0); //每次界面刷新都将界面光标移动到(0,0)的位置 72 for(hang=0;hang<20;hang++) //历遍行 73 { 74 if(hang==0) 75 { 76 for(lie=0;lie<20;lie++) 77 { 78 printw("--"); //判断为第0行时,绘制‘--’边界符号 79 } 80 printw("\n"); 81 } 82 if(hang>=0 || hang<=19) 83 { 84 for(lie=0;lie<=20;lie++) 85 { 86 if(lie==0 || lie==20) 87 { 88 printw("|");//第0和19列绘制‘|’边界符号 89 } 90 else if(hasSnakeNode(hang,lie)) 91 { 92 printw("[]");//行列值满足蛇的节点坐标时,绘制‘[]’符号 93 } 94 else if(hasFoodnode(hang,lie)) 95 { 96 printw("");//行列之满足食物节点坐标时,绘制‘’符号 97 } 98 else 99 { 100 printw(" ");//地图上空闲位置绘制‘ ’空格符号 101 } 102 } 103 printw("\n"); //每绘制完一行,该处添加一处换行符 104 } 105 if(hang==19) 106 { 107 for(lie=0;lie<20;lie++) 108 { 109 printw("--"); //判断为第19行时,绘制‘--’边界符号 110 } 111 printw("\n"); 112 printw("By chenguanxiong\n"); 113 printw("%d",key); 114 } 115 } 116 } 117 118 /*添加蛇身节点*/ 119 void addNode() 120 { 121 struct Snake *new; //新节点变量 122 new = (struct Snake *)malloc(sizeof(struct Snake));//为新节点开辟内存空间 123 124 switch(dir) //方向键判断 125 { 126 case UP: 127 new->hang = tail->hang - 1; //向上,行减1,上移 128 new->lie = tail->lie; //列保持 129 break; 130 case DOWN: 131 new->hang = tail->hang + 1; //向下,行加1,下移 132 new->lie = tail->lie; //列保持不变 133 break; 134 case LEFT: 135 new->hang = tail->hang; //行保持不变 136 new->lie = tail->lie - 1; //向左,列减1,左移 137 break; 138 case RIGHT: 139 new->hang = tail->hang; //行保持不变 140 new->lie = tail->lie + 1; //向右,列加1,右移 141 break; 142 default: 143 break; 144 145 } 146 147 new->next = NULL; //新节点的下一个节点指向为NULL 148 tail->next = new; //尾部的下一个节点指向新节点 149 tail = new; //新节点复制给尾部节点 150 } 151 152 /*初始化蛇*/ 153 void initSnake() 154 { 155 struct Snake *p; //临时变量,指向蛇的链表头 156 dir = RIGHT; //运动方向初始化为向右 157 while(head != NULL) //当链表头不为空时进入,用于释放蛇当前的链表占用内存空间 158 { 159 p = head; //p指向链表头 160 head = head->next; //链表头指向下一个节点 161 free(p); //释放链表头内存 162 } 163 164 initFoodnode(); //初始化食物 165 head = (struct Snake *)malloc(sizeof(struct Snake)); //为链表头开辟新的内存空间 166 head->hang = 2; //链表头行初始值为2 167 head->lie = 2; //链表头列初始值为2 168 head->next = NULL; //链表头的下一个节点指向为NULL 169 tail = head; //链表尾指向链表头 170 171 addNode(); //为链表添加新节点 172 addNode(); 173 addNode(); 174 addNode(); 175 } 176 177 /*节点删除*/ 178 void deleteNode() 179 { 180 struct Snake *p; //创建临时节点 181 p = head; //节点指向链表头 182 head = head->next; //链表头指向下一个节点 183 free(p); //释放p的空间(原链表头) 184 } 185 186 /*判断蛇是否越界或自残*/ 187 int ifSnakedie() 188 { 189 struct Snake *p; //创建临时节点 190 p = head; //指向链表头 191 192 if(tail->hang<0 | tail->lie==0 | tail->hang==20 | tail->lie==20) 193 { 194 return 1; //当蛇链表的尾部坐标等于边界值时,返回1 195 } 196 197 while(p->next != NULL) 198 { 199 if((p->hang==tail->hang)&&(p->lie==tail->lie)) 200 { 201 return 1; //当蛇链表其它的节点与尾部节点坐标相同,返回1 202 } 203 p = p->next; 204 } 205 206 return 0; //无越界,无自残,返回0 207 } 208 209 /*蛇移动*/ 210 void moveSnake() 211 { 212 addNode(); //添加新节点 213 if(hasFoodnode(tail->hang,tail->lie)) 214 { 215 initFoodnode(); //当蛇链表尾节点坐标值和食物坐标值一样,刷新食物位置 216 } 217 else 218 { 219 deleteNode(); //否则,删除蛇链表中的头节点 220 } 221 222 if(ifSnakedie()) 223 { 224 initSnake(); //如果满足越界或者自残条件,重新初始化蛇链表 225 } 226 } 227 228 /*地图界面刷新线程函数*/ 229 void* refreshjiemian() 230 { 231 while(1) 232 { 233 moveSnake(); //蛇链表移动 234 gamePic(); //地图刷新 235 refresh(); //执行刷新 236 usleep(150000); //线程休眠函数,150ms 237 } 238 } 239 240 /*方向函数*/ 241 void turn(int direction) 242 { 243 if(abs(dir) != abs(direction)) 244 { 245 dir = direction; //方向取绝对值比较,当左右运动时只有上下输入才生效 246 } 247 } 248 249 /*键盘方向输入监测线程函数*/ 250 void* changeDir() 251 { 252 while(1) 253 { 254 key = getch(); //获取键盘输入 255 switch(key) 256 { 257 case KEY_UP: 258 turn(UP); //上 259 break; 260 case KEY_DOWN: 261 turn(DOWN); //下 262 break; 263 case KEY_LEFT: 264 turn(LEFT); //左 265 break; 266 case KEY_RIGHT: 267 turn(RIGHT); //右 268 break; 269 default: 270 break; 271 } 272 } 273 } 274 275 int main() 276 { 277 pthread_t t1; //创建线程变量t1 278 pthread_t t2; //创建线程变量t2 279 280 initscr(); //Ncurses初始化 281 keypad(stdscr,1); //在std中接受键盘的功能键 282 noecho(); //控制键盘输入进来的字符 283 284 initSnake(); //初始化蛇列表 285 gamePic(); //地图初始化 286 287 pthread_create(&t1,NULL,refreshjiemian,NULL); //创建界面刷新线程 288 pthread_create(&t2,NULL,changeDir,NULL); //创建键盘方向输入监测线程 289 290 while(1); 291 292 getch(); 293 endwin(); 294 295 return 0; 296 }