[C] 俄罗斯方块 →→→→→进入此内容的聊天室

来自 姚奕琛, 2024-03-15, 写在 C, 查看 28 次.
URL http://www.code666.cn/view/7f1de994
  1. #include <stdio.h>
  2. #include <Windows.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. #include <conio.h>
  6.  
  7. #define ROW 29 //游戏区行数
  8. #define COL 20 //游戏区列数
  9.  
  10. #define DOWN 80 //方向键:下
  11. #define LEFT 75 //方向键:左
  12. #define RIGHT 77 //方向键:右
  13.  
  14. #define SPACE 32 //空格键
  15. #define ESC 27 //Esc键
  16.  
  17. struct Face
  18. {
  19.   int data[ROW][COL + 10]; //用于标记指定位置是否有方块(1为有,0为无)
  20.   int color[ROW][COL + 10]; //用于记录指定位置的方块颜色编码
  21. }face;
  22.  
  23. struct Block
  24. {
  25.   int space[4][4];
  26.   }block[7][4]; //用于存储7种基本形状方块的各自的4种形态的信息,共28种
  27.  
  28. //隐藏光标
  29. void HideCursor();
  30. //光标跳转
  31. void CursorJump(int x, int y);
  32. //初始化界面
  33. void InitInterface();
  34. //初始化方块信息
  35. void InitBlockInfo();
  36. //颜色设置
  37. void color(int num);
  38. //画出方块
  39. void DrawBlock(int shape, int form, int x, int y);
  40. //空格覆盖
  41. void DrawSpace(int shape, int form, int x, int y);
  42. //合法性判断
  43. int IsLegal(int shape, int form, int x, int y);
  44. //判断得分与结束
  45. int JudeFunc();
  46. //游戏主体逻辑函数
  47. void StartGame();
  48. //从文件读取最高分
  49. void ReadGrade();
  50. //更新最高分到文件
  51. void WriteGrade();
  52.  
  53. int max, grade; //全局变量
  54. int main()
  55. {
  56. #pragma warning (disable:4996) //消除警告
  57.   max = 0, grade = 0; //初始化变量
  58.   system("title 俄罗斯方块"); //设置cmd窗口的名字
  59.   system("mode con lines=29 cols=60"); //设置cmd窗口的大小
  60.   HideCursor(); //隐藏光标
  61.   ReadGrade(); //从文件读取最高分到max变量  
  62.   InitInterface(); //初始化界面
  63.   InitBlockInfo(); //初始化方块信息
  64.   srand((unsigned int)time(NULL)); //设置随机数生成的起点
  65.   StartGame(); //开始游戏
  66.   return 0;
  67. }
  68.  
  69. //隐藏光标
  70. void HideCursor()
  71. {
  72.   CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
  73.   curInfo.dwSize = 1;  //如果没赋值的话,隐藏光标无效
  74.   curInfo.bVisible = FALSE; //将光标设置为不可见
  75.   HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
  76.   SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
  77. }
  78. //光标跳转
  79. void CursorJump(int x, int y)
  80. {
  81.   COORD pos; //定义光标位置的结构体变量
  82.   pos.X = x; //横坐标设置
  83.   pos.Y = y; //纵坐标设置
  84.   HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
  85.   SetConsoleCursorPosition(handle, pos); //设置光标位置
  86. }
  87. //初始化界面
  88. void InitInterface()
  89. {
  90.   color(7); //颜色设置为白色
  91.   for (int i = 0; i < ROW; i++)
  92.   {
  93.     for (int j = 0; j < COL + 10; j++)
  94.     {
  95.       if (j == 0 || j == COL - 1 || j == COL + 9)
  96.       {
  97.         face.data[i][j] = 1; //标记该位置有方块
  98.         CursorJump(2 * j, i);
  99.         printf("■");
  100.       }
  101.       else if (i == ROW - 1)
  102.       {
  103.         face.data[i][j] = 1; //标记该位置有方块
  104.         printf("■");
  105.       }
  106.       else
  107.         face.data[i][j] = 0; //标记该位置无方块
  108.     }
  109.   }
  110.   for (int i = COL; i < COL + 10; i++)
  111.   {
  112.     face.data[8][i] = 1; //标记该位置有方块
  113.     CursorJump(2 * i, 8);
  114.     printf("■");
  115.   }
  116.  
  117.   CursorJump(2 * COL, 1);
  118.   printf("下一个方块:");
  119.  
  120.   CursorJump(2 * COL + 4, ROW - 19);
  121.   printf("左移:←");
  122.  
  123.   CursorJump(2 * COL + 4, ROW - 17);
  124.   printf("右移:→");
  125.  
  126.   CursorJump(2 * COL + 4, ROW - 15);
  127.   printf("加速:↓");
  128.  
  129.   CursorJump(2 * COL + 4, ROW - 13);
  130.   printf("旋转:空格");
  131.  
  132.   CursorJump(2 * COL + 4, ROW - 11);
  133.   printf("暂停: S");
  134.  
  135.   CursorJump(2 * COL + 4, ROW - 9);
  136.   printf("退出: Esc");
  137.  
  138.   CursorJump(2 * COL + 4, ROW - 7);
  139.   printf("重新开始:R");
  140.  
  141.   CursorJump(2 * COL + 4, ROW - 5);
  142.   printf("最高纪录:%d", max);
  143.  
  144.   CursorJump(2 * COL + 4, ROW - 3);
  145.   printf("当前分数:%d", grade);
  146. }
  147. //初始化方块信息
  148. void InitBlockInfo()
  149. {
  150.   //“T”形
  151.   for (int i = 0; i <= 2; i++)
  152.     block[0][0].space[1][i] = 1;
  153.   block[0][0].space[2][1] = 1;
  154.  
  155.   //“L”形
  156.   for (int i = 1; i <= 3; i++)
  157.     block[1][0].space[i][1] = 1;
  158.   block[1][0].space[3][2] = 1;
  159.  
  160.   //“J”形
  161.   for (int i = 1; i <= 3; i++)
  162.     block[2][0].space[i][2] = 1;
  163.   block[2][0].space[3][1] = 1;
  164.  
  165.   for (int i = 0; i <= 1; i++)
  166.   {
  167.     //“Z”形
  168.     block[3][0].space[1][i] = 1;
  169.     block[3][0].space[2][i + 1] = 1;
  170.     //“S”形
  171.     block[4][0].space[1][i + 1] = 1;
  172.     block[4][0].space[2][i] = 1;
  173.     //“O”形
  174.     block[5][0].space[1][i + 1] = 1;
  175.     block[5][0].space[2][i + 1] = 1;
  176.   }
  177.  
  178.   //“I”形
  179.   for (int i = 0; i <= 3; i++)
  180.     block[6][0].space[i][1] = 1;
  181.  
  182.   int temp[4][4];
  183.   for (int shape = 0; shape < 7; shape++) //7种形状
  184.   {
  185.     for (int form = 0; form < 3; form++) //4种形态(已经有了一种,这里每个还需增加3种)
  186.     {
  187.       //获取第form种形态
  188.       for (int i = 0; i < 4; i++)
  189.       {
  190.         for (int j = 0; j < 4; j++)
  191.         {
  192.           temp[i][j] = block[shape][form].space[i][j];
  193.         }
  194.       }
  195.       //将第form种形态顺时针旋转,得到第form+1种形态
  196.       for (int i = 0; i < 4; i++)
  197.       {
  198.         for (int j = 0; j < 4; j++)
  199.         {
  200.           block[shape][form + 1].space[i][j] = temp[3 - j][i];
  201.         }
  202.       }
  203.     }
  204.   }
  205. }
  206. //颜色设置
  207. void color(int c)
  208. {
  209.   switch (c)
  210.   {
  211.   case 0:
  212.     c = 13; //“T”形方块设置为紫色
  213.     break;
  214.   case 1:
  215.   case 2:
  216.     c = 12; //“L”形和“J”形方块设置为红色
  217.     break;
  218.   case 3:
  219.   case 4:
  220.     c = 10; //“Z”形和“S”形方块设置为绿色
  221.     break;
  222.   case 5:
  223.     c = 14; //“O”形方块设置为黄色
  224.     break;
  225.   case 6:
  226.     c = 11; //“I”形方块设置为浅蓝色
  227.     break;
  228.   default:
  229.     c = 7; //其他默认设置为白色
  230.     break;
  231.   }
  232.   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
  233.   //注:SetConsoleTextAttribute是一个API(应用程序编程接口)
  234. }
  235. //画出方块
  236. void DrawBlock(int shape, int form, int x, int y)
  237. {
  238.   for (int i = 0; i < 4; i++)
  239.   {
  240.     for (int j = 0; j < 4; j++)
  241.     {
  242.       if (block[shape][form].space[i][j] == 1) //如果该位置有方块
  243.       {
  244.         CursorJump(2 * (x + j), y + i); //光标跳转到指定位置
  245.         printf("■"); //输出方块
  246.       }
  247.     }
  248.   }
  249. }
  250. //空格覆盖
  251. void DrawSpace(int shape, int form, int x, int y)
  252. {
  253.   for (int i = 0; i < 4; i++)
  254.   {
  255.     for (int j = 0; j < 4; j++)
  256.     {
  257.       if (block[shape][form].space[i][j] == 1) //如果该位置有方块
  258.       {
  259.         CursorJump(2 * (x + j), y + i); //光标跳转到指定位置
  260.         printf("  "); //打印空格覆盖(两个空格)
  261.       }
  262.     }
  263.   }
  264. }
  265. //合法性判断
  266. int IsLegal(int shape, int form, int x, int y)
  267. {
  268.   for (int i = 0; i < 4; i++)
  269.   {
  270.     for (int j = 0; j < 4; j++)
  271.     {
  272.       //如果方块落下的位置本来就已经有方块了,则不合法
  273.       if ((block[shape][form].space[i][j] == 1) && (face.data[y + i][x + j] == 1))
  274.         return 0; //不合法
  275.     }
  276.   }
  277.   return 1; //合法
  278. }
  279. //判断得分与结束
  280. int JudeFunc()
  281. {
  282.   //判断是否得分
  283.   for (int i = ROW - 2; i > 4; i--)
  284.   {
  285.     int sum = 0; //记录第i行的方块个数
  286.     for (int j = 1; j < COL - 1; j++)
  287.     {
  288.       sum += face.data[i][j]; //统计第i行的方块个数
  289.     }
  290.     if (sum == 0) //该行没有方块,无需再判断其上的层次(无需再继续判断是否得分)
  291.       break; //跳出循环
  292.     if (sum == COL - 2) //该行全是方块,可得分
  293.     {
  294.       grade += 10; //满一行加10分
  295.       color(7); //颜色设置为白色
  296.       CursorJump(2 * COL + 4, ROW - 3); //光标跳转到显示当前分数的位置
  297.       printf("当前分数:%d", grade); //更新当前分数
  298.       for (int j = 1; j < COL - 1; j++) //清除得分行的方块信息
  299.       {
  300.         face.data[i][j] = 0; //该位置得分后被清除,标记为无方块
  301.         CursorJump(2 * j, i); //光标跳转到该位置
  302.         printf("  "); //打印空格覆盖(两个空格)
  303.       }
  304.       //把被清除行上面的行整体向下挪一格
  305.       for (int m = i; m >1; m--)
  306.       {
  307.         sum = 0; //记录上一行的方块个数
  308.         for (int n = 1; n < COL - 1; n++)
  309.         {
  310.           sum += face.data[m - 1][n]; //统计上一行的方块个数
  311.           face.data[m][n] = face.data[m - 1][n]; //将上一行方块的标识移到下一行
  312.           face.color[m][n] = face.color[m - 1][n]; //将上一行方块的颜色编号移到下一行
  313.           if (face.data[m][n] == 1) //上一行移下来的是方块,打印方块
  314.           {
  315.             CursorJump(2 * n, m); //光标跳转到该位置
  316.             color(face.color[m][n]); //颜色设置为还方块的颜色
  317.             printf("■"); //打印方块
  318.           }
  319.           else //上一行移下来的是空格,打印空格
  320.           {
  321.             CursorJump(2 * n, m); //光标跳转到该位置
  322.             printf("  "); //打印空格(两个空格)
  323.           }
  324.         }
  325.         if (sum == 0) //上一行移下来的全是空格,无需再将上层的方块向下移动(移动结束)
  326.           return 1; //返回1,表示还需调用该函数进行判断(移动下来的可能还有满行)
  327.       }
  328.     }
  329.   }
  330.   //判断游戏是否结束
  331.   for (int j = 1; j < COL - 1; j++)
  332.   {
  333.     if (face.data[1][j] == 1) //顶层有方块存在(以第1行为顶层,不是第0行)
  334.     {
  335.       Sleep(1000); //留给玩家反应时间
  336.       system("cls"); //清空屏幕
  337.       color(7); //颜色设置为白色
  338.       CursorJump(2 * (COL / 3), ROW / 2 - 3);
  339.       if (grade>max)
  340.       {
  341.         printf("恭喜你打破最高记录,最高记录更新为%d", grade);
  342.         WriteGrade();
  343.       }
  344.       else if (grade == max)
  345.       {
  346.         printf("与最高记录持平,加油再创佳绩", grade);
  347.       }
  348.       else
  349.       {
  350.         printf("请继续加油,当前与最高记录相差%d", max - grade);
  351.       }
  352.       CursorJump(2 * (COL / 3), ROW / 2);
  353.       printf("GAME OVER");
  354.       while (1)
  355.       {
  356.         char ch;
  357.         CursorJump(2 * (COL / 3), ROW / 2 + 3);
  358.         printf("再来一局?(y/n):");
  359.         scanf("%c", &ch);
  360.         if (ch == 'y' || ch == 'Y')
  361.         {
  362.           system("cls");
  363.           main();
  364.         }
  365.         else if (ch == 'n' || ch == 'N')
  366.         {
  367.           CursorJump(2 * (COL / 3), ROW / 2 + 5);
  368.           exit(0);
  369.         }
  370.         else
  371.         {
  372.           CursorJump(2 * (COL / 3), ROW / 2 + 4);
  373.           printf("选择错误,请再次选择");
  374.         }
  375.       }
  376.     }
  377.   }
  378.   return 0; //判断结束,无需再调用该函数进行判断
  379. }
  380. //游戏主体逻辑函数
  381. void StartGame()
  382. {
  383.   int shape = rand() % 7, form = rand() % 4; //随机获取方块的形状和形态
  384.   while (1)
  385.   {
  386.     int t = 0;
  387.     int nextShape = rand() % 7, nextForm = rand() % 4; //随机获取下一个方块的形状和形态
  388.     int x = COL / 2 - 2, y = 0; //方块初始下落位置的横纵坐标
  389.     color(nextShape); //颜色设置为下一个方块的颜色
  390.     DrawBlock(nextShape, nextForm, COL + 3, 3); //将下一个方块显示在右上角
  391.     while (1)
  392.     {
  393.       color(shape); //颜色设置为当前正在下落的方块
  394.       DrawBlock(shape, form, x, y); //将该方块显示在初始下落位置
  395.       if (t == 0)
  396.       {
  397.         t = 15000; //这里t越小,方块下落越快(可以根据此设置游戏难度)
  398.       }
  399.       while (--t)
  400.       {
  401.         if (kbhit() != 0) //若键盘被敲击,则退出循环
  402.           break;
  403.       }
  404.       if (t == 0) //键盘未被敲击
  405.       {
  406.         if (IsLegal(shape, form, x, y + 1) == 0) //方块再下落就不合法了(已经到达底部)
  407.         {
  408.           //将当前方块的信息录入face当中
  409.           //face:记录界面的每个位置是否有方块,若有方块还需记录该位置方块的颜色。
  410.           for (int i = 0; i < 4; i++)
  411.           {
  412.             for (int j = 0; j < 4; j++)
  413.             {
  414.               if (block[shape][form].space[i][j] == 1)
  415.               {
  416.                 face.data[y + i][x + j] = 1; //将该位置标记为有方块
  417.                 face.color[y + i][x + j] = shape; //记录该方块的颜色数值
  418.               }
  419.             }
  420.           }
  421.           while (JudeFunc()); //判断此次方块下落是否得分以及游戏是否结束
  422.           break; //跳出当前死循环,准备进行下一个方块的下落
  423.         }
  424.         else //未到底部
  425.         {
  426.           DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置
  427.           y++; //纵坐标自增(下一次显示方块时就相当于下落了一格了)
  428.         }
  429.       }
  430.       else //键盘被敲击
  431.       {
  432.         char ch = getch(); //读取keycode
  433.         switch (ch)
  434.         {
  435.         case DOWN: //方向键:下
  436.           if (IsLegal(shape, form, x, y + 1) == 1) //判断方块向下移动一位后是否合法
  437.           {
  438.             //方块下落后合法才进行以下操作
  439.             DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置
  440.             y++; //纵坐标自增(下一次显示方块时就相当于下落了一格了)
  441.           }
  442.           break;
  443.         case LEFT: //方向键:左
  444.           if (IsLegal(shape, form, x - 1, y) == 1) //判断方块向左移动一位后是否合法
  445.           {
  446.             //方块左移后合法才进行以下操作
  447.             DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置
  448.             x--; //横坐标自减(下一次显示方块时就相当于左移了一格了)
  449.           }
  450.           break;
  451.         case RIGHT: //方向键:右
  452.           if (IsLegal(shape, form, x + 1, y) == 1) //判断方块向右移动一位后是否合法
  453.           {
  454.             //方块右移后合法才进行以下操作
  455.             DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置
  456.             x++; //横坐标自增(下一次显示方块时就相当于右移了一格了)
  457.           }
  458.           break;
  459.         case SPACE: //空格键
  460.           if (IsLegal(shape, (form + 1) % 4, x, y + 1) == 1) //判断方块旋转后是否合法
  461.           {
  462.             //方块旋转后合法才进行以下操作
  463.             DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置
  464.             y++; //纵坐标自增(总不能原地旋转吧)
  465.             form = (form + 1) % 4; //方块的形态自增(下一次显示方块时就相当于旋转了)
  466.           }
  467.           break;
  468.         case ESC: //Esc键
  469.           system("cls"); //清空屏幕
  470.           color(7);
  471.           CursorJump(COL, ROW / 2);
  472.           printf("  游戏结束  ");
  473.           CursorJump(COL, ROW / 2 + 2);
  474.           exit(0); //结束程序
  475.         case 's':
  476.         case 'S':  //暂停
  477.           system("pause>nul"); //暂停(按任意键继续)
  478.           break;
  479.         case 'r':
  480.         case 'R': //重新开始
  481.           system("cls"); //清空屏幕
  482.           main(); //重新执行主函数
  483.         }
  484.       }
  485.     }
  486.     shape = nextShape, form = nextForm; //获取下一个方块的信息
  487.     DrawSpace(nextShape, nextForm, COL + 3, 3); //将右上角的方块信息用空格覆盖
  488.   }
  489. }
  490. //从文件读取最高分
  491. void ReadGrade()
  492. {
  493.   FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "r"); //以只读方式打开文件
  494.   if (pf == NULL) //打开文件失败
  495.   {
  496.     pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件(文件不存在可以自动创建该文件)
  497.     fwrite(&grade, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高历史得分初始化为0
  498.   }
  499.   fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
  500.   fread(&max, sizeof(int), 1, pf); //读取文件中的最高历史得分到max当中
  501.   fclose(pf); //关闭文件
  502.   pf = NULL; //文件指针及时置空
  503. }
  504. //更新最高分到文件
  505. void WriteGrade()
  506. {
  507.   FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件
  508.   if (pf == NULL) //打开文件失败
  509.   {
  510.     printf("保存最高得分记录失败\n");
  511.     exit(0);
  512.   }
  513.   fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中(更新最高历史得分)
  514.   fclose(pf); //关闭文件
  515.   pf = NULL; //文件指针及时置空
  516. }

回复 "俄罗斯方块"

这儿你可以回复上面这条便签

captcha