数组和函数实践:扫雷游戏

简介: 数组和函数实践:扫雷游戏

前言:

       通过前面的学习,我们已经了解过了数组与函数。今天我们就一起做一个小项目来巩固实践一下吧。


一、扫雷游戏分析和设计

       1.1 扫雷游戏的功能说明

       使⽤控制台实现经典的扫雷游戏

       游戏可以通过菜单实现继续玩或者退出游戏

       扫雷的棋盘是9*9的格⼦

       默认随机布置10个雷

       可以排查雷 ◦ 如果位置不是雷,就显⽰周围有⼏个雷

       如果位置是雷,就炸死游戏结束

       把除10个雷之外的所有⾮雷都找出来,排雷成功,游戏结束

       1.2 游戏的分析和设计

               扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些 信息。 因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个9*9的数组来存放 信息。

       那如果这个位置布置雷,我们就存放1,没有布置雷就存放0。

       我们可以推测:可能需要一个9*9的棋牌。从我们之前玩游戏的经历来看,排雷需要对周围一圈扫描,如果是9*9,那势必会造成越界访问,那雷不就成了薛定谔雷。

       通过综上分析:我们需要一个11*11的棋牌。

       再继续分析,我们在棋盘上布置了雷,棋盘上雷的信息(1)和⾮雷的信息(0),假设我们排查了某 ⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录 存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪⾥呢?如果存放在布 置雷的数组中,这样雷的信息和雷的个数信息就可能或产⽣混淆和打印上的困难。

        这⾥我们肯定有办法解决,⽐如:雷和⾮雷的信息不要使⽤数字,使⽤某些字符就⾏,这样就避免冲 突了,但是这样做棋盘上有雷和⾮雷的信息,还有排查出的雷的个数信息,就⽐较混杂,不够⽅便。 这⾥我们采⽤另外⼀种⽅案,我们专⻔给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再 给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置到 mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期 排查参考。 同时为了保持神秘,show数组开始时初始化为字符 '*',为了保持两个数组的类型⼀致,可以使⽤同⼀ 套函数处理,mine数组最开始也初始化为字符'0',布置雷改成'1'。

       1.3 ⽂件结构设计

       本次游戏代码我们计划采取多文件结构设计,即分为:游戏的测试逻辑,游戏函数的实现,函数声明定义。

1. test.c //⽂件中写游戏的测试逻辑 
2. game.c //⽂件中写游戏中函数的实现等
3. game.h //⽂件中写游戏需要的数据类型和函数声明等

二、代码实现

       2.1 多文件创作的必要操作

  •        函数的声明、类型的声明放在头⽂件(.h)中,函数的实现是放在源⽂件(.c)⽂中。
  •        在game.h头文件处加入#pragma.once
  •        源文件引用头文件采用#include“”(引号中放入需要引用的头文件)

       2.2 实现test.c文件

1. #include"game.h"
2. void menu()
3. {
4.  printf("***********************\n");
5.  printf("******* 1. play *******\n");
6.  printf("******* 0. exit *******\n");
7.  printf("***********************\n");
8. }
9. void game()
10. {
11.   char mine[ROWS][COLS];
12.   char show[ROWS][COLS];
13.   InitBoard(mine,ROWS,COLS,'0');
14.   InitBoard(show, ROWS, COLS, '*');
15.   Disboard(show, ROW, COL);
16.   SetMine(mine, ROW, COL);
17.   FindMine(mine,show, ROW, COL);
18. }
19. int main()
20. {
21.   srand((unsigned int)time(NULL));
22.   int input = 0;
23.   do
24.   {
25.   menu();
26.   printf("请选择:> ");
27.   scanf("%d", &input);
28. 
29.     switch (input)
30.     {
31.     case 1:
32.       printf("开始游戏\n");
33.       game();
34.       break;
35.     case 0:
36.       printf("游戏结束\n");
37.     default:
38.       printf("选择错误,重新选择\n");
39.       break;
40.     }
41.   } while (input);
42.   return 0;
43. }

       2.3 game.h文件

       

