贪吃蛇的代码和解析

到了本章,你已经学完了大部分C语言的基础知识,详细讲解贪吃蛇代码的条件就具备了。 
本教程教你编写的贪吃蛇不依赖TC环境,不依赖任何第三方库,可以在VC 6.0、VS等常见IDE中编译通过,请看下图: 
更多效果图请查看:游戏初始化、游戏进行中、游戏结束。 请大家先把贪吃蛇的源码下载下来浏览一下,我们再具体分析,这样将会有更好的效果。 
贪吃蛇源代码下载:http://pan.baidu.com/s/1bnwJB8V    提取密码:81qm 
贪吃蛇设计思路 
上图中的红色空心方框(□)表示边框,是贪吃蛇的边界,贪吃蛇不能碰到它,否则就“死掉”,游戏结束。绿色实心方框(■)表示贪吃蛇的活动范围,贪吃蛇可以自由移动,食物(苹果)也会随机出现在这个区域。我们不妨将贪吃蛇的活动范围称为“贪吃蛇地图”,而加上边框就称为“全局地图”。 
我们需要记录地图中每一个节点的信息,包括: 位置:也就是第几行几列; 
类型:这个节点出现的是贪吃蛇、食物、边框,还是什么都没有(绿色的背景)。 
索引:也就是数组下标,稍后会说明是什么意思。 所以需要定义一个结构体二维数组: 

 

struct{
char type; int index;  }globalMap[MAXWIDTH][MAXHEIGHT];

 

用一维下标和二维下标表示位置;用

 type 

表示类型,不同的类

型用不同的数字代表;用

 index 

表示索引。

 

直观上讲,应该将

 type 

定义为

int

类型,不过

int

占用四个字

节,而节点类型的取值范围非常有限,一个字节就足够了,所以为了

节省内存才定义为

char

类型。

 

同时,再建立一个足够大的一维数组,让贪吃蛇在数组内活动:

 

struct{ int x; int y; 
} snakeMap[ (MAXWIDTH-2)*(MAXHEIGHT-2) ] 

 

x、y 表示行和列,也就是 globalMap 数组的两个下标。globalMap 数组中的索引 index 就是 snakeMap 数组的下标。 
globalMap 表示了所有节点的信息,而 snakeMap 只表示了贪吃蛇的活动区域。通过 snakeMap 可以定位 globalMap 中的元素,反过来通过 globalMap 也可以找到 snakeMap 中的元素。请看下图: 
图1:globalMap 和 snakeMap 的初始对应关系 
贪吃蛇向左移动时,headerIndex 指向 404,tailIndex指向 406。 
为什么设计的这么晦涩和复杂呢?因为这样设计有以下几个好处: 

贪吃蛇移动时不用处理所有节点,只要添加蛇头、删除蛇尾、重建 globalMap 和 snakeMap 的对应关系就可以; 
随机生成食物一次就可以成功,不用担心食物会占用边框或贪吃蛇的位置; 
贪吃蛇移动时,不用遍历数组就可以知道是否与自身相撞。 这些优点,如果你自己尝试过其他方案就会深有体会。

 

 

