【08 数组和函数实战: 扫雷游戏】

目录

  1. 玩法说明
  2. 分析和设计
  3. 代码实现
  4. 扩展
  5. 扩展修改

1. 玩法说明

在这里插入图片描述
控制台界面,在地图上提前布置好雷,地图内容用户看不到。开始选择点击位置,如果不是雷,显示周围雷的数量,如果是雷,则被炸死,游戏结束。将所有雷都找出来,或者地图上剩余位置全是雷,胜利,游戏结束

2. 分析和设计

2.1 数据结构分析

布置雷的地图数据和排查出显示的数据都需要存储,首先想到用一个9*9的二维数组存放。雷的位置为1,非雷用0表示
在这里插入图片描述
如果排查位置是(2,5)这个坐标,访问周围8个位置,统计周围雷的个数显示在当前位置

在这里插入图片描述
图里(2,5)坐标本身存的值为0,遍历周围位置共有1个雷,所以该位置显示给用户1表示周围有1个雷

问题:

  1. 有两个需要显示的信息,一个是地图本身是雷和非雷的信息,用0和1表示。另一个是需要给用户看到的信息,也就是是雷或周围雷的总数的信息。这里如果都放在1个数组,会有不明确的问题,且比较混乱,如1无法分辨是周围雷的总数还是是地雷表示的1
  2. 在统计最边上数字的雷的数量的时候,容易越界。如果每一次都判断是否越界,比较麻烦

解决方案:
3. 我们可以用两个数组,一个存放是地雷和非雷的信息,另一个用来给用户显示周围地雷的信息。统一使用char类型二维数组,方便打印字符和传参的统一。第二个地图刚开始全部为“”字符,是初始化的状态
4. 需要存放数据的是9
9的数组,将其扩展为1111的数组,使用里面99的元素,这样最边上的统计也不会越界
5. 尽量使用同一套函数处理,参数统一 在这里插入图片描述在这里插入图片描述

对应的数组:

char minemap[11][11] = {0};//⽤来存放布置好的雷的信息
char flipmap[11][11] = {0};//⽤来存放排查出的雷的个数信息

2.2 文件结构的设计

分三个文件编写,一个是游戏内容模块和主程序

test.c //测试逻辑
game.h //游戏数据类型和函数定义
game.c //游戏函数的实现

2.3 函数说明

  1. 显示菜单

void menu();

  1. 变量定义

#define ROWS 11       //地图行数
#define COLS 11         //地图列数
#define ROW ROWS-2   //使用的行数
#define COL COLS-2     //使用的列数
#define MINE 10         //地雷个数

3.游戏函数

//1.初始化地图
void InitMap(char ary[ROWS][COLS],int rows,int cols);
//2.设置地雷
void SetMine(char mine[ROWS][COLS],int row,int col);
//3.显示地图
void ShowMap(char ary[ROWS][COLS], int row, int col);
//4.找雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);

2.4 界面展示

  1. 开始界面
    在这里插入图片描述
  2. 选择1,进入游戏,分别打印雷的存放地图 (此地图无需展示,用于测试),用户显示的地图。下面输入想翻开的坐标

坐标输入错误,报非法重新输入

在这里插入图片描述

输入翻开2,3坐标,显示周围地雷总数2

在这里插入图片描述
在这里插入图片描述

  1. 点到地雷,提示游戏结束,回到主界面在这里插入图片描述

  2. 排查完所有地雷,提示胜利,回到主界面
    在这里插入图片描述

3. 代码实现

3.1 难点分析

  1. 游戏内需要多处改动的统一定义为宏定义,如地图行数和地雷个数
  2. 统一接口传入二维数组,行和列根据需要的不同更改
  3. 地图为了方便用户输入,同时打印行和列号的标识
  4. 计算周围地雷个数,目标点周围几个坐标的变化,因为最后加的总数是char字符的ascii总和的int类型,所以减去8个字符’0’转化为地雷的数量
  5. 每次点开一个,记录一下次数,如果等于格子数减去地雷数,则雷全部找出,胜利

3.2 代码

1.game.h

#pragma once

#define ROWS 11       //地图行数
#define COLS 11       //地图列数
#define ROW  ROWS-2   //使用的行数
#define COL  COLS-2   //使用的列数
#define MINE 10 //地雷个数

//1.初始化地图
void InitMap(char ary[ROWS][COLS],int rows,int cols);
//2.设置地雷
void SetMine(char mine[ROWS][COLS],int row,int col);
//3.显示地图
void ShowMap(char ary[ROWS][COLS], int row, int col);
//4.找雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);

2.game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void InitMap(char ary[ROWS][COLS], int rows, int cols,char set)
{
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			ary[i][j] = set;
		}
	}
}

void SetMine(char mine[ROWS][COLS],int row, int col)
{
	int count = MINE;
	while (count > 0) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0') {
			mine[x][y] = '1';
			count--;
		}
	}
		
			
}