1. #define _CRT_SECURE_NO_WARNINGS 1
2. #pragma.once
3. #include <stdio.h>
4. #include<time.h>
5. #include<stdlib.h>
6. 
7. #define EASY_COUNT 10
8. 
9. #define ROW 9
10. #define COL 9
11. 
12. #define ROWS ROW+2
13. #define COLS COL+2
14. 
15. void InitBoard(char board[ROWS][COLS],int rows ,int cols,  char set);//构建棋牌
16. void Disboard(char board[ROWS][COLS],int row, int col);//打印棋牌
17. void SetMine(char board[ROWS][COLS], int row,int col);//埋雷
18. void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col);//扫雷

2.4.game.c 文件

       

1. #define _CRT_SECURE_NO_WARNINGS 1
2. #include"game.h"
3. void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) 
4. {
5.  for (int i = 0; i < rows; i++)
6.  {
7.    for (int j = 0; j < cols; j++)
8.    {
9.      board[i][j] = set;
10.     }
11.   }
12. }
13. void Disboard(char board[ROWS][COLS], int row, int col)
14. {
15.   for (int i = 0; i <= col; i++)
16.   {
17.     printf("%2d ", i);
18.   }
19.   printf("\n");
20.   for (int i = 1; i < row; i++)
21.   {
22.     printf("%2d", i);
23.     for (int j = 0; j < col; j++)
24.     {
25.       printf("%3c", board[i][j]);
26.     }
27.     printf("\n");
28.   }
29. }
30. void SetMine(char board[ROWS][COLS], int row, int col)
31. {
32.   int count = EASY_COUNT;
33.   while (count)
34.   {
35.     int x = rand() % 10 + 1;
36.     int y = rand() % 10 + 1;
37.     if (board[x][y] == '0')
38.     {
39.       board[x][y] = '1';
40.       count--;
41.     }
42.   }
43. }int GetMine(char mine[ROWS][COLS], int x, int y)
44. {
45.   return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x+1][y]+
46.     mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
47. }void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
48. {
49.   int x = 0;
50.   int y = 0;
51.   int win = 0;
52.   while(x >= 1 && x <= row && y >= 1 && y <= col)
53.   {
54.     printf("请输入坐标:");
55.     scanf("%d %d", &x, & y);
56.     if (x >= 1 && x <= row && y >= 1 && y <= col)
57.     {
58.       if (mine[x][y] == '1')
59.       {
60.         printf("很遗憾,你被炸死了\n");
61.         DisplayBoard(mine, ROW, COL);
62.         break;
63.       }
64.       else
65.       {
66.         //该位置不是雷,就统计这个坐标周围有⼏个雷
67.         int count = GetMineCount(mine, x, y);
68.         show[x][y] = count + '0';
69.         DisplayBoard(show, ROW, COL);
70.         win++;
71.       }
72.     }
73.     else
74.     {
75.       printf("坐标⾮法,重新输⼊\n");
76.     }
77.   }
78.   if (win == row * col - EASY_COUNT)
79.   {
80.     printf("恭喜你,排雷成功\n");
81.     DisplayBoard(mine, ROW, COL);
82.   }
83. }

2.5小结

               以上便是扫雷代码的简单实现,按照大多数的套路此处应该是:好了本次的学习到这里就结束了,喜欢的还请一件三连(此事确实重要),再见。

               但是,这篇文章能和大多数比吗?答案显而易见,自在你们心中。

               所以,预知后事如何,且看下回分晓!

三、下回

       文接上回,我们已学习了简单的扫雷代码实现,能不能来点复杂的呢?没问题,满足你。马上就安排上。

       正所谓,扫雷就要能标记上,不然咱们的努力就会白费。代码如下:

       

