扫雷(C 语言)

目录

  • 一、游戏设计分析
  • 二、各个步骤的代码实现
    • 1. 游戏菜单界面的实现
    • 2. 游戏初始化
    • 3. 开始扫雷
  • 三、完整代码
  • 四、总结

一、游戏设计分析

本次设计的扫雷游戏是展示一个 9 * 9 的棋盘,然后输入坐标进行判断,若是雷,则游戏结束,否则继续判断,直到排除所有雷,游戏胜利。然后选择退出或者重新开始游戏。

(1)游戏菜单界面实现
提供开始游戏和退出游戏两个选项,游戏结束后可以再次选择,直到退出游戏。
(2)游戏初始化
开始游戏后,初始化棋盘,随机设置地雷,然后显示棋盘。这里需要两个二维数组,一个用来显示棋盘,一个用来存储地雷的信息。
(3)开始扫雷
提示玩家输入坐标,然后检查坐标是否合法,最后判断位置是否为雷。若为雷,则游戏结束;若不为雷则计算周围地雷的个数,若周围地雷的个数不为 0,则显示将其显示在棋盘上;若周围地雷个数为 0,则使用刚才的方法继续检查周围的 8 个位置,以此类推,检查结束后把信息全部显示在棋盘上。然后继续下一次坐标输入,直到把所有的雷找出,游戏获胜。

上述需要计算输入坐标周围的的雷的个数,如果在该坐标在最边上一圈,那么会存在排查的坐标非法的情况。这里可以使用 11 * 11 的棋盘,然后通过把雷设置为字符 ‘1’,非雷设置为字符 ‘0’,当计算时把周围八个坐标相加然后减去 8 个字符 ‘0’ 即可。
(4)游戏结束或重新开始游戏

二、各个步骤的代码实现

1. 游戏菜单界面的实现

游戏菜单界面需要提供开始游戏和退出游戏两个选项,且玩完一局游戏之后可以选择再玩一局。在主函数使用一个循环来控制实现,如下:

(1)test.c 测试文件

// 头文件
#include "Mine.h"int main()
{// 所需变量int select;// 选择do{// 菜单menu();// 输入scanf("%d", &select);// 判断switch (select){case 1:printf("扫雷游戏\n");//Minesweeper_game();break;case 0:printf("游戏结束!\n");break;default:printf("输入错误,请重新输入!\n");break;}} while (select);return 0;
}

(2)Mine.h 头文件

// 头文件
#include <stdio.h>// 函数声明
// 菜单
void menu();

(3)Mine.c 函数实现文件

// 头文件
#include "Mine.h"// 函数定义
// 菜单
void menu()
{printf("*****************************************\n");printf("*************    1. play    *************\n");printf("*************    0. exit    *************\n");printf("*****************************************\n");
}

(4)代码运行效果:
在这里插入图片描述

2. 游戏初始化

游戏初始化需要显示棋盘,然后随机设置雷。这里需要使用两个数组,一个数组用来显示棋盘,另一个数组用来存储雷的信息。为了方便计算,两个数组都采用 11*11 的字符数组,初始棋盘使用符号 ‘*’,地雷使用字符 ‘1’,非地雷使用字符 ‘0’。数组大小使用在头文件中定义的符号常量,设计一个初始化函数 InitBoard() 和设置雷函数 SetMine(),还有显示棋盘函数 PrintBoard()。

InitBoard() 函数可以一次性传两个数组,也可以通过参数传递设置的值。SetMine() 函数需要使用随机数。PrintBoard() 函数显示的时候可以在左边和上面加上行号和列号,方便玩家输入坐标。

(1)test.c 测试文件
添加了扫雷游戏函数 Minesweeper_game()

// 头文件
#include "Mine.h"int main()
{// 所需变量int select;// 选择do{// 菜单menu();// 输入scanf("%d", &select);// 判断switch (select){case 1:Minesweeper_game();  // 扫雷游戏break;case 0:printf("游戏结束!\n");break;default:printf("输入错误,请重新输入!\n");break;}} while (select);return 0;
}

