有bug

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <string>
#include <cstdio>

#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#define Sleep(x) usleep((x)*1000)
#endif

using namespace std;

// 跨平台按键检测与读取
#ifdef _WIN32
int key_hit() { return _kbhit(); }
int get_key() { return _getch(); }
#else
int key_hit() {
    struct termios oldt, newt;
    int oldf;
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
    int ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    fcntl(STDIN_FILENO, F_SETFL, oldf);
    if (ch != EOF) {
        ungetc(ch, stdin);
        return 1;
    }
    return 0;
}
int get_key() {
    struct termios oldt, newt;
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    int ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    return ch;
}
#endif

// 跨平台清屏
void cls() {
#ifdef _WIN32
    system("cls");
#else
    system("clear");
#endif
}

// 控制台颜色设置(跨平台)
void color(int c) {
#ifdef _WIN32
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
#else
    switch (c) {
        case 1: printf("\033[31m"); break;  // 红
        case 2: printf("\033[32m"); break;  // 绿
        case 3: printf("\033[33m"); break;  // 黄
        case 4: printf("\033[34m"); break;  // 蓝
        case 5: printf("\033[35m"); break;  // 紫
        case 6: printf("\033[36m"); break;  // 青
        case 7: printf("\033[37m"); break;  // 白
        default: printf("\033[0m"); break; // 重置
    }
#endif
}

void resetColor() { color(0); }

// 光标定位(x列,y行)
void gotoxy(int x, int y) {
#ifdef _WIN32
    COORD pos = {(short)x, (short)y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
#else
    printf("\033[%d;%dH", y + 1, x + 1);
#endif
}

// 隐藏光标
void hideCursor() {
#ifdef _WIN32
    CONSOLE_CURSOR_INFO cci;
    cci.bVisible = FALSE;
    cci.dwSize = 1;
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
#else
    printf("\033[?25l");
#endif
}

// 显示光标(用于游戏结束后恢复)
void showCursor() {
#ifdef _WIN32
    CONSOLE_CURSOR_INFO cci;
    cci.bVisible = TRUE;
    cci.dwSize = 1;
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
#else
    printf("\033[?25h");
#endif
}

//================================================================================
// 贪吃蛇游戏
//================================================================================
#define S_W 24  // 蛇游戏宽度
#define S_H 14  // 蛇游戏高度

struct Point {
    int x, y;
};

Point snake[100];  // 蛇身坐标
int s_len;         // 蛇长度
int fx, fy;        // 食物坐标
int dir;           // 移动方向:1上 2下 3左 4右
bool s_over;       // 游戏结束标记

// 初始化贪吃蛇
void s_init() {
    s_over = false;
    s_len = 3;
    // 初始蛇身位置(中间)
    snake[0] = {S_W / 2, S_H / 2};
    snake[1] = {S_W / 2 - 1, S_H / 2};
    snake[2] = {S_W / 2 - 2, S_H / 2};
    dir = 4;  // 初始向右
    srand(time(0));
    // 随机生成食物
    fx = rand() % S_W;
    fy = rand() % S_H;
}

// 绘制贪吃蛇游戏界面
void s_draw() {
    gotoxy(0, 0);
    // 绘制上边框
    color(3);
    for (int i = 0; i < S_W + 2; i++) printf("#");
    resetColor();
    printf("\n");

    // 绘制游戏区域
    for (int y = 0; y < S_H; y++) {
        color(3);
        printf("#");  // 左边框
        resetColor();
        for (int x = 0; x < S_W; x++) {
            bool isSnake = false;
            // 绘制蛇身
            for (int i = 0; i < s_len; i++) {
                if (snake[i].x == x && snake[i].y == y) {
                    color(2);
                    printf("O");
                    resetColor();
                    isSnake = true;
                    break;
                }
            }
            // 不是蛇身则判断是否是食物
            if (!isSnake) {
                if (x == fx && y == fy) {
                    color(1);
                    printf("*");
                    resetColor();
                } else {
                    printf(" ");
                }
            }
        }
        color(3);
        printf("#\n");  // 右边框
        resetColor();
    }

    // 绘制下边框
    color(3);
    for (int i = 0; i < S_W + 2; i++) printf("#");
    resetColor();
    // 显示分数(长度-初始长度)和操作提示
    printf("\nScore: %d   W/A/S/D移动  ESC退出", s_len - 3);
}

// 处理贪吃蛇输入
void s_input() {
    if (key_hit()) {
        int c = get_key();
        if (c == 27) { s_over = true; return; }  // ESC退出
        // 方向控制(防止反向移动)
        if ((c == 'w' || c == 'W') && dir != 2) dir = 1;  // 上
        if ((c == 's' || c == 'S') && dir != 1) dir = 2;  // 下
        if ((c == 'a' || c == 'A') && dir != 4) dir = 3;  // 左
        if ((c == 'd' || c == 'D') && dir != 3) dir = 4;  // 右
    }
}

// 贪吃蛇逻辑处理
void s_logic() {
    // 蛇身跟随移动(从后往前复制)
    for (int i = s_len - 1; i > 0; i--) snake[i] = snake[i - 1];
    // 蛇头移动
    switch (dir) {
        case 1: snake[0].y--; break;  // 上
        case 2: snake[0].y++; break;  // 下
        case 3: snake[0].x--; break;  // 左
        case 4: snake[0].x++; break;  // 右
    }
    // 边界检测
    if (snake[0].x < 0 || snake[0].x >= S_W || snake[0].y < 0 || snake[0].y >= S_H) {
        s_over = true;
    }
    // 撞自己检测
    for (int i = 1; i < s_len; i++) {
        if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
            s_over = true;
            break;
        }
    }
    // 吃食物逻辑
    if (snake[0].x == fx && snake[0].y == fy) {
        s_len++;  // 长度+1
        // 重新生成食物
        fx = rand() % S_W;
        fy = rand() % S_H;
    }
}

