简单的SDL扫雷游戏

#include <SDL.h>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#undef main  // 解除宏定义,防止与main函数冲突

// 常量定义
const int WIDTH = 9;  // 游戏板的宽度(格子数)
const int HEIGHT = 9; // 游戏板的高度(格子数)
const int MINES = 10; // 地雷的数量
const int CELL_SIZE = 32; // 每个格子的大小(像素)
const int WINDOW_WIDTH = WIDTH * CELL_SIZE; // 窗口的宽度
const int WINDOW_HEIGHT = HEIGHT * CELL_SIZE; // 窗口的高度

// 结构体定义,表示一个格子的状态
struct Cell {
    bool mine;           // 是否有地雷
    bool revealed;       // 是否被揭露
    int minesAround;     // 周围的地雷数量
};

// 函数声明
void initBoard(Cell board[HEIGHT][WIDTH], int mines); // 初始化游戏板
void drawBoard(SDL_Renderer* renderer, Cell board[HEIGHT][WIDTH]); // 绘制游戏板
void handleEvents(SDL_Event& e, Cell board[HEIGHT][WIDTH]); // 处理事件
bool isMine(Cell board[HEIGHT][WIDTH], int x, int y); // 判断某位置是否有地雷
void countMines(Cell board[HEIGHT][WIDTH]); // 计算每个格子周围的地雷数量
int countAround(Cell board[HEIGHT][WIDTH], int x, int y); // 计算特定格子周围的地雷数量
bool gameWon(Cell board[HEIGHT][WIDTH]); // 判断游戏是否胜利
bool allNonMinesRevealed(Cell board[HEIGHT][WIDTH]); // 判断所有非地雷格子是否都被揭露
void drawNumber(SDL_Renderer* renderer, int num, int x, int y); // 绘制数字
void drawDigit(SDL_Renderer* renderer, int digit, int x, int y, SDL_Color color); // 绘制单个数字

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) < 0) { // 初始化SDL
        std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
        return 1;
    }

    SDL_Window* window = SDL_CreateWindow( // 创建窗口
        "Mine Sweeper",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
        SDL_WINDOW_SHOWN
    );

    if (!window) { // 检查窗口创建是否成功
        std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); // 创建渲染器
    if (!renderer) { // 检查渲染器创建是否成功
        std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    Cell board[HEIGHT][WIDTH]; // 定义游戏板
    initBoard(board, MINES); // 初始化游戏板

    bool running = true; // 控制主循环运行标志
    SDL_Event e; // 事件对象

    while (running) { // 主循环
        while (SDL_PollEvent(&e)) { // 检测事件
            handleEvents(e, board); // 处理事件
            if (e.type == SDL_QUIT) { // 如果是退出事件
                running = false; // 停止主循环
            }
        }

        drawBoard(renderer, board); // 绘制游戏板

        if (gameWon(board)) { // 如果游戏胜利
            std::cout << "You won!" << std::endl;
            running = false; // 停止主循环
        }

        SDL_RenderPresent(renderer); // 更新屏幕
    }

    SDL_DestroyRenderer(renderer); // 销毁渲染器
    SDL_DestroyWindow(window); // 销毁窗口
    SDL_Quit(); // 卸载SDL

    return 0;
}

// 初始化游戏板
void initBoard(Cell board[HEIGHT][WIDTH], int mines) {
    int i, j, k = 0;

    // 设置所有格子为未揭示且无地雷状态
    for (i = 0; i < HEIGHT; ++i)
        for (j = 0; j < WIDTH; ++j) {
            board[i][j].mine = false;
            board[i][j].revealed = false;
            board[i][j].minesAround = 0;
        }

    // 随机放置地雷
    while (k < mines) {
        int x = rand() % WIDTH;
        int y = rand() % HEIGHT;
        if (!board[y][x].mine) {
            board[y][x].mine = true;
            ++k;
        }
    }
    countMines(board); // 计算每个格子周围的地雷数量
}

// 计算每个格子周围的地雷数量
void countMines(Cell board[HEIGHT][WIDTH]) {
    for (int i = 0; i < HEIGHT; ++i)
        for (int j = 0; j < WIDTH; ++j)
            if (!board[i][j].mine)
                board[i][j].minesAround = countAround(board, i, j);
}

// 计算特定格子周围的地雷数量
int countAround(Cell board[HEIGHT][WIDTH], int x, int y) {
    int count = 0;
    for (int dx = -1; dx <= 1; dx++)
        for (int dy = -1; dy <= 1; dy++) {
            if (dx == 0 && dy == 0) continue; // 不计算自身
            int nx = x + dx;
            int ny = y + dy;
            if (nx >= 0 && nx < HEIGHT && ny >= 0 && ny < WIDTH && board[nx][ny].mine)
                count++; // 如果周围有地雷,计数加一
        }
    return count;
}

