C语言-扫雷

news2024/11/16 19:46:58

文章目录

  • 完整扫雷
    • 1. 说明
    • 2. 思路
    • 3. 各个功能实现
      • 3.1 雷盘初始化与打印
        • 1)雷盘定义
        • 2) 随机布置雷
      • 3.2 玩家排查雷
        • 1) 获取坐标周围雷数
        • 2) 递归展开
        • 3)胜负判断
        • 3) 显示雷位置
    • 4. 游戏试玩
    • 5. 游戏完整代码
      • game.h
      • test.c
      • game.c

完整扫雷

请添加图片描述

1. 说明

扫雷大家应该都知道,翻开一个格子,显示的数字就是周围 8 格所含的雷数。例如,红色框框里的 1 周围 8 格就只有一个雷。

2. 思路

  1. 我们定义两个数组来实现,show 数组存放玩家看到的棋盘,mine 数组存放隐藏的雷盘

  2. 这两个数组搭配使用,就能计算某个位置周围的雷数,并且修改 show 数组来显示这个位置的雷数。

请添加图片描述

  1. 但是如果计算边缘格子周围的雷数时,数组会越界。

  2. 我们只要在周围留一圈就能解决这个问题

    • 如下图所示,假设我们要玩 9x9 的大小,我们的数组大小就定义为 11x11

请添加图片描述

3. 各个功能实现

3.1 雷盘初始化与打印

1)雷盘定义

请添加图片描述

请添加图片描述

  1. 初始化雷盘
  2. 初始化 show,mine 两个数组,show 存放 ‘*’ , mine 存放 ‘0’
void init_board(char board[ROWS][cols], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
打印雷盘
//打印 show 雷盘

void display_board(char board[ROWS][cols], int row, int col)
{
int i = 0;
int j = 0;

    //这里打印上面一行数字
    printf("|");
    for (j = 0; j <= col; j++)
    {
    	printf(" %-2d |", j);
    }
    printf("\n");


    for (i = 1; i <= row; i++)
    {
    	printf("|");
    	for (j = 0; j <= col; j++)
    	{
    		printf("----|");//打印两行之间的分割线
    	}
    	printf("\n");

    	printf("|");
    	printf(" %-2d |", i);//打印左边一列数字

    	for (j = 1; j <= col; j++)
    	{
    		printf(" %2c |",board[i][j]);//打印show数组

    	}
    	printf("\n");
    }

}

效果图:

请添加图片描述

2) 随机布置雷

  1. 随机布置雷,将 mine 中的 ‘0’ 改为 ‘1’

  2. 随机布置雷,在 mine 数组里随机设置 COUNT 个雷

void set_mine(char mine[ROWS][cols], int row, int col)
{
int count = COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;

    	if (mine[x][y] == '0')
    	{
    		mine[x][y] = '1';  //地雷设置为 '1'
    		count--;
    	}
    }
}

3.2 玩家排查雷

1) 获取坐标周围雷数

1… 计算该坐标在 mine 中 周围 '1’的个数
2… 获取一个格子周围的雷数

int get_mine_count(char mine[ROWS][cols], int x, int y)
{
//mine 中存放的是字符'0' 和 '1'
return (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] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 \* '0');
}

2) 递归展开

请添加图片描述

  1. 我们玩扫雷时,翻开一个格子会展开一片,如上图所示,翻开黑格子,展开紫色区域。
  2. 我们可以用递归来实现
  3. 当这个格子周围没雷时,显示空白,然后继续递归它周围的八个格子
  4. 有雷时,显示雷数,停止递归。
void expand(char mine[ROWS][cols], char show[ROWS][cols], int x, int y, int\* win)
{

    if (x >= 1 && x <= ROW && y >= 1 && y <= COL) //限制在棋盘内展开,防止越界
    {
    	int count = get_mine_count(mine, x, y);//获取雷数

    	if (count == 0) //四周没雷,进入递归展开
    	{
    		show[x][y] = ' ';//四周没雷的改为 空格  ' '


    		int i = 0;
    		//向四周共8个位置递归
    		for (i = x - 1; i <= x + 1; i++)
    		{
    			int j = 0;
    			for (j = y - 1; j <= y + 1; j++)
    			{

    				//只对 '*' 进行展开,防止死循环
    				if (show[i][j] == '*')
    				{
    					expand(mine, show, i, j, win);
    				}

    			}
    		}
    	}
    	else   //四周有雷显示雷数
    	{
    		show[x][y] = count + '0';
    	}

    	//记录展开的数量
    	(*win)++;
    }
}

