C语言实现扫雷游戏
前言
扫雷游戏是一个经典的小游戏,一个简单的扫雷游戏由9x9的方格组成,里面包含了10个雷,当我们找出全部的雷,即可获得游戏的胜利。通过C语言写一个简单的扫雷游戏只需掌握数组以及会调用函数即可实现一个输入坐标来排查雷的扫雷游戏,下面由我来一步一步讲解这个游戏的具体实现,希望对大家有帮助。
清屏以及暂停指令(使游戏更加美观)
#include<Windows.h>
#include<stdlib.h>
system("cls");//清屏
system("pause");//暂停
system
是头文件stdlib.h
头文件里的一个函数,可以调用Windows的一些指令,调用这些Windows下的指令需要包含头文件Windows.h
,其中本次我们用到的指令有cls
清屏指令(每排出一个坐标,就可以清空屏幕,去掉一些不需要的东西)和pause
指令(暂停指令可以使游戏进程停到需要的步骤)
游戏实现分析及设计思路
1.菜单打印及游戏循环主体
//菜单打印
void GameMenu()
{
printf("****欢迎来到扫雷游戏****\n");
printf("************************\n");
printf("****** 1.进入游戏 ******\n");
printf("****** 0.退出游戏 ******\n");
printf("************************\n");
}
int main()
{
srand((unsigned int)time(NULL));//随机数生成
while (1) {
GameMenu();//打印棋盘
int n;
printf("请输入你的选择:");
scanf("%d", &n);
switch (n) {
case 0://0退出游戏,程序停止
break;
case 1://1进入游戏
system("cls");//清除菜单,简化页面
printf("进入游戏\n");
//下面可以加一些游戏规则
printf("游戏规则:\n");
printf("1.输入要排查雷的坐标\n");
printf("2.通过标记雷来完成游戏,标记够数且标记对即可获得胜利\n");
printf("3.扫出的数字为周围雷的数量\n");
system("pause");//此语句当读完其游戏规则后,按任意位置即可开始游戏
system("cls");//清除屏幕
Game();
break;
default:
system("cls");//输入错误,重新进入循环
printf("输入错误,请重输入!\n");
system("pause");
system("cls");//清空屏幕
break;
}
}
return 0;
}
2.棋盘初始化
一个最简单的扫雷游戏是由9x9的棋盘组成,其中包含了10个雷,每个格子是否是雷,我们需要用一个字符来确定,这时我们就可以考虑用字符‘1’表示雷,字符‘0’表示不是雷。
要想实现扫雷游戏,我们需要用两个上面的棋盘,一个用来存放雷的信息,一个用来向玩家展示的棋盘。我们先对棋盘进行初始化,雷区棋盘初始化为‘0’,展示棋盘初始为‘*’。写出下面函数。
void BoardInit(char Board[ROWS][COLS], char a)
//Board是一个二维数组为需要初始化的棋盘,a为初始化的字符
//展示棋盘初始化a为‘*’,雷棋盘初始化a为‘0’
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)Board[i][j] = a;
}
}
3.雷区布雷
我们要在初始化后的雷区放置地雷需要将相应的‘0’转化为‘1’,为了使雷随机布置,我们可以使用rand函数,配合time.h
使用,生成随机坐标用‘1’来替代0。
#include<time.h>
#define BOMBNUM 10//定义宏来表示需要布置炸弹数量
srand((unsigned int)time(NULL));//生成随机数
void PutBomb(char Board[ROWS][COLS])
{
int count = BOMBNUM;//指定雷数量
while (count){//当布置炸弹数量足够停止循环
int x = rand() % 9 + 1;//生成1~9的随机数
int y = rand() % 9 + 1;
//布雷前提需要此位置之前没有布置上雷,下面语句可以判断
if (Board[x][y] == '0') {
Board[x][y] = '1';//布置雷
count--;//成功布雷,炸弹数量减1
}
}
}
4.棋盘打印
我们需要打印展示棋盘来给玩家使用,由于我们这个游戏通过输入坐标来实现扫雷,我们可以为棋盘加一个坐标轴,方便玩家输入坐标。
void BoardPrint(char Board[ROWS][COLS])
{
for (int i = 0; i <= ROW; i++)printf("%d ", i);
printf("x轴\n");//坐标轴x轴
for (int i = 1; i <= ROW; i++)
{
printf("%d ", i);
for (int j = 1; j <= COL; j++)printf("%c ", Board[j][i]);
printf("\n");
}
printf("y轴\n");//坐标轴y轴
}
5.周围雷数量写入(核心部分)
如上方图片所示,当我们排查(6,4)这个坐标时,可以通过s[x][y] = (r[x-1][y-1] - '0') + (r[x-1][y] - '0') + (r[x-1][y+1] - '0') + (r[x][y-1] - '0') + (r[x][y+1] - '0') + (r[x+1][y-1] - '0') + (r[x+1][y] - '0') + (r[x+1][y+1]) ;
很简单就可以计算这个位置周围的雷的数量为2;但是如果排查(8,9)这个坐标时,就不能用这个公式,有一部分数组没有数据,会造成数组越界,所以我们可以考虑右边这个方法,给整个格子加一圈,方便计算雷的数量。
int GameBegain(char realboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y)
//x,y是排查的坐标
{
if (realboard[x][y] == '1') {//排查坐标为雷,被炸死
system("cls");
printf("你被炸死了!!\n");
BoardPrint(realboard);
system("pause"); system("cls");
return 0;
}
else{//不是雷计算出周围雷的数量
showboard[x][y] = (realboard[x-1][y-1] - '0') + (realboard[x-1][y] - '0') + (realboard[x-1][y+1] - '0') + (realboard[x][y-1] - '0') + (realboard[x][y+1] - '0') + (realboard[x+1][y-1] - '0') + (realboard[x+1][y] - '0') + (realboard[x+1][y+1]) ;
return 1;
}
}
6.周边排雷
我们玩扫雷游戏时,点击的不是雷周围如果没有雷就会连片展出相应区域。
要想实现这个功能我们可以通过函数递归来实现,函数基础知识可见C语言:递归函数基础知识。
void ChainFindBomb(char realboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y)
{
if (realboard[x][y] != '1'&&1<=x&&x<=ROW&&1<=y&&y<=COL) {
char a = (realboard[x - 1][y - 1] - '0') + (realboard[x - 1][y] - '0') + (realboard[x - 1][y + 1] - '0') + (realboard[x][y - 1] - '0') + (realboard[x][y + 1] - '0') + (realboard[x + 1][y - 1] - '0') + (realboard[x + 1][y] - '0') + (realboard[x + 1][y + 1]);
if (a == '0'||a=='1') {//当周围雷的数量为1或者0时,递归复用函数
if (showboard[x][y] != '*'||showboard[x][y]=='+') {
return;//被排查或被标记返回,减少递归次数
}
showboard[x][y] = a;//连片显示
SroundFind(realboard,showboard,x,y);//函数调用函数,实现递归
}
else return;//返回,停止递归
}
}
//周围排查
void SroundFind(char RealBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y) {
ChainFindBomb(RealBoard, ShowBoard, x - 1, y - 1);
ChainFindBomb(RealBoard, ShowBoard, x - 1, y);
ChainFindBomb(RealBoard, ShowBoard, x - 1, y + 1);
ChainFindBomb(RealBoard, ShowBoard, x, y - 1);
ChainFindBomb(RealBoard, ShowBoard, x, y + 1);
ChainFindBomb(RealBoard, ShowBoard, x + 1, y - 1);
ChainFindBomb(RealBoard, ShowBoard, x + 1, y);
ChainFindBomb(RealBoard, ShowBoard, x + 1, y + 1);
}
图片复现
由1个坐标向外扩散,当遇到标记、已经被点开、周围雷的数量大于1时开始返回,并使展现棋盘赋予相应的值。
7.标记雷和取消标记雷
我们直接输入标记雷的坐标就可以将展示棋盘的字符替换成标记物,就可以实现标记功能,这时我们在定义一个变量(记录标记确实为雷数量),当变量为雷的数量时,即排雷成功。
void MarkBomb(char showboard[ROWS][COLS], char realboard[ROWS][COLS],int* cou)
{
int x, y;
printf("请输入要标记的坐标:"); scanf("%d %d", &x, &y);
if (x > ROW || x < 1 || y>COL || y < 1 || showboard[x][y] != '*') {
printf("非法输入!\n"); return;//输入超限,显示输入错误
}
showboard[x][y] = '+';
if (realboard[x][y] == '1')//标记确实为雷
*cou+=1;//数量加1
}
当我们发现标记错误就要取消标记,当取消标记的位置确实为雷,那我们标记数量也要为减1。
void DisMarkBomb(char showboard[ROWS][COLS],char realboard[ROWS][COLS], int* cou)
{
int x, y;
printf("请输入要标记的坐标:"); scanf("%d %d", &x, &y);
if (x > ROW || x < 1 || y>COL || y < 1 || showboard[x][y] != '+') {
printf("非法输入!\n"); return;//输入超限,显示输入错误
}
showboard[x][y] = '*';
if (realboard[x][y] == '1')//取消标记位置确实为雷
*cou-=1;//数量减1
}
完整代码
将上面的代码整合,我们就得到了一个通过C语言写的一个简单的扫雷游戏,以下是我的全部代码。
1.bomb.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2//扩展雷区
#define COLS COL+2//扩展雷区
#define BOMBNUM 10//炸弹数量
//菜单打印
void GameMenu();
//游戏页面主体
void MainGame();
void Game();
//棋盘初始化和打印
void BoardInit(char Board[ROWS][COLS], char a);
void BoardPrint(char Board[ROWS][COLS]);
//放雷
void PutBomb(char Board[ROWS][COLS]);
//正式游戏开始
int GameBegain(char realboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y);
//连锁扫雷
void ChainFindBomb(char realboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y);
//周围雷区查找
void SroundFind(char RealBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y);
//标记雷
void MarkBomb(char showboard[ROWS][COLS], char realboard[ROWS][COLS], int* cou);
//取消标记雷
void DisMarkBomb(char showboard[ROWS][COLS], char realboard[ROWS][COLS], int* cou);
2.bomb.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"bomb.h"
//菜单打印
void GameMenu()
{
printf("****欢迎来到扫雷游戏****\n");
printf("************************\n");
printf("****** 1.进入游戏 ******\n");
printf("****** 0.退出游戏 ******\n");
printf("************************\n");
}
//扫雷游戏主体
void Game()
{
char ShowBoard[ROWS][COLS];
char RealBoard[ROWS][COLS];
char n = '*', m = '0';
BoardInit(ShowBoard, n);
BoardInit(RealBoard, m);
PutBomb(RealBoard);
//BoardPrint(RealBoard);
BoardPrint(ShowBoard);
int flag = 1;
int count = 0;
while (flag)
{
int x, y;
printf("请输入要排查的坐标:"); scanf("%d %d", &x, &y);
if (x > ROW || x < 1 || y>COL || y < 1) {
printf("输入错误,请重新输入!");
continue;
}
flag = GameBegain(RealBoard, ShowBoard, x, y);
if (flag)
{
SroundFind(RealBoard, ShowBoard, x, y);
system("cls"); BoardPrint(ShowBoard);
//BoardPrint(RealBoard);
while (1) {
int n;
printf("是否需要标记雷(输入1(是),0(否)):"); scanf("%d", &n);
if (n==0)break;
else if (n == 1) {
MarkBomb(ShowBoard, RealBoard, &count);
system("cls");
BoardPrint(ShowBoard);
}
else printf("非法输入!\n");
}
while (1) {
int n;
printf("是否需要取消标记雷(输入1(是),0(否)):"); scanf("%d", &n);
if (n == 0)break;
else if (n == 1) {
DisMarkBomb(ShowBoard, RealBoard, &count);
system("cls");
BoardPrint(ShowBoard);
}
else printf("非法输入!\n");
}
system("cls"); BoardPrint(ShowBoard);
}
if (count == BOMBNUM) {
printf("恭喜你找出了所有的雷,排雷成功!\n");
system("pause");
system("cls");
break;
}
}
}
//棋盘初始化和打印
void BoardInit(char Board[ROWS][COLS], char a)
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)Board[i][j] = a;
}
}
void BoardPrint(char Board[ROWS][COLS])
{
for (int i = 0; i <= ROW; i++)printf("%d ", i);
printf("x轴\n");
for (int i = 1; i <= ROW; i++)
{
printf("%d ", i);
for (int j = 1; j <= COL; j++)printf("%c ", Board[j][i]);
printf("\n");
}
printf("y轴\n");
}
//放雷
void PutBomb(char Board[ROWS][COLS])
{
int count = BOMBNUM;
while (count)
{
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (Board[x][y] == '0') {
Board[x][y] = '1';
count--;
}
}
}
//正式游戏开始
int GameBegain(char realboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y)
{
//printf("%c %d %d", realboard[x][y],x,y);
if (realboard[x][y] == '1') {
system("cls");
printf("你被炸死了!!\n");
BoardPrint(realboard);
system("pause"); system("cls");
return 0;
}
else
{
showboard[x][y] = (realboard[x-1][y-1] - '0') + (realboard[x-1][y] - '0') + (realboard[x-1][y+1] - '0') + (realboard[x][y-1] - '0') + (realboard[x][y+1] - '0') + (realboard[x+1][y-1] - '0') + (realboard[x+1][y] - '0') + (realboard[x+1][y+1]) ;
return 1;
}
}
void ChainFindBomb(char realboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y)
{
if (realboard[x][y] != '1'&&1<=x&&x<=ROW&&1<=y&&y<=COL) {
char a = (realboard[x - 1][y - 1] - '0') + (realboard[x - 1][y] - '0') + (realboard[x - 1][y + 1] - '0') + (realboard[x][y - 1] - '0') + (realboard[x][y + 1] - '0') + (realboard[x + 1][y - 1] - '0') + (realboard[x + 1][y] - '0') + (realboard[x + 1][y + 1]);
if (a == '0'||a=='1') {
if (showboard[x][y] != '*'||showboard[x][y]=='+') {
return;
}
showboard[x][y] = a;
SroundFind(realboard,showboard,x,y);
}
else return;
}
}
void SroundFind(char RealBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y) {
ChainFindBomb(RealBoard, ShowBoard, x - 1, y - 1);
ChainFindBomb(RealBoard, ShowBoard, x - 1, y);
ChainFindBomb(RealBoard, ShowBoard, x - 1, y + 1);
ChainFindBomb(RealBoard, ShowBoard, x, y - 1);
ChainFindBomb(RealBoard, ShowBoard, x, y + 1);
ChainFindBomb(RealBoard, ShowBoard, x + 1, y - 1);
ChainFindBomb(RealBoard, ShowBoard, x + 1, y);
ChainFindBomb(RealBoard, ShowBoard, x + 1, y + 1);
}
//标记雷
void MarkBomb(char showboard[ROWS][COLS], char realboard[ROWS][COLS],int* cou)
{
int x, y;
printf("请输入要标记的坐标:"); scanf("%d %d", &x, &y);
if (x > ROW || x < 1 || y>COL || y < 1 || showboard[x][y] != '*') {
printf("非法输入!\n"); return;
}
showboard[x][y] = '+';
if (realboard[x][y] == '1')
*cou+=1;
}
//取消标记雷
void DisMarkBomb(char showboard[ROWS][COLS],char realboard[ROWS][COLS], int* cou)
{
int x, y;
printf("请输入要标记的坐标:"); scanf("%d %d", &x, &y);
if (x > ROW || x < 1 || y>COL || y < 1 || showboard[x][y] != '+') {
printf("非法输入!\n"); return;
}
showboard[x][y] = '*';
if (realboard[x][y] == '1')
*cou-=1;
}
3.test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"bomb.h"
int main()
{
srand((unsigned int)time(NULL));
while (1) {
GameMenu();
int n;
printf("请输入你的选择:");
scanf("%d", &n);
switch (n) {
case 0:
break;
case 1:
system("cls");
printf("进入游戏\n");
printf("游戏规则:\n");
printf("1.输入要排查雷的坐标\n");
printf("2.通过标记雷来完成游戏,标记够数且标记对即可获得胜利\n");
printf("3.扫出的数字为周围雷的数量\n");
system("pause");
system("cls");
Game();
break;
default:
system("cls");
printf("输入错误,请重输入!\n");
system("pause");
system("cls");
break;
}
}
return 0;
}
4.游戏结果
1.成功案例
2.失败案例