C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次。
是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第二天写了第二版和第三版。
相信C语言写个小游戏或小项目是大多数计算机相关专业的学生都做的事情,但是作为一个数学专业的学生,我们教研室的老师对C语言的要求也就比较低了,大一没有让我们做个小项目实践一次。至今为止用C/C++做过的三个小项目(大作业),一个是外校同学让我帮忙写的学生信息管理系统(天呐,这名字都被大学用烂了...),一个是本校计院的同学让我帮忙写的链表设计的大作业,实话说,真的很无聊...
最后一个小项目就是贪吃蛇了,比前两个有意思多了,强烈建议大学老师让学生做些简单的界面程序而非黑框框下的所谓管理系统....让学生体会到乐趣才是真谛啊。
附:源码及游戏程序下载地址:贪吃蛇 访问密码 51b4
第一版:
首先是第一版的源码奉上,因为没有学过MFC,这一版是借鉴了其他网站上的C语言贪吃蛇算法思路和图形写成的。
我认为这里的贪吃蛇算法思路非常棒,虽然不知道这种算法的来由但是我对此算法创始人由衷敬佩...
这里的算法思路其实是将图上每一个点的二维坐标转换成一维序列,然后通过一维序列映射地图上的每一个坐标
最让我敬佩的地方就是在蛇头重绘的时候,这里的贪吃蛇移动地图上的二维坐标和原始地图的一维序列将随着蛇头的移动而不断发生交替改变,以适应贪吃蛇循环队列的算法要求。
操作说明:WSAD控制方向。
1 #include <stdio.h> 2 #include <ctype.h> 3 #include <conio.h> 4 #include <time.h> 5 #include <windows.h> 6 7 //背景宽-高 8 #define HEIGHT 20 9 #define WIDTH 20 10 //地图中心 11 #define XCENTER HEIGHT/2 12 #define YCENTER WIDTH/2 13 //最大蛇长 14 #define SNAKE_MAXLEN ((HEIGHT - 2) * (WIDTH - 2)) 15 //坐标->编号 16 #define NUM(x,y) ((x-1)*(WIDTH-2)+(y-1)) 17 18 //模型 19 struct Model{ 20 char *ch; 21 int color; 22 char flag; 23 } 24 border = { "□", 4, 1 }, 25 background = { "█", 2, 2 }, 26 snakeHead = { "★", 0xE, 3 }, 27 snakeBody = { "■", 0xE, 3 }, 28 food = { "●", 0xC, 4 }; 29 30 int snakeLen = 3; //贪吃蛇长度 31 32 //地图 33 struct Map { 34 int flag; //模型标识 35 int num; //坐标编号 36 }map[WIDTH][HEIGHT]; 37 38 //坐标 39 //贪吃蛇移动范围(循环队列),分数位置 40 struct Coordinate{ 41 int x, y; 42 }snake[SNAKE_MAXLEN], scorePos; 43 44 45 int score; //分数 46 int header, tail; //蛇头蛇尾下标 47 HANDLE hConsole; 48 49 void setCursor(int x, int y) 50 { 51 //参数x为行,y为列-与光标设置相反 52 COORD coord; 53 coord.X = 2*y; //宽字符决定占两列 54 coord.Y = x; 55 SetConsoleCursorPosition(hConsole, coord); 56 } 57 58 void setColor(int color) 59 { 60 SetConsoleTextAttribute(hConsole, color); 61 } 62 63 //随机生成食物 64 void createFood() 65 { 66 int num; //新食物对应的坐标编号 67 int range; //新食物所在范围 68 srand((unsigned)time(NULL)); //初始种子 69 range = SNAKE_MAXLEN - snakeLen; //食物生成范围 70 if (tail < header) 71 num = rand() % range + tail + 1; 72 else { 73 num = rand() % range; 74 if (num >= header) 75 num += snakeLen; 76 } 77 //绘制食物 78 int tx = snake[num].x; 79 int ty = snake[num].y; 80 setCursor(tx, ty); 81 setColor(food.color); 82 printf("%s", food.ch); 83 map[tx][ty].flag = food.flag; 84 } 85 86 //游戏结束 87 void gameOver() 88 { 89 setCursor(XCENTER, YCENTER - 4); 90 setColor(0xC); 91 92 printf("Game Over!"); 93 getch(); 94 exit(0); //结束程序 95 } 96 97 void move(char direction) 98 { 99 //新蛇头 100 int tx = snake[header].x; 101 int ty = snake[header].y; 102 switch (direction) 103 { 104 case ‘w‘: 105 tx--; break; 106 case ‘s‘: 107 tx++; break; 108 case ‘a‘: 109 ty--; break; 110 case ‘d‘: 111 ty++; break; 112 } 113 114 //判断是否会出界或撞到自己 115 if (map[tx][ty].flag == border.flag || map[tx][ty].flag == snakeBody.flag) 116 gameOver(); 117 118 //新蛇头绘制(此时未refresh模型标识) 119 setCursor(tx, ty); 120 setColor(snakeHead.color); 121 printf("%s", snakeHead.ch); 122 //原蛇头重绘制 123 setCursor(snake[header].x, snake[header].y); 124 printf("%s", snakeBody.ch); //小方块 125 126 //蛇头更新-队首移动 127 header = header == 0 ? SNAKE_MAXLEN - 1 : header - 1; 128 //旧编号位置更正 129 int preNum = map[tx][ty].num; //蛇头位置旧编号 130 snake[preNum].x = snake[header].x; 131 snake[preNum].y = snake[header].y; 132 map[snake[preNum].x][snake[preNum].y].num = preNum; 133 //蛇头位置更正 134 snake[header].x = tx; 135 snake[header].y = ty; 136 map[tx][ty].num = header; //蛇头位置新编号 137 138 //判断是否吃到食物 139 if (map[tx][ty].flag == food.flag) 140 { 141 createFood(); //随机生成食物 142 snakeLen++; //蛇身加长 143 //更新-score 144 setCursor(scorePos.x, scorePos.y); 145 printf("%d", ++score); 146 } 147 else { 148 //删除蛇尾 149 setCursor(snake[tail].x, snake[tail].y); 150 map[snake[tail].x][snake[tail].y].flag = background.flag; 151 setColor(background.color); 152 printf("%s", background.ch); 153 tail = tail == 0 ? SNAKE_MAXLEN - 1 : tail - 1; 154 } 155 156 //更新-蛇头模型标识 157 map[tx][ty].flag = snakeBody.flag; 158 } 159 160 //初始化界面 161 void init() 162 { 163 CONSOLE_CURSOR_INFO cci; //光标信息 164 header = 0; 165 tail = snakeLen - 1; 166 167 //设置光标不可见 168 hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 169 GetConsoleCursorInfo(hConsole, &cci); 170 cci.bVisible = 0; 171 SetConsoleCursorInfo(hConsole, &cci); 172 173 //dos命令设置窗口大小 174 system("mode con:cols=100 lines=32"); 175 176 //绘制背景 177 for (int x = 0; x < HEIGHT; x++) 178 { 179 for (int y = 0; y < WIDTH; y++) 180 { 181 if (x == 0 || y == 0 || x == HEIGHT - 1 || y == WIDTH - 1) 182 { 183 map[x][y].flag = border.flag; 184 setColor(border.color); 185 printf("%s", border.ch); 186 } 187 else { 188 int num = NUM(x,y); 189 snake[num].x = x; 190 snake[num].y = y; 191 //背景设置 192 map[x][y].num = num; 193 map[x][y].flag = background.flag; 194 setColor(background.color); 195 printf("%s", background.ch); 196 } 197 } 198 printf("\n"); 199 } 200 201 //绘制初始贪吃蛇 202 setCursor(XCENTER, YCENTER - snakeLen / 2); 203 header = NUM(XCENTER, YCENTER - 1); //蛇头位置 204 tail = header + snakeLen - 1; 205 setColor(snakeBody.color); 206 for (int i = header; i <= tail; i++) 207 { 208 if (i == header) printf("%s", snakeHead.ch); 209 else printf("%s", snakeBody.ch); 210 map[snake[i].x][snake[i].y].flag = snakeBody.flag; 211 } 212 213 //随机生成食物 214 createFood(); 215 216 //其他信息 217 setCursor(XCENTER - 1, WIDTH + 2); 218 printf(" score: 0"); 219 setCursor(XCENTER, WIDTH + 2); 220 printf(" Author: InkMark"); 221 setCursor(XCENTER + 1, WIDTH + 2); 222 printf(" Blog: www.cnblogs.com/inkblots"); 223 //分数位置 224 scorePos.x = XCENTER - 1; 225 scorePos.y = WIDTH + 7; 226 } 227 228 229 int main() 230 { 231 //方向及其他初始化 232 char direction = ‘a‘; 233 init(); 234 235 char ch = tolower(getch()); 236 if (ch == ‘a‘ || ch == ‘w‘ || ch == ‘s‘) 237 direction = ch; 238 239 while (1) { 240 if (kbhit()) { 241 ch = tolower(getch()); 242 if (ch == ‘ ‘) 243 ch = getch(); //实现空格-暂停 244 if(ch == ‘a‘ || ch == ‘d‘ || ch == ‘w‘ || ch==‘s‘) 245 if (ch + direction != 234 && ch + direction != 197) 246 direction = ch; //不为反向时改变方向 247 } 248 move(direction); 249 Sleep(500); 250 } 251 252 return 0; 253 }
= 。=
第二版:
第二版在第一版的算法基础上改进了图形界面和源码管理,新增了“加速”技能(比较方便实现)。
控制方向也改为方向键了。
第二版源码分为三个文件,运行源码需要链接一次。
先是一个头文件,定义了要用到的库文件,常用常量和结构体,以及声明main中要用到的函数
1 #ifndef CATALOG_H 2 #define CATALOG_H 3 4 #include<stdio.h> 5 #include<conio.h> 6 #include<time.h> 7 #include<windows.h> 8 9 //背景上下伸展长度 10 #define TOP_EXTEND 3 11 #define BOTTOM_EXTEND 3 12 //背景宽-高 13 #define WIDTH 33 14 #define HEIGHT 20 15 //地图中心 16 #define XCENTER ((HEIGHT + 1)/2 + TOP_EXTEND) 17 #define YCENTER (WIDTH + 1)/2 18 //最大蛇长 19 #define SNAKE_MAXLEN ((HEIGHT - 2) * (WIDTH - 2)) 20 //坐标->序号 21 #define ORDER(x,y) ((x - TOP_EXTEND - 1) * (WIDTH - 2) + (y - 1)) 22 //点:在移动范围内 23 #define IN_RANGE(x,y) (x > TOP_EXTEND && x < HEIGHT + TOP_EXTEND - 1 && y > 0 && y < WIDTH - 1) 24 //默认暂停时间 25 #define PAUSE_TIME 500 26 27 /*全局变量*/ 28 extern int snakeLen; 29 extern int direction; 30 extern int header, tail; 31 extern int score; 32 extern int skill; 33 extern int pauseTime; 34 extern HANDLE hConsole; 35 36 //模型 37 typedef struct Model { 38 char *ch; 39 int color; 40 int flag; 41 }Model; 42 43 //地图 44 typedef struct Map { 45 int flag; //模型标识 46 int order; //序号 47 }Map; 48 49 //坐标 50 //贪吃蛇移动范围(循环队列),分数位置 51 typedef struct Coordinate { 52 int x, y; 53 }Coordinate; 54 55 56 /******界面设定******/ 57 58 void setCursor(int x, int y); 59 60 void setColor(int color); 61 62 void control(char ch); 63 64 /******框架及功能******/ 65 66 void init(); 67 68 int move(char direction); 69 70 void createFood(); 71 72 73 #endif
catalog.h
第二个是功能部分源码
1 #include "catalog.h" 2 3 Model border = { NULL, 0x7, 1 }, 4 snakeHead = { "★", 0xE, 2 }, 5 snakeBody = { "■", 0xE, 2 }, 6 food = { "●", 0xC, 3 }; 7 8 Map map[HEIGHT][WIDTH]; 9 10 Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos; 11 12 /************游戏界面设定************/ 13 14 //隐藏光标 15 void hideCursor() 16 { 17 CONSOLE_CURSOR_INFO cci; //光标信息 18 hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 19 GetConsoleCursorInfo(hConsole, &cci); 20 cci.bVisible = 0; 21 SetConsoleCursorInfo(hConsole, &cci); 22 } 23 24 //设置光标位置 25 void setCursor(int x, int y) 26 { 27 //参数x为行,y为列-与光标设置相反 28 COORD coord; 29 coord.X = 2 * y; //宽字符决定占两列 30 coord.Y = x; 31 SetConsoleCursorPosition(hConsole, coord); 32 } 33 34 //设置文字颜色 35 void setColor(int color) 36 { 37 SetConsoleTextAttribute(hConsole, color); 38 } 39 40 //绘制游戏窗口(包括dos环境初始窗口) 41 void createWindow() 42 { 43 //dos命令设置窗口大小 44 system("mode con:cols=72 lines=27"); 45 46 //╔ ╗ ╝ ╚ ═ ║ ╠ ╣ ╦ ╩ 47 setColor(0x7); 48 printf("╔════════════════╦════════════════╗\n"); //0~34 49 for (int i = 1; i < TOP_EXTEND; i++) 50 printf("║ ║ ║\n"); 51 printf("╠════════════════╩════════════════╣\n"); 52 for (int i = 1; i < HEIGHT - 1; i++) 53 printf("║ ║\n"); 54 printf("╠═════════════════════════════════╣\n"); 55 for (int i = 1; i < BOTTOM_EXTEND; i++) 56 printf("║ ║\n"); 57 printf("╚═════════════════════════════════╝\n"); 58 setCursor(HEIGHT + TOP_EXTEND, 1); 59 printf(" 操作指南:方向键控制,空格可暂停,5分后可获得技能."); 60 setCursor(HEIGHT + TOP_EXTEND + 1, 1); 61 printf(" Version : 2.0"); 62 63 for (int x = TOP_EXTEND; x < HEIGHT + TOP_EXTEND; x++) 64 { 65 for (int y = 0; y < WIDTH; y++) 66 if (IN_RANGE(x, y)) 67 { 68 int order = ORDER(x, y); 69 snake[order].x = x; 70 snake[order].y = y; 71 //背景设置 72 map[x][y].order = order; 73 map[x][y].flag = 0; 74 } 75 else map[x][y].flag = border.flag; 76 } 77 //其他信息 78 setColor(0x7); 79 setCursor(1, 1); 80 printf(" 你的分数:0"); 81 setCursor(2, 1); 82 printf(" 技能:无"); 83 setCursor(1, YCENTER + 1); 84 printf(" Author: InkMark"); 85 setCursor(2, YCENTER + 1); 86 printf(" Blog: www.cnblogs.com/inkblots"); 87 88 //分数位置 89 scorePos.x = 1; scorePos.y = 11; 90 skillPos.x = 2; skillPos.y = 9; 91 } 92 93 //控制键 94 void control(char ch) 95 { 96 //本应取方向键高位再取低位,在此以简便为主 97 if (ch != -32) return; 98 ch = getch(); 99 //上72下80左75右77 100 if (ch == 72 || ch == 80 || ch == 75 || ch == 77) //低位相同 101 if (ch + direction != 152) direction = ch; //不为反向时改变方向 102 } 103 104 /************游戏信息输出************/ 105 106 void getSkill() 107 { 108 setCursor(skillPos.x, skillPos.y); 109 printf("加速(S键)"); 110 skill = 1; //加速技能开启 111 } 112 113 void alterScore(int addition) 114 { 115 score += addition; 116 if (score >= 5) 117 getSkill(); 118 setCursor(scorePos.x, scorePos.y); 119 setColor(0x7); 120 printf("%d", score); 121 } 122 123 void gameOver() 124 { 125 if (IDYES == MessageBox(NULL, "是否重新开始游戏?", "Snake_2.0", MB_YESNO)) 126 { 127 system("CLS"); 128 return; 129 } 130 131 exit(0); 132 } 133 134 /************游戏框架及功能************/ 135 136 //变量初始化及界面初始化 137 void init() 138 { 139 pauseTime = PAUSE_TIME; 140 skill = 0; 141 score = 0; 142 snakeLen = 1; 143 header = 0; 144 tail = snakeLen - 1; 145 146 hideCursor();//隐藏光标 147 createWindow(); //绘制背景 148 } 149 150 //贪吃蛇移动 151 //return:1为成功,0为失败 152 int move(char direction) 153 { 154 //新蛇头 155 int tx = snake[header].x; 156 int ty = snake[header].y; 157 //上72下80左75右77 158 switch (direction) 159 { 160 case 72: tx--; break; 161 case 80: tx++; break; 162 case 75: ty--; break; 163 case 77: ty++; break; 164 } 165 166 //判断是否撞墙或撞到自己 167 if (map[tx][ty].flag == border.flag || map[tx][ty].flag == snakeBody.flag) 168 { 169 gameOver(); 170 return 0; 171 } 172 173 //新蛇头绘制(此时未refresh模型标识) 174 setCursor(tx, ty); 175 setColor(snakeHead.color); 176 printf("%s", snakeHead.ch); 177 //原蛇头重绘制 178 setCursor(snake[header].x, snake[header].y); 179 printf("%s", snakeBody.ch); 180 181 //蛇头更新-队首移动 182 header = header == 0 ? SNAKE_MAXLEN - 1 : header - 1; 183 //旧编号位置更正 184 int preNum = map[tx][ty].order; //蛇头位置旧编号 185 snake[preNum].x = snake[header].x; 186 snake[preNum].y = snake[header].y; 187 map[snake[preNum].x][snake[preNum].y].order = preNum; 188 //蛇头位置更正 189 snake[header].x = tx; 190 snake[header].y = ty; 191 map[tx][ty].order = header; //蛇头位置新编号 192 193 //判断是否吃到食物 194 if (map[tx][ty].flag == food.flag) 195 { 196 snakeLen++; //蛇身加长 197 createFood(); //随机生成食物 198 //更新-score 199 alterScore(1); 200 } 201 else { 202 //删除蛇尾 203 setCursor(snake[tail].x, snake[tail].y); 204 map[snake[tail].x][snake[tail].y].flag = 0; 205 printf(" "); 206 tail = tail == 0 ? SNAKE_MAXLEN - 1 : tail - 1; 207 } 208 209 //更新-蛇头模型标识 210 map[tx][ty].flag = snakeBody.flag; 211 212 return 1; 213 } 214 215 //随机生成食物 216 void createFood() 217 { 218 int order; //新食物对应的坐标编号 219 int range; //新食物所在范围 220 srand((unsigned)time(NULL)); //初始种子 221 range = SNAKE_MAXLEN - snakeLen; //食物生成范围 222 if (tail < header) 223 order = rand() % range + tail + 1; 224 else { 225 order = rand() % range; 226 if (order >= header) 227 order += snakeLen; 228 } 229 //绘制食物 230 int tx = snake[order].x; 231 int ty = snake[order].y; 232 setCursor(tx, ty); 233 setColor(food.color); 234 printf("%s", food.ch); 235 map[tx][ty].flag = food.flag; 236 }
Util.c
第三个是主函数及初始化部分
1 #include "catalog.h" 2 3 int snakeLen; //贪吃蛇长度 4 int direction; //贪吃蛇移动方向 5 int header, tail; //蛇头蛇尾下标 6 int score; //分数 7 int skill; //技能开关 8 int pauseTime; //暂停时间(Ms) 9 HANDLE hConsole; 10 11 extern Model border, snakeHead, snakeBody, food; 12 extern Map map[HEIGHT][WIDTH]; 13 extern Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos; 14 15 //初始化方案 16 void scheme() 17 { 18 //变量初始化及界面初始化 19 init(); 20 21 //绘制初始贪吃蛇 22 setCursor(XCENTER, YCENTER - snakeLen / 2); 23 header = ORDER(XCENTER, YCENTER - snakeLen / 2); //蛇头位置 24 tail = header + snakeLen - 1; 25 setColor(snakeBody.color); 26 for (int i = header; i <= tail; i++) 27 { 28 if (i == header) printf("%s", snakeHead.ch); 29 else printf("%s", snakeBody.ch); 30 map[snake[i].x][snake[i].y].flag = snakeBody.flag; 31 } 32 33 //随机生成食物 34 createFood(); 35 36 //初始方向处理 37 direction = 75; 38 char ch = getch(); 39 control(ch); 40 } 41 42 43 int main() 44 { 45 //初始方案 46 scheme(); 47 48 while (1) { 49 if (kbhit()) { 50 char ch = getch(); 51 if (ch == ‘ ‘) MessageBox(NULL, " 暂停中...\n 请单击确定继续游戏", "暂停游戏", MB_OK); 52 if (skill == 1 && (ch == ‘s‘ || ch == ‘S‘)) 53 pauseTime = pauseTime == PAUSE_TIME ? PAUSE_TIME / 2 : PAUSE_TIME; 54 else control(ch); //控制方向 55 } 56 if (move(direction)) 57 Sleep(pauseTime); 58 else scheme(); 59 } 60 61 return 0; 62 }
SnakeMain.c
第三版:
第三版加入了菜单,优化了部分界面,并将游戏分为两个基本模式。
和第二版一样,分为三个文件
头文件:
1 #ifndef CATALOG_H 2 #define CATALOG_H 3 4 #include<stdio.h> 5 #include<conio.h> 6 #include<time.h> 7 #include<windows.h> 8 9 //背景上下伸展长度 10 #define TOP_EXTEND 3 11 #define BOTTOM_EXTEND 3 12 //背景宽-高 13 #define WIDTH 33 14 #define HEIGHT 20 15 //地图中心 16 #define XCENTER ((HEIGHT + 1)/2 + TOP_EXTEND) 17 #define YCENTER (WIDTH + 1)/2 18 //最大蛇长 19 #define SNAKE_MAXLEN ((HEIGHT - 2) * (WIDTH - 2)) 20 //坐标->序号 21 #define ORDER(x,y) ((x - TOP_EXTEND - 1) * (WIDTH - 2) + (y - 1)) 22 //点:在移动范围内 23 #define IN_RANGE(x,y) (x > TOP_EXTEND && x < HEIGHT + TOP_EXTEND - 1 && y > 0 && y < WIDTH - 1) 24 //默认暂停时间 25 #define PAUSE_TIME 500 26 27 /*全局变量*/ 28 extern int snakeLen; 29 extern int direction; 30 extern int header, tail; 31 extern int score; 32 extern int skill; 33 extern int pauseTime; 34 extern int mode; 35 extern HANDLE hConsole; 36 37 //模型 38 typedef struct Model { 39 char *ch; 40 int color; 41 int flag; 42 }Model; 43 44 //地图 45 typedef struct Map { 46 int flag; //模型标识 47 int order; //序号 48 }Map; 49 50 //坐标 51 //贪吃蛇移动范围(循环队列),分数位置 52 typedef struct Coordinate { 53 int x, y; 54 }Coordinate; 55 56 57 /******界面设定******/ 58 59 extern void setCursor(int x, int y); 60 61 extern void setColor(int color); 62 63 extern void control(char ch); 64 65 /******框架及功能******/ 66 67 extern void init(); 68 69 extern int move(char direction); 70 71 extern void createFood(); 72 73 74 #endif
catalog.h
功能文件:
1 #include "catalog.h" 2 3 Model border = { NULL, 0x7, 1 }, 4 snakeHead = { "★", 0xE, 2 }, 5 snakeBody = { "■", 0xE, 2 }, 6 food = { "●", 0xC, 3 }; 7 8 Map map[HEIGHT][WIDTH]; 9 10 Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos, dotPos; 11 12 #define UP 4 //向上偏移量 13 #define LEFT 6 //左移偏移量 14 15 /************游戏界面设定************/ 16 17 //隐藏光标 18 void hideCursor() 19 { 20 CONSOLE_CURSOR_INFO cci; //光标信息 21 hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 22 GetConsoleCursorInfo(hConsole, &cci); 23 cci.bVisible = 0; 24 SetConsoleCursorInfo(hConsole, &cci); 25 } 26 27 //设置光标位置 28 void setCursor(int x, int y) 29 { 30 //参数x为行,y为列-与光标设置相反 31 COORD coord; 32 coord.X = 2 * y; //宽字符决定占两列 33 coord.Y = x; 34 SetConsoleCursorPosition(hConsole, coord); 35 } 36 37 //设置文字颜色 38 void setColor(int color) 39 { 40 SetConsoleTextAttribute(hConsole, color); 41 } 42 43 //绘制游戏窗口(包括dos环境初始窗口) 44 void createWindow() 45 { 46 //dos命令设置窗口大小 47 system("mode con:cols=72 lines=28"); 48 49 //╔ ╗ ╝ ╚ ═ ║ ╠ ╣ ╦ ╩ 50 setColor(0x7); 51 printf("╔════════════════╦════════════════╗\n"); //0~34 52 for (int i = 1; i < TOP_EXTEND; i++) 53 printf("║ ║ ║\n"); 54 printf("╠════════════════╩════════════════╣\n"); 55 for (int i = 1; i < HEIGHT - 1; i++) 56 printf("║ ║\n"); 57 printf("╠═════════════════════════════════╣\n"); 58 for (int i = 1; i < BOTTOM_EXTEND; i++) 59 printf("║ ║\n"); 60 printf("╚═════════════════════════════════╝\n"); 61 setCursor(HEIGHT + TOP_EXTEND, 1); 62 printf(" 操作指南:上下键及回车键选择."); 63 setCursor(HEIGHT + TOP_EXTEND + 1, 1); 64 printf(" Version : 3.0"); 65 66 for (int x = TOP_EXTEND; x < HEIGHT + TOP_EXTEND; x++) 67 { 68 for (int y = 0; y < WIDTH; y++) 69 if (IN_RANGE(x, y)) 70 { 71 int order = ORDER(x, y); 72 snake[order].x = x; 73 snake[order].y = y; 74 //背景设置 75 map[x][y].order = order; 76 map[x][y].flag = 0; 77 } 78 else map[x][y].flag = border.flag; 79 } 80 //其他信息 81 setColor(0x7); 82 setCursor(1, 1); 83 printf(" 你的分数:0"); 84 setCursor(2, 1); 85 printf(" 技能:无"); 86 setCursor(1, YCENTER + 1); 87 printf(" Author: InkMark"); 88 setCursor(2, YCENTER + 1); 89 printf(" Blog: www.cnblogs.com/inkblots"); 90 91 //分数位置 92 scorePos.x = 1; scorePos.y = 11; 93 skillPos.x = 2; skillPos.y = 9; 94 } 95 96 void menuButton(char ch) 97 { 98 int tx = dotPos.x; 99 int ty = dotPos.y; 100 if (ch == 72) tx--; 101 else tx++; 102 103 if (tx > XCENTER - UP && tx < XCENTER - UP + 4) 104 { 105 setCursor(dotPos.x, dotPos.y); 106 printf(" "); 107 setCursor(tx, ty); 108 printf("●"); 109 dotPos.x = tx; 110 dotPos.y = ty; 111 } 112 } 113 114 //绘制菜单并进行选择 115 void menu() 116 { 117 //╔ ╗ ╚ ╝ ═ ║ ╠ ╣ ╦ ╩ 118 setCursor(XCENTER - UP, YCENTER - LEFT); 119 printf("╔═════════╗"); //11 120 for (int i = 1; i <= 3; i++) 121 { 122 setCursor(XCENTER - UP + i, YCENTER - LEFT); 123 printf("║ ║"); 124 } 125 setCursor(XCENTER - UP + 4, YCENTER - LEFT); 126 printf("╚═════════╝"); 127 128 dotPos.x = XCENTER - UP + 1; 129 dotPos.y = YCENTER - 4; 130 setCursor(dotPos.x, dotPos.y); 131 printf("● 1.死亡模式"); 132 setCursor(XCENTER - UP + 2, YCENTER - 2); 133 printf("2.技能模式"); 134 setCursor(XCENTER - UP + 3, YCENTER - 2); 135 printf("3.退出游戏"); 136 137 char ch; 138 while ((ch = getch()) != 13) //确定键:13 139 { 140 if (ch != -32) continue; 141 ch = getch(); 142 if (ch == 72 || ch == 80) 143 menuButton(ch); 144 } 145 146 for (int i = 0; i < 5; i++) 147 { 148 setCursor(XCENTER - UP + i, YCENTER - LEFT); 149 printf(" "); 150 } 151 152 mode = dotPos.x - XCENTER + UP; 153 if (mode == 3) exit(0); 154 } 155 156 //控制键 157 void control(char ch) 158 { 159 //本应取方向键高位再取低位,在此以简便为主 160 if (ch != -32) return; 161 ch = getch(); 162 //上72下80左75右77 163 if (ch == 72 || ch == 80 || ch == 75 || ch == 77) //低位相同 164 if (ch + direction != 152) 165 direction = ch; //不为反向时改变方向 166 } 167 168 /************游戏信息输出************/ 169 170 void getSkill() 171 { 172 setCursor(skillPos.x, skillPos.y); 173 printf("加速(S键)"); 174 skill = 1; //加速技能开启 175 } 176 177 void alterScore(int addition) 178 { 179 score += addition; 180 if (mode == 2 && score >= 8) 181 getSkill(); 182 setCursor(scorePos.x, scorePos.y); 183 setColor(0x7); 184 printf("%d", score); 185 } 186 187 void printInfo() 188 { 189 //╔ ╗ ╚ ╝ ═ ║ ╠ ╣ ╦ ╩ 190 int col = 28; 191 setCursor(HEIGHT + TOP_EXTEND - 1, col); 192 printf("╦"); 193 setCursor(HEIGHT + TOP_EXTEND, col); 194 printf("║"); 195 setCursor(HEIGHT + TOP_EXTEND + 1, col); 196 printf("║ 模 式"); 197 if (mode == 1) { 198 199 setCursor(HEIGHT + TOP_EXTEND, 1); 200 printf(" 操作指南:方向键控制,空格可暂停."); 201 setCursor(HEIGHT + TOP_EXTEND, col + 1); 202 printf(" 无 尽"); 203 } 204 else { 205 setCursor(HEIGHT + TOP_EXTEND, 1); 206 printf(" 操作指南:方向键控制,空格可暂停,8分后有惊喜."); 207 setCursor(HEIGHT + TOP_EXTEND, col + 1); 208 printf(" 技 能"); 209 } 210 setCursor(HEIGHT + TOP_EXTEND + 2, col); 211 printf("╩"); 212 } 213 214 void gameOver() 215 { 216 if (IDYES == MessageBox(NULL, "是否重新开始游戏?", "Snake_2.0", MB_YESNO)) 217 { 218 system("CLS"); 219 return; 220 } 221 222 exit(0); 223 } 224 225 /************游戏框架及功能************/ 226 227 //变量初始化及界面初始化 228 void init() 229 { 230 pauseTime = PAUSE_TIME; 231 skill = 0; 232 score = 0; 233 snakeLen = 1; 234 header = 0; 235 tail = snakeLen - 1; 236 237 hideCursor();//隐藏光标 238 createWindow(); //绘制背景 239 240 menu(); //菜单操作 241 242 printInfo(); 243 } 244 245 //贪吃蛇移动 246 //return:1为成功,0为失败 247 int move(char direction) 248 { 249 //新蛇头 250 int tx = snake[header].x; 251 int ty = snake[header].y; 252 //上72下80左75右77 253 switch (direction) 254 { 255 case 72: tx--; break; 256 case 80: tx++; break; 257 case 75: ty--; break; 258 case 77: ty++; break; 259 } 260 261 //判断是否撞墙或撞到自己 262 if (map[tx][ty].flag == border.flag || map[tx][ty].flag == snakeBody.flag) 263 { 264 gameOver(); 265 return 0; 266 } 267 268 //新蛇头绘制(此时未refresh模型标识) 269 setCursor(tx, ty); 270 setColor(snakeHead.color); 271 printf("%s", snakeHead.ch); 272 //原蛇头重绘制 273 setCursor(snake[header].x, snake[header].y); 274 printf("%s", snakeBody.ch); 275 276 //蛇头更新-队首移动 277 header = header == 0 ? SNAKE_MAXLEN - 1 : header - 1; 278 //旧编号位置更正 279 int preNum = map[tx][ty].order; //蛇头位置旧编号 280 snake[preNum].x = snake[header].x; 281 snake[preNum].y = snake[header].y; 282 map[snake[preNum].x][snake[preNum].y].order = preNum; 283 //蛇头位置更正 284 snake[header].x = tx; 285 snake[header].y = ty; 286 map[tx][ty].order = header; //蛇头位置新编号 287 288 //判断是否吃到食物 289 if (map[tx][ty].flag == food.flag) 290 { 291 snakeLen++; //蛇身加长 292 createFood(); //随机生成食物 293 //更新-score 294 if(pauseTime == PAUSE_TIME) alterScore(1); 295 else alterScore(2); 296 } 297 else { 298 //删除蛇尾 299 setCursor(snake[tail].x, snake[tail].y); 300 map[snake[tail].x][snake[tail].y].flag = 0; 301 printf(" "); 302 tail = tail == 0 ? SNAKE_MAXLEN - 1 : tail - 1; 303 } 304 305 //更新-蛇头模型标识 306 map[tx][ty].flag = snakeBody.flag; 307 308 return 1; 309 } 310 311 //随机生成食物 312 void createFood() 313 { 314 int order; //新食物对应的坐标编号 315 int range; //新食物所在范围 316 srand((unsigned)time(NULL)); //初始种子 317 range = SNAKE_MAXLEN - snakeLen; //食物生成范围 318 if (tail < header) 319 order = rand() % range + tail + 1; 320 else { 321 order = rand() % range; 322 if (order >= header) 323 order += snakeLen; 324 } 325 //绘制食物 326 int tx = snake[order].x; 327 int ty = snake[order].y; 328 setCursor(tx, ty); 329 setColor(food.color); 330 printf("%s", food.ch); 331 map[tx][ty].flag = food.flag; 332 }
Util.c
主文件:
1 #include "catalog.h" 2 3 int snakeLen; //贪吃蛇长度 4 int direction; //贪吃蛇移动方向 5 int header, tail; //蛇头蛇尾下标 6 int score; //分数 7 int skill; //技能开关 8 int pauseTime; //暂停时间(Ms) 9 int mode; 10 HANDLE hConsole; 11 12 extern Model border, snakeHead, snakeBody, food; 13 extern Map map[HEIGHT][WIDTH]; 14 extern Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos; 15 16 //初始化方案 17 void scheme() 18 { 19 //变量初始化及界面初始化 20 init(); 21 22 //绘制初始贪吃蛇 23 setCursor(XCENTER, YCENTER - snakeLen / 2); 24 header = ORDER(XCENTER, YCENTER - snakeLen / 2); //蛇头位置 25 tail = header + snakeLen - 1; 26 setColor(snakeBody.color); 27 for (int i = header; i <= tail; i++) 28 { 29 if (i == header) printf("%s", snakeHead.ch); 30 else printf("%s", snakeBody.ch); 31 map[snake[i].x][snake[i].y].flag = snakeBody.flag; 32 } 33 34 //随机生成食物 35 createFood(); 36 37 //初始方向处理 38 direction = 75; 39 char ch = getch(); 40 control(ch); 41 } 42 43 44 int main() 45 { 46 //初始方案 47 scheme(); 48 49 while (1) { 50 if (kbhit()) { 51 char ch = getch(); 52 if (ch == ‘ ‘) MessageBox(NULL, " 暂停中...\n 请单击确定继续游戏", "暂停游戏", MB_OK); 53 if (mode == 2 && skill == 1 && (ch == ‘s‘ || ch == ‘S‘)) 54 pauseTime = pauseTime == PAUSE_TIME ? PAUSE_TIME / 2 : PAUSE_TIME; 55 else control(ch); //控制方向 56 } 57 if (move(direction)) 58 Sleep(pauseTime); 59 else scheme(); 60 } 61 62 return 0; 63 }
SnakeMain.c