源码:

 

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <math.h>
  4 #include <conio.h>
  5 #include <time.h>
  6 #include <windows.h>
  7  
  8 //MAXWIDTH、MAXHEIGHT、INITLEN 以字符记
  9 #define MAXWIDTH (30)
 10 #define MAXHEIGHT MAXWIDTH
 11 #define INITLEN (3)  //贪吃蛇的初始长度 
 12  
 13 //程序中用到的各种字符,以及它们的颜色和类型(以数字表示)
 14 struct{
 15     char *ch;
 16     int color;
 17     char type;
 18 }
 19 charBorder = {"", 4, 1},  //边框
 20 charBg = {"", 2, 2},  //背景
 21 charSnake = {"", 0xe, 3},  //贪吃蛇节点
 22 charFood = {"", 0xc, 4};  //食物
 23  
 24 //用一个结构体数组保存地图中的各个点
 25 struct{
 26     char type;
 27     int index;
 28 }globalMap[MAXWIDTH][MAXHEIGHT];
 29  
 30 //贪吃蛇有效活动范围地图的索引
 31 struct{
 32     int x;
 33     int y;
 34 } snakeMap[ (MAXWIDTH-2)*(MAXHEIGHT-2) ], scoresPostion;
 35  
 36 int scores = 0;  //得分
 37 int snakeMapLen = (MAXWIDTH-2)*(MAXHEIGHT-2);
 38 int headerIndex, tailIndex;  //蛇头蛇尾对应的snakeMap中的索引(下标)
 39 HANDLE hStdin;  //控制台句柄
 40  
 41 // 设置光标位置,x为行,y为列
 42 void setPosition(int x, int y){
 43     COORD coord;
 44     coord.X = 2*y;
 45     coord.Y = x;
 46     SetConsoleCursorPosition(hStdin, coord);
 47 }
 48  
 49 // 设置颜色
 50 void setColor(int color){
 51     SetConsoleTextAttribute(hStdin, color);
 52 }
 53  
 54 //创建食物
 55 void createFood(){
 56     int index, rang, x, y;
 57     //产生随机数,确定 snakeMap 数组的索引 
 58     srand((unsigned)time(NULL));
 59     if(tailIndex<headerIndex){
 60         rang = headerIndex-tailIndex-1;
 61         index = rand()%rang + tailIndex + 1;
 62     }else{
 63         rang = snakeMapLen - (tailIndex - headerIndex+1);
 64         index = rand()%rang;
 65         if(index>=headerIndex){
 66             index += (tailIndex-headerIndex+1);
 67         }
 68     }
 69      
 70     x = snakeMap[index].x;
 71     y = snakeMap[index].y;
 72     setPosition(x, y);
 73     setColor(charFood.color);
 74     printf("%s", charFood.ch);
 75     globalMap[x][y].type=charFood.type;
 76 }
 77  
 78 //死掉
 79 void die(){
 80     int xCenter = MAXHEIGHT%2==0 ? MAXHEIGHT/2 : MAXHEIGHT/2+1;
 81     int yCenter = MAXWIDTH%2==0 ? MAXWIDTH/2 : MAXWIDTH/2+1;
 82  
 83     setPosition(xCenter, yCenter-5);
 84     setColor(0xC);
 85  
 86     printf("You die! Game Over!");
 87     getch();
 88     exit(0);
 89 }
 90  
 91 // 蛇移动
 92 void move(char direction){
 93     int newHeaderX, newHeaderY;  //新蛇头的坐标
 94     int newHeaderPreIndex;  //新蛇头坐标以前对应的索引
 95     int newHeaderPreX, newHeaderPreY;  //新蛇头的索引以前对应的坐标
 96     int newHeaderPreType;  //新蛇头以前的类型
 97     int oldTailX, oldTailY;  //老蛇尾坐标
 98     // -----------------------------------------------
 99     //新蛇头的坐标
100     switch(direction){
101         case 'w':
102             newHeaderX = snakeMap[headerIndex].x-1;
103             newHeaderY = snakeMap[headerIndex].y;
104             break;
105         case 's':
106             newHeaderX = snakeMap[headerIndex].x+1;
107             newHeaderY = snakeMap[headerIndex].y;
108             break;
109         case 'a':
110             newHeaderX = snakeMap[headerIndex].x;
111             newHeaderY = snakeMap[headerIndex].y-1;
112             break;
113         case 'd':
114             newHeaderX = snakeMap[headerIndex].x;
115             newHeaderY = snakeMap[headerIndex].y+1;
116             break;
117     }
118     //新蛇头的索引
119     headerIndex = headerIndex==0 ? snakeMapLen-1 : headerIndex-1;
120     // -----------------------------------------------
121     //新蛇头坐标以前对应的索引
122     newHeaderPreIndex = globalMap[newHeaderX][newHeaderY].index;
123     //新蛇头的索引以前对应的坐标
124     newHeaderPreX = snakeMap[headerIndex].x;
125     newHeaderPreY = snakeMap[headerIndex].y;
126  
127     //双向绑定新蛇头索引与坐标的对应关系
128     snakeMap[headerIndex].x = newHeaderX;
129     snakeMap[headerIndex].y = newHeaderY;
130     globalMap[newHeaderX][newHeaderY].index = headerIndex;
131  
132     //双向绑定以前的索引与坐标的对应关系
133     snakeMap[newHeaderPreIndex].x = newHeaderPreX;
134     snakeMap[newHeaderPreIndex].y = newHeaderPreY;
135     globalMap[newHeaderPreX][newHeaderPreY].index = newHeaderPreIndex;
136  
137     //新蛇头以前的类型
138     newHeaderPreType = globalMap[newHeaderX][newHeaderY].type;
139     //设置新蛇头类型
140     globalMap[newHeaderX][newHeaderY].type = charSnake.type;
141     // 判断是否出界或撞到自己
142     if(newHeaderPreType==charBorder.type || newHeaderPreType==charSnake.type ){
143         die();
144     }
145     //输出新蛇头
146     setPosition(newHeaderX, newHeaderY);
147     setColor(charSnake.color);
148     printf("%s", charSnake.ch);
149     //判断是否吃到食物
150     if(newHeaderPreType==charFood.type){  //吃到食物
151         createFood();
152         //更改分数
153         setPosition(scoresPostion.x, scoresPostion.y);
154         printf("%d", ++scores);
155     }else{
156         //老蛇尾坐标
157         oldTailX = snakeMap[tailIndex].x;
158         oldTailY = snakeMap[tailIndex].y;
159         //删除蛇尾
160         setPosition(oldTailX, oldTailY);
161         setColor(charBg.color);
162         printf("%s", charBg.ch);
163         globalMap[oldTailX][oldTailY].type = charBg.type;
164         tailIndex = (tailIndex==0) ? snakeMapLen-1 : tailIndex-1;
165     }
166 }
167  
168 //下次移动的方向
169 char nextDirection(char ch, char directionOld){
170     int sum = ch+directionOld;
171     ch = tolower(ch);
172     if( (ch=='w' || ch=='a' || ch=='s' || ch=='d') && sum!=197 && sum!=234 ){
173         return ch;
174     }else{
175         return directionOld;
176     }
177 }
178  
179 //暂停
180 char pause(){
181     return getch();
182 }
183  
184 // 初始化
185 void init(){
186     // 设置相关变量 
187     int x, y, i, index;
188     int xCenter = MAXHEIGHT%2==0 ? MAXHEIGHT/2 : MAXHEIGHT/2+1;
189     int yCenter = MAXWIDTH%2==0 ? MAXWIDTH/2 : MAXWIDTH/2+1;
190     CONSOLE_CURSOR_INFO cci;  //控制台光标信息
191  
192     //判断相关设置是否合理
193     if(MAXWIDTH<16){
194         printf("'MAXWIDTH' is too small!");
195         getch();
196         exit(0);
197     }
198  
199     //设置窗口大小 
200     system("mode con: cols=96 lines=32");
201  
202     //隐藏光标
203     hStdin = GetStdHandle(STD_OUTPUT_HANDLE);
204     GetConsoleCursorInfo(hStdin, &cci);
205     cci.bVisible = 0;
206     SetConsoleCursorInfo(hStdin, &cci);
207      
208     //打印背景 
209     for(x=0; x<MAXHEIGHT; x++){
210         for(y=0; y<MAXWIDTH; y++){
211             if(y==0 || y==MAXWIDTH-1 || x==0 || x==MAXHEIGHT-1){
212                 globalMap[x][y].type = charBorder.type;
213                 setColor(charBorder.color);
214                 printf("%s", charBorder.ch);
215             }else{
216                 index = (x-1)*(MAXWIDTH-2)+(y-1);
217                 snakeMap[index].x= x;
218                 snakeMap[index].y= y;
219                 globalMap[x][y].type = charBg.type;
220                 globalMap[x][y].index = index;
221  
222                 setColor(charBg.color);
223                 printf("%s", charBg.ch);
224             }
225         }
226         printf("\n");
227     }
228      
229     //初始化贪吃蛇
230     globalMap[xCenter][yCenter-1].type = globalMap[xCenter][yCenter].type = globalMap[xCenter][yCenter+1].type = charSnake.type;
231  
232     headerIndex = (xCenter-1)*(MAXWIDTH-2)+(yCenter-1) - 1;
233     tailIndex = headerIndex+2;
234     setPosition(xCenter, yCenter-1);
235     setColor(charSnake.color);
236     for(y = yCenter-1; y<=yCenter+1; y++){
237         printf("%s", charSnake.ch);
238     }
239     //生成食物
240     createFood();
241  
242     //设置程序信息
243     setPosition(xCenter-1, MAXWIDTH+2);
244     printf("   Apples : 0");
245     setPosition(xCenter, MAXWIDTH+2);
246     printf("   Author : xiao p");
247     setPosition(xCenter+1, MAXWIDTH+2);
248     printf("Copyright : c.biancheng.net");
249     scoresPostion.x = xCenter-1;
250     scoresPostion.y = MAXWIDTH+8;
251 }
252  
253 int main(){
254     char charInput, direction = 'a';
255     init();
256      
257     charInput = tolower(getch());
258     direction = nextDirection(charInput, direction);
259  
260     while(1){
261         if(kbhit()){
262             charInput = tolower(getch());
263             if(charInput == ' '){
264                 charInput = pause();
265             }
266             direction = nextDirection(charInput, direction);
267         }
268         move(direction);
269         Sleep(500);
270     }
271      
272     getch();
273     return 0;
274 }

 

posted @ 2016-12-22 13:25  silvercell  阅读(5813)  评论(0)    收藏  举报