C语言伪图形与键盘操作加扫雷实例


大家好,这一篇博客讲的是我对C语言伪图形与键盘操作的理解。如果有错误,还请读者大大们指出。

    准备test.c源文件,game.c源文件,game.h头文件。

界面选择与坐标绑定

在game.h头文件中:

#pragma once

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <conio.h> // getch()函数需要的头文件
#include <Windows.h> // gotoxy()函数与HideCursor()函数需要的头文件

void gotoxy(int a, int b);

void game();

在test.c源文件中:

#include "game.h"

void gotoxy(int a, int b) // 固定画面位置
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X = a;
	pos.Y = b;
	SetConsoleCursorPosition(handle, pos);
}

void HideCursor() // 隐藏光标
{
	CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

void menuPrint(int y)
{
	char arr[2][13] = { "开始游戏", "退出游戏" };

	printf("********************\n");
	// 坐标与数组连接
	if (y == 0) // 传首元素地址
		printf("****%12s****\n", &arr[0][0]); // %12s占12格右对齐
	else
		printf("****%-12s****\n", &arr[0][0]); // %-12s占12格左对齐

	if (y == 1)
		printf("****%12s****\n", &arr[1][0]);
	else
		printf("****%-12s****\n", &arr[1][0]);

	printf("********************\n");
}

void menuOperate(char ch, int* y)
{
	switch (ch)
	{
	case 0x48: // 0x48对应向上方向键
		*y = (*y - 1 + 2) % 2; // 加2后%2,可防止成为负数这两个范围
		break;
	case 0x50: // 0x50对应向下方向键
		*y = (*y + 1) % 2; // %2防止超出数组最大下标 
		break;
	case 'a':
		//game();
		break;
	}
}

void menu()
{
	int y = 0; // 只支持纵向移动
	char ch = 0; // 接收字符
	while (1)
	{
		gotoxy(0, 0);
		menuPrint(y); // 1.菜单打印
		ch = _getch(); // 2.输入字符(getch函数不需要输入回车即可执行)
		menuOperate(ch, &y); // 3.菜单操作
	}
}

int main()
{
	HideCursor(); // 隐藏光标
	menu(); // 菜单选择
	return 0;
}

程序展示:

QQ录屏20231221184108

加入 \033特殊转义字符方式改变颜色

void menuPrint(int y)
{
	char arr[2][13] = { "开始游戏", "退出游戏" };

	printf("********************\n");
	// 坐标与数组连接
	if (y == 0) // 传首元素地址
		printf("****\033[41m%12s\033[0m****\n", &arr[0][0]); // 红色
	else
		printf("****%-12s****\n", &arr[0][0]); // %-12s占12格左对齐

	if (y == 1)
		printf("****\033[41m%12s\033[0m****\n", &arr[1][0]); // 红色
	else
		printf("****%-12s****\n", &arr[1][0]);

	printf("********************\n");
}

程序展示:

QQ111

进入游戏与操作执行

在game.h头文件中:

#pragma once

#define HIGH 5 // 设置游戏画面高度
#define WIGHT 5 // 设置游戏画面宽度

#include <stdio.h>
#include <conio.h> // getch()函数需要的头文件
#include <Windows.h> // gotoxy()函数与HideCursor()函数需要的头文件

void gotoxy(int a, int b);

void game();

在test.c源文件中:

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

void gotoxy(int a, int b) // 固定画面位置
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X = a;
	pos.Y = b;
	SetConsoleCursorPosition(handle, pos);
}

