当前位置: 首页 > news >正文

俄罗斯方块的代码实现

文章目录

  • 首先是头文件的引入部分
  • 接下来是一些预处理指令
  • 接下来定义了两个结构体:
  • 接下来是全局变量`g_hConsoleOutput`,用于存储控制台输出句柄。
  • 之后是一系列函数的声明
  • 最后是`main`函数
  • 源码

首先是头文件的引入部分

包括stdio.hstring.hstdlib.htime.hconio.hwindows.h。这些头文件提供标准输入输出、字符串处理、内存管理、时间处理、控制台输入输出和Windows系统相关的函数

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <windows.h>

在这里插入图片描述

接下来是一些预处理指令

主要根据不同的编译器版本来定义一些与特定环境相关的数据类型和宏定义。

#ifdef _MSC_VER // M$的编译器要给予特殊照顾#if _MSC_VER <= 1200 // VC6及以下版本#error 你是不是还在用VC6呐?!#else // VC6以上版本#if _MSC_VER >= 1600 // 据说VC10及以上版本有stdint.h了#include <stdint.h>#else // VC10以下版本,自己定义int8_t和uint16_ttypedef signed char int8_t;typedef unsigned short uint16_t;#endif#ifndef __cplusplus typedef int bool;#define true 1#define false 0#endif#endif
#else #include <stdint.h>#ifndef __cplusplus // 不用C++编译,需要stdbool.h里的bool#include <stdbool.h>#endif
#endif//=============================================================================
// 7种方块的4旋转状态(4位为一行)
static const uint16_t gs_uTetrisTable[7][4] =
{{ 0x00F0U, 0x2222U, 0x00F0U, 0x2222U }, // I型{ 0x0072U, 0x0262U, 0x0270U, 0x0232U }, // T型{ 0x0223U, 0x0074U, 0x0622U, 0x0170U }, // L型{ 0x0226U, 0x0470U, 0x0322U, 0x0071U }, // J型{ 0x0063U, 0x0264U, 0x0063U, 0x0264U }, // Z型{ 0x006CU, 0x0462U, 0x006CU, 0x0462U }, // S型{ 0x0660U, 0x0660U, 0x0660U, 0x0660U } // O型
};// =============================================================================
// 初始状态的游戏池
// 每个元素表示游戏池的一行,下标大的是游戏池底部
// 两端各置2个1,底部2全置为1,便于进行碰撞检测
// 这样一来游戏池的宽度为12列
// 如果想要传统的10列,只需多填两个1即可(0xE007),当然显示相关部分也要随之改动
// 当某个元素为0xFFFFU时,说明该行已被填满
// 顶部4行用于给方块,不显示出来
// 再除去底部2行,显示出来的游戏池高度为22行
static const uint16_t gs_uInitialTetrisPool[28] =
{0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xFFFFU, 0xFFFFU
};#define COL_BEGIN 2
#define COL_END 14
#define ROW_BEGIN 4
#define ROW_END 26// 

接下来定义了两个结构体:

TetrisManagerTetrisControl
TetrisManager结构体存储了游戏的相关数据,包括游戏池、当前方块的坐标、下一个方块的类型和旋转状态、得分、已消行数等。
TetrisControl结构体存储了与控制相关的数据,包括暂停状态、旋转方向、移动方向、游戏池中每个方块的颜色等。


typedef struct TetrisManager // 这个结构体存储游戏相关数据
{uint16_t pool[28]; // 游戏池int8_t x; // 当前方块x坐标,此处坐标为方块左上角坐标int8_t y; // 当前方块y坐标int8_t type[3]; // 当前、下一个和下下一个方块类型int8_t orientation[3]; // 当前、下一个和下下一个方块旋转状态unsigned score; // 得分unsigned erasedCount[4]; // 消行数unsigned erasedTotal; // 消行总数unsigned tetrisCount[7]; // 各方块数unsigned tetrisTotal; // 方块总数bool dead; // 挂
} TetrisManager;// =============================================================================
typedef struct TetrisControl // 这个结构体存储控制相关数据
{bool pause; // 暂停bool clockwise; // 旋转方向:顺时针为trueint8_t direction; // 移动方向:0向左移动 1向右移动// 游戏池内每格的颜色// 由于此版本是彩色的,仅用游戏池数据无法存储颜色信息// 当然,如果只实现单色版的,就没必要用这个数组了int8_t color[28][16];
} TetrisControl;

接下来是全局变量g_hConsoleOutput,用于存储控制台输出句柄。

HANDLE g_hConsoleOutput; // 控制台输出句柄

之后是一系列函数的声明

用于初始化游戏、重新开始游戏、给方块、碰撞检测、方块移动、旋转方块、消行检测、按键控制等。

// 函数声明
// 如果使用全局变量方式实现,就没必要传参了
void initGame(TetrisManager *manager, TetrisControl *control); // 初始化游戏
void restartGame(TetrisManager *manager, TetrisControl *control); // 重新开始游戏
void giveTetris(TetrisManager *manager); // 给一个方块
bool checkCollision(const TetrisManager *manager); // 碰撞检测
void insertTetris(TetrisManager *manager); // 插入方块
void removeTetris(TetrisManager *manager); // 移除方块
void horzMoveTetris(TetrisManager *manager, TetrisControl *control); // 水平移动方块
void moveDownTetris(TetrisManager *manager, TetrisControl *control); // 向下移动方块
void rotateTetris(TetrisManager *manager, TetrisControl *control); // 旋转方块
void dropDownTetris(TetrisManager *manager, TetrisControl *control); // 方块直接落地
bool checkErasing(TetrisManager *manager, TetrisControl *control); // 消行检测
void keydownControl(TetrisManager *manager, TetrisControl *control, int key); // 键按下
void setPoolColor(const TetrisManager *manager, TetrisControl *control); // 设置颜色
void gotoxyWithFullwidth(short x, short y); // 以全角定位
void printPoolBorder(); // 显示游戏池边界
void printTetrisPool(const TetrisManager *manager, const TetrisControl *control); // 显示游戏池
void printCurrentTetris(const TetrisManager *manager, const TetrisControl *control); // 显示当前方块
void printNextTetris(const TetrisManager *manager); // 显示下一个和下下一个方块
void printScore(const TetrisManager *manager); // 显示得分信息
void runGame(TetrisManager *manager, TetrisControl *control); // 运行游戏
void printPrompting(); // 显示提示信息
bool ifPlayAgain(); // 再来一次

