一、简介

其中包括一个头文件function.h

两个源文件game.c和function.c

二、function.h代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 10

void menu();
void initial_show_board(char show_board[ROW][COL]);
void lay_mine(int data_board[ROWS][COLS]);
void scan_mine(int data_board[ROWS][COLS], char show_board[ROW][COL]);
void printf_data_board(int data_board[ROWS][COLS]);
void printf_show_board(char show_board[ROW][COL]);
int sum_mine(int data_board[ROWS][COLS], int row, int col);
void scan_diffusion(int data_board[ROWS][COLS], char show_board[ROW][COL], int x, int y,  int* win);
void printf_result(int data_board[ROWS][COLS], char show_board[ROW][COL]);

三、function.c代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"function.h"
void menu() {
	printf("********************\n");
	printf("****** 1.play ******\n");
	printf("****** 2.exit ******\n");
	printf("****** 3.rule ******\n");
	printf("********************\n");
	printf("\n请输入1或2或3进行选择:>");
}

void initial_show_board(char show_board[ROW][COL]) {
	for (int row=0;row < ROW;row++) {
		for (int col=0;col < COL;col++) {
			show_board[row][col] = '*';
		}
	}
}

void lay_mine(int data_board[ROWS][COLS]) {
	//埋雷下标范围为1--9即(1--(ROWS-2))
	int mine = 0;
	srand((unsigned)time(NULL));
	while(mine < MINE) {
		int rows= rand() % (ROWS-2) + 1;
		int cols= rand() % (COLS-2) + 1;
		if (data_board[rows][cols] == 0) {    //此处用if判断的目的是防止在同一位置重复埋雷
			data_board[rows][cols] = 1;
			mine++;
		}
		else
			;
	}
}

void scan_mine(int data_board[ROWS][COLS], char show_board[ROW][COL]) {
	int x = 0;
	int y = 0;
	int win = 0;  //计数器,计算已经扫描的区域
	while (win<(ROW*COL-MINE)) {   
		printf_show_board(show_board);
		printf("\n");
		printf("请输入扫雷坐标:>");
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= COL) && (y >= 1 && y <= ROW)) {  //x和y必须合法
			//用户输入x=9,y=1代表
			//在show棋盘中的第1行第9列,对应下标为[y-1][x-1]
			//在data棋盘中的第2行第10列,对应下标为[y][x]
			if (data_board[y][x] == 1) {  
				show_board[y-1][x-1] = 'X';
				printf("game over\n\n");
				printf_result(data_board, show_board);
				Sleep(2000);
				break;
			}
			else {
				if(show_board[y - 1][x - 1] != '*')
				printf("此坐标已扫描\n\n");
				else
					scan_diffusion(data_board, show_board, x, y,&win);	
			}
		}	
		else
			printf("非法输入\n\n");
	}
	if (win == (ROW * COL - MINE)){
		printf("扫雷成功\n\n");
		printf_result(data_board, show_board);
		Sleep(2000);
	}
		
}

void printf_result(int data_board[ROWS][COLS], char show_board[ROW][COL]) {
	for (int row = 1;row < (ROWS-1);row++) {
		for (int col = 1;col < (COLS-1);col++) {
			if (data_board[row][col] == 1)
				show_board[row - 1][col - 1] = 'X';
		}
	}
}

int sum_mine(int data_board[ROWS][COLS], int row, int col) {
	//统计此坐标周围的8个坐标中值的总数
	int sum = data_board[row][col + 1] + data_board[row][col - 1]
		+ data_board[row + 1][col + 1] + data_board[row + 1][col] + data_board[row + 1][col - 1]
		+ data_board[row - 1][col + 1] + data_board[row - 1][col] + data_board[row - 1][col - 1];
	return  sum;
}

