【C语言】简易版扫雷游戏(数组、函数的练习)

目录

一、分析和设计

1.1、扫雷游戏的功能分析

1.2、文件结构设计(多文件的练习)

1.3、数据结构的设计

二、代码

三、效果展示

三、优化


一、分析和设计

1.1、扫雷游戏的功能分析

        以在线版的扫雷游戏为参考,分析它的功能:扫雷游戏网页版 - Minesweeper

  • 使用控制台的输入控制游戏,控制台的输出打印游戏。
  • 简单版:9 x 9 尺寸的棋盘,10个雷。
  • 排雷规则:
(1)如果不是雷,则显示周围(8个位置)的雷个数。(2)如果是雷,游戏失败。(3)如果把雷之外的所有非雷位置找出,游戏成功。

1.2、文件结构设计(多文件的练习)

test.c:游戏的主逻辑,可用于测试。game.c:实现游戏的各种函数。game.c:实现游戏的各种函数的声明、数据的声明。

1.3、数据结构的设计

       存储棋盘形状的数据,使用二维数组再好不过了,易理解易操作。需要存储的数据:① 哪些位置是雷(用0和1表示)。② 打印在控制台的扫雷画面(未排雷的位置显示 ' * ' ,已排雷且非雷的位置显示周围雷的个数)。

        因为有字符' * ',所以存周围雷个数的数组的元素用char类型;虽然存雷位置的数组都是数字0和1,但是统一起见,元素的类型都定义为char,这样写代码的时候不用区分char还是int,不宜弄错。

        虽然说这两种数据可以放到一起,比如非雷位置放9,雷位置放10;排雷时非雷位置(9)可改成周围雷个数(≤8);打印时,等于9或10的位置打印' * ',小于等于8的位置打印周围雷个数。但是这样在打印时会多一些判断,读代码的人理解起来也没有分成两个数组容易。

        数据结构如下:

char mine[ROWS][COLS] = {0};
char show[ROWS][COLS] = {0};

        排雷时,如果找到非雷,则显示周围雷的个数。当这个非雷是二维数组的边缘部分时,统计周围雷的个数会发生越界:

        为了解决这个问题,需要把数组的大小增大一圈(两个数组统一增大,免得写代码判断的时候范围不统一,记混了):

二、代码

        game.c

#define _CRT_SECURE_NO_WARNINGS 1;
#include "game.h"void print_menu(){printf("******************************\n");printf("*********    1.play   ********\n");printf("*********    2.exit   ********\n");printf("******************************\n");printf("请选择:");
}void init_mine(char mine[][EASY_COLS]) {for (int i = 0; i < EASY_ROWS; i++)for (int j = 0; j < EASY_COLS; j++)mine[i][j] = '0';
}void init_show(char show[][EASY_COLS]) {for (int i = 0; i < EASY_ROWS; i++)for (int j = 0; j < EASY_COLS; j++)show[i][j] = '*';
}void set_mine(char mine[][EASY_COLS]) {int r = 0;int c = 0;for (int i = 0; i < EASY_COUNT; i++) {r = rand() % EASY_ROW + 1; // 雷要在棋盘里边,不能在增加的一圈里c = rand() % EASY_COL + 1;if ('0' == mine[r][c]) // 这个位置不是雷,放雷mine[r][c] = '1';elsei--; // 这个位置已经是雷了,这轮不算}
}void print_mine(char mine[][EASY_COLS]) {for (int i = 0; i <= EASY_ROW; i++)printf("%d ", i);printf("\n");for (int i = 1; i <= EASY_ROW; i++) {printf("%d ", i);for (int j = 1; j <= EASY_COL; j++)printf("%c ", mine[i][j]);printf("\n");}
}void print_show(char show[][EASY_COLS]) {for (int i = 0; i <= EASY_ROW; i++)printf("%d ", i);printf("\n");for (int i = 1; i <= EASY_ROW; i++) {printf("%d ", i);for (int j = 1; j <= EASY_COL; j++)printf("%c ", show[i][j]);printf("\n");}printf("输入坐标:");
}void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS]) {int x = 0; // 输入的坐标int y = 0;int count = 0; // 已找到的的非雷while (count != EASY_ROW * EASY_COL - EASY_COUNT) { // 没有找到所有非雷,游戏就继续print_show(show); // 打印棋盘scanf("%d %d", &x, &y); // 输入坐标system("cls");if (mine[x][y] == '1') {printf("炸弹!游戏失败!\n");break;}else if (mine[x][y] == '0') {char c = count_mine(mine, x, y); // 计算周围雷的个数show[x][y] = c;count++; //找到一个非雷}}if (count == EASY_ROW * EASY_COL - EASY_COUNT)printf("排雷成功!\n");
}char count_mine(char mine[][EASY_COLS], int x, int y) {return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1]+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0' + '0';
}

        game.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>#define EASY_ROW 9
