zoukankan      html  css  js  c++  java
  • 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 ", map[i][j]);
    }
    printf("% ");
    }

    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("赢得游戏
    ");
     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("输了游戏
    ");
     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	", map[i][j]);
    444         }
    445         printf("
    ");
    446     }
    447     printf("
    
    ");*/
    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

  • 相关阅读:
    配置FTP服务2(vsftpd 配置虚拟账号)
    配置FTP服务(pure-ftpd安装配置测试)
    asp.net学习——Repeater控件
    C#方法,属性,和事件
    (转)AspNetPager使用方法
    WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping
    SQL Server 操作数据
    SQL Server——增、删、改、查。
    数组——求和、平均分、最值
    数组——抽奖&&句子组合
  • 原文地址:https://www.cnblogs.com/Yuuki-/p/10647112.html
Copyright © 2011-2022 走看看