// 启动贪吃蛇游戏
void playSnake() {
    cls();
    hideCursor();
    s_init();
    while (!s_over) {
        s_draw();
        s_input();
        s_logic();
        Sleep(130);  // 控制游戏速度
    }
    // 游戏结束
    cls();
    color(1);
    printf("==== Game Over ====\n");
    resetColor();
    printf("Score: %d\nPress any key back...\n", s_len - 3);
    // 清空按键缓冲区
    while (key_hit()) get_key();
    // 等待按键
    while (!key_hit()) Sleep(50);
    get_key();
    showCursor();
}

//================================================================================
// 俄罗斯方块游戏
//================================================================================
#define T_W 10  // 方块宽度
#define T_H 20  // 方块高度

int t_map[T_H][T_W] = {0};  // 游戏地图(0空 1有方块)
// 7种方块形状(I/L/J/Z/S/T/O)
int shape[7][4][4] = {
    {{0,0,0,0},{1,1,1,1},{0,0,0,0},{0,0,0,0}},  // I
    {{0,1,0,0},{0,1,0,0},{0,1,1,0},{0,0,0,0}},  // L
    {{0,1,0,0},{0,1,0,0},{1,1,0,0},{0,0,0,0}},  // J
    {{0,0,1,0},{0,1,1,0},{0,1,0,0},{0,0,0,0}},  // Z
    {{0,1,0,0},{0,1,1,0},{0,0,1,0},{0,0,0,0}},  // S
    {{0,1,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},  // T
    {{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}}   // O
};

int cx, cy;     // 当前方块坐标
int t_type;     // 当前方块类型
bool t_over;    // 游戏结束标记

// 绘制俄罗斯方块界面
void t_draw() {
    gotoxy(0, 0);
    // 上边框
    color(6);
    for (int i = 0; i < T_W + 2; i++) printf("#");
    resetColor();
    printf("\n");

    // 游戏区域
    for (int y = 0; y < T_H; y++) {
        color(6);
        printf("#");  // 左边框
        resetColor();
        for (int x = 0; x < T_W; x++) {
            // 绘制已固定的方块
            if (t_map[y][x]) {
                color(5);
                printf("■");
                resetColor();
            } else {
                // 绘制当前移动的方块
                bool isCurrent = false;
                for (int i = 0; i < 4; i++) {
                    for (int j = 0; j < 4; j++) {
                        if (shape[t_type][i][j] && cx + j == x && cy + i == y) {
                            color(4);
                            printf("■");
                            resetColor();
                            isCurrent = true;
                            break;
                        }
                    }
                    if (isCurrent) break;
                }
                if (!isCurrent) printf(" ");
            }
        }
        color(6);
        printf("#\n");  // 右边框
        resetColor();
    }

    // 下边框
    color(6);
    for (int i = 0; i < T_W + 2; i++) printf("#");
    resetColor();
    // 操作提示
    printf("\nW:旋转  A:左移  D:右移  S:下移  ESC:退出");
}

// 检测方块移动/旋转是否合法
bool t_check(int x, int y, int ty) {
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (shape[ty][i][j]) {
                int nx = x + j, ny = y + i;
                // 边界检测
                if (nx < 0 || nx >= T_W || ny >= T_H) return false;
                // 碰撞检测(ny>=0才检测,避免方块在顶部外的情况)
                if (ny >= 0 && t_map[ny][nx]) return false;
            }
        }
    }
    return true;
}

