C语言学习记录——鹏哥扫雷项目实现及递归展开、记录雷坐标

简介: C语言学习记录——鹏哥扫雷项目实现及递归展开、记录雷坐标

前言

扫雷游戏项目将预设建立一个头文件和两个源文件,如:game.c  test.c  game.h

test.c ——扫雷游戏的测试

game.c ——游戏函数的实现

game.h ——游戏函数的声明

下面先实现扫雷基本的功能,再尝试完整实现扫雷的功能,可根据目录跳转。

基本实现

基本还原扫雷的思路,排查一个坐标后,扫描周围八个方格的雷数

无自动排查无雷的其他方格、无标记雷的功能

代码(game.h)

#include <stdio.h>
#include <windows.h>
 
#define EASY_count 10  //简易难度下的雷的个数 设为10
 
#define ROW 9          //雷盘的行数
#define COL 9          //雷盘的列数
 
#define ROWS 11        //实际定义雷盘数组的行数和列数
#define COLS 11        //往大了定义是为了排查雷时不出现数组越界的情况
 
int win;               //用于判断游戏是否胜利
                       //即当win == row * col - EASY_count时
 
void game();
 
//初始化棋盘
void Intboard(char board[ROWS][COLS], int rows, int cols, char aim);
 
//打印棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);
 
//布置雷
void Setmine(char board[ROWS][COLS], int row, int col);
 
//排查雷
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

代码(test.c)

#include "game.h"
 
//简易菜单界面,随意发挥
void menu()
{
  printf("                              \n");
  printf("                              \n");
  printf("                              \n");
  printf("          扫雷游戏            \n");
  printf("           1.开始             \n");
  printf("           0.退出             \n");
  printf("                              \n");
  printf("                              \n");
  printf("                              \n");
}
 
void game()
{
  Sleep(1000);
  system("cls");
  char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
  char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
 
  //初始化棋盘
  Intboard(mine, ROWS, COLS, '0');
  Intboard(show, ROWS, COLS, '*');
 
  //打印棋盘
  //Displayboard(mine, ROW, COL);用于检测雷是否正常布置
  Displayboard(show, ROW, COL);
 
  //布置雷
  Setmine(mine, ROW, COL);
  //Displayboard(mine, ROW, COL);必要时显示雷盘,便于测试
 
  //排查雷
  Findmine(mine,show, ROW, COL);
}
int main()
{
  srand((unsigned int)time(NULL));
  int input = 0;
  do
  {
    menu();
    printf("请选择:");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      game();
      break;
    case 0:
      printf("退出游戏\n");
      break;
    default:
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (input);
  return 0;
}

代码(game.c)

#include "game.h"
 
//棋盘初始化函数的定义
void Intboard(char board[ROWS][COLS],int rows, int cols, char aim)
{
  int a, b;
  for (a = 0; a < rows; a++)
  {
    for (b = 0; b < cols; b++)
    {
      board[a][b] = aim;
    }
  }
}
 
//打印棋盘函数的定义
void Displayboard(char board[ROWS][COLS], int row, int col)
{
  int a, b;
 
  for (a = 0; a <= col; a++)
  {
    printf("%d ", a);
  }
  printf("\n");
  for (a = 1; a <= row; a++)
  {
    printf("%d ", a);
    for (b = 1; b <= col; b++)
    {
      printf("%c ", board[a][b]);
    }
    printf("\n");
  }
}
 
//布置雷的函数的定义
void Setmine(char board[ROWS][COLS], int row, int col)
{
  int count = EASY_count;
  while (count)
  {
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (board[x][y] != '1')
    {
      board[x][y] = '1';
      count--;
    }
  }
}
 
 
//检测排查坐标周围的雷,加static修饰,使得其只能在当前源文件中使用
static int get_mine_count(char mine[ROWS][COLS],int x,int y)
{
      return mine[x - 1][y - 1] +
           mine[x - 1][y] +
           mine[x - 1][y + 1] +
           mine[x][y - 1] +
           mine[x][y + 1] +
           mine[x + 1][y - 1] +
           mine[x + 1][y] +
           mine[x + 1][y + 1] - 8 * '0';
}
 
//排查雷的函数的定义
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
  //一、输入要排查的坐标
  //二、判断输入的坐标是否合理
    //三、记录win值,全部排查完则显示游戏胜利
  int x, y,flag = 0,win = 0;
  
  while (1)
  {
    printf("请输入要排查的雷\n坐标(行号 列号):");
    scanf("%d %d", &x, &y);
    system("cls");
    if (x >= 1 && x <= row && y >= 1 && y <= col)
    {
      if (mine[x][y] == '1')
      {
        printf("很遗憾,你被炸死了\n");
        Displayboard(mine, row, col);
        do
        {
          printf("输入0进行下一步操作:");
          scanf("%d", &flag);
          if (flag == 0)
            continue;
          else
            printf("输入错误,请重新输入\n");
          Sleep(2500);
        } while (flag);
        break;
      }
      else 
      {
        int count = get_mine_count(mine, x, y);
        show[x][y] = count + '0';
        
        //显示排查的信息
        Displayboard(show, row, col);
        win++;
      }
    }
    else
    {
      printf("坐标不合法,请重新输入\n");
    }
    if (win == row * col - EASY_count)
    {
      printf("恭喜你,排雷成功\n");
      Sleep(5000);
      break;
    }
  }
}