void HideCursor() // 隐藏光标
{
	CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

void menuPrint(int y)
{
	char arr[2][13] = { "开始游戏", "退出游戏" };

	printf("********************\n");
	// 坐标与数组连接
	if (y == 0) // 传首元素地址
		printf("****\033[41m%12s\033[0m****\n", &arr[0][0]); // %12s占12格右对齐
	else
		printf("****%-12s****\n", &arr[0][0]); // %-12s占12格左对齐

	if (y == 1)
		printf("****\033[41m%12s\033[0m****\n", &arr[1][0]);
	else
		printf("****%-12s****\n", &arr[1][0]);

	printf("********************\n");
}

void menuOperate(char ch, int* y)
{
	switch (ch)
	{
	case 0x48:	// 0x48对应向上方向键
		*y = (*y - 1 + 2) % 2; // 加2后%2,可防止成为负数这两个范围
		break;
	case 0x50:	// 0x50对应向下方向键
		*y = (*y + 1) % 2; // %2防止超出数组最大下标 
		break;
	case 'a':
		system("cls"); // 清屏一次
		game();
		break;
	}
}

void menu()
{
	int y = 0; // 只支持纵向移动
	char ch = 0; // 接收字符
	while (1)
	{
		gotoxy(0, 0);
		menuPrint(y); // 1.菜单打印
		ch = _getch(); // 2.输入字符(getch函数不需要输入回车即可执行)
		menuOperate(ch, &y); // 3.菜单操作
	}
}

int main()
{
	HideCursor(); // 隐藏光标
	menu(); // 菜单选择
	return 0;
}

在game.c源文件中:

#include "game.h"

void init(char arr[HIGH][WIGHT])
{
	for (int i = 0; i < HIGH; i++)
		for (int j = 0; j < WIGHT; j++)
			arr[i][j] = '*';
}

void game()
{
	char arr[HIGH][WIGHT] = { 0 };
	init(arr);
	int y = 0;
	int x = 0;
	char ch = 0;
	while (1)
	{
		gotoxy(0, 0);
		// 1.游戏打印
		for (int i = 0; i < HIGH; i++)
		{
			for (int j = 0; j < WIGHT; j++)
			{
				if (i == y && j == x) // 若对应坐标y, x,则打印背景色
				{
					printf("\033[42m%c\033[0m ", arr[i][j]); // 背景色绿色
				}
				else // 其它正常打印
				{
					printf("%c ", arr[i][j]);
				}
			}
			printf("\n");
		}
		// 2.输入字符
		ch = _getch();
		// 3.游戏操作
		switch (ch)
		{
		case 0x48:// 0x48对应向上方向键
			y = (y - 1 + HIGH) % HIGH; // 成为负数时转到HIGH - 1坐标
			break;
		case 0x50:// 0x50对应向下方向键
			y = (y + 1) % HIGH; // 超过HIGH - 1时转到0坐标
			break;
		case 0x4b:// 0x4b对应向左方向键
			x = (x - 1 + WIGHT) % WIGHT; // 成为负数时转到WIGHT - 1坐标
			break;
		case 0x4d:// 0x4d对应向右方向键
			x = (x + 1) % WIGHT; // 超过WIGHT - 1时转到0坐标
			break;
		case 'a': // 输入小写字母a将'*'转为'#'
			if (arr[y][x] == '*')
				arr[y][x] = '#';
				break;
		case 's': // 输入小写字母s将'#'转为'*'
			if (arr[y][x] == '#')
				arr[y][x] = '*';
			break;
		}
	}
}

程序展示:

程序展示3

总结

想要使用伪图形,首先准备gotoxy()函数固定画面,HideCursor()函数隐藏光标(也可以只使用system(“cls”)清屏函数,不过效果不如前两者),随后确定坐标要移动的方向,字符变量与其他要准备的条件,最后在循环中执行1.打印2.输入3.操作即可。

实例:将C语言扫雷实现伪图形与键盘操作

(注:以下的代码和被修改的代码来自本人上一篇博客《C语言扫雷小游戏(VS2022版)》)

开始菜单

void menu1()
{
	char a = 0;
	int y = 0; // 只限纵轴移动
	while (1)
	{
		gotoxy(0, 0);
		// 1.打印
		char arr[2][13] = { "开始游戏", "退出游戏" };
		printf("*************************\n");
		if (y == 0)
			printf("****   \033[41m->%-10s  \033[0m****\n", &arr[0][0]);
		else
			printf("****   %-12s  ****\n", &arr[0][0]);
		if (y == 1)
			printf("****   \033[41m->%-10s  \033[0m****\n", &arr[1][0]);
		else
			printf("****   %-12s  ****\n", &arr[1][0]);
		printf("*************************\n");
		printf("操作说明:>\n");
		printf("上下方向键选择\n");
		printf("小写字母a确定键\n");
		// 2.输入
		a = _getch();
		// 3.操作
		switch (a)
		{
		case 0x48:
			y = (y - 1 + 2) % 2;
			break;
		case 0x50:
			y = (y + 1 + 2) % 2;
			break;
		case 'a':
			if (y == 0)
			{
				system("cls"); // 先清屏
				game();
			}
			else
			{
				system("cls");// 先清屏
				printf("%s\n", "退出游戏");
				exit(0);
			}
			break;
		}
	}
}

选择难度界面

int option(int* rows, int* cols)
{
	char a = 0;
	int y = 0; // 只限纵轴移动
	while (1)
	{
		gotoxy(0, 0); // 固定画面
		// 1.打印画面
		char arr[4][20] = { "1.初级(9×9)", "2.中级(16×16)", "3.高级(16×30)", "4.自定义" };

		printf("*************************\n");
		if (y == 0)
			printf("****   \033[41m%-15s\033[0m****\n", &arr[0][0]);
		else
			printf("****   %-15s****\n", &arr[0][0]);
		if (y == 1)
			printf("****   \033[41m%-15s\033[0m****\n", &arr[1][0]);
		else
			printf("****   %-15s****\n", &arr[1][0]);
		if (y == 2)
			printf("****   \033[41m%-15s\033[0m****\n", &arr[2][0]);
		else
			printf("****   %-15s****\n", &arr[2][0]);
		if (y == 3)
			printf("****   \033[41m%-14s\033[0m****\n", &arr[3][0]);
		else
			printf("****   %-14s****\n", &arr[3][0]);
		printf("*************************\n");
		printf("操作说明:>\n");
		printf("上下方向键选择\n");
		printf("小写字母a确定键\n");
		// 2.输入字符
		a = _getch();
		// 3.移动操作
		switch (a)
		{
		case 0x48:
			y = (y - 1 + 4) % 4;
			break;
		case 0x50:
			y = (y + 1 + 4) % 4;
			break;
		case 'a':
			if (y == 0)
			{
				*rows = 11;
				*cols = 11;
				return 1;
			}
			else if (y == 1)
			{
				*rows = 18;
				*cols = 18;
				return 2;
			}
			else if (y == 2)
			{
				*rows = 18;
				*cols = 32;
				return 3;
			}
			else if (y == 3)
			{
				system("cls");// 先清屏
				myApply(rows, cols);
				return 4;
			}
			break;
		}
	}
	return 1;
}

打印画面

对于findMine函数:

void findMine(char** mine, char** show, int row, int col)
{
	// 注:由于扫雷下标从1开始,在 3.游戏操作 中需要变通一下
	int x = 1; // 横轴移动
	int y = 1; // 纵轴移动
	int time1 = (int)time(NULL); // 获取进入游戏的时间
	int win = 0;
	int first = 1;
	while (win < row * col - getMineCount && win >= 0) // 当win为负数意思为被雷炸死
	{
		char ch = 0;
		// 1.打印

		int FCount = display(show, row, col, y, x, &time1);

		// 2.输入
		
		// kbhit()函数是为防止 程序停在getch()函数接收字符时 导致时间不流逝
		if (_kbhit()) 
			ch = _getch();

		// 3.游戏操作
		switch (ch)
		{
		case 'a':
			if (show[y][x] == '*')                    // 判断输入的坐标是否被占用
			{
				if (first)
				{
					setMine(mine, row, col, y, x);
					first = 0;
				}
				if (mine[y][x] == '1')                // 判断输入的坐标是否是雷
				{
					win = -9;
				}
				else
				{
					int count = mineCount(mine, y, x);
					show[y][x] = count + '0';
					win++;
					spread(mine, show, y, x, &win, row, col);
				}
			}
			break;
		case 's':
			// 扫雷游戏有一个细节:当旗子数与雷数对应就不可插旗了
			if (show[y][x] == '*' && FCount < getMineCount) 
				show[y][x] = 'F';
			break;
		case 'd':
			if (show[y][x] == 'F')
				show[y][x] = '*';
			break;
		case 'f':
			if (show[y][x] >= '1' && show[y][x] <= '7')
				numberSpread(mine, show, y, x, show[y][x] - '0', &win, row, col);
			break;
		case 0x48: // 向下移动
			if (y > 1) // y大于1
				y = y - 1;
			else
				y = row; // y不大于1直接转到row
			break;
		case 0x50: // 向上移动
			if (y < row) // y小与row
				y = y + 1;
			else
				y = 1; // y等于row直接转到1
			break;
		case 0x4b: // 向左移动
			if (x > 1) 
				x = x - 1; 
			else
				x = col; // x不大于1直接转到col
			break;
		case 0x4d: // 向右移动
			if (x < col)
				x = x + 1;
			else
				x = 1; // x等于col直接转到1
			break;
		case 27:
			system("cls");
			return ;
		}
	}
	if (win == row * col - getMineCount)
	{
		printf("**********************\n");
		printf("******  你赢了  ******\n");
		printf("**********************\n");
		printf("按任意键返回:>\n");
		char winTime = _getch();
		system("cls");
	}
	else
	{
		display(mine, row, col, y, x, &time1);
		printf("**********************\n");
		printf("**很遗憾,你被炸死了**\n");
		printf("**********************\n");
		printf("按任意键返回:>\n");
		char falseTime = _getch();
		system("cls");
	}
}

对于display函数:

int display(char** arr, int row, int col, int y, int x, int* time1)
{
	gotoxy(0, 0);
	int count = 0;
//游戏进行时间 为   当前时间    减去 进入游戏的时间  
	int time2 = (int)time(NULL) - *time1;
	int i, j;
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("time:%d", time2); // 打印游戏时间
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++) // 打印扫雷内容
		{
			if (i == y && j == x)
			{
				printf("\033[42m%c\033[0m ", arr[i][j]);
				if (arr[i][j] == 'F')
					count++;
			}
			else
			{
				if (arr[i][j] == '*')
					printf("%c ", arr[i][j]);
				if (arr[i][j] == '0')
					printf("\033[30m%c\033[0m ", arr[i][j]); // 黑色
				if (arr[i][j] == '1')
					printf("\033[36m%c\033[0m ", arr[i][j]); // 浅蓝色
				if (arr[i][j] == '2')
					printf("\033[34m%c\033[0m ", arr[i][j]); // 蓝色
				if (arr[i][j] == '3')
					printf("\033[33m%c\033[0m ", arr[i][j]); // 黄色
				if (arr[i][j] == '4')
					printf("\033[31m%c\033[0m ", arr[i][j]); // 红色
				if (arr[i][j] == '5')
					printf("\033[35m%c\033[0m ", arr[i][j]); // 紫色
				if (arr[i][j] == '6')
					printf("%c ", arr[i][j]);
				if (arr[i][j] == '7')
					printf("%c ", arr[i][j]);
				if (arr[i][j] == 'F')
				{
					printf("\033[32m%c\033[0m ", arr[i][j]); // 绿色
					count++;
				}
			}
		}
		printf("\n"); // 换行
	}
	if (count > getMineCount) // 防止雷数量成为负数
		count = getMineCount;
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("雷:\033[31m%d\033[0m", getMineCount - count); // 表示当前雷的数量
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("\n");

	printf("********************操作说明**********************\n");
	printf("**** Esc.返回 a.查找 s.插旗 d.取旗 f.数字展开 ****\n");
	printf("**************************************************\n");
	return count; // 获得当前F的数量
}

