目录
之前,我们通过数组的知识完成了三子棋游戏的设计,同样是利用二维数组,我们是不是也就可以设计出一个扫雷游戏呢?
扫雷游戏,是在一个m*n的格子区间内具有一定数量的雷,而没有雷的格子会根据附近3*3区间的雷的数量来产生相应的数字,而我们便需要通过观察点击格子所出现的数字来将所有雷的位置找出来。
那么,在知道游戏是如何运转之后,我们就可以着手来实现这个游戏。
任何游戏都需要有游戏菜单的指引,那么我们便再次设计一个简单的菜单
- int menu()
- {
- int input=0;
- printf("1---开始游戏 2---退出\n");
- scanf("%d",&input);
- return input;
- }
而选择具有返回值的自定义函数,可以让我们通过返回值决定下一步游戏的进行
而根据返回值,我们可以确立出我们的主函数,并将剩余函数打包进入game()函数中
- int main()
- {
- while(1)
- {
- int begin=menu();
- if(begin==1)
- {
- printf("加载中---\n");
- sleep(2);
- system("cls");
- game();
- break;
- }
- else if(begin==2)
- break;
- else
- {
- printf("输入错误,请重新输入\n");
- continue;
- }
- }
-
- return 0;
- }
类似于三子棋,我们需要使用二维数组来存放雷与数字
由于二维数组的行和列只能为常量表达式,所以我们选择在头文件中用“#define“”定义全局变量ROW、COL、M作为二维数组的行和列、以及雷的个数。
以下的代码我们选择这样的数据(谁能拒绝高级的扫雷呢)
- #define ROW 16
- #define COL 30
- #define M 99
但与三子棋不同的是,这些雷与数字在游戏的初期便已经决定好了,而我们一开始是看不到的,所以我们选择使用两个二维数组,第一个用来存放所有的雷和数字第二个为空白,当我们选择要点击的坐标时,再将第一个数组中对应位置的雷和数字赋予第二个数组。
因此,我们首先要完成数组的创建与初始化
- char arr1[ROW][COL];
- char arr2[ROW][COL];
- init(arr2);
- void init(char arr[ROW][COL])
- {
- int i,j;
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL;j++)
- {
- arr[i][j]=' ';
- }
- }
- }
而我们将二维数组放置在一个列表中可以让我们的程序更加美观简洁
- void list(char arr[ROW][COL])
- {
- int i,j;
- printf(" ");
- for(j=1;j<=COL;j++)
- {
- if(j<10)
- printf(" %d ",j);
- else if(j>=10&&j<COL)
- printf(" %d ",j);
- else
- printf(" %d \n",j);
- }
- printf("----");
- for(j=0;j<COL;j++)
- {
- if(j<COL-1)
- printf("|---");
- else
- printf("|---|\n");
- }
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<=COL;j++)
- {
- if(i<9&&j==0)
- printf(" %d ",i+1);
- else if(i>=9&&j==0)
- printf(" %d ",i+1);
- else if(j<COL)
- printf("| %c ",arr[i][j-1]);
- else if(j==COL)
- {
- printf("| %c |\n",arr[i][j-1]);
- for(j=0;j<COL+1;j++)
- {
- if(j==0)
- printf("----");
- else if(j>0&&j<COL)
- printf("|---");
- else
- printf("|---|\n");
- }
- }
- else
- printf(" %c \n",arr[i][j-1]);
- }
- }
- }
这样我们便可以得到一个这样的列表

