首页 > 其他分享 >零基础学C 之 用C做扫雷小游戏

零基础学C 之 用C做扫雷小游戏

时间:2024-10-28 22:20:01浏览次数:8  
标签:count show int 基础 mine char 小游戏 扫雷 坐标

这两天学习了如何用C语言做扫雷小游戏,具体过程及实现思路请看下方代码。
(已完成递归排查雷功能,但标记雷和取消标记雷、标记后显示雷的个数 这两个功能还没做,到时候做完再进行更新。)

递归思路: 首先将单个坐标的周围地雷的地雷信息传给一个3*3的数组,我们先称其为方阵, 方阵中心的元素不需要用到,所以是空符号。
扫雷有一个规则,当这个坐标的正上方和正左方坐标的地雷个数信息都不为’0’时,
则本坐标左上方的坐标将不会被玩家得知,如果至少有一方的地雷个数信息是’0’时,
则将左上方的坐标信息告知玩家,右上方、左下方、右下方的坐标亦是如此。
所以我们可以先判断方阵中的元素满不满足上述条件,满足则给玩家提供(左上。。。右下)坐标信息, 然后再以左上。。。右下坐标为起点,进行递归。
其中递归的条件是该坐标周围没有地雷。而起点坐标的正上、下、左、右的坐标不用通过数组判断,只要 满足递归条件就进行递归。

practice.c

#define _CRT_SECURE_NO_WARNINGS

# include"game.h"

void menu()  //菜单
{
	printf("--------------------------\n");
	printf("--------- 1.Play ---------\n");
	printf("--------- 0.Exit ---------\n");
	printf("--------------------------\n");
}

void game()
{
	srand((unsigned int)time(NULL)); //time函数返回类型是time_t,所以要将类型强制转换为srand函数的形参所需要的类型unsigned int
	char board_mine[ROWS][COLS]; //这里要做两个棋盘,一个用于放置地雷,一个用于显示地雷信息
	char board_show[ROWS][COLS]; //通过将两个棋盘的比对来进行扫雷
	//初始化棋盘
	init_board(board_mine, ROWS, COLS, '0'); //在棋盘外围加上一圈以放置排查地雷的时候数组访问越界
	init_board(board_show, ROWS, COLS, '*');
	//打印棋盘
	//display_board(board_mine, ROW, COL);
	display_board(board_show, ROW, COL);
	//布置雷
	set_mine(board_mine, ROW, COL);
	//display_board(board_mine, ROW, COL); //这里用于开金手指进行程序测试
	//排查雷
	find_mine(board_mine, board_show, ROW, COL);
}

void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请从新选择!\n");
			break;
		}

	} while (input);
}


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

/*
拓展:
1.对安全坐标周围的8个坐标做递归操作
条件:1.该坐标不是雷;2.该坐标周围没有雷;3.该坐标没有被排查过
	这个目标完成了,并且进行递归思路升级,详见game.c的
	void recursion_check(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)

2.标记雷和取消标记雷,标记后显示雷的个数
	这个目标还没有完成
*/

game.h

#pragma once

#define ROW 9 //扫雷区域大小
#define COL 9 
#define ROWS ROW+2 //加宽棋盘
#define COLS COL+2
#define EASY_COUNT 10 //设置地雷个数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