伪图形C语言扫雷源代码

在game.h头文件中:

#pragma once

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <time.h> // time 函数需要的头文件
#include <stdlib.h> // rand、 srand、malloc 函数需要的头文件
#include <conio.h>
#include <windows.h>

void initBoard(char** arr, int rows, int cols, char set);

int display(char** arr, int row, int col, int y, int x, int* time);

void setMine(char** mine, int row, int col, int y, int x);

void findMine(char** mine, char** show, int row, int col);

void spread(char** mine, char** show, int y, int x, int* win, int row, int col);

void numberSpread(char** mine, char** show, int y, int x, int num, int* win, int row, int col);

// 动态扫雷实现
char** apply(int* rows, int* cols);

void release(char** arr, int rows, int cols);

int getMine();

void myApply(int* rows, int* cols);

int myGetMine();

// 伪图形扫雷实现
void game();

void gotoxy(int a, int b);

void HideCursor();

在test.c源文件中:

#include "game.h"

int getMineCount = 0;

void menu1()
{
	char a = 0;
	int y = 0; // 只限纵轴移动
	while (1)
	{
		gotoxy(0, 0);
		// 1.打印
		char arr[2][13] = { "开始游戏", "退出游戏" };
		printf("*************************\n");
		if (y == 0)
			printf("****   \033[41m->%-10s  \033[0m****\n", &arr[0][0]);
		else
			printf("****   %-12s  ****\n", &arr[0][0]);
		if (y == 1)
			printf("****   \033[41m->%-10s  \033[0m****\n", &arr[1][0]);
		else
			printf("****   %-12s  ****\n", &arr[1][0]);
		printf("*************************\n");
		printf("操作说明:>\n");
		printf("上下方向键选择\n");
		printf("小写字母a确定键\n");
		// 2.读取
		a = _getch();
		// 3.选择
		switch (a)
		{
		case 0x48:
			y = (y - 1 + 2) % 2;
			break;
		case 0x50:
			y = (y + 1 + 2) % 2;
			break;
		case 'a':
			if (y == 0)
			{
				system("cls"); // 先清屏
				game();
			}
			else
			{
				system("cls");// 先清屏
				printf("%s\n", "退出游戏");
				exit(0);
			}
			break;
		}
	}
}