#define EASY_COL 9#define EASY_COUNT 10#define EASY_ROWS EASY_ROW+2
#define EASY_COLS EASY_COL+2void print_menu();
void init_mine(char mine[][EASY_COLS]);
void init_show(char show[][EASY_COLS]);
void set_mine(char mine[][EASY_COLS]);
void print_mine(char mine[][EASY_COLS]);
void print_show(char show[][EASY_COLS]);
void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS]);
char count_mine(char mine[][EASY_COLS], int x, int y);

        test.c

#define _CRT_SECURE_NO_WARNINGS 1;
#include "game.h"void game() {char mine[EASY_ROWS][EASY_COLS]; // 存储生成的雷char show[EASY_ROWS][EASY_COLS]; // 存储排雷后雷的个数init_mine(mine); // 初始化,全放'0'init_show(show); // 初始化棋盘,全为'*'set_mine(mine); // 随机放雷find_mine(mine, show);// 扫雷
}int main() {int choose = 0;srand((unsigned int)time(NULL)); // 设置随机种子do{print_menu(); // 打印主菜单scanf("%d", &choose); // 输入选择system("cls"); // 清屏switch (choose) {case 1: // 开始游戏game();break;case 2: // 退出break;default:printf("输入错误,请重新输入。\n");}} while (choose != 2);return 0;
}

三、效果展示

三、优化

        代码还有很多需要优化的地方,比如:

  • 如果排查的位置不是雷,它周围也没雷,周围可以进行展开。
  • 可以选择游戏难度:简单(9x9,10个雷)、中等(16x16,40个雷)、困难(16x30,99个雷)、自定义。
  • 可以标记雷。
  • 游戏中,可以显示时间。

(1)增加展开、标记雷功能

        代码如下:

        game.h:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>#define EASY_ROW 9
#define EASY_COL 9#define EASY_COUNT 10#define EASY_ROWS EASY_ROW+2
#define EASY_COLS EASY_COL+2// 打印字体颜色
#define NONE "\033[m"
#define BLUE "\033[0;32;34m"
#define RED "\033[0;32;31m"void print_menu();
void init_mine(char mine[][EASY_COLS]);
void init_show(char show[][EASY_COLS]);
void set_mine(char mine[][EASY_COLS]);
void print_mine(char mine[][EASY_COLS]);
void print_show(char show[][EASY_COLS]);
void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS], char flag[][EASY_COLS]);
char count_mine(char mine[][EASY_COLS], int x, int y);
void find_other_mine(char mine[][EASY_COLS], char show[][EASY_COLS], int x, int y, int* cnt);
void mark_mine(char flag[][EASY_COLS]);

        game.c:

#define _CRT_SECURE_NO_WARNINGS 1;
#include "game.h"void print_menu(){ // 打印主菜单printf("******************************\n");printf("*********    1.play   ********\n");printf("*********    2.exit   ********\n");printf("******************************\n");printf("请选择:");
}void init_mine(char mine[][EASY_COLS]) { // 初始化雷信息for (int i = 0; i < EASY_ROWS; i++)for (int j = 0; j < EASY_COLS; j++)mine[i][j] = '0';
}void init_show(char show[][EASY_COLS]) { //初始化棋盘for (int i = 0; i < EASY_ROWS; i++)for (int j = 0; j < EASY_COLS; j++)show[i][j] = '*';
}void set_mine(char mine[][EASY_COLS]) { // 随即放置雷int r = 0;int c = 0;for (int i = 0; i < EASY_COUNT; i++) {r = rand() % EASY_ROW + 1; // 雷要在棋盘里边,不能在增加的一圈里c = rand() % EASY_COL + 1;if ('0' == mine[r][c]) // 这个位置不是雷,放雷mine[r][c] = '1';elsei--; // 这个位置已经是雷了,这轮不算}
}void print_mine(char mine[][EASY_COLS]) { // 打印雷信息for (int i = 0; i <= EASY_ROW; i++)printf(BLUE"%d "NONE, i);printf("\n");for (int i = 1; i <= EASY_ROW; i++) {printf(BLUE"%d "NONE, i);for (int j = 1; j <= EASY_COL; j++)printf("%c ", mine[i][j]);printf("\n");}
}void print_show(char show[][EASY_COLS], char flag[][EASY_COLS]) { // 打印棋盘for (int i = 0; i <= EASY_ROW; i++)printf(BLUE"%d "NONE, i);printf("\n");for (int i = 1; i <= EASY_ROW; i++) {printf(BLUE"%d "NONE, i);for (int j = 1; j <= EASY_COL; j++) {if('1' == flag[i][j])printf(RED"%c "NONE, show[i][j]);elseprintf("%c ", show[i][j]);}printf("\n");}printf("输入坐标(输入0 0标记雷):");
}void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS], char flag[][EASY_COLS]) { // 排雷int x; // 输入的坐标int y;int count = 0; // 已找到的的非雷while (count != EASY_ROW * EASY_COL - EASY_COUNT) { // 没有找到所有非雷,游戏就继续x = 0;y = 0;while (x == 0 && y == 0) {print_show(show, flag); // 打印棋盘scanf("%d %d", &x, &y); // 输入坐标if (0 == x && 0 == y) // 标记雷mark_mine(flag);system("cls");}if (mine[x][y] == '1') { // 碰到炸弹了printf("炸弹!游戏失败!\n");break;}else if (mine[x][y] == '0' && show[x][y] == '*') { // (x,y)不是炸弹,并且没有被排查过find_other_mine(mine, show, x, y, &count);}}if (count == EASY_ROW * EASY_COL - EASY_COUNT) // 所有非雷点找齐了printf("排雷成功!\n");
}char count_mine(char mine[][EASY_COLS], int x, int y) { // 计算非雷点(x,y)周围有几个雷return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1]+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0' + '0';
}void find_other_mine(char mine[][EASY_COLS], char show[][EASY_COLS], int x, int y, int* cnt) { // 递归,排查的位置不是雷,周围也没有雷,可以展开char c = count_mine(mine, x, y); // 计算周围雷的个数if(c == '0')show[x][y] = ' ';elseshow[x][y] = c;(*cnt)++; //找到一个非雷if (show[x][y] != ' ') { // 周围有雷,就不需要扩展了return;}for (int i = -1; i <= 1; i++) {for (int j = -1; j <= 1; j++) {int xt = x + i;int yt = y + j;if (show[xt][yt] != '*' || mine[xt][yt] == '1') // 已经排查过的,就不需要重复扩展了;本身是炸弹的,也不要扩展continue;if(xt >= 1 && yt >= 1 && xt <= EASY_ROW && yt <= EASY_COL) // 拓展的坐标要在棋盘范围内find_other_mine(mine, show, xt, yt, cnt);}}
}void mark_mine(char flag[][EASY_COLS]) {int x;int y;printf("输入雷的坐标:");scanf("%d %d", &x, &y); // 输入坐标flag[x][y] = '1';
}

        test.c:

#define _CRT_SECURE_NO_WARNINGS 1;
#include "game.h"void game() {char mine[EASY_ROWS][EASY_COLS]; // 存储生成的雷char show[EASY_ROWS][EASY_COLS]; // 存储排雷后雷的个数char flag[EASY_ROWS][EASY_COLS]; // 屏幕上,标记的雷init_mine(mine); // 初始化,全放'0'init_show(show); // 初始化棋盘,全为'*'init_mine(flag); // 初始化全'0','0' 表示未标记,‘1’表示已标记set_mine(mine); // 随机放雷find_mine(mine, show, flag);// 扫雷
}int main() {int choose = 0;srand((unsigned int)time(NULL)); // 设置随机种子do{print_menu(); // 打印主菜单scanf("%d", &choose); // 输入选择system("cls"); // 清屏switch (choose) {case 1: // 开始游戏game();break;case 2: // 退出break;default:printf("输入错误,请重新输入。\n");}} while (choose != 2);return 0;
}

        效果展示:

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

⌈ 传知代码 ⌋ 基于矩阵乘积态的生成模型

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

钉耙编程(3)

1001深度自同构 Problem Description 对于无向图中的点&#xff0c;定义一个点的度为与其相连的边的条数。 对于一棵有根树&#xff0c;定义一个点的深度为该点到根的距离。 对于由若干有根树构成的森林&#xff0c;定义该森林是深度自同构的&#xff0c;当且仅当森林中任意…

【论文解读|Data Intelligence】 Dr.ICL: Demonstration-Retrieved In-context Learning

论文链接&#xff1a; 来源&#xff1a; Data Intelligence 论文介绍&#xff1a; 该研究由亚利桑那州立大学和谷歌研究团队的专家撰写&#xff0c;深入探讨了通过利用基于检索的方法来提高大型语言模型&#xff08;LLM&#xff09;性能的策略。 主要亮点&#xff1a; • 创…

解开基于大模型的Text2SQL的神秘面纱

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

程序员修炼之路

成为一名优秀的程序员&#xff0c;需要广泛而深入地学习多个领域的知识。这些课程不仅帮助建立扎实的编程基础&#xff0c;还培养了问题解决、算法设计、系统思维等多方面的能力。以下是一些核心的必修课&#xff1a; 计算机基础 计算机组成原理&#xff1a;理解计算机的硬件组…

GD 32 滤波算法

快速排序知识补充 http://t.csdnimg.cn/gVOsohttp://t.csdnimg.cn/gVOso GD32硬件滤波算法 程序代码&#xff1a; #include <stdint.h> #include <stdio.h> #include "gd32f30x.h" #include "delay.h"static void GpioInit(void) {rcu_periph…

项目实战_表白墙(简易版)

你能学到什么 一个比较简单的项目&#xff1a;表白墙&#xff08;简易版&#xff09;&#xff0c;浏览器&#xff1a;谷歌升级版将在下个博客发布 效果如下 正文 说明 我们是从0开始一步一步做这个项目的&#xff0c;里面的各种问题&#xff0c;我也会以第一人称视角来解…

经验分享:大数据多头借贷风险对自身的不利影响?

在现代金融体系中&#xff0c;大数据技术的应用使得多头借贷成为一种普遍现象。多头借贷指的是个人或企业在短时间内同时或近期内申请多笔贷款或信用产品&#xff0c;这种行为可能带来一系列财务和信用风险。以下是大数据多头借贷风险对个人自身可能产生的不利影响&#xff1a;…

如何编写一个多线程、非阻塞的python代码

一、【写在前面】 最近csdn每天写两篇文章有推广券&#xff0c;趁这个机会写一个python相关的文章吧。 一般我们的任务都可以分为计算密集型任务和IO密集型任务。 python因为全局GIL锁的存在&#xff0c;任何时候只有一个python线程在运行&#xff0c;所以说不能利用多核CPU…

数字的位操作——326、504、263、190、191、476、461、477、693

326. 3 的幂&#xff08;简单&#xff09; 给定一个整数&#xff0c;写一个函数来判断它是否是 3 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 整数 n 是 3 的幂次方需满足&#xff1a;存在整数 x 使得 n 3x 示例 1&#xff1a; 输入&a…