最后是main函数

主函数main()是程序的入口点,它包含了游戏的主要流程控制逻辑。

  1. 声明了两个结构体变量,tetrisManagertetrisControl,用于存储游戏的数据和控制信息。

int main()
{TetrisManager tetrisManager;TetrisControl tetrisControl;initGame(&tetrisManager, &tetrisControl); // 初始化游戏do{printPrompting(); // 显示提示信息printPoolBorder(); // 显示游戏池边界runGame(&tetrisManager, &tetrisControl); // 运行游戏if (ifPlayAgain()) // 再来一次{SetConsoleTextAttribute(g_hConsoleOutput, 0x7);system("cls"); // 清屏restartGame(&tetrisManager, &tetrisControl); // 重新开始游戏}else{break;}} while (1);gotoxyWithFullwidth(0, 0);CloseHandle(g_hConsoleOutput);return 0;
}
  1. 调用initGame()函数进行游戏的初始化。initGame()函数主要完成以下操作:
    • 设置控制台输出句柄,隐藏光标。
    • 设置控制台的标题为“俄罗斯方块控制台版
    • 调用restartGame()函数重新开始游戏。

void initGame(TetrisManager *manager, TetrisControl *control)
{CONSOLE_CURSOR_INFO cursorInfo = { 1, FALSE }; // 光标信息g_hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); // 获取控制台输出句柄SetConsoleCursorInfo(g_hConsoleOutput, &cursorInfo); // 设置光标隐藏SetConsoleTitleA("俄罗斯方块控制台版");restartGame(manager, control);
}// 重新开始游戏
void restartGame(TetrisManager *manager, TetrisControl *control)
{memset(manager, 0, sizeof(TetrisManager)); // 全部置0// 初始化游戏池memcpy(manager->pool, gs_uInitialTetrisPool, sizeof(uint16_t [28]));srand((unsigned)time(NULL)); // 设置随机种子manager->type[1] = rand() % 7; // 下一个manager->orientation[1] = rand() & 3;manager->type[2] = rand() % 7; // 下下一个manager->orientation[2] = rand() & 3;memset(control, 0, sizeof(TetrisControl)); // 全部置0giveTetris(manager); // 给下一个方块setPoolColor(manager, control); // 设置颜色
}
  1. 使用一个do-while循环,循环条件为1,表示无限循环,直到break跳出循环。
    • 调用printPrompting()函数,提示用户游戏操作。
    • 调用printPoolBorder()函数显示游戏池边界。
    • 调用runGame()函数运行游戏。
    • 调用ifPlayAgain()函数询问玩玩家是否再次玩游戏。
// 显示游戏池边界
void printPoolBorder()
{
int8_t y;SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);
for (y = ROW_BEGIN; y < ROW_END; ++y) // 不显示顶部4行和底部2行
{
gotoxyWithFullwidth(10, y - 3);
printf("%2s", "");
gotoxyWithFullwidth(23, y - 3);
printf("%2s", "");
}gotoxyWithFullwidth(10, y - 3); // 底部边界
printf("%28s", "");
}// 定位到游戏池中的方格
#define gotoxyInPool(x, y) gotoxyWithFullwidth(x + 9, y - 3)// =============================================================================