int option(int* rows, int* cols)
{
	char a = 0;
	int y = 0; // 只限纵轴移动
	while (1)
	{
		gotoxy(0, 0); // 固定画面
		// 1.打印画面
		char arr[4][20] = { "1.初级(9×9)", "2.中级(16×16)", "3.高级(16×30)", "4.自定义" };

		printf("*************************\n");
		if (y == 0)
			printf("****   \033[41m%-15s\033[0m****\n", &arr[0][0]);
		else
			printf("****   %-15s****\n", &arr[0][0]);
		if (y == 1)
			printf("****   \033[41m%-15s\033[0m****\n", &arr[1][0]);
		else
			printf("****   %-15s****\n", &arr[1][0]);
		if (y == 2)
			printf("****   \033[41m%-15s\033[0m****\n", &arr[2][0]);
		else
			printf("****   %-15s****\n", &arr[2][0]);
		if (y == 3)
			printf("****   \033[41m%-14s\033[0m****\n", &arr[3][0]);
		else
			printf("****   %-14s****\n", &arr[3][0]);
		printf("*************************\n");
		printf("操作说明:>\n");
		printf("上下方向键选择\n");
		printf("小写字母a确定键\n");
		// 2.输入字符
		a = _getch();
		// 3.移动操作
		switch (a)
		{
		case 0x48:
			y = (y - 1 + 4) % 4;
			break;
		case 0x50:
			y = (y + 1 + 4) % 4;
			break;
		case 'a':
			if (y == 0)
			{
				*rows = 11;
				*cols = 11;
				return 1;
			}
			else if (y == 1)
			{
				*rows = 18;
				*cols = 18;
				return 2;
			}
			else if (y == 2)
			{
				*rows = 18;
				*cols = 32;
				return 3;
			}
			else if (y == 3)
			{
				system("cls");// 先清屏
				myApply(rows, cols);
				return 4;
			}
			break;
		}
	}
	return 1;
}

