C++学习(三十九)(C语言部分)之 游戏项目(2048游戏)

 /***************************项目 2048**********************
c语言编写 图形库制作
时间:2019.04.03

准备工具: vs2013 图形库 ico素材(作为exe的图标) 背景图(jpg格式)
知识点: 循环 数组 函数 随机数

项目步骤分析:
2048 通过方向键 WASD控制方向合并相同数字直到生成2048 游戏结束
1、4X4的棋盘 存放数字 ---->数组 游戏操作 ---->键盘操作 界面 ---->需要操作结果


2、步骤
a、准备数组 生成两个隋杰的位置和随机的数字(2或4) //初始化操作
b、等待用户输入 根据上下左右处理数组
//添加一个判断输赢e
c、生成新的随机位置 和新的数字(2或4)
d、打印界面 当前输出结果 等待下一轮输入
e、输赢条件 输-->数组满,不能移动 赢-->出现2048,游戏就赢了


3、拆分函数 如果同一个功能或者相似的功能可以写成一个函数,减少代码量和难点
a 初始化

函数声明:

void init(int map[][4]); 

函数定义:

void init(int map[][4])
{

rand();
int x, y;
for (int i = 0; i < 2;)
{
x = rand() % 4; 
y = rand() % 4;
if (map[x][y] == 0) 
{
map[x][y] = rand()%2*2+2; 
}
}
}

在主函数中测试效果:

int main()
{
int map[4][4];
init(map); 
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
printf("%d\t", map[i][j]);
}
printf("%\n");
}

getchar();
return 0;
}

效果如图所示:

b 等待用户输入操作 随机位置

用switch进行键盘消息判断

各方向键的代码比对如下:

 

  

 

也可以用枚举法定义方向键并对其进行消息的判断

 

对比如下:

   

  

 

c 判断输赢


d 打印结果 显示地图

打印地图的函数:

 

暂时测试结果如下:

 


目的:
复习 总结 综合运用
***************************项目 2048**********************/