游戏测试

修改代码:

一、

二、

运行并测试:

完整实现(递归展开,标记雷)

代码(game.h)

#include <stdio.h>
#include <windows.h>
 
#define EASY_count 10  //简易难度下的雷的个数 设为10
 
#define ROW 9          //雷盘的行数
#define COL 9          //雷盘的列数
 
#define ROWS 11        //实际定义雷盘数组的行数和列数
#define COLS 11        //往大了定义是为了排查雷时不出现数组越界的情况
 
              
                       //与之前相比,多了一种判定胜利的方法
int win_s;             //win_s用于记录标记了多少正确的的雷                      
                       //当win_s == EASY_count时,则游戏胜利
 
void game();
 
//初始化棋盘
void Intboard(char board[ROWS][COLS], int rows, int cols, char aim);
 
//打印棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);
 
//布置雷
void Setmine(char board[ROWS][COLS], int row, int col);
 
//排查雷
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
 
//优化排查雷
void get_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col,int* p);
 
//标记雷
void Setnote(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col);

代码(test.c)

#include "game.h"
 
void Description()
{
  int n;
  printf("欢迎来到C语言扫雷游戏,此处为游戏说明\n在游戏中,‘0’表示无雷,‘1’表示有雷\n");
  printf("'*'表示未被排查的区域,‘@’表示自己所标记的雷的位置\n");
  printf("如果您已悉知,请按‘任意键+回车键’确定正式进入游戏选择界面");
  scanf("%d", &n);
  system("cls");
}
 
void menu()
{
  printf("                              \n");
  printf("                              \n");
  printf("                              \n");
  printf("          扫雷游戏            \n");
  printf("           1.开始             \n");
  printf("           0.退出             \n");
  printf("                              \n");
  printf("                              \n");
  printf("                              \n");
}
 
void game()
{
  Sleep(1000);
  system("cls");
  char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
  char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
 
  //初始化棋盘
  Intboard(mine, ROWS, COLS, '0');
  Intboard(show, ROWS, COLS, '*');
 
  //打印棋盘
  //Displayboard(mine, ROW, COL);用于检测雷是否正常布置
  Displayboard(show, ROW, COL);
 
  //布置雷
  Setmine(mine, ROW, COL);
  //Displayboard(mine, ROW, COL);//必要时显示雷盘,便于测试
 
  //排查雷
  Findmine(mine,show, ROW, COL);
}
int main()
{
  srand((unsigned int)time(NULL));
  int input = 0;
    Description();
  do
  {
    menu();
    printf("请选择:");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      game();
      break;
    case 0:
      printf("退出游戏\n");
      break;
    default:
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (input);
  return 0;
}

代码(game.c)

#include "game.h"
 
//初始化棋盘
void Intboard(char board[ROWS][COLS],int rows, int cols, char aim)
{
  int a, b;
  for (a = 0; a < rows; a++)
  {
    for (b = 0; b < cols; b++)
    {
      board[a][b] = aim;
    }
  }
}
 
//打印棋盘
 
void Displayboard(char board[ROWS][COLS], int row, int col)
{
  int a, b;
  for (a = 0; a <= col; a++)
  {
    if (a == 0)
      printf("    ");
    else
        printf("%d ", a);
  }
  printf("\n");
  for (a = 0; a <= col; a++)
  {
    if (a == 0)
      printf("    ");
    else
      printf("%c ", 'v');
  }
  printf("\n");
  for (a = 1; a <= row; a++)
  {
    printf("%d > ", a);
    for (b = 1; b <= col; b++)
    {
      printf("%c ", board[a][b]);
    }
    printf("\n");
  }
}
 
 
//布置雷
void Setmine(char board[ROWS][COLS], int row, int col)
{
  int count = EASY_count;
  while (count)
  {
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (board[x][y] != '1')
    {
      board[x][y] = '1';
      count--;
    }
  }
}
 
 
//排查周围八格总计的雷数
static int get_mine_count(char mine[ROWS][COLS],int x,int y)
{ 
      return mine[x - 1][y - 1] +
           mine[x - 1][y] +
           mine[x - 1][y + 1] +
           mine[x][y - 1] +
           mine[x][y + 1] +
           mine[x + 1][y - 1] +
           mine[x + 1][y] +
           mine[x + 1][y + 1] - 8 * '0';
}
 
//游戏排查雷的主函数
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    //选择要进行排查雷或标记雷
  //输入要排查的坐标或要标记的坐标
  //判断输入的坐标是否合理
  int x = 0, y = 0,flag = 0,note = 0,win = 0;
  int* p = &win;
  while (1)
  {
    printf("请选择要:0.排查雷  1.标记雷\n");
    scanf("%d", &note);
    switch (note)
    {
    case 0:
               opt:
         printf("请输入要排查的雷\n坐标(行号 列号):");
         scanf("%d %d", &x, &y);
         system("cls");
         if (x >= 1 && x <= row && y >= 1 && y <= col)
         {
           if (mine[x][y] == '1')
           {
             printf("很遗憾,你被炸死了\n");
             Displayboard(mine, row, col);
             do
             {
               printf("输入0进行下一步操作:");
               scanf("%d", &flag);
               if (flag == 0)
                 goto exit;
                else
               {
                 printf("输入错误,请重新输入\n");
                 Displayboard(show, row, col);
               }
               Sleep(2500);
             } while (flag);
             break;
           }
           else
           {
                       //用递归实现自动排查周围无雷的格子
             get_mine(mine, show, x, y,row,col,p);
             //显示排查的信息
             Displayboard(show, row, col);
            
           }
         }
         else
         {
           printf("坐标不合法,请重新输入\n");
                   Displayboard(show, row, col);
                   goto opt;
         }
         
         break;
    case 1:
         Setnote(mine, show, x, y,row,col);
         break;
          
    }
    if (win == row * col - EASY_count || win_s == EASY_count)
    {
      printf("恭喜你,排雷成功\n");
      win = 0;
      win_s = 0;
      Sleep(5000);
          exit:
      system("cls");
      break;
    }
  }
}
 