// 显示游戏池
void printTetrisPool(const TetrisManager *manager, const TetrisControl *control)
{int8_t x, y;for (y = ROW_BEGIN; y < ROW_END; ++y) // 不显示顶部4行和底部2行{gotoxyInPool(2, y); // 定点到游戏池中的方格for (x = COL_BEGIN; x < COL_END; ++x) // 不显示左右边界{if ((manager->pool[y] >> x) & 1) // 游戏池该方格有方块{// 用相应颜色,显示一个实心方块SetConsoleTextAttribute(g_hConsoleOutput, control->color[y][x]);printf("■");}else // 没有方块,显示空白{SetConsoleTextAttribute(g_hConsoleOutput, 0);printf("%2s", "");}}}
}
// 显示当前方块
void printCurrentTetris(const TetrisManager *manager, const TetrisControl *control)
{int8_t x, y;// 显示当前方块是在移动后调用的,为擦去移动前的方块,需要扩展显示区域// 由于不可能向上移动,故不需要向下扩展y = (manager->y > ROW_BEGIN) ? (manager->y - 1) : ROW_BEGIN; // 向上扩展一格for (; y < ROW_END && y < manager->y + 4; ++y){x = (manager->x > COL_BEGIN) ? (manager->x - 1) : COL_BEGIN; // 向左扩展一格for (; x < COL_END && x < manager->x + 5; ++x) // 向右扩展一格{gotoxyInPool(x, y); // 定点到游戏池中的方格if ((manager->pool[y] >> x) & 1) // 游戏池该方格有方块{// 用相应颜色,显示一个实心方块SetConsoleTextAttribute(g_hConsoleOutput, control->color[y][x]);printf("■");}else // 没有方块,显示空白{SetConsoleTextAttribute(g_hConsoleOutput, 0);printf("%2s", "");}}}
}
// 显示下一个和下下一个方块
void printNextTetris(const TetrisManager *manager)
{int8_t i;uint16_t tetris;// 边框SetConsoleTextAttribute(g_hConsoleOutput, 0xF);gotoxyWithFullwidth(26, 1);printf("┏━━━━┳━━━━┓");gotoxyWithFullwidth(26, 2);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 3);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 4);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 5);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 6);printf("┗━━━━┻━━━━┛");// 下一个,用相应颜色显示tetris = gs_uTetrisTable[manager->type[1]][manager->orientation[1]];SetConsoleTextAttribute(g_hConsoleOutput, manager->type[1] | 8);for (i = 0; i < 16; ++i){gotoxyWithFullwidth((i & 3) + 27, (i >> 2) + 2);((tetris >> i) & 1) ? printf("■") : printf("%2s", "");}// 下下一个,不显示彩色tetris = gs_uTetrisTable[manager->type[2]][manager->orientation[2]];SetConsoleTextAttribute(g_hConsoleOutput, 8);for (i = 0; i < 16; ++i){gotoxyWithFullwidth((i & 3) + 32, (i >> 2) + 2);((tetris >> i) & 1) ? printf("■") : printf("%2s", "");}
}
// 显示得分信息
void printScore(const TetrisManager *manager)
{static const char *tetrisName = "ITLJZSO";int8_t i;SetConsoleTextAttribute(g_hConsoleOutput, 0xE);gotoxyWithFullwidth(2, 2);printf("■得分:%u", manager->score);gotoxyWithFullwidth(1, 6);printf("■消行总数:%u", manager->erasedTotal);for (i = 0; i < 4; ++i){gotoxyWithFullwidth(2, 8 + i);printf("□消%d:%u", i + 1, manager->erasedCount[i]);}gotoxyWithFullwidth(1, 15);printf("■方块总数:%u", manager->tetrisTotal);for (i = 0; i < 7; ++i){gotoxyWithFullwidth(2, 17 + i);printf("□%c形:%u", tetrisName[i], manager->tetrisCount[i]);}
}
// 显示提示信息
void printPrompting()
{SetConsoleTextAttribute(g_hConsoleOutput, 0xB);gotoxyWithFullwidth(26, 10);printf("■控制:");gotoxyWithFullwidth(27, 12);printf("□向左移动:← A 4");gotoxyWithFullwidth(27, 13);printf("□向右移动:→ D 6");gotoxyWithFullwidth(27, 14);printf("□向下移动:↓ S 2");gotoxyWithFullwidth(27, 15);printf("□顺时针转:↑ W 8");gotoxyWithFullwidth(27, 16);printf("□逆时针转:0");gotoxyWithFullwidth(27, 17);printf("□直接落地:空格");gotoxyWithFullwidth(27, 18);printf("□暂停游戏:回车");gotoxyWithFullwidth(25, 23);printf("■By:muchunfeng");
}

// 运行游戏
void runGame(TetrisManager *manager, TetrisControl *control)
{clock_t clockLast, clockNow;clockLast = clock(); // 计时printTetrisPool(manager, control); // 显示游戏池while (!manager->dead) // 没挂{while (_kbhit()) // 有键按下{keydownControl(manager, control, _getch()); // 处理按键}if (!control->pause) // 未暂停{clockNow = clock(); // 计时// 两次记时的间隔超过0.45秒if (clockNow - clockLast > 0.45F * CLOCKS_PER_SEC){clockLast = clockNow;keydownControl(manager, control, 80); // 方块往下移}}}
}
  1. ifPlayAgain()函数返回true的情况下,即玩家选择再次玩游戏,执行以下操作:

    • 使用SetConsoleTextAttribute()函数设置控制台文本颜色为默认颜色(0x7)。
    • 调用system("cls")清屏,清除上一局游戏的画面。
    • 调用restartGame()函数重新开始游戏。
  2. ifPlayAgain()函数返回false的情况下,即玩家选择退出游戏,跳出循环。

  3. 在循环结束后,调用gotoxyWithFullwidth(0, 0)将光标定位到控制台的左上角。

  4. 最后调用CloseHandle(g_hConsoleOutput)关闭控制台输出句柄。

// 再来一次
bool ifPlayAgain()
{int ch;SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);gotoxyWithFullwidth(15, 10);printf("游戏结束");gotoxyWithFullwidth(13, 11);printf("按Y重玩,按N退出");do{ch = _getch();if (ch == 'Y' || ch == 'y'){return true;}else if (ch == 'N' || ch == 'n'){return false;}} while (1);
}

