扫雷游戏是以前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;
}
}
}