(2)Mine.h 头文件

// 头文件
#include <stdio.h>// 常量声明
#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2// 函数声明
// 菜单
void menu();// 初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set);// 设置雷
void SetMine(char Board[ROWS][COLS], int row, int col);// 显示棋盘
void PrintBoard(char Board[ROWS][COLS], int row, int col);// 扫雷游戏
void Minesweeper_game();

(3)Mine.c 函数实现文件

// 初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set)
{int i;for (i = 0; i < rows; ++i){int j;for (j = 0; j < cols; ++j){Board[i][j] = set;}}
}// 设置雷
void SetMine(char Board[ROWS][COLS], int row, int col)
{// 所需变量int x, y;  // 雷的坐标int num = MINE_COUNT;  // 雷的数量// 设置雷while (num){// 随机设置坐标值x = rand() % row + 1;  // 11 * 11 实际坐标 1 - 9y = rand() % col + 1;// 检查坐标是否重复if (Board[x][y] == '0'){Board[x][y] = '1';--num;}}
}// 显示棋盘
void PrintBoard(char Board[ROWS][COLS], int row, int col)
{// 分界线printf("-----------------------扫雷----------------------\n");// 列号printf("  ");int j;for (j = 0; j < col; ++j){printf("%d ", j + 1);}printf("\n");// 棋盘int i;for (i = 0; i < row; ++i){// 列号printf("%d ", i + 1);for (j = 0; j < col; ++j){printf("%c ", Board[i+1][j+1]);}// 下一行printf("\n");}// 分界线printf("-----------------------扫雷----------------------\n");
}// 扫雷游戏
void Minesweeper_game()
{// 创建两个棋盘char board[ROWS][COLS];char mine[ROWS][COLS];// 初始化棋盘InitBoard(board, ROWS, COLS, '*');printf("初始化两个数组:\n");  // 删除PrintBoard(board, ROW, COL);  // 删除InitBoard(mine, ROWS, COLS, '0');PrintBoard(mine, ROW, COL);  // 删除// 设置雷SetMine(mine, ROW, COL);printf("雷的信息:\n");  // 删除PrintBoard(mine, ROW, COL);  // 删除
}

上述标记了删除的语句都是不需要的,这是作者写完代码用来检测的。

(4)代码运行效果
在这里插入图片描述

3. 开始扫雷

开始扫雷,设计一个函数 find(),要求玩家输入坐标,检查该坐标是否合法,然后判断该位置是否为雷,是雷则游戏结束,不是雷则使用函数 calc_mine() 计算周围一圈的雷的个数。如果周围一圈没有雷,则按照刚才的步骤检查周围一圈,实现函数 calc_mine() 递归。然后继续扫雷,直到找出所有的雷。

函数 calc_mine() 只在 find() 函数中使用,可以设置为 Mine.c 函数实现文件中的静态函数。判断游戏结束也需要使用一个变量,这里使用一个全局变量 REAMIN 来进行控制,当 REAMIN 的值为棋子总数减去雷数时,则排除所有雷,游戏结束。

代码测试时,可以一个一个功能进行测试,一个功能完成无误后再进入下一个。作者是全部测试好了才放上来的。

(1)Mine.h 头文件

// ...// 找雷
void find(char Board[ROWS][COLS], int row, int col);// 扫雷游戏
void Minesweeper_game();

(2)Mine.c 函数实现文件