// 判断某位置是否有地雷
bool isMine(Cell board[HEIGHT][WIDTH], int x, int y) {
    return board[y][x].mine;
}

// 绘制游戏板
void drawBoard(SDL_Renderer* renderer, Cell board[HEIGHT][WIDTH]) {
    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); // 设置背景颜色
    SDL_RenderClear(renderer); // 清除渲染目标

    // 循环遍历每个格子
    for (int i = 0; i < HEIGHT; ++i)
        for (int j = 0; j < WIDTH; ++j) {
            SDL_Rect rect = { j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE }; // 计算格子位置
            if (board[i][j].revealed) { // 如果格子已揭示
                SDL_SetRenderDrawColor(renderer, 192, 192, 192, 255); // 设置格子颜色
                if (board[i][j].mine) { // 如果是地雷
                    SDL_RenderFillRect(renderer, &rect); // 绘制地雷格子
                    // 可以在这里添加一个地雷的图像来表示地雷
                }
                else { // 如果不是地雷
                    SDL_RenderFillRect(renderer, &rect); // 绘制非地雷格子
                    if (board[i][j].minesAround > 0) { // 如果周围有地雷
                        drawNumber(renderer, board[i][j].minesAround, rect.x + 5, rect.y + 5); // 绘制地雷数量
                    }
                }
            }
            else { // 如果格子未揭示
                SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255); // 设置格子颜色
                SDL_RenderFillRect(renderer, &rect); // 绘制未揭示格子
            }
        }
}

// 绘制数字
void drawNumber(SDL_Renderer* renderer, int num, int x, int y) {
    std::string text = std::to_string(num); // 将数字转换成字符串
    for (size_t i = 0; i < text.length(); ++i) { // 遍历字符串中的每个字符
        drawDigit(renderer, text[i] - '0', x, y, { 0, 0, 0 }); // 绘制单个数字
        x += 20; // 假设每个数字宽度为20
    }
}

// 绘制单个数字
void drawDigit(SDL_Renderer* renderer, int digit, int x, int y, SDL_Color color) {
    SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 255); // 设置颜色
    switch (digit) { // 根据数字绘制不同形状
    case 1:
        SDL_RenderDrawLine(renderer, x, y, x + 20, y + 20); // 数字1
        SDL_RenderDrawLine(renderer, x, y + 20, x + 20, y);
        break;
    case 2:
        SDL_RenderDrawLine(renderer, x, y, x + 20, y); // 数字2
        SDL_RenderDrawLine(renderer, x + 20, y, x + 20, y + 20);
        SDL_RenderDrawLine(renderer, x + 20, y + 20, x, y + 20);
        SDL_RenderDrawLine(renderer, x, y + 20, x, y);
        break;
    case 3:
        SDL_RenderDrawLine(renderer, x, y, x + 20, y); // 数字3
        SDL_RenderDrawLine(renderer, x + 20, y, x + 20, y + 10);
        SDL_RenderDrawLine(renderer, x + 20, y + 10, x, y + 10);
        SDL_RenderDrawLine(renderer, x, y + 10, x, y + 20);
        SDL_RenderDrawLine(renderer, x, y + 20, x + 20, y + 20);
        break;
        // 其他数字类似
    default:
        break;
    }
}

// 处理事件
void handleEvents(SDL_Event& e, Cell board[HEIGHT][WIDTH]) {
    if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT) { // 左键点击
        int x, y;
        SDL_GetMouseState(&x, &y); // 获取鼠标位置
        int col = x / CELL_SIZE; // 计算点击的列
        int row = y / CELL_SIZE; // 计算点击的行
        if (col >= 0 && col < WIDTH && row >= 0 && row < HEIGHT) { // 如果点击在有效范围内
            board[row][col].revealed = true; // 揭示该格子
            if (isMine(board, col, row)) { // 如果该格子是地雷
                std::cout << "Game Over!" << std::endl;
                exit(0); // 结束程序
            }
        }
    }
}

// 判断游戏是否胜利
bool gameWon(Cell board[HEIGHT][WIDTH]) {
    return allNonMinesRevealed(board); // 调用辅助函数判断
}

// 判断所有非地雷格子是否都被揭露
bool allNonMinesRevealed(Cell board[HEIGHT][WIDTH]) {
    int nonMineCells = 0; // 非地雷格子计数器
    for (int i = 0; i < HEIGHT; ++i)
        for (int j = 0; j < WIDTH; ++j)
            if (!board[i][j].mine && board[i][j].revealed)
                nonMineCells++; // 如果是非地雷且已揭露,计数加一

    return nonMineCells == (HEIGHT * WIDTH - MINES); // 如果非地雷格子数等于总数减去地雷数,则胜利
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值