源码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <windows.h>#ifdef _MSC_VER #if _MSC_VER <= 1200 // VC6及以下版本#error 你是不是还在用VC6?!#else // VC6以上版本#if _MSC_VER >= 1600 // 据说VC10及以上版本有stdint.h了#include <stdint.h>#else // VC10以下版本,自己定义int8_t和uint16_ttypedef signed char int8_t;typedef unsigned short uint16_t;#endif#ifndef __cplusplus typedef int bool;#define true 1#define false 0#endif#endif
#else // 其他的编译器都好说#include <stdint.h>#ifndef __cplusplus // 不用C++编译,需要stdbool.h里的bool#include <stdbool.h>#endif
#endif// =============================================================================
// 7种方块的4旋转状态(4位为一行)
static const uint16_t gs_uTetrisTable[7][4] =
{{ 0x00F0U, 0x2222U, 0x00F0U, 0x2222U }, // I型{ 0x0072U, 0x0262U, 0x0270U, 0x0232U }, // T型{ 0x0223U, 0x0074U, 0x0622U, 0x0170U }, // L型{ 0x0226U, 0x0470U, 0x0322U, 0x0071U }, // J型{ 0x0063U, 0x0264U, 0x0063U, 0x0264U }, // Z型{ 0x006CU, 0x0462U, 0x006CU, 0x0462U }, // S型{ 0x0660U, 0x0660U, 0x0660U, 0x0660U } // O型
};// =============================================================================
// 初始状态的游戏池
// 每个元素表示游戏池的一行,下标大的是游戏池底部
// 两端各置2个1,底部2全置为1,便于进行碰撞检测
// 这样一来游戏池的宽度为12列
// 如果想要传统的10列,只需多填两个1即可(0xE007),当然显示相关部分也要随之改动
// 当某个元素为0xFFFFU时,说明该行已被填满
// 顶部4行用于给方块,不显示出来
// 再除去底部2行,显示出来的游戏池高度为22行
static const uint16_t gs_uInitialTetrisPool[28] =
{0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U,0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xC003U, 0xFFFFU, 0xFFFFU
};#define COL_BEGIN 2
#define COL_END 14
#define ROW_BEGIN 4
#define ROW_END 26// =============================================================================
typedef struct TetrisManager // 这个结构体存储游戏相关数据
{uint16_t pool[28]; // 游戏池int8_t x; // 当前方块x坐标,此处坐标为方块左上角坐标int8_t y; // 当前方块y坐标int8_t type[3]; // 当前、下一个和下下一个方块类型int8_t orientation[3]; // 当前、下一个和下下一个方块旋转状态unsigned score; // 得分unsigned erasedCount[4]; // 消行数unsigned erasedTotal; // 消行总数unsigned tetrisCount[7]; // 各方块数unsigned tetrisTotal; // 方块总数bool dead; // 挂
} TetrisManager;// =============================================================================
typedef struct TetrisControl // 这个结构体存储控制相关数据
{bool pause; // 暂停bool clockwise; // 旋转方向:顺时针为trueint8_t direction; // 移动方向:0向左移动 1向右移动// 游戏池内每格的颜色// 由于此版本是彩色的,仅用游戏池数据无法存储颜色信息// 当然,如果只实现单色版的,就没必要用这个数组了int8_t color[28][16];
} TetrisControl;HANDLE g_hConsoleOutput; // 控制台输出句柄// =============================================================================
// 函数声明
// 如果使用全局变量方式实现,就没必要传参了
void initGame(TetrisManager *manager, TetrisControl *control); // 初始化游戏
void restartGame(TetrisManager *manager, TetrisControl *control); // 重新开始游戏
void giveTetris(TetrisManager *manager); // 给一个方块
bool checkCollision(const TetrisManager *manager); // 碰撞检测
void insertTetris(TetrisManager *manager); // 插入方块
void removeTetris(TetrisManager *manager); // 移除方块
void horzMoveTetris(TetrisManager *manager, TetrisControl *control); // 水平移动方块
void moveDownTetris(TetrisManager *manager, TetrisControl *control); // 向下移动方块
void rotateTetris(TetrisManager *manager, TetrisControl *control); // 旋转方块
void dropDownTetris(TetrisManager *manager, TetrisControl *control); // 方块直接落地
bool checkErasing(TetrisManager *manager, TetrisControl *control); // 消行检测
void keydownControl(TetrisManager *manager, TetrisControl *control, int key); // 键按下
void setPoolColor(const TetrisManager *manager, TetrisControl *control); // 设置颜色
void gotoxyWithFullwidth(short x, short y); // 以全角定位
void printPoolBorder(); // 显示游戏池边界
void printTetrisPool(const TetrisManager *manager, const TetrisControl *control); // 显示游戏池
void printCurrentTetris(const TetrisManager *manager, const TetrisControl *control); // 显示当前方块
void printNextTetris(const TetrisManager *manager); // 显示下一个和下下一个方块
void printScore(const TetrisManager *manager); // 显示得分信息
void runGame(TetrisManager *manager, TetrisControl *control); // 运行游戏
void printPrompting(); // 显示提示信息
bool ifPlayAgain(); // 再来一次// =============================================================================
// 主函数
int main()
{TetrisManager tetrisManager;TetrisControl tetrisControl;initGame(&tetrisManager, &tetrisControl); // 初始化游戏do{printPrompting(); // 显示提示信息printPoolBorder(); // 显示游戏池边界runGame(&tetrisManager, &tetrisControl); // 运行游戏if (ifPlayAgain()) // 再来一次{SetConsoleTextAttribute(g_hConsoleOutput, 0x7);system("cls"); // 清屏restartGame(&tetrisManager, &tetrisControl); // 重新开始游戏}else{break;}} while (1);gotoxyWithFullwidth(0, 0);CloseHandle(g_hConsoleOutput);return 0;
}// =============================================================================
// 初始化游戏
void initGame(TetrisManager *manager, TetrisControl *control)
{CONSOLE_CURSOR_INFO cursorInfo = { 1, FALSE }; // 光标信息g_hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); // 获取控制台输出句柄SetConsoleCursorInfo(g_hConsoleOutput, &cursorInfo); // 设置光标隐藏SetConsoleTitleA("俄罗斯方块控制台版");restartGame(manager, control);
}// =============================================================================
// 重新开始游戏
void restartGame(TetrisManager *manager, TetrisControl *control)
{memset(manager, 0, sizeof(TetrisManager)); // 全部置0// 初始化游戏池memcpy(manager->pool, gs_uInitialTetrisPool, sizeof(uint16_t [28]));srand((unsigned)time(NULL)); // 设置随机种子manager->type[1] = rand() % 7; // 下一个manager->orientation[1] = rand() & 3;manager->type[2] = rand() % 7; // 下下一个manager->orientation[2] = rand() & 3;memset(control, 0, sizeof(TetrisControl)); // 全部置0giveTetris(manager); // 给下一个方块setPoolColor(manager, control); // 设置颜色
}// =============================================================================
// 给一个方块
void giveTetris(TetrisManager *manager)
{uint16_t tetris;manager->type[0] = manager->type[1]; // 下一个方块置为当前manager->orientation[0] = manager->orientation[1];manager->type[1] = manager->type[2];// 下下一个置方块为下一个manager->orientation[1] = manager->orientation[2];manager->type[2] = rand() % 7;// 随机生成下下一个方块manager->orientation[2] = rand() & 3;tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]]; // 当前方块// 设置当前方块y坐标,保证刚给出时只显示方块最下面一行// 这种实现使得玩家可以以很快的速度将方块落在不显示出来的顶部4行内if (tetris & 0xF000){manager->y = 0;}else{manager->y = (tetris & 0xFF00) ? 1 : 2;}manager->x = 6; // 设置当前方块x坐标if (checkCollision(manager)) // 检测到碰撞{manager->dead = true; // 标记游戏结束}else // 未检测到碰撞{insertTetris(manager); // 将当前方块加入游戏池}++manager->tetrisTotal; // 方块总数++manager->tetrisCount[manager->type[0]]; // 相应方块数printNextTetris(manager); // 显示下一个方块printScore(manager); // 显示得分信息
}// =============================================================================
// 碰撞检测
bool checkCollision(const TetrisManager *manager)
{// 当前方块uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];uint16_t dest = 0;// 获取当前方块在游戏池中的区域:// 游戏池坐标x y处小方格信息,按低到高存放在16位无符号数中dest |= (((manager->pool[manager->y + 0] >> manager->x) << 0x0) & 0x000F);dest |= (((manager->pool[manager->y + 1] >> manager->x) << 0x4) & 0x00F0);dest |= (((manager->pool[manager->y + 2] >> manager->x) << 0x8) & 0x0F00);dest |= (((manager->pool[manager->y + 3] >> manager->x) << 0xC) & 0xF000);// 若当前方块与目标区域存在重叠(碰撞),则位与的结果不为0return ((dest & tetris) != 0);
}// =============================================================================
// 插入方块
void insertTetris(TetrisManager *manager)
{// 当前方块uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];// 当前方块每4位取出,位或到游戏池相应位置,即完成插入方块manager->pool[manager->y + 0] |= (((tetris >> 0x0) & 0x000F) << manager->x);manager->pool[manager->y + 1] |= (((tetris >> 0x4) & 0x000F) << manager->x);manager->pool[manager->y + 2] |= (((tetris >> 0x8) & 0x000F) << manager->x);manager->pool[manager->y + 3] |= (((tetris >> 0xC) & 0x000F) << manager->x);
}// =============================================================================
// 移除方块
void removeTetris(TetrisManager *manager)
{// 当前方块uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];// 当前方块每4位取出,按位取反后位与到游戏池相应位置,即完成移除方块manager->pool[manager->y + 0] &= ~(((tetris >> 0x0) & 0x000F) << manager->x);manager->pool[manager->y + 1] &= ~(((tetris >> 0x4) & 0x000F) << manager->x);manager->pool[manager->y + 2] &= ~(((tetris >> 0x8) & 0x000F) << manager->x);manager->pool[manager->y + 3] &= ~(((tetris >> 0xC) & 0x000F) << manager->x);
}// =============================================================================
// 设置颜色
void setPoolColor(const TetrisManager *manager, TetrisControl *control)
{// 由于显示游戏池时,先要在游戏池里判断某一方格有方块才显示相应方格的颜色// 这里只作设置即可,没必要清除// 当移动方块或给一个方块时调用int8_t i, x, y;// 当前方块uint16_t tetris = gs_uTetrisTable[manager->type[0]][manager->orientation[0]];for (i = 0; i < 16; ++i){y = (i >> 2) + manager->y; // 待设置的列if (y > ROW_END) // 超过底部限制{break;}x = (i & 3) + manager->x; // 待设置的行if ((tetris >> i) & 1) // 检测的到小方格属于当前方块区域{control->color[y][x] = (manager->type[0] | 8); // 设置颜色}}
}// =============================================================================
// 旋转方块
void rotateTetris(TetrisManager *manager, TetrisControl *control)
{int8_t ori = manager->orientation[0]; // 记录原旋转状态removeTetris(manager); // 移走当前方块// 顺/逆时针旋转manager->orientation[0] = (control->clockwise) ? ((ori + 1) & 3) : ((ori + 3) & 3);if (checkCollision(manager)) // 检测到碰撞{manager->orientation[0] = ori; // 恢复为原旋转状态insertTetris(manager); // 放入当前方块。由于状态没改变,不需要设置颜色}else{insertTetris(manager); // 放入当前方块setPoolColor(manager, control); // 设置颜色printCurrentTetris(manager, control); // 显示当前方块}
}// =============================================================================
// 水平移动方块
void horzMoveTetris(TetrisManager *manager, TetrisControl *control)
{int x = manager->x; // 记录原列位置removeTetris(manager); // 移走当前方块control->direction == 0 ? (--manager->x) : (++manager->x); // 左/右移动if (checkCollision(manager)) // 检测到碰撞{manager->x = x; // 恢复为原列位置insertTetris(manager); // 放入当前方块。由于位置没改变,不需要设置颜色}else{insertTetris(manager); // 放入当前方块setPoolColor(manager, control); // 设置颜色printCurrentTetris(manager, control); // 显示当前方块}
}// =============================================================================
// 向下移动方块
void moveDownTetris(TetrisManager *manager, TetrisControl *control)
{int8_t y = manager->y; // 记录原行位置removeTetris(manager); // 移走当前方块++manager->y; // 向下移动if (checkCollision(manager)) // 检测到碰撞{manager->y = y; // 恢复为原行位置insertTetris(manager); // 放入当前方块。由于位置没改变,不需要设置颜色if (checkErasing(manager, control)) // 检测到消行{printTetrisPool(manager, control); // 显示游戏池}}else{insertTetris(manager); // 放入当前方块setPoolColor(manager, control); // 设置颜色printCurrentTetris(manager, control); // 显示当前方块}
}// =============================================================================
// 方块直接落地
void dropDownTetris(TetrisManager *manager, TetrisControl *control)
{removeTetris(manager); // 移走当前方块for (; manager->y < ROW_END; ++manager->y) // 从上往下{if (checkCollision(manager)) // 检测到碰撞{break;}}--manager->y; // 上移一格当然没有碰撞insertTetris(manager); // 放入当前方块setPoolColor(manager, control); // 设置颜色checkErasing(manager, control); // 检测消行printTetrisPool(manager, control); // 显示游戏池
}// =============================================================================
// 消行检测
bool checkErasing(TetrisManager *manager, TetrisControl *control)
{static const unsigned scores[5] = { 0, 10, 30, 90, 150 }; // 消行得分int8_t count = 0;int8_t k = 0, y = manager->y + 3; // 从下往上检测do{if (y < ROW_END && manager->pool[y] == 0xFFFFU) // 有效区域内且一行已填满{++count;// 消除一行方块memmove(manager->pool + 1, manager->pool, sizeof(uint16_t) * y);// 颜色数组的元素随之移动memmove(control->color[1], control->color[0], sizeof(int8_t [16]) * y);}else{--y;++k;}} while (y >= manager->y && k < 4);manager->erasedTotal += count; // 消行总数manager->score += scores[count]; // 得分if (count > 0){++manager->erasedCount[count - 1]; // 消行}giveTetris(manager); // 给下一个方块setPoolColor(manager, control); // 设置颜色return (count > 0);
}// =============================================================================
// 键按下
void keydownControl(TetrisManager *manager, TetrisControl *control, int key)
{if (key == 13) // 暂停/解除暂停{control->pause = !control->pause;}if (control->pause) // 暂停状态,不作处理{return;}switch (key){case 'w': case 'W': case '8': case 72: // 上control->clockwise = true; // 顺时针旋转rotateTetris(manager, control); // 旋转方块break;case 'a': case 'A': case '4': case 75: // 左control->direction = 0; // 向左移动horzMoveTetris(manager, control); // 水平移动方块break;case 'd': case 'D': case '6': case 77: // 右control->direction = 1; // 向右移动horzMoveTetris(manager, control); // 水平移动方块break;case 's': case 'S': case '2': case 80: // 下moveDownTetris(manager, control); // 向下移动方块break;case ' ': // 直接落地dropDownTetris(manager, control);break;case '0': // 反转control->clockwise = false; // 逆时针旋转rotateTetris(manager, control); // 旋转方块break;default:break;}
}// =============================================================================
// 以全角定位
void gotoxyWithFullwidth(short x, short y)
{static COORD cd;cd.X = (short)(x << 1);cd.Y = y;SetConsoleCursorPosition(g_hConsoleOutput, cd);
}// =============================================================================
// 显示游戏池边界
void printPoolBorder()
{int8_t y;SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);for (y = ROW_BEGIN; y < ROW_END; ++y) // 不显示顶部4行和底部2行{gotoxyWithFullwidth(10, y - 3);printf("%2s", "");gotoxyWithFullwidth(23, y - 3);printf("%2s", "");}gotoxyWithFullwidth(10, y - 3); // 底部边界printf("%28s", "");
}// 定位到游戏池中的方格
#define gotoxyInPool(x, y) gotoxyWithFullwidth(x + 9, y - 3)// =============================================================================
// 显示游戏池
void printTetrisPool(const TetrisManager *manager, const TetrisControl *control)
{int8_t x, y;for (y = ROW_BEGIN; y < ROW_END; ++y) // 不显示顶部4行和底部2行{gotoxyInPool(2, y); // 定点到游戏池中的方格for (x = COL_BEGIN; x < COL_END; ++x) // 不显示左右边界{if ((manager->pool[y] >> x) & 1) // 游戏池该方格有方块{// 用相应颜色,显示一个实心方块SetConsoleTextAttribute(g_hConsoleOutput, control->color[y][x]);printf("■");}else // 没有方块,显示空白{SetConsoleTextAttribute(g_hConsoleOutput, 0);printf("%2s", "");}}}
}// =============================================================================
// 显示当前方块
void printCurrentTetris(const TetrisManager *manager, const TetrisControl *control)
{int8_t x, y;// 显示当前方块是在移动后调用的,为擦去移动前的方块,需要扩展显示区域// 由于不可能向上移动,故不需要向下扩展y = (manager->y > ROW_BEGIN) ? (manager->y - 1) : ROW_BEGIN; // 向上扩展一格for (; y < ROW_END && y < manager->y + 4; ++y){x = (manager->x > COL_BEGIN) ? (manager->x - 1) : COL_BEGIN; // 向左扩展一格for (; x < COL_END && x < manager->x + 5; ++x) // 向右扩展一格{gotoxyInPool(x, y); // 定点到游戏池中的方格if ((manager->pool[y] >> x) & 1) // 游戏池该方格有方块{// 用相应颜色,显示一个实心方块SetConsoleTextAttribute(g_hConsoleOutput, control->color[y][x]);printf("■");}else // 没有方块,显示空白{SetConsoleTextAttribute(g_hConsoleOutput, 0);printf("%2s", "");}}}
}// =============================================================================
// 显示下一个和下下一个方块
void printNextTetris(const TetrisManager *manager)
{int8_t i;uint16_t tetris;// 边框SetConsoleTextAttribute(g_hConsoleOutput, 0xF);gotoxyWithFullwidth(26, 1);printf("┏━━━━┳━━━━┓");gotoxyWithFullwidth(26, 2);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 3);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 4);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 5);printf("┃%8s┃%8s┃", "", "");gotoxyWithFullwidth(26, 6);printf("┗━━━━┻━━━━┛");// 下一个,用相应颜色显示tetris = gs_uTetrisTable[manager->type[1]][manager->orientation[1]];SetConsoleTextAttribute(g_hConsoleOutput, manager->type[1] | 8);for (i = 0; i < 16; ++i){gotoxyWithFullwidth((i & 3) + 27, (i >> 2) + 2);((tetris >> i) & 1) ? printf("■") : printf("%2s", "");}// 下下一个,不显示彩色tetris = gs_uTetrisTable[manager->type[2]][manager->orientation[2]];SetConsoleTextAttribute(g_hConsoleOutput, 8);for (i = 0; i < 16; ++i){gotoxyWithFullwidth((i & 3) + 32, (i >> 2) + 2);((tetris >> i) & 1) ? printf("■") : printf("%2s", "");}
}// =============================================================================
// 显示得分信息
void printScore(const TetrisManager *manager)
{static const char *tetrisName = "ITLJZSO";int8_t i;SetConsoleTextAttribute(g_hConsoleOutput, 0xE);gotoxyWithFullwidth(2, 2);printf("■得分:%u", manager->score);gotoxyWithFullwidth(1, 6);printf("■消行总数:%u", manager->erasedTotal);for (i = 0; i < 4; ++i){gotoxyWithFullwidth(2, 8 + i);printf("□消%d:%u", i + 1, manager->erasedCount[i]);}gotoxyWithFullwidth(1, 15);printf("■方块总数:%u", manager->tetrisTotal);for (i = 0; i < 7; ++i){gotoxyWithFullwidth(2, 17 + i);printf("□%c形:%u", tetrisName[i], manager->tetrisCount[i]);}
}// =============================================================================
// 显示提示信息
void printPrompting()
{SetConsoleTextAttribute(g_hConsoleOutput, 0xB);gotoxyWithFullwidth(26, 10);printf("■控制:");gotoxyWithFullwidth(27, 12);printf("□向左移动:← A 4");gotoxyWithFullwidth(27, 13);printf("□向右移动:→ D 6");gotoxyWithFullwidth(27, 14);printf("□向下移动:↓ S 2");gotoxyWithFullwidth(27, 15);printf("□顺时针转:↑ W 8");gotoxyWithFullwidth(27, 16);printf("□逆时针转:0");gotoxyWithFullwidth(27, 17);printf("□直接落地:空格");gotoxyWithFullwidth(27, 18);printf("□暂停游戏:回车");gotoxyWithFullwidth(25, 23);printf("■By:muchunfeng");
}// =============================================================================
// 运行游戏
void runGame(TetrisManager *manager, TetrisControl *control)
{clock_t clockLast, clockNow;clockLast = clock(); // 计时printTetrisPool(manager, control); // 显示游戏池while (!manager->dead) // 没挂{while (_kbhit()) // 有键按下{keydownControl(manager, control, _getch()); // 处理按键}if (!control->pause) // 未暂停{clockNow = clock(); // 计时// 两次记时的间隔超过0.45秒if (clockNow - clockLast > 0.45F * CLOCKS_PER_SEC){clockLast = clockNow;keydownControl(manager, control, 80); // 方块往下移}}}
}// =============================================================================
// 再来一次
bool ifPlayAgain()
{int ch;SetConsoleTextAttribute(g_hConsoleOutput, 0xF0);gotoxyWithFullwidth(15, 10);printf("游戏结束");gotoxyWithFullwidth(13, 11);printf("按Y重玩,按N退出");do{ch = _getch();if (ch == 'Y' || ch == 'y'){return true;}else if (ch == 'N' || ch == 'n'){return false;}} while (1);
}

