c语言扫雷游戏,可以递归展开非雷位置,第一次不踩雷

一、 问题

c语言实现扫雷游戏,非雷位置可以递归展开,且为保证游戏体验,用户第一次扫雷时不能踩中雷。

二、解决思路

  • 扫雷游戏大家都玩过,具体规则我也就不再赘述。要实现扫雷游戏,我们至少得需要两个棋盘,一张给用户展示,让用户输入在这张棋盘上进行扫雷,另一张为埋雷棋盘,这张棋盘上埋藏若干个雷,将用户输入的坐标与这张棋盘上对应的坐标进行对比,如果是雷,则游戏结束,如果不是雷则继续。

  • 程序需要实现的功能以及实现该功能的函数。

  • menu函数,指引用户做出选择。

  • game函数,游戏从这里开始。

  • setmine函数,实现埋雷,并找到一个没有雷的坐标,用于保护用户第一次不能踩中雷。

  • show函数,展示棋盘。

  • minecount函数,计算该位置周围雷的个数。

  • open函数,对非雷位置进行递归展开。

  • judge函数,判断用户是否扫雷成功。

三、代码实现

  • 第一步,编写game.h头文件,把需要用到的函数声明及一些宏定义写在里面。
#ifndef __GAME_H__
#define __GAME_H__

#include <stdio.h>
#include <windows.h>
#include <time.h>

#pragma warning(disable:4996)

#define ROW 12
#define COL 12
#define MINENUM 20

void menu();
void setmine(char mineboard[][COL],int * x, int * y);
void show(char board[][COL]);
int minecount(char mineboard[][COL], int x, int y);
void game();
void open(char mineboard[][COL], char sweepboard[][COL], int x, int y);
int judge(char sweepboard[][COL]);

#endif

  • 第二步,编写main函数,从这里调用函数
#include "game.h"

int main() 
{
 	menu();
	system("pause");
	return 0;
}
  • 第三步,编写game.c,把需要用到的函数都写在里面。
  • menu函数
void menu() //menu函数,指引用户做出选择。
{
	int quit = 1;
	while (quit) {
		printf("###############################\n");
		printf("#######  欢迎来到扫雷  ########\n");
		printf("###############################\n");
		printf("#####1:play               ####\n");
		printf("#####2:exit               ####\n");
		printf("###############################\n");
		printf("###############################\n");
		int k = 0;
		scanf("%d", &k);
		switch (k) {
		case 1:
			game();//输入1,调用game函数,游戏开始。
			break;
		case 2:
			quit = 0;
			printf("Bye bye!\n");//输入0,程序运行结束。
			break;
		default:
			printf("输入有误,请重新输入:\n");
			break;
		}
	}
}
  • game函数
void game()//  game函数,游戏开始。
{
	int flag = 1;
	int x0 = 0;
	int y0 = 0;
	char sweepboard[ROW][COL];//第一个二维数组,扫描棋盘,用于给用户展示。为方便计算
	                          // 定义数组为12 * 12,实际用到的是10 * 10的棋盘,后续棋盘均指10 * 10棋盘。
	char mineboard[ROW][COL];//第二个二维数组,埋雷棋盘,用于埋雷。
	memset(sweepboard, '*', sizeof(sweepboard));//扫描棋盘初始化均为‘*’
	memset(mineboard, '0', sizeof(mineboard));//埋雷棋盘初始化均为‘0’
	setmine(mineboard, &x0, &y0);//调用setmine函数,系统随机埋雷,整型变量x0和y0用于传回一个非雷的坐标。
	while (1) {
		system("cls");
		show(sweepboard); //show函数,展示棋盘。
		printf("请输入坐标:");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x < 1 || x > 10 || y < 1 || y > 10) {
			printf("输入坐标有误,请重新输入!\n");
			Sleep(1500);
			continue;
		}
		if (sweepboard[x][y] != '*') {
			printf("该位置已被扫过,请重新输入!\n");
			Sleep(1500);
			continue;
		}
		if (mineboard[x][y] == '1') {
			if (flag == 1) {
				mineboard[x][y] = '0';
				mineboard[x0][y0] = '1';
				flag = 0;
			}//用户踩到雷了,如果是第一次,就与x0,y0坐标进行交换。
			else {
				printf("很遗憾,你输了!\n");
				Sleep(1500);
				printf("雷达:(1代表有雷,0代表没雷)\n");
				Sleep(1500);
				show(mineboard);//游戏结束,输出埋雷棋盘,让用户明白输在哪里。
				break;
			}
		}
		open(mineboard, sweepboard, x, y);//若没有踩到雷,调用open函数,判定是否需要展开。
		if (judge(sweepboard) == 1) { //调用judge函数,判定是否扫雷成功。
			printf("恭喜你,扫雷成功!\n");
			break;
		}
		flag = 0;//用户第一次扫完,不在有第一次不可能踩中雷的保护。
	}
}
  • setmine函数