程序员面试题------N皇后问题算法实现

N皇后问题是一个著名的计算机科学问题&#xff0c;它要求在NN的棋盘上放置N个皇后&#xff0c;使得它们之间不能相互攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上。这个问题可以看作是一个回溯算法问题&#xff0c;通过逐步尝试不同的放置位置&#xf…

订单状态统计业务

文章目录 概要整体架构流程技术细节小结 概要 订单状态统计是电子商务、供应链管理、客户服务等多个领域中的一项核心业务需求. 需求分析以及接口设计 技术细节 1.Controller层: ApiOperation("各个状态的订单统计")GetMapping("/statistics")public Re…

检索增强生成(RAG):智能内容生成的新纪元

引言 在大 AI 时代&#xff0c;生成式人工智能&#xff08;GenAI&#xff09;模型&#xff0c;尤其是大型语言模型&#xff08;LLM&#xff09;&#xff0c;已经展现出了令人瞩目的能力。然而&#xff0c;这些模型在提供信息的准确、即时、专业、权威等方面仍存在局限。检索增…

用Python打造精彩动画与视频,3.2 基本的剪辑和合并操作

3.2 基本的剪辑和合并操作 在这一节中&#xff0c;我们将学习如何使用 MoviePy 库对视频进行基本的剪辑和合并操作。MoviePy 是一个用于视频编辑的 Python 库&#xff0c;可以轻松地实现视频的剪辑、合并、添加音频等操作。 准备工作 首先&#xff0c;确保你已经安装了 Movi…

c++----类与对象(下)

当我们简单的学习了上一篇日期类。简单的理解并且使用了我们前面学习的知识。当然这还只是我们c的九牛一毛。并且我们的类与对象的知识还没学习完。今天我们来把类与对象的知识完善一下。 初始化列表 那么今天我们就不讲废话了&#xff0c;我们直接来主题。首先我们可以看到我…

防火墙Firewalld(iptables)

目录 一、Linux防火墙基础 1.什么是防火墙 2.防火墙的功能 3.防火墙的类型 二、Linux防火墙工具 1.iptables 2. netfilter 3.四表五链结构 3.1四表 3.2五链 3.3总结 4.数据包过滤的匹配流程 4.1规则表之间的顺序 4.2规则链之间的顺序 4.3规则链内的匹配顺序 …

项目实战_表白墙(升级版)

你能学到什么 表白墙&#xff08;升级版&#xff09;Mybatis的一些简单应用 正文 前⾯的案例中, 我们写了表⽩墙, 但是⼀旦服务器重启, 数据就会丢失. 要想数据不丢失, 需要把数据存储在数据库中&#xff0c;接下来咱们借助MyBatis来实现数据库的操作。 数据准备 如果我们…

Kubernetes Prometheus 系列 | AlertManager实现企业微信报警

helm部署prometheusgrafana直通车&#xff08;与本文章关联&#xff09; 首先注册企业微信&#xff1a;https://work.weixin.qq.com/ 目录 一、第一种根据企业id&#xff0c;应用secret等绑定二、第二种方式-添加群聊天机器人webhook&#xff08;推荐&#xff09; 前言&#x…

AI Agent学习系列:利用扣子智能体快速生成字体大小可控的金句海报

像这样的金句海报是如何生成的&#xff1f; 利用智能体可以轻松实现&#xff0c;还能控制字体大小&#xff0c;下面就介绍这个智能体的搭建过程。 一、创建扣子bot 打开扣子&#xff0c;点击“创建Bot”&#xff0c;手动创建一个bot。 在Bot创建页面输入Bot名称&#xff0c;比…

【项目实战】—— 高并发内存池

文章目录 什么是高并发内存池&#xff1f;项目介绍一、项目背景二、项目目标三、核心组件四、关键技术五、应用场景六、项目优势 什么是高并发内存池&#xff1f; 高并发内存池是一种专门设计用于高并发环境下的内存管理机制。它的原型是Google的一个开源项目tcmalloc&#xff…