手搓了一个扫雷游戏(C),欢迎品尝!

需要后续版本(丕定有)可以留言催更~

就不贴图了,感兴趣的自己自然会拿来跑的对吧~

/**
 * 扫雷极速版!!!
 *
 * 支持 MSVC,GCC ~ Win32,Linux 平台!
 * (初版,部分汉化)
 *
 * @author Ervoconite
 */

#define _CRT_SECURE_NO_WARNINGS

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/**
 * cross-platform.h
 */
#ifdef _WIN32
// 以下字符串均为以 GBK 编码的字节串:
const char* clear_cmds = "cls";
const char* title =  // 扫雷极速版!!!
    "\xc9\xa8\xc0\xd7\xbc\xab\xcb\xd9\xb0\xe6";
const char* circle_c =  // ●
    "\xa1\xf1";
const char* square_c =  // ■
    "\xa1\xf6";
const char* e_square_c =  // □
    "\xa1\xf5";
const char* win_c =  // 你赢了
    "\xc4\xe3\xd3\xae\xc1\xcb";
const char* over_c =  // 游戏结束
    "\xd3\xce\xcf\xb7\xbd\xe1\xca\xf8";
const char* prompt_c =  // 输入扫雷坐标:
"\xca\xe4\xc8\xeb\xd7\xf8\xb1\xea(\xd0\xd0 \xc1\xd0)";
char overflow[] =  // 坐标超出边界
    "\xd7\xf8\xb1\xea\xb3\xac\xb3\xf6\xb1\xdf\xbd\xe7 !";
#elif __unix__
// 以下字符串均为以 UTF-8 编码的字节串:
const char* clear_cmds = "clear";
const char* title =  // 扫雷极速版!!!
    "\xe6\x89\xab\xe9\x9b\xb7\xe6\x9e\x81\xe9\x80\x9f\xe7\x89\x88";
const char* circle_c =  // ●
    "\xe2\x97\x8f";
const char* square_c =  // ■
    "\xe2\x96\xa0";
const char* e_square_c =  // □
    "\xe2\x96\xa1";
const char* win_c =  // 你赢了
    "\xe4\xbd\xa0\xe8\xb5\xa2\xe4\xba\x86";
const char* over_c =  // 游戏结束
    "\xe6\xb8\xb8\xe6\x88\x8f\xe7\xbb\x93\xe6\x9d\x9f";
const char* prompt_c =  // 输入扫雷坐标:
"\xe8\xbe\x93\xe5\x85\xa5\xe5\x9d\x90\xe6\xa0\x87(\xe8\xa1\x8c \xe5\x88\x97)";
char overflow[] =  // 坐标超出边界
    "\xe5\x9d\x90\xe6\xa0\x87\xe8\xb6\x85\xe5\x87\xba\xe8\xbe\xb9\xe7\x95"
    "\x8c !";
#endif

void initLocale() {
    // #include <locale.h>
    // #include <wchar.h>
#ifdef _WIN32
    // setlocale(LC_ALL, "en_US.UTF8");
#elif __unix__
    // setlocale(LC_ALL, "en_US.UTF8");
#else
    puts("Unknown operating system.");
    exit(1);
#endif
}
void clearScreen() { system(clear_cmds); }
void prompt(char* warning) {
    static char tip[] = "Enter(0 0)to exit";
    static char* prompt_prefix = (char*)&tip;
    if (warning) {
        prompt_prefix = warning;
        return;
    }
    printf("%s\n%s: ", prompt_prefix, prompt_c);
    prompt_prefix = (char*)&tip;
}
void stepMine() { printf("\n   >>> %s! <<<\n", over_c); }
void win() { printf("\n   >>> %s! <<<\n", win_c); }
void clearStdin() {
    while (getchar() != '\n')
        ;
}
//
//
//
//
//
//

/**
 * game.h
 */
// 地雷记号,改动无效。
#define MINE '*'
// 稀疏度:3最合适,不要超过5,不然容易玩不了。
#define MINE_sparsity 3

// #include "cross-platform.h"

typedef unsigned char byte;

typedef void _;

typedef struct GameBoard {
    int size;    // 扩展的地图大小,真实地图从1开始到border-1。
    int border;  // 扩展后的右边框下标,值为size+1。
    int min;     // 最小下标
    int max;     // 最大下标
    int n_total;    // 地图总大小,值为(size-2)*(size-2)。
    int n_mine;     // 地雷数量
    int n_shown;    // unveil数量。
    byte** map;     // 地图,每个元素为地雷或者周围地雷数量。
    byte** unveil;  // 掀开的点的地图。
    byte** tempMap;  // 用于搜索算法的缓存。
} Board;

