首页 > 其他分享 >【C语言】扫雷游戏

【C语言】扫雷游戏

时间:2024-04-08 12:29:23浏览次数:24  
标签:ROWS 游戏 int COLS C语言 char 扫雷 printf row

目录

1 扫雷游戏功能说明

1.1 扫雷游戏介绍

1.2 游戏界面

2 游戏分析与设计

2.1 读入用户指令

2.2 地雷数据生成,处理与储存

2.3 地雷标记及展开

2.4 用户界面

2.5 游戏循环主体

3 代码实现

game.h

game.c

test.c


1 扫雷游戏功能说明

1.1 扫雷游戏介绍

        使用控制台实现经典扫雷游戏:

                输入“m n”排查雷,输入“f m n”标记/取消标记,m为行数,n为列数;

        可以选择游戏难度:

                简单 9*9 棋盘,10个雷

                中等 16*16 棋盘,40个雷

                困难 16*30 棋盘,99个雷

        根据当前格雷数及标记,自动展开周围非雷格;

        显示游玩时间。

1.2 游戏界面

        数字代表周围雷数,*为未排查区域,#为标记。失败时*为地雷。

2 游戏分析与设计

2.1 读入用户指令

        为防止用户非法指令造成程序死循环,可以直接使用fgets函数读取整行,再用sscanf解析信息:

int x,y;
char line[100];
while(fgets(line, sizeof(line), stdin)){
		if (sscanf(line, "%d%d", &x, &y) == 2){
			if (x >= 1 && x <= row && y >= 1 && y <= col){
				break;
			}else{
				printf("坐标非法,重新输入\n");
			}
		}else{
			printf("输入错误,请输入两个整数\n");
		}
	}

2.2 地雷数据生成,处理与储存

        如图,以9*9棋盘为例,用一个 长宽=边界+2 的二维数组储存地雷信息,“0”为无雷,“1”为有雷。通过搜索周围确定当前格显示雷数,存入另一个二维数组。因为多开两行,搜索时无需考虑超出边界的问题。

        我们可以设计两个二维数组分别储存实际雷和显示雷数:mine, show。在游戏开始,为保持神秘,用 * 填充数组。

show数组
mine数组

        对于这两个数组,编写函数初始化:

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;
		}
	}
}

        接下来是利用rand函数实现随机填雷。有关rand函数详见【C语言】猜数字游戏-CSDN博客

void SetMine(char board[ROWS][COLS], int row, int col, int a, int b){//生成随机的坐标,布置雷
	int count = COUNT;
	while (count){
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0' && !(a==x && b==y)){//此处传入开局坐标避免开局暴毙
			board[x][y] = '1';
			count--;
		}
	}
}

        找雷函数:

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

2.3 地雷标记及展开

        如果根据已有信息及标记可以判断某些格子没有地雷,需要帮助玩家把这些格子自动展开。

        首先判断周围的标记数量:

int GetFlagCount(char show[ROWS][COLS], int x, int y){
	int cnt = 0;
	for (int dx = -1; dx <= 1; dx++) {
		for (int dy = -1; dy <= 1; dy++) {
			if ((dx == 0 && dy == 0)) continue; // 跳过自身
			if(show[x+dx][y+dy]=='#') cnt++;
		}
	}
	return cnt+'0';
}

        展开:

int explore(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int a,int b){
	if(mine[a][b]=='1') return 1;//踩雷返回1
	if(show[a][b]=='*'){
		int count = GetMineCount(mine, a, b);//该位置不是雷,就统计这个坐标周围有几个雷
		show[a][b] = count + '0';
		win++;//统计已经展开的格子,用于判断胜利条件
	}
	if(GetFlagCount(show, a, b)-show[a][b]==0){	
		for (int dx = -1; dx <= 1; dx++) {
			for (int dy = -1; dy <= 1; dy++) {
				if ((dx == 0 && dy == 0) || (a+dx<1 || a+dx>row) || (b+dy<1 || b+dy>col || show[a+dx][b+dy]!='*')) continue; // 跳过自身、边界及排查过的区域
				if(explore(mine, show, row, col, a+dx, b+dy)) return 1;
			}
		}
	}
	return 0;
}

2.4 用户界面

        扫雷界面展示show数组内容:

void DisplayBoard(char board[ROWS][COLS], int row, int col){
	endTime = clock();//更新时间
	printf("------扫雷游戏------ 用时:%.2lfs\n", ((double) (endTime - startTime)) / CLOCKS_PER_SEC);
	printf(" 0 ");
	for (int i = 1; i <= col; i++){
		if(i<10) printf(" %d", i);
		else if(i==10) printf(" X");
		else if(i==20) printf(" D");
		else printf(" %d", i%10);
	}
	printf("\n\n");
	for (int i = 1; i <= row; i++){
		printf("%2d  ", i);
		for (int j = 1; j <= col; j++){
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

        失败展示mine数组内容:

void DisplayOver(char board[ROWS][COLS], int row, int col){
	printf("------扫雷游戏-----\n");
	printf(" 0 ");
	for (int i = 1; i <= col; i++){
		if(i<10) printf(" %d", i);
		else if(i==10) printf(" X");
		else if(i==20) printf(" D");
		else printf(" %d", i%10);
	}
	printf("\n\n");
	for (int i = 1; i <= row; i++){
		printf("%2d  ", i);
		for (int j = 1; j <= col; j++){
			if(board[i][j]=='1') printf("* ");
			else printf("  ");
		}
		printf("\n");
	}
	printf("\n");
}

2.5 游戏循环主体

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int a,int b){
	win = 0;//记录已展开格数
	int x = a;//传入第一次搜索坐标
	int y = b;
	char f;
	int flag = 1;
	char line[100];
	if(flag) {flag=0;goto here;}
	while(win <row*col- COUNT && fgets(line, sizeof(line), stdin)){
		if (sscanf(line, "%d%d", &x, &y) == 2){
			if (x >= 1 && x <= row && y >= 1 && y <= col){
here:			if (explore(mine, show, row, col, x, y)){
					printf("很遗憾,你被炸死了\n");
					DisplayOver(mine, row, col);
					break;
				}else{
					if(win <row*col- COUNT) DisplayBoard(show, row, col);
				}
			}else{
				printf("坐标非法,重新输入\n");
			}
		}else if(sscanf(line, "%c%d%d", &f, &x, &y) == 3){
			if (x >= 1 && x <= row && y >= 1 && y <= col && f=='f'){
				if (show[x][y] == '*'){
					show[x][y] = '#';
					DisplayBoard(show, row, col);
				}else if(show[x][y] == '#'){
					show[x][y] = '*';
					DisplayBoard(show, row, col);
				}else{
					printf("非法指令,重新输入\n");
				}
			}else{
				printf("非法指令,重新输入\n");
			}
		}else{
			printf("输入错误,请输入两个整数\n");
		}
	}
	if (win == row * col - COUNT){
		endTime = clock();
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, row, col);
	}
}

3 代码实现

        代码分为game.h,game.c,test.c三个文件,分别储存声明,函数,主体,在test文件运行。

game.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 16
#define COL 30
#define ROWS ROW+2
#define COLS COL+2
int COUNT,row,col,rows,cols,win;
clock_t startTime, endTime;
//初始化难度
void difficulty();
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col, int a, int b);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int a,int b);
//展开雷
int explore(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int a,int b);
//游戏结束
void DisplayOver(char board[ROWS][COLS], int row, int col);

game.c

#include "game.h"