在游戏开始之前,我们先要将M个雷随机分布在arr1这个数组中。(我们这里将字符‘X’当做雷)
既然要随机分布,我们就又要利用时间戳来作随机数
首先将srand函数放在game函数的开始
srand((unsigned int) time(NULL));
之后我们进行雷的排布
- while(count<M)//当所排雷的个数小于预期个数进行循环
- {
- int a,b,i=0;
- a=rand()%ROW;
- b=rand()%COL;
- if(arr1[a][b]!='X')//避免重复排雷
- {
- arr1[a][b]='X';
- count++;//每成功排一个雷,count++
- continue;
- }
- else
- continue;
- }
(当我们初步完成程序的编写时,我们会将雷的分布进一步改进,避免第一步直接选择到雷,这一部分我们将放在第四个大板块来讲)
当我们完成雷的排布之后,我们便可以通过雷的个数来计算数组各个位置数字的大小
- void num_set(char arr[ROW][COL])
- {
- int x,y,m,n,count=0;
- for(x=0;x<ROW;x++)
- {
- for(y=0;y<COL;y++)//循环判断每一个数组位置
- {
- if(arr[x][y]=='X')//位置为雷时不做变化
- ;
- else
- {
- int lx=x-1,ly=y-1,rx=x+1,ry=y+1;
- if(x==0)
- lx=0;
- if(y==0)
- ly=0;
- if(x==ROW)
- rx=x;
- if(y==COL)
- ry=y;
- count=0;
- for(m=lx;m<=rx;m++)
- {
- for(n=ly;n<=ry;n++)//对每个位置3*3区域内(边缘位置稍作改变)的雷进行查找
- {
- if(arr[m][n]=='X')
- count++;//循环结束count的值为周围雷的个数
- }
- }
- arr[x][y]=count+48;//将数字转化为ASCII码值
- }
- }
- }
- }
在数字计算完毕后,arr1在列表中便会是以下形式

