首页 > 其他分享 >C语言入门:扫雷小游戏的实现

C语言入门:扫雷小游戏的实现

时间:2024-11-01 23:18:33浏览次数:5  
标签:ROWS 入门 int COLS C语言 char game 小游戏 mine

       

目录

一.基本介绍

二.创建棋盘

三.布置随机雷

四.判断并反馈

五.总结

        


一.基本介绍

        扫雷,想必大家并不陌生。

b5a8a9ee2dd346edab90f21ccc9f3140.png

         这样一个9*9棋盘里,藏着十个雷,我们随便点开一个方格:

40858f7abdff4587884c8edbd36a8dc5.png

        这样子,就出现了一些数字,而数字代表这个方格相邻的八个方格里的雷的个数。

47da0e549a834e53847c15b5f9ade656.png

         根据这个规律,我们就能轻松获得游戏胜利!

        学习完C语言基础知识后,我们就可以用C语言设计一个简易的扫雷小游戏啦!

        怎么设计呢?我们可以列出大概步骤:

        第一,菜单“开始游戏or结束游戏”。

        这点很简单,不必多说。

        第二,创建9*9棋盘

        我们可以用二维数组创建,也不难。

        第三,布置随机雷

        有了上期“C语言简单程序:猜数字游戏”的知识后,生成随机雷也必然不在话下。

        第四,判断玩家排查点周围雷的个数并反馈。

        玩家排查点周围的八个位置都要统计,这一点需要注意

        经过简单的分析,其实扫雷游戏就只要这几个步骤,那我们一步一步来就好了。

        当然,写出来的代码也比较多,所以我们可以分成game.hgame.ctest.c。游戏需要的函数声明、类型说明放在game.h,游戏中函数的实现放在game.c,游戏的测试逻辑放在test.c。这样子更加利于管理和修改。

       二.创建棋盘

        菜单部分对游戏主体并无影响,所以先往后看:创建9*9棋盘

        既要用来展示给玩家,又要记录雷的位置,因此我们需要两份二维数组创建两份棋盘。

        同时,因为玩家排查点周围的八个位置都要统计,那就有一个问题了:

954cf4e62c2d44f0b983a1ffc4984230.png

        如果是边缘或者角落处,想要统计“周围八个位置”,就会有越界访问的风险,那就必须要判断,边缘和拐角都不少,判断起来非常麻烦。因此,我们可以直接创建11*11的棋盘,在边缘和拐角全部设为非雷,再只打印中间9*9棋盘即可,这样就会方便很多。

        两份棋盘创建好后就要初始化,一份全部为“ * ”展示给玩家当作隐藏方格(show);一份全部为“0”作为放置雷的地方(mine)。

        代码如下:

//game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//game.c test.c里使用库函数需要的头文件可以在game.h里声明

#define ROW 9
#define COL 9//定义行和列

#define ROWS ROW + 2
#define COLS COL + 2//使程序能创建11*11的棋盘

void InitBoard(char Board[ROWS][COLS], int row, int col,char set);//初始化棋盘

void PrintBoard(char Board[ROWS][COLS], int row, int col);//打印棋盘
//game.c

#include "game.h"
//包含自定义的头文件用“”