测试代码笔记如下:

  1 //**************************头文件部分*********************
  2 //头文件 宏定义 类型定义 全局变量
  3 
  4 //头文件
  5 #include<stdio.h>
  6 #include<memory.h>  //memset的头文件 或者string.h
  7 #include<stdlib.h>  //srand函数  rand函数
  8 #include<time.h>  //time函数
  9 #include<conio.h>  //getch函数
 10 #include<graphics.h>  //图形库
 11 //#define COLOR1 RGB(238,106,80)
 12 //#define COLOR2 RGB(238,169,184)
 13 //#define COLOR3 RGB(131,139,131)
 14 //#define COLOR4 RGB(193,205,205)
 15 
 16 //类型定义 枚举
 17 enum dir
 18 {
 19     UP = 72, DOWN = 80, LEFT = 75, RIGHT = 77
 20 };  //定义方向键
 21 
 22 IMAGE img;//存放图片变量
 23 
 24 //**************************函数声明***********************
 25 //写个函数的声明
 26 void init(int map[][4]);  //用函数给数组赋值初始化
 27 
 28 void play(int map[][4]);  //处理用户输入
 29 
 30 void drawMap(int map[][4]);  //打印 画图
 31 
 32 void newNum(int map[][4]);  //随机一个新元素
 33 
 34 int isGameOver(int map[][4]);//判断游戏是否结束的一个函数
 35 
 36 //**************************主函数*************************
 37 //main函数
 38 int main()
 39 {
 40     int map[4][4];  //准备数组 大小4X4的地图
 41     init(map);  //传参(实参) 传的是数组名
 42     
 43     while (1)  //死循环
 44     {
 45         drawMap(map);
 46         play(map);
 47         //system("cls");  //清屏函数 用于控制台
 48         //newNum(map);
 49         if (isGameOver(map) != 0)  break;//结束循环
 50     }
 51 
 52     if (isGameOver(map) == 1)
 53     {
 54         //赢得游戏
 55         drawMap(map);
 56         //printf("赢得游戏\n");
 57         MessageBox(GetHWnd(), L"OK", L"WIN", MB_OK);
 58         //第一个 是窗口句柄  表示显示在这个窗口前面   可以写NULL 或者0
 59         //第二个参数 是文本框内容
 60         //第三次参数 窗口标题
 61         //最后一个参数是按钮形式
 62         
 63     }
 64     else
 65     {
 66         //输了游戏
 67         drawMap(map);
 68         //printf("输了游戏\n");
 69         MessageBox(GetHWnd(), L"OOPS", L"LOSE", MB_OK);
 70     }
 71 
 72     //getchar();
 73     closegraph();//关闭窗口
 74     return 0;
 75 }
 76 
 77 //**************************函数定义************************
 78 //上述函数的定义放在这里
 79 
 80 //给数组赋值 初始化
 81 void init(int map[][4])  //int (*map[4])(形参)  是数组指针 两种形式都可以
 82 {
 83     //给数组赋值 第一种方式用循环
 84     //for (int i = 0; i < 4; ++i)  //数组用 下标进行循环
 85     //{
 86     //    for (int j = 0; j < 4; ++j)
 87     //    {
 88     //        map[i][j] = 0;  //刚开始全部赋值为0
 89     //    }
 90     //}
 91 
 92     //第二种方式用函数 memset 初始化一块内存  头文件是stdlib,h
 93     memset(map, 0, sizeof(int)* 4 * 4); //三个参数 内存首地址 初值 字节大小
 94     //随机两个2和4  null用于指针 随机数 借用两个函数srand  rand --->头文件stlib.h  还需要一个函数 time(时间函数)--->头文件time.h
 95     srand((unsigned)time(NULL));  //设置随机数种子  不要写在循环里面 设置一次就够了
 96     //得到某个范围内的随机数 用求余控制范围  0-->100之间 int x=rand()%101; 66-->100之间 -->66+(0-->43)  rand()%35+66
 97     //得到随机位置
 98     int x, y;
 99     for (int i = 0; i < 2;)
100     {
101         x = rand() % 4;  //下标3至3  所以对4求余
102         y = rand() % 4;
103         if (map[x][y] == 0)  //这个位置没有元素
104         {
105             map[x][y] = rand()%2*2+2; //2或4 2=1*2   4=2*2
106             i++;  //赋值之后才算一次
107         }
108         //++i放上面 只能确保循环两次 不能确保赋值两次 所以放下面
109     }
110 }
111 
112 //处理用户输入
113 void play(int map[][4])
114 {
115     //方式 键盘操作
116     //getch  读取键盘中的一个字符 conio.h
117     //kbhit  conio.h  检测当前是否有键盘消息
118     //dir di;  //枚举变量
119     switch (getch())  //判断键盘消息
120     {
121     case 'w':
122     case 'W'://往上
123         for (int j = 0; j < 4; ++j)  //四列
124         {
125             for (int i = 0; i < 3; ++i)  //判断前三个能否和后面的合并
126             {
127                 if (map[i][j] == 0) //判断是否为0
128                 {
129                     //到后面找一个不是0的元素 换过来
130                     int k = i + 1;
131                     for (; k < 4; ++k)
132                     {
133                         if (map[k][j] != 0)
134                         {
135                             map[i][j] = map[k][j];
136                             map[k][j] = 0;
137                             break;
138                         }
139                     }
140                     if (k == 4)break;  //表明这一行全是0
141                 }
142                 //判断map[i][j]和后面的元素是否可以合并
143                 for (int k = i + 1; k < 4; ++k)  //判断后面的元素
144                 {
145                     if (map[k][j] == 0) continue;
146                     else if (map[i][j] == map[k][j])
147                     {
148                         //相同 合并
149                         map[i][j] *= 2;  //合并
150                         map[k][j] = 0;
151                         break;
152                     }
153                     else break;  //不相等 往后找 退出
154                 }
155             }
156         }
157         newNum(map);
158         break;
159     case 's':
160     case 'S'://往下
161         for (int j = 0; j < 4; ++j)//四列
162         {
163             //每一行进行合并
164             for (int i = 3; i >0; --i)//判断前三个能否和后面的进行合并
165             {
166                 if (map[i][j] == 0)//判断map[i][j]是否为0
167                 {
168                     //到后面找一个不是0的元素 换到这个位置
169                     int k = i - 1;
170                     for (; k >= 0; --k)
171                     {
172                         if (map[k][j] != 0)
173                         {
174                             map[i][j] = map[k][j];
175                             map[k][j] = 0;
176                             break;
177                         }
178                     }
179                     if (k == 0)break;//表明这一行全是0  直接找下一行
180                 }
181                 //判断map[i][j] 和后面的元素能否合并
182                 for (int k = i - 1; k >= 0; --k)//判断后面的几个元素
183                 {
184                     if (map[k][j] == 0) continue;
185                     else if (map[i][j] == map[k][j])
186                     {
187                         map[i][j] *= 2;//合并
188                         map[k][j] = 0;
189                         break;
190                     }
191                     else  break;//不相等 往后找 退出
192                 }
193             }
194         }
195         newNum(map);
196         break;
197     case 'a':  
198     case 'A'://往左
199         for (int i = 0; i < 4; ++i)  //四行
200         {
201             //每一行进行合并 从左往右 1、判断第一个元素是不是0,如果是0,就到右边找到第一个不是0的元素,放到为0的位置上 不是0,就进行下一步,(没有找到 说明全是0,那么就直接下一行)
202             //2、找到剩下的位置中不是0的元素 如果和这个位置的相同的话合并到下一个位置
203             for (int j = 0; j < 3; ++j)  //判断前三个能否和后面的合并
204             {
205                 if (map[i][j] == 0) //判断是否为0
206                 {
207                     //到后面找一个不是0的元素 换过来
208                     int k = j + 1;
209                     for (; k < 4; ++k)
210                     {
211                         if (map[i][k] != 0)
212                         {
213                             map[i][j] = map[i][k];
214                             map[i][k] = 0;
215                             break;
216                         }
217                     }
218                     if (k == 4)break;  //表明这一行全是0
219                 }
220                 //判断map[i][j]和后面的元素是否可以合并
221                 for (int k = j + 1; k < 4; ++k)  //判断后面的元素
222                 {
223                     if (map[i][k] == 0) continue;  
224                     else if (map[i][j] == map[i][k])
225                     {
226                         //相同 合并
227                         map[i][j] *= 2;  //合并
228                         map[i][k] = 0;
229                         break;
230                     }
231                     else break;  //不相等 往后找 退出
232                 }
233             }
234         }
235         newNum(map);
236         break;
237     case 'd':  
238     case 'D'://往右
239         for (int i = 0; i < 4; ++i) //四行
240         {
241             for (int j = 3; j >0; --j)  //判断前三个能否和后面的合并
242             {
243                 if (map[i][j] == 0) //判断是否为0
244                 {
245                     //到后面找一个不是0的元素 换过来
246                     int k = j - 1;
247                     for (; k >=0; --k)
248                     {
249                         if (map[i][k] != 0)
250                         {
251                             map[i][j] = map[i][k];
252                             map[i][k] = 0;
253                             break;
254                         }
255                     }
256                     if (k == -1)break;  //表明这一行全是0
257                 }
258                 //判断map[i][j]和后面的元素是否可以合并
259                 for (int k = j - 1; k >=0; --k)  //判断后面的元素
260                 {
261                     if (map[i][k] == 0) continue;
262                     else if (map[i][j] == map[i][k])
263                     {
264                         //相同 合并
265                         map[i][j] *= 2;  //合并
266                         map[i][k] = 0;
267                         break;
268                     }
269                     else break;  //不相等 往后找 退出
270                 }
271             }
272         }
273         newNum(map);
274         break;
275     case 224:  //表示使用方向键  方向键是组合件 用getch视为两个部分
276         switch (getch())
277         {
278         case UP:
279             for (int j = 0; j < 4; ++j)//四列
280             {
281                 //每一行进行合并
282                 for (int i = 0; i < 3; ++i)//判断前三个能否和后面的进行合并
283                 {
284                     if (map[i][j] == 0)//判断map[i][j]是否为0
285                     {
286                         //到后面找一个不是0的元素 换到这个位置
287                         int k = i + 1;
288                         for (; k<4; ++k)
289                         {
290                             if (map[k][j] != 0)
291                             {
292                                 map[i][j] = map[k][j];
293                                 map[k][j] = 0;
294                                 break;
295                             }
296                         }
297                         if (k == 4)break;//表明这一行全是0  直接找下一行
298                     }
299                     //判断map[i][j] 和后面的元素能否合并
300                     for (int k = i + 1; k < 4; ++k)//判断后面的几个元素
301                     {
302                         if (map[k][j] == 0) continue;
303                         else if (map[i][j] == map[k][j])
304                         {
305                             map[i][j] *= 2;//合并
306                             map[k][j] = 0;
307                             break;
308                         }
309                         else  break;//不相等 往后找 退出
310                     }
311                 }
312             }
313             newNum(map);
314             break;
315         case DOWN:
316             for (int j = 0; j < 4; ++j)//四列
317             {
318                 //每一行进行合并
319                 for (int i = 3; i >0; --i)//判断前三个能否和后面的进行合并
320                 {
321                     if (map[i][j] == 0)//判断map[i][j]是否为0
322                     {
323                         //到后面找一个不是0的元素 换到这个位置
324                         int k = i - 1;
325                         for (; k >= 0; --k)
326                         {
327                             if (map[k][j] != 0)
328                             {
329                                 map[i][j] = map[k][j];
330                                 map[k][j] = 0;
331                                 break;
332                             }
333                         }
334                         if (k == 0)break;//表明这一行全是0  直接找下一行
335                     }
336                     //判断map[i][j] 和后面的元素能否合并
337                     for (int k = i - 1; k >= 0; --k)//判断后面的几个元素
338                     {
339                         if (map[k][j] == 0) continue;
340                         else if (map[i][j] == map[k][j])
341                         {
342                             map[i][j] *= 2;//合并
343                             map[k][j] = 0;
344                             break;
345                         }
346                         else  break;//不相等 往后找 退出
347                     }
348                 }
349             }
350             newNum(map);
351             break;
352         case LEFT:
353             for (int i = 0; i < 4; ++i)//四行
354             {
355                 //每一行进行合并
356                 for (int j = 0; j < 3; ++j)//判断前三个能否和后面的进行合并
357                 {
358                     if (map[i][j] == 0)//判断map[i][j]是否为0
359                     {
360                         //到后面找一个不是0的元素 换到这个位置
361                         int k = j + 1;
362                         for (; k<4; ++k)
363                         {
364                             if (map[i][k] != 0)
365                             {
366                                 map[i][j] = map[i][k];
367                                 map[i][k] = 0;
368                                 break;
369                             }
370                         }
371                         if (k == 4)break;//表明这一行全是0  直接找下一行
372                     }
373                     //判断map[i][j] 和后面的元素能否合并
374                     for (int k = j + 1; k < 4; ++k)//判断后面的几个元素
375                     {
376                         if (map[i][k] == 0) continue;
377                         else if (map[i][j] == map[i][k])
378                         {
379                             map[i][j] *= 2;//合并
380                             map[i][k] = 0;
381                             break;
382                         }
383                         else  break;//不相等 往后找 退出
384                     }
385                 }
386             }
387             newNum(map);
388             break;
389         case RIGHT:
390             for (int i = 0; i < 4; ++i)//四行
391             {
392                 //每一行进行合并
393                 for (int j = 3; j >0; --j)//判断前三个能否和后面的进行合并
394                 {
395                     if (map[i][j] == 0)//判断map[i][j]是否为0
396                     {
397                         //到后面找一个不是0的元素 换到这个位置
398                         int k = j - 1;
399                         for (; k >= 0; --k)
400                         {
401                             if (map[i][k] != 0)
402                             {
403                                 map[i][j] = map[i][k];
404                                 map[i][k] = 0;
405                                 break;
406                             }
407                         }
408                         if (k == -1)break;//表明这一行全是0  直接找下一行
409                     }
410                     //判断map[i][j] 和后面的元素能否合并
411                     for (int k = j - 1; k >= 0; --k)//判断后面的几个元素
412                     {
413                         if (map[i][k] == 0) continue;
414                         else if (map[i][j] == map[i][k])
415                         {
416                             map[i][j] *= 2;//合并
417                             map[i][k] = 0;
418                             break;
419                         }
420                         else  break;//不相等 往后找 退出
421                     }
422                 }
423             }
424             newNum(map);
425             break;
426         default:
427             break;
428         }
429         break;
430     default:
431         break;
432     }
433 
434 }
435 
436 //打印  贴图
437 void drawMap(int map[][4])
438 {
439     /*for (int i = 0; i < 4; ++i)
440     {
441         for (int j = 0; j < 4; ++j)
442         {
443             printf("%d\t", map[i][j]);
444         }
445         printf("\n");
446     }
447     printf("\n\n");*/
448 
449     //cleardevice();//图形库清除屏幕内容  //如果加上 有闪屏的问题
450     putimage(0, 0, &img);//贴背景图
451     char arr[10];//准备字符串
452     for (int i = 0; i < 4; ++i)
453     {
454         for (int j = 0; j < 4; ++j)
455         {
456             //加数字进来
457             //outtextxy根据坐标输出字符串
458             //itoa 将int转换成字符串
459             /*if (map[i][j] != 0)
460             {
461             sprintf(arr,"%d",map[i][j]);
462             outtextxy(160 * j+20, 160 * i+20, arr);
463             }*/
464 
465             //有素材  switch 根据不同数字进行贴图
466             //通过数字确定背景
467             //238 106 80
468             //238 169 184
469             //131 139 131
470             //193 205 205
471 
472             /*switch (map[i][j])
473             {
474             case 0:
475             case 32:
476             setfillcolor(COLOR1);
477             fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
478             break;
479             case 2:
480             case 64:
481             setfillcolor(COLOR2);
482             fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
483             break;
484             case 4:
485             case 128:
486             setfillcolor(COLOR3);
487             fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
488             break;
489             case 8:
490             case 256:
491             setfillcolor(COLOR4);
492             fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
493             break;
494             }*/
495 
496             sprintf(arr, "%d", map[i][j]);
497             outtextxy(160 * j + 20, 160 * i + 20, arr[10]);
498             //根据数字 确定不同的背景图  然后在背景图上 写数字
499             //if ()
500             //2^1  2^2  2^3 2^4
501             //2^5  2^6
502 
503             //debug 调试版本  比较慢
504             //release 发行版本   -->主要发给别人用的
505         }
506     }
507 }
508 
509 //随机一个新元素
510 void newNum(int map[][4])
511 {
512     //判断是不是满
513     int empty = 0;
514     for (int i = 0; i < 4; ++i)
515     {
516         for (int j = 0; j < 4; ++j)
517         {
518             if (map[i][j] == 0) empty = 1;
519         }
520         if (empty == 1) break;//如果找到这个空的位置  不需要继续循环
521     }
522     if (empty == 0) return;
523 
524     int x, y;
525     do
526     {
527         x = rand() % 4;
528         y = rand() % 4;
529     } while (map[x][y] != 0);
530     map[x][y] = rand() % 2 * 2 + 2;  //随机2和4
531     //如果地图满的话 我们不能随机元素 所以最后 加上一个判断地图满的函数
532 }
533 
534 //判断游戏是否结束
535 int isGameOver(int map[][4])
536 {
537     //赢  返回1    输 -1  还没赢 还没输 返回0
538     //游戏输赢条件
539     //出现2048  游戏赢
540     //如果游戏不能走动  游戏输掉
541     int empty = 0;//如果判断的时候 map[i][j]==0  empty置为1  
542     //如果有相邻元素 并且相同的话  也将empty置为1
543     for (int i = 0; i < 4; ++i)
544     {
545         for (int j = 0; j < 4; ++j)
546         {
547             if (map[i][j] >= 2048)//赢的条件
548             {
549                 //赢得游戏
550                 return 1;
551             }
552         }
553     }
554     //条件1  数字全满 并且 相邻没有同样的数字
555     for (int i = 0; i < 4; ++i)
556     {
557         for (int j = 0; j < 4; ++j)
558         {
559             if (map[i][j] == 0)  empty = 1;
560             if (i + 1<4 && map[i][j] == map[i + 1][j]) empty = 1;
561             if (j + 1<4 && map[i][j] == map[i][j + 1]) empty = 1;
562         }
563     }
564     if (empty == 1)
565     {
566         //游戏还没有结束
567         return 0;
568     }
569     else
570     {
571         //游戏结束 //
572         return -1;
573     }
574 }

结果展示:

注:代码部分仅供学习参考,完全复制下来不一定能够实现

2019-04-03  12:04:42

猜你喜欢

转载自www.cnblogs.com/Yuuki-/p/10647112.html