end

相关文章:

俄罗斯方块的代码实现

文章目录 首先是头文件的引入部分接下来是一些预处理指令接下来定义了两个结构体&#xff1a;接下来是全局变量g_hConsoleOutput&#xff0c;用于存储控制台输出句柄。之后是一系列函数的声明最后是main函数源码 首先是头文件的引入部分 包括stdio.h、string.h、stdlib.h、tim…...

出海企业哪种组网方案更省事?

对于出海企业而言&#xff0c;建立跨地区的数据传输和协同工作至关重要&#xff0c;以提升运营效率。因此&#xff0c;网络构建变得迫在眉睫。通过构建企业组网&#xff0c;企业能够加强与海外分支、客户和合作伙伴之间的联系&#xff0c;加速海外业务的发展。 然而&#xff0c…...

triton编译学习

一 流程 Triton-MLIR: 从DSL到PTX - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/671434808Superjomns blog | OpenAI/Triton MLIR 迁移工作简介https://superjom...

源码知识付费系统,在线教学平台需要优化什么?

在线教育关于广大的关注者而言属于快捷度非常高的传达途径&#xff0c;尤其是白日没有过多时间的上班族或学习繁忙的学生&#xff0c;均能够通过可靠的在线教育完结自己的目的。如此巨大的市场潜力使得以在线教育为主的公司数量呈现出直线上升的趋势&#xff0c;很多的在线教育…...