void game()
{
	int rows = 0;
	int cols = 0;
	int num = option(&rows, &cols);
	char** mine = apply(&rows, &cols);
	char** show = apply(&rows, &cols);
	getMineCount = getMine(num);
	int row = rows - 2;
	int col = cols - 2;
	initBoard(mine, rows, cols, '0');
	initBoard(show, rows, cols, '*');
	system("cls");// 先清屏
	findMine(mine, show, row, col);
	release(mine, rows, cols);
	release(show, rows, cols);
}

void test()
{
	HideCursor();
	srand((unsigned int)time(NULL)); // 使rand函数产生伪随机数
	menu1();
}

int main()
{
	test();
	return 0;
}

在game.c源文件中:

#include "game.h"

extern int getMineCount;

void gotoxy(int a, int b)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X = a;
	pos.Y = b;
	SetConsoleCursorPosition(handle, pos);
}

void HideCursor()
{
	CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), & cursor_info);
}

void initBoard(char** arr, int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
		for (int j = 0; j < cols; j++)
			arr[i][j] = set;
}

int display(char** arr, int row, int col, int y, int x, int* time1)
{
	gotoxy(0, 0);
	int count = 0;
//游戏进行时间 为   当前时间    减去 进入游戏的时间  
	int time2 = (int)time(NULL) - *time1;
	int i, j;
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("time:%d", time2);
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++) // 打印扫雷内容
		{
			if (i == y && j == x)
			{
				printf("\033[42m%c\033[0m ", arr[i][j]);
				if (arr[i][j] == 'F')
					count++;
			}
			else
			{
				if (arr[i][j] == '*')
					printf("%c ", arr[i][j]);
				if (arr[i][j] == '0')
					printf("\033[30m%c\033[0m ", arr[i][j]); // 黑色
				if (arr[i][j] == '1')
					printf("\033[36m%c\033[0m ", arr[i][j]); // 浅蓝色
				if (arr[i][j] == '2')
					printf("\033[34m%c\033[0m ", arr[i][j]); // 蓝色
				if (arr[i][j] == '3')
					printf("\033[33m%c\033[0m ", arr[i][j]); // 黄色
				if (arr[i][j] == '4')
					printf("\033[31m%c\033[0m ", arr[i][j]); // 红色
				if (arr[i][j] == '5')
					printf("\033[35m%c\033[0m ", arr[i][j]); // 紫色
				if (arr[i][j] == '6')
					printf("%c ", arr[i][j]);
				if (arr[i][j] == '7')
					printf("%c ", arr[i][j]);
				if (arr[i][j] == 'F')
				{
					printf("\033[32m%c\033[0m ", arr[i][j]); // 绿色
					count++;
				}
			}
		}
		printf("\n"); // 换行
	}
	if (count > getMineCount) // 防止雷数量成为负数
		count = getMineCount;
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("雷:\033[31m%d\033[0m", getMineCount - count); // 表示当前雷的数量
	for (i = 0; i <= col / 2 - 2; i++)
		printf("--");
	printf("\n");

	printf("********************操作说明**********************\n");
	printf("**** Esc.返回 a.查找 s.插旗 d.取旗 f.数字展开 ****\n");
	printf("**************************************************\n");
	return count; // 获得当前F的数量
}