//初始化棋盘
void init_board(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void display_board(char board[ROWS][COLS], int row, int col);
//放置地雷
void set_mine(char board[ROWS][COLS], int row, int col);
//排查地雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//递归检查周围情况
void recursion_check(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
//输赢判断
int is_win(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//废弃函数,记录自己行不通的思路
void recursion_check_complement(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

game.c

#define _CRT_SECURE_NO_WARNINGS
# include"game.h"

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

void display_board(char board[ROWS][COLS], int row, int col)
{
	printf("------------------------------\n");
	printf("  ");
	for (int c = 1; c <= col; c++)
	{
		printf("%d ", c);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("------------------------------\n");
}

void set_mine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1; //坐标,范围1-9,使地雷都落在棋盘内
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}

}

//地雷信息个数传递
static int count_mines(char mine[ROWS][COLS],int x,int y) //坐标周围地雷个数检查
{
	char count = 0;
	//printf("%d\n", count);
	for (int i = -1; i < 2; i++)
	{
		for (int j = -1; j < 2; j++)
		{
			if (i == 0 && j == 0)
				continue;
			count += mine[x + i][y + j]-'0';
			//printf("%d\n", count);
		}
	}
	//将坐标(x,y)周围的元素加起来后减去八个字符0,得到的是周围地雷的数量,字符0的ASCII是48
	return count;

}

//将坐标周围地雷个数信息传给数组arr
static void get_info_around(char mine[ROWS][COLS], char arr[3][3], int x, int y)
{
	for (int i = -1; i < 2; i++)
	{
		if (x + i >= 1 && x + i <= ROW) //防止访问数组超出行范围
		{
			for (int j = -1; j < 2; j++)
			{
				if (i == 0 && j == 0)  //避免排查本身坐标
					continue;
				if (y + j >= 1 && y + j <= COL) //防止访问数组超出列范围
				{
					arr[i + 1][j + 1] = count_mines(mine, x + i, y + j) + '0';
				}
			}
		}
	}
}

void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int step = 0;
	int flag = 0;
	while (1)
	{
		flag = is_win(mine, show, row, col);
		printf("flag = %d\n", flag);
		if (flag) //排查够71次后就说明排查完了
		{
			printf("恭喜你,排雷成功!\n");
			break;
		}
		printf("请输入排查坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标已被排查过,请重新选择。\n");
				continue;
			}
			else if (mine[x][y] == '1')
			{
				printf("你踩到雷了,游戏结束。\n");
				display_board(mine, ROW, COL);
				break;
			}
			else
			{
				show[x][y] = count_mines(mine,x,y)+'0'; //此处的0不是数字0,而是字符0,ASCII码为48
				if(show[x][y] == '0')
					recursion_check(mine, show, x, y);
				display_board(show, ROW, COL);
				step++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}

}


/*
递归思路:
	首先将单个坐标的周围地雷的地雷信息传给一个3*3的数组,我们先称其为方阵,
方阵中心的元素不需要用到,所以是空符号。
	扫雷有一个规则,当这个坐标的正上方和正左方坐标的地雷个数信息都不为'0'时,
则本坐标左上方的坐标将不会被玩家得知,如果至少有一方的地雷个数信息是'0'时,
则将左上方的坐标信息告知玩家,右上方、左下方、右下方的坐标亦是如此。
	所以我们可以先判断方阵中的元素满不满足上述条件,满足则给玩家提供(左上。。。右下)坐标信息,
然后再以左上。。。右下坐标为起点,进行递归。
	其中递归的条件是该坐标周围没有地雷。而起点坐标的正上、下、左、右的坐标不用通过数组判断,只要
满足递归条件就进行递归。

*/
void recursion_check(char mine[ROWS][COLS],char show[ROWS][COLS], int x, int y)
{
	char arr[3][3] = { ' ' };
	get_info_around(mine, arr, x, y); //将坐标周围地雷信息传给数组arr
	for (int i = -1; i < 2; i++)
	{
		if (x + i >= 1 && x + i <= ROW) //防止访问数组超出行范围
		{
			for (int j = -1; j < 2; j++)
			{
				if (i == 0 && j == 0)  //避免排查本身坐标
					continue;
				if (y + j >= 1 && y + j <= COL) //防止访问数组超出列范围
				{
					if (mine[x + i][y + j] == '0')
					{
						if (show[x + i][y + j] == '*') //当这个坐标没被排查过时才进行递归检查
						{
							if (i == -1 && j == -1) //坐标左上检查
							{
								if (arr[0][1] == '0' || arr[1][0] == '0')
								{
									//show[x + i][y + j] = count_mines(mine, x + i, y + j) + '0';
									show[x + i][y + j] = arr[0][0]; //将数组左上方的元素传给show棋盘
									if (show[x + i][y + j] == '0') //如果该坐标不是字符0则停止进入递归
										recursion_check(mine, show, x + i, y + j);
								}
							}

							if (i == -1 && j == 1) //坐标右上检查
							{
								if (arr[0][1] == '0' || arr[1][2] == '0')
								{
									//show[x + i][y + j] = count_mines(mine, x + i, y + j) + '0';
									show[x + i][y + j] = arr[0][2];
									if (show[x + i][y + j] == '0')
										recursion_check(mine, show, x + i, y + j);
								}
							}

							if (i == 1 && j == -1) //坐标左下检查
							{
								if (arr[1][0] == '0' || arr[2][1] == '0')
								{
									//show[x + i][y + j] = count_mines(mine, x + i, y + j) + '0';
									show[x + i][y + j] = arr[2][0];
									if (show[x + i][y + j] == '0')
										recursion_check(mine, show, x + i, y + j);
								}
							}

							if (i == 1 && j == 1) //坐标右下检查
							{
								if (arr[1][2] == '0' || arr[2][1] == '0')
								{
									//show[x + i][y + j] = count_mines(mine, x + i, y + j) + '0';
									show[x + i][y + j] = arr[2][2];
									if (show[x + i][y + j] == '0')
										recursion_check(mine, show, x + i, y + j);
								}
							}
							else
							{
								show[x + i][y + j] = count_mines(mine, x + i, y + j) + '0';
								if (show[x + i][y + j] == '0')
									recursion_check(mine, show, x + i, y + j);
							}
						}
					}

				}
			}
		}
	}
}

int is_win(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int count_show = 0;
	int count_mine = 0;
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				count_show++;
		}
	}
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (mine[i][j] == '1')
				count_mine++;
		}
	}
	printf("count_show = %d\n", count_show);
	printf("count_mines = %d\n", count_mine);
	if (count_show == count_mine)
	{
		return 1;
	}
	
	return 0;
}

