扫雷游戏(C语言实现)
《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
以上就是扫雷的介绍,来源百度百科。
我在网上找了几个扫雷游戏,对于这样一个游戏我们不仅仅要实现其基础的功能,还要为了提升游戏性,我们需要实现两个功能
1.当第一个为炸弹时,我们需要将炸弹移动到别的地方,防止游戏一开始就结束
2.当我们选择的位置周边不存在炸弹,同时以九宫格的方式扩撒,将周边不存在炸弹的地方展开,这样可以大大的减去不必要的游戏过程,提高游戏的效率与可玩性。
这是我在CSDN上实现的第三个小游戏,这个游戏与前几个很相似,我们可以借用前几个的界面和框架来完成这个游戏
1.https://blog.csdn.net/qq_35423154/article/details/101311952猜数字游戏
2.https://blog.csdn.net/qq_35423154/article/details/102490244三子棋游戏
首先我们需要明确思路,我们可以用两个棋盘来实现这个游戏,一个用来存放炸弹,一个用来给玩家显示炸弹的位置及周围的炸弹数,这样一个二维的棋盘我们可以使用二维数组来实现。
二维数组的建立及初始化
例如我们要使用一个9X9的棋盘,我们这时候应当建立一个11X11的即(N+2)x(N+2)的棋盘
这是我们显示的9X9的棋盘
但是我们实际建立的是这样一个棋盘
为什么我们需要在每一个边界扩大一行呢?因为如果当我们要判断周围的雷数时,如果不扩大,对于边界的格子可能要判断周围两个位置,四个位置,而不在边界的要判断八个位置,我们需要增加多种条件,为了简化算法,我们可以采用整体扩大一圈,来保证每一个格子周围都有八个格子,而边上扩大的那一圈仅仅辅助计算,不显示也不作用。
宏定义棋盘及炸弹
我们设置好行数和列数之后,再在其基础上设置一个辅助棋盘既周围扩大一圈的棋盘,分别在行数及列数的基础上加二,然后我们设置炸弹数,用宏定义的好处是后期修改时方便
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define BOMBSNUM 10
#define SAFENUM ROW*COL-BOMBSNUM
初始化棋盘
我们需要两个棋盘,一个给用户进行游戏,一个用来存放炸弹。给用户游戏的我们全部初始化为‘*’,存放炸弹的全部初始化为‘0’
void InitBoard(char Board[ROWS][COLS], int row, int col,char ch)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
Board[i][j] = ch;
}
}
}
用字符0和1来代表炸弹可以方便我们后面计算周围的炸弹
放置炸弹
void SetBombs(char Bombs[ROWS][COLS], int row, int col, int Bombsnum)
{
int x, y,count=0;
while( count < Bombsnum )
{
x = rand() % row+1;
y = rand() % col+1;
if (Bombs[x][y] != '1')
{
Bombs[x][y] = '1';
count++;
}
}
}
使用rand函数来获取一个随机的坐标,当坐标不重复并且所处位置没有炸弹时,将炸弹存放
选择菜单
void GameMenu()
{
printf("*****************************************\n");
printf("********* 1.开始游戏 *********\n");
printf("********* 0.结束游戏 *********\n");
printf("*****************************************\n");
}
显示棋盘
void ShowBoard(char Board[ROWS][COLS], int row, int col)
{
int i, j;
printf(" ");
for (i = 0; i < row+1; i++)
{
printf("%d ",i);
}
printf("\n");
for (i = 0; i < row; i++)
{
if (0 == i)
printf(" |");
else
printf("|");
printf("---");
}
printf("|\n");
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (0 == j)
{
printf(" ");
printf("%d ", i + 1);
printf("|");
}
printf(" %c |", Board[i+1][j+1]);
}
printf("\n");
for (j = 0; j < col; j++)
{
if ( 0 == j )
printf(" |");
printf("---|");
}
printf("\n");
}
}
用这个函数来向玩家展示棋盘,这里用了大量的代码其实是我为了让棋盘更加美观,可根据情况修改。
以上的几个函数在上一个三子棋游戏中都详细讲解过,在这里就不再多提
计算周围的炸弹数
因为我们之前初始化时将用字符1代表有炸弹,字符0代表没有炸弹,所以在这一部我们可以利用这个规律,来计算周围的炸弹数。
我们只需要让选择的位置周边的八个格子全部相加,然后减去8个字符0,即可得到整形的炸弹数,而后在主函数给其加上一个字符0将其将这个数字变为字符型的对应数字,存放与棋盘中
int BombsAround(char Bombs[ROWS][COLS], int row, int col)
{
return Bombs[row - 1][col] + Bombs[row + 1][col] + Bombs[row][col - 1] + Bombs[row][col + 1] + Bombs[row + 1][col + 1] + Bombs[row - 1][col - 1] + Bombs[row + 1][col - 1] + Bombs[row - 1][col + 1] - 8 * '0';
}
胜利条件
int WinGame(char Board[ROWS][COLS], int row, int col)
{
int i, j, flag=0;
for (i=1; i < row; i++)
{
for (j = 1; j < col; j++)
{
if (Board[i][j] == '*')
flag++;
}
}
return flag;
}
我们只需要计算棋盘中剩余的炸弹数,当剩余炸弹数等于我们的放置炸弹数时,游戏胜利
附加功能1.当第一次为炸弹时,将炸弹移动到别的位置
这个函数实现的方法很简单,我们只需要将那个位置的炸弹清空,再次调用SetBombs函数重新设置一个炸弹即可
int BombsAround(char Bombs[ROWS][COLS], int row, int col)
{
return Bombs[row - 1][col] + Bombs[row + 1][col] + Bombs[row][col - 1] + Bombs[row][col + 1] + Bombs[row + 1][col + 1] + Bombs[row - 1][col - 1] + Bombs[row + 1][col - 1] + Bombs[row - 1][col + 1] - 8 * '0';
}
附加功能2.当我们选择的位置周边不存在炸弹,同时以九宫格的方式扩撒,将周边不存在炸弹的地方展开
这项功能是windows系统扫雷自带的一个功能,当我们选择一个周围没有炸弹的位置时,以九宫格扩撒,将不存在炸弹的格子显示为空,直到有一个格子周围存在炸弹。
这个很简单我们就可以想到用递归函数来完成,我们只需要用BombsAround函数来知道这个位置是否存在炸弹,如果不存在则显示为‘ ’,如果存在则显示炸弹数。我们可以用递归的方式,当位置合法时,对他周围的八个格子进行判断,那八个格子再对他们周围的格子进行判断
void SafeArea(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int flag;
flag = BombsAround(Bombs, row, col);
if (0 == flag)
{
Board[row][col] = ' ';
if (row - 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row - 1][col] == '*')
SafeArea(Board, Bombs, row-1, col);
if (row + 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row +1 ][col] == '*')
SafeArea(Board, Bombs, row+1, col);
if (row > 0 && row <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row][col - 1] == '*')
SafeArea(Board, Bombs, row, col-1);
if (row > 0 && row <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row][col + 1] == '*')
SafeArea(Board, Bombs, row, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row - 1][col - 1] == '*')
SafeArea(Board, Bombs, row-1, col-1);
if (row + 1 > 0 && row + 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row + 1][col + 1] == '*')
SafeArea(Board, Bombs, row+1, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row - 1][col + 1] == '*')
SafeArea(Board, Bombs, row-1, col+1);
if (row + 1 > 0 && row + 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row + 1][col - 1] == '*')
SafeArea(Board, Bombs, row+1, col-1);
}
else
{
Board[row][col] = BombsAround(Bombs, row, col) + '0';
}
}
排查炸弹
char FindBombs(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int x, y,count=1;
while(count)
{
printf("请输入行和列:\n");
scanf("%d%d",&x,&y);
system("CLS");
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (Bombs[x][y] == '1')
{
if (1 == count)
{
MoveBomb(Bombs, x, y);
Board[x][y] = BombsAround(Bombs, x, y) + '0';
if (Board[x][y] == '0')
Board[x][y] = ' ';
SafeArea(Board, Bombs, x, y);
count++;
}
else
{
ShowBoard(Bombs, ROW, COL);
return 'F';
}
}
else if (Board[x][y] >= '0' && Board[x][y] <= '8' && Board[x][y] != '*' && Board[x][y] != ' ')
{
printf("该坐标已被输入\n");
}
else
{
Board[x][y] = BombsAround(Bombs, x, y) + '0';
SafeArea(Board, Bombs, x, y);
count++;
if (WinGame(Board, ROW, COL) == BOMBSNUM)
return 'T';
ShowBoard(Board, ROW, COL);
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
}
我们需要判断输入是否合法,输入是否重复,如果错误则重新输入,当输入的位置为炸弹时,如果是第一次输入,则将炸弹移动到别的位置,如果不是第一次输入,则代表游戏失败,返回’F’。每次输入完一个位置,我们将那个位置周围的炸弹数保存到Board棋盘中,既玩家看到的棋盘,然后再使用展开函数来展开周围不必要的格子,并且用WinGame函数判断是否胜利
游戏控制函数
void Game(char Board[ROWS][COLS],char Bombs[ROWS][COLS], int row, int col)
{
char flag;
InitBoard(Board, ROWS, COLS, '*');
InitBoard(Bombs, ROWS, COLS, '0');
SetBombs(Bombs, ROW, COL, BOMBSNUM);
ShowBoard(Board, ROW, COL);
printf("\n");
ShowBoard(Bombs, ROW, COL);
flag = FindBombs(Board, Bombs, ROW, COL);
if (flag == 'F')
{
printf("该位置为炸弹,游戏结束\n");
}
else
{
printf("游戏胜利\n");
}
}
这一步只需调用前面写完的函数即可
完整代码
game.c
#include "head.h"
void GameMenu()
{
printf("*****************************************\n");
printf("********* 1.开始游戏 *********\n");
printf("********* 0.结束游戏 *********\n");
printf("*****************************************\n");
}
void InitBoard(char Board[ROWS][COLS], int row, int col,char ch)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
Board[i][j] = ch;
}
}
}
//初始化
void SetBombs(char Bombs[ROWS][COLS], int row, int col, int Bombsnum)
{
int x, y,count=0;
while( count < Bombsnum )
{
x = rand() % row+1;
y = rand() % col+1;
if (Bombs[x][y] != '1')
{
Bombs[x][y] = '1';
count++;
}
}
}
//放置炸弹
void ShowBoard(char Board[ROWS][COLS], int row, int col)
{
int i, j;
printf(" ");
for (i = 0; i < row+1; i++)
{
printf("%d ",i);
}
printf("\n");
for (i = 0; i < row; i++)
{
if (0 == i)
printf(" |");
else
printf("|");
printf("---");
}
printf("|\n");
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (0 == j)
{
printf(" ");
printf("%d ", i + 1);
printf("|");
}
printf(" %c |", Board[i+1][j+1]);
}
printf("\n");
for (j = 0; j < col; j++)
{
if ( 0 == j )
printf(" |");
printf("---|");
}
printf("\n");
}
}
//界面
int BombsAround(char Bombs[ROWS][COLS], int row, int col)
{
return Bombs[row - 1][col] + Bombs[row + 1][col] + Bombs[row][col - 1] + Bombs[row][col + 1] + Bombs[row + 1][col + 1] + Bombs[row - 1][col - 1] + Bombs[row + 1][col - 1] + Bombs[row - 1][col + 1] - 8 * '0';
}
//算出周围的炸弹
void MoveBomb(char Bombs[ROWS][COLS], int row, int col)
{
Bombs[row][col] = '0';
SetBombs(Bombs, ROW, COL, 1);
}
//防止玩家第一次选择时遇到炸弹,将炸弹移动到别的位置
void SafeArea(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int flag;
flag = BombsAround(Bombs, row, col);
if (0 == flag)
{
Board[row][col] = ' ';
if (row - 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row - 1][col] == '*')
SafeArea(Board, Bombs, row-1, col);
if (row + 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row +1 ][col] == '*')
SafeArea(Board, Bombs, row+1, col);
if (row > 0 && row <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row][col - 1] == '*')
SafeArea(Board, Bombs, row, col-1);
if (row > 0 && row <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row][col + 1] == '*')
SafeArea(Board, Bombs, row, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row - 1][col - 1] == '*')
SafeArea(Board, Bombs, row-1, col-1);
if (row + 1 > 0 && row + 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row + 1][col + 1] == '*')
SafeArea(Board, Bombs, row+1, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row - 1][col + 1] == '*')
SafeArea(Board, Bombs, row-1, col+1);
if (row + 1 > 0 && row + 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row + 1][col - 1] == '*')
SafeArea(Board, Bombs, row+1, col-1);
}
else
{
Board[row][col] = BombsAround(Bombs, row, col) + '0';
}
}
int WinGame(char Board[ROWS][COLS], int row, int col)
{
int i, j, flag=0;
for (i=1; i < row; i++)
{
for (j = 1; j < col; j++)
{
if (Board[i][j] == '*')
flag++;
}
}
return flag;
}
char FindBombs(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int x, y,count=1;
while(count)
{
printf("请输入行和列:\n");
scanf("%d%d",&x,&y);
system("CLS");
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (Bombs[x][y] == '1')
{
if (1 == count)
{
MoveBomb(Bombs, x, y);
Board[x][y] = BombsAround(Bombs, x, y) + '0';
SafeArea(Board, Bombs, x, y);
count++;
}
else
{
ShowBoard(Bombs, ROW, COL);
return 'F';
}
}
else if (Board[x][y] >= '0' && Board[x][y] <= '8' && Board[x][y] != '*' && Board[x][y] != ' ')
{
printf("该坐标已被输入\n");
}
else
{
Board[x][y] = BombsAround(Bombs, x, y) + '0';
SafeArea(Board, Bombs, x, y);
count++;
if (WinGame(Board, ROW, COL) == BOMBSNUM)
return 'T';
ShowBoard(Board, ROW, COL);
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
}
//排查炸弹
void Game(char Board[ROWS][COLS],char Bombs[ROWS][COLS], int row, int col)
{
char flag;
InitBoard(Board, ROWS, COLS, '*');
InitBoard(Bombs, ROWS, COLS, '0');
SetBombs(Bombs, ROW, COL, BOMBSNUM);
ShowBoard(Board, ROW, COL);
printf("\n");
flag = FindBombs(Board, Bombs, ROW, COL);
if (flag == 'F')
{
printf("该位置为炸弹,游戏结束\n");
}
else
{
printf("游戏胜利\n");
}
}
test.c
#include "head.h"
int main()
{
int n;
char board[ROWS][COLS] = { 0 };
char bombs[ROWS][COLS] = { 0 };
srand((int)time(NULL));
do
{
GameMenu();
printf("请输入您的选择:\n");
scanf("%d", &n);
switch (n)
{
case 1:
Game(board, bombs, ROWS, COLS);
break;
case 0:
printf("结束游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}while (n);
return 0;
}
head.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define BOMBSNUM 10
void GameMenu();
void InitBoard(char Board[ROWS][COLS], int row, int col, char ch);
void SetBombs(char Bombs[ROWS][COLS], int row, int col, int Bombsnum);
void ShowBoard(char Board[ROWS][COLS], int row, int col);
int BombsAround(char Bombs[ROWS][COLS], int row, int col);
char FindBombs(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col);
void Game(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col);
void MoveBomb(char Bombs[ROWS][COLS], int row, int col);
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y);
void SafeArea(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col);
代码链接:
https://github.com/HONGYU-LEE/test/tree/master/project/Mine%20sweeper
上一篇: C语言经典项目之二——扫雷
下一篇: 实现黑客帝国的代码雨