ITPub博客

首页 > > > C语言编写-扫雷游戏详解,附带分析源码

C语言编写-扫雷游戏详解,附带分析源码

作者:cdfoxman 时间:2019-12-16 10:30:39 0 删除 编辑

扫雷游戏大家应该都不陌生,一个扫雷游戏要满足的基本要求是:

1. 第一次扫的位置不能是雷

2. 每展开一个位置要显示该位置周围雷的个数

3. 若该位置周围没雷,要把周围展开

该游戏的界面是10X10的,有10个雷,大家可根据自己的需求参考去改

该游戏的大体思路是:

1. 首先你肯定想到要设置一个数组放雷,有雷为1,无雷为0,玩家每点开一个位置然后根据坐标判断改位置周围雷的个数,然后你要怎样把你点的这个位置雷的个数显示出来呢?

是在放雷的那个数组里显示吗?当然不是啦,放雷的那个数组每个格都是可见的,肯定不能给玩家看,再说那样会改变放雷数组里的变量(你算出该位置周围的雷的个数肯定要赋值给该位置,而雷数组里只能是1或0。

所以我们要再设置一个数组给玩家显示该位置周围雷的个数。

如下所示:

char Mine[ROW+2][COL+2] = { 0 };//放雷数组
char Board[ROW+2][COL+2] = { 0 };//玩家显示数组
//数组的大小为啥要加2?

C语言

我这个游戏是10X10的界面,也就是ROW和COL都是10,那为什么我在设置数组大小时要设成12X12的呢?

因为我们每点开一个位置要计算它周围雷的个数,除了 第一行 和 最后一行 还有 第一列 和 最后一列 这四种情况外,其它位置周围都有8个格,难道我们每输入一个位置都要判断它是在中间还是在那四种情况中的哪一种?不不不,这样太麻烦了。

我们把数组设置成12X12的,让它周围那一圈雷的个数都是0,这样不管计算中间位置还是边上的位置它周围雷的个数都可以使用同一个算法计算(把周围8个位置加起来)

如图所示,只用在红色区域内布雷,显示的时候不显示最边上的那两列两行。所以横、纵坐标的范围都是是1至10,这样也符合人的正常习惯(因为数组是从横、纵坐标都是0开始的)

2. 然后把数组初始化后,就要放雷了,我这只放了10个雷,利用随机数产生雷的位置

//rang()%10产生的随机数是0至9,加1后是1至10
i = rand() % 10 + 1;
j = rand() % 10 + 1;

3. 把雷设好了,该考虑玩家怎样扫雷了,玩家第一次扫不能被炸死,所以第一次扫的位置如果是雷,就要把这个雷移到一个没有雷的位置上

if (count == 1)
{ 
 if (Mine[x][y] == '1')
 {
 Mine[x][y] = '0';
 while (1)
 {
 if (Mine[rand() % 10 + 1][rand() % 10 + 1] == '0')
 {
 Mine[rand() % 10 + 1][rand() % 10 + 1] = '1';
 printf("更新后的雷盘的界面\n");
 Display(Mine, ROW + 2, COL + 2);
 break;
 }
 }
 }

4. 如果该玩家点的该位置周围没雷就要展开周围一片,所以要判断该位置周围雷的个数是否等于0,若等于0则调用展开周围的一片的函数

5. 最后就是如何判断玩家如何赢呢?

因为玩家不能点有雷的位置,所以玩家界面有雷的位置是“*”,所以只要判断改界面有10个“*”,那玩家就赢了

以上就是设计该游戏的大体思路

该游戏要用到以下函数:saolei.h

#ifndef __SAOLEI_H__ 
//防止头文件被重复引用后,当预处理是在主函数里头文件被多次替换而耗时,当使用这条语句后,可避免头文件的重复引用
#define __SAOLEI_H__
#define ROW 10
#define COL 10
//菜单
void menu();
//游戏
void game();
//雷的初始化
void Init_Mine(char Mine[ROW+2][COL+2], int row, int col);
//雷盘的初始化
void Init_Board(char Board[ROW+2][COL+2], int row, int col);
//设置雷
void Set_Mine(char Mine[ROW+2][COL+2], int row, int col);
//计算每个空周围雷的个数
int Calcu_Mine(char Mine[ROW+2][COL+2], int x, int y);
//展示雷盘
void Display(char board[ROW+2][COL+2], int row, int col);
//玩家扫雷
int Player(char Mine[ROW+2][COL+2], char Board[ROW+2][COL+2], int row, int col,int count);
//判断是否赢
int Is_Win(char Board[ROW+2][COL+2], int row, int col);
//当前格和周围的几个格是不是0,若是则展开
void If_Blank(char Mine[ROW + 2][COL + 2], char Board[ROW + 2][COL + 2], int x, int y);
#endif//__SAOLEI_H__

以下是这些函数的实现:saolei.c

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include"saolei.h"
void menu()
{
 printf("****************\n");
 printf("*****1.play*****\n");
 printf("*****0.exit*****\n");
 printf("****************\n");
}
//雷盘的初始化
void Init_Mine(char Mine[ROW+2][COL+2], int row, int col)
{
 int i = 0;
 int j = 0;
 for (i = 0; i < row; i++)
 {
 for (j = 0; j < col; j++)
 {
 Mine[i][j] = '0';
 }
 }
}
//玩家界面的初始化
void Init_Board(char Board[ROW+2][COL+2], int row, int col)
{
 int i = 0;
 int j = 0;
 for (i = 0; i < row; i++)
 {
 for (j = 0; j < col; j++)
 {
 Board[i][j] = '*';
 }
 }
}
//放雷
void Set_Mine(char Mine[ROW+2][COL+2], int row, int col)
{
 int i = 0;
 int j = 0;
 int count = 10;//通过循环放10个雷
 while (count--)
 {
 i = rand() % 10 + 1;//rang()%10产生的随机数是0至9,加1后是1至10
 j = rand() % 10 + 1;
 if (Mine[i][j] = '0')//随机产生的这个位置如果没雷,就把这个位置放上雷
 {
 Mine[i][j] = '1';
 }
 //如果这个位置有雷,这个位置就不能放了,要换个位置放
 //因为要产生10个雷,是通过count来计数的,因为刚刚count减了1,但又没放雷
 //所以此时要给count加1
 else
 {
 count++;
 }
 }
}
//计算该位置周围有多少雷
//整形数字与字符数字的ASC码差了48,0是0,‘0’是48
//所以一个字符数字转换成整形数字,用该字符减去48或者‘0’
int Calcu_Mine(char Mine[ROW+2][COL+2], int x, int y)
{
 return (Mine[x - 1][y - 1] - '0' + Mine[x - 1][y] - '0'
 + Mine[x - 1][y + 1] - '0' + Mine[x][y - 1] - '0'
 + Mine[x][y + 1] - '0' + Mine[x + 1][y - 1]- '0'
 + Mine[x + 1][y] - '0'+ Mine[x + 1][y + 1]- '0');
}
//该位置周围展开
void If_Blank(char Mine[ROW + 2][COL + 2], char Board[ROW + 2][COL + 2], int x, int y)
{
 Board[x - 1][y - 1] = Calcu_Mine(Mine, x - 1, y - 1) + '0';
 Board[x - 1][y] = Calcu_Mine(Mine, x - 1, y) + '0';
 Board[x - 1][y + 1] = Calcu_Mine(Mine, x - 1, y + 1) + '0';
 Board[x][y - 1] = Calcu_Mine(Mine, x, y - 1) + '0';
 Board[x][y + 1] = Calcu_Mine(Mine, x, y + 1) + '0';
 Board[x + 1][y - 1] = Calcu_Mine(Mine, x + 1, y - 1) + '0';
 Board[x + 1][y] = Calcu_Mine(Mine, x + 1, y) + '0';
 Board[x + 1][y + 1] = Calcu_Mine(Mine, x + 1, y + 1) + '0';
}
//玩家下棋
int Player(char Mine[ROW+2][COL+2], char Board[ROW+2][COL+2], int row, int col,int count)
{ 
 int x = 0;
 int y = 0;
 printf("请输入坐标 > \n");
 while (1)
 {
 scanf("%d %d", &x, &y);
 if (x < 1 || x >= row || y < 1 || y >= col)
 {
 printf("坐标输入错误,请重新输入>\n");
 }
 else
 {
 //第一次不被炸死,如果是雷,把位置换到没雷的地方了
 if (count == 1)
 {
 if (Mine[x][y] == '1')
 {
 Mine[x][y] = '0';
 while (1)
 {
 if (Mine[rand() % 10 + 1][rand() % 10 + 1] == '0')
 {
 Mine[rand() % 10 + 1][rand() % 10 + 1] = '1';
 printf("更新后的雷盘的界面\n");
 Display(Mine, ROW + 2, COL + 2);
 break;
 }
 }
 }
 }
 if (Board[x][y] != '*')
 {
 printf("此处已经被扫过,请重新输入位置>\n");
 }
 else
 break;
 }
 }
 if (Mine[x ][y] == '1')
 {
 Board[x][y] = '1';
 Display(Board, ROW + 2, COL + 2);
 printf("哈哈哈哈,你踩到雷了,GAME OVER!!!\n");
 return 0;
 }
 else
 {
 Board[x][y] = Calcu_Mine(Mine, x, y) + '0';
 if (Calcu_Mine(Mine, x, y) == 0)
 {
 If_Blank(Mine, Board, x, y);
 }
 return 1;
 }
}
void Display(char board[ROW+2][COL+2],int row, int col)
{
 int i = 0;
 int j = 0;
 printf(" 1 2 3 4 5 6 7 8 9 10\n");
 printf("---------------------- \n");
 for (i = 1; i < row-1; i++)
 {
 printf("%-2d", i);
 printf("|");
 for (j = 1; j < col-1; j++)
 {
 printf("%c", board[i][j]);
 printf(" ");
 }
 printf("\n");
 }
 printf("---------------------- \n");
}
//当最后玩家界面剩10个*时,玩家胜
int Is_Win(char Board[ROW+2][COL+2], int row, int col)
{
 int i = 0;
 int j = 0;
 int count = 0;
 for (i = 0; i < row; i++)
 {
 for (j = 0; j < col;j++)
 if (Board[i][j] == '*')
 {
 count++;
 }
 }
 if (count == 10)
 {
 printf("你真棒,雷被你扫完啦!\n");
 return 1;
 }
 return 0;
}
void game()
{
 char Mine[ROW+2][COL+2] = { 0 };
 char Board[ROW+2][COL+2] = { 0 };
 Init_Mine(Mine, ROW+2, COL+2);
 Set_Mine(Mine, ROW+2, COL+2);
 Display(Mine, ROW+2, COL+2);
 Init_Board(Board, ROW+2, COL+2);
 Display(Board, ROW+2, COL+2);
 int ret = 0;
 int count = 1;
 while (1)
 {
 ret=Player(Mine,Board, ROW+2, COL+2,count++);
 Display(Board, ROW+2, COL+2);
 if (ret == 0)
 {
 break;
 }
 if (Is_Win(Board, ROW + 2, COL + 2) == 1)
 {
 Display(Mine, ROW + 2, COL + 2);
 break;
 }
 }
}

主函数:main.c

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include"saolei.h"
int main()
{
 int input;
 srand((unsigned int)time(NULL));
 do
 {
 menu();
 scanf("%d", &input);
 switch (input)
 {
 case 1:game();
 case 0:printf("退出游戏!\n");
 break;
 default:printf("输入错误,请重新输入");
 break;
 }
 } while (input);
 return 0;
}


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/625/viewspace-3121660/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-04-12

  • 博文量
    237
  • 访问量
    1739