void InitBoard(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 PrintBoard(char Board[ROWS][COLS], int row, int col)//打印棋盘
{
	for (int j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	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");
	}
}
//test.c

#include "game.h"

char mine[ROWS][COLS];
char show[ROWS][COLS];

InitBoard(mine, ROWS, COLS,'0'); 
InitBoard(show, ROWS, COLS,'*');//字符,注意用单引号

PrintBoard(show, ROW, COL);
PrintBoard(mine, ROW, COL);

        这里需要注意,我们在头文件定义了行和列,这是因为后面所有跟棋盘有关的操作都会用到棋盘的行和列,所以在这定义之后,可以直接使用ROW和COL,如果我们想要修改行和列的信息,可以直接修改ROW和COL的值即可。

        同时,玩家每次输入后程序肯定都要再打印一次棋盘,所以需要写入了一个打印棋盘的函数,其中也要包含横坐标和纵坐标,方便玩家查找对应坐标。

        我们可以先打印两份棋盘看看效果:

        b4e8ef86c2764b86be6c7585345148af.png

        这样,创建棋盘就完成啦!

         三.布置随机雷

        随机数我们调用srand()rand()函数即可,这个随机数肯定是作为数组坐标去设置雷,那么只需要生成1~9的随机数就可以了,我们就需要一些运算符限制随机数的范围,并且只要十个雷就完成任务了。综上,我们可以写出以下代码:

//game.h

#EASY_COUNT 10

void SetBoare(char Board[ROWS][COLS], int row, int col);//布置雷

//game.c

void SetBoard(char Board[ROWS][COLS], int row, int col)
{
	int number = EASY_COUNT;
	while (number)
	{
		int i = rand() % 10 + 1;
		int j = rand() % 10 + 1;
		if (i <= row && j <= col)//对坐标进行限制
		{
			if (Board[i][j] != '1') //是字符不是字符串,用‘’单引号
			{
				Board[i][j] = '1';//防止重复赋值,保证满足十个雷
				number--;
			}
		}
		
	}
}

srand((unsigned int)time(NULL)); 

 test.c里的main函数调用一次srand函数,再使用该函数就可以啦,

        我们综合起来运行看看:

6eb6c936a10a45889ba37a3455d727bb.png

        非常成功!

四.判断并反馈

        获取玩家输入的坐标后,我们就可以判断该坐标周围八个坐标的雷个数总和,并赋值给玩家输入的坐标,再打印出来反馈给玩家。

        我们先考虑一种最简单的情况,玩家很不幸运,直接”踩雷“,也就是玩家输入坐标正好是“1”所在位置,直接输入“你被炸死了”就可以跳出循环,准备下一次游戏了。

        另一种情况就是玩家没有“踩雷”,这时候就要开始判断了。

        这里的判断需要我们注意:

        输入坐标x y,那我们需要获取周围八个坐标,只需要(x+1)(x-1)(x)和(y+1)(y-1)(y)之间互相“配对”,就能产生周围的八个坐标,怎么判断有多少雷呢?我们可以将这八个坐标所代表的值相加,是1 就会加1,是0就不会变,就能达到需要的效果了。当然,这里数组存放的是字符类型,运算时会用对应的ACSII码值进行计算,所以需要(- ‘ 0 ’ * 8),再(+ ‘ 0 ’ ),这个时候计算得来的值才能重新储存在mine数组里。

        代码如下:

//game.h

int GetCount(char Board[ROWS][COLS],int x, int y);
//判断玩家输入坐标的周围雷的个数

void StartBoard(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);
//与玩家互动部分
//game.c

int GetCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x][y - 1] + mine[x][y + 1] + mine[x - 1][y]
		+ mine[x - 1][y + 1] + mine[x - 1][y - 1] + mine[x + 1][y]
		+ mine[x + 1][y - 1] + mine[x + 1][y + 1] - 8 * '0');
}

void StartBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	int win = 0;
	while (win < row * col - EASY_COUNT)//判断是否将所有非雷全部排查,防止死循环
	{

		printf("请输入你想排查的坐标:");
		int n = scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入坐标是否合法
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了!!!\n");
				PrintBoard(mine, ROW, COL);//玩家失败游戏后展示mine数组即揭示答案
				break;

			}
			else
			{
				int count = GetCount(mine, x, y);

				show[x][y] = count + '0';//注意字符的计算时对应ASCII码值

				PrintBoard(show, ROW, COL);

			}
		}
		else
		{
			printf("坐标非法输入,请重新输入\n");
		}
		
		if (win == row * col - EASY_COUNT)
		{
			printf("恭喜你,扫雷成功,游戏胜利!!!\n");

			PrintBoard(mine, ROW, COL);
		}	

	}

}

       

五.总结

         至此,游戏整体已经差不多完成了,再加开始游戏的菜单等等,一个简单的扫雷小游戏就诞生啦!

        总代码如下:

//game.h

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

#define EASY_COUNT 10

