C语言实现扫雷游戏操作步骤及源码

作者:袖梨 2026-08-08

C语言实现扫雷游戏操作步骤及源码并不只看表面做法,关键还要理解相关条件、限制和后续影响。

一、源码简介

这是一个可以进行扫雷游戏的小程序,采用C语言进行编写。

上下左右控制光标位置,按j键进行标记,按k进行点击探雷,并且当光标 放在数字上,且周围的雷都已经被正确标记时,按k可以点开周围所有的空白,不过出错会结束游戏。

雷区长宽为25格,初始有10雷,每过一关增加20雷。

编译环境:VC6.0(采取纯C语言写法)

第三方库:无

二、运行截图

img_6a7675e9ac72630.webp

img_6a7675e9ac72c31.webp

三、源码解析

我们先来看游戏的主体逻辑。

虽然下面的代码很长,但逻辑还是较为清晰的。

以下循环

若游戏未开始,初始化。

若游戏开始,则检测键盘输入

按下ASDW则移动光标

第一次按下K,则初始化雷区

按下K则点开空白,或者清雷,并检测是否胜利

按下j则进行标记

游戏结束跳出循环

void Gamerun()//游戏主体{       int first;       while(1)       {              if(gamestate==0)//初始化              {                     first=0;                     m_time=time(NULL);//时间初始化                     Init_display();//初始化显示区域                     CONSOLE_CURSOR_INFO cursor_info = { 1,0 };                         SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);//设置指定控制台屏幕缓冲区的光标的大小和可见性                     gamestate=1;              }              else if(gamestate==1)//游戏中              {                     RemainderMine();//计算剩余的雷                     Draw_display();                     MoveCursor();                     PressJ();                     if(GetAsyncKeyState('K')&1)//如果按下K                     {                            if(first==0)                            {                                Init_mine(pos.y,pos.x);                                OpenDisplay(pos.y,pos.x);                                first++;                                if(Victory())//判定是否获胜                                {                                       Showmine();                                       Draw_display();                                       UINT uint=MessageBox(NULL,TEXT("恭喜过关!是否继续。"),TEXT("提示"),1);                                       if(uint==IDOK)                                       {                                              count+=20;                                              level++;                                              gamestate=0;                                       }                                       else if(uint==IDCANCEL)                                       {                                              gamestate=2;                                       }                                       else;                                }                                   else;                         }                            else                            {                                   if(TreadMine(pos.y,pos.x))//判断是否踩到雷                                   {                                          Showmine();                                          Draw_display();                                          UINT uint=MessageBox(NULL,TEXT("扫雷失败!是否重新开始?"),TEXT("提示"),MB_OKCANCEL|MB_ICONERROR);                                          if(uint==IDOK)                                       {                                              gamestate=0;                                       }                                       else if(uint==IDCANCEL)                                      {                                             gamestate=2;                                       }                                       else;                                   }                                   else                                   {                                          OpenDisplay(pos.y,pos.x);                                   }                                   if(OpenNumDisplay1(pos.y,pos.x))//按K也可以清雷,返回值为1表示踩雷                                {                                       Showmine();                                       Draw_display();                                          UINT uint=MessageBox(NULL,TEXT("扫雷失败!是否重新开始?"),TEXT("提示"),MB_OKCANCEL|MB_ICONERROR);                                          if(uint==IDOK)                                       {                                              gamestate=0;                                       }                                       else if(uint==IDCANCEL)                                      {                                             gamestate=2;                                       }                                       else;                                }                                else;                                   if(Victory())//判定是否获胜                                {                                       Showmine();                                       Draw_display();                                       UINT uint=MessageBox(NULL,TEXT("恭喜过关!是否继续。"),TEXT("提示"),1);                                       if(uint==IDOK)                                       {                                              count+=20;                                              level++;                                              gamestate=0;                                       }                                       else if(uint==IDCANCEL)                                       {                                              gamestate=2;                                       }                                       else;                                }                                   else;                            }                     }                     else;              }              else if(gamestate==2)//游戏结束              {                     printf("GAME END!");                     break;              }              else;       }}

下面看一些重要部分的实现。

改变光标位置以及定义为输出模式

void Pos(int x,int y)//定义一个设置光标位置到(x,y)的函数{    COORD pos;    HANDLE hOutput;    pos.X=x;    pos.Y=y;    hOutput=GetStdHandle(STD_OUTPUT_HANDLE);    SetConsoleCursorPosition(hOutput,pos);}

初始化雷区实现原理:

void Init_mine(int m, int n)//初始化雷区{       int num=count;       int x,y;       srand((unsigned)time(NULL));//利用时间获取随机种子       while(num>0)       {              x=rand()%height+1;//1-25              y=rand()%width+1;//1-25              if(mine[x][y]==0&&(m!=x||n!=y))//避免重复              {                     mine[x][y]=1;                     num--;              }              else;       }}

这个函数在第一次按下k的时候调用,输入值为光标的位置。在生成雷的时候,如果与之前的雷或者光标重合,那么会重新生成一个雷,这样做可以避免直接失败。

检测某一点周围的雷数目,以及标记数目

这两者的原理都是一样的。直接对光标周围八个点进行判定,若是则++。

绘制游戏画面

