赞
踩
使用工具:Visual Studio 2022
前言:对扫雷的数据结构分析略讲,并主要讲解具体代码和代码的逻辑
扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。
如果创建⼀个99的数组来存放信息,在最外围的一圈进行地雷排查时会出现越界的情况,所以创建1111的数组进行信息存储。
创建两个字符数组,一个数组(mine)存储雷和非雷的信息:雷用'1'
非雷用'0'
,另⼀个数组(show)存放排查出的雷的信息,show数组开始时初始化为字符 ‘*’。
将所有库函数引用的头文件放入game.h文件中,直接在test.c和game.c文件中引用:#include “game.h”
int main()
{
test();//在主函数外另封装一个函数进行具体的游戏测试
return 0;
}
在控制台打印菜单,选择是否开始游戏,用switch语句进行菜单选择,do-while循环让游戏可以重复游玩
void menu1() { printf("********************\n"); printf("***** 1.paly *****\n"); printf("***** 0.exit *****\n"); printf("********************\n"); } void test() { int input = 0; srand((unsigned int)time(NULL)); do { menu1(); printf("请选择>:"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); }
四个变量ROW、COL、ROWS、COLS在game.h文件中被定义,剩余函数在game.h文件中声明,在game.c文件中定义
void game() { char mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出的雷的信息 //初始化面板(棋盘) //初始化数组mine中全为字符0 //初始化数组show中全为字符* InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*'); //展示面板(打印棋盘) DisplayBoard(show, ROW, COL); //DisplayBoard(mine, ROW, COL); //布置地雷 SetMine(mine, ROW, COL); //DisplayBoard(mine, ROW, COL); //排查显示地雷的信息 FindMine(mine,show, ROW, COL); }
对数组mine和数组show进行初始化,mine全部初始化为'0'
,show全部初始化为'*'
void InitBoard(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;
}
}
}
对数组内容进行打印,以9*9棋盘的形式打印
void DisplayBoard(char Board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("\n-------------扫雷-------------\n"); for (i = 0; i <= col; i++) { printf("%2d ", i); } printf("\n"); for (i = 1; i <= row; i++) { printf("%2d ", i); for (j = 1; j <= col; j++) { printf("%2c ", Board[i][j]); } printf("\n"); } }
⽣成a~b的随机数: a + rand()%(b-a+1)
变量EASY_COUNT在game.h文件中被定义,为简单模式下地雷个数
用Board[x][y] = '1’表示埋下地雷
void SetMine(char Board[ROWS][COLS], int row, int col) { int count = EASY_COUNT; int x = 0; int y = 0; while (count) { x = rand() % row + 1; y = rand() % col + 1; if (Board[x][y] != '1') { Board[x][y] = '1'; count--; } } }
要对输入坐标进行判断,因为输入的坐标可能会超出棋盘的界限;而在界限内的坐标如果踩雷(当输入坐标所得mine[x][y] == '1’时),没有踩雷则对周围八个坐标中雷的个数进行统计,并让show[x][y]等于所统计个数,使用GetMineCount函数统计
void FindMine(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 - EASY_COUNT) { printf("请输入坐标>:"); scanf("%d %d", &x, &y); if (x > 0 && x <= row && y > 0 && y <= col) { if (mine[x][y] == '1') { DisplayBoard(mine, row, col); printf("游戏失败\n"); break; } else { int count = GetMineCount(mine, x, y);//该位置不是雷,就统计这个坐标周围有⼏个雷 show[x][y] = count + '0'; DisplayBoard(show, row, col); win++; } } else printf("输入错误,请重新输入\n"); } if (win == row * col - EASY_COUNT) { printf("恭喜你,成功了\n"); DisplayBoard(mine, row, col); } }
此函数应该放在FindMine函数前,它只在game.c文件中被FindMine函数引用
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
count += (mine[x + i][y + j] - '0');//数组mine是字符型数组,返回的ASCII值减去0的ASCII值为字符本身所表示的数字
}
}
return count;
}
变量的定义和函数的声明
#include <stdio.h>
#include <stdlib.h>
#include <time.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 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, );
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
包含头文件 #include <stdlib.h>
所用库函数system( );
system("cls");
因为使用后会直接清屏,所以需要一些技巧
位置:test.c文件
所用函数:Select函数、menu2函数、WinMine函数、FindMine函数(改)
因为新的难度需要更多的行数和地雷实现,所以行数、地雷个数要增加定义,而二维数组,因为定义数组设置大小只能用常量,所以为了避免越界,我们将棋盘大小设为三种难度中棋盘最大的那一个
#define EASY_COUNT 10 #define EASY_ROW 9 #define EASY_COL 9 #define EASY_ROWS EASY_ROW+2 #define EASY_COLS EASY_COL+2 #define MID_COUNT 40 #define MID_ROW 16 #define MID_COL 16 #define MID_ROWS MID_ROW+2 #define MID_COLS MID_COL+2 #define DIF_COUNT 99 #define DIF_ROW 16 #define DIF_COL 30 #define DIF_ROWS DIF_ROW+2 #define DIF_COLS DIF_COL+2
之前数组的全部名称都要由
char name[ROWS][COLS]
变为char name[DIF_ROWS][DIF_COLS]
void menu2()
{
printf("********************\n");
printf("***** 1.简单 *****\n");
printf("***** 2.中等 *****\n");
printf("***** 3.困难 *****\n");
printf("********************\n");
}
void select() { int input = 0; int i = 0; do { menu2(); printf("请选择>:"); scanf("%d", &input); system("cls"); switch (input) { case 1: ROW = EASY_ROW; COL = EASY_COL; ROWS = EASY_ROWS; COLS = EASY_COLS; count = EASY_COUNT; i = 0; break; case 2: ROW = MID_ROW; COL = MID_COL; ROWS = MID_ROWS; COLS = MID_COLS; count = MID_COUNT; i = 0; break; case 3: ROW = DIF_ROW; COL = DIF_COL; ROWS = DIF_ROWS; COLS = DIF_COLS; count = DIF_COUNT; i = 0; break; default: printf("选择错误,请重新选择\n"); i = 1; break; } } while (i); }
WinMine函数返回的值是row * col - count,其中row、col、count分别为不同难度中的行、列、地雷个数;同时WinMine函数应该在FindMine函数前运行
int WinMine(char show[DIF_ROWS][DIF_COLS], int row, int col)
{
int i = 0;
int j = 0;
int win = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if(show[i][j] !='*'&& show[i][j] != '!')
win++;
}
}
return win;
}
void FindMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col, int count) { int x = 0; int y = 0; int i = 0; while (WinMine(show,row,col) < row * col - count) { printf("请输入坐标>:"); scanf("%d %d", &x, &y); system("cls"); if (x > 0 && x <= row && y > 0 && y <= col) { if (mine[x][y] == '1') { DisplayBoard(show, row, col); printf("请输入坐标>:%d %d", x, y); DisplayBoard(mine, row, col); printf("游戏失败\n"); printf("输入1继续>"); scanf("%d", &i); system("cls"); break; } else { int count = GetMineCount(mine, x, y); show[x][y] = count + '0'; //DisplayBoard(mine, row, col); DisplayBoard(show, row, col); } } else { DisplayBoard(show, row, col); printf("输入错误,请重新输入\n"); } } if (WinMine(show, row, col) == row * col - count) { printf("恭喜你,成功了\n"); DisplayBoard(mine, row, col); printf("输入1继续>"); scanf("%d", &i); system("cls"); } }
位置:game.c文件
所用函数:MarkMine函数、Mark函数、Unmark函数、FindMine函数(2改)
在FindMine函数运行时(即排查地雷输入坐标时),输入0 0,进入标记地雷的函数
void FindMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col, int count) { int x = 0; int y = 0; int i = 0; while (WinMine(show,row,col) < row * col - count) { printf("输入0 0进行标记\n"); printf("请输入坐标>:"); scanf("%d %d", &x, &y); system("cls"); if (x > 0 && x <= row && y > 0 && y <= col) { if (mine[x][y] == '1') { DisplayBoard(show, row, col); printf("请输入坐标>:%d %d", x, y); DisplayBoard(mine, row, col); printf("游戏失败\n"); printf("输入1继续>"); scanf("%d", &i); system("cls"); break; } else { int count = GetMineCount(mine, x, y); show[x][y] = count + '0'; //DisplayBoard(mine, row, col); DisplayBoard(show, row, col); } } else if (x == 0 && y == 0) { MarkMine(mine, show, row, col); } else { DisplayBoard(show, row, col); printf("输入错误,请重新输入\n"); } } if (WinMine(show, row, col) == row * col - count) { printf("恭喜你,成功了\n"); DisplayBoard(mine, row, col); printf("输入1继续>"); scanf("%d", &i); system("cls"); } }
在选择1时进入Mark函数,选择2时进入Unmark函数,所以这两个函数要在MarkMine函数前
void MarkMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col) { int input = 0; int i = 0; do { DisplayBoard(show, row, col); printf("1.进行标记\n2.取消标记\n0.退出标记\n"); printf("请选择>:"); scanf("%d", &input); switch (input) { case 1: Mark(show, row, col); break; case 2: Unmark(show, row, col); break; case 0: system("cls"); DisplayBoard(show, row, col); break; default: printf("选择错误,请重新选择\n"); printf("输入1继续>"); scanf("%d", &i); system("cls"); break; } } while (input); }
要对已标记,已排查的坐标进行说明,同时要有退出的功能
void Mark(char show[DIF_ROWS][DIF_COLS], int row, int col) { int x = 0; int y = 0; int i = 0; while(1) { system("cls"); DisplayBoard(show, row, col); printf("正在进行标记,输入0 0退出\n"); printf("请输入要标记的坐标>:"); scanf("%d %d", &x, &y); if (x > 0 && x <= row && y > 0 && y <= col) { if (show[x][y] == '*') { show[x][y] = '!'; system("cls"); break; } else if (show[x][y] == '!') { printf("已标记,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } else { printf("已排查,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } } else if (x == 0 && y == 0) { system("cls"); break; } else printf("输入错误,请重新输入\n"); } }
要对未标记,已排查的坐标进行说明,同时要有退出的功能,不然在游戏还未进行标记时,会出现无法退出的情况
void Unmark(char show[DIF_ROWS][DIF_COLS], int row, int col) { int x = 0; int y = 0; int i = 0; while (1) { system("cls"); DisplayBoard(show, row, col); printf("正在取消标记,输入0 0退出\n"); printf("请输入要取消标记的坐标>:"); scanf("%d %d", &x, &y); if(x > 0 && x <= row && y > 0 && y <= col) { if (show[x][y] == '!') { show[x][y] = '*'; system("cls"); break; } else if (show[x][y] == '*') { printf("未标记,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } else { printf("已排查,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } } else if (x == 0 && y == 0) { system("cls"); break; } else printf("输入错误,请重新输入\n"); } }
位置:game.c文件
所用函数:SpreadMine函数、FindMine函数(2改)
会用到递推函数
注意:这里的show[m][n] == '*'
不能改写为show[m][n] != '0'
,因为我们创建的二维数组行列数比实际棋盘的行列数均多出两行,而多出的两行初始化均为字符0
void SpreadMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col, int x, int y) { if (GetMineCount(mine, x - 1, y) == 0 && show[x - 1][y] == '*') { show[x - 1][y] = '0'; //x = x - 1; SpreadMine(mine, show, row, col, x - 1, y); } if (GetMineCount(mine, x + 1, y) == 0 && show[x + 1][y] == '*') { show[x + 1][y] = '0'; //x = x + 1; SpreadMine(mine, show, row, col, x + 1, y); } if (GetMineCount(mine, x, y - 1) == 0 && show[x][y - 1] == '*') { show[x ][y - 1] = '0'; //y = y - 1; SpreadMine(mine, show, row, col, x, y - 1); } if (GetMineCount(mine, x, y + 1) == 0 && show[x][y + 1] == '*') { show[x][y + 1] = '0'; //y = y + 1; SpreadMine(mine, show, row, col, x, y + 1); } }
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" int ROW = 0; int COL = 0; int ROWS = 0; int COLS = 0; int count = 0; //菜单1 void menu1() { printf("********************\n"); printf("***** 1.paly *****\n"); printf("***** 0.exit *****\n"); printf("********************\n"); } //菜单2 void menu2() { printf("********************\n"); printf("***** 1.简单 *****\n"); printf("***** 2.中等 *****\n"); printf("***** 3.困难 *****\n"); printf("********************\n"); } //难度选择 void select() { int input = 0; int i = 0; do { menu2(); printf("请选择>:"); scanf("%d", &input); system("cls"); switch (input) { case 1: ROW = EASY_ROW; COL = EASY_COL; ROWS = EASY_ROWS; COLS = EASY_COLS; count = EASY_COUNT; i = 0; break; case 2: ROW = MID_ROW; COL = MID_COL; ROWS = MID_ROWS; COLS = MID_COLS; count = MID_COUNT; i = 0; break; case 3: ROW = DIF_ROW; COL = DIF_COL; ROWS = DIF_ROWS; COLS = DIF_COLS; count = DIF_COUNT; i = 0; break; default: printf("选择错误,请重新选择\n"); i = 1; break; } } while (i); } //游戏运行 void game() { select(); char mine[DIF_ROWS][DIF_COLS];//存放布置好的雷 char show[DIF_ROWS][DIF_COLS];//存放排查出的雷的信息 //初始化面板(棋盘) //初始化数组mine中全为字符0 //初始化数组show中全为字符* InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*'); //展示面板(打印棋盘) DisplayBoard(show, ROW, COL); //DisplayBoard(mine, ROW, COL); //布置地雷 SetMine(mine, ROW, COL, count); //DisplayBoard(show, ROW, COL); //DisplayBoard(mine, ROW, COL); //排查显示地雷的信息 FindMine(mine,show, ROW, COL, count); } //选择开始 void test() { int input = 0; srand((unsigned int)time(NULL)); do { menu1(); printf("请选择>:"); scanf("%d", &input); switch (input) { case 1: system("cls"); game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); } int main() { test(); return 0; }
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" //初始化面板 void InitBoard(char Board[DIF_ROWS][DIF_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; } } } //展示面板 void DisplayBoard(char Board[DIF_ROWS][DIF_COLS], int row, int col) { int i = 0; int j = 0; printf("\n-------------扫雷-------------\n"); for (i = 0; i <= col; i++) { printf("%2d ", i); } printf("\n"); for (i = 1; i <= row; i++) { printf("%2d ", i); for (j = 1; j <= col; j++) { printf("%2c ", Board[i][j]); } printf("\n"); } } //布置地雷 void SetMine(char Board[DIF_ROWS][DIF_COLS], int row, int col, int count) { int x = 0; int y = 0; while (count) { x = rand() % row + 1; y = rand() % col + 1; if (Board[x][y] != '1') { Board[x][y] = '1'; count--; } } } //得到输入坐标周围地雷个数 int GetMineCount(char mine[DIF_ROWS][DIF_COLS], int x, int y) { int i = 0; int j = 0; int count = 0; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { count += (mine[x + i][y + j] - '0'); } } return count; } //排查周围,不是雷,进行扩展 void SpreadMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col, int x, int y) { if (GetMineCount(mine, x - 1, y) == 0 && show[x - 1][y] == '*') { show[x - 1][y] = '0'; //x = x - 1; SpreadMine(mine, show, row, col, x - 1, y); } if (GetMineCount(mine, x + 1, y) == 0 && show[x + 1][y] == '*') { show[x + 1][y] = '0'; //x = x + 1; SpreadMine(mine, show, row, col, x + 1, y); } if (GetMineCount(mine, x, y - 1) == 0 && show[x][y - 1] == '*') { show[x ][y - 1] = '0'; //y = y - 1; SpreadMine(mine, show, row, col, x, y - 1); } if (GetMineCount(mine, x, y + 1) == 0 && show[x][y + 1] == '*') { show[x][y + 1] = '0'; //y = y + 1; SpreadMine(mine, show, row, col, x, y + 1); } } //进行标记 void Mark(char show[DIF_ROWS][DIF_COLS], int row, int col) { int x = 0; int y = 0; int i = 0; while(1) { system("cls"); DisplayBoard(show, row, col); printf("正在进行标记,输入0 0退出\n"); printf("请输入要标记的坐标>:"); scanf("%d %d", &x, &y); if (x > 0 && x <= row && y > 0 && y <= col) { if (show[x][y] == '*') { show[x][y] = '!'; system("cls"); break; } else if (show[x][y] == '!') { printf("已标记,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } else { printf("已排查,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } } else if (x == 0 && y == 0) { system("cls"); break; } else printf("输入错误,请重新输入\n"); } } //取消标记 void Unmark(char show[DIF_ROWS][DIF_COLS], int row, int col) { int x = 0; int y = 0; int i = 0; while (1) { system("cls"); DisplayBoard(show, row, col); printf("正在取消标记,输入0 0退出\n"); printf("请输入要取消标记的坐标>:"); scanf("%d %d", &x, &y); if(x > 0 && x <= row && y > 0 && y <= col) { if (show[x][y] == '!') { show[x][y] = '*'; system("cls"); break; } else if (show[x][y] == '*') { printf("未标记,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } else { printf("已排查,请重新输入\n"); printf("输入1确定>"); scanf("%d", &i); system("cls"); } } else if (x == 0 && y == 0) { system("cls"); break; } else printf("输入错误,请重新输入\n"); } } //标记地雷 void MarkMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col) { int input = 0; int i = 0; do { DisplayBoard(show, row, col); printf("1.进行标记\n2.取消标记\n0.退出标记\n"); printf("请选择>:"); scanf("%d", &input); switch (input) { case 1: Mark(show, row, col); break; case 2: Unmark(show, row, col); break; case 0: system("cls"); DisplayBoard(show, row, col); break; default: printf("选择错误,请重新选择\n"); printf("输入1继续>"); scanf("%d", &i); system("cls"); break; } } while (input); } //游戏胜利所需要的值 int WinMine(char show[DIF_ROWS][DIF_COLS], int row, int col) { int i = 0; int j = 0; int win = 0; for (i = 1; i <= row; i++) { for (j = 1; j <= col; j++) { if(show[i][j] !='*'&& show[i][j] != '!') win++; } } return win; } //排查显示地雷的信息 void FindMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col, int count) { int x = 0; int y = 0; int i = 0; while (WinMine(show,row,col) < row * col - count) { printf("输入0 0进行标记\n"); printf("请输入坐标>:"); scanf("%d %d", &x, &y); system("cls"); if (x > 0 && x <= row && y > 0 && y <= col) { if (mine[x][y] == '1') { DisplayBoard(show, row, col); printf("请输入坐标>:%d %d", x, y); DisplayBoard(mine, row, col); printf("游戏失败\n"); printf("输入1继续>"); scanf("%d", &i); system("cls"); break; } else { int count = GetMineCount(mine, x, y); show[x][y] = count + '0'; SpreadMine(mine, show, row, col, x, y); //DisplayBoard(mine, row, col); DisplayBoard(show, row, col); } } else if (x == 0 && y == 0) { MarkMine(mine, show, row, col); } else { DisplayBoard(show, row, col); printf("输入错误,请重新输入\n"); } } if (WinMine(show, row, col) == row * col - count) { printf("恭喜你,成功了\n"); DisplayBoard(mine, row, col); printf("输入1继续>"); scanf("%d", &i); system("cls"); } }
#pragma once #include <stdio.h> #include <stdlib.h> #include <time.h> //#define EASY_COUNT 10 //#define ROW 9 //#define COL 9 //#define ROWS ROW+2 //#define COLS COL+2 #define EASY_COUNT 10 #define EASY_ROW 9 #define EASY_COL 9 #define EASY_ROWS EASY_ROW+2 #define EASY_COLS EASY_COL+2 #define MID_COUNT 40 #define MID_ROW 16 #define MID_COL 16 #define MID_ROWS MID_ROW+2 #define MID_COLS MID_COL+2 #define DIF_COUNT 99 #define DIF_ROW 16 #define DIF_COL 30 #define DIF_ROWS DIF_ROW+2 #define DIF_COLS DIF_COL+2 void InitBoard(char Board[DIF_ROWS][DIF_COLS], int rows, int cols, char set); void DisplayBoard(char Board[DIF_ROWS][DIF_COLS], int row, int col); void SetMine(char Board[DIF_ROWS][DIF_COLS], int row, int col, int count); void FindMine(char mine[DIF_ROWS][DIF_COLS], char show[DIF_ROWS][DIF_COLS], int row, int col, int count);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。