void setMine(char** mine, int row, int col, int y, int x)
{
	int count = 0;
	int i, j;
	// 记录输入行和旁边的两行
	int judgeRow[3] = { 0 };
	for (int k = -1, a = 0; k <= 1; k++, a++)
		judgeRow[a] = k + y;

	// 记录输入列和旁边的两列
	int judgeCol[3] = { 0 };
	for (int k = -1, a = 0; k <= 1; k++, a++)
		judgeCol[a] = k + x;

	while (count < getMineCount) // 布置雷的数量,数量到达时则跳出循环
	{
		i = rand() % row + 1; // 行

		// 当输入行且旁边两行与布置行不同时
		if (i != judgeRow[0] && i != judgeRow[1] && i != judgeRow[2])
			j = rand() % col + 1; // 列

		// 当输入行或旁边两行与布置行相同时
		else
		{
			do
			{
				j = rand() % col + 1;
				// 若输入列或旁边两列与布置列相同则进入循环
			} while (j == judgeCol[0] || j == judgeCol[1] || j == judgeCol[2]);
		}
		if (mine[i][j] == '0') // 若为 '0',则放置雷
		{
			mine[i][j] = '1';
			count++;
		}
	}
}

int mineCount(char** mine, int y, int x)
{
	int count = 0;
	for (int i = -1; i <= 1; i++)
		for (int j = -1; j <= 1; j++)
			if (mine[y + i][x + j] == '1')
				count++;

	return count;
}

