贪吃蛇是一款儿时爱不释手的游戏。近日修行,想玩玩游戏开发。便简单写了个控制台版的贪吃蛇。
程序的简单框架:
建立一张固定大小的MAP,保存输出信息。
当信息有变动时,用system("cls")进行清屏操作,再重新输出实现伪动态。
重点算法在蛇身的移动,转向与增长。三者均基于链表实现。
移动与转向:通过判定移动方向,确定下一步移动的位置后,新建表头结点。将新表头结点置为表头。删除末尾结点。
增长:通过判断尾部移动方向,确定位置后在尾部添加节点。
熟练运用链表的同学,相信也是小菜一碟了。
演示代码如下。
1 /* 2 * windy 2016 7.9 3 * Snake FirstStep 4 * A program of the GreadySnake 5 */ 6 #include <iostream> 7 #include <conio.h> //包含_kbhit()函数的头文件,_kbhit()运用可GOOGLE 8 #include <string.h> 9 #include <time.h> 10 #include <windows.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #define ROW 25 // height of MAP 14 #define COL 75 // weight of MAP 15 16 using namespace std; 17 18 //蛇身结点结构体 19 typedef struct Node { 20 int row; 21 int col; 22 Node* next; 23 24 Node() {}; 25 Node(int _r, int _c) { 26 row = _r; 27 col = _c; 28 } 29 }SnakeNode; 30 31 //果实结构体 32 typedef struct { 33 int row; 34 int col; 35 bool isEaten = true; //果实生成后,isEaten置为false,被吃后,重新置为true 36 }Fruit; 37 38 //draw the map outline 39 void drawMap(); 40 41 //create the fruit randomly 42 void createFruit(); 43 44 //create the snake body 45 void createSnake(); 46 47 //add snake body node 48 void addNode(SnakeNode* head); 49 50 //let the snake move 51 void snakeMove(); 52 53 //print the snake 54 void snakePrint(); 55 56 //check if snake eat the fruit, if snake pump the boundary 57 void check(); 58 59 //snake turn 60 void snakeTurn(); 61 62 //erase the tail 63 void tailErase(SnakeNode* newHead); 64 65 //snake body extend 66 void snakeExtend(); 67 68 //set global value 69 Fruit fruit; 70 //建立一张全是空格的地图 71 char MAP[ROW][COL]; 72 //创建蛇头 73 SnakeNode* head = new SnakeNode(10, 20); 74 //记录分数 75 int cnt = 0; 76 //移动速度 77 int TIME = 60; 78 79 int main() 80 { 81 system("color 0C"); //设置背景色 82 system("title 贪吃蛇小游戏~"); //设置窗口名字 83 system("mode con cols=80 lines=30"); //设置窗口大小 84 memset(MAP, ‘ ‘, sizeof(MAP)); 85 createSnake(); //生成蛇 86 drawMap(); //主要函数,详见下面代码 87 return 0; 88 } 89 90 void drawMap() 91 { 92 while (true) { 93 //绘制地图外框 94 for (int row = 0; row != ROW; row++) { 95 for (int col = 0; col != COL; col++) { 96 if (row == 0 || row == ROW - 1) 97 MAP[row][col] = ‘-‘; 98 else if (col == 0 || col == COL - 1) 99 MAP[row][col] = ‘*‘; 100 } 101 } 102 103 if (!_kbhit()) 104 snakeMove(); //无键盘输入信号,继续向前移动 105 else snakeTurn(); //有键盘输入信号,进行转向判定 106 check(); //检查移动后是否吃到果实,是否越界,是否自撞 107 if (fruit.isEaten) { 108 createFruit(); 109 fruit.isEaten = false; 110 } 111 //打印地图 112 for (int row = 0; row != ROW; row++) { 113 for (int col = 0; col != COL; col++) { 114 printf("%c", MAP[row][col]); 115 } 116 printf("\n"); //换行 117 } 118 119 Sleep(TIME); 120 system("cls"); 121 } 122 } 123 124 void createFruit() 125 { 126 srand((unsigned)time(NULL)); 127 int row = rand() % (ROW - 1) + 1; 128 int col = rand() % (COL - 1) + 1; 129 130 //检查果实是否在蛇身生成 131 SnakeNode* cur = head; 132 while (cur) { 133 if (cur->row == row && cur->col == col) { 134 row = rand() % (ROW - 1) + 1; //若是,重新生成果实 135 col = rand() % (COL - 1) + 1; 136 cur = head; 137 continue; 138 } 139 cur = cur->next; 140 } 141 142 fruit.row = row; 143 fruit.col = col; 144 MAP[row][col] = ‘$‘; 145 } 146 147 void createSnake() 148 { 149 head->next = NULL; 150 151 //生成蛇身 152 const int N = 5; 153 for (int i = 0; i != N; i++) { 154 addNode(head); 155 } 156 157 //在MAP绘制蛇身 158 snakePrint(); 159 } 160 161 void addNode(SnakeNode* head) 162 { 163 SnakeNode* curBody = head; 164 165 while (curBody->next) { 166 curBody = curBody->next; 167 } 168 169 SnakeNode* malc = new SnakeNode(curBody->row, curBody->col - 1); 170 malc->next = NULL; 171 172 curBody->next = malc; 173 } 174 175 void snakeMove() 176 { 177 SnakeNode* newHead = new SnakeNode(); 178 //前进状态 179 if (head->row == head->next->row) { 180 if (head->col > head->next->col) newHead->col = head->col + 1; //若向右行 181 else newHead->col = head->col - 1; //若向左行 182 newHead->row = head->row; 183 newHead->next = head; 184 } 185 else if (head->col == head->next->col) { 186 if (head->row > head->next->row) newHead->row = head->row + 1; //若向下行 187 else newHead->row = head->row - 1; //若向上行 188 newHead->col = head->col; 189 newHead->next = head; 190 } 191 //蛇移动后,将蛇尾结点删除 192 tailErase(newHead); 193 head = newHead; 194 snakePrint(); 195 } 196 197 void snakePrint() 198 { 199 MAP[head->row][head->col] = ‘@‘; 200 SnakeNode* cur = head->next; 201 while (cur) { 202 MAP[cur->row][cur->col] = ‘#‘; 203 cur = cur->next; 204 } 205 } 206 207 void check() 208 { 209 if (fruit.row == head->row && fruit.col == head->col) { 210 fruit.isEaten = true; 211 cnt += 10; 212 TIME -= 5; 213 //snake extend while eating the fruit 214 snakeExtend(); 215 } 216 if (head->row == ROW-1 || head->row == 0 || head->col == COL-1 || head->col == 0) { 217 printf(" 撞到墙啦!游戏结束 -_- \n\n\n\n 本次得分%d!\n\n\n\n",cnt); 218 if ( cnt <= 40 ) printf(" 为何如此彩笔!\n"); 219 if (cnt > 40 && cnt < 80) printf(" 你离极限玩家不远了!\n"); 220 if (cnt >= 80) printf(" 你已经超神了!\n"); 221 system("pause"); 222 exit(1); 223 } 224 225 SnakeNode* cur = head->next; 226 while (cur->next) { 227 if (head->row == cur->row && head->col == cur->col) { 228 printf(" 撞到自己啦!游戏结束 -_- \n\n\n\n 本次得分%d!\n\n\n\n", cnt); 229 if (cnt <= 40) printf(" 为何如此彩笔!\n"); 230 if (cnt > 40 && cnt < 80) printf(" 你离极限玩家不远了!\n"); 231 if (cnt >= 80) printf(" 你已经超神了!\n"); 232 system("pause"); 233 exit(1); 234 } 235 cur = cur->next; 236 } 237 } 238 239 void snakeTurn() 240 { 241 char key; 242 if (_kbhit()) { //kbhit函数在有键盘输入是,返回0.. 检测是否有键盘输入 243 while (_kbhit()) //存在多次输入时,以最后一次输入为主 244 key = _getch(); 245 SnakeNode* newHead = new SnakeNode(); 246 switch (key) { //判断转向 247 case ‘w‘: { //上转 248 if (head->row <= head->next->row) { 249 newHead->row = head->row - 1; 250 newHead->col = head->col; 251 newHead->next = head; 252 tailErase(newHead); 253 head = newHead; 254 } 255 }break; 256 case ‘s‘: {//下转 257 if (head->row >= head->next->row) { 258 newHead->row = head->row + 1; 259 newHead->col = head->col; 260 newHead->next = head; 261 tailErase(newHead); 262 head = newHead; 263 } 264 }break; 265 case ‘a‘: {//左转 266 if (head->col <= head->next->col) { 267 newHead->row = head->row; 268 newHead->col = head->col - 1; 269 newHead->next = head; 270 tailErase(newHead); 271 head = newHead; 272 } 273 }break; 274 case ‘d‘: {//右转 275 if (head->col >= head->next->col) { 276 newHead->row = head->row; 277 newHead->col = head->col + 1; 278 newHead->next = head; 279 tailErase(newHead); 280 head = newHead; 281 } 282 }break; 283 default: { 284 snakeMove(); 285 }break; 286 } 287 snakePrint(); 288 } 289 } 290 291 void tailErase(SnakeNode* newHead) 292 { 293 //删除尾部节点 294 SnakeNode* cur = newHead; 295 while (cur->next->next) { 296 cur = cur->next; 297 } 298 //将尾部结点置空 299 MAP[cur->next->row][cur->next->col] = ‘ ‘; 300 free(cur->next); 301 cur->next = NULL; 302 } 303 304 void snakeExtend() 305 { 306 SnakeNode* cur = head; 307 308 while (cur->next->next) { 309 cur = cur->next; 310 } 311 312 SnakeNode* newTail = new SnakeNode(); 313 if (cur->row == cur->next->row) //尾部横向 314 if (cur->col > cur->next->col) { //尾巴在左 315 newTail->row = cur->next->row; 316 newTail->col = cur->next->col - 1; 317 } 318 else { 319 newTail->row = cur->next->row; 320 newTail->col = cur->next->col + 1; //尾巴在右 321 } 322 else /*if( cur->col == cur->next->col )*/ //尾部竖向 323 if (cur->row > cur->next->row) {//尾巴在上 324 newTail->row = cur->next->row - 1; 325 newTail->col = cur->next->col; 326 } 327 else { 328 newTail->row = cur->next->row + 1;//尾巴在下 329 newTail->col = cur->next->col; 330 } 331 332 newTail->next = NULL; 333 cur->next->next = newTail; 334 snakePrint(); 335 }
简单简单的控制台贪吃蛇就完成啦~
时间: 2024-10-02 13:50:06