Board createBoard(unsigned size) {
    Board board;
    board.n_total = size * size;
    board.n_mine = board.n_shown = 0;
    board.min = 1;
    board.max = size;
    board.size = board.max + 2;
    board.border = board.max + 1;
    board.map = (byte**)malloc(board.size * sizeof(byte*));
    board.unveil = (byte**)malloc(board.size * sizeof(byte*));
    for (int i = 0; i < board.size; i++) {
        board.map[i] = (byte*)calloc(board.size, sizeof(byte));
        board.unveil[i] = (byte*)calloc(board.size, sizeof(byte));
    }
    return board;
}

bool isOverFlow(Board* board, int x, int y) {
    if (x < board->min || x > board->max || y < board->min || y > board->max)
        return true;
    else
        return false;
}

bool isComplete(Board* board) {
    return board->n_mine + board->n_shown >= board->n_total ? true : false;
}

_ cleanTemp(Board* board) {
    for (int i = 0; i < board->size; i++) {
        memset(board->tempMap[i], 0, board->size * sizeof(byte));
    }
}

_ destroyBoard(Board* board) {
    for (int i = 0; i < board->size; i++) {
        free(board->map[i]);
        free(board->unveil[i]);
    }
    free(board->map);
    free(board->unveil);
    board->map = board->unveil = NULL;
}

byte __initBoard_surroundings(int i, int j, byte** map) {
    byte count = 0;
    count += map[i][j - 1] == MINE;      // 左 ←
    count += map[i][j + 1] == MINE;      // 右 →
    count += map[i - 1][j] == MINE;      // 上 ↑
    count += map[i + 1][j] == MINE;      // 下 ↓
    count += map[i - 1][j - 1] == MINE;  // 左上 ↖
    count += map[i - 1][j + 1] == MINE;  // 左下 ↙
    count += map[i + 1][j - 1] == MINE;  // 右上 ↗
    count += map[i + 1][j + 1] == MINE;  // 右下 ↘
    return count;
}

_ initBoard(Board* board, int init_x, int init_y) {
    // 布置地雷
    srand((unsigned)time(NULL));
    for (int i = 1; i < board->border; i++) {
        for (int j = 1; j < board->border; j++) {
            board->map[i][j] =
                (rand() % MINE_sparsity ? 0 : (board->n_mine++, MINE));
        }
    }
    // 确保第一次点击的地方不是地雷
    if (board->map[init_x][init_y] == MINE) {
        board->map[init_x][init_y] = 0;
        board->n_mine--;
    }
    // 确保至少一颗地雷
    int x2 = init_x;
    while (x2 == init_x) {
        x2 = 1 + rand() % board->max;
    }
    init_y = 1 + rand() % board->max;
    if (board->map[x2][init_y] != MINE) {
        board->map[x2][init_y] = (board->n_mine++, MINE);
    }

    // 计算周边
    for (int i = 1; i < board->border; i++) {
        for (int j = 1; j < board->border; j++) {
            if (board->map[i][j] == MINE) continue;
            board->map[i][j] = __initBoard_surroundings(i, j, board->map);
        }
    }
}

_ printBoard(bool inGame, Board* board) {
    if (inGame)
        clearScreen();
    else
        puts("");
    // 打印顶部坐标轴
    printf("   ");
    for (int i = 1; i < board->border; i++) {
        printf("%2d", i);
    }
    putchar('\n');
    // 横线
    printf("   ");
    for (int i = 1; i < board->border; i++) {
        printf("--");
    }
    putchar('\n');
    // 打印棋盘
    for (int i = 1; i < board->border; i++) {
        // 左侧坐标轴
        printf("%2d|", i);
        // 每行
        for (int j = 1; j < board->border; j++) {
            // 非游戏中,完全打印:
            if (!inGame) {
                if (board->map[i][j] == MINE) {
                    printf(" %s", circle_c);
                } else if (board->map[i][j] == 0) {
                    // printf(" %s", e_square_c);
                    printf(" %c", ' ');
                } else {
                    printf(" %c", board->map[i][j] + '0');
                }
            }
            // 游戏中,按 unveil 打印:
            else if (board->unveil[i][j]) {
                if (board->map[i][j] == 0)
                    // printf(" %s", e_square_c);
                    printf(" %c", ' ');
                else
                    printf(" %c", board->map[i][j] + '0');
            } else {
                printf(" %s", square_c);
            }
        }
        puts("|");
    }
    // 横线
    printf("   ");
    for (int i = 1; i < board->border; i++) {
        printf("--");
    }
    putchar('\n');
}