void findMine(char** mine, char** show, int row, int col)
{
	// 注:由于扫雷下标从1开始,在 3.游戏操作 中需要变通一下
	int x = 1; // 横轴移动
	int y = 1; // 纵轴移动
	int time1 = (int)time(NULL); // 获取进入游戏的时间
	int win = 0;
	int first = 1;
	while (win < row * col - getMineCount && win >= 0) // 当win为负数意思为被雷炸死
	{
		char ch = 0;
		// 1.打印

		int FCount = display(show, row, col, y, x, &time1);

		// 2.输入
		
		// kbhit()函数是为防止 程序停在getch()函数接收字符时 导致的时间不流逝
		if (_kbhit()) 
			ch = _getch();

		// 3.游戏操作
		switch (ch)
		{
		case 'a':
			if (show[y][x] == '*')                    // 判断输入的坐标是否被占用
			{
				if (first)
				{
					setMine(mine, row, col, y, x);
					first = 0;
				}
				if (mine[y][x] == '1')                // 判断输入的坐标是否是雷
				{
					win = -9;
				}
				else
				{
					int count = mineCount(mine, y, x);
					show[y][x] = count + '0';
					win++;
					spread(mine, show, y, x, &win, row, col);
				}
			}
			break;
		case 's':
			// 扫雷游戏有一个细节:当旗子数与雷数对应就不可插旗了
			if (show[y][x] == '*' && FCount < getMineCount) 
				show[y][x] = 'F';
			break;
		case 'd':
			if (show[y][x] == 'F')
				show[y][x] = '*';
			break;
		case 'f':
			if (show[y][x] >= '1' && show[y][x] <= '7')
				numberSpread(mine, show, y, x, show[y][x] - '0', &win, row, col);
			break;
		case 0x48: // 向下移动
			if (y > 1) // y大于1
				y = y - 1;
			else
				y = row; // y不大于1直接转到row
			break;
		case 0x50: // 向上移动
			if (y < row) // y小与row
				y = y + 1;
			else
				y = 1; // y等于row直接转到1
			break;
		case 0x4b: // 向左移动
			if (x > 1) 
				x = x - 1; 
			else
				x = col; // x不大于1直接转到col
			break;
		case 0x4d: // 向右移动
			if (x < col)
				x = x + 1;
			else
				x = 1; // x等于col直接转到1
			break;
		case 27:
			system("cls");
			return ;
		}
	}
	if (win == row * col - getMineCount)
	{
		printf("**********************\n");
		printf("******  你赢了  ******\n");
		printf("**********************\n");
		printf("按任意键返回:>\n");
		char winTime = _getch();
		system("cls");
	}
	else
	{
		display(mine, row, col, y, x, &time1);
		printf("**********************\n");
		printf("**很遗憾,你被炸死了**\n");
		printf("**********************\n");
		printf("按任意键返回:>\n");
		char falseTime = _getch();
		system("cls");
	}
}

void spread(char** mine, char** show, int y, int x, int* win, int row, int col)
{
	if (show[y][x] == '0')       // 若周围没有雷才进入
	{
		for (int i = -1; i <= 1; i++) // 行
		{
			for (int j = -1; j <= 1; j++) // 列
			{
				if (y + i >= 1 && y + i <= row && x + j >= 1 && x + j <= col) // 防止超出9×9的范围
				{
					if (show[y + i][x + j] == '*')        // 防止反复递归同一个坐标
					{
						int count = mineCount(mine, y + i, x + j);
						show[y + i][x + j] = count + '0'; // 将已经递归过的坐标显示它周围雷的数量,防止反复递归同一个坐标
						(*win)++;     // 增加查找非雷坐标的数量
						spread(mine, show, y + i, x + j, win, row, col); // 进入下一次递归
					}
				}
			}
		}
	}
}