// 计算周围的雷
void calc_mine(char Board[ROWS][COLS], char Mine[ROWS][COLS], int row, int col, int x, int y)
{// 坐标非法或者已经排查或该位置为雷则退出if (x < 1 || x > 9 || y < 1 || y > 9 || Board[x][y] != '*' || Mine[x][y] != '0')return;// 所需变量int num = 0;int i;for (i = x - 1; i <= x + 1; ++i){int j;for (j = y - 1; j <= y + 1; ++j){num += Mine[i][j];}}// 加的是字符 '0' 所以要减去num = num - 9 * '0';// 排雷 +1++REMAIN;if (num){Board[x][y] = num + '0';}else{Board[x][y] = ' ';// 如果该坐标周围没雷,则对其周围八个坐标进行检查,形成递归for (i = x - 1; i <= x + 1; ++i){int j;for (j = y - 1; j <= y + 1; ++j){calc_mine(Board, Mine, row, col, i, j);}}}
}// 找雷
int find(char Board[ROWS][COLS], char Mine[ROWS][COLS], int row, int col)
{// 所需变量int x, y;// 输入排查坐标while (1){printf("请输入排雷坐标:");scanf("%d %d", &x, &y);// 判断坐标是否合法if (Board[x][y] != '*'){printf("该坐标已被排查!\n");continue;}if (x >= 1 && x <= row && y >= 1 && y <= col){if (Mine[x][y] == '1'){printf("很遗憾,你被炸死了!\n");printf("以下是本局雷的信息\n");return 0;}else{calc_mine(Board, Mine, row, col, x, y);return 1;}}}
}// 扫雷游戏
void Minesweeper_game()
{// 重置REMAIN = 0;// 创建两个棋盘char board[ROWS][COLS];char mine[ROWS][COLS];// 初始化棋盘InitBoard(board, ROWS, COLS, '*');InitBoard(mine, ROWS, COLS, '0');// 设置雷SetMine(mine, ROW, COL);int ret = 0;while (REMAIN != ROW * COL - MINE_COUNT){// 显示棋盘PrintBoard(board, ROW, COL);// 排雷ret = find(board, mine, ROW, COL);// 如果是雷,则显示雷的信息,游戏结束if (!ret){PrintBoard(mine, ROW, COL);break;}}// 判断if (REMAIN == ROW * COL - MINE_COUNT)\{printf("恭喜你排雷成功,游戏结束!\n");printf("以下是本局雷的信息:\n");PrintBoard(mine, ROW, COL);}
}

(3)代码运行效果:
下图是完整代码运行效果,一整局游戏的运行图:
在这里插入图片描述

三、完整代码

完整代码分为三个文件:头文件 Mine.h,测试文件 test.c,函数实现文件 Mine.c

头文件 Mine.h

#pragma once// 头文件
#include <stdio.h>
#include <stdlib.h>// 常量声明
#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define MINE_COUNT 10// 函数声明
// 菜单
void menu();// 初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set);// 设置雷
void SetMine(char Board[ROWS][COLS], int row, int col);// 显示棋盘
void PrintBoard(char Board[ROWS][COLS], int row, int col);// 找雷
int find(char Board[ROWS][COLS],char Mine[ROWS][COLS], int row, int col);// 扫雷游戏
void Minesweeper_game();

测试文件 test.c

// 头文件
#include "Mine.h"
#include <time.h>int main()
{// 设置随机数种子srand((unsigned)time(0));// 所需变量int select;// 选择do{// 菜单menu();// 输入scanf("%d", &select);// 判断switch (select){case 1:Minesweeper_game();  // 扫雷游戏break;case 0:printf("游戏结束!\n");break;default:printf("输入错误,请重新输入!\n");break;}} while (select);return 0;
}

函数实现文件 Mine.c