后端常用技能:解决java项目前后端传输数据中文出现乱码、问号问题

0. 问题背景 最近做一个解析数据的小工具&#xff0c;本地运行时都正常&#xff0c;发布到服务器上后在导出文件数据时发现中文全部变成了问号&#xff0c;特此记录下问题解决的思路和过程 1. 环境 java 1.8 springboot 2.6.13 额外引入了fastjson&#xff0c;commons-csv等…...

SpringBoot中使用MongoDB

目录 搭建实体类 基本的增删改查操作 分页查询 使用MongoTemplate实现复杂的功能 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> 在ap…...

【TS】入门

创建项目 vscode自动编译ts 生成配置文件 tsc --init 然后发现终端也改变了&#xff1a;...

Apache ECharts

Apache ECharts介绍&#xff1a; Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 官网地址&#xff1a;https://echarts.apache.org/zh/index.html Apache ECh…...

超详细的胎教级Stable Diffusion使用教程(四)

这套课程分为五节课&#xff0c;会系统性的介绍sd的全部功能和实操案例&#xff0c;让你打下坚实牢靠的基础 一、为什么要学Stable Diffusion&#xff0c;它究竟有多强大&#xff1f; 二、三分钟教你装好Stable Diffusion 三、小白快速上手Stable Diffusion 四、Stable dif…...