void numberSpread(char** mine, char** show, int y, int x, int num, int* win, int row, int col)
{
	int unknown = 0;     // 未知坐标的数量
	int unknownBlank = 0;// 未知坐标且未插旗的数量
	int FCount = 0;		 // 插旗坐标的数量
	// 记录周围三者的数量
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1; j <= 1; j++)
		{
			if (show[y + i][x + j] == '*' || show[y + i][x + j] == 'F')
				unknown++;
			if (show[y + i][x + j] == 'F')
				FCount++;
			if (show[y + i][x + j] == '*')
				unknownBlank++;
		}
	}

	if (unknown > num && FCount >= num && unknownBlank != 0) // 周围未知的坐标的数量要大于周围雷的数量
	{
		for (int i = -1; i <= 1; i++) // 行
		{
			for (int j = -1; j <= 1; j++) // 列
			{
				// 防止进入边框坐标
				if (y + i >= 1 && y + i <= row && x + j >= 1 && x + j <= col)
				{
					// 数字展开时扫到雷
					if (show[y + i][x + j] == '*' && mine[y + i][x + j] == '1')
					{
						(*win) = -9;
					}
					// 数字展开扫到非雷
					else if (show[y + i][x + j] == '*' && mine[y + i][x + j] == '0')
					{
						int count = mineCount(mine, y + i, x + j);
						show[y + i][x + j] = count + '0';
						(*win)++;
						spread(mine, show, y + i, x + j, win, row, col);
					}
				}
			}
		}
	}
}

char** apply(int* rows, int* cols)
{
	// 申请二级指针(二维数组)行的数量
	char** arr = (char**)malloc(sizeof(char*) * (*rows));
	if (NULL == arr)
	{
		printf("游戏异常,已退出\n");
		exit(-1);
	}

	// 申请每行之中列的数量
	for (int i = 0; i < *rows; i++)
	{
		arr[i] = (char*)malloc(sizeof(char) * (*cols));
		if (NULL == arr[i])
		{
			printf("游戏异常,已退出\n");
			exit(-1);
		}
	}

	// 返回首元素地址
	return arr;
}

void release(char** arr, int rows, int cols)
{
	// 先释放一级指针空间
	for (int i = 0; i < rows; i++)
		free(arr[i]);
	// 后释放二级指针的空间
	free(arr);
}

int getMine(int num)
{
	switch (num)
	{
	case 1:
		return 10;
	case 2:
		return 40;
	case 3:
		return 99;
	case 4:
		return myGetMine();
	}
	return 0;
}

void myApply(int* rows, int* cols)
{
	printf("***********************************************\n");
	printf("****************** 提前声明:******************\n");
	printf("*自定义行和列建议至少全部都大于3,否者后果自负*\n");
	printf("***********************************************\n");
	printf("请输入行数:>");
	scanf("%d", rows);
	getchar();
	if (*rows < 1)
	{
		*rows = 4;
		printf("行数小于1,自动改成4行\n");
	}
	printf("请输入列数:>");
	scanf("%d", cols);
	getchar();
	if (*cols < 1)
	{
		*cols = 4;
		printf("列数小于1,自动改成4列\n");
	}
	(*rows) = (*rows) + 2;
	(*cols) = (*cols) + 2;
}

int myGetMine()
{
	int n = 0;
	printf("***********************************************\n");
	printf("****************** 提前声明:******************\n");
	printf("******自定义雷的数量建议最多:行 × 列 - 9 *****\n");
	printf("*****************否者后果自负******************\n");
	printf("请布置雷的数量:>");
	scanf("%d", &n);
	getchar();
	if (n < 0)
	{
		printf("数量小于1,改为0");
		n = 0;
	}
	return n;
}

程序展示:

petal_20231221_221540

结语

以上便是所有内容,若文章有错误或游戏有Bug,请联系我,我会及时更改。

谢谢观看

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值