// 头文件
#include "Mine.h"// 全局静态变量
static REMAIN = 0;// 函数定义
// 菜单
void menu()
{printf("*****************************************\n");printf("*************    1. play    *************\n");printf("*************    0. exit    *************\n");printf("*****************************************\n");
}// 初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set)
{int i;for (i = 0; i < rows; ++i){int j;for (j = 0; j < cols; ++j){Board[i][j] = set;}}
}// 设置雷
void SetMine(char Board[ROWS][COLS], int row, int col)
{// 所需变量int x, y;  // 雷的坐标int num = MINE_COUNT;  // 雷的数量// 设置雷while (num){// 随机设置坐标值x = rand() % row + 1;  // 11 * 11 实际坐标 1 - 9y = rand() % col + 1;// 检查坐标是否重复if (Board[x][y] == '0'){Board[x][y] = '1';--num;}}
}// 显示棋盘
void PrintBoard(char Board[ROWS][COLS], int row, int col)
{// 分界线printf("-----------------------扫雷----------------------\n");// 列号printf("  ");int j;for (j = 0; j < col; ++j){printf("%d ", j + 1);}printf("\n");// 棋盘int i;for (i = 0; i < row; ++i){// 列号printf("%d ", i + 1);for (j = 0; j < col; ++j){printf("%c ", Board[i+1][j+1]);}// 下一行printf("\n");}// 分界线printf("-----------------------扫雷----------------------\n");
}// 计算周围的雷
void calc_mine(char Board[ROWS][COLS], char Mine[ROWS][COLS], int row, int col, int x, int y)
{// 坐标非法或者已经排查或该位置为雷则退出if (x < 1 || x > 9 || y < 1 || y > 9 || Board[x][y] != '*' || Mine[x][y] != '0')return;// 所需变量int num = 0;int i;for (i = x - 1; i <= x + 1; ++i){int j;for (j = y - 1; j <= y + 1; ++j){num += Mine[i][j];}}// 加的是字符 '0' 所以要减去num = num - 9 * '0';// 排雷 +1++REMAIN;if (num){Board[x][y] = num + '0';}else{Board[x][y] = ' ';// 如果该坐标周围没雷,则对其周围八个坐标进行检查,形成递归for (i = x - 1; i <= x + 1; ++i){int j;for (j = y - 1; j <= y + 1; ++j){calc_mine(Board, Mine, row, col, i, j);}}}
}// 找雷
int find(char Board[ROWS][COLS], char Mine[ROWS][COLS], int row, int col)
{// 所需变量int x, y;// 输入排查坐标while (1){printf("请输入排雷坐标:");scanf("%d %d", &x, &y);// 判断坐标是否合法if (Board[x][y] != '*'){printf("该坐标已被排查!\n");continue;}if (x >= 1 && x <= row && y >= 1 && y <= col){if (Mine[x][y] == '1'){printf("很遗憾,你被炸死了!\n");printf("以下是本局雷的信息\n");return 0;}else{calc_mine(Board, Mine, row, col, x, y);return 1;}}}
}// 扫雷游戏
void Minesweeper_game()
{// 重置REMAIN = 0;// 创建两个棋盘char board[ROWS][COLS];char mine[ROWS][COLS];// 初始化棋盘InitBoard(board, ROWS, COLS, '*');InitBoard(mine, ROWS, COLS, '0');// 设置雷SetMine(mine, ROW, COL);int ret = 0;while (REMAIN != ROW * COL - MINE_COUNT){// 显示棋盘PrintBoard(board, ROW, COL);// 排雷ret = find(board, mine, ROW, COL);// 如果是雷,则显示雷的信息,游戏结束if (!ret){PrintBoard(mine, ROW, COL);break;}}// 判断if (REMAIN == ROW * COL - MINE_COUNT)\{printf("恭喜你排雷成功,游戏结束!\n");printf("以下是本局雷的信息:\n");PrintBoard(mine, ROW, COL);}
}

四、总结

本次扫雷游戏的代码实现,总体来说还比较可以。基本功能均实现,游戏也能正常进行。本游戏还有许多可以提升的地方,比如:代码的优化,雷的标记,难度的选择等等。

本代码稍微难一点的地方在于排查坐标周围地雷数的函数 calc_mine() 的递归,需要明确何时需要递归,和递归的结束条件。

复习一下递归需要满足两个条件:
(1)需要结束条件
(2)每次递归都更加靠近这个结束条件

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

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

