扫雷游戏是以前Windows自带的一款经典益智游戏,游戏玩法简单,不懂的朋友可以去百度然后尝试一下就知道了,现在我们就用C语言来实现一下这款游戏。
首先我们理清整体的思路,可以采用两个二维数组来作为游戏棋盘,一个用来埋雷(只有玩家通过游戏后显示一次),一个作为游戏棋盘,这里可以将埋雷棋盘初始化为全字符0,用字符1表示地雷,将游戏棋盘初始化为全'*'。(注:在确定数组大小时,我们可以在所需棋盘大小的基础上再在周围增加一圈棋盘,即行列都加2,这样便于后续统计扫雷位置周围地雷个数)。
除此之外,我们好需要通过数字、下划线以及一些其他符号来打印一个可视化的棋盘和一个选择菜单。
棋盘、菜单示例
效果比较粗糙,大家可以自己进行调整改进,使棋盘更加美观。
代码实现:
void Menu()//选择菜单 { printf("#######################\n"); printf("# 1.Play 2.Exit ##\n"); printf("#######################\n"); printf("请输入你的选择: "); } void ShowBoard(char board[][COL], int row, int col)//打印棋盘 { printf(" "); for (int i = 1; i <= row - 2; i++){ printf("%4d", i); } printf("\n "); for (int i = 1; i <= row - 2; i++){ printf(" ___"); } for (int j = 1; j <= col - 2; j++){ printf("\n"); printf("%2d", j); printf(" |"); for (int i = 1; i <= row - 2; i++){ printf("_%c_|", board[j][i]); } } printf("\n"); return; }
随机埋雷
首先确定地雷个数,可以采用宏定义NUM,便于我们改变个数而不改变后续的逻辑,然后就可以利用rand函数来产生随机坐标进行埋雷了。代码实现:
void SetMines(char board[][COL], int row, int col){//随机埋雷 int i = 0; while (i < NUM) { int _x = rand() % (row-2) + 1; int _y = rand() % (col-2) + 1; if (board[_x][_y] == BOOM) { continue; } board[_x][_y] = BOOM; i++; } }
统计周围地雷个数
int CountMines(char board[][COL], int x, int y){// 统计附近地雷个数 return board[x - 1][y - 1] + board[x - 1][y] + \ board[x - 1][y + 1] + board[x][y - 1] + \ board[x][y + 1] + board[x + 1][y - 1] + \ board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';//('1' + '0' + '0'...'0')- ('0'+'0') }
判断游戏是否胜利
通过统计游戏棋盘上非字符’*‘的字符个数,若等于棋盘上格子总数减去地雷个数则游戏胜利返回1,反之游戏继续。代码实现:
int WinGame(char board[][COL], int row, int col) {//游戏胜利条件:胜利1 游戏继续0 int total = (row - 2) * (col - 2) - NUM; int flag = 0; for (int i = 1; i < row; i++) { for (int j = 1; j < col; j++) { if ((board[i][j] != '*')) { flag++; } } } return (flag ==total ) ? 1 : 0; }
展开安全区域
当我们扫了一个位置后,如果当前位置周围的地雷个数为0,则需要进行向四周展开直至附件有雷的格子,并在当前格子显示附件地雷的个数,增加游戏的可完性,根据需求特点我们可以利用函数递归来实现,那么递归的出口就有两个,一个就是向四周展开都找到了附近有雷的格子(边界除外),另一个就是展开了所有没雷的格子达到了游戏胜利的条件。代码实现:
int SafeBoard(char board[][COL], char mine_board[][COL], int row, int col, int x, int y) {//可展开的安全区域 int tmp; tmp = CountMines(mine_board, x, y); if (x >= 1 && x <= row - 2 && y >= 1 && y <= col - 2) { if (tmp == 0) { board[x][y] = ' '; if (mine_board[x - 1][y - 1] != '1' && board[x - 1][y - 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x - 1, y - 1); } if (mine_board[x - 1][y + 1] != '1' && board[x - 1][y + 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x - 1, y + 1); } if (mine_board[x - 1][y] != '1' && board[x - 1][y] == '*') { SafeBoard(board, mine_board, ROW, COL, x - 1, y); } if (mine_board[x][y + 1] != '1' && board[x][y + 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x, y + 1); } if (mine_board[x - 1][y - 1] != '1' && board[x][y - 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x, y - 1); } if (mine_board[x + 1][y] != '1' && board[x + 1][y] == '*') { SafeBoard(board, mine_board, ROW, COL, x + 1, y); } if (mine_board[x + 1][y + 1] != '1' && board[x + 1][y + 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x + 1, y + 1); } if (mine_board[x + 1][y - 1] != '1' && board[x + 1][y - 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x + 1, y - 1); } } else { board[x][y] = tmp + '0'; if (WinGame(board,ROW,COL)) { return 0; } } } return 1; }
游戏主体
最后就是游戏的整个控制函数了,只需判断玩家输入的坐标是否合法进行对应的操作和提示即可。
代码实现:
int main() { srand((unsigned long)time(NULL)); int quit = 0; while (!quit) { Menu(); int select = 0; scanf("%d", &select); switch (select) { case 1: Game(); break; case 2: quit = 1; break; default: printf("输入有误, 重新选择!\n"); break; } } printf("再见!\n"); return 0; } void Game(){ char show_board[ROW][COL];//游戏棋盘 char mine_board[ROW][COL];//炸弹棋盘 memset(show_board, '*', sizeof(show_board));//棋盘全‘*’初始化 memset(mine_board, '0', sizeof(mine_board)); SetMines(mine_board, ROW, COL);//随机埋雷 while (1) { int x = 0; int y = 0; ShowBoard(show_board, ROW, COL);//打印棋盘 printf("请输入坐标<x, y> "); scanf("%d %d", &x, &y); system("cls"); if (!(x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2)) { printf("扫雷位置不合法,请重新输入!\n"); continue; } if (show_board[x][y] != '*') { printf("扫雷的位置已经被排除,请重新输入!\n"); continue; } if (mine_board[x][y] == '1') { printf("对不起,你被炸死了!\n"); break; } int tmp = SafeBoard(show_board, mine_board, ROW, COL, x, y); if (tmp == 0) { ShowBoard(mine_board, ROW, COL); printf("恭喜你,游戏通过!\n"); return ; } else { continue; } } }
完整代码
MineSweeper.h
#pragma once #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #define ROW 12 #define COL 12 #define NUM 10 #define BOOM '1' void Game();
game.c
int main() { srand((unsigned long)time(NULL)); int quit = 0; while (!quit) { Menu(); int select = 0; scanf("%d", &select); switch (select) { case 1: Game(); break; case 2: quit = 1; break; default: printf("输入有误, 重新选择!\n"); break; } } printf("再见!\n"); return 0; }
MineSweeper.c
#include "MineSweeper.h" void SetMines(char board[][COL], int row, int col){//随机埋雷 int i = 0; while (i < NUM) { int _x = rand() % (row-2) + 1; int _y = rand() % (col-2) + 1; if (board[_x][_y] == BOOM) { continue; } board[_x][_y] = BOOM; i++; } } void ShowBoard(char board[][COL], int row, int col)//打印棋盘 { printf(" "); for (int i = 1; i <= row - 2; i++){ printf("%4d", i); } printf("\n "); for (int i = 1; i <= row - 2; i++){ printf(" ___"); } for (int j = 1; j <= col - 2; j++){ printf("\n"); printf("%2d", j); printf(" |"); for (int i = 1; i <= row - 2; i++){ printf("_%c_|", board[j][i]); } } printf("\n"); return; } int CountMines(char board[][COL], int x, int y){// 统计附近地雷个数 return board[x - 1][y - 1] + board[x - 1][y] + \ board[x - 1][y + 1] + board[x][y - 1] + \ board[x][y + 1] + board[x + 1][y - 1] + \ board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';//('1' + '0' + '0'...'0')- ('0'+'0') } int WinGame(char board[][COL], int row, int col) {//游戏胜利条件:胜利1 游戏继续0 int total = (row - 2) * (col - 2) - NUM; int flag = 0; for (int i = 1; i < row; i++) { for (int j = 1; j < col; j++) { if ((board[i][j] != '*')) { flag++; } } } return (flag ==total ) ? 1 : 0; } int SafeBoard(char board[][COL], char mine_board[][COL], int row, int col, int x, int y) {//可展开的安全区域 int tmp; tmp = CountMines(mine_board, x, y); if (x >= 1 && x <= row - 2 && y >= 1 && y <= col - 2) { if (tmp == 0) { board[x][y] = ' '; if (mine_board[x - 1][y - 1] != '1' && board[x - 1][y - 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x - 1, y - 1); } if (mine_board[x - 1][y + 1] != '1' && board[x - 1][y + 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x - 1, y + 1); } if (mine_board[x - 1][y] != '1' && board[x - 1][y] == '*') { SafeBoard(board, mine_board, ROW, COL, x - 1, y); } if (mine_board[x][y + 1] != '1' && board[x][y + 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x, y + 1); } if (mine_board[x - 1][y - 1] != '1' && board[x][y - 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x, y - 1); } if (mine_board[x + 1][y] != '1' && board[x + 1][y] == '*') { SafeBoard(board, mine_board, ROW, COL, x + 1, y); } if (mine_board[x + 1][y + 1] != '1' && board[x + 1][y + 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x + 1, y + 1); } if (mine_board[x + 1][y - 1] != '1' && board[x + 1][y - 1] == '*') { SafeBoard(board, mine_board, ROW, COL, x + 1, y - 1); } } else { board[x][y] = tmp + '0'; if (WinGame(board,ROW,COL)) { return 0; } } } return 1; } void Game(){ char show_board[ROW][COL];//游戏棋盘 char mine_board[ROW][COL];//炸弹棋盘 memset(show_board, '*', sizeof(show_board));//棋盘全‘*’初始化 memset(mine_board, '0', sizeof(mine_board)); SetMines(mine_board, ROW, COL);//随机埋雷 while (1) { int x = 0; int y = 0; ShowBoard(show_board, ROW, COL);//打印棋盘 printf("请输入坐标<x, y> "); scanf("%d %d", &x, &y); system("cls"); if (!(x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2)) { printf("扫雷位置不合法,请重新输入!\n"); continue; } if (show_board[x][y] != '*') { printf("扫雷的位置已经被排除,请重新输入!\n"); continue; } if (mine_board[x][y] == '1') { printf("对不起,你被炸死了!\n"); break; } int tmp = SafeBoard(show_board, mine_board, ROW, COL, x, y); if (tmp == 0) { ShowBoard(mine_board, ROW, COL); printf("恭喜你,游戏通过!\n"); return ; } else { continue; } } }