#define ROW 9
#define COL 9//定义行和列

#define ROWS ROW + 2
#define COLS COL + 2

void InitBoard(char Board[ROWS][COLS], int row, int col,char set);//初始化棋盘

void PrintBoard(char Board[ROWS][COLS], int row, int col);//打印棋盘

void SetBoare(char Board[ROWS][COLS], int row, int col);//布置雷

int GetCount(char Board[ROWS][COLS],int x, int y);//判断玩家输入坐标的周围雷的个数

void StartBoard(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);
//与玩家互动部分
//game.c

#include "game.h"

void InitBoard(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 PrintBoard(char Board[ROWS][COLS], int row, int col)//打印棋盘
{
	for (int j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	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");
	}
}

void SetBoard(char Board[ROWS][COLS], int row, int col)
{
	int number = EASY_COUNT;
	while (number)
	{
		int i = rand() % 10 + 1;
		int j = rand() % 10 + 1;
		if (i <= row && j <= col)
		{
			if (Board[i][j] != '1') //是字符不是字符串
			{
				Board[i][j] = '1';
				number--;
			}
		}
		
	}
}

int GetCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x][y - 1] + mine[x][y + 1] + mine[x - 1][y]
		+ mine[x - 1][y + 1] + mine[x - 1][y - 1] + mine[x + 1][y]
		+ mine[x + 1][y - 1] + mine[x + 1][y + 1] - 8 * '0');
}

void StartBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{

		printf("请输入你想排查的坐标:");

		int n = scanf("%d %d", &x, &y);

		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了!!!\n");

				PrintBoard(mine, ROW, COL);
				break;

			}
			else
			{
				int count = GetCount(mine, x, y);

				show[x][y] = count + '0';

				PrintBoard(show, ROW, COL);
			}
		}
		else
		{
			printf("坐标非法输入,请重新输入\n");
		}
		
		if (win == row * col - EASY_COUNT)
		{
			printf("恭喜你,扫雷成功,游戏胜利!!!\n");

			PrintBoard(mine, ROW, COL);
		}
		

	}

}
//test.c

#include "game.h"

void menu()
{

	printf("*************************\n");
	printf("*******1 . 开始游戏******\n");
	printf("*******0 . 退出游戏******\n");
	printf("*************************\n");
	printf("请选择:");
}

void game()
{
	printf("------游戏开始-----\n");
	char mine[ROWS][COLS];
	char show[ROWS][COLS];

	InitBoard(mine, ROWS, COLS,'0'); 
    //准备两个棋盘,一个给观众用来记录并展示对应点周围的雷的数目

	InitBoard(show, ROWS, COLS,'*');
    //另一个用来保存初始状态的雷的位置

	PrintBoard(show, ROW, COL);

	SetBoard(mine, ROW, COL);
	//PrintBoard(mine, ROW, COL);//注意这个mine数组不能展示给玩家

	StartBoard(mine, show, ROW, COL);
	

}

int main()
{
	printf("扫雷游戏\n");
	printf("你只需要输入坐标即可扫雷\n");
	printf("输入格式:纵坐标 横坐标\n");
	srand((unsigned int)time(NULL));
	int input;
	
	do
	{
		menu();
		int a =scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();//进入游戏主题
			break;
		case 0:
			printf("游戏结束。");
			break;
		default:
			printf("选择错误,请重新你输入:\n");
			break;
		}
	} while (input);
		
	return 0;
}

         当然,上述的扫雷游戏,它不能一次展开周围非雷的一片,不能标识雷,不能记录游戏时间,不能选择难度,这么一看还是非常简陋的,但是学习过相关基础知识后,增加更多功能也不过是多定义几个函数而已,也是很轻松的。

        夯实基础,无限进步。如有疑惑或者更多见解,欢迎讨论!!!

标签:ROWS,入门,int,COLS,C语言,char,game,小游戏,mine
From: https://blog.csdn.net/T_L_the_language/article/details/143378480