void Draw_display()//绘制游戏画面{       Pos(7, 7);//光标定位       int i,j;       for(i=1;i<=height;i++)//逐行逐列输出游戏画面       {              for(j=1;j<=width;j++)              {                     if(j==pos.x&&i==pos.y)                            SetConsoleTextAttribute(ColorHandle,0X5|0XC);//对不同类型的图像设置不同颜色                     else if(map[i][j]==10)                            SetConsoleTextAttribute(ColorHandle,0X94);                     else                            SetConsoleTextAttribute(ColorHandle,0X97);                     switch(map[i][j])                     {                            case 0:printf("  ");break;                            case 1:printf("1 ");break;                            case 2:printf("2 ");break;                            case 3:printf("3 ");break;                            case 4:printf("4 ");break;                            case 5:printf("5 ");break;                            case 6:printf("6 ");break;                            case 7:printf("7 ");break;                            case 8:printf("8 ");break;                            case 9:printf("■");break;//未知区域                            case 10:printf("▲");break;//标记                            case 11:printf("雷");break;//雷                     }                     if(j==pos.x&&i==pos.y)                            SetConsoleTextAttribute(ColorHandle,0X7);//关闭颜色                     else if(map[i][j]==10)                            SetConsoleTextAttribute(ColorHandle,0X7);                     else                            SetConsoleTextAttribute(ColorHandle,0X7);              }              Pos(7,7+i);//光标移到下一行       }       Pos(7,3);       printf("未排除的雷:%d   ",RemMine);//打印各种文字信息       Pos(7,5);       printf("时间:%ld 秒   ",long(time(NULL)-m_time));       Pos(27,3);       printf("关卡:%d  ",level);}

采用双层循环结构,对区域的每个点进行一次判定,将对应的标识及颜色输出就可以了。注意不要混淆图像数组和雷的信息数组。

展开无雷区域

在扫雷游戏中,当我们第一次点击时,很有可能点开一大片区域。其实现如下面的代码所示。

void OpenDisplay(int x,int y)//单击后展开显示无雷区域{       int i,j;       int offsetX[] = { 0,1,1,1,0,-1,-1,-1 };       int offsetY[] = { -1,-1,0,1,1,1,0,-1 };       if(map[x][y]==9&&x>0&&x<=width&&y>0&&y<=height)//中心点显示有几颗雷       {              map[x][y]=Judge_mine(x,y);              if(map[x][y]>0)//某点周围有雷就不展开              {                     return;              }              else;       }       else if(map[x][y]==10||(map[x][y]>=1&&map[x][y]<=8))//某点为标记或数字 不展开       {              return;       }       else;       for(i=0;i<8;i++)//周围8点       {              if(x>0&&x<=width&&y>0&&y<=height)              {                     if(map[x+offsetX[i]][y+offsetY[i]]==10&&mine[x+offsetX[i]][y+offsetY[i]]!=1)//展开的时候,如果周围某点为标记且不是雷                     {                            map[x+offsetX[i]][y+offsetY[i]]=9;//改为未知区域                     }                     if(mine[x+offsetX[i]][y+offsetY[i]]==0&&map[x+offsetX[i]][y+offsetY[i]]==9)//无雷且未知,则展开                     {                            map[x+offsetX[i]][y+offsetY[i]]=Judge_mine(x+offsetX[i],y+offsetY[i]);//得到某点周围的雷的个数                            if(map[x+offsetX[i]][y+offsetY[i]]==0)//该点无雷且无数字                            {                                   OpenDisplay(x+offsetX[i],y+offsetY[i]);//递归                            }                     }              }       }       return;}

具体来说,就是采取递归的算法。先判定该点是否产生变化,如果产生,则对周围8个点同样做出一次判定。如果这些点周围的雷数为0,则递归调用该函数。

清雷动作

当一个数字周围的雷全部被点开之后,在其上按k会点开周围无雷的区域

int OpenNumDisplay1(int x,int y)//将数字周围区域显示出来。返回值表明是否因为该动作踩雷,1为是{       int i,j;       int offsetX[]={ 0,1,1,1,0,-1,-1,-1 };       int offsetY[]={ -1,-1,0,1,1,1,0,-1 };       if(map[x][y]<9&&map[x][y]>0)//1-8       {              if(Judge_mine(x,y)==Judge_cur(x,y))//若周围标记数量等于周围雷的数量,展开              {                     for(i=0;i<8;i++)                     {                            if(x>0&&x<=width&&y>0&&y<=height)//越界判断                            {                                   if(map[x+offsetX[i]][y+offsetY[i]]==9||map[x+offsetX[i]][y+offsetY[i]]==10)//为标记和未知区域时,进行判定                                   {                                          if(map[x+offsetX[i]][y+offsetY[i]]==10&&mine[x+offsetX[i]][y+offsetY[i]]==1)//标记正确                                          {                                                 continue;                                          }                                          else if(map[x+offsetX[i]][y+offsetY[i]]==10&&mine[x+offsetX[i]][y+offsetY[i]]!=1) //标记错误                                          {                                                 return 1;//踩雷                                          }                                          else;                                   }                                   else;                            }                            else;                     }                     for(i=0;i<8;i++)                     {                            if(x>0&&x<=width&&y>0&&y<=height)//越界判断                            {                                   if(map[x+offsetX[i]][y+offsetY[i]]==9)//为未知区域则正常展开                                   {                                          OpenDisplay(x+offsetX[i],y+offsetY[i]);//展开                                   }                            }                     }              }       }       return 0;}

输入为按下k时的光标位置。判定周围标记数是否与雷数相同,是则检查每一个标记处是否为雷,若全部对应,则清雷,若有不对应的,则游戏结束。

其余定义的函数还有检测是否踩雷,计算剩余的雷,判定是否胜利,显示雷,移动光标,做标记。这些函数一般都是用双重循环,或者直接改变变量的方法进行的。这里暂时不给出源码,但完整源码也会有对应的内容。

四、完整源码

C语言扫雷完整源码

相关文章

精彩推荐