相关文章

FPGA实现PCIE采集电脑端视频转SFP光口UDP输出,基于XDMA+GTX架构,提供4套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案1G/2.5G Ethernet Subsystem实现物理层方案1G/2.5G Ethernet PCS/PMA or SGMII Tri Mode Ethernet MAC实现物理层方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图电脑端视频PCIE视频采集QT上位机X…

VSCODE c++不能自动补全的问题

最近安装了vscode&#xff0c;配置了C/C扩展&#xff0c;也按照网上说的配置了头文件路径 我发现有部分头文件是没办法解析的&#xff0c;只要包含这些头文件中的一个或者多个&#xff0c;就没有代码高亮和代码自动补全了&#xff0c;确定路径配置是没问题的&#xff0c;因为鼠…

【GT240X】【3】Wmware17和Centos 8 安装

文章目录 一、说明二、安装WMware2.1 下载WMware2.2 安装2.3 虚拟机的逻辑结构 三、安装Centos3.1 获取最新版本Centos3.2 创建虚拟机 四、问题和简答4.1 centos被淘汰了吗&#xff1f;4.2 centos里面中文显示成小方块的解决方法4.3 汉语-英语输入切换4.4 全屏和半屏切换 五、练…

【图论】(一)图论理论基础与岛屿问题

图论理论基础与岛屿问题 图论理论基础深度搜索&#xff08;dfs&#xff09;广度搜索&#xff08;bfs&#xff09;岛屿问题概述 岛屿数量岛屿数量-深搜版岛屿数量-广搜版 岛屿的最大面积孤岛的总面积沉没孤岛建造最大人工岛水流问题岛屿的周长 图论理论基础 这里仅对图论相关核…

精英高匿ip的自述

大家好&#xff0c;我是精英高匿IP。在网络世界里&#xff0c;我有着自己独特的看家本领&#xff0c;今天我就让大家见识一下我的本事。 我是一个神秘的网络侠客&#xff0c;我在数据的江湖中穿梭自如且不留痕迹。大家可能好奇我为什么叫精英高匿IP。“精英”代表着我拥有卓越…

【命令操作】Linux上通过mdadm配置软RAID _ 统信 _ 麒麟 _ 方德

往期好文&#xff1a;【功能介绍】麒麟2403支持配置任务栏上的图标“从不合并”啦&#xff01; Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于如何在Linux系统上使用mdadm工具配置软件RAID&#xff08;Redundant Array of Independent Disks&#xff0c;独立磁…

高频面试手撕

手撕高频结构 前言&#xff0c;以下内容&#xff0c;都是博主在秋招面试中&#xff0c;遇到的面试手撕代码题目&#xff0c;包含常见的数据结构、多线程以及数据库连接池等。 ArrayList 实现了ArrayList的基本功能&#xff0c;包括随机访问和自动扩容。 添加元素时&#xff…

施磊C++ | 进阶学习笔记 | 1.对象的应用优化、右值引用的优化

一.对象的应用优化、右值引用的优化 文章目录 一.对象的应用优化、右值引用的优化1.1 构造&#xff0c;拷贝&#xff0c;赋值&#xff0c;析构中的优化课后练习&#xff1a; 1.2 函数调用过程中对象背后调用的方法1.3 对象优化三原则1.4 右值引用、move移动语意、完美转发 1.1 …

ThingsBoard规则链节点:Clear Alarm节点详解

引言 Clear Alarm 节点含义 使用场景 实际项目中的运用场景 智能建筑管理系统 工业生产线监控 远程医疗监护 结论 引言 ThingsBoard 是一个开源的物联网平台&#xff0c;它提供了设备管理、数据收集、处理和可视化等功能。在 ThingsBoard 中&#xff0c;规则链&#xff…

QExcel 保存数据 (QtXlsxWriter库 编译)