相关文章

  • 为什么C语言写的Nginx从来不会挂掉
    C语言编写的Nginx之所以具有极高的稳定性,主要是因为C语言的高性能、精简高效的代码设计、模块化架构、稳定的内存管理以及严格的错误处理机制。特别是在内存管理方面,Nginx使用了高效的内存池技术,这种技术允许程序预分配内存块,并在这些内存块内高效地分配和回收小块内存,从而避免了......
  • 初识Python从入门到精通—— 语法及入门 1( 专为Python零基础)
    本文前4章为学习python的辅助章节,简单了解即可,可根据自身情况进行选择学习。程序:指挥计算机工作的指令序列。1.计算机如何处理程序?按照冯·诺依曼存储程序的原理,计算机的工作流程大致如下:1.用户打开程序,程序开始执行;2.操作系统将程序内容和相关数据送入计算机的内存......
  • 零基础3分钟快速入门MYSQL关系数据库1
    MySQL的语法规范1.不区分大小写,但建议关键字大写,表名、列名小写2.每条命令最好用分号结尾3.每条命令根据需要,可以进行缩进或换行4.注释ctrl+/单行注释:#注释文字  单行注释:--注释文字  多行注释:/*注释文字*/1.安装mysql、安装navicat2.navicat连接mysql(1)打......
  • C语言内幕--全局变量(结合内存分区、汇编视角看类型、连接器)
    前言学习资源:b站up主:底层技术栈学过C语言都知道,全局变量可以再全局中使用,其实全局变量内部还是涉及到不少知识,这里从内存分区、汇编视角看类型、连接器等角度看待全局变量;由于涉及到底层技术,如果有错误,请各位大佬指点......
  • 零基础3分钟快速入门MYSQL关系数据库2【1-6见上篇】
    1-6点此链接→  零基础3分钟快速入门MYSQL【1-6】7、数据操作1、插入数据:insertinto表名(字段名1,字段名2....) values(字段对应的值1,字段对应的值2,.......),(字段对应的值1,字段对应的值2,.......);2、修改数据:update表名set 字段名1=新的数据值,字段......
  • 当下最好的Java做人工智能应用的开源框架-Spring AI alibaba介绍-含免费的菜鸟入门教
    在过去,Java缺乏一个统一且高效的AI应用框架,这导致开发者在使用不同AI提供商(如OpenAI、Azure或阿里云)时需要频繁地调整代码和接口适配。现在有了Spring团队推出的springAI,他能够很好的解决上面的问题,本文会先介绍一下springai这个应用框架的核心能力和功能。 然后再用s......
  • C语言中的数组初始化和使用技巧
    在编程语言C中,数组初始化和有效使用是至关重要的。数组是一种存储固定大小顺序集合的变量,通常用于存储相同类型的元素。数组的初始化涉及为数组元素赋予初始值、辨识数组边界,以及选择合适的数组类型等。例如,初始化一个整型数组可以使用intarr[10]={0};这会创建一个有10个整......
  • 嵌入式课程day05-C语言运算符和选择结构
    4.8其他运算符自增自减:++--三目运算符:?:复合运算符:+= -=*=/=%= &= |=^= <<=>>=逗号运算符:,4.8.1自增自减:++--实现变量的+1或者-1操作++:单目运算符前置:++a后置:a++①如果++运算符作为单独语句使用++在前,++在后没有区别②如果++运算符参与其他......
  • C语言浮点数实现之实例(二)
            上一章《C语言浮点数实现(一)》给大家讲解了浮点数的由来、组成以及由于浮点数导致的世界级重大事故,以提高大家对学习浮点数积极性,了解浮点数的重要性。虽说大多数场景基本上不会在意这些细节,但是难免会遇见少数场景哈!例如:某迪的底盘团队就遇见过,如果没记错的话,......
  • C语言系列之函数
    C语言系列之函数一、函数的概念二、库函数2.1标准库和头文件2.2库函数的使用2.2.1功能2.2.2头文件包含2.2.3实践2.2.4库函数文档的一般格式三、自定义函数3.1函数的语法形式3.2自定义函数的实例四、形参和实参4.1实参4.2形参4.3形参与实参的关系五、return语句六、数......