void scan_diffusion(int data_board[ROWS][COLS], char show_board[ROW][COL], int x, int y, int* win) {
	if (show_board[y - 1][x - 1] == '*') {
		*win = *win + 1;
		int numb_mine = sum_mine(data_board, y, x); //统计周围8格的雷数
		if (numb_mine != 0) {
			show_board[y - 1][x - 1] = 48 + numb_mine; //show_board是char型,对照ascll码表,数字0ascll码为48
		}
		else {
			show_board[y - 1][x - 1] = ' ';
			//用递归的思想传递扫描
			int min_x = x - 1;
			int max_x = x + 1;
			int min_y = y - 1;
			int max_y = y + 1;
			int temp_x = x;
			int temp_y = y;
			if ((min_x >= 1 && min_x <= COL) && (y >= 1 && y <= ROW)) {
				x = min_x;
				scan_diffusion(data_board, show_board, x, y, win);
			}
			x =temp_x;   //每一个if语句之间要相互独立,上一个if不能影响到下一个if的x和y值
			y = temp_y;

			if ((max_x >= 1 && max_x <= COL) && (y >= 1 && y <= ROW)) {
				x = max_x;
				scan_diffusion(data_board, show_board, x, y, win);
			}
			x = temp_x;
			y = temp_y;

			if ((min_x >= 1 && min_x <= COL) && (min_y >= 1 && min_y <= ROW)) {
				x = min_x;
				y = min_y;
				scan_diffusion(data_board, show_board, x, y, win);
			}
			x = temp_x;
			y = temp_y;

			if ((x >= 1 && x <= COL) && (min_y >= 1 && min_y <= ROW)) {
				y = min_y;
				scan_diffusion(data_board, show_board, x, y, win);
			}
			x = temp_x;
			y = temp_y;

			if ((max_x >= 1 && max_x <= COL) && (min_y >= 1 && min_y <= ROW)) {
				x = max_x;
				y = min_y;
				scan_diffusion(data_board, show_board, x, y, win);
			}
			x = temp_x;
			y = temp_y;

			if ((min_x >= 1 && min_x <= COL) && (max_y >= 1 && max_y <= ROW)) {
				x = min_x;
				y = max_y;
				scan_diffusion(data_board, show_board, x, y, win);
			}
			x = temp_x;
			y = temp_y;

			if ((x >= 1 && x <= COL) && (max_y >= 1 && max_y <= ROW)) {
				y = max_y;
				scan_diffusion(data_board, show_board, x, y, win);
			}
			x = temp_x;
			y = temp_y;

			if ((max_x >= 1 && max_x <= COL) && (max_y >= 1 && max_y <= ROW)) {
				x = max_x;
				y = max_y;
				scan_diffusion(data_board, show_board, x, y, win);
			}
		}
	}
}



void printf_data_board(int data_board[ROWS][COLS]) {
	for (int row=1;row < ROWS-1;row++) {
		for (int col=1;col < COLS-1;col++) {
			printf("%d ", data_board[row][col]);
		}
		printf("\n");
	}
}

void printf_show_board(char show_board[ROW][COL]) {
	int x = 0;  //打印x和y的坐标
	int y = 1;
	for (x;x <= COL;x++)
		printf("%d ", x);
	printf("\n");
	for (int row=0;row < ROW;row++) {
		printf("%d ", y);
		y++;
		for (int col=0;col < COL;col++) {
			printf("%c ", show_board[row][col]);
		}
		printf("\n");
	}
}

四、game.c代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"function.h"
void game() {
	int data_board[ROWS][COLS] = { 0 };  //数据棋盘11x11
	char show_board[ROW][COL] = { 0 };   //展示棋盘9x9
	initial_show_board(show_board);  //初始化棋盘
	lay_mine(data_board);    //布设地雷
	//printf_data_board(data_board); //调试时查看data_board
	//printf("\n");
	scan_mine(data_board, show_board);  //扫雷
	printf_show_board(show_board);
	printf("\n");
	Sleep(5000);
	

}
int main() {
	while (1) {
		int input = 0;
		menu();
		scanf("%d", &input);
		switch (input) {
		case 1:
			printf("\n扫雷\n\n");
			game();
			break;
		case 2:
			printf("\n退出游戏\n");
			break;
		case 3:
			printf("\n输入坐标后,*下显示的数字为周围地雷总数(周围指上下左右和右上右下左上左下,共八个位置)\n\n");
			break;
		default:
			printf("\n非法输入\n\n");
			break;
		}
		if (input == 2)
			break;
	}
	return 0;
}


五、写程序时犯的错误

1.在二维数组传参时报错:访问权限冲突。

收集资料后发现这种报错通常是由数组访问越界引起的,但检查逻辑之后并没有发现此类型错误,最后发现是二维数组传参时格式错误。

定义函数时应为function(arr[a][b]),但使用函数时直接function(arr),不要加下标!

2.传址调用在递归中的转变

在scan_diffusion函数里我们想控制函数之外的变量,所以用了传址调用,把int型win(定义的变量,用于计数)转为了指针,但在递归时scan_diffusion函数反复调用它本身,此时win已经是指针,不用再&win,应直接用win。