//废弃函数,记录自己行不通的递归思路(栈溢出,应该是访问数组时越界了)
void recursion_check_complement(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{

	if (x - 1 >= 1 && x - 1 <= ROW && x + 1 <= ROW && x + 1 >= 1 && y - 1 >= 1 && y - 1 <= ROW && y + 1 <= COL && y + 1 >= 1)
	{
		if (show[x - 1][y] == '*') //坐标未排查过才进行排查
			show[x - 1][y] = count_mines(mine, x - 1, y) + '0'; //正上坐标排查
		if (show[x][y - 1] == '*')
			show[x][y - 1] = count_mines(mine, x, y - 1) + '0'; //正左坐标排查
		if (show[x][y + 1] == '*')
			show[x][y + 1] = count_mines(mine, x, y + 1) + '0'; //正右坐标排查
		if (show[x + 1][y] == '*')
			show[x + 1][y] = count_mines(mine, x - 1, y) + '0'; //正下坐标排查
		if (show[x - 1][y] == '0' || show[x][y - 1] == '0') //左上方坐标排查条件
		{
			if (show[x - 1][y - 1] == '*') //坐标未排查过才进行排查
				show[x - 1][y - 1] = count_mines(mine, x - 1, y - 1) + '0';
		}
		if (show[x - 1][y] == '0' || show[x][y + 1] == '0') //右上方坐标排查条件
		{
			if (show[x - 1][y + 1] == '*') //坐标未排查过才进行排查
				show[x - 1][y + 1] = count_mines(mine, x - 1, y + 1) + '0';
		}
		if (show[x + 1][y] == '0' || show[x][y - 1] == '0') //左下方坐标排查条件
		{
			if (show[x + 1][y - 1] == '*') //坐标未排查过才进行排查
				show[x + 1][y - 1] = count_mines(mine, x + 1, y - 1) + '0';
		}
		if (show[x + 1][y] == '0' || show[x][y + 1] == '0') //右下方坐标排查条件
		{
			if (show[x + 1][y - 1] == '*') //坐标未排查过才进行排查
				show[x + 1][y - 1] = count_mines(mine, x + 1, y + 1) + '0';
		}
	
		
		//如果满足条件后,进行递归

		//1. 左上坐标进行递归判断
		if (show[x - 1][y - 1] == '0')
		{
			recursion_check_complement(mine, show, x - 1, y - 1);
		}
		//2. 正上坐标进行递归判断
		if (show[x - 1][y] == '0')
		{
			recursion_check_complement(mine, show, x - 1, y);
		}
		//3. 右上坐标进行递归判断
		if (show[x - 1][y + 1] == '0')
		{
			recursion_check_complement(mine, show, x - 1, y + 1);
		}
		//4. 左正坐标进行递归判断
		if (show[x][y - 1] == '0')
		{
			recursion_check_complement(mine, show, x, y - 1);
		}
		//5. 右正坐标进行递归判断
		if (show[x][y + 1] == '0')
		{
			recursion_check_complement(mine, show, x, y + 1);
		}
		//6. 左下坐标进行递归判断
		if (show[x + 1][y - 1] == '0')
		{
			recursion_check_complement(mine, show, x + 1, y - 1);
		}
		//7. 左下坐标进行递归判断
		if (show[x + 1][y - 1] == '0')
		{
			recursion_check_complement(mine, show, x + 1, y - 1);
		}
		//8. 正下坐标进行递归判断
		if (show[x + 1][y] == '0')
		{
			recursion_check_complement(mine, show, x + 1, y);
		}
		//9. 正下坐标进行递归判断
		if (show[x + 1][y + 1] == '0')
		{
			recursion_check_complement(mine, show, x + 1, y + 1);
		}

	}

}


程序截图:
在这里插入图片描述
在这里插入图片描述
更改下雷的数量,快速看结果
在这里插入图片描述

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

标签:count,show,int,基础,mine,char,小游戏,扫雷,坐标
From: https://blog.csdn.net/weixin_39559852/article/details/143257916

相关文章

  • 鸿蒙基础篇-语句-分支-循环
    “在科技的浪潮中,鸿蒙操作系统宛如一颗璀璨的新星,引领着创新的方向。作为鸿蒙开天组,今天我们将一同踏上鸿蒙基础的探索之旅,为您揭开这一神奇系统的神秘面纱。”各位小伙伴们我们又见面了,我就是鸿蒙开天组,下面让我们进入今天的学习,鸿蒙基础篇-进阶布局语句:语句是程序执行......
  • 物理学基础精解【139】
    文章目录统计物理学无偏估计(UnbiasedEstimation)无偏方差估计无偏方差估计公式的推导贝塞尔修正(Bessel'sCorrection)修正原理修正效果应用领域无偏方差估计公式的推导过程一、基本概念二、推导过程三、结论四、自由度解释样本方差公式是通过以下步骤推导出来1.样本方......
  • 最新Java零基础知识(第二章标识符与关键字)
    2.1章节目标与知识框架2.1.1章节目标了解构成java源程序的标识符和关键字都是什么,掌握标识符的命名规则以及规范。能够识别标识符是否合法2.1.2知识框架2.2标识符概述(了解)标识符(identifier)是指用来标识某个实体的一个符号,在不同的应用环境下有不同的含义。......
  • Linux基础命令:轻松掌握终端操作
    引言在现代IT行业中,Linux因其稳定性和灵活性广受欢迎。作为后端开发、系统管理和数据科学等领域的必备技能,熟练掌握Linux基本命令将使你在职场中更加游刃有余。无论你是刚接触Linux的新手,还是希望提升技能的开发者,了解Linux命令行的基本用法都是至关重要的。今天,我们将探讨一......
  • C语言教学——编程基础与C语言入门
    引言在上一篇中,我们介绍了计算机的基本组成和工作原理。本篇文章将深入探讨编程的基本概念,特别是C语言的特性和基本语法,帮助初学者更好地理解如何编写程序。我们将从编程语言的分类入手,逐步引导读者进入C语言的世界。1.编程的定义编程是指通过编写代码来创建计算机程序的过......
  • JAVA基础:万年历 【习题笔记】
    基础版publicstaticvoidmain(String[]args){System.out.println("请输入年份:");Scannerinput=newScanner(System.in);intyear=input.nextInt();System.out.println("请输入月份:");intmonth=input.nex......
  • JAVA基础:面向对象 (习题笔记)
    面向对象【初】1.猫要求:使用面向对象的思想,编写自定义类描述猫通过构造函数实现对所有属性赋值吃饭的方法通过接收输入参数,描述喜欢吃的食物玩耍的方法实现根据心情的好坏,表现不同的玩耍状态,信息中包含名字,品种,颜色  的属性内容 编写测试方法,通过构造函数实例......
  • 黑客入门教程(非常详细)从零基础入门到精通,看完这一篇就够了
    想要成为黑客,却苦于没有方向,不知道从何学起,下面这篇黑客入门教程可以帮你实现自己的黑客梦想,如果想学,可以继续看下去,文章有点长,希望你可以耐心看到最后1、 Web安全相关概念(2周) ·熟悉基本概念(SQL注入、上传、XSS、 、CSRF、一句话木马等)。 通过关键字(SOL注入、上传、XS......
  • DICOM 基础知识:深入理解DICOM数据结构与标签说明
    目录DICOM图像概念DICOM图像关键特性:DICOM文件结构常见数据元素:数据元素示例详解DICOM-VR数据类型说明DICOM标准支持的数据集结语     DICOM图像概念        DICOM(DigitalImagingandCommunicationsinMedicine)是一种用于存储、传输和处......
  • Python小白学习教程从入门到入坑------第十八课 异常模块与包【上】(语法基础)
    一、异常 在Python中,异常(Exception)是一种用于处理在程序运行时可能发生的错误情况的机制异常允许程序在检测到错误时不是简单地崩溃,而是能够优雅地处理这些错误,可能包括记录错误信息、清理资源、或者向用户提供有用的反馈1.1异常的基本概念1、异常类型:Python内置了许多异......