C语言两百行代码实现简易扫雷

文章目录

  • 前言
  • 一.代码实现
  • 二.设计思路
  • main()函数搭建框架
  • reset ( )函数
  • dis_play( )函数
  • setmine( )函数
  • player_move( )函数

  • 前言

    扫雷应该是我们接触到的第一个电脑游戏,用c语言实现扫雷对初学者来说是一个不错的锻炼
    编写扫雷只需要用到数组、函数和生成随机数的知识,所以比较适合成为编程学习者编写的第一个小游戏。

    如果不熟悉生成随机数的知识,可以去我的上一篇文章看看《C生成随机数》


    一.代码实现

    第一部分是源码复制就可以使用,每一个自定义函数在第二部分设计思路中都有详细解释,结合代码实现设计思路理解会有一个更好的效果

    #define _CRT_SECURE_NO_WARNINGS 1
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<time.h>
    #define ROW 9
    #define COL 9
    #define ROWS 11
    #define COLS 11
    #define MINE_NUMBER 10
    
    
    
    void reset(char arr[ROWS][COLS], char ch)//将数组的每一个元素赋值为给定值
    {
    	for (int i = 0; i < ROWS; i++)
    	{
    		for (int j = 0; j < COLS; j++)
    		{
    			arr[i][j] = ch;
    		}
    	}
    }
    
    void setmine(char arr[ROWS][COLS])//利用生成随机数的知识使用随机的行号列标在mine数组放置地雷直到将宏定义的地雷数放置完
    {
    	int mine_number = MINE_NUMBER;
    	int row = rand() % 9 + 1;//生成一到九的行号
    	int col = rand() % 9 + 1;//生成一到九的列标
    
    	while (mine_number > 0)
    	{
    		if (arr[row][col] == '0')//用来不是地雷的地方才能放置地雷
    		{
    			arr[row][col] = '1';//安装地雷
    			mine_number--;
    		}
    		else
    		{
    			row = rand() % 9 + 1;
    			col = rand() % 9 + 1;
    		}
    
    	}
    }
    
    void display(char arr[ROWS][COLS])
    {
    	for (int i = 0; i <= COL; i++)
    	{
    		printf(" %d  ", i);//打印行号
    	}
    	printf("\n\n");
    	for (int i = 1; i <= ROW; i++)//第一个和第二个for进去分别打印一行数组和一条分割线
    	{                             //数组行为:(空格)元素(空格)|(空格)元素(空格)|(空格)元素(空格)  
    								  //分割线为: -      -      -   |   -      -     -   |   -     -      -
    		printf(" %d  ", i);//打印列标
    
    		for (int j = 1; j <= COL; j++)
    		{
    
    			printf(" %c ", arr[i][j]);
    			if (j < COL)
    			{
    				printf("|");//为了美观,不打印最后一个'|'
    			}
    
    		}
    		printf("\n");
    		printf("    ");
    
    
    		for (int k = 1; k <= COL; k++)
    		{
    			printf("---");
    			if (k < COL)
    			{
    				printf("|");//为了美观,不打印最后一个'|'
    			}
    
    		}
    		printf("\n");
    	}
    }
    
    
    
    
    
    
    
    char nearmine(char mine[ROWS][COLS], int row, int col)//返回所选坐标字符型的地雷数量
    {
    	return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1]
    		+ mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - 7 * '0');
    }
    
    int player_move(char mine[ROWS][COLS], char show[ROWS][COLS])//首先输入合法坐标然后玩家移动,每一步结果只有两个:踩到雷还有没踩到
    
    {
    	int row = 0, col = 0;
    	int flag = 1;
    	do//输入合法坐标,在行号列标范围内,并且没有被走过
    	{
    		printf("\n---------------------------------------输入坐标,中间用空格隔开:>");
    		scanf("%d %d", &row, &col);
    		if (row >= 1 && row <= 9 && col >= 1 && col <= 9 && show[row][col] == '*')
    			flag = 0;
    		else
    			printf("输入错误\n");
    	} while (flag);
    	if (mine[row][col] == '1')//踩到雷,返回-1
    		return -1;
    	else
    	{
    		show[row][col] = nearmine(mine, row, col);//nearmine将会返回字符型的地雷数
    												  //将用来展示的show数组的相应坐标的元素赋值为周围的地雷数
    		return 1;
    	}
    
    }
    
    void meau()//菜单
    {
    	printf("------------------\n");
    	printf("|     1.play     |\n");
    	printf("|----------------|\n");
    	printf("|     0.exit     |\n");
    	printf("------------------\n");
    
    }
    
    
    void game()
    {
    
    	char mine[ROWS][COLS];//mine:地雷
    	char show[ROWS][COLS];
    	reset(mine, '0');//初始化mine为全零
    	reset(show, '*');//初始化reset为全*
    	setmine(mine);//安置地雷
    	//display(mine);
    	display(show);//展现
    	int count = 0;
    
    	do
    	{
    		int move = player_move(mine, show);
    		if (move == -1)//踩到雷游戏结束
    		{
    			printf("\n---------------------------------------踩到地雷,游戏结束>\n");
    			display(mine);
    			break;
    
    		}
    		else if (move == 1)//没有踩到雷则步数count加一
    		{
    			count++;
    			if (count == ROW * COL - MINE_NUMBER)//判断是否走完了步数,走完了游戏就成功了
    			{
    				printf("\n---------------------------------------排除地雷,游戏结束>\n");
    				display(mine);
    				break;
    
    			}
    
    		}
    
    		display(show);
    
    	} while (1);
    
    
    
    }
    
    int main()
    {
    	int input = 0;
    	srand((unsigned)time(NULL));
    	do
    	{
    		meau();
    		scanf("%d", &input);
    		switch (input)
    		{
    		case 1:
    			game(); break;
    		case 0:
    			break;
    		default:
    			printf("输入错误,请重新输入\n"); break;
    
    		}
    
    	} while (input);
    	return 0;
    
    
    
    }
    

    二.设计思路

    main()函数搭建框架

    1.main()函数搭建框架:像所有的电脑游戏一样,我们需要一个菜单,通过菜单选择进入游戏和退出游戏,当一盘游戏结束时可以再次选择进入或者退出,菜单用printf()打印就可以解决,循环的进入游戏用do while()循环就可以解决

    void meau()//菜单
    {
    	printf("------------------\n");
    	printf("|     1.play     |\n");
    	printf("|----------------|\n");
    	printf("|     0.exit     |\n");
    	printf("------------------\n");
    
    }
    
    
    void game()
    {
        char mine[ROWS][COLS] = {0};//mine:地雷
    	char show[ROWS][COLS] = {0};//show: 展示。0
    	
      ········
    }
    
    int main()
    {
    	int input = 0;
    	srand((unsigned)time(NULL));
    	do
    	{
    		meau();
    		scanf("%d", &input);
    		switch (input)
    		{
    		case 1 :
    			game(); break;
    		case 0 :
    			break;
    		default:
    			printf("输入错误,请重新输入\n"); break;
    
    		}
    
    	} while (input);
    	return 0;
    
    	
    	
    }
    

    当我们输入1,进入case 1时,将运行game()函数,game函数中的代码为具体游戏的实现
    我们需要一个容器来存放游戏数据,二维数组是最好的选择,我们创建一个字符数组mine存放数据,一般扫雷游戏都是9X9的棋盘界面,所以我们可以设置行数和列数都为9,但在实际代码运行中9行9列还是不够的,会出现数组下标越界,所以最好在原来的上下左右各放一行将原来的9×9数组包起,所以我们实际需要一个11×11的数组,由于我们经常会使用,不妨对它宏定义一下

    #define ROW 9
    #define COL 9

    #define ROWS 11
    #define COLS 11

    考虑到扫雷游戏中每选择一步如果没有踩雷,都会修改字符数组mine[ROW][COL]的相应元素来显示周围的地雷个数,这种修改会对游戏造成影响,所以不妨再定义一个数组show[ROW][COL],专门显示地雷数量,将判断和显示分离开

    char mine[ROW][COL]:存放放置地雷和非地雷数据,我们用字符‘0’表示安全区;字符‘1’表示地雷,通过赋值填满数组
    char show[ROW][COL]:当初用来展示的数组,初始化时全部赋值为字符‘*’,每选择一步,就在相应元素处返回周围地雷个数(mine数组相应元素周围‘1’的个数)

    reset ( )函数

    reset() :我们完成一局扫雷,还想下该怎么办?这时候我们构造一个函数reset(),将mine和show数组的每一个元素重置为‘0’和‘*’

    该函数实际功能就是把接收到的数组中全部元素赋值为给定符号

    reset()函数,遍历数组,赋值为给定字符

    void reset(char arr[ROWS][COLS], char ch)//将数组的每一个元素赋值为给定值
    {
    	for (int i = 0; i < ROWS; i++)
    	{
    		for (int j = 0; j < COLS; j++)
    		{
    			arr[i][j] = ch;
    		}
    	}
    }
    

    dis_play( )函数

    dis_play( ):我们扫雷肯定需要“雷区”来显示我们的数据,我们通过构造一个函数dis_play()将数组打印出来来实现这一功能

    dis_play()功能就是借助’|'和‘_’等特殊字符,将数组以类棋盘形式打印出来

    void display(char arr[ROWS][COLS])//借助于‘|’和‘-’,将数组以游戏界面的显示打印出来
    {
    	for (int i = 0; i <= COL; i++)
    	{
    		printf(" %d  ", i);//打印行号
    	}
    	printf("\n\n");
    	for (int i = 1; i <= ROW; i++)//第一个和第二个for进去分别打印一行数组和一条分割线
    	{                             //数组行为:(空格)元素(空格)|(空格)元素(空格)|(空格)元素(空格)  
    								  //分割线为: -      -      -   |   -      -     -   |   -     -      -
    		printf(" %d  ", i);//打印列标
    
    		for (int j = 1; j <= COL; j++)
    		{
    
    			printf(" %c ", arr[i][j]);
    			if (j < COL)
    			{
    				printf("|");//为了美观,不打印最后一个'|'
    			}
    
    		}
    		printf("\n");
    		printf("    ");
    
    
    		for (int k = 1; k <= COL; k++)
    		{
    			printf("---");
    			if (k < COL)
    			{
    				printf("|");//为了美观,不打印最后一个'|'
    			}
    
    		}
    		printf("\n");
    	}
    
    }
    
    
    
    

    运行代码,打印雷区

    运行代码,打印show

    setmine( )函数

    setmine( ):经过reset()初始化后的mine数组中是没有地雷的,我们需要利用生成随机数的知识放置地雷,这时我们构建一个函数完成

    使用头文件#include<stdlib.h>中的rand()函数可以生成一个0~32767的伪随机数,但使用rand()前先要使用srand()设置伪随机数起点
    起点只要写一次,我们将srand((unsigned)time(NULL))定义在主函数,此处是一种固定的写法,time()函数需要引<time.h>头文件
    将rand()%9+1即%ROW+1可以生产1~9这九个随机数,可以用来做数组的行号和列标

    int main()
    {
    	int input = 0;
    	srand((unsigned)time(NULL));//设置起点的固定写法
    	do
    	{
    		meau();
    		scanf("%d", &input);
    		switch (input)
    		{
    		case 1 :
    			game(); break;
    		case 0 :
    			break;
    		default:
    			printf("输入错误,请重新输入\n"); break;
    
    		}
    
    	} while (input);
    	return 0;
    
    	
    	
    }
    
    void setmine(char arr[ROWS][COLS])//利用生成随机数的知识使用随机的行号列标在mine数组放置地雷直到将宏定义的地雷数放置完
    {
    	int mine_number = MINE_NUMBER;
    	int row = rand() % 9 + 1;//生成一到九的行号
    	int col = rand() % 9 + 1;//生成一到九的列标
    
    	while (mine_number > 0)
    	{
    		if (arr[row][col] == '0')//用来不是地雷的地方才能放置地雷
    		{
    			arr[row][col] = '1';//安装地雷
    			mine_number--;
    		}
    		else
    		{
    			row = rand() % 9 + 1;
    			col = rand() % 9 + 1;
    		}
    
    	}
    	
    
    

    运行代码–setmine()前

    运行代码–setmine()后

    player_move( )函数

    player_move( ):扫雷用的棋盘打印好了,地雷放置好了下面要实现的就是移动这一个操作了,扫雷的移动只有两个结果:踩到雷还有没踩到雷,游戏扫胜利就是把除了雷之外的地块都走一次。另外,当我们每一次没有踩到雷时,要返回这个地块周围的地雷数>>

    player_move()函数函数分为两个功能,第一个功能实现合法输入,输入的行号列标都应该在1~9这个范围中,另外,之前排除过的部分就不能再踩了。第二功能实现判断,踩到了雷就返回-1,没有踩到就返回1,并且应该赋值周围地雷雷数给show数组的相应元素,这个操作我们可以构造nearmine()函数完成,由于是字符型数组,nearmine()函数也要返回字符型的数字

    char nearmine(char mine[ROWS][COLS], int row, int col)//返回所选坐标周围8格字符型的地雷数量
    {
       //return 周围8格地雷数的ascll码值用下面这种方法就可以完成
    	return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1]
    		+ mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - 7 * '0');
    }
    
    int player_move(char mine[ROWS][COLS], char show[ROWS][COLS])//首先输入合法坐标然后玩家移动,每一步结果只有两个:踩到雷还有没踩到
                                                                 
    {
    	int row = 0, col = 0;
    	int flag = 1;
    	do//输入合法坐标,在行号列标范围内,并且没有被走过
    	{
    		printf("\n---------------------------------------输入坐标,中间用空格隔开:>");
    		scanf("%d %d", &row, &col);
    		if (row >= 1 && row <= 9 && col >= 1 && col <= 9 && show[row][col] == '*')
    			flag = 0;
    		else
    			printf("输入错误\n");
    	} while (flag);
    	if (mine[row][col] == '1')//踩到雷,返回-1
    		return -1;
    	else
    	{
    		show[row][col] = nearmine(mine, row, col);//nearmine将会返回字符型的地雷数
    		                                          //将用来展示的show数组的相应坐标的元素赋值为周围的地雷数
    		return 1;
    	}
    
    
    
    }
    

    game( )

    在game()函数我们编写程序去接收player_move( )函数返回的“-1”和“1”,接收到“1”,踩到地雷,游戏结束
    接收到“1”,步数count++,并且判断conut是否等于非地雷地块的数量,一旦相等,游戏胜利,并且结束

    void game()
    {
    	
    	char mine[ROWS][COLS] ;//mine:地雷
    	char show[ROWS][COLS] ;
    	reset(mine,'0');//初始化mine为全零
    	reset(show,'*');//初始化reset为全*
    	setmine(mine);//安置地雷
    	//display(mine);
    	display(show);//展现
    	int count = 0;
    	
    	do
    	{
    		int move = player_move(mine, show);
    		if (move==-1)//踩到雷游戏结束
    		{
    			printf("\n---------------------------------------踩到地雷,游戏结束>\n");
    			display(mine);
    			break;
    
    		}
    		else if (move == 1)//没有踩到雷则步数count加一
    		{
    			count++;
    			if (count == ROW * COL - MINE_NUMBER)//判断是否走完了步数,走完了游戏就成功了
    			{
    				printf("\n---------------------------------------排除地雷,游戏结束>\n");
    				display(mine);
    				break;
    
    			}
    
    		}
    		
    		display(show);
    		
    	} while (1);
    
    	
    
    }
    

    整理不易,如果有帮助的话,那就给个三联吧,谢谢

    物联沃分享整理
    物联沃-IOTWORD物联网 » C语言两百行代码实现简易扫雷

    发表评论