// 锁定方块到地图
void t_lock() {
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (shape[t_type][i][j]) {
                t_map[cy + i][cx + j] = 1;
            }
        }
    }
    // 消除满行
    for (int y = T_H - 1; y >= 0; y--) {
        bool isFull = true;
        for (int x = 0; x < T_W; x++) {
            if (!t_map[y][x]) {
                isFull = false;
                break;
            }
        }
        if (isFull) {
            // 行上移
            for (int yy = y; yy > 0; yy--) {
                memcpy(t_map[yy], t_map[yy - 1], T_W * sizeof(int));
            }
            memset(t_map[0], 0, T_W * sizeof(int));  // 最上行清空
            y++;  // 重新检查当前行
        }
    }
}

// 生成新方块
void t_new() {
    cx = T_W / 2 - 2;
    cy = 0;
    t_type = rand() % 7;
    // 无法生成新方块则游戏结束
    if (!t_check(cx, cy, t_type)) t_over = true;
}

// 移动方块
void t_move(int dx, int dy) {
    if (t_check(cx + dx, cy + dy, t_type)) {
        cx += dx; 
        cy += dy;
    } else if (dy != 0) {
        // 下移失败则锁定方块并生成新方块
        t_lock();
        t_new();
    }
}

// 旋转方块
void t_rot() {
    // 保存原形状
    int tmp[4][4];
    memcpy(tmp, shape[t_type], sizeof(tmp));
    // 顺时针旋转90度
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            shape[t_type][i][j] = tmp[3 - j][i];
        }
    }
    // 旋转后不合法则恢复原形状
    if (!t_check(cx, cy, t_type)) {
        memcpy(shape[t_type], tmp, sizeof(tmp));
    }
}

// 启动俄罗斯方块游戏
void playTetris() {
    cls();
    hideCursor();
    memset(t_map, 0, sizeof(t_map));
    t_over = false;
    srand(time(0));
    t_new();
    int cnt = 0;
    while (!t_over) {
        t_draw();
        // 处理输入
        if (key_hit()) {
            int c = get_key();
            if (c == 27) break;  // ESC退出
            if (c == 'a' || c == 'A') t_move(-1, 0);  // 左移
            if (c == 'd' || c == 'D') t_move(1, 0);   // 右移
            if (c == 's' || c == 'S') t_move(0, 1);   // 下移
            if (c == 'w' || c == 'W') t_rot();        // 旋转
        }
        // 自动下落
        if (++cnt % 18 == 0) t_move(0, 1);
        Sleep(30);
    }
    // 游戏结束
    cls();
    color(1);
    printf("==== Game Over ====\n");
    resetColor();
    printf("Press any key back...\n");
    while (key_hit()) get_key();
    while (!key_hit()) Sleep(50);
    get_key();
    showCursor();
}

//================================================================================
// 扫雷游戏
//================================================================================
#define M_W 9   // 扫雷宽度
#define M_H 9   // 扫雷高度
#define M_NUM 10 // 地雷数量

int m_map[M_H][M_W];   // 地图:-1=地雷 0-8=周围地雷数
int m_show[M_H][M_W];  // 显示状态:0=未点开 1=已点开 2=标记
bool m_over, m_win;    // 游戏结束/胜利标记

// 初始化扫雷
void m_init() {
    memset(m_map, 0, sizeof(m_map));
    memset(m_show, 0, sizeof(m_show));
    m_over = m_win = false;
    srand(time(0));
    
    // 随机生成地雷
    int c = 0;
    while (c < M_NUM) {
        int x = rand() % M_W, y = rand() % M_H;
        if (m_map[y][x] != -1) {
            m_map[y][x] = -1;
            c++;
        }
    }
    
    // 计算每个格子周围地雷数
    for (int y = 0; y < M_H; y++) {
        for (int x = 0; x < M_W; x++) {
            if (m_map[y][x] != -1) {
                int cnt = 0;
                // 遍历8个方向
                for (int dy = -1; dy <= 1; dy++) {
                    for (int dx = -1; dx <= 1; dx++) {
                        if (dx == 0 && dy == 0) continue;
                        int nx = x + dx, ny = y + dy;
                        if (nx >= 0 && nx < M_W && ny >= 0 && ny < M_H && m_map[ny][nx] == -1) {
                            cnt++;
                        }
                    }
                }
                m_map[y][x] = cnt;
            }
        }
    }
}