void ShowMap(char ary[ROWS][COLS], int row, int col)
{
	printf("--------扫雷--------\r\n");
	//先打印列号
	for (int i = 0; i <= row; i++) {
		printf("%d ", i);
	}
	printf("\r\n");
	for (int i = 1; i <= row; i++) {
		//打印行号
		printf("%d ", i);
		for (int j = 1; j <= col; j++) {
			printf("%c ", ary[i][j]);
		}
		printf("\r\n");
	}
}

//计算周围地雷数
static int MnieCnt(char mine[ROWS][COLS],int x, int y) {
	return mine[x - 1][y - 1] + mine[x + 1][y + 1]+
		mine[x][y - 1] + mine[x][y + 1] +
		mine[x + 1][y - 1] + mine[x - 1][y + 1] +
		mine[x - 1][y] + mine[x + 1][y]	
		- 8 * '0';
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
	int win = 0;  //记录排查的雷个数
	while (win < row * col -MINE) {
		printf("输入要点的坐标:");
		int x, y;
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
			if (mine[x][y] == '1') {
				system("cls");
				printf("点到地雷,游戏结束\r\n");
				ShowMap(mine,ROW,COL);
				return;
			}
		}
		else {
			system("cls");
			printf("坐标非法,重新输入\r\n");
			ShowMap(show, ROW, COL);
			continue;
		}
		win++;
		int n = MnieCnt(mine, x, y);
		show[x][y] = n + '0';
		system("cls");
		ShowMap(show, ROW, COL);
	}
	
	printf("排雷成功\r\n");
}

3.main.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include "game.h"

void menu() {
	printf("********************\r\n");
	printf("*****1.开始游戏*****\r\n");
	printf("*****0.退出游戏*****\r\n");
	printf("********************\r\n");
}

void game() {
	char minemap[ROWS][COLS];  //地雷地图
	char flipmap[ROWS][COLS];  //显示的地图
	//1.初始化地图
	InitMap(minemap,ROWS,COLS,'0');
	InitMap(flipmap, ROWS, COLS, '*');
	//2.设置地雷
	SetMine(minemap,ROW,COL);
	//3.显示地图
	ShowMap(minemap, ROW, COL);
	ShowMap(flipmap, ROW, COL);
	//4.找雷
	FindMine(minemap, flipmap,ROW,COL);

}

int main()
{

	srand((unsigned int)time(NULL));
	int sel;   //获取用户选择
	do
	{
		menu();
		scanf("%d", &sel);
		switch (sel) {
		case 1:
			system("cls");
			game();
			break;
		case 0:
			printf("退出游戏\r\n");
			return;
			break;
		default:
			printf("输入错误\r\n");
			break;
		}

	} while (1);
	return 0;
}

4.扩展

  • 选择游戏难度
     *简单9*9棋盘,10个雷
     *中等16*16棋盘,40个雷
     *困难30*30棋盘,99个雷
  • 如果排查位置不是雷,周围也不是雷,可以展开一片
  • 可以标记雷
  • 加上排雷的时间显示

5. 扩展修改

  1. 加入计时显示
  2. 标记地雷
    重写FindMine函数,加入clock_t开始和结束计时变量,新增输入1为标记地雷,地雷显示为@符号
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
	int win = 0;  //记录排查的雷个数
	clock_t start,end;
	int sec = 0;
	start = clock();
	while (win < row * col -MINE) {
		end = clock();
		sec = end - start;
		printf("已用时间:%ds ,0输入坐标 1标记地雷:",sec/ CLOCKS_PER_SEC);
		int n;
		scanf("%d", &n);
		if (n == 0) {
			sec = end - start;
			printf("已用时间:%ds,输入要点的坐标:", sec / CLOCKS_PER_SEC);
			int x, y;
			scanf("%d %d", &x, &y);

			if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
				if (mine[x][y] == '1') {
					system("cls");
					printf("点到地雷,游戏结束\r\n");
					ShowMap(mine, ROW, COL);
					return;
				}
			}
			else {
				system("cls");
				printf("坐标非法,重新输入\r\n");
				ShowMap(show, ROW, COL);
				continue;
			}
			win++;
			int n = MnieCnt(mine, x, y);
			show[x][y] = n + '0';
			system("cls");
			ShowMap(show, ROW, COL);
		}
		else if (n == 1) {
			sec = end - start;
			printf("已用时间:%ds ,输入标记的坐标(显示@,重新输入取消标记):", sec / CLOCKS_PER_SEC);
			int x, y;
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
				if(show[x][y]='@')
					show[x][y] = '*';
				show[x][y] = '@';
				system("cls");
				ShowMap(show, ROW, COL);
			}
		}
		
	}
	
	printf("排雷成功\r\n");
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值