3)胜负判断

  1. 这里定义了一个 win 来表示翻开的格子数,当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功。
  2. 玩家排查雷
void find_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//初始化 翻开的格子数量

    while (win < row * col - COUNT)//当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功
    {
    	printf("请输入要排查的坐标:>");
    	scanf("%d %d", &x, &y);

    	//检查坐标是否越界
    	if (x >= 1 && x <= row && y >= 1 && y <= col)
    	{

    		//检查坐标是否排查过了
    		if (show[x][y] == '*')
    		{

    			if (mine[x][y] == '1')
    			{
    				system("cls");
    				show_mine(mine, show, row, col);
    				printf("-----------很遗憾,你被炸死了-----------\n");
    				break;
    			}
    			else
    			{
    				//展开
    				expand(mine, show, x, y, &win);

    				system("cls");//清屏
    				display_board(show, row, col);
    				printf("--------------还需翻开%d格--------------\n", row * col - COUNT - win);
    			}
    		}
    		else
    		{
    			printf("该坐标已排查,请重新输入\n");
    		}
    	}
    	else
    	{
    		printf("坐标非法,请重新输入\n");
    	}
    }

    if (win == row* col - COUNT)
    {
    	system("cls");
        show_mine(mine, show, row, col);//展示地雷位置
        printf("------------恭喜你,排雷成功-----------\n");
    }

}

3) 显示雷位置

显示地雷位置,排雷成功或被炸死后 向玩家展示地雷位置

void show_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int i = 0;
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= COL; j++)
{
if (mine[i][j] == '1')
{
show[i][j] = '@'; //将地雷改成 '@'
}
}
}
display_board(show, row, col); //打印
}

4. 游戏试玩

  1. 游戏设置 10 行 ,10 列 ,15 个雷
游戏设置
开始菜单
开始排雷
排雷
  1. 排雷成功

请添加图片描述

  1. 排雷失败

请添加图片描述

5. 游戏完整代码

game.h

头文件 常量定义,函数声明

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//玩家看到的大小
#define ROW 10
#define COL 10

//实际数组大小,防止越界
#define ROWS ROW+2
#define COLS COL+2

#define COUNT 10//雷数

//初始化棋盘
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 mine[ROWS][cols], int row, int col);

//获取坐标周围地雷数
int get_mine_count(char mine[ROWS][cols], int x, int y);

//显示地雷位置并打印
void show_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col);

//递归展开
void expand(char mine[ROWS][cols], char show[ROWS][cols], int x, int y, int\* win);

//排查雷
void find_mine(char mine[ROWS][cols],char show[ROWS][cols],int row, int col);

test.c

游戏测试文件

#define \_CRT_SECURE_NO_WARNINGS 1

#include "game.h"

//菜单
void menu()
{
printf("======================\n");
printf("|| 扫雷 ||\n");
printf("|| 1-开始游戏 ||\n");
printf("|| 0-退出游戏 ||\n");
printf("======================\n");
}

//游戏流程
void game()
{
char mine[ROWS][cols] = { 0 };//存放布置的雷(隐藏的)
char show[ROWS][cols] = { 0 };//存放排查的雷(游戏看到的)

    //初始化棋盘
    //mine 全为'0'
    //show 全为'*'
    init_board(mine, ROWS, COLS, '0');
    init_board(show, ROWS, COLS, '*');

    //随机布置雷
    set_mine(mine,ROW,COL);

    //打印棋盘
    //display_board(mine, ROW, COL);
    display_board(show, ROW, COL);
    printf("--------------需要翻开%d格--------------\n", ROW * COL - COUNT);

    //排查雷(游戏开始)
    find_mine(mine, show, ROW, COL);

}