在arr2赋值完成后,我们便可以开始设计游戏过程
玩家要做的是选择一个坐标
- int x,y;
- printf("请输入坐标=>\n");
- scanf("%d %d",&x,&y);
在玩家选择坐标之后,我们就要根据所输入的坐标选择将arr2哪些位置赋值给arr1
根据扫雷游戏可知
当玩家选择雷时,游戏失败
当玩家选择非0数字时,显示该位置数字
当玩家选择数字0时,显示周围3*3范围内的数字
(游戏本身是如果在3*3范围内存在数字0时继续在新的位置进行判断,我们简称自动排空。但我们这是只是进行简单的思路设计,就先跳过,会在第四个大板块介绍)
知道以上规则后,我们便可以进行设计一个judge()函数
- int judge(char arr1[ROW][COL],char arr2[ROW][COL],int x,int y)
- {
- if(arr1[x][y]=='X')//当选择的是雷
- {
- arr1[x][y]='!';
- list(arr1);//失败后将雷变作!提示玩家并列出列表
- return 2;//返回值为2时判断为失败
- }
- else if(arr1[x][y]=='0'&&arr2[x][y]==' ')
- {
- int m,n;
- int lx=x-1,ly=y-1,rx=x+1,ry=y+1;
- if(x==0)
- lx=0;
- if(y==0)
- ly=0;
- if(x==ROW)
- rx=x;
- if(y==COL)
- ry=y;
- for(m=lx;m<=rx;m++)
- {
- for(n=ly;n<=ry;n++)
- {
- arr2[m][n]=arr1[m][n];//对3*3范围内的arr2进行赋值
- }
- }
- return 1; //返回值为1时继续游戏
- }
- else //当选择的是非0数字
- {
- arr2[x][y]=arr1[x][y];
- return 1;//返回值为1时继续游戏
- }
- }
当arr2中空格位置在arr1中都是雷,即所有数字被赋值给arr2时,游戏胜利
首先在game函数中写一个循环进行玩家的选择和胜利的判断
- while(1)
- {
- printf("请输入坐标=>\n");
- scanf("%d %d",&x,&y);
- re=judge(arr1,arr2,x-1,y-1);
- if(re==2)
- {
- printf("defeat");
- break;
- }
- else if(re==1)
- {
- system("cls");
- list(arr2);
- w=win(arr2);
- if(w==1)
- {
- printf("you win");
- break;
- }
- else
- continue;
- }
- }
之后进行win()函数的设计
- int win(char arr[ROW][COL])
- {
- int i,j,count=ROW*COL-M;//总数-雷数=数字的个数
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL;j++)
- {
- if (arr[i][j]!=' ')//一个位置不为' ',意思便是被赋予了数字
- count--;//每当上述条件成立,count-1
- }
- }
- if(count==0)//当count被减到0,即所有非雷位置都被赋予了数字
- return 1;
- }
最后我们便可以将所有代码整合优化,完成这个简单的扫雷游戏
- #define ROW 16
- #define COL 30
- #define M 99
- int menu();
- void game();
- void init(char arr[ROW][COL]);
- void list(char arr[ROW][COL]);
- void num_set(char arr[ROW][COL]);
- int judge(char arr1[ROW][COL],char arr2[ROW][COL],int x,int y);
- int win(char arr[ROW][COL]);
- #include<stdio.h>
- #include"test.h"
- #include<windows.h>
- void game()
- {
- srand((unsigned int) time(NULL));
- int count=0;
- char arr1[ROW][COL];
- char arr2[ROW][COL];
- init(arr2);
- list(arr2);
- while(count<M)
- {
- int a,b,i=0;
- a=rand()%ROW;
- b=rand()%COL;
- if(arr1[a][b]!='X')
- {
- arr1[a][b]='X';
- count++;
- continue;
- }
- else
- continue;
- }
- num_set(arr1);
- list(arr2);
- int re,x,y;
- while(1)
- {
- printf("请输入坐标=>\n");
- scanf("%d %d",&x,&y);
- re=judge(arr1,arr2,x-1,y-1);
- if(re==2)
- {
- printf("defeat");
- break;
- }
- else if(re==1)
- {
- system("cls");
- list(arr2);
- if(win(arr2))
- {
- printf("you win");
- break;
- }
- else
- continue;
- }
- }
- }
- int main()
- {
- while(1)
- {
- int begin=menu();
- if(begin==1)
- {
- printf("加载中---\n");
- sleep(2);
- system("cls");
- game();
- break;
- }
- else if(begin==2)
- break;
- else
- {
- printf("输入错误,请重新输入\n");
- continue;
- }
- }
-
- return 0;
- }
- #include<stdio.h>
- #include"test.h"
- int menu()
- {
- int input=0;
- printf("1---开始游戏 2---退出\n");
- scanf("%d",&input);
- return input;
- }
- void init(char arr[ROW][COL])
- {
- int i,j;
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL;j++)
- {
- arr[i][j]=' ';
- }
- }
- }
- void list(char arr[ROW][COL])
- {
- int i,j;
- printf(" ");
- for(j=1;j<=COL;j++)
- {
- if(j<10)
- printf(" %d ",j);
- else if(j>=10&&j<COL)
- printf(" %d ",j);
- else
- printf(" %d \n",j);
- }
- printf("----");
- for(j=0;j<COL;j++)
- {
- if(j<COL-1)
- printf("|---");
- else
- printf("|---|\n");
- }
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL+1;j++)
- {
- if(i<9&&j==0)
- printf(" %d ",i+1);
- else if(i>=9&&j==0)
- printf(" %d ",i+1);
- else if(j<COL)
- printf("| %c ",arr[i][j-1]);
- else if(j==COL)
- {
- printf("| %c |\n",arr[i][j-1]);
- for(j=0;j<=COL;j++)
- {
- if(j==0)
- printf("----");
- else if(j>0&&j<COL)
- printf("|---");
- else
- printf("|---|\n");
- }
- }
- else
- printf(" %c \n",arr[i][j-1]);
- }
- }
- }
- void num_set(char arr[ROW][COL])
- {
- int x,y,m,n,count=0;
- for(x=0;x<ROW;x++)
- {
- for(y=0;y<COL;y++)
- {
- if(arr[x][y]=='X')
- ;
- else
- {
- int lx=x-1,ly=y-1,rx=x+1,ry=y+1;
- if(x==0)
- lx=0;
- if(y==0)
- ly=0;
- if(x==ROW)
- rx=x;
- if(y==COL)
- ry=y;
- count=0;
- for(m=lx;m<=rx;m++)
- {
- for(n=ly;n<=ry;n++)
- {
- if(arr[m][n]=='X')
- count++;
- }
- }
- arr[x][y]=count+48;
-
- }
- }
- }
- }
- int win(char arr[ROW][COL])
- {
- int i,j,count=0;
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL;j++)
- {
- if (arr[i][j]==' ')
- count++;
- }
- }
- if(count==M)
- return 1;
- else
- return 0;
- }
- int judge(char arr1[ROW][COL],char arr2[ROW][COL],int x,int y)
- {
- if(arr1[x][y]=='X')
- {
- arr1[x][y]='!';
- list(arr1);
- return 2;
- }
- else if(arr1[x][y]=='0'&&arr2[x][y]==' ')
- {
- int m,n;
- arr2[x][y]=arr1[x][y];
- int lx=x-1,ly=y-1,rx=x+1,ry=y+1;
- if(x==0)
- lx=0;
- if(y==0)
- ly=0;
- if(x==ROW)
- rx=x;
- if(y==COL)
- ry=y;
- for(m=lx;m<=rx;m++)
- {
- for(n=ly;n<=ry;n++)
- {
- arr2[m][n]=arr1[m][n];
- }
- }
- return 1;
- }
- else
- {
- arr2[x][y]=arr1[x][y];
- return 1;
- }
- }
在完成初步的设计后,我们便可以针对自动排空作进一步的改进
扫雷游戏本身是在初步判断时如果在3*3范围内存在数字0时继续在新的位置进行判断
我们可以很容易想到这很符合递归
因此我们便可以利用递归来完成这个功能
- else if(arr1[x][y]=='0'&&arr2[x][y]==' ')
- {
- int m,n;
- arr2[x][y]=arr1[x][y];
- int lx=x-1,ly=y-1,rx=x+1,ry=y+1;
- if(x==0)
- lx=0;
- if(y==0)
- ly=0;
- if(x==ROW)
- rx=x;
- if(y==COL)
- ry=y;
- for(m=lx;m<=rx;m++)
- {
- for(n=ly;n<=ry;n++)
- {
- if(arr1[m][n]=='0')
- {
- judge(arr1,arr2,m,n);//递归进行不断的判断
- }
- else
- arr2[m][n]=arr1[m][n];
- }
- }
- return 1;
- }
可以看到,我们在两层for循环内添加了分支的判断与递归
这便可以很好的完成自动排空的功能

