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