int main()
{
int input = 0;
srand((unsigned int)time(NULL));

    do
    {
    	menu();
    	printf("请选择:>");
    	scanf("%d", &input);
    	switch (input)
    	{
    	case 1:
    		system("cls");
    		game();
    		break;
    	case 0:
    		printf("退出游戏\n");
    		break;
    	default:
    		printf("选择错误,请重新输入\n");
    		break;
    	}
    } while (input);

    return 0;

}

game.c

函数定义

#define \_CRT_SECURE_NO_WARNINGS 1

#include "game.h"

//将两个数组初始化,show 全为'\*' ,mine 全为'0'

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

//打印 show 棋盘

void display_board(char board[ROWS][cols], int row, int col)
{
int i = 0;
int j = 0;

    //这里打印上面一行数字
    printf("|");
    for (j = 0; j <= col; j++)
    {
    	printf(" %-2d |", j);
    }
    printf("\n");


    for (i = 1; i <= row; i++)
    {
    	printf("|");
    	for (j = 0; j <= col; j++)
    	{
    		printf("----|");//打印两行之间的分割线
    	}
    	printf("\n");

    	printf("|");
    	printf(" %-2d |", i);//打印左边一列数字

    	for (j = 1; j <= col; j++)
    	{
    		printf(" %2c |",board[i][j]);//打印show数组

    	}
    	printf("\n");
    }

}

//随机设置雷,在 mine 数组里随机设置 COUNT 个雷

void set_mine(char mine[ROWS][cols], int row, int col)
{
int count = COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;

    	if (mine[x][y] == '0')
    	{
    		mine[x][y] = '1';  //地雷设置为 '1'
    		count--;
    	}
    }

}

//获取一个格子周围的雷数

int get_mine_count(char mine[ROWS][cols], int x, int y)
{
//mine 中存放的是字符'0' 和 '1'
return (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] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 \* '0');
}

//递归展开

void expand(char mine[ROWS][cols], char show[ROWS][cols], int x, int y, int\* win)
{

    if (x >= 1 && x <= ROW && y >= 1 && y <= COL) //限制在棋盘内展开,防止越界
    {
    	int count = get_mine_count(mine, x, y);//获取雷数

    	if (count == 0) //四周没雷,进入递归展开
    	{
    		show[x][y] = ' ';//四周没雷的改为 空格  ' '


    		int i = 0;
    		//向四周共8个位置递归调用
    		for (i = x - 1; i <= x + 1; i++)
    		{
    			int j = 0;
    			for (j = y - 1; j <= y + 1; j++)
    			{

    				//只对 '*' 进行展开,防止死循环
    				if (show[i][j] == '*')
    				{
    					expand(mine, show, i, j, win);
    				}

    			}
    		}
    	}
    	else   //四周有雷显示雷数
    	{
    		show[x][y] = count + '0';
    	}

    	//记录展开的数量
    	(*win)++;
    }

}

//显示地雷位置,排雷成功或被炸死后 向玩家展示地雷位置

void show_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int i = 0;
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= COL; j++)
{
if (mine[i][j] == '1')
{
show[i][j] = '@'; //将地雷改成 '@'
}
}
}
display_board(show, row, col); //打印
}

//玩家排查雷