可以看到当我们选择数字为0的坐标时,不只是显示周围3*3的数字,而是根据周围是否存在0来作不断的延伸。
其实想要解决“开局杀”这个问题,我们只需要将雷的排布放置在第一次点击之后并根据第一次点击做出一些改变就可以了。
- int x,y;
- printf("请输入坐标=>\n");
- scanf("%d %d",&x,&y);
- arr1[x-1][y-1]='Y';
- while(count<M)
- {
- int a,b,c,d,i=0;
- c=a=rand()%ROW;
- d=b=rand()%COL;
- if(arr1[a][b]!='X'&&arr1[a][b]!='Y')//避免在第一次选择的坐标上放置雷
- {
- arr1[a][b]='X';
- count++;
- continue;
- }
- else
- continue;
- }
- num_set(arr1);
- judge(arr1,arr2,x-1,y-1);
- system("cls");
- list(arr2);
- int re=2;
- while(1)
- {
- printf("请输入坐标=>\n");
- scanf("%d %d",&x,&y);
- re=judge(arr1,arr2,x-1,y-1);
- if(re==2)
- {
- printf("defeat");
- break;
- }
- else if(re==1)
- {
- system("cls");
- list(arr2);
- if(win(arr2))
- {
- printf("you win");
- break;
- }
- else
- continue;
- }
- }
- }
可以看到,我们在排雷之前添加了第一次坐标的选择,由于此时为进行数字的计算,我们暂时将这个位置赋值为‘Y’,之后对if的判断语句做出相应的改变,就完成了“开局杀”的避免。后续就可以按照原本的代码进行了。
- #define ROW 16
- #define COL 30
- #define M 99
- int menu();
- void game();
- void init(char arr[ROW][COL]);
- void list(char arr[ROW][COL]);
- void num_set(char arr[ROW][COL]);
- int judge(char arr1[ROW][COL],char arr2[ROW][COL],int x,int y);
- int win(char arr[ROW][COL]);
- #include<stdio.h>
- #include"test.h"
- #include"windows.h"
- void game()
- {
- srand((unsigned int) time(NULL));
- int count=0;
- char arr1[ROW][COL];
- char arr2[ROW][COL];
- init(arr2);
- list(arr2);
- int x,y;
- printf("请输入坐标=>\n");
- scanf("%d %d",&x,&y);
- arr1[x-1][y-1]='Y';
- while(count<M)
- {
- int a,b,c,d,i=0;
- c=a=rand()%ROW;
- d=b=rand()%COL;
- if(arr1[a][b]!='X'&&arr1[a][b]!='Y')
- {
- arr1[a][b]='X';
- count++;
- continue;
- }
- else
- continue;
- }
- num_set(arr1);
- judge(arr1,arr2,x-1,y-1);
- system("cls");
- list(arr2);
- int re=2;
- while(1)
- {
- printf("请输入坐标=>\n");
- scanf("%d %d",&x,&y);
- re=judge(arr1,arr2,x-1,y-1);
- if(re==2)
- {
- printf("defeat");
- break;
- }
- else if(re==1)
- {
- system("cls");
- list(arr2);
- if(win(arr2))
- {
- printf("you win");
- break;
- }
- else
- continue;
- }
- }
- }
- int main()
- {
- while(1)
- {
- int begin=menu();
- if(begin==1)
- {
- printf("加载中---\n");
- Sleep(2);
- system("cls");
- game();
- break;
- }
- else if(begin==2)
- break;
- else
- {
- printf("输入错误,请重新输入\n");
- continue;
- }
- }
-
- return 0;
- }
- #include<stdio.h>
- #include"test.h"
- int menu()
- {
- int input=0;
- printf("1---开始游戏 2---退出\n");
- scanf("%d",&input);
- return input;
- }
- void init(char arr[ROW][COL])
- {
- int i,j;
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL;j++)
- {
- arr[i][j]=' ';
- }
- }
- }
- void list(char arr[ROW][COL])
- {
- int i,j;
- printf(" ");
- for(j=1;j<=COL;j++)
- {
- if(j<10)
- printf(" %d ",j);
- else if(j>=10&&j<COL)
- printf(" %d ",j);
- else
- printf(" %d \n",j);
- }
- printf("----");
- for(j=0;j<COL;j++)
- {
- if(j<COL-1)
- printf("|---");
- else
- printf("|---|\n");
- }
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL+1;j++)
- {
- if(i<9&&j==0)
- printf(" %d ",i+1);
- else if(i>=9&&j==0)
- printf(" %d ",i+1);
- else if(j<COL)
- printf("| %c ",arr[i][j-1]);
- else if(j==COL)
- {
- printf("| %c |\n",arr[i][j-1]);
- for(j=0;j<=COL;j++)
- {
- if(j==0)
- printf("----");
- else if(j>0&&j<COL)
- printf("|---");
- else
- printf("|---|\n");
- }
- }
- else
- printf(" %c \n",arr[i][j-1]);
- }
- }
- }
- void num_set(char arr[ROW][COL])
- {
- int x,y,m,n,count=0;
- for(x=0;x<ROW;x++)
- {
- for(y=0;y<COL;y++)
- {
- if(arr[x][y]=='X')
- ;
- else
- {
- int lx=x-1,ly=y-1,rx=x+1,ry=y+1;
- if(x==0)
- lx=0;
- if(y==0)
- ly=0;
- if(x==ROW)
- rx=x;
- if(y==COL)
- ry=y;
- count=0;
- for(m=lx;m<=rx;m++)
- {
- for(n=ly;n<=ry;n++)
- {
- if(arr[m][n]=='X')
- count++;
- }
- }
- arr[x][y]=count+48;
-
- }
- }
- }
- }
- int win(char arr[ROW][COL])
- {
- int i,j,count=0;
- for(i=0;i<ROW;i++)
- {
- for(j=0;j<COL;j++)
- {
- if (arr[i][j]==' ')
- count++;
- }
- }
- if(count==M)
- return 1;
- else
- return 0;
- }
- int judge(char arr1[ROW][COL],char arr2[ROW][COL],int x,int y)
- {
- if(arr1[x][y]=='X')
- {
- arr1[x][y]='!';
- list(arr1);
- return 2;
- }
- else if(arr1[x][y]=='0'&&arr2[x][y]==' ')
- {
- int m,n;
- arr2[x][y]=arr1[x][y];
- int lx=x-1,ly=y-1,rx=x+1,ry=y+1;
- if(x==0)
- lx=0;
- if(y==0)
- ly=0;
- if(x==ROW)
- rx=x;
- if(y==COL)
- ry=y;
- for(m=lx;m<=rx;m++)
- {
- for(n=ly;n<=ry;n++)
- {
- if(arr1[m][n]=='0')
- {
- judge(arr1,arr2,m,n);
- }
- else
- arr2[m][n]=arr1[m][n];
- }
- }
- return 1;
- }
- else
- {
- arr2[x][y]=arr1[x][y];
- return 1;
- }
- }
如果大家还有更好的优化方案,欢迎评论