QtXlsxWriter 是一个用于在 Qt 应用程序中创建和操作 Excel XLSX 文件的库。它提供了一个简单的 API&#xff0c;使开发者能够轻松地生成和修改 Excel 文件&#xff0c;而无需依赖 Microsoft Excel 或其他外部应用程序。支持初始化、写文件、读文件、格式设置、合并单元格、加粗…

scala 高阶函数 (下)

一.fold fold的作用 idea实例 二.sorted函数 sort基础知识 idea实例 三.sortWith sortWith基础知识 idea实例

音乐播放器项目专栏介绍​

1.简介 本专栏使用Qt QWidget作为显示界面&#xff0c;你将会学习到以下内容&#xff1a; 1.大量ui美化的实例。 2.各种复杂ui布局。 3.常见显示效果实现。 4.大量QSS实例。 5.Qt音频播放&#xff0c;音乐歌词文件加载&#xff0c;展示。 6.播放器界面换肤。 相信学习了本专栏…

Java学习-JUC

目录 1. 简介 2. Atomic包 2.1 什么是原子类 2.2 Atomic包里的类 3. CAS 3.1 CAS是什么 3.2 Java中对CAS的实现 3.3 CAS的缺陷 4. JUC里面的常见锁 4.1 锁分类 4.1.1 按上锁方式划分 4.1.2 按特性划分 4.1.3 其他锁 4.2 Synchronized和JUC的锁对比 5. 锁原理分析…

智慧园区能带来哪些便利?

所谓智慧园区&#xff0c;是指通过信息化手段&#xff0c;实现园区内各项业务的数字化和智能化管理。园区管理者可以利用智能化平台实时监控各项运营情况&#xff0c;如能源使用、安全监控和物流运输等&#xff0c;及时调整管理策略&#xff0c;提高运营效率。智慧园区利用大数…

pycharm 找不到conda环境

参考&#xff1a;新版Pycharm解决Conda executable is not found-CSDN博客

WNMP环境本地搭建并配置公网地址远程搭建动态网站或服务器

文章目录 前言1.Wnmp下载安装2.Wnmp设置3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 本教程主要介绍如何在Windows系统电脑本地下载安装 Wnmp&#xff0c;以及结合cpolar内网穿透工具一键配…

【C++】——多态(上)

【C】——多态&#xff08;上&#xff09; 1 多态的概念2 多态的定义及实现2.1 多态的构成条件2.1.1 实现多态的必要条件2.1.2 虚函数2.1.3 感受多态2.1.4 判断是否满足多态2.1.5 多态场景的一道选择题2.1.6 虚函数重写的一些其他问题2.1.6.1 协变2.1.6.2 析构函数的重写 2.1.7…

深度学习-26-基于PyTorch的多层感知机DNN

文章目录 1 代码分析1.1 加载数据集1.2 定义模型1.3 定义损失函数和优化器1.4 定义训练函数1.4.1 定义累加器Accumulator1.4.2 计算准确率accuracy1.4.3 评估函数evaluate_accuracy1.4.4 单轮训练函数train_epoch1.4.5 训练函数train1.2 执行训练2 整体代码3 参考附录1 代码分析…

使用Python构建一个高级计算器

使用Python构建一个高级计算器 在现代编程中&#xff0c;构建一个功能强大的计算器不仅是学习编程的好项目&#xff0c;还有助于提高对数据处理和用户交互的理解。本文将带您通过使用Python构建一个高级计算器&#xff0c;该计算器支持基本运算、科学运算以及简单的图形用户界…

将SpringBoot项目部署到linux服务器使得本地可以访问

首先我们要先从本地打包jar文件上传到linux中&#xff0c;这些的步骤可以参考其他打包上传的博客哈 打包上传后&#xff0c;可以看到对应的 .jar 文件 如果这样直接运行java -jar code-sandbox-0.0.1-SNAPSHOT.jar 是不行的&#xff0c;因为你还没有在服务器上开放端口&#x…