void find_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//初始化 翻开的格子数量

    while (win < row * col - COUNT)//当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功
    {
    	printf("请输入要排查的坐标:>");
    	scanf("%d %d", &x, &y);

    	//检查坐标是否越界
    	if (x >= 1 && x <= row && y >= 1 && y <= col)
    	{

    		//检查坐标是否排查过了
    		if (show[x][y] == '*')
    		{

    			if (mine[x][y] == '1')
    			{
    				system("cls");
    				show_mine(mine, show, row, col);
    				printf("-----------很遗憾,你被炸死了-----------\n");
    				break;
    			}
    			else
    			{
    				//展开
    				expand(mine, show, x, y, &win);

    				system("cls");//清屏
    				display_board(show, row, col);
    				printf("--------------还需翻开%d格--------------\n", row * col - COUNT - win);
    			}
    		}
    		else
    		{
    			printf("该坐标已排查,请重新输入\n");
    		}
    	}
    	else
    	{
    		printf("坐标非法,请重新输入\n");
    	}
    }

    if (win == row* col - COUNT)
    {
    	system("cls");
        show_mine(mine, show, row, col);//展示地雷位置
        printf("------------恭喜你,排雷成功-----------\n");
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【定时任务】---- xxl-job、@Scheduled

一、Scheduled注解实现的定时任务 要实现计划任务&#xff0c;首先通过在配置类注解EnableScheduling来开启对计划任务的支持&#xff0c;然后在要执行计划任务的方法上注解Scheduled&#xff0c;声明这是一个计划任务。 在Spring Boot 的入口类 XXXApplication 中,必然会有S…

东南大学洪伟教授评述:毫米波与太赫兹技术

今日推荐文章作者为东南大学毫米波国家重点实验室主任、IEEE Fellow 著名毫米波专家洪伟教授&#xff0c;本文选自《毫米波与太赫兹技术》&#xff0c;发表于《中国科学: 信息科学》2016 年第46卷第8 期——《信息科学与技术若干前沿问题评述专刊》。 本文概要介绍了毫米波与太…

CSS知识点精学6-精灵图、背景图片大小、文字阴影、盒子阴影、过渡

目录 一.精灵图 1.精灵图的介绍 2.精灵图的使用步骤 二.背景图片大小 三.文字阴影 四.盒子阴影 五.过渡 一.精灵图 1.精灵图的介绍 场景&#xff1a;项目中将多张小图片&#xff0c;合成一张大图片&#xff0c;这张图片称之为精灵图 优点&#xff1a;减少服务器发送次…

clickhouse入门学习以及数据迁移

本文主要介绍如何入门clickhouse&#xff0c;以及将mariadb数据迁移过来&#xff0c;最后介绍当前几种的训练的示例数据库集。1、中文教程&#xff1a;中文教程&#xff1a;中文教程有了教程&#xff0c;需要有数据可以训练&#xff0c;教程提供示例数据集&#xff0c;但是数据…

Java基础之《netty(22)—Protobuf》

一、Protobuf基本介绍 1、Protobuf是Google发布的开源项目&#xff0c;全称Google Protobuf Buffers&#xff0c;是一种轻便高效的结构化数据存储格式&#xff0c;可以用于结构化数据串行化&#xff0c;或者说序列化。它很适合做数据存储或RPC数据交换格式。 2、参考文档 htt…

粒子系统-主模块参数

目录 Duration Looping Prewarm Start Lifttime Start Speed Start Size 3D Start Rotation Start Rotation Start color Simulation Space Max Particles Duration 粒子系统的工作时长&#xff0c;如果不勾选Looping的话&#xff0c;在5秒后就再也没有粒子发射 L…

HTTPS头部的Referer字段

目录 Referrer-policy 如何设置referer 盗链 防盗链的工作原理 绕过图片防盗链 利用https网站盗链http资源网站&#xff0c;refer不会发送 利用iframe伪造请求referer 利用XMLHttpRequest Referer请求头包含了当前请求页面的来源页面的地址&#xff0c;即表示当前页面是…

【Proteus仿真】【STM32单片机】智能窗帘控制系统设计

文章目录一、功能简介二、软件设计三、实验现象联系作者一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用LCD1602显示模块、按键模块、HC05蓝牙、DHT11温湿度、PCF8591 ADC模块、光线传感器、28BYJ48步进电机等。 主要功能&#xff1a; 系统运行后&…

链表算法-回文结构、两个链表公共节点

最近一直在刷算法&#xff0c;以前没有重视这块&#xff0c;偶然巧合下&#xff0c;想到了某几次的面试&#xff0c;虽然没有以这个为主&#xff0c;但是也都有问过算法的题&#xff0c;因为没有这方面的积累&#xff0c;所以心底里一直抗拒&#xff0c;最近也有时间&#xff0…

git第n次学习笔记

git工作流程git四个工作区域Workspace&#xff1a;工作区&#xff0c;就是你平时存放项目代码的地方Index/Stage&#xff1a;暂存区&#xff0c;用于临时存放你的改动&#xff0c;事实上它只是一个文件&#xff0c;保存即将提交到文件列表信息Repository&#xff1a;仓库区&…

CDGA|想做好数据安全,数据治理是核心

在数字化转型渐进成熟下&#xff0c;企业加强数据治理&#xff0c;保障数据安全&#xff0c;为数字经济持续发展筑牢安全屏障&#xff0c;是时代发展的客观需要。 首先&#xff0c;整个安全能力是在应用内部的&#xff0c;我们对数据流的精确感知和管控&#xff0c;能做到和应用…

【七】Netty JBoss Marshallin 编解码

Netty JBoss Marshallin 编解码介绍Marshallin 开发环境maven 依赖业务场景模拟流程图代码展示订购采购消息 POJO 类订购应答消息 POJO 类SubscribeReqServer 服务端启动类MarshallingCodeCFactory服务端业务处理类 SubscribeServerHandler客户端启动类 SubscribeClient客户端 …

leetcode.2471 逐层排序二叉树所需的最少操作数目 - bfs + 置换环算法 + 并查集

2471. 逐层排序二叉树所需的最少操作数目 目录 1、循环标记置换环 2、并查集置换环 思路&#xff1a; 总操作数目 每一层最小操作数之和 每一层元素个数 - 置换环数 先用bfs对树进行层序遍历&#xff0c;一层一层地计算 置换环&#xff1a;对每个节点&#xff0c;将其指向…

全国首例:新一代仿生型人工心脏在福建成功植入

此时此刻&#xff0c;福建福清吴先生的体内正搏动着一颗新款的“人工心脏”。心脏是生命的中枢&#xff0c;一旦衰竭生命也将终止&#xff0c;人工心脏为这些心衰患者带来了新的希望。福建医科大学附属协和医院心外科团队&#xff0c;将科幻电影里装着人工心脏的“钢铁侠”变成…

六派巨量转移技术概述

1. 巨量转移技术概述 与OLED显示技术不同&#xff0c;无机LED无法在玻璃或其他大尺寸衬底进行大面积的制作&#xff0c;因此需要在半导体衬底上进行制作&#xff0c;然后再转移到驱动背板上。当前LED所采用的衬底一般为蓝宝石&#xff0c;但蓝宝石与外延层之间的晶格和热膨胀系…

国产技术迎来突破,光量子芯片横空出世,中文编程也有好消息

国外光刻机不再牛&#xff0c;随着这项技术问世&#xff0c;我们摆脱芯片卡脖子困境成为可能&#xff01; 欧美技术如此领先&#xff0c;我们凭什么实现弯道超车&#xff1f;就凭国内领先全球的量子技术&#xff0c;还有惊艳问世的光量子芯片&#xff0c;让欧美震惊不已&#x…

Fossid简介及API接口调用开发

FOSSID简介 FOSSID 是由瑞典FOSSID 公司开发的一款开源代码检测和管理工具&#xff0c;能够全面、准确、高效的发现用户代码库中的开源代码及其风险。 FOSSID 是一个软件解决方案&#xff0c;能够单独部署使用&#xff0c;也可以与现有的开发流程进行无缝集成&#xff0c;能够…

Apache Shiro教程(4)

shiro授权 授权 授权&#xff0c;即访问控制&#xff0c;控制谁能访问哪些资源&#xff0c;主体进行身份认证后需要分配权限方可访问系统的资源&#xff0c;对于某些资源没有权限是无法访问的。 关键对象 授权可简单理解为 who 对 what 进行how操作 授权流程 授权方式 基于角…

如何优化Blender内存不足问题

众所周知&#xff0c; Blender是一款免费软件&#xff0c;在动画、3D 电影等所有领域都易于使用。您是否曾经在 Blender 场景中工作&#xff0c;突然无缘无故崩溃&#xff1f;或者简单地说&#xff0c;您在渲染过程中突然移动缓慢甚至导致卡顿。当你有最后期限时&#xff0c;这…

13_4、Java的IO流之缓冲流的使用

一、引入为了提高数据读写的速度&#xff0c;Java API提供了带缓冲功能的流类&#xff0c;在使用这些流类 时&#xff0c;会创建一个内部缓冲区数组&#xff0c;缺省使用8192个字节(8Kb)的缓冲区。 二、缓冲流涉及到的类字节输入流&#xff1a;BufferedInputStream 字节输出流B…