1. void Mark(char board[ROWS][COLS], int row, int col)
2. {
3.  int x = 0;
4.  int y = 0;
5.  while (1)
6.  {
7.    printf("\n请输入要标记的坐标>>");
8.    scanf("%d %d", &x, &y);
9.    if (x >= 1 && x <= row && y >= 1 && y <= col)       //判断玩家输入坐标是否合法
10.     {
11.       if (board[x][y] == '!')       //若坐标元素为 '!' 则表示该坐标已被标记过
12.       {
13.         printf("\n<该坐标已标记,无需重复标记,请重新输入>\n");
14.       }
15.       else       //该坐标未被标记
16.       {
17.         board[x][y] = '!';       //将 '!' 赋值给该坐标元素
18.         DisplayBoard(board, row, col);       //将标记地雷后的雷区展示于玩家
19.         break;
20.       }
21.     }
22.     else
23.     {
24.       printf("\n<坐标非法,请重新输入>\n\n");
25.     }
26.   }
27. }

       万一标记错了,那如何取消呢?代码实现如下:’

       

1. void UnMark(char board[ROWS][COLS], int row, int col)
2. {
3.  int x = 0;
4.  int y = 0;
5.  while (1)
6.  {
7.    printf("\n请输入要取消标记的坐标>>");
8.    scanf("%d %d", &x, &y);
9.    if (x >= 1 && x <= row && y >= 1 && y <= col)       //判断玩家输入坐标是否合法
10.     {
11.       if (board[x][y] != '!')       //该坐标未被标记
12.       {
13.         int i = 0;
14.         int j = 0;
15.         int flag = 1;       // flag 用于判断雷区内是否有标记,若雷区内没有标记则 flag = 1
16.         for (i = 1; i <= row; i++)       //对 show 数组遍历
17.         {
18.           for (j = 1; j <= col; j++)
19.           {
20.             if (board[i][j] == '!')       //雷区内存在标记
21.             {
22.               flag = 0;       //雷区内存在标记,则将 flag 赋值为0
23.               goto out;       //利用 goto 语句将程序的进程跳转至第27行的 out 处开始执行
24.             }
25.           }
26.         }
27.       out:
28.         if (flag)       // flag == 1,则表示雷区内不存在标记;flag == 0,则表示雷区内存在标记
29.         {
30.           printf("\n<雷区尚未做标记,无需取消>\n\n");
31.           return;
32.         }
33.         else       //雷区内存在标记,但玩家输入的坐标未被标记
34.         {
35.           printf("\n<该坐标未标记,无需取消,请重新输入>\n");
36.         }
37.       }
38.       else       //该坐标已被标记
39.       {
40.         board[x][y] = '*';       //用字符 '*' 来覆盖 '!',从而达到取消标记的效果
41.         DisplayBoard(board, row, col);       //将取消标记后的雷区展示于玩家
42.         break;
43.       }
44.     }
45.     else
46.     {
47.       printf("\n<坐标非法,请重新输入>\n\n");
48.     }
49.   }
50. }

 

   四、完整代码

       game.h

1. #define _CRT_SECURE_NO_WARNINGS 1
2. #pragma.once
3. #include <stdio.h>
4. #include<time.h>
5. #include<stdlib.h>
6. 
7. #define EASY_COUNT 10
8. 
9. #define ROW 9
10. #define COL 9
11. 
12. #define ROWS ROW+2
13. #define COLS COL+2
14. 
15. void InitBoard(char board[ROWS][COLS],int rows ,int cols,  char set);//构建棋牌
16. void Disboard(char board[ROWS][COLS],int row, int col);//打印棋牌
17. void SetMine(char board[ROWS][COLS], int row,int col);//埋雷
18. void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col);//扫雷
19. void Mark(char board[ROWS][COLS], int row, int col);//标记雷
20. void UnMark(char board[ROWS][COLS], int row, int col);//取消标记

        game.h'

       