串口属性中的BM延时计时器问题

如果使用程序修改则需要修改注册表对应位置如下 第一个示例&#xff08;217&#xff09; 第二个示例&#xff08;219&#xff09; 需要注意的事情是修改前必须点查看串口名称&#xff08;例如上图是com5&#xff09; 程序修改&#xff1a; 有没有办法以编程方式更改USB <…...

PyQt6--Python桌面开发(8.QPlainTextEdit纯文本控件)

QPlainTextEdit纯文本控件...

Java | Leetcode Java题解之第83题删除排序链表中的重复元素

题目&#xff1a; 题解&#xff1a; class Solution {public ListNode deleteDuplicates(ListNode head) {if (head null) {return head;}ListNode cur head;while (cur.next ! null) {if (cur.val cur.next.val) {cur.next cur.next.next;} else {cur cur.next;}}return…...

重生奇迹mu再生宝石怎么用有什么用

重生奇迹mu再生宝石有2个用处&#xff1a; 1、在玛雅哥布林处给380装备加PVP属性4追4以上的380级装备,守护宝石一颗,再生宝石一颗,成功得到PVP装备,失败宝石消失,装备无变化&#xff1b; 2、给非套装点强化属性用法跟祝福,灵魂,生命一样直接往装备上敲,成功得到随机强化属性一…...

