Console version Tetris written in C language (bit operation implementation)

From Little Mousedeer, 1 Year ago, written in C, viewed 201 times.
URL https://pastebin.vip/view/4639b890
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. #include <conio.h>
  6. #include <windows.h>
  7.  
  8. #ifdef _MSC_VER  // M$的编译器要给予特殊照顾
  9. #if _MSC_VER <= 1200  // VC6及以下版本
  10. #error 你是不是还在用VC6?!
  11. #else  // VC6以上版本
  12. #if _MSC_VER >= 1600  // 据说VC10及以上版本有stdint.h了
  13. #include <stdint.h>
  14. #else  // VC10以下版本,自己定义int8_t和uint16_t
  15. typedef signed char int8_t;
  16. typedef unsigned short uint16_t;
  17. #endif
  18. #ifndef __cplusplus  // 据说VC都没有stdbool.h,不用C++编译,自己定义bool
  19. typedef int bool;
  20. #define true 1
  21. #define false 0
  22. #endif
  23. #endif
  24. #else  // 其他的编译器都好说
  25. #include <stdint.h>
  26. #ifndef __cplusplus  // 不用C++编译,需要stdbool.h里的bool
  27. #include <stdbool.h>
  28. #endif
  29. #endif
  30.  
  31. // =============================================================================
  32. // 7种方块的4旋转状态(4位为一行)
  33. static const uint16_t gs_uTetrisTable[7][4] = { { 0x00F0U, 0x2222U, 0x00F0U,
  34.         0x2222U },  // I型
  35.         { 0x0072U, 0x0262U, 0x0270U, 0x0232U },  // T型
  36.         { 0x0223U, 0x0074U, 0x0622U, 0x0170U },  // L型
  37.         { 0x0226U, 0x0470U, 0x0322U, 0x0071U },  // J型
  38.         { 0x0063U, 0x0264U, 0x0063U, 0x0264U },  // Z型
  39.         { 0x006CU, 0x0462U, 0x006CU, 0x0462U },  // S型
  40.         { 0x0660U, 0x0660U, 0x0660U, 0x0660U }   // O型
  41. };
  42.  
  43. // =============================================================================
  44. // 初始状态的游戏池
  45. // 每个元素表示游戏池的一行,下标大的是游戏池底部
  46. // 两端各置2个1,底部2全置为1,便于进行碰撞检测
  47. // 这样一来游戏池的宽度为12列
  48. // 如果想要传统的10列,只需多填两个1即可(0xE007),当然显示相关部分也要随之改动
  49. // 当某个元素为0xFFFFU时,说明该行已被填满
  50. // 顶部4行用于给方块,不显示出来
  51. // 再除去底部2行,显示出来的游戏池高度为22行
  52. static const uint16_t gs_uInitialTetrisPool[28] = { 0xC003U, 0xC003U, 0xC003U,
  53.         0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,
  54.         0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,
  55.         0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xFFFFU,
  56.         0xFFFFU };
  57.  
  58. #define COL_BEGIN 2
  59. #define COL_END 14
  60. #define ROW_BEGIN 4
  61. #define ROW_END 26
  62.  
  63. // =============================================================================
  64. typedef struct TetrisManager  // 这个结构体存储游戏相关数据
  65. {
  66.     uint16_t pool[28];  // 游戏池
  67.     int8_t x;  // 当前方块x坐标,此处坐标为方块左上角坐标
  68.     int8_t y;  // 当前方块y坐标
  69.     int8_t type[3];  // 当前、下一个和下下一个方块类型
  70.     int8_t orientation[3];  // 当前、下一个和下下一个方块旋转状态
  71.     unsigned score;  // 得分
  72.     unsigned erasedCount[4];  // 消行数
  73.     unsigned erasedTotal;  // 消行总数
  74.     unsigned tetrisCount[7];  // 各方块数
  75.     unsigned tetrisTotal;  // 方块总数
  76.     bool dead;  // 挂
  77. } TetrisManager;
  78.  
  79. // =============================================================================
  80. typedef struct TetrisControl  // 这个结构体存储控制相关数据
  81. {
  82.     bool pause;  // 暂停
  83.     bool clockwise;  // 旋转方向:顺时针为true
  84.     int8_t direction;  // 移动方向:0向左移动 1向右移动
  85.     // 游戏池内每格的颜色
  86.     // 由于此版本是彩色的,仅用游戏池数据无法存储颜色信息
  87.     // 当然,如果只实现单色版的,就没必要用这个数组了
  88.     int8_t color[28][16];
  89. } TetrisControl;
  90.  
  91. HANDLE g_hConsoleOutput;  // 控制台输出句柄
  92.  
  93. // =============================================================================
  94. // 函数声明
  95. // 如果使用全局变量方式实现,就没必要传参了
  96. void initGame(TetrisManager *manager, TetrisControl *control);  // 初始化游戏
  97. void restartGame(TetrisManager *manager, TetrisControl *control);  // 重新开始游戏
  98. void giveTetris(TetrisManager *manager);  // 给一个方块
  99. bool checkCollision(const TetrisManager *manager);  // 碰撞检测
  100. void insertTetris(TetrisManager *manager);  // 插入方块
  101. void removeTetris(TetrisManager *manager);  // 移除方块
  102. void horzMoveTetris(TetrisManager *manager, TetrisControl *control);  // 水平移动方块
  103. void moveDownTetris(TetrisManager *manager, TetrisControl *control);  // 向下移动方块
  104. void rotateTetris(TetrisManager *manager, TetrisControl *control);  // 旋转方块
  105. void dropDownTetris(TetrisManager *manager, TetrisControl *control);  // 方块直接落地
  106. bool checkErasing(TetrisManager *manager, TetrisControl *control);  // 消行检测
  107. void keydownControl(TetrisManager *manager, TetrisControl *control, int key); // 键按下
  108. void setPoolColor(const TetrisManager *manager, TetrisControl *control); // 设置颜色
  109. void gotoxyWithFullwidth(short x, short y);  // 以全角定位
  110. void printPoolBorder();  // 显示游戏池边界
  111. void printTetrisPool(const TetrisManager *manager, const TetrisControl *control); // 显示游戏池
  112. void printCurrentTetris(const TetrisManager *manager,
  113.         const TetrisControl *control);  // 显示当前方块
  114. void printNextTetris(const TetrisManager *manager);  // 显示下一个和下下一个方块
  115. void printScore(const TetrisManager *manager);  // 显示得分信息
  116. void runGame(TetrisManager *manager, TetrisControl *control);  // 运行游戏
  117. void printPrompting();  // 显示提示信息
  118. bool ifPlayAgain();  // 再来一次
  119.  
  120. // =============================================================================
  121. // 出处:http://www.oschina.net/code/snippet_255612_16922
  122. // 主函数
  123. int main() {
  124.     TetrisManager tetrisManager;
  125.     TetrisControl tetrisControl;
  126.  
  127.     initGame(&tetrisManager, &tetrisControl);  // 初始化游戏
  128.     do {
  129.         printPrompting();  // 显示提示信息
  130.         printPoolBorder();  // 显示游戏池边界
  131.         runGame(&tetrisManager, &tetrisControl);  // 运行游戏
  132.         if (ifPlayAgain())  // 再来一次
  133.         {
  134.             SetConsoleTextAttribute(g_hConsoleOutput, 0x7);
  135.             system("cls");  // 清屏
  136.             restartGame(&tetrisManager, &tetrisControl);  // 重新开始游戏
  137.         } else {
  138.             break;
  139.         }
  140.     } while (1);
  141.     gotoxyWithFullwidth(0, 0);
  142.     CloseHandle(g_hConsoleOutput);
  143.     return 0;
  144. }
  145.  
  146. // =============================================================================
  147. // 初始化游戏
  148. void initGame(TetrisManager *manager, TetrisControl *control) {
  149.     CONSOLE_CURSOR_INFO cursorInfo = { 1, FALSE };  // 光标信息
  150.  
  151.     g_hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);  // 获取控制台输出句柄
  152.     SetConsoleCursorInfo(g_hConsoleOutput, &cursorInfo);  // 设置光标隐藏
  153.     SetConsoleTitleA("俄罗斯方块");
  154.  
  155.     restartGame(manager, control);
  156. }
  157.  
  158. // =============================================================================
  159. // 重新开始游戏
  160. void restartGame(TetrisManager *manager, TetrisControl *control) {
  161.     memset(manager, 0, sizeof(TetrisManager));  // 全部置0
  162.  
  163.     // 初始化游戏池
  164.     memcpy(manager->pool, gs_uInitialTetrisPool, sizeof(uint16_t[28]));
  165.     srand((unsigned) time(NULL ));  // 设置随机种子
  166.  
  167.     manager->type[1] = rand() % 7;  // 下一个
  168.     manager->orientation[1] = rand() & 3;
  169.  
  170.     manager->type[2] = rand() % 7;  // 下下一个
  171.     manager->orientation[2] = rand() & 3;
  172.  
  173.     memset(control, 0, sizeof(TetrisControl));  // 全部置0
  174.  
  175.     giveTetris(manager);  // 给下一个方块
  176.     setPoolColor(manager, control);  // 设置颜色
  177. }
  178.  
  179. // =============================================================================
  180. // 给一个方块
  181. void giveTetris(TetrisManager *manager) {
  182.     uint16_t tetris;
  183.  
  184.     manager->type[0] = manager->type[1];  // 下一个方块置为当前
  185.     manager->orientation[0] = manager->orientation[1];
  186.  
  187.     manager->type[1] = manager->type[2];  // 下下一个置方块为下一个
  188.     manager->orientation[1] = manager->orientation[2];
  189.  
  190.     manager->type[2] = rand() % 7;  // 随机生成下下一个方块
  191.     manager->orientation[2] = rand() & 3;
  192.  
  193.     tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]]; // 当前方块
  194.  
  195.     // 设置当前方块y坐标,保证刚给出时只显示方块最下面一行
  196.     // 这种实现使得玩家可以以很快的速度将方块落在不显示出来的顶部4行内
  197.     if (tetris & 0xF000) {
  198.         manager->y = 0;
  199.     } else {
  200.         manager->y = (tetris & 0xFF00) ? 1 : 2;
  201.     }
  202.     manager->x = 6;  // 设置当前方块x坐标
  203.  
  204.     if (checkCollision(manager))  // 检测到碰撞
  205.             {
  206.         manager->dead = true;  // 标记游戏结束
  207.     } else  // 未检测到碰撞
  208.     {
  209.         insertTetris(manager);  // 将当前方块加入游戏池
  210.     }
  211.  
  212.     ++manager->tetrisTotal;  // 方块总数
  213.     ++manager->tetrisCount[manager->type[0]];  // 相应方块数
  214.  
  215.     printNextTetris(manager);  // 显示下一个方块
  216.     printScore(manager);  // 显示得分信息
  217. }
  218.  
  219. // =============================================================================
  220. // 碰撞检测
  221. bool checkCollision(const TetrisManager *manager) {
  222.     // 当前方块
  223.     uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];
  224.     uint16_t dest = 0;
  225.  
  226.     // 获取当前方块在游戏池中的区域:
  227.     // 游戏池坐标x y处小方格信息,按低到高存放在16位无符号数中
  228.     dest |= (((manager->pool[manager->y + 0] >> manager->x) << 0x0) & 0x000F);
  229.     dest |= (((manager->pool[manager->y + 1] >> manager->x) << 0x4) & 0x00F0);
  230.     dest |= (((manager->pool[manager->y + 2] >> manager->x) << 0x8) & 0x0F00);
  231.     dest |= (((manager->pool[manager->y + 3] >> manager->x) << 0xC) & 0xF000);
  232.  
  233.     // 若当前方块与目标区域存在重叠(碰撞),则位与的结果不为0
  234.     return ((dest & tetris) != 0);
  235. }
  236.  
  237. // =============================================================================
  238. // 插入方块
  239. void insertTetris(TetrisManager *manager) {
  240.     // 当前方块
  241.     uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];
  242.  
  243.     // 当前方块每4位取出,位或到游戏池相应位置,即完成插入方块
  244.     manager->pool[manager->y + 0] |= (((tetris >> 0x0) & 0x000F) << manager->x);
  245.     manager->pool[manager->y + 1] |= (((tetris >> 0x4) & 0x000F) << manager->x);
  246.     manager->pool[manager->y + 2] |= (((tetris >> 0x8) & 0x000F) << manager->x);
  247.     manager->pool[manager->y + 3] |= (((tetris >> 0xC) & 0x000F) << manager->x);
  248. }
  249.  
  250. // =============================================================================
  251. // 移除方块
  252. void removeTetris(TetrisManager *manager) {
  253.     // 当前方块
  254.     uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];
  255.  
  256.     // 当前方块每4位取出,按位取反后位与到游戏池相应位置,即完成移除方块
  257.     manager->pool[manager->y + 0] &=
  258.             ~(((tetris >> 0x0) & 0x000F) << manager->x);
  259.     manager->pool[manager->y + 1] &=
  260.             ~(((tetris >> 0x4) & 0x000F) << manager->x);
  261.     manager->pool[manager->y + 2] &=
  262.             ~(((tetris >> 0x8) & 0x000F) << manager->x);
  263.     manager->pool[manager->y + 3] &=
  264.             ~(((tetris >> 0xC) & 0x000F) << manager->x);
  265. }
  266.  
  267. // =============================================================================
  268. // 设置颜色
  269. void setPoolColor(const TetrisManager *manager, TetrisControl *control) {
  270.     // 由于显示游戏池时,先要在游戏池里判断某一方格有方块才显示相应方格的颜色
  271.     // 这里只作设置即可,没必要清除
  272.     // 当移动方块或给一个方块时调用
  273.  
  274.     int8_t i, x, y;
  275.  
  276.     // 当前方块
  277.     uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];
  278.  
  279.     for (i = 0; i < 16; ++i) {
  280.         y = (i >> 2) + manager->y;  // 待设置的列
  281.         if (y > ROW_END)  // 超过底部限制
  282.         {
  283.             break;
  284.         }
  285.         x = (i & 3) + manager->x;  // 待设置的行
  286.         if ((tetris >> i) & 1)  // 检测的到小方格属于当前方块区域
  287.                 {
  288.             control->color[y][x] = (manager->type[0] | 8);  // 设置颜色
  289.         }
  290.     }
  291. }
  292.  
  293. // =============================================================================
  294. // 旋转方块
  295. void rotateTetris(TetrisManager *manager, TetrisControl *control) {
  296.     int8_t ori = manager->orientation[0];  // 记录原旋转状态
  297.  
  298.     removeTetris(manager);  // 移走当前方块
  299.  
  300.     // 顺/逆时针旋转
  301.     manager->orientation[0] =
  302.             (control->clockwise) ? ((ori + 1) & 3) : ((ori + 3) & 3);
  303.  
  304.     if (checkCollision(manager))  // 检测到碰撞
  305.             {
  306.         manager->orientation[0] = ori;  // 恢复为原旋转状态
  307.         insertTetris(manager);  // 放入当前方块。由于状态没改变,不需要设置颜色
  308.     } else {
  309.         insertTetris(manager);  // 放入当前方块
  310.         setPoolColor(manager, control);  // 设置颜色
  311.         printCurrentTetris(manager, control);  // 显示当前方块
  312.     }
  313. }
  314.  
  315. // =============================================================================
  316. // 水平移动方块
  317. void horzMoveTetris(TetrisManager *manager, TetrisControl *control) {
  318.     int x = manager->x;  // 记录原列位置
  319.  
  320.     removeTetris(manager);  // 移走当前方块
  321.     control->direction == 0 ? (--manager->x) : (++manager->x);  // 左/右移动
  322.  
  323.     if (checkCollision(manager))  // 检测到碰撞
  324.             {
  325.         manager->x = x;  // 恢复为原列位置
  326.         insertTetris(manager);  // 放入当前方块。由于位置没改变,不需要设置颜色
  327.     } else {
  328.         insertTetris(manager);  // 放入当前方块
  329.         setPoolColor(manager, control);  // 设置颜色
  330.         printCurrentTetris(manager, control);  // 显示当前方块
  331.     }
  332. }
  333.  
  334. // =============================================================================
  335. // 向下移动方块
  336. void moveDownTetris(TetrisManager *manager, TetrisControl *control) {
  337.     int8_t y = manager->y;  // 记录原行位置
  338.  
  339.     removeTetris(manager);  // 移走当前方块
  340.     ++manager->y;  // 向下移动
  341.  
  342.     if (checkCollision(manager))  // 检测到碰撞
  343.             {
  344.         manager->y = y;  // 恢复为原行位置
  345.         insertTetris(manager);  // 放入当前方块。由于位置没改变,不需要设置颜色
  346.         if (checkErasing(manager, control))  // 检测到消行
  347.                 {
  348.             printTetrisPool(manager, control);  // 显示游戏池
  349.         }
  350.     } else {
  351.         insertTetris(manager);  // 放入当前方块
  352.         setPoolColor(manager, control);  // 设置颜色
  353.         printCurrentTetris(manager, control);  // 显示当前方块
  354.     }
  355. }
  356.  
  357. // =============================================================================
  358. // 方块直接落地
  359. void dropDownTetris(TetrisManager *manager, TetrisControl *control) {
  360.     removeTetris(manager);  // 移走当前方块
  361.     for (; manager->y < ROW_END; ++manager->y)  // 从上往下
  362.             {
  363.         if (checkCollision(manager))  // 检测到碰撞
  364.                 {
  365.             break;
  366.         }
  367.     }
  368.     --manager->y;  // 上移一格当然没有碰撞
  369.  
  370.     insertTetris(manager);  // 放入当前方块
  371.     setPoolColor(manager, control);  // 设置颜色
  372.  
  373.     checkErasing(manager, control);  // 检测消行
  374.     printTetrisPool(manager, control);  // 显示游戏池
  375. }
  376.  
  377. // =============================================================================
  378. // 消行检测
  379. bool checkErasing(TetrisManager *manager, TetrisControl *control) {
  380.     static const unsigned scores[5] = { 0, 10, 30, 90, 150 };  // 消行得分
  381.     int8_t count = 0;
  382.     int8_t k = 0, y = manager->y + 3;  // 从下往上检测
  383.  
  384.     do {
  385.         if (y < ROW_END && manager->pool[y] == 0xFFFFU)  // 有效区域内且一行已填满
  386.                 {
  387.             ++count;
  388.             // 消除一行方块
  389.             memmove(manager->pool + 1, manager->pool, sizeof(uint16_t) * y);
  390.             // 颜色数组的元素随之移动
  391.             memmove(control->color[1], control->color[0],
  392.                     sizeof(int8_t[16]) * y);
  393.         } else {
  394.             --y;
  395.             ++k;
  396.         }
  397.     } while (y >= manager->y && k < 4);
  398.  
  399.     manager->erasedTotal += count;  // 消行总数
  400.     manager->score += scores[count];  // 得分
  401.  
  402.     if (count > 0) {
  403.         ++manager->erasedCount[count - 1];  // 消行
  404.     }
  405.  
  406.     giveTetris(manager);  // 给下一个方块
  407.     setPoolColor(manager, control);  // 设置颜色
  408.  
  409.     return (count > 0);
  410. }
  411.  
  412. // =============================================================================
  413. // 键按下
  414. void keydownControl(TetrisManager *manager, TetrisControl *control, int key) {
  415.     if (key == 13)  // 暂停/解除暂停
  416.             {
  417.         control->pause = !control->pause;
  418.     }
  419.  
  420.     if (control->pause)  // 暂停状态,不作处理
  421.     {
  422.         return;
  423.     }
  424.  
  425.     switch (key) {
  426.     case 'w':
  427.     case 'W':
  428.     case '8':
  429.     case 72:  // 上
  430.         control->clockwise = true;  // 顺时针旋转
  431.         rotateTetris(manager, control);  // 旋转方块
  432.         break;
  433.     case 'a':
  434.     case 'A':
  435.     case '4':
  436.     case 75:  // 左
  437.         control->direction = 0;  // 向左移动
  438.         horzMoveTetris(manager, control);  // 水平移动方块
  439.         break;
  440.     case 'd':
  441.     case 'D':
  442.     case '6':
  443.     case 77:  // 右
  444.         control->direction = 1;  // 向右移动
  445.         horzMoveTetris(manager, control);  // 水平移动方块
  446.         break;
  447.     case 's':
  448.     case 'S':
  449.     case '2':
  450.     case 80:  // 下
  451.         moveDownTetris(manager, control);  // 向下移动方块
  452.         break;
  453.     case ' ':  // 直接落地
  454.         dropDownTetris(manager, control);
  455.         break;
  456.     case '0':  // 反转
  457.         control->clockwise = false;  // 逆时针旋转
  458.         rotateTetris(manager, control);  // 旋转方块
  459.         break;
  460.     default:
  461.         break;
  462.     }
  463. }
  464.  
  465. // =============================================================================
  466. // 以全角定位
  467. void gotoxyWithFullwidth(short x, short y) {
  468.     static COORD cd;
  469.  
  470.     cd.X = (short) (x << 1);
  471.     cd.Y = y;
  472.     SetConsoleCursorPosition(g_hConsoleOutput, cd);
  473. }
  474.  
  475. // =============================================================================
  476. // 显示游戏池边界
  477. void printPoolBorder() {
  478.     int8_t y;
  479.  
  480.     SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);
  481.     for (y = ROW_BEGIN; y < ROW_END; ++y)  // 不显示顶部4行和底部2行
  482.             {
  483.         gotoxyWithFullwidth(10, y - 3);
  484.         printf("%2s", "");
  485.         gotoxyWithFullwidth(23, y - 3);
  486.         printf("%2s", "");
  487.     }
  488.  
  489.     gotoxyWithFullwidth(10, y - 3);  // 底部边界
  490.     printf("%28s", "");
  491. }
  492.  
  493. // 定位到游戏池中的方格
  494. #define gotoxyInPool(x, y) gotoxyWithFullwidth(x + 9, y - 3)
  495.  
  496. // =============================================================================
  497. // 显示游戏池
  498. void printTetrisPool(const TetrisManager *manager, const TetrisControl *control) {
  499.     int8_t x, y;
  500.  
  501.     for (y = ROW_BEGIN; y < ROW_END; ++y)  // 不显示顶部4行和底部2行
  502.             {
  503.         gotoxyInPool(2, y);
  504.         // 定点到游戏池中的方格
  505.         for (x = COL_BEGIN; x < COL_END; ++x)  // 不显示左右边界
  506.                 {
  507.             if ((manager->pool[y] >> x) & 1)  // 游戏池该方格有方块
  508.                     {
  509.                 // 用相应颜色,显示一个实心方块
  510.                 SetConsoleTextAttribute(g_hConsoleOutput, control->color[y][x]);
  511.                 printf("■");
  512.             } else  // 没有方块,显示空白
  513.             {
  514.                 SetConsoleTextAttribute(g_hConsoleOutput, 0);
  515.                 printf("%2s", "");
  516.             }
  517.         }
  518.     }
  519. }
  520.  
  521. // =============================================================================
  522. // 显示当前方块
  523. void printCurrentTetris(const TetrisManager *manager,
  524.         const TetrisControl *control) {
  525.     int8_t x, y;
  526.  
  527.     // 显示当前方块是在移动后调用的,为擦去移动前的方块,需要扩展显示区域
  528.     // 由于不可能向上移动,故不需要向下扩展
  529.     y = (manager->y > ROW_BEGIN) ? (manager->y - 1) : ROW_BEGIN;  // 向上扩展一格
  530.     for (; y < ROW_END && y < manager->y + 4; ++y) {
  531.         x = (manager->x > COL_BEGIN) ? (manager->x - 1) : COL_BEGIN;  // 向左扩展一格
  532.         for (; x < COL_END && x < manager->x + 5; ++x)  // 向右扩展一格
  533.                 {
  534.             gotoxyInPool(x, y);
  535.             // 定点到游戏池中的方格
  536.             if ((manager->pool[y] >> x) & 1)  // 游戏池该方格有方块
  537.                     {
  538.                 // 用相应颜色,显示一个实心方块
  539.                 SetConsoleTextAttribute(g_hConsoleOutput, control->color[y][x]);
  540.                 printf("■");
  541.             } else  // 没有方块,显示空白
  542.             {
  543.                 SetConsoleTextAttribute(g_hConsoleOutput, 0);
  544.                 printf("%2s", "");
  545.             }
  546.         }
  547.     }
  548. }
  549.  
  550. // =============================================================================
  551. // 显示下一个和下下一个方块
  552. void printNextTetris(const TetrisManager *manager) {
  553.     int8_t i;
  554.     uint16_t tetris;
  555.  
  556.     // 边框
  557.     SetConsoleTextAttribute(g_hConsoleOutput, 0xF);
  558.     gotoxyWithFullwidth(26, 1);
  559.     printf("┏━━━━┳━━━━┓");
  560.     gotoxyWithFullwidth(26, 2);
  561.     printf("┃%8s┃%8s┃", "", "");
  562.     gotoxyWithFullwidth(26, 3);
  563.     printf("┃%8s┃%8s┃", "", "");
  564.     gotoxyWithFullwidth(26, 4);
  565.     printf("┃%8s┃%8s┃", "", "");
  566.     gotoxyWithFullwidth(26, 5);
  567.     printf("┃%8s┃%8s┃", "", "");
  568.     gotoxyWithFullwidth(26, 6);
  569.     printf("┗━━━━┻━━━━┛");
  570.  
  571.     // 下一个,用相应颜色显示
  572.     tetris = gs_uTetrisTable[manager->type[1]][manager->orientation[1]];
  573.     SetConsoleTextAttribute(g_hConsoleOutput, manager->type[1] | 8);
  574.     for (i = 0; i < 16; ++i) {
  575.         gotoxyWithFullwidth((i & 3) + 27, (i >> 2) + 2);
  576.         ((tetris >> i) & 1) ? printf("■") : printf("%2s", "");
  577.     }
  578.  
  579.     // 下下一个,不显示彩色
  580.     tetris = gs_uTetrisTable[manager->type[2]][manager->orientation[2]];
  581.     SetConsoleTextAttribute(g_hConsoleOutput, 8);
  582.     for (i = 0; i < 16; ++i) {
  583.         gotoxyWithFullwidth((i & 3) + 32, (i >> 2) + 2);
  584.         ((tetris >> i) & 1) ? printf("■") : printf("%2s", "");
  585.     }
  586. }
  587.  
  588. // =============================================================================
  589. // 显示得分信息
  590. void printScore(const TetrisManager *manager) {
  591.     static const char *tetrisName = "ITLJZSO";
  592.     int8_t i;
  593.  
  594.     SetConsoleTextAttribute(g_hConsoleOutput, 0xE);
  595.  
  596.     gotoxyWithFullwidth(2, 2);
  597.     printf("■得分:%u", manager->score);
  598.  
  599.     gotoxyWithFullwidth(1, 6);
  600.     printf("■消行总数:%u", manager->erasedTotal);
  601.     for (i = 0; i < 4; ++i) {
  602.         gotoxyWithFullwidth(2, 8 + i);
  603.         printf("□消%d:%u", i + 1, manager->erasedCount[i]);
  604.     }
  605.  
  606.     gotoxyWithFullwidth(1, 15);
  607.     printf("■方块总数:%u", manager->tetrisTotal);
  608.  
  609.     for (i = 0; i < 7; ++i) {
  610.         gotoxyWithFullwidth(2, 17 + i);
  611.         printf("□%c形:%u", tetrisName[i], manager->tetrisCount[i]);
  612.     }
  613. }
  614.  
  615. // =============================================================================
  616. // 显示提示信息
  617. void printPrompting() {
  618.     SetConsoleTextAttribute(g_hConsoleOutput, 0xB);
  619.     gotoxyWithFullwidth(26, 10);
  620.     printf("■控制:");
  621.     gotoxyWithFullwidth(27, 12);
  622.     printf("□向左移动:← A 4");
  623.     gotoxyWithFullwidth(27, 13);
  624.     printf("□向右移动:→ D 6");
  625.     gotoxyWithFullwidth(27, 14);
  626.     printf("□向下移动:↓ S 2");
  627.     gotoxyWithFullwidth(27, 15);
  628.     printf("□顺时针转:↑ W 8");
  629.     gotoxyWithFullwidth(27, 16);
  630.     printf("□逆时针转:0");
  631.     gotoxyWithFullwidth(27, 17);
  632.     printf("□直接落地:空格");
  633.     gotoxyWithFullwidth(27, 18);
  634.     printf("□暂停游戏:回车");
  635.     gotoxyWithFullwidth(25, 23);
  636.     printf("■By: wohaaitinciu 12.12.29");
  637. }
  638.  
  639. // =============================================================================
  640. // 运行游戏
  641. void runGame(TetrisManager *manager, TetrisControl *control) {
  642.     clock_t clockLast, clockNow;
  643.  
  644.     clockLast = clock();  // 计时
  645.     printTetrisPool(manager, control);  // 显示游戏池
  646.  
  647.     while (!manager->dead)  // 没挂
  648.     {
  649.         while (_kbhit())  // 有键按下
  650.         {
  651.             keydownControl(manager, control, _getch());  // 处理按键
  652.         }
  653.  
  654.         if (!control->pause)  // 未暂停
  655.         {
  656.             clockNow = clock();  // 计时
  657.             // 两次记时的间隔超过0.45秒
  658.             if (clockNow - clockLast > 0.45F * CLOCKS_PER_SEC ) {
  659.                 clockLast = clockNow;
  660.                 keydownControl(manager, control, 80);  // 方块往下移
  661.             }
  662.         }
  663.     }
  664. }
  665.  
  666. // =============================================================================
  667. // 再来一次
  668. bool ifPlayAgain() {
  669.     int ch;
  670.  
  671.     SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);
  672.     gotoxyWithFullwidth(15, 10);
  673.     printf("游戏结束");
  674.     gotoxyWithFullwidth(13, 11);
  675.     printf("按Y重玩,按N退出");
  676.  
  677.     do {
  678.         ch = _getch();
  679.         if (ch == 'Y' || ch == 'y') {
  680.             return true;
  681.         } else if (ch == 'N' || ch == 'n') {
  682.             return false;
  683.         }
  684.     } while (1);
  685. }
  686.  

Reply to "Console version Tetris written in C language (bit operation implementation)"

Here you can reply to the paste above

captcha

https://burned.cc - Burn After Reading Website