Bootstrap

扫雷进阶版


前言

提示:这里可以添加本文要记录的大概内容:

今天来和大家说说扫雷如何写。


提示:以下是本篇文章正文内容,下面案例可供参考

一、整体思路

和上文五子棋呢一样,我们依然用三个文件,game.c game.h 源.c这样分模块实现更直观,更清晰。

二、解决思路

1.主函数的设计

代码如下(示例):
主函数我们用do while 和switch来实现,当然也可以用while (, , ,)加逗号表达式来实现。

#include "game.h"
void menu()
{
	printf("*******************\n");
	printf("***0.exit 1.play***\n");
	printf("*******************\n");
}
void game()
{
	char mine[Rows][Cols] = { 0 };//布置雷
	char show[Rows][Cols] = { 0 };//显示雷
	InitBoard(mine, Rows, Cols, '0');//初始化mine棋盘
	InitBoard(show, Rows, Cols, '*');//初始化show棋盘
	setmine(mine, Row, Col);//布置雷区
	print_Board(show, Row, Col);
	print_Board(mine, Row, Col);
	find_mine(mine, show, Row, Col);
}
int main()
{
	int input = 0;
	srand((unsigned)time(NULL));
	do
	{
		menu();
		printf("请输入 :");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("游戏结束\n");
			break;
		case 1:
			game();
			break;
		default:
			printf("请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

2.头文件的设计

头文件我们全部放在game.h这个文件夹里
代码如下(示例):

#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <stdlib.h>
#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2
#define Easy_count 5
void InitBoard(char Board[Rows][Cols], int rows, int cols, char set);
void print_Board(char Board[Rows][Cols], int row, int col);
void setmine(char Board[Rows][Cols], int row, int col);
void find_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col);
int mine_count(char mine[Rows][Cols], int x, int y);
void open(char mine[Rows][Cols], char show[Rows][Cols], int x, int y);
void mark(char show[Rows][Cols], int row, int col);

3.棋盘的初始化

遍历整个棋盘并且放入一个字符来接收我们想要初始化的字符,增加了函数的可复用性。

void InitBoard(char Board[Rows][Cols], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < Rows; i++)
	{
		int j = 0;
		for (j = 0; j < Cols; j++)
		{
			Board[i][j] = set;
		}
	}
}

4.棋盘的打印

打印行和打印列,这样可以看起来更直观。

void print_Board(char Board[Rows][Cols], int row, int col)
{
	int i = 0;
	printf("-------------------\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);//打印行
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);//打印列
		for (j = 1; j <= col; j++)
		{
			printf("%c ", Board[i][j]);
		}
		printf("\n");
	}
	printf("-------------------\n");
}

4.布置雷区

用rand函数随机布置雷区,调用rand需要用srand生成随机数起点,并且srand只能生成一次,因为srand里的参数是时间戳,如果连续生成新的起点,随机数会很接近。

void setmine(char Board[Rows][Cols], int row, int col)
{
	int count = Easy_count;
	while (count)
	{
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (Board[x][y] == '0')
		{
			Board[x][y] = '1';
			count--;
		}
	}
}

5.获取选取坐标周围的雷的数量

这里我们通过循环来实现选取坐标周围8个数的和。

int mine_count(char mine[Rows][Cols], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		int j = 0;
		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
				count++;
		}
	}
	return count;
}

6.判断输赢

判断输赢还是独立出来好,这样增加其他拓展时更独立,不需要更改。

int Is_win(char show[Rows][Cols], int row, int col)
{
	int i = 0;
	int count = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				count++;
		}
	}
	return count;
}

7.扫雷

到了最关键的扫雷时刻,首先判断坐标的合法性,最好这里再判断一下是不是输入了重复坐标,这里没有写。每次打印棋盘前清屏,这样更美观。

void find_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
{
	int x = 0;
	int y = 0;
	mark(show, row, col);
	while (Is_win(show, row, col)>Easy_count)
	{
		printf("请输入坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '0')
				{
					int count = mine_count(mine, x, y);
					if (count == 0)
					{
						system("cls");
						open(mine, show, x, y);
						print_Board(show, row, col);
					}
					else
					{
						system("cls");
						show[x][y] = count + '0';
						print_Board(show, row, col);
					}
				}
				else
				{
					system("cls");
					print_Board(show, row, col);
					printf("很遗憾,你被炸死了\n");
					break;
				}
			}
			else
			{
				printf("坐标已被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标不合法,请重新输入\n");
		}
	}
	if (Is_win(show, row, col)==Easy_count)
	{
		printf("恭喜你,获胜了\n");
	}
}

8.拓展扫雷

拓展扫雷有三个关键点
1.拓展时选取坐标没有雷
2.选取坐标周围如果有雷则不继续拓展,显示周围8个坐标的 雷的数量
3.防止死递归。
在这里插入图片描述

void open(char mine[Rows][Cols], char show[Rows][Cols], int x, int y)
{
	int i = 0;
	int count = mine_count(mine, x, y);
	if (count == 0)
	{
		show[x][y] = ' ';
		for (i = -1; i <= 1; i++)
		{
			int j = 0;
			for (j = -1; j <= 1; j++)
			{
				if (show[x + i][y + j] == '*')
					open(mine, show, x + i, y + j);
			}
		}
	}
	else
		show[x][y] = count + '0';
}

9.标记扫雷

标记功能还是很容易实现的,这里随便用一个字符来实现标记,取消标记用初始化字符改回来就好。

void mark(char show[Rows][Cols], int row, int col)
{
	int x = 0; int y = 0; int input = 0;
	do
	{
		printf("0.开始扫雷\n");
		printf("1.开始标记\n");
		printf("2.取消标记\n");
		scanf("%d", &input);
		if (input > 0)
		{
			printf("请输入标记坐标:");
			scanf("%d %d", &x, &y);
		}
		switch (input)
		{
		case 0:
			break;
		case 1:
			show[x][y] = '#';
			system("cls");
			print_Board(show, row, col);
			break;
		case 2:
			show[x][y] = '*';
			system("cls");
			print_Board(show, row, col);
			break;
		default:
			printf("请重新输入:");
			break;
		}
	} while (input);
}

三 效果展示

在这里插入图片描述

;