最终效果图(部分)
上方先感慨一下:实属不易,研究了较长时间最终成功了= =
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"扫雷.h"
void menu()
{
printf("******************\n");
printf("***** 1. game ****\n");
printf("***** 0. exit ****\n");
printf("******************\n");
}
// 安排两个二维数组,一个储存雷的信息,一个储存排查的信息
void game()
{
int number = 0;
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
Init_operation(mine, ROWS, COLS, '0');
Init_operation(show, ROWS, COLS, '*');
set_mine(mine, ROW, COL);
print_board(mine, ROW, COL);
print_board(show, ROW, COL);
check_mine(mine, show, ROW, COL);
//print_show(mine, ROW, COL);
//while (1)
//{
// int ret = check_mine(mine, show, ROW, COL, number);
// if (!ret)
// {
// printf("游戏失败,扫雷失败\n");
// print_show(mine, ROW, COL);
// printf("游戏失败,扫雷失败\n");
// break;
// }
// if (number == (ROW - 2) * (COL - 2) - MINE)
// {
// printf("游戏胜利,扫雷成功\n");
// print_show(mine, ROW, COL);
// printf("游戏胜利,扫雷成功\n");
// break;
// }
// print_show(show, ROW, COL);
//}
}
int main()
{
srand((unsigned int)time(NULL));
int ret = 0;
do
{
menu();
printf("请输入选项:\n");
scanf("%d", &ret);
switch (ret)
{
case 1:
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (ret);
return 0;
}
扫雷.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<ctime>
#include<stdlib.h>
#include<iostream>
using namespace std;
//#define ROW 11
//#define COL 11
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 9
// 初始化操作
void Init_operation(char arr[ROWS][COLS], int row, int col, char flag);
// 布置雷
void set_mine(char arr[ROWS][COLS], int row, int col);
// 打印雷盘
void print_board(char arr[ROWS][COLS], int row, int col);
//排查雷
void check_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
扫雷.cpp
Init_operation
void Init_operation(char arr[ROWS][COLS], int rows, int cols, char flag)
{
// 初始化操作,雷盘初始化为0,打印初始化为*
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = flag;
}
}
}
用于初始化棋盘,mine棋盘初始化为0(后面布置雷为1) ,show初始化为*。注意此处为cols,rows。后面的是row,col。
set_mine?
void set_mine(char arr[ROWS][COLS], int row, int col)
{
int num = MINE;
while (num)
{
int x = rand() % row + 1; //1-9
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
num--;
}
}
}
布置雷
print_board
void print_board(char arr[ROWS][COLS], int row, int col)
{
for (int i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
打印棋盘,里面用的是row,col,而不是一些常量
number_of_mine
int number_of_mine(char mine[ROWS][COLS], int x, int y)
{
// 我改的是show数组 标记的时候改为# 一旦改正成功 ,则mine数组中相应位置的1应该改为0
int ret = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
ret += mine[i][j] - '0';
}
}
return ret;
}
统计非雷坐标周围的雷的个数
check_pro
void check_pro(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int flag = 1;
if (show[x][y] == '*'/* && mine[x][y] != '1'*/) //没有被排查过
{
flag = 0; //0则没有被排查过
}
int ret = number_of_mine(mine, x, y);
if (ret == 0)
show[x][y] = ' ';
else
show[x][y] = ret + '0';
if(flag == 0 && ret == 0)
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
//周围的可以进一步展开的条件是:1. 它周围没有雷 2.它是*
check_pro(mine, show, i, j);
}
}
}
用于递归展开
check_mine
void check_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
//排查雷的功能有:1. 选择某个地方排查, 2. 标记棋子 3. 在排查的时候要递归展开一片
int tag = 0; //旗子个数
int count = 0;//排查出的个数
int input = 0;
int x = 0, y = 0;
while (tag != MINE && count != row * col - MINE)
{
count = 0;
printf("请选择你要进行的操作:\n");
printf("1.check 2.tag\n");
cin >> input;
printf("请输入要操作的位置:\n");
cin >> x >> y;
if ((input == 1 || input == 2) && x >= 1 && x <= row && y >= 1 && y <= col)
{
switch (input)
{
case 1:
if (mine[x][y] != '1') //不是雷
{
int num = number_of_mine(mine, x, y);
//这里的操作是:雷区确实不是雷且没排查过
if (num == 0)
{
check_pro(mine, show, x, y);
print_board(show, row, col); //展开后,打印出show
}
else
{
show[x][y] = num + '0';
print_board(show, row, col);
}
}
else
{
printf("排查失败,%d,%d是雷\n", x, y);
printf("扫雷失败\n");
print_board(mine, row, col);
return;
}
break;
case 2:
if (mine[x][y] == '1') //是雷
{
if (show[x][y] != '#')
tag++;
show[x][y] = '#';
//mine[x][y] = '0'; //!!!!!!!!!!!!!!!!!!!!!
print_board(show, row, col);
}
else
{
printf("排查错误,%d,%d处不为雷\n", x, y);
printf("扫雷失败\n");
print_board(mine, row, col);
return;
}
break;
}
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if (mine[i][j] == '0' && show[i][j] != '*')
count++;
}
}
}
else
{
printf("输入错误\n");
}
}
printf("扫雷成功!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
}
扫雷的主体函数
总结?
1. 有关为什么要用两个数组:一个用于存储雷的信息,一个用于存储扫出的信息。原因就略了,大致是因为这样方便吧。。。
2. 加了插旗子的操作,(和实际扫雷游戏还是有出入),当你判断某个地方确定是雷,可以进行插旗子操作,判断错误则游戏结束。当棋子个数等于雷数时,即全部找出。则胜利
3. 除了上一点中的胜利条件以外,还有一种方法就是,将所有的非雷区域找出即可。
4. 有关递归展开:当坐标xy周围雷数为0才展开,若非0则不展开。与实际游戏相符。 展开的条件:①. 该坐标周围雷数为0? ?②.该坐标之前没有被展开或判断过(若展开过则会形成死递归,若被判断过,无非就是0或非0,也就相当于展开过了)。
5. 有关统计已经被排出的非雷点的方法:最开始用的是变量,每统计出一个就++。但是一直成功不了。后面改为了新办法,比较简单易懂:每次操作后统计一下即可。
待改进
当你把某个地方设为棋子之后,则这个地方相当于排除了,这个雷周围的点如果是1的话,那么再check时应该属于可以展开一片,但是目前这个功能还没实现。
|