1. #define _CRT_SECURE_NO_WARNINGS 1
2. #include"game.h"
3. void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) 
4. {
5.  for (int i = 0; i < rows; i++)
6.  {
7.    for (int j = 0; j < cols; j++)
8.    {
9.      board[i][j] = set;
10.     }
11.   }
12. }
13. void Disboard(char board[ROWS][COLS], int row, int col)
14. {
15.   for (int i = 0; i <= col; i++)
16.   {
17.     printf("%2d ", i);
18.   }
19.   printf("\n");
20.   for (int i = 1; i < row; i++)
21.   {
22.     printf("%2d", i);
23.     for (int j = 0; j < col; j++)
24.     {
25.       printf("%3c", board[i][j]);
26.     }
27.     printf("\n");
28.   }
29. }
30. void SetMine(char board[ROWS][COLS], int row, int col)
31. {
32.   int count = EASY_COUNT;
33.   while (count)
34.   {
35.     int x = rand() % 10 + 1;
36.     int y = rand() % 10 + 1;
37.     if (board[x][y] == '0')
38.     {
39.       board[x][y] = '1';
40.       count--;
41.     }
42.   }
43. 
44. }int GetMine(char mine[ROWS][COLS], int x, int y)
45. {
46.   return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x+1][y]+
47.     mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
48. }void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
49. {
50.   int x = 0;
51.   int y = 0;
52.   int win = 0;
53.   while(x >= 1 && x <= row && y >= 1 && y <= col)
54.   {
55.     printf("请输入坐标:");
56.     scanf("%d %d", &x, & y);
57.     if (x >= 1 && x <= row && y >= 1 && y <= col)
58.     {
59.       if (mine[x][y] == '1')
60.       {
61.         printf("很遗憾,你被炸死了\n");
62.         DisplayBoard(mine, ROW, COL);
63.         break;
64.       }
65.       else
66.       {
67.         //该位置不是雷,就统计这个坐标周围有⼏个雷
68.         int count = GetMineCount(mine, x, y);
69.         show[x][y] = count + '0';
70.         DisplayBoard(show, ROW, COL);
71.         win++;
72.       }
73.     }
74.     else
75.     {
76.       printf("坐标⾮法,重新输⼊\n");
77.     }
78.   }
79.   if (win == row * col - EASY_COUNT)
80.   {
81.     printf("恭喜你,排雷成功\n");
82.     DisplayBoard(mine, ROW, COL);
83.   }
84. }
85. void Mark(char board[ROWS][COLS], int row, int col)
86. {
87.   int x = 0;
88.   int y = 0;
89.   while (1)
90.   {
91.     printf("\n请输入要标记的坐标>>");
92.     scanf("%d %d", &x, &y);
93.     if (x >= 1 && x <= row && y >= 1 && y <= col)       //判断玩家输入坐标是否合法
94.     {
95.       if (board[x][y] == '!')       //若坐标元素为 '!' 则表示该坐标已被标记过
96.       {
97.         printf("\n<该坐标已标记,无需重复标记,请重新输入>\n");
98.       }
99.       else       //该坐标未被标记
100.      {
101.        board[x][y] = '!';       //将 '!' 赋值给该坐标元素
102.        DisplayBoard(board, row, col);       //将标记地雷后的雷区展示于玩家
103.        break;
104.      }
105.    }
106.    else
107.    {
108.      printf("\n<坐标非法,请重新输入>\n\n");
109.    }
110.  }
111. }
112. void UnMark(char board[ROWS][COLS], int row, int col)
113. {
114.  int x = 0;
115.  int y = 0;
116.  while (1)
117.  {
118.    printf("\n请输入要取消标记的坐标>>");
119.    scanf("%d %d", &x, &y);
120.    if (x >= 1 && x <= row && y >= 1 && y <= col)       //判断玩家输入坐标是否合法
121.    {
122.      if (board[x][y] != '!')       //该坐标未被标记
123.      {
124.        int i = 0;
125.        int j = 0;
126.        int flag = 1;       // flag 用于判断雷区内是否有标记,若雷区内没有标记则 flag = 1
127.        for (i = 1; i <= row; i++)       //对 show 数组遍历
128.        {
129.          for (j = 1; j <= col; j++)
130.          {
131.            if (board[i][j] == '!')       //雷区内存在标记
132.            {
133.              flag = 0;       //雷区内存在标记,则将 flag 赋值为0
134.              goto out;       //利用 goto 语句将程序的进程跳转至第27行的 out 处开始执行
135.            }
136.          }
137.        }
138.      out:
139.        if (flag)       // flag == 1,则表示雷区内不存在标记;flag == 0,则表示雷区内存在标记
140.        {
141.          printf("\n<雷区尚未做标记,无需取消>\n\n");
142.          return;
143.        }
144.        else       //雷区内存在标记,但玩家输入的坐标未被标记
145.        {
146.          printf("\n<该坐标未标记,无需取消,请重新输入>\n");
147.        }
148.      }
149.      else       //该坐标已被标记
150.      {
151.        board[x][y] = '*';       //用字符 '*' 来覆盖 '!',从而达到取消标记的效果
152.        DisplayBoard(board, row, col);       //将取消标记后的雷区展示于玩家
153.        break;
154.      }
155.    }
156.    else
157.    {
158.      printf("\n<坐标非法,请重新输入>\n\n");
159.    }
160.  }
161. }

       test.c

       

1. #define _CRT_SECURE_NO_WARNINGS 1
2. #include"game.h"
3. void menu()
4. {
5.  printf("***********************\n");
6.  printf("******* 1. play *******\n");
7.  printf("******* 0. exit *******\n");
8.  printf("***********************\n");
9. }
10. void game()
11. {
12.   char mine[ROWS][COLS];
13.   char show[ROWS][COLS];
14.   InitBoard(mine,ROWS,COLS,'0');
15.   InitBoard(show, ROWS, COLS, '*');
16.   Disboard(show, ROW, COL);
17.   SetMine(mine, ROW, COL);
18.   FindMine(mine,show, ROW, COL);
19. }
20. int main()
21. {
22.   srand((unsigned int)time(NULL));
23.   int input = 0;
24.   do
25.   {
26.   menu();
27.   printf("请选择:> ");
28.   scanf("%d", &input);
29. 
30.     switch (input)
31.     {
32.     case 1:
33.       printf("开始游戏\n");
34.       game();
35.       break;
36.     case 0:
37.       printf("游戏结束\n");
38.     default:
39.       printf("选择错误,重新选择\n");
40.       break;
41.     }
42.   } while (input);
43.   return 0;
44. }
相关文章
|
1月前
|
存储 C语言
数组和函数实践:扫雷游戏玩法和棋盘初始化(1)
数组和函数实践:扫雷游戏玩法和棋盘初始化(1)
|
1月前
|
C语言
C语言之详解数组【附三子棋和扫雷游戏实战】(二)
C语言之详解数组【附三子棋和扫雷游戏实战】(二)
|
1月前
扫雷游戏(优化版)
扫雷游戏(优化版)
32 0
扫雷游戏(优化版)
|
1月前
|
存储 编译器 C语言
C语言之详解数组【附三子棋和扫雷游戏实战】(一)
C语言之详解数组【附三子棋和扫雷游戏实战】(一)
|
8月前
|
C语言
[C语言][小游戏][猜拳游戏]
[C语言][小游戏][猜拳游戏]
[C语言][小游戏][猜拳游戏]
|
7月前
|
C语言
扫雷游戏(数组与函数实现)
扫雷游戏(数组与函数实现)
|
10月前
|
C语言
【C语言】数组的应用实例:扫雷游戏
扫雷的游戏规则 扫雷就是要把所有非地雷的格子揭开即胜利;踩到地雷格子就算失败。 游戏主区域由很多个方格组成。使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字;方格中数字则表示其周围的8个方格隐藏了几颗雷。
89 0
【C语言】数组的应用实例:扫雷游戏
|
10月前
|
编译器 程序员 C语言
【C语言】 数组的应用实例:三子棋游戏
三子棋规则 将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了 三子棋游戏运行步骤,进入游戏界面,选择进入游戏或者退出游戏,输入1或者0来实现进入游戏或者退出游戏,棋盘进行初始化处理,打印空棋盘,玩家进行下棋,电脑下棋,判断输赢 判断输赢分为: 玩家赢 电脑赢 棋盘无空位(双方平局) 继续下棋(返回第6步)
97 0
【C语言】 数组的应用实例:三子棋游戏
|
10月前
小白的第二个项目--扫雷游戏
小白的第二个项目--扫雷游戏
58 0
|
10月前
|
C语言
扫雷小游戏 递归展开
扫雷小游戏 递归展开