c语言实现功能最全的扫雷游戏
游戏介绍
该扫雷游戏具有以下功能:
计时,显示时间,难度模式选择,开雷和标记雷位置,动态显示雷个数等
代码讲解
初始化雷区
使用了两个二维数组,mine数组是用来存放布置好的雷的信息,show数组是用来存放排查出的雷的信息(显示在屏幕中)
void InitBoard(char board[][COLS], int rows, int cols, char ch)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = ch;
}
}
}
char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
选择模式代码
游戏模式共分成三种模式:1、简单模式 2、中等模式 3、困难模式(在以后的函数中用digit接收此序号)。利用switch-case语句实现模式选择
printf("游戏模式共分成三种模式:1、简单模式 2、中等模式 3、困难模式\n");
printf("请选择游戏模式:");
scanf("%d", &choice);
switch (choice)
随机设置雷的位置代码
利用rand()和srand()函数生成的随机数作为雷的位置,期间进行位置重复的判断
//布置NUMBEROFMINE个雷
void SetMine(char board[][COLS], int row, int col, int digit)
{
srand((unsigned)time(NULL));
int count = 0;
if (digit == 1)
count = EASY_NUMBEROFMINE;
else if (digit == 2)
count = MEDIUM_NUMBEROFMINE;
else
count = EXPERT_NUMBEROFMINE;
while (count > 0)
{
int r = rand() % row + 1;
int c = rand() % col + 1;
if (board[r][c] == '0')
{
board[r][c] = '1';
count--;
}
}
}
排查雷代码
跟据前期选择的模式,使用if-else语句找到对应的代码位置。每一种模式均拥有1、打开位置和0、标记位置两个功能,当玩家选择0时,屏幕上边会显示雷的数量,当标记位置是雷的位置时,显示的数量会减1,当减为0时玩家胜利游戏结束;当玩家选择1时,如果该位置是雷,则游戏结束玩家失败;否则会统计该位置周围雷的个数,如果个数为0,则会以该位置为中心向外自动展开格子,直至遇到非零格子。
以简单模式为例:
void FindMine(char mine[][COLS], char show[][COLS], int row, int col, int digit)
{
int count = 0;
if (digit == 1)
{
count = EASY_NUMBEROFMINE;
while (cnt1 > 0) {
int x = 0, y = 0, choice = 0;
printf("\t输入1表示打开'*',输入0表示标记雷位置\n");
printf("请选择:");
scanf("%d", &choice);
if (choice == 0)
{
printf("请输入标记雷的坐标:");
scanf("%d%d", &x, &y);
show[x][y] = '#';
PrintBoard(show, row, col);
if (mine[x][y] == '1')
{
count--;
}
printf("还剩%d个雷\n", count);
if (count == 0)
{
break;
}
}
else if (choice == 1)
{
printf("请输入要打开'*'的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,您被炸了!!!\n");
printf("Game Over\n");
PrintBoard(mine, ROW, COL);
printf("以上是雷的分布\n");
break;
}
else
{
OutwardRadiation(mine, show, x, y, digit);
PrintBoard(show, row, col);
}
}
else
{
printf("输入非法,请重新输入!\n");
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
if (cnt1 == 0 || count == 0)
{
printf("恭喜你,排雷成功!!!\n");
}
}
}
统计周围雷的个数代码
show数组在初始化和添加雷的时候,我们以’1’作为雷,以’0’作为非雷,此时让该位置四周的八个位置处字符相加再减去8*0(原理:1+‘0’=‘1’),即可得到该位置周围雷的数量。
//统计周围雷的个数
int GetMinNumber(char mine[][COLS], int x, int y)
{
return mine[x - 1][y - 1]
+ mine[x][y - 1]
+ mine[x + 1][y - 1]
+ mine[x - 1][y]
+ mine[x + 1][y]
+ mine[x - 1][y + 1]
+ mine[x][y + 1]
+ mine[x + 1][y + 1] - 8 * '0';
}
点击到空白格子时自动展开代码(递归)
此代码用递归实现。
//实现辐射式显示雷个数
void OutwardRadiation(char mine[][COLS], char show[][COLS], int x, int y, int digit)
{
if (x == 0 || y == 0 || x == ROWS - 1 || y == COLS - 1)//位置是否越界
{
return;
}
if (show[x][y] != '*')//遍历过的位置不再遍历,防止出现死递归
{
return;
}
//不同模式
if (digit == 1)
{
cnt1--;
}
else if (digit == 2)
{
cnt2--;
}
else
{
cnt3--;
}
int count = GetMinNumber(mine, x, y, digit) + '0';
//该位置不为0时递归停止
if (count != '0')
{
show[x][y] = count;
return;
}
else
{
show[x][y] = '0';
OutwardRadiation(mine, show, x - 1, y - 1, digit);
OutwardRadiation(mine, show, x, y - 1, digit);
OutwardRadiation(mine, show, x + 1, y - 1, digit);
OutwardRadiation(mine, show, x - 1, y, digit);
OutwardRadiation(mine, show, x + 1, y, digit);
OutwardRadiation(mine, show, x - 1, y + 1, digit);
OutwardRadiation(mine, show, x, y + 1, digit);
OutwardRadiation(mine, show, x + 1, y + 1, digit);
}
}
显示时间代码
在开始游戏时利用系统时间库函数显示当前时间–“xx年xx月xx日 xx时xx分xx秒 星期x”
//显示时间
void ShowTime()
{
SYSTEMTIME sys;
printf("");
GetLocalTime(&sys);//获取系统时间
printf("\t当前时间为:%4d/%02d/%02d %02d:%02d:%02d ",
sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond);
switch (sys.wDayOfWeek)
{
case 1:
{
printf("星期一\n");
break;
}
case 2:
{
printf("星期二\n");
break;
}
case 3:
{
printf("星期三\n");
break;
}
case 4:
{
printf("星期四\n");
break;
}
case 5:
{
printf("星期五\n");
break;
}
case 6:
{
printf("星期六\n");
break;
}
case 7:
{
printf("星期日\n");
break;
}
default:
{
break;
}
}
}
计算玩游戏用时代码
利用clock_t实现
clock_t start, end;
start = clock();
Game(1);
end = clock();
printf("完成游戏用时%f分钟", (double)(end - start) / (CLOCKS_PER_SEC * 60));
整体代码展示
扫雷游戏的实现共使用了3个文件,一个头文件和两个源文件,分别是test.c,game.h,game.c。
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int main()
{
int input = 0, choice = 0;
do {
menu();
printf("请输入您的选择:");
scanf("%d", &input);
switch (input)
{
case 1:
{
printf("\t\t 游戏开始\n");
printf("游戏模式共分成三种模式:1、简单模式 2、中等模式 3、困难模式\n");
printf("请选择游戏模式:");
scanf("%d", &choice);
switch (choice)
{
case 1:
{
printf("\t总共有%d个雷,Have a good time!\n", EASY_NUMBEROFMINE);
clock_t start, end;
start = clock();
Game(1);
end = clock();
printf("完成游戏用时%f分钟", (double)(end - start) / (CLOCKS_PER_SEC * 60));
break;
}
case 2:
{
printf("\t总共有%d个雷,Have a good time!\n", MEDIUM_NUMBEROFMINE);
clock_t start, end;
start = clock();
Game(2);
end = clock();
printf("完成游戏用时%f分钟", (double)(end - start) / (CLOCKS_PER_SEC * 60));
break;
}
case 3:
{
printf("\t总共有%d个雷,Have a good time!\n", EXPERT_NUMBEROFMINE);
clock_t start, end;
start = clock();
Game(3);
end = clock();
printf("完成游戏用时%f分钟", (double)(end - start) / (CLOCKS_PER_SEC * 60));
break;
}
default:
{
printf("输入错误,请重新输入!\n");
break;
}
}
break;
}
case 0:
{
printf("感谢您的参与,再见\n");
break;
}
default:
{
printf("选择错误,请重新输入\n");
break;
}
}
Sleep(8888);
system("cls");
} while (input != 0);
return 0;
}
game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#define ROW 9//格子行有效长度
#define ROWS ROW+2//格子行总长度
#define COL 9//格子列有效长度
#define COLS COL+2//格子列总长度
#define EASY_NUMBEROFMINE 10//简单模式雷的个数
#define MEDIUM_NUMBEROFMINE 15//中等模式雷的个数
#define EXPERT_NUMBEROFMINE 25//困难模式雷的个数
extern int cnt1;//简单模式'*'个数
extern int cnt2;//中等模式'*'个数
extern int cnt3;//困难模式'*'个数
void Game(int digit);//进行游戏
void InitBoard(char board[][COLS], int rows, int cols, char ch);//初始化棋盘
void PrintBoard(char board[][COLS], int row, int col);//打印棋盘
void SetMine(char board[][COLS], int row, int col, int digit);//布置NUMBEROFMINE个雷
void FindMine(char mine[][COLS], char show[][COLS], int row, int col, int digit);//排查雷
int GetMinNumber(char mine[][COLS], int x, int y);//统计周围雷的个数
void OutwardRadiation(char mine[][COLS], char show[][COLS], int x, int y, int digit);//实现辐射式显示雷个数
void ShowTime();//显示时间
void menu();//菜单
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int cnt1 = ROW * COL - EASY_NUMBEROFMINE;
int cnt2 = ROW * COL - MEDIUM_NUMBEROFMINE;
int cnt3 = ROW * COL - EXPERT_NUMBEROFMINE;
//菜单
void menu()
{
printf("****************************************************\n");
printf("\t\t欢迎来到扫雷游戏\n");
printf("\t 输入1开始游戏 输入0结束游戏\n");
ShowTime();
printf("****************************************************\n");
}
//初始化棋盘
void InitBoard(char board[][COLS], int rows, int cols, char ch)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = ch;
}
}
}
//打印棋盘
void PrintBoard(char board[][COLS], int row, int col)
{
printf("***********************扫雷************************\n");
for (int i = 0; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
if (i == 0)
{
printf("%d ", j);
}
else
{
printf("%c ", board[i][j]);
}
}
printf("\n");
}
}
//布置NUMBEROFMINE个雷
void SetMine(char board[][COLS], int row, int col, int digit)
{
srand((unsigned)time(NULL));
int count = 0;
if (digit == 1)
count = EASY_NUMBEROFMINE;
else if (digit == 2)
count = MEDIUM_NUMBEROFMINE;
else
count = EXPERT_NUMBEROFMINE;
while (count > 0)
{
int r = rand() % row + 1;
int c = rand() % col + 1;
if (board[r][c] == '0')
{
board[r][c] = '1';
count--;
}
}
}
//排查雷
void FindMine(char mine[][COLS], char show[][COLS], int row, int col, int digit)
{
int count = 0;
if (digit == 1)
{
count = EASY_NUMBEROFMINE;
while (cnt1 > 0) {
int x = 0, y = 0, choice = 0;
printf("\t输入1表示打开'*',输入0表示标记雷位置\n");
printf("请选择:");
scanf("%d", &choice);
if (choice == 0)
{
printf("请输入标记雷的坐标:");
scanf("%d%d", &x, &y);
show[x][y] = '#';
PrintBoard(show, row, col);
if (mine[x][y] == '1')
{
count--;
}
printf("还剩%d个雷\n", count);
if (count == 0)
{
break;
}
}
else if (choice == 1)
{
printf("请输入要打开'*'的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,您被炸了!!!\n");
printf("Game Over\n");
PrintBoard(mine, ROW, COL);
printf("以上是雷的分布\n");
break;
}
else
{
OutwardRadiation(mine, show, x, y, digit);
PrintBoard(show, row, col);
}
}
else
{
printf("输入非法,请重新输入!\n");
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
if (cnt1 == 0 || count == 0)
{
printf("恭喜你,排雷成功!!!\n");
}
}
else if (digit == 2)
{
count = MEDIUM_NUMBEROFMINE;
while (cnt2 > 0) {
int x = 0, y = 0, choice = 0;
printf("\t输入1表示打开'*',输入0表示标记雷位置\n");
printf("请选择:");
scanf("%d", &choice);
if (choice == 0)
{
printf("请输入标记雷的坐标:");
scanf("%d%d", &x, &y);
show[x][y] = '#';
PrintBoard(show, row, col);
if (mine[x][y] == '1')
{
count--;
}
printf("还剩%d个雷\n", count);
if (count == 0)
{
break;
}
}
else if (choice == 1)
{
printf("请输入要打开'*'的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,您被炸了!!!\n");
printf("Game Over\n");
PrintBoard(mine, ROW, COL);
printf("以上是雷的分布\n");
break;
}
else
{
OutwardRadiation(mine, show, x, y, digit);
PrintBoard(show, row, col);
}
}
else
{
printf("输入非法,请重新输入!\n");
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
if (cnt2 == 0 || count == 0)
{
printf("恭喜你,排雷成功!!!\n");
}
}
else
{
count = EXPERT_NUMBEROFMINE;
while (cnt3 > 0) {
int x = 0, y = 0, choice = 0;
printf("\t输入1表示打开'*',输入0表示标记雷位置\n");
printf("请选择:");
scanf("%d", &choice);
if (choice == 0)
{
printf("请输入标记雷的坐标:");
scanf("%d%d", &x, &y);
show[x][y] = '#';
PrintBoard(show, row, col);
if (mine[x][y] == '1')
{
count--;
}
printf("还剩%d个雷\n", count);
if (count == 0)
{
break;
}
}
else if (choice == 1)
{
printf("请输入要打开'*'的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,您被炸了!!!\n");
printf("Game Over\n");
PrintBoard(mine, ROW, COL);
printf("以上是雷的分布\n");
break;
}
else
{
OutwardRadiation(mine, show, x, y, digit);
PrintBoard(show, row, col);
}
}
else
{
printf("输入非法,请重新输入!\n");
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
if (cnt3 == 0 || count == 0)
{
printf("恭喜你,排雷成功!!!\n");
}
}
}
//实现辐射式显示雷个数
void OutwardRadiation(char mine[][COLS], char show[][COLS], int x, int y, int digit)
{
if (x == 0 || y == 0 || x == ROWS - 1 || y == COLS - 1)//位置是否越界
{
return;
}
if (show[x][y] != '*')//遍历过的位置不再遍历,防止出现死递归
{
return;
}
//不同模式
if (digit == 1)
{
cnt1--;
}
else if (digit == 2)
{
cnt2--;
}
else
{
cnt3--;
}
int count = GetMinNumber(mine, x, y, digit) + '0';
//该位置不为0时递归停止
if (count != '0')
{
show[x][y] = count;
return;
}
else
{
show[x][y] = '0';
OutwardRadiation(mine, show, x - 1, y - 1, digit);
OutwardRadiation(mine, show, x, y - 1, digit);
OutwardRadiation(mine, show, x + 1, y - 1, digit);
OutwardRadiation(mine, show, x - 1, y, digit);
OutwardRadiation(mine, show, x + 1, y, digit);
OutwardRadiation(mine, show, x - 1, y + 1, digit);
OutwardRadiation(mine, show, x, y + 1, digit);
OutwardRadiation(mine, show, x + 1, y + 1, digit);
}
}
//统计周围雷的个数
int GetMinNumber(char mine[][COLS], int x, int y)
{
return mine[x - 1][y - 1]
+ mine[x][y - 1]
+ mine[x + 1][y - 1]
+ mine[x - 1][y]
+ mine[x + 1][y]
+ mine[x - 1][y + 1]
+ mine[x][y + 1]
+ mine[x + 1][y + 1] - 8 * '0';
}
//显示时间
void ShowTime()
{
SYSTEMTIME sys;
printf("");
GetLocalTime(&sys);//获取系统时间
printf("\t当前时间为:%4d/%02d/%02d %02d:%02d:%02d ",
sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond);
switch (sys.wDayOfWeek)
{
case 1:
{
printf("星期一\n");
break;
}
case 2:
{
printf("星期二\n");
break;
}
case 3:
{
printf("星期三\n");
break;
}
case 4:
{
printf("星期四\n");
break;
}
case 5:
{
printf("星期五\n");
break;
}
case 6:
{
printf("星期六\n");
break;
}
case 7:
{
printf("星期日\n");
break;
}
default:
{
break;
}
}
}
//进行游戏
void Game(int digit)
{
char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印棋盘
SetMine(mine, ROW, COL, digit);//布置10个雷
PrintBoard(show, ROW, COL);
FindMine(mine, show, ROW, COL, digit);
}
运行结果