/** @attention 不能是炸弹,该函数会修改 n_shown */
_ stirUp(Board* board, int x, int y) {
    // 搜索连续空白
    if (isOverFlow(board, x, y) || board->map[x][y] == MINE) {
        return;
    }
    byte** map = board->map;
    byte** unveil = board->unveil;
    unveil[x][y] = 1;
    board->n_shown++;
    // 上 ↑
    if (map[x - 1][y] != MINE && unveil[x - 1][y] == 0) stirUp(board, x - 1, y);
    // 下 ↓
    if (map[x + 1][y] != MINE && unveil[x + 1][y] == 0) stirUp(board, x + 1, y);
    // 左 ←
    if (map[x][y - 1] != MINE && unveil[x][y - 1] == 0) stirUp(board, x, y - 1);
    // 右 →
    if (map[x][y + 1] != MINE && unveil[x][y + 1] == 0) stirUp(board, x, y + 1);
}

#define Wins 2
#define OK 1
#define Boom 0
#define Breaks -1
#define OverFlow -2

int sweep(bool first, Board* board) {
    int x, y;
    prompt(NULL);
    int sret = scanf("%d%d", &x, &y);
    clearStdin();
    if (sret == EOF || (x == y && y == 0)) return Breaks;

    if (isOverFlow(board, x, y)) {
        return OverFlow;
    } else if (first) {
        initBoard(board, x, y);
        stirUp(board, x, y);
        // 判断排完
        if (isComplete(board))
            return Wins;
        else
            return OK;
    } else if (board->map[x][y] == MINE) {
        board->map[x][y] = 'X' - '0';
        return Boom;
    } else {
        stirUp(board, x, y);
        // 判断排完
        if (isComplete(board))
            return Wins;
        else
            return OK;
    }
}

#define BOARD_SIZE_MIN 5
#define BOARD_SIZE_MAX 88

_ play(int size) {
    if (size < BOARD_SIZE_MIN || size > BOARD_SIZE_MAX) {
        fprintf(stderr, "Func[Play]: ValueError(size has error value %d!)",
                size);
        exit(1);
    }

    // 初始化,置零
    Board board = createBoard(size);

    int state;
    bool first = true;

    // 开始游戏
    while (true) {
        printBoard(true, &board);
        state = sweep(first, &board);
        first = false;

        if (state == Wins) {
            printBoard(false, &board);
            win();
        } else if (state == Boom) {
            printBoard(false, &board);
            stepMine();
        } else if (state == Breaks) {
            if (board.n_mine) printBoard(false, &board);
        } else if (state == OverFlow) {
            prompt(overflow);
            continue;
        } else if (state == OK) {
            continue;
        }
        break;
    }
    // 释放
    destroyBoard(&board);
    printf("\n\n Press enter to continue...");
    getchar();
}
//
//
//
//
//
//

/**
 * main.c
 */

#define LVL_SIMPLE 5
#define LVL_NORMAL 11
#define LVL_HARD 24

// #include "game.h"

void printMenu(char* prompt[]) {
    clearScreen();
    printf("\n    %s ! ! !\n\n", title);
    puts("***********************************");
    puts("******  1. Simple Mode (5)   ******");
    puts("******  2. Normal Mode (11)  ******");
    puts("******  3. Hard Mode (24)    ******");
    puts("******  4. Custom (5~88)     ******");
    puts("******  5. Exit              ******");
    puts("***********************************");
    puts("");
    printf("%s", *prompt);
    *prompt = "";
    printf("Select(1~5) >> ");
}

int main(int argc, char** argv) {
    initLocale();
    int choice;
    char* prompt = "";
    printMenu(&prompt);
    while (EOF != scanf("%d", &choice)) {
        clearStdin();
        if (choice == 5)
            break;
        else if (choice == 1) {
            play(LVL_SIMPLE);
        } else if (choice == 2) {
            play(LVL_NORMAL);
        } else if (choice == 3) {
            play(LVL_HARD);
        } else if (choice == 4) {
            unsigned size;
            printf("Input Size: ");
            scanf("%u", &size);
            clearStdin();
            if (size < BOARD_SIZE_MIN || size > BOARD_SIZE_MAX) {
                prompt = "Invalid size!!!\n";
            } else {
                play(size);
            }
        }
        printMenu(&prompt);
    }

    // puts("\n\n Press any key to exit\n\n");
    // getchar();

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ervoconite

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值