今天我们用c语言写一个简单版本的扫雷,等以后,我写的游戏多了,我们再去写一个游戏大厅,可以供玩家选择自己想玩的游戏
分析:
(1)我们用两个二维数组分别存储给玩家展示的面板和储存地雷的
(2)地雷用字符‘1’存储,不是地雷用‘0’存储,这样方便我们判断出玩家选的地方是否是雷,如果不是雷,也方便我们知道周围雷的个数
(3)可自动显示无雷区的思想:
如果周围雷的个数为0的话,就可以通过递归,把周围都展开,展开后如果还出现‘0’此(‘0’为周围没有雷),将再通过递归,一直展示出非‘0’数字(非‘0’数字指的是有0处)或到达边界位置
给大家举个例:
这是雷的分布图,就是上面说用一个二维数组存储出来的,‘0’表示不是雷,‘1’表示雷,显然可以看见如果玩家输入一个3 3,那么就会显示为‘0’,表示周围格子都不是雷,那样玩家就要又输入8个坐标,那样会大大的打击玩家的耐心。
当我们开辟了可自动显示无雷区的功能后,再输入(3 3)这个坐标时,就可以,展示所有显然已知周围不是雷的地方,如下图,输入3 3坐标后
(4)就给玩家展示了这么多,是不是很方便,这是通过一个递归实现的,那我们如何判断玩家是否胜利了呢?
写一个方法,统计出我们所用的10*10的格子内有多少个
‘*’,返回值为int类型,返回‘*’的个数。然后我们可以
在一个循环外,定义一个count,来接受该方法,类似如下
如何判断玩家最后是否,是选中炸弹或者除了炸弹全部打开了,可看上面那张图片中,有一个break,可以提前结束循环,如果是自动跳出循环,那么count肯定大于雷的个数,那么就说明踩到炸弹了,失败,如果count小于等于雷的个数,那么就说明自己通过判断条件,退出循环的,那么说明剩下的格子全是雷了,那说明玩家胜利了,代码可如下图
这是选择是否进去游戏的一个界面
这是进去后,给的图
下面是点到炸弹后,输了界面,还会给玩家显示地雷分布图,然后再让玩家选择是否再来一把
这就是一个大部分的分析,然后后面是源代码,我注释写的很清楚,每一部分是干嘛的,我都写了,希望对大家有帮助
MineSweep.h
#ifndef _MINESWEEP_H_
#define _MINESWEEP_H_
#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
#include<time.h>
#pragma warning(disable:4996)
#define ROW 12 //行
#define LIE 12 //列
#define MINE 20 //地雷个数
int determine(char board[][LIE], int row, int lie); //判断还剩下多少*位置,就可以知道玩家什么时候胜利,知道剩下格子数减去炸弹数,但是我们有两行两列,是不用的,所以我们只统计[1-(row-2)]之间的'*'
void voluntarily(char board[][LIE], char mineBoard[][LIE], int row, int lie); //可自动展示无雷区,然后把如果点到'0',周围都打开,并且,如果周围也有'0',周围也自动打开
void boardInit(char mineBoard[][LIE], int row, int lie, char ch); //给面板初始化 最后一个形参为把面板初始化为什么‘字符’
char isNunmber(char mineBoard[][LIE], int row, int lie); //确定mineBoard[row][lie]不是炸弹,返回他周围炸弹的个数,但是是字符。
int judgeMine(char mineBoard[][LIE], int row, int lie); //判断玩家选的位置是否是炸弹,返回1,则是炸弹,返回0则不是炸弹
void printBoard(char mineBoard[][LIE], int row, int lie); //显示面板
void landMines(char mineBoard[][LIE],int row,int lie); //给存放地雷的面板,随机存放20颗地雷,字符1代表是地雷,字符0代表无地雷
void mineSweepGame(); //扫雷游戏
#endif
MineSweep.c
#include "MineSweep.h"
//确定mineBoard[row][lie]不是炸弹,返回他周围炸弹的个数,但是是字符。
char isNunmber(char mineBoard[][LIE], int row, int lie){
if (row >=1 && row <= 10 && lie >=1 && lie <= 10){
char ch = mineBoard[row - 1][lie - 1] + mineBoard[row - 1][lie] + mineBoard[row - 1][lie + 1] + mineBoard[row][lie - 1] + mineBoard[row][lie + 1] + mineBoard[row + 1][lie - 1] + mineBoard[row + 1][lie] + mineBoard[row + 1][lie + 1] - 7 * '0';
return ch;
}
}
//判断玩家选的位置是否是炸弹,返回1,则是炸弹,返回0则不是炸弹
int judgeMine(char mineBoard[][LIE], int row, int lie){
if (mineBoard[row][lie] == '1'){
return 1;
}
else{
return 0;
}
}
//显示面板
void printBoard(char mineBoard[][LIE], int row, int lie){
printf(" ");
for (int i = 1; i <= lie - 2; i++){
if (i != lie - 2){
printf(" | %2d", i);
}
else{
printf(" | %2d |", i);
}
}
printf("\n");
printf("-------------------------------------------------------\n");
for (int i = 1; i <=lie-2; i++){
if (i != 10){
printf("%2d ", i);
}
else{
printf("%2d ", i);
}
for (int j = 1; j <= row-2; j++){
if (j != row - 2){
printf(" | %2c", mineBoard[i][j]);
}
else{
printf(" | %2c |", mineBoard[i][j]);
}
}
printf("\n");
printf("-------------------------------------------------------\n");
}
}
//给面板初始化 最后一个形参为把面板初始化为什么‘字符’
void boardInit(char mineBoard[][LIE], int row, int lie,char ch) {
for (int i = 0; i < row; i++){
for (int j = 0; j < lie; j++){
mineBoard[i][j] = ch;
}
}
}
//给存放地雷的面板,随机存放20颗地雷,字符1代表是地雷,字符0代表无地雷
void landMines(char mineBoard[][LIE], int row, int lie){
//产生[1,10]之间的随机数字,因为只用有最外面一圈,我们不用
//k=rand()%(Y-X+1)+X; 这是一个公式,可产生[x,y]之间的随机数
int count = MINE;
while (count>0){
row = rand() % (10 - 1 + 1) + 1;
lie = rand() % (10 - 1 + 1) + 1;
//printf("%d %d\n", row,lie);
if (mineBoard[row][lie] == '0'){
mineBoard[row][lie] = '1';
count--;
}
else{
continue;
}
}
}
//可自动展示无雷区,然后把如果点到'0',周围都打开,并且,如果周围也有'0',周围也自动打开
void voluntarily(char board[][LIE], char mineBoard[][LIE], int row, int lie){
//先判断row和lie是否越界
if (row >= 1 && row <= 10 && lie >= 1 && lie <= 10){
if (isNunmber(mineBoard, row, lie) == '0'){
if (board[row][lie] == '*'){
board[row][lie] = '0'; //先给当前位置赋值为'0',把周围八个位置全部递归,递归出口:(1)越界(2)周围不再有无雷区
voluntarily(board, mineBoard, (row - 1), (lie - 1));
voluntarily(board, mineBoard, (row - 1), (lie));
voluntarily(board, mineBoard, (row - 1), (lie + 1));
voluntarily(board, mineBoard, (row), (lie - 1));
voluntarily(board, mineBoard, (row), (lie + 1));
voluntarily(board, mineBoard, (row + 1), (lie - 1));
voluntarily(board, mineBoard, (row + 1), (lie));
voluntarily(board, mineBoard, (row + 1), (lie + 1));
}
}
else{
board[row][lie] = isNunmber(mineBoard, row, lie);
}
}
}
//判断还剩下多少*位置,就可以知道玩家什么时候胜利,知道剩下格子数减去炸弹数,但是我们有两行两列,是不用的,所以我们只统计[1-(row-2)]之间的'*'
int determine(char board[][LIE], int row, int lie){
int count = 0;
for (int i = 1; i <= row-2; i++){
for (int j = 1; j <= lie-2; j++){
if (board[i][j] == '*'){
count++;
}
}
}
return count;
}
void mineSweepGame(){
/*先定义两个相同大小的二维数组,行列都定义为12
定义为12×12,但是我们只用10×10,因为扫雷边角的那些,
都不需要判断周围8个那么多,但是我们行列都多定义了两行就可以,把用的每个都判断周围8个
*/
char board[ROW][LIE]; //给玩家展示的面板
char mineBoard[ROW][LIE]; //存放地雷的面板
boardInit(board,ROW,LIE,'*'); //给玩家展示的面板初始化
boardInit(mineBoard, ROW, LIE, '0'); //给玩家展示的面板初始化
srand((unsigned int)(time(NULL))); //种下随机数的种子
landMines(mineBoard,ROW,LIE); //给存放地雷的面板,随机存放地雷
int count = determine(board, ROW, LIE); //记录还剩下多少个'*'
while (count>MINE){
system("cls");
printBoard(board, ROW, LIE); //显示面板
//printBoard(mineBoard, ROW, LIE); //显示地雷面板
int row = 0;
int lie = 0;
printf("请输入一个位置的坐标=>:");
scanf("%d %d",&row,&lie);
//判断输入位置是否正确
if (row >= 1 && row <= 10 && lie >= 1 && lie <= 10&&board[row][lie]=='*'){
int judge = judgeMine(mineBoard, row, lie); //判断玩家输入的位置是否为炸弹,如果是炸弹返回1,如果不是返回0;
if (judge){
break;
}
else{
voluntarily(board, mineBoard, row, lie); //递归实现可自动显示无雷区
}
}
else{
printf("您输入坐标有误,请重新输入!!\n");
Sleep(1000);
}
count = determine(board, ROW, LIE); //更新'*'的数量
}
if (count>MINE){
printf("########################################\n");
printf("## 您被炸死了:< ##\n");
printf("########################################\n");
printBoard(mineBoard, ROW, LIE); //显示面板
}
else{
printf("########################################\n");
printf("## 恭喜你,胜利了:> ##\n");
printf("########################################\n");
printBoard(mineBoard, ROW, LIE); //显示面板
}
}
main.c
#include "MineSweep.h"
void menu(){
printf("########################################\n");
printf("############欢迎来到扫雷游戏############\n");
printf("########################################\n");
printf("## 1.Play #################### 2.Exit ##\n");
printf("########################################\n");
printf("Please Player Choice=>:");
}
int main(){
int selet=0; //控制while循环,
while (!selet){
menu();
int choose=0; //供玩家选择进入游戏或者退出
scanf("%d", &choose);
setbuf(stdin, NULL); //如果玩家输入字母,也不会出错
switch (choose){
case 1:
mineSweepGame();
printf("请问是否再来一把?\n");
Sleep(500);
break;
case 2:
printf("欢迎下次再来玩\n");
selet = 1;
break;
default:
printf("输入有误,请重新输入!!!\n");
Sleep(500);
break;
}
}
system("pause");
}