void difficulty(){
	int diff;
	char l[100];
	printf("*******************\n");
	printf("**** 1. easy ******\n");
	printf("**** 2. medium ****\n");
	printf("**** 3. hard ******\n");
	printf("*******************\n");
	printf("请选择难度:>");
	while(fgets(l, sizeof(l),stdin)){
		if(sscanf(l,"%d",&diff)==1){
			if(diff<=3 && diff>=0){
				switch (diff) {
					case 1:
						COUNT=10;
						row=9;
						col=9;
						break;
					case 2:
						COUNT=40;
						row=16;
						col=16;
						break;
					case 3:
						COUNT=99;
						row=16;
						col=30;
						break;
					default:	
						break;
				}break;
			}else{
				printf("输入错误,请重新输入\n");
			}	
		}else{
			printf("输入错误,请重新输入\n");
		}
	}
	rows=row+2;
	cols=col+2;
}

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 DisplayBoard(char board[ROWS][COLS], int row, int col){
	endTime = clock();
	printf("------扫雷游戏------ 用时:%.2lfs\n", ((double) (endTime - startTime)) / CLOCKS_PER_SEC);
	printf(" 0 ");
	for (int i = 1; i <= col; i++){
		if(i<10) printf(" %d", i);
		else if(i==10) printf(" X");
		else if(i==20) printf(" D");
		else printf(" %d", i%10);
	}
	printf("\n\n");
	for (int i = 1; i <= row; i++){
		printf("%2d  ", i);
		for (int j = 1; j <= col; j++){
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

void SetMine(char board[ROWS][COLS], int row, int col, int a, int b){//生成随机的坐标,布置雷
	int count = COUNT;
	while (count){
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0' && !(a==x && b==y)){
			board[x][y] = '1';
			count--;
		}
	}
}

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

int GetFlagCount(char show[ROWS][COLS], int x, int y){
	int cnt = 0;
	for (int dx = -1; dx <= 1; dx++) {
		for (int dy = -1; dy <= 1; dy++) {
			if ((dx == 0 && dy == 0)) continue; // 跳过自身
			if(show[x+dx][y+dy]=='#') cnt++;
		}
	}
	return cnt+'0';
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int a,int b){
	win = 0;
	int x = a;
	int y = b;
	char f;
	int flag = 1;
	char line[100];
	if(flag) {flag=0;goto here;}
	while(win <row*col- COUNT && fgets(line, sizeof(line), stdin)){
		if (sscanf(line, "%d%d", &x, &y) == 2){
			if (x >= 1 && x <= row && y >= 1 && y <= col){
here:			if (explore(mine, show, row, col, x, y)){
					printf("很遗憾,你被炸死了\n");
					DisplayOver(mine, row, col);
					break;
				}else{
					if(win <row*col- COUNT) DisplayBoard(show, row, col);
				}
			}else{
				printf("坐标非法,重新输入\n");
			}
		}else if(sscanf(line, "%c%d%d", &f, &x, &y) == 3){
			if (x >= 1 && x <= row && y >= 1 && y <= col && f=='f'){
				if (show[x][y] == '*'){
					show[x][y] = '#';
					DisplayBoard(show, row, col);
				}else if(show[x][y] == '#'){
					show[x][y] = '*';
					DisplayBoard(show, row, col);
				}else{
					printf("非法指令,重新输入\n");
				}
			}else{
				printf("非法指令,重新输入\n");
			}
		}else{
			printf("输入错误,请输入两个整数\n");
		}
	}
	if (win == row * col - COUNT){
		endTime = clock();
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, row, col);
	}
}

int explore(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int a,int b){
	if(mine[a][b]=='1') return 1;
	if(show[a][b]=='*'){
		int count = GetMineCount(mine, a, b);//该位置不是雷,就统计这个坐标周围有几个雷
		show[a][b] = count + '0';
		win++;
	}
	if(GetFlagCount(show, a, b)-show[a][b]==0){	
		for (int dx = -1; dx <= 1; dx++) {
			for (int dy = -1; dy <= 1; dy++) {
				if ((dx == 0 && dy == 0) || (a+dx<1 || a+dx>row) || (b+dy<1 || b+dy>col || show[a+dx][b+dy]!='*')) continue; // 跳过自身、边界及排查过的区域
				if(explore(mine, show, row, col, a+dx, b+dy)) return 1;
			}
		}
	}
	return 0;
}

void DisplayOver(char board[ROWS][COLS], int row, int col){
	printf("------扫雷游戏-----\n");
	printf(" 0 ");
	for (int i = 1; i <= col; i++){
		if(i<10) printf(" %d", i);
		else if(i==10) printf(" X");
		else if(i==20) printf(" D");
		else printf(" %d", i%10);
	}
	printf("\n\n");
	for (int i = 1; i <= row; i++){
		printf("%2d  ", i);
		for (int j = 1; j <= col; j++){
			if(board[i][j]=='1') printf("* ");
			else printf("  ");
		}
		printf("\n");
	}
	printf("\n");
}

test.c

#include "game.h"
#include "game.c"
void menu(){
	printf("*******************\n");
	printf("***** 1. play *****\n");
	printf("***** 0. exit *****\n");
	printf("*******************\n");
}
void game(){
	char mine[ROWS][COLS];//存放布置好的雷
	char show[ROWS][COLS];//存放排查出的雷的信息
	difficulty();//设置难度          //初始化棋盘
	InitBoard(mine, rows, cols, '0');//1. mine数组最开始是全'0'
	InitBoard(show, rows, cols, '*');//2. show数组最开始是全'*'
	startTime = clock();
	DisplayBoard(show, row, col);//打印棋盘
	int x,y;//防止开局暴毙
	char line[100];
	printf("请输入要排查的坐标:>");
	while(fgets(line, sizeof(line), stdin)){
		if (sscanf(line, "%d%d", &x, &y) == 2){
			if (x >= 1 && x <= row && y >= 1 && y <= col){
				break;
			}else{
				printf("坐标非法,重新输入\n");
			}
		}else{
			printf("输入错误,请输入两个整数\n");
		}
	}
	SetMine(mine, row, col, x, y);//1. 布置雷 
	FindMine(mine, show, row, col, x, y);//2. 排查雷
}
int main(){
	int input = 0;
	srand((unsigned int)time(NULL));
	do{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		getchar();
		switch (input){
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

标签:ROWS,游戏,int,COLS,C语言,char,扫雷,printf,row
From: https://blog.csdn.net/Morihiro/article/details/137001807

相关文章

  • C语言程序设计-实验报告4
    实验项目名称:选择结构程序设计实验项目类型:验证性实验日期:2024年4月1日一、实验目的1.在熟练掌握if语句和switch语句的基础上,能灵活使用if语句和switch语句进行选择结构的程序设计2.学习调试程序二、实验硬、软件环境Windows计算机、Devc6.0三、实验内容及步骤实验......
  • Java 方法真人游戏网站app
    在前面几个章节中我们经常使用到 System.out.println(),那么它是什么呢?println()是一个方法。System是系统类。out是标准输出对象。这句话的用法是调用系统类System中的标准输出对象out中的方法println()。那么什么是方法呢?Java方法是语句的集合,它们在......
  • 游戏开发:设计软件的充值流程
    渠道后台(ChannelServer)渠道SDK(SoftwareDevelopmentKit)软件中央数据后台(CenterServer)软件服务端(Server)软件客户端(Client)创单流程(CreateOrder)Client向Server发起创单请求,Server转发请求到CenterServer,CenterServer生成本次充值订单号(研发订单号:developorder)并返回;Cli......
  • 力扣经典150题第九题:跳跃游戏
    目录1.简介2.问题描述3.解题思路方法一:贪心算法4.算法实现方法一:贪心算法5.示例与测试6.总结与展望7.结语1.简介本篇博客将讨论力扣经典150题中的跳跃游戏问题。给定一个非负整数数组nums,数组中的每个元素代表在该位置可以跳跃的最大长度,判断是否能够从......
  • C语言面试题目
    1.不能做switch()的参数类型是:switch的参数不能为实型。2.static有什么用途?(请至少说明两种)1)限制变量的作用域 2)设置变量的存储域static修饰局部变量时,会改变局部变量的存储位置,从而使得局部变量的生命周期变长。static修饰全局变量时,会......
  • 基于C语言用递归思想实现斐波那契数列的函数设计
    用C语言并利用递归思想实现设计一个程序,完成斐波那契数列的函数设计,利用递归实现!/********************************************************************* filename: * author :[email protected]* date :2024/04/07* function:利用递归思想实现设计......
  • 用C语言实现,找出一个整数数组中,第二大的数
    用C语言实现,找出一个整数数组中,第二大的数/********************************************************************* filename: * author :[email protected]* date :2024/04/07* function:找出一个整数数组中,第二大的数* note :None** Copy......
  • c语言的数组以及简单函数
    数组与函数的基本定义数组正常数组数组的四要素数组名:本质上就是一个标识符常量元素:同一个数组中的元素数据类型要相同下标:数组的索引可以通过下标查找数组里的某一个值从0开始数组的长:表示元素的数量数组的定义先指定元素类型与个数,后在进行赋值(初始化)intarr[7];......
  • 蓝桥杯练习系统(算法训练)ALGO-963 转圈游戏
    资源限制内存限制:128.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s问题描述n个小伙伴(编号从0到n-1)围坐一圈玩游戏。按照顺时针方向给n个位置编号,从0到n-1。最初,第0号小伙伴在第0号位置,第1号小伙伴在第1号位置,……,依此类推。游戏规......
  • 【UnityRPG游戏制作】Unity_RPG项目之界面面板分离和搭建
    ......