C语言递归版扫雷
绅士·永 人气:0思路
清晰的逻辑
为方便将其分为三个文件:text.c(测试) game.c(函数实现) game.h(头文件声明)
在排雷的时候为了方便,我们需要将每一行每一列对应的行数,列数打印出来。
#define LEI 10 #define ROW 10 #define LOW 10 #define ROWS ROW+2 #define LOWS LOW+2 //在定义棋盘的长宽时,特意加上2,便于标记行数列数。
菜单
打印的菜单只需要有开始游戏、退出游戏的选项即可
void menu() { printf("*************************************\n"); printf("************1.开始游戏***************\n"); printf("************0.退出游戏***************\n"); printf("*************************************\n"); }
棋盘
1.雷盘
2.棋盘
扫雷需要先记录雷的信息再进行排雷,如果使用一个棋盘太过于复杂,所以我们使用两个棋盘,一个用于布置雷,一个用于玩家排雷。
两个棋盘初始化
布置雷的棋盘初始化,将字符‘0’作为非雷,字符‘1’作为雷。
玩家盘将字符‘*’作为还没有扫的地方
board(arr1, ROWS, LOWS, '0');//雷盘 board(arr2, ROWS, LOWS, '*');//玩家盘
因为两个的初始化方式不同,所以我们采用传参ret初始化
//初始化棋盘 void board(char arr1[ROWS][LOWS], int rows, int lows, char ret) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < lows; j++) { arr1[i][j] = ret; } }
布置雷
布置的雷放置需要随机,所以采用两个随机数来定位坐标。
//布置雷 void Get_lei(char arr1[ROWS][LOWS], int row, int low) { int count = LEI; while (count) { int x = rand() % row + 1; int y = rand() % low + 1; if (arr1[x][y] == '0') { arr1[x][y] = '1'; count--; } } //displayboard(arr1, ROW, LOW);//用于测试 }
排雷
当我们输入一个坐标时,我们需要知道这个坐标周围雷的个数,定义一个Get_num函数来获取雷个数。但此时只能获取一个坐标的信息,我们知道一般的扫雷,如果当前坐标雷的个数为0,就会展开,这个过程较为复杂,所以我们使用递归来实现
//玩家盘 static int Get_num(char arr1[ROWS][LOWS],int x, int y)//获得当前坐标周围雷的个数 { int count = 0; int i = 0; for (i = x - 1; i <= x + 1; i++) { int j = 0; for (j = y - 1; j <= y + 1; j++) { if (arr1[i][j] == '1') { count++; } } } return count; } //判断是否展开,实现函数 static void Judge(char arr2[ROWS][LOWS], char arr1[ROWS][LOWS], int x, int y) { if (x > 0 && x <= ROW && y > 0 && y <= LOW) { int ret = Get_num(arr1, x, y); if (ret != 0) arr2[x][y] = ret + '0';//记录雷的个数 //递归散开 else if (arr1[x][y] != ' ') { arr2[x][y] = '0'; arr1[x][y] = ' '; int i = 0; for (i = x - 1; i <= x + 1; i++) { int j = 0; for (j = y - 1; j <= y + 1; j++) { Judge(arr2, arr1, i, j); } } } else { return; } } }
判断输赢
输:即每排一次雷,检查一下雷盘对应的信息,如果是雷,就被炸死,如果不是,就继续排雷。
赢:当玩家将所有的非雷的区域都排查出来时,判断为赢。(这里采用一个计数器,没排一次雷计数器就++一下,当计数器与总的非雷的区域数目相同时,判断为赢)
void Out_lei(char arr2[ROWS][LOWS], int row, int low, char arr1[ROWS][LOWS]) { int x = 0; int y = 0; while (1) { printf("请输入坐标:>"); scanf("%d,%d", &x, &y); if (x >= 1 && x <= ROW && y >= 1 && y <= LOW) { if (arr1[x][y] == '1') { arr2[x][y] = '#'; displayboard(arr2, ROW, LOW);//排雷 printf("遗憾你输了\n"); break; } else { Judge(arr2, arr1, x, y); displayboard(arr2, ROW, LOW);//排雷 } } else { printf("输入错误!\n"); } //判断扫雷是否赢 int i = 0, flag = 0; for (i = 1; i <= ROW; i++) { int j = 0; for (j = 1; j <= LOW; j++) { if (arr2[i][j] != '*') { flag++; } } } if (flag == ROW*LOW - LEI) { printf("你赢了!\n"); break; } } }
text.c实现
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" //菜单 void menu() { printf("*************************************\n"); printf("************1.开始游戏***************\n"); printf("************0.退出游戏***************\n"); printf("*************************************\n"); } void game() { //初始化棋盘 char arr1[ROWS][LOWS] = { 0 };//雷盘 char arr2[ROWS][LOWS] = { 0 };//玩家盘 board(arr1, ROWS, LOWS, '0'); board(arr2, ROWS, LOWS, '*'); //打印棋盘 //displayboard(arr1, ROW, LOW);//布置雷 displayboard(arr2, ROW, LOW);//排雷 //布置雷 Get_lei(arr1,ROW,LOW); //排雷 Out_lei(arr2,ROW,LOW, arr1); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>"); scanf("%d",&input); switch (input) { case 1: { printf("扫雷\n"); 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" //初始化棋盘 void board(char arr1[ROWS][LOWS], int rows, int lows, char ret) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < lows; j++) { arr1[i][j] = ret; } } } //打印棋盘 void displayboard(char arr1[ROWS][LOWS], int row, int low) { printf("<———扫雷游戏———>\n"); int i = 0; for (i = 1; i <= row; i++) { int j = 0; if (i == 1) { for (j = 0; j <= low; j++) { printf("%2d ", j); } printf("\n"); } for (j = 1; j <= low; j++) { if (j == 1) { printf("%2d ", i); } printf("%2c ", arr1[i][j]); } printf("\n"); } printf("<———扫雷游戏———>\n"); } //布置雷 void Get_lei(char arr1[ROWS][LOWS], int row, int low) { int count = LEI; while (count) { int x = rand() % row + 1; int y = rand() % low + 1; if (arr1[x][y] == '0') { arr1[x][y] = '1'; count--; } } //displayboard(arr1, ROW, LOW); } //玩家盘 static int Get_num(char arr1[ROWS][LOWS],int x, int y) { int count = 0; int i = 0; for (i = x - 1; i <= x + 1; i++) { int j = 0; for (j = y - 1; j <= y + 1; j++) { if (arr1[i][j] == '1') { count++; } } } return count; } //判断是否展开,实现函数 static void Judge(char arr2[ROWS][LOWS], char arr1[ROWS][LOWS], int x, int y) { if (x > 0 && x <= ROW && y > 0 && y <= LOW) { int ret = Get_num(arr1, x, y); if (ret != 0) arr2[x][y] = ret + '0'; //递归散开 else if (arr1[x][y] != ' ') { arr2[x][y] = '0'; arr1[x][y] = ' '; int i = 0; for (i = x - 1; i <= x + 1; i++) { int j = 0; for (j = y - 1; j <= y + 1; j++) { Judge(arr2, arr1, i, j); } } } else { return; } } } void Out_lei(char arr2[ROWS][LOWS], int row, int low, char arr1[ROWS][LOWS]) { int x = 0; int y = 0; while (1) { printf("请输入坐标:>"); scanf("%d,%d", &x, &y); if (x >= 1 && x <= ROW && y >= 1 && y <= LOW) { if (arr1[x][y] == '1') { arr2[x][y] = '#'; displayboard(arr2, ROW, LOW);//排雷 printf("遗憾你输了\n"); break; } else { Judge(arr2, arr1, x, y); displayboard(arr2, ROW, LOW);//排雷 } } else { printf("输入错误!\n"); } //判断扫雷是否赢 int i = 0, flag = 0; for (i = 1; i <= ROW; i++) { int j = 0; for (j = 1; j <= LOW; j++) { if (arr2[i][j] != '*') { flag++; } } } if (flag == ROW*LOW - LEI) { printf("你赢了!\n"); break; } } }
game.h实现
#pragma once #include <stdio.h> #include <stdlib.h> #define LEI 10 #define ROW 10 #define LOW 10 #define ROWS ROW+2 #define LOWS LOW+2 //初始化棋盘 void board(char arr1[ROWS][LOWS],int rows,int lows,char ret); //打印棋盘 void displayboard(char arr1[ROWS][LOWS], int row, int low); //布置雷 void Get_lei(char arr1[ROWS][LOWS], int row, int low); //玩家盘 void Out_lei(char arr2[ROWS][LOWS], int row, int low, char arr1[ROWS][LOWS]);
递归部分详解
递归条件:1.有停止的条件。2.每一次递归都会向这个条件靠拢。
那么这里的停止条件是什么呢?
递归:当返回雷的个数为0时,就符合继续递归的条件,我们需要将当前坐标周围的点全部排除。且需要将已经排查了的坐标做一个标记,否则就会不停的排查下去,就会形成死递归。所以
停止条件:当前这个坐标是已被排查过的,就停止递归。
因为每一次排查都会的一个标记,所以这就是那个不断向停止条件靠拢的过程。
//判断是否展开,实现函数 static void Judge(char arr2[ROWS][LOWS], char arr1[ROWS][LOWS], int x, int y) { if (x > 0 && x <= ROW && y > 0 && y <= LOW) { int ret = Get_num(arr1, x, y); if (ret != 0) arr2[x][y] = ret + '0'; //递归散开 else if (arr1[x][y] != ' ') { arr2[x][y] = '0';//玩家盘 arr1[x][y] = ' ';//雷盘 int i = 0; for (i = x - 1; i <= x + 1; i++) { int j = 0; for (j = y - 1; j <= y + 1; j++) { Judge(arr2, arr1, i, j); } } } else { return; } } }
总结
加载全部内容