pdf 文件版面分析--pdfplumber (python 文档解析提取)

pdfplumber 的特点 1、它是一个纯 python 第三方库&#xff0c;适合 python 3.x 版本 2、它用来查看pdf各类信息&#xff0c;能有效提取文本、表格 3、它不支持修改或生成pdf&#xff0c;也不支持对pdf扫描件的处理 import glob import pdfplumber import re from collection…...

PostgreSQL的学习心得和知识总结(一百四十三)|深入理解PostgreSQL数据库之Support event trigger for logoff

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…...

https免费证书获取

获取免费证书的网址&#xff1a; Certbot 1. 进入你的linux系统&#xff0c;先安装snapd&#xff0c; yum install snapd 2. 启动snapd service snapd start 3.安装 Certbot snap install --classic certbot 注意如下出现此错误时&#xff0c;需要先建立snap 软连接后&am…...

C语言 | Leetcode C语言题解之第74题搜索二维矩阵

题目&#xff1a; 题解&#xff1a; bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target) {int m matrixSize, n matrixColSize[0];int low 0, high m * n - 1;while (low < high) {int mid (high - low) / 2 low;int x matrix[mid /…...

杰发科技AC7840——软件Sent_HAL39X

0. 序 使用PWM模拟Sent测试下7840的软件sent功能。 参考链接&#xff1a;SENT协议应用笔记 - TechPlus汽车工坊的文章 - 知乎 SENT协议 1. Sent功能测试 使用提供的软件Sent代码在7840上测试&#xff0c;接收数据OK 2. 参考资料 3. 数据解析 我们个根据上述参考资料尝试解析…...

IOS 开发 - block 使用详解

1.Blobk的定义 block的写法相对难记,不必司机应被,只需要在xcode里打出"inlineBlock"--回车, 系统会自动帮你把基础版写法给你匹配出来 //Block的基础声明//等号""之前是blobk的声明,等号“”后面是block的实现/*returnType:返回类型(void、int、String *…...

BUU-[极客大挑战 2019]Http

考察点 信息收集 http构造请求数据包 题目 解题步骤 参考文章&#xff1a;https://zhuanlan.zhihu.com/p/367051798 查看源代码 发现有一个a标签&#xff0c;但是οnclick"return false"就是点击后不会去跳转到Secret.php的页面 所以我就自己拼接url http://no…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...

数据挖掘是什么?数据挖掘技术有哪些?

目录 一、数据挖掘是什么 二、常见的数据挖掘技术 1. 关联规则挖掘 2. 分类算法 3. 聚类分析 4. 回归分析 三、数据挖掘的应用领域 1. 商业领域 2. 医疗领域 3. 金融领域 4. 其他领域 四、数据挖掘面临的挑战和未来趋势 1. 面临的挑战 2. 未来趋势 五、总结 数据…...

TMC2226超静音步进电机驱动控制模块

目前已经使用TMC2226量产超过20K,发现在静音方面做的还是很不错。 一、TMC2226管脚定义说明 二、原理图及下载地址 一、TMC2226管脚定义说明 引脚编号类型功能OB11电机线圈 B 输出 1BRB2线圈 B 的检测电阻连接端。将检测电阻靠近该引脚连接到地。使用内部检测电阻时,将此引…...