void setmine(char mineboard[][COL],int * x, int * y )//setmine函数,开始埋雷。
{
	int times = MINENUM;//循环执行次数,即雷的个数。
	srand((unsigned long)time(NULL));
	while (times) {
		int x = rand() % (ROW - 2) + 1;
		int y = rand() % (COL - 2) + 1;
		if (mineboard[x][y] != '1') {
			mineboard[x][y] = '1';
			times--;
		}
	}
	while (1) {
		*x = rand() % (ROW - 2) + 1;
		*y = rand() % (COL - 2) + 1;
		if (mineboard[*x][*y] == '0') {
			break;
		}
	}//在埋雷棋盘中找到一个随机非雷坐标。
}
  • show函数
void show(char board[][COL])//show函数,展示棋盘。
{
	int i = 1;
	int j = 0;
	printf("    ");//调整列标位置。
	for (; i < 11; i++) {
		printf("%d   ", i);//输出列标。
	}
	printf("\n");
	for (i = 1; i < 11; i++) {
		printf("   ----------------------------------------\n");//输出每一行之间分割线。
		printf("%2d|", i);//输出行标。
		for (j = 1; j < 11; j++) {
			printf(" %c |", board[i][j]);//输出棋盘元素。
		}
		printf("\n");
	}
	printf("   ----------------------------------------\n");//输出最后一行分割线。
}
  • minecount函数
int minecount(char mineboard[][COL], int x, int y)//minecount函数,统计该坐标周围雷的个数。
{
	int c = mineboard[x - 1][y - 1] + mineboard[x - 1][y] + mineboard[x - 1][y + 1] +\
		mineboard[x][y - 1] + mineboard[x][y + 1] + mineboard[x + 1][y - 1] +\
		mineboard[x + 1][y] + mineboard[x + 1][y + 1] - 8 * '0';//棋盘是字符型的,把该坐标周围8个坐标里的字符加起来
	                                                            //再减去8 * '0',计算时用的是acsii值,所得结果就是雷的个数。
	return c;
}
  • open函数
void open(char mineboard[][COL], char sweepboard[][COL], int x, int y)//open函数,判断是否需要递归式展开。
{
	if (!minecount(mineboard, x, y))//调用minecount函数,计算周围雷的个数,若为0,则需要展开。
	{
		sweepboard[x][y] = ' ';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (sweepboard[i][j] == '*' &&\
					i > 0 && i < 11 && j > 0 && j < 11)//若该位置未被扫过且在棋盘范围内则继续递归调用open函数。
					open(mineboard, sweepboard, i, j);
			}
		}
	}
	else {
		sweepboard[x][y] = minecount(mineboard, x, y) + '0';//若不需要展开,则为该位置赋值周围雷的个数,
		                                                    //棋盘是字符型的,需要加'0'才是对应数字的字符。
	}
}
  • judge函数
int judge(char sweepboard[][COL]) //judge函数,判定用户是否扫雷成功。
{
	int i = 1;
	int j = 1;
	int count = 0;
	for (; i <= ROW - 2; i++) {
		for (j = 1; j <= COL - 2; j++) {
			if (sweepboard[i][j] == '*') {
				count++;
			}//遍历棋盘元素,若该位置未被扫过,则记录下来。
			if (count > MINENUM) {
				return 0;
			}//若未被扫过的个数大于雷的个数,则游戏继续。
		}
	}
	return 1;
}

四、运行结果

  • 开始界面:

在这里插入图片描述

  • 扫雷过程:
    空格部分就是递归展开的部分。在这里插入图片描述
  • 游戏结束:
    在这里插入图片描述
    在这里插入图片描述

五,思考

还是多动手吧,写的过程中好多小错误。如循环时不记得给循环变量赋值。还有就是这个程序还可以优化一下,比如做成闯关模式,或者让用户自己选择棋盘大小和雷的个数。应该会很有意思。

发布了8 篇原创文章 · 获赞 23 · 访问量 1217

猜你喜欢

转载自blog.csdn.net/qq_43647942/article/details/102929843