//定义递归函数
void get_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col,int* p)
{
 
  if (x > 0 && x <= row && y > 0 && y <= col)
  {
    int count = get_mine_count(mine, x, y);
    if (count == 0)
    {
      (*p)++;
      int a, b;
      show[x][y] = '0';
      for (a = -1; a <= 1; a++)
      {
        for (b = -1; b <= 1; b++)
        {
          if (show[x + a][y + b] == '*')
            get_mine(mine, show, x + a, y + b, row,col,p);
        }
      }
    }
    else
    {
      (*p)++;
      show[x][y] = count + '0';
    }
  }
}
 
//标记雷的函数定义
void Setnote(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col)
{
  while (1)
  {
    printf("请输入要标记的雷的坐标:");
    scanf("%d %d", &x, &y);
    system("cls");
    if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '0' && show[x][y] != '1')
    {
      show[x][y] = '@';
      if ( mine[x][y] == '1')
        win_s++;
      break;
    }
    else
    {
      printf("输入坐标不合法,请重新输入\n");
      Displayboard(show, row, col);
    }
  }
  Displayboard(show, row, col);
}

游戏测试 *

一、测试递归展开

首先将雷数改为3个

再把雷的位置展示一下

然后输入坐标排查一次,看棋盘的情况



运行正常

二、测试雷的标记功能

我们保持雷数为3个不变

将雷盘展示出来,把所有雷都标记出来


 

正常运行

小结

  • 对于要基本实现扫雷的简单玩法,我们要对之前学过的知识点掌握得相对熟悉并且综合运用。
  • 大致掌握循环语句,选择语句等、基本的嵌套循环、宏定义、二维数组的应用、函数的声明及调用
  • 而对于要实现扫雷的大部分玩法,我们则需要运用到函数递归,指针的简单运用,以及适应更加复杂的逻辑。

值得一提的是:一个小项目分模块分别实现其功能的尝试,所练习到的思路,我想未来在大项目中也是同样有用的。

目录
相关文章
|
11天前
|
存储 编译器 C语言
|
21天前
|
C语言
C语言实战项目——学生试卷分数统计
C语言实战项目——学生试卷分数统计
|
22天前
|
C语言
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
10 1
|
22天前
|
存储 编译器 C语言
c语言学习历程【1】
c语言学习历程【1】
17 1
|
22天前
|
存储 安全 Serverless
扫雷游戏C语言代码实现——万字长文超详细,手把手教你实现,新手也能学会
扫雷游戏C语言代码实现——万字长文超详细,手把手教你实现,新手也能学会
|
12天前
|
存储 算法 程序员
C语言编程—递归
递归是函数自我调用的编程技术,常用于解决分治问题,如计算阶乘和斐波那契数列。示例中展示了C语言的阶乘和斐波那契数列递归实现。递归需满足:问题可转化为规模更小的同类问题,存在结束条件以防止无限循环,并可能消耗大量时间和栈空间。栈用于存储函数调用信息,过多递归可能导致栈溢出。递归虽简洁,但非最优效率选择,递推算法通常是更好的替代方案。
17 0
|
21天前
|
C语言
|
22天前
|
存储 C语言
c语言实现扫雷
c语言实现扫雷
8 0
|
23天前
|
C语言
【C语言】:递归题
【C语言】:递归题
15 0
|
C# C语言 C++
从头开始学习c语言
以前的时候学习C语言时候认为C语言不过是一个学习的工具,学习一些理论知识就达到目的了,谁会用这么傻的语言啊,连个界面也没有,不像vb一下子就做出一个窗体来,放上几个按钮就可以了 后来学习C++的时候,认为C++与C是一种完全不同的语言,两者基本没有什么兼容性,当时在学校里看书的时候,一直比较纳闷为会c++的书上会写C++/C语言教程,到了现在才明白,我去C++与C本来就是一体是一脉相承的
1038 0