一、首先放上全部源码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <windows.h> 3 #include <conio.h> 4 #include <time.h> 5 //游戏窗口 6 #define FrameX 4 //游戏窗口左上角的X轴坐标 7 #define FrameY 4 //游戏窗口左上角的Y轴坐标 8 #define Frame_height 20 //游戏窗口的高度 9 #define Frame_width 18 //游戏窗口的宽度 10 //定义全局变量 11 int i,j,temp,temp1,temp2; //temp,temp1,temp2用于记住和转换方块变量的值 12 int a[80][80]={0}; //标记游戏屏幕的图案:2,1,0分别表示该位置为游戏边框、方块、无图案;初始化为无图案 13 int b[4]; //标记4个"口"方块:1表示有方块,0表示无方块 14 15 //声明俄罗斯方块的结构体 16 struct Tetris 17 { 18 int x; //中心方块的x轴坐标 19 int y; //中心方块的y轴坐标 20 int flag; //标记方块类型的序号 21 int next; //下一个俄罗斯方块类型的序号 22 int speed; //俄罗斯方块移动的速度 23 int count; //产生俄罗斯方块的个数 24 int score; //游戏的分数 25 int level; //游戏的等级 26 }; 27 //函数原型声明 28 //光标移到指定位置 29 void gotoxy(HANDLE hOut, int x, int y); 30 //制作游戏窗口 31 void make_frame(); 32 //随机产生方块类型的序号 33 void get_flag(struct Tetris *); 34 //制作俄罗斯方块 35 void make_tetris(struct Tetris *); 36 //打印俄罗斯方块 37 void print_tetris(HANDLE hOut,struct Tetris *); 38 //清除俄罗斯方块的痕迹 39 void clear_tetris(HANDLE hOut,struct Tetris *); 40 //判断是否能移动,返回值为1,能移动,否则,不动 41 int if_moveable(struct Tetris *); 42 //判断是否满行,并删除满行的俄罗斯方块 43 void del_full(HANDLE hOut,struct Tetris *); 44 //开始游戏 45 void start_game(); 46 47 int main() 48 { 49 //制作游戏窗口 50 make_frame(); 51 //开始游戏 52 start_game(); 53 } 54 /******光标移到指定位置**************************************************************/ 55 void gotoxy(HANDLE hOut, int x, int y) 56 { 57 COORD pos; 58 pos.X = x; //横坐标 59 pos.Y = y; //纵坐标 60 SetConsoleCursorPosition(hOut, pos); 61 } 62 /******制作游戏窗口******************************************************************/ 63 void make_frame() 64 { 65 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //定义显示器句柄变量 66 gotoxy(hOut,FrameX+Frame_width-5,FrameY-2); //打印游戏名称 67 printf("俄罗斯方块"); 68 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+7); //打印选择菜单 69 printf("**********下一个方块:"); 70 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+13); 71 printf("**********"); 72 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+17); 73 printf("↑键:变体"); 74 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+19); 75 printf("空格:暂停游戏"); 76 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+15); 77 printf("Esc :退出游戏"); 78 gotoxy(hOut,FrameX,FrameY); //打印框角并记住该处已有图案 79 printf("╔"); 80 gotoxy(hOut,FrameX+2*Frame_width-2,FrameY); 81 printf("╗"); 82 gotoxy(hOut,FrameX,FrameY+Frame_height); 83 printf("╚"); 84 gotoxy(hOut,FrameX+2*Frame_width-2,FrameY+Frame_height); 85 printf("╝"); 86 a[FrameX][FrameY+Frame_height]=2; 87 a[FrameX+2*Frame_width-2][FrameY+Frame_height]=2; 88 for(i=2;i<2*Frame_width-2;i+=2) 89 { 90 gotoxy(hOut,FrameX+i,FrameY); 91 printf("═"); //打印上横框 92 } 93 for(i=2;i<2*Frame_width-2;i+=2) 94 { 95 gotoxy(hOut,FrameX+i,FrameY+Frame_height); 96 printf("═"); //打印下横框 97 a[FrameX+i][FrameY+Frame_height]=2; //记住下横框有图案 98 } 99 for(i=1;i<Frame_height;i++) 100 { 101 gotoxy(hOut,FrameX,FrameY+i); 102 printf("║"); //打印左竖框 103 a[FrameX][FrameY+i]=2; //记住左竖框有图案 104 } 105 for(i=1;i<Frame_height;i++) 106 { 107 gotoxy(hOut,FrameX+2*Frame_width-2,FrameY+i); 108 printf("║"); //打印右竖框 109 a[FrameX+2*Frame_width-2][FrameY+i]=2; //记住右竖框有图案 110 } 111 } 112 /******制作俄罗斯方块********************************************************************/ 113 void make_tetris(struct Tetris *tetris) 114 { 115 a[tetris->x][tetris->y]=b[0]; //中心方块位置的图形状态:1-有,0-无 116 switch(tetris->flag) //共6大类,19种类型 117 { 118 case 1: //田字方块 119 { 120 a[tetris->x][tetris->y-1]=b[1]; 121 a[tetris->x+2][tetris->y-1]=b[2]; 122 a[tetris->x+2][tetris->y]=b[3]; 123 break; 124 } 125 case 2: //直线方块:---- 126 { 127 a[tetris->x-2][tetris->y]=b[1]; 128 a[tetris->x+2][tetris->y]=b[2]; 129 a[tetris->x+4][tetris->y]=b[3]; 130 break; 131 } 132 case 3: //直线方块: | 133 { 134 a[tetris->x][tetris->y-1]=b[1]; 135 a[tetris->x][tetris->y-2]=b[2]; 136 a[tetris->x][tetris->y+1]=b[3]; 137 break; 138 } 139 case 4: //T字方块 140 { 141 a[tetris->x-2][tetris->y]=b[1]; 142 a[tetris->x+2][tetris->y]=b[2]; 143 a[tetris->x][tetris->y+1]=b[3]; 144 break; 145 } 146 case 5: //T字顺时针转90度方块 147 { 148 a[tetris->x][tetris->y-1]=b[1]; 149 a[tetris->x][tetris->y+1]=b[2]; 150 a[tetris->x-2][tetris->y]=b[3]; 151 break; 152 } 153 case 6: //T字顺时针转180度方块 154 { 155 a[tetris->x][tetris->y-1]=b[1]; 156 a[tetris->x-2][tetris->y]=b[2]; 157 a[tetris->x+2][tetris->y]=b[3]; 158 break; 159 } 160 case 7: //T字顺时针转270度方块 161 { 162 a[tetris->x][tetris->y-1]=b[1]; 163 a[tetris->x][tetris->y+1]=b[2]; 164 a[tetris->x+2][tetris->y]=b[3]; 165 break; 166 } 167 case 8: //Z字方块 168 { 169 a[tetris->x][tetris->y+1]=b[1]; 170 a[tetris->x-2][tetris->y]=b[2]; 171 a[tetris->x+2][tetris->y+1]=b[3]; 172 break; 173 } 174 case 9: //Z字顺时针转90度方块 175 { 176 a[tetris->x][tetris->y-1]=b[1]; 177 a[tetris->x-2][tetris->y]=b[2]; 178 a[tetris->x-2][tetris->y+1]=b[3]; 179 break; 180 } 181 case 10: //Z字顺时针转180度方块 182 { 183 a[tetris->x][tetris->y-1]=b[1]; 184 a[tetris->x-2][tetris->y-1]=b[2]; 185 a[tetris->x+2][tetris->y]=b[3]; 186 break; 187 } 188 case 11: //Z字顺时针转270度方块 189 { 190 a[tetris->x][tetris->y+1]=b[1]; 191 a[tetris->x+2][tetris->y-1]=b[2]; 192 a[tetris->x+2][tetris->y]=b[3]; 193 break; 194 } 195 case 12: //7字方块 196 { 197 a[tetris->x][tetris->y-1]=b[1]; 198 a[tetris->x][tetris->y+1]=b[2]; 199 a[tetris->x-2][tetris->y-1]=b[3]; 200 break; 201 } 202 case 13: //7字顺时针转90度方块 203 { 204 a[tetris->x-2][tetris->y]=b[1]; 205 a[tetris->x-2][tetris->y+1]=b[2]; 206 a[tetris->x+2][tetris->y]=b[3]; 207 break; 208 } 209 case 14: //7字顺时针转180度方块 210 { 211 a[tetris->x][tetris->y-1]=b[1]; 212 a[tetris->x][tetris->y+1]=b[2]; 213 a[tetris->x+2][tetris->y+1]=b[3]; 214 break; 215 } 216 case 15: //7字顺时针转270度方块 217 { 218 a[tetris->x-2][tetris->y]=b[1]; 219 a[tetris->x+2][tetris->y-1]=b[2]; 220 a[tetris->x+2][tetris->y]=b[3]; 221 break; 222 } 223 case 16: //倒7字方块 224 { 225 a[tetris->x][tetris->y+1]=b[1]; 226 a[tetris->x][tetris->y-1]=b[2]; 227 a[tetris->x+2][tetris->y-1]=b[3]; 228 break; 229 } 230 case 17: //倒7字顺指针转90度方块 231 { 232 a[tetris->x-2][tetris->y]=b[1]; 233 a[tetris->x-2][tetris->y-1]=b[2]; 234 a[tetris->x+2][tetris->y]=b[3]; 235 break; 236 } 237 case 18: //倒7字顺时针转180度方块 238 { 239 a[tetris->x][tetris->y-1]=b[1]; 240 a[tetris->x][tetris->y+1]=b[2]; 241 a[tetris->x-2][tetris->y+1]=b[3]; 242 break; 243 } 244 case 19: //倒7字顺时针转270度方块 245 { 246 a[tetris->x-2][tetris->y]=b[1]; 247 a[tetris->x+2][tetris->y+1]=b[2]; 248 a[tetris->x+2][tetris->y]=b[3]; 249 break; 250 } 251 } 252 } 253 //******判断是否可动*************************************************************************/ 254 int if_moveable(struct Tetris *tetris) 255 { 256 if(a[tetris->x][tetris->y]!=0)//当中心方块位置上有图案时,返回值为0,即不可移动 257 { 258 return 0; 259 } 260 else 261 { 262 if( //当为田字方块且除中心方块位置外,其他"口"字方块位置上无图案时,返回值为1,即可移动 263 ( tetris->flag==1 && ( a[tetris->x][tetris->y-1]==0 && 264 a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 265 //或为直线方块且除中心方块位置外,其他"口"字方块位置上无图案时,返回值为1,即可移动 266 ( tetris->flag==2 && ( a[tetris->x-2][tetris->y]==0 && 267 a[tetris->x+2][tetris->y]==0 && a[tetris->x+4][tetris->y]==0 ) ) || 268 ( tetris->flag==3 && ( a[tetris->x][tetris->y-1]==0 && 269 a[tetris->x][tetris->y-2]==0 && a[tetris->x][tetris->y+1]==0 ) ) || 270 ( tetris->flag==4 && ( a[tetris->x-2][tetris->y]==0 && 271 a[tetris->x+2][tetris->y]==0 && a[tetris->x][tetris->y+1]==0 ) ) || 272 ( tetris->flag==5 && ( a[tetris->x][tetris->y-1]==0 && 273 a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y]==0 ) ) || 274 ( tetris->flag==6 && ( a[tetris->x][tetris->y-1]==0 && 275 a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 276 ( tetris->flag==7 && ( a[tetris->x][tetris->y-1]==0 && 277 a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 278 ( tetris->flag==8 && ( a[tetris->x][tetris->y+1]==0 && 279 a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) || 280 ( tetris->flag==9 && ( a[tetris->x][tetris->y-1]==0 && 281 a[tetris->x-2][tetris->y]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) || 282 ( tetris->flag==10 && ( a[tetris->x][tetris->y-1]==0 && 283 a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 284 ( tetris->flag==11 && ( a[tetris->x][tetris->y+1]==0 && 285 a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 286 ( tetris->flag==12 && ( a[tetris->x][tetris->y-1]==0 && 287 a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y-1]==0 ) ) || 288 ( tetris->flag==13 && ( a[tetris->x-2][tetris->y]==0 && 289 a[tetris->x-2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 290 ( tetris->flag==14 && ( a[tetris->x][tetris->y-1]==0 && 291 a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) || 292 ( tetris->flag==15 && ( a[tetris->x-2][tetris->y]==0 && 293 a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 294 ( tetris->flag==16 && ( a[tetris->x][tetris->y+1]==0 && 295 a[tetris->x][tetris->y-1]==0 && a[tetris->x+2][tetris->y-1]==0 ) ) || 296 ( tetris->flag==17 && ( a[tetris->x-2][tetris->y]==0 && 297 a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) || 298 ( tetris->flag==18 && ( a[tetris->x][tetris->y-1]==0 && 299 a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) || 300 ( tetris->flag==19 && ( a[tetris->x-2][tetris->y]==0 && 301 a[tetris->x+2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ) 302 { 303 return 1; 304 } 305 } 306 return 0; 307 } 308 /******随机产生俄罗斯方块类型的序号**********************************************************/ 309 void get_flag(struct Tetris *tetris) 310 { 311 tetris->count++; //记住产生方块的个数 312 srand((unsigned)time(NULL)); //初始化随机数 313 if(tetris->count==1) 314 { 315 tetris->flag = rand()%19+1; //记住第一个方块的序号 316 } 317 tetris->next = rand()%19+1; //记住下一个方块的序号 318 } 319 /******打印俄罗斯方块**********************************************************************/ 320 void print_tetris(HANDLE hOut,struct Tetris *tetris) 321 { 322 for(i=0;i<4;i++) 323 { 324 b[i]=1; //数组b[4]的每个元素的值都为1 325 } 326 make_tetris(tetris); //制作俄罗斯方块 327 for( i=tetris->x-2; i<=tetris->x+4; i+=2 ) 328 { 329 for(j=tetris->y-2;j<=tetris->y+1;j++) 330 { 331 if( a[i][j]==1 && j>FrameY ) 332 { 333 gotoxy(hOut,i,j); 334 printf("□"); //打印边框内的方块 335 } 336 } 337 } 338 //打印菜单信息 339 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+1); 340 printf("level : %d",tetris->level); 341 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+3); 342 printf("score : %d",tetris->score); 343 gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+5); 344 printf("speed : %dms",tetris->speed); 345 } 346 /******清除俄罗斯方块的痕迹****************************************************************/ 347 void clear_tetris(HANDLE hOut,struct Tetris *tetris) 348 { 349 for(i=0;i<4;i++) 350 { 351 b[i]=0; //数组b[4]的每个元素的值都为0 352 } 353 make_tetris(tetris); //制作俄罗斯方块 354 for( i=tetris->x-2; i<=tetris->x+4; i+=2 ) 355 { 356 for(j=tetris->y-2;j<=tetris->y+1;j++) 357 { 358 if( a[i][j]==0 && j>FrameY ) 359 { 360 gotoxy(hOut,i,j); 361 printf(" "); //清除方块 362 } 363 } 364 } 365 } 366 /******判断是否满行并删除满行的俄罗斯方块****************************************************/ 367 void del_full(HANDLE hOut,struct Tetris *tetris) 368 { //当某行有Frame_width-2个方块时,则满行 369 int k,del_count=0; //分别用于记录某行方块的个数和删除方块的行数的变量 370 for(j=FrameY+Frame_height-1;j>=FrameY+1;j--) 371 { 372 k=0; 373 for(i=FrameX+2;i<FrameX+2*Frame_width-2;i+=2) 374 { 375 if(a[i][j]==1) //竖坐标依次从下往上,横坐标依次由左至右判断是否满行 376 { 377 k++; //记录此行方块的个数 378 if(k==Frame_width-2) 379 { 380 for(k=FrameX+2;k<FrameX+2*Frame_width-2;k+=2) 381 { //删除满行的方块 382 a[k][j]=0; 383 gotoxy(hOut,k,j); 384 printf(" "); 385 Sleep(1); 386 } 387 for(k=j-1;k>FrameY;k--) 388 { //如果删除行以上的位置有方块,则先清除,再将方块下移一个位置 389 for(i=FrameX+2;i<FrameX+2*Frame_width-2;i+=2) 390 { 391 if(a[i][k]==1) 392 { 393 a[i][k]=0; 394 gotoxy(hOut,i,k); 395 printf(" "); 396 a[i][k+1]=1; 397 gotoxy(hOut,i,k+1); 398 printf("□"); 399 } 400 } 401 } 402 j++; //方块下移后,重新判断删除行是否满行 403 del_count++; //记录删除方块的行数 404 } 405 } 406 } 407 } 408 tetris->score+=100*del_count; //每删除一行,得100分 409 if( del_count>0 && ( tetris->score%1000==0 || tetris->score/1000>tetris->level-1 ) ) 410 { //如果得1000分即累计删除10行,速度加快20ms并升一级 411 tetris->speed-=20; 412 tetris->level++; 413 } 414 } 415 /******开始游戏******************************************************************************/ 416 void start_game() 417 { 418 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //定义显示器句柄变量 419 struct Tetris t,*tetris=&t; //定义结构体的指针并指向结构体变量 420 unsigned char ch; //定义接收键盘输入的变量 421 tetris->count=0; //初始化俄罗斯方块数为0个 422 tetris->speed=300; //初始移动速度为300ms 423 tetris->score=0; //初始游戏的分数为0分 424 tetris->level=1; //初始游戏为第1关 425 while(1) 426 {//循环产生方块,直至游戏结束 427 get_flag(tetris); //得到产生俄罗斯方块类型的序号 428 temp=tetris->flag; //记住当前俄罗斯方块序号 429 //打印下一个俄罗斯方块的图形(右边窗口) 430 tetris->x=FrameX+2*Frame_width+6; 431 tetris->y=FrameY+10; 432 tetris->flag = tetris->next; 433 print_tetris(hOut,tetris); 434 tetris->x=FrameX+Frame_width; //初始中心方块x坐标 435 tetris->y=FrameY-1; //初始中心方块y坐标 436 tetris->flag=temp; //取出当前的俄罗斯方块序号 437 while(1) 438 {//控制方块方向,直至方块不再下移 439 label:print_tetris(hOut,tetris);//打印俄罗斯方块 440 Sleep(tetris->speed); //延缓时间 441 clear_tetris(hOut,tetris); //清除痕迹 442 temp1=tetris->x; //记住中心方块横坐标的值 443 temp2=tetris->flag; //记住当前俄罗斯方块序号 444 if(kbhit()) 445 { //判断是否有键盘输入,有则用ch↓接收 446 ch=getch(); 447 if(ch==75) //按←键则向左动,中心横坐标减2 448 { 449 tetris->x-=2; 450 } 451 if(ch==77) //按→键则向右动,中心横坐标加2 452 { 453 tetris->x+=2; 454 } 455 if(ch==72) //按↑键则变体即当前方块顺时针转90度 456 { 457 if( tetris->flag>=2 && tetris->flag<=3 ) 458 { 459 tetris->flag++; 460 tetris->flag%=2; 461 tetris->flag+=2; 462 } 463 if( tetris->flag>=4 && tetris->flag<=7 ) 464 { 465 tetris->flag++; 466 tetris->flag%=4; 467 tetris->flag+=4; 468 } 469 if( tetris->flag>=8 && tetris->flag<=11 ) 470 { 471 tetris->flag++; 472 tetris->flag%=4; 473 tetris->flag+=8; 474 } 475 if( tetris->flag>=12 && tetris->flag<=15 ) 476 { 477 tetris->flag++; 478 tetris->flag%=4; 479 tetris->flag+=12; 480 } 481 if( tetris->flag>=16 && tetris->flag<=19 ) 482 { 483 tetris->flag++; 484 tetris->flag%=4; 485 tetris->flag+=16; 486 } 487 } 488 if(ch==32) //按空格键,暂停 489 { 490 print_tetris(hOut,tetris); 491 while(1) 492 { 493 if(kbhit()) //再按空格键,继续游戏 494 { 495 ch=getch(); 496 if(ch==32) 497 { 498 goto label; 499 } 500 } 501 } 502 } 503 if(if_moveable(tetris)==0) //如果不可动,上面操作无效 504 { 505 tetris->x=temp1; 506 tetris->flag=temp2; 507 } 508 else //如果可动,执行操作 509 { 510 goto label; 511 } 512 } 513 tetris->y++; //如果没有操作指令,方块向下移动 514 if(if_moveable(tetris)==0) //如果向下移动且不可动,方块放在此处 515 { 516 tetris->y--; 517 print_tetris(hOut,tetris); 518 del_full(hOut,tetris); 519 break; 520 } 521 } 522 for(i=tetris->y-2;i<tetris->y+2;i++) 523 {//游戏结束条件:方块触到框顶位置 524 if(i==FrameY) 525 { 526 j=0; //如果游戏结束,j=0 527 } 528 } 529 if(j==0) 530 { 531 system("cls"); 532 getch(); 533 break; 534 } 535 //清除下一个俄罗斯方块的图形(右边窗口) 536 tetris->flag = tetris->next; 537 tetris->x=FrameX+2*Frame_width+6; 538 tetris->y=FrameY+10; 539 clear_tetris(hOut,tetris); 540 } 541 }
二、整个游戏的流程图
三、函数解析