// 递归展开空白区域
void m_open(int x, int y) {
    // 边界/已点开/标记检测
    if (x < 0 || x >= M_W || y < 0 || y >= M_H || m_show[y][x] != 0) return;
    
    m_show[y][x] = 1;  // 标记为已点开
    
    // 空白格(0)则递归展开周围
    if (m_map[y][x] == 0) {
        for (int dy = -1; dy <= 1; dy++) {
            for (int dx = -1; dx <= 1; dx++) {
                if (dx == 0 && dy == 0) continue;
                m_open(x + dx, y + dy);
            }
        }
    }
    
    // 检查胜利条件:所有非地雷格子都已点开
    int opened = 0;
    for (int y = 0; y < M_H; y++) {
        for (int x = 0; x < M_W; x++) {
            if (m_map[y][x] != -1 && m_show[y][x] == 1) {
                opened++;
            }
        }
    }
    if (opened == M_W * M_H - M_NUM) {
        m_win = true;
        m_over = true;
    }
}

// 绘制扫雷界面
void m_draw() {
    gotoxy(0, 0);
    color(3);
    printf("==== 扫雷 ====\n");
    resetColor();
    
    // 绘制列号
    printf("  ");
    for (int x = 0; x < M_W; x++) {
        printf("%d ", x + 1);
    }
    printf("\n");
    
    // 绘制游戏区域
    for (int y = 0; y < M_H; y++) {
        // 行号
        color(3);
        printf("%d ", y + 1);
        resetColor();
        
        for (int x = 0; x < M_W; x++) {
            if (m_over && m_map[y][x] == -1) {
                // 游戏结束显示地雷
                color(1);
                printf("* ");
                resetColor();
            } else if (m_show[y][x] == 2) {
                // 标记
                color(5);
                printf("! ");
                resetColor();
            } else if (m_show[y][x] == 1) {
                // 已点开
                if (m_map[y][x] == 0) {
                    printf("  ");
                } else {
                    color(4);
                    printf("%d ", m_map[y][x]);
                    resetColor();
                }
            } else {
                // 未点开
                color(7);
                printf("# ");
                resetColor();
            }
        }
        printf("\n");
    }
    
    // 提示信息
    if (m_win) {
        color(2);
        printf("恭喜胜利!ESC退出\n");
        resetColor();
    } else if (m_over) {
        color(1);
        printf("踩到地雷!ESC退出\n");
        resetColor();
    } else {
        printf("操作:W/A/S/D移动光标  空格点开  F标记  ESC退出\n");
    }
}

// 扫雷输入处理
void m_input(int &cx, int &cy) {
    if (key_hit()) {
        int c = get_key();
        switch (c) {
            case 27: m_over = true; break;  // ESC退出
            case 'w': case 'W': if (cy > 0) cy--; break;  // 上
            case 's': case 'S': if (cy < M_H - 1) cy++; break;  // 下
            case 'a': case 'A': if (cx > 0) cx--; break;  // 左
            case 'd': case 'D': if (cx < M_W - 1) cx++; break;  // 右
            case ' ':  // 空格点开
                if (m_show[cy][cx] == 0) {
                    if (m_map[cy][cx] == -1) {
                        m_over = true;  // 踩雷
                    } else {
                        m_open(cx, cy);  // 展开区域
                    }
                }
                break;
            case 'f': case 'F':  // 标记/取消标记
                if (m_show[cy][cx] == 0) {
                    m_show[cy][cx] = 2;
                } else if (m_show[cy][cx] == 2) {
                    m_show[cy][cx] = 0;
                }
                break;
        }
    }
}

// 启动扫雷游戏
void playMinesweeper() {
    cls();
    hideCursor();
    m_init();
    int cx = 0, cy = 0;  // 光标位置
    while (!m_over) {
        m_draw();
        // 绘制光标
        gotoxy(cx * 2 + 2, cy + 2);
        m_input(cx, cy);
        Sleep(50);
    }
    // 最后绘制一次(显示地雷/胜利)
    m_draw();
    // 等待按键
    printf("Press any key back...\n");
    while (key_hit()) get_key();
    while (!key_hit()) Sleep(50);
    get_key();
    showCursor();
}

//================================================================================
// 主菜单
//================================================================================
void showMenu() {
    cls();
    color(3);
    printf("==== 小游戏合集 ====\n");
    resetColor();
    printf("1. 贪吃蛇\n");
    printf("2. 俄罗斯方块\n");
    printf("3. 扫雷\n");
    printf("0. 退出\n");
    printf("请选择(0-3):");
}

int main() {
    int choice = -1;
    while (choice != 0) {
        showMenu();
        // 读取选择
        while (!key_hit()) Sleep(50);
        choice = get_key() - '0';
        switch (choice) {
            case 1: playSnake(); break;
            case 2: playTetris(); break;
            case 3: playMinesweeper(); break;
            case 0: break;
            default: 
                cls();
                printf("输入错误,请重新选择!\n");
                Sleep(1000);
                break;
        }
    }
    cls();
    printf("感谢游玩!\n");
    return 0;
}