C语言实现简单的扫雷游戏

前言

本篇博客是描述使用C语言做一个简易的扫雷游戏

游戏规则

用户进入会看到一个菜单,输入1开始游戏,输入0即结束游戏,输入除0和1外的其他数字,会提示重新输入。

输入1之后,会打印一个9x9的输出,默认值是*,在这边我将它叫做棋盘,用户只需输入想要点开的棋盘坐标

  • *代表为被点开
  • #代表炸弹
  • 数字代表周围的一圈有几个炸弹

代码实现

使用头文件引用一些用到的库和定义一些全局变量

  • 这里我用game.h来命名

    • 这里有个问题就是如果是一个9x9的棋盘,四边没法判断地雷的个数,例如左下角的周围只有3个

    • 于是这里我采取是将整个棋盘放大一圈

    • 在这里插入图片描述

    • #pragma once
      
      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      
      #define ROW 9  // 棋盘行数
      #define COL 9  // 棋盘列数
      #define ROWS ROW+2  // 棋盘放大后的行数
      #define COLS COL+2  // 棋盘放大后的列数
      #define MINE_COUNT 10  // 炸弹数
      

实现用户进入游戏的功能

这里我新建了一个test.c的文件,实现用户输入1进入游戏,0退出的功能。

  • test.c

    • #define _CRT_SECURE_NO_WARNINGS 1
      
      #include "game.h"  // 引用头文件
      
      
      void menu()
      {
      	printf("***************************************\n");
      	printf("*****            1 play          ******\n");
      	printf("*****            0 exit          ******\n");
      	printf("***************************************\n");
      }
      
      void game()
      {
      	// 游戏主函数
      }
      
      int main()
      {
      	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;
      }
      

游戏主函数

创建并初始化棋盘

这里我采用的是初始化两个A,B棋盘,A上显示每个左边对应的值,B显示的全是*,用来给用户选择,选中的情况下就将A上对应的值赋值到B上。

mine <–> A棋盘,show <–> B棋盘

  • test.c中的game函数中实现

    • 
      void game()
      {
      	// 创建
      	char mine[ROWS][COLS];  // A棋盘
      	char show[ROWS][COLS];  // B棋盘
      }
      
  • 创建game.c,在这个文件中完成初始化函数

    • // 初始化函数
      // board->二维数组,rows->行数,cols->列数,set->初始化成什么字符串
      void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
      {
      	int i = 0;
      	int j = 0;
      	for (i = 0; i < rows; i++)
      	{
      		for (j = 0; j < cols; j++)
      		{
      			board[i][j] = set;
      		}
      	}
      }
      
  • game.h中声明

    • // 初始化
      void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
      
  • 然后再在test.c中引用

    • void game()
      {
      	// 创建并初始化
      	char mine[ROWS][COLS];
      	char show[ROWS][COLS];
      	InitBoard(mine, ROWS, COLS, '0');
      	InitBoard(show, ROWS, COLS, '*');
      }
      

打印棋盘

  • game.c实现打印棋盘的函数,这里为了更好的视觉效果,我在左侧和上面标注了行号和列号

    • // 打印
      void DisplayBoard(char board[ROWS][COLS], int row, int col)
      {
      	int i = 0;
      	int j = 0;
      	printf("\n\n------------- 开始位置 -----------------\n");
      	for (i = 0; i <= col; i++)
      		printf("%d ", i);  // 打印行号
      	printf("\n");
      	for (i = 1; i <= row; i++)
      	{
      		printf("%d ", i);  // 打印列号
      		for (j = 1; j <= col; j++)
      		{
      			printf("%c ", board[i][j]);
      		}
      		printf("\n");
      	}
      	printf("------------- 结束位置 -----------------\n\n\n");
      }
      
  • game.h中声明

    • // 打印
      void DisplayBoard(char board[ROWS][COLS], int row, int col);
      
  • test.c中引用,因为进游戏的第一时间需要将B棋盘打印出来,即全是*的那个

    • void game()
      {
      	// 创建并初始化
      	char mine[ROWS][COLS];  // A棋盘
      	char show[ROWS][COLS];  // B棋盘
      	InitBoard(mine, ROWS, COLS, '0');
      	InitBoard(show, ROWS, COLS, '*');
          
          // 打印
         	DisplayBoard(show, ROW, COL);
      }
      

布置棋盘

  • 先在game.c中完成相关的函数

    • // 在布置非雷的位置,需要统计统计周围的个数
      int get_mine_count(char mine[ROWS][COLS], int x, int y)
      {
      	int i = 0;
      	int j = 0;
      	int count = 0;
      	for (i = -1; i <= 1; i++)
      	{
      		for (j = -1; j <= 1; j++)
      		{
      			if (mine[x + i][y + j] == '#')
      				count++;
      		}
      	}
      	return count;
      }
      
      // 布置整个布局 --> 在布置雷的时候应该将其他的位置也设置好了
      void LayoutBoard(char mine[ROWS][COLS], int row, int col)
      {
      	// 1.布置雷
      	int count = MINE_COUNT;
      	while (count>0)
      	{
      		int x = rand() % row + 1;
      		int y = rand() % col + 1;
      		if (mine[x][y] == '#')
      			// 已经是雷的情况下
      			continue;
      
      		// 不是雷的情况下,布置
      		mine[x][y] = '#';
      		count--;
      	}
          
      	// 布置除雷外的其他位置
      	int i = 0;
      	int j = 0;
      	for (i = 1; i <= row; i++)
      	{
      		for (j = 1; j <= col; j++)
      		{
      			if (mine[i][j] != '#')
      			{
                       // 这里有个知识点,例如周围只有一个雷,返回的是1,但是mine是一个字符数组,所以这里我需要加上一个字符0,这样就能变成字符1,具体可以看一下ascii码值表
      				mine[i][j] = get_mine_count(mine, i, j) + '0';  
      			}
      		}
      	}
      }
      
  • game.h中声明

    • // 布置棋盘
      void LayoutBoard(char mine[ROWS][COLS], int row, int col);
      
  • test.c中引用

    • void game()
      {
      	// 创建并初始化
      	char mine[ROWS][COLS];  // A棋盘
      	char show[ROWS][COLS];  // B棋盘
      	InitBoard(mine, ROWS, COLS, '0');
      	InitBoard(show, ROWS, COLS, '*');
          
          // 打印
         	DisplayBoard(show, ROW, COL);
          
         	// 布置整个布局(雷+雷数)这里布置的是mine,也就是A棋盘,因为A棋盘是不给玩家看的,所以可以直接布局
      	LayoutBoard(mine, ROW, COL);
      }
      

玩家开始排查雷

  • game.c实现相关函数

    • 排查雷分为以下几种方案

      1. 输入的坐标超出棋盘,提示用户重新输入
      2. 输入正确,排查是不是雷
        1. 是雷的情况下,结束
        2. 不是雷的情况下
          1. 判断用户是否已经点开,点开了提示用户重新输入
          2. 没有点开
            1. 如果不是0,继续下一轮
            2. 如果是0,递归点开周围一片,直到非0
      3. 当B棋盘中的*的数量等于地雷的数量,玩家获胜结束

      以下函数CheckMine是主函数,reveal是递归点开周围一片的,NoDisplay是统计未点开的*的个数

    • // 判断点开的是否是0,是就递归点开四周,直到出现非0,不是就只显示当前点开的这个
      void reveal(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
      {
      	if (x< 1 || y< 1 || x>ROW || y>COL || mine[x][y] == '#' || show[x][y] != '*')
      		// 超出边界/是地雷/点开了  结束递归
      		return;
      
      	show[x][y] = mine[x][y];  // 先将点开的显示
      	if (mine[x][y] != '0')
      	{
      		// 当点开的不是0,结束递归
      		return;
      	}
      
      	for (int i = -1; i <= 1; i++)
      	{
      		for (int j = -1; j <= 1; j++)
      		{
      			// 开始递归
      			reveal(mine, show, x + i, y + j);
      		}
      	}
      
      }
      
      // 统计还有多少个是*
      int NoDisplay(char show[ROWS][COLS], int row, int col)
      {
      	int count = 0;
      	for (int i = 1; i <= row; i++)
      	{
      		for (int j = 1; j <= col; j++)
      		{
      			if (show[i][j] == '*')
      				count += 1;
      		}
      	}
      	return count;
      }
      
      
      // 排查雷
      void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
      {
      	int x = 0;
      	int y = 0;
      	while (1)
      	{
      		printf("请输入你要选的坐标:>");
      		scanf("%d %d", &x, &y);
      		if (x >= 1 && x <= row && y >= 1 && y <= col)
      		{
                   // 当用户输入的坐标在范围内,因为棋盘四周加了一圈,所以实际的是小一圈的,即1-row,1-col
      			if (show[x][y] != '*')
      			{
                        // 判断是否已经选过了
      				printf("该坐标您已经选过了,请重新选择!\n\n");
      				continue;
      			}
      
      			if (mine[x][y] == '#')
      			{
                        // 是雷的情况下
      				printf("游戏结束,以下是所有的布局!\n");
      				DisplayBoard(mine, row, col);  // 游戏结束,将答案显示
      				break;
      			}
      			else
      			{
      				// 不是雷的情况
      				reveal(mine, show, x, y);  // 调用函数
      				DisplayBoard(show, row, col);  // 显示棋盘让用户看到自己选的
      				int win = NoDisplay(show, row, col);  // 获取没有点开的数量
      				if (win != MINE_COUNT)
      				{
                            // 提示用户有多少没点开 
      					printf("你还有%d个坐标没选!\n\n\n", win);
      				}
      				else
      				{
                            // 剩余的*下都是雷,用户赢了,结束游戏,并显示答案
      					printf("你居然赢了!\n");
      					DisplayBoard(mine, col, row);
      					break;
      				}
      				
      			}
      		}
      		else
      		{
      			// 输入坐标超出范围的情况下
      			printf("输入的坐标错误,请重新输入!\n\n");
      		}
      	}
      }
      
  • game.h中声明

    • // 排查雷
      void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
      
  • test.c引用

    • void game()
      {
      	// 初始化
      	char mine[ROWS][COLS];
      	char show[ROWS][COLS];
      	InitBoard(mine, ROWS, COLS, '0');
      	InitBoard(show, ROWS, COLS, '*');
      
      	// 打印
      	//DisplayBoard(mine, ROW, COL);
      	DisplayBoard(show, ROW, COL);
      
      	// 布置整个布局(雷+雷数)
      	LayoutBoard(mine, ROW, COL);
          
      	// 排查雷
      	CheckMine(mine, show, ROW, COL);
      }
      

总结

这里采用的是整个布局一起布置,也可以在边排查雷的时候变算出点开的位置周围有几个雷并输出。

这边也没有实现填雷的功能,不过很简单,只需要让用户输入一个指定的数字进入填雷状态,然后提示用户输入坐标,将B棋盘中用户输入的坐标改成相应的字符就行了。

以下是全部代码,有问题可以留言

  • game.h

    • #pragma once
      
      
      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      
      #define ROW 9
      #define COL 9
      #define ROWS ROW+2
      #define COLS COL+2
      #define MINE_COUNT 10
      
      // 初始化
      void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
      
      // 打印
      void DisplayBoard(char board[ROWS][COLS], int row, int col);
      
      // 布置棋盘
      void LayoutBoard(char mine[ROWS][COLS], int row, int col);
      
      // 排查雷
      void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
      
  • game.c

    • #define _CRT_SECURE_NO_WARNINGS 1
      
      #include "game.h"
      
      void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
      {
      	int i = 0;
      	int j = 0;
      	for (i = 0; i < rows; i++)
      	{
      		for (j = 0; j < cols; j++)
      		{
      			board[i][j] = set;
      		}
      	}
      }
      
      // 打印
      void DisplayBoard(char board[ROWS][COLS], int row, int col)
      {
      	int i = 0;
      	int j = 0;
      	printf("\n\n------------- 开始位置 -----------------\n");
      	for (i = 0; i <= col; i++)
      		printf("%d ", i);
      	printf("\n");
      	for (i = 1; i <= row; i++)
      	{
      		printf("%d ", i);
      		for (j = 1; j <= col; j++)
      		{
      			printf("%c ", board[i][j]);
      		}
      		printf("\n");
      	}
      	printf("------------- 结束位置 -----------------\n\n\n");
      }
      
      // 统计周围的个数
      int get_mine_count(char mine[ROWS][COLS], int x, int y)
      {
      	int i = 0;
      	int j = 0;
      	int count = 0;
      	for (i = -1; i <= 1; i++)
      	{
      		for (j = -1; j <= 1; j++)
      		{
      			if (mine[x + i][y + j] == '#')
      				count++;
      		}
      	}
      	return count;
      }
      
      // 布置整个布局 --> 在布置雷的时候应该将其他的位置也设置好了
      void LayoutBoard(char mine[ROWS][COLS], int row, int col)
      {
      	// 1.布置雷
      	int count = MINE_COUNT;
      	while (count>0)
      	{
      		int x = rand() % row + 1;
      		int y = rand() % col + 1;
      		if (mine[x][y] == '#')
      			// 已经是雷的情况下
      			continue;
      
      		// 不是雷的情况下,布置
      		mine[x][y] = '#';
      		count--;
      	}
      	// 布置除雷外的其他位置
      	int i = 0;
      	int j = 0;
      	for (i = 1; i <= row; i++)
      	{
      		for (j = 1; j <= col; j++)
      		{
      			if (mine[i][j] != '#')
      			{
      				mine[i][j] = get_mine_count(mine, i, j) + '0';
      			}
      		}
      	}
      }
      
      void reveal(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
      {
      	if (x< 1 || y< 1 || x>ROW || y>COL || mine[x][y] == '#' || show[x][y] != '*')
      		// 超出边界/地雷/选过了
      		return;
      
      	show[x][y] = mine[x][y];
      	if (mine[x][y] != '0')
      	{
      		// 结束递归
      		return;
      	}
      
      	for (int i = -1; i <= 1; i++)
      	{
      		for (int j = -1; j <= 1; j++)
      		{
      			// 开始递归
      			reveal(mine, show, x + i, y + j);
      		}
      	}
      
      }
      
      // 统计还有多少个是*
      int NoDisplay(char show[ROWS][COLS], int row, int col)
      {
      	int count = 0;
      	for (int i = 1; i <= row; i++)
      	{
      		for (int j = 1; j <= col; j++)
      		{
      			if (show[i][j] == '*')
      				count += 1;
      		}
      	}
      	return count;
      }
      
      
      // 排查雷
      void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
      {
      	/* 
      		超出范围,提示一下
      		排查是不是雷
      			是雷的情况下,游戏结束
      			不是雷的情况下, 需要统计周围雷的个数
      	*/
      	int x = 0;
      	int y = 0;
      	while (1)
      	{
      		int sw = 0;
      		//printf("0代表选择坐标,1代表进入填雷状态!\n您本次是想选择还是想填雷(0/1)>");
      		//scanf("%d", &sw);
      		printf("请输入你要选的坐标:>");
      		scanf("%d %d", &x, &y);
      		if (x >= 1 && x <= row && y >= 1 && y <= col)
      		{
      			// 判断是否已经选过了
      			if (show[x][y] != '*')
      			{
      				printf("该坐标您已经选过了,请重新选择!\n\n");
      				continue;
      			}
      
      			// 判断是不是雷
      			if (mine[x][y] == '#')
      			{
      				printf("游戏结束,以下是所有的布局!\n");
      				DisplayBoard(mine, row, col);  // 显示所有
      				break;
      			}
      			else
      			{
      				// 不是雷的情况
      				reveal(mine, show, x, y);
      				DisplayBoard(show, row, col);
      				int win = NoDisplay(show, row, col);
      				if (win != MINE_COUNT)
      				{
      					printf("你还有%d个坐标没选!\n\n\n", win);
      				}
      				else
      				{
      					printf("你居然赢了!\n");
      					DisplayBoard(mine, col, row);
      					break;
      				}
      			}
      		}
      		else
      		{
      			// 输入坐标超出范围的情况下
      			printf("输入的坐标错误,请重新输入!\n\n");
      		}
      	}
      }
      
  • test.c

    • #define _CRT_SECURE_NO_WARNINGS 1
      
      #include "game.h"
      
      
      void menu()
      {
      	printf("***************************************\n");
      	printf("*****            1 play          ******\n");
      	printf("*****            0 exit          ******\n");
      	printf("***************************************\n");
      }
      
      void game()
      {
      	// 初始化
      	char mine[ROWS][COLS];
      	char show[ROWS][COLS];
      	InitBoard(mine, ROWS, COLS, '0');
      	InitBoard(show, ROWS, COLS, '*');
      
      	// 打印
      	//DisplayBoard(mine, ROW, COL);
      	DisplayBoard(show, ROW, COL);
      
      	// 布置整个布局(雷+雷数)
      	LayoutBoard(mine, ROW, COL);
      	//DisplayBoard(mine, ROW, COL);
      	// 排查雷
      	CheckMine(mine, show, ROW, COL);
      }
      
      int main()
      {
      	int input = 0;
      	srand((unsigned int)time(NULL));
      	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;
      }
      
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值