一,正常扫雷
1,开局
经典开局是40*16的格子,所有格子都是未知的,里面有99个雷
2,操作
正常的扫雷,是串行的,即一个回合只能对一个格子进行操作。
扫雷有4种操作:左键单击打开、右键单击标记,右键单击取消标记、左键双击。
具体来说,左键双击的效果就是,把该格子周围未标记的所有格子都打开。
四种操作对操作位置都有要求,不满足要求的格子是无效操作。
而一个回合有多少个格子的状态发生改变,取决于雷的分布,后台会自动进行递归。
3,自动递归
如果点开一个格子,周围没有雷,则自动执行左键双击。
我这个描述可能和读者在其他地方看到的不太一样,但是是等价的。
4,胜负
只要所有非雷的格子点开即可获胜。
不看标记。
点开任意一个雷所在的格子立刻判负。
二,串行扫雷
1,规则微调
(1)取消右键单击取消标记,改成只要标记错误立刻判负。
(2)取消左键双击功能的操作位置校验,改成只要双击就把该格子以及周围未标记的所有格子都打开。
显然,微调之后只是不允许误操作而已,实际上规则是等价的,玩的难度也是一样的。
微调的好处就是代码实现的难度降低了很多。
2,扫雷代码V1
#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <functional>
#include <algorithm>
#include <vector>
#include <queue>
#include <numeric>
#include <map>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <array>
#include <functional>
#include <string.h>
#include <math.h>
#include <fstream>
#include <streambuf>
#include <stdio.h>
#include <mutex>
#include <unordered_map>
#include <iostream>
#include <vector>
#include <iomanip>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <functional>
#include <limits>
#include <climits>
#include <stdint.h>
#include <windows.h>
using namespace std;
#define LOCAL
int ROW = 16;
int COL = 40;
int BOMB = 99;
enum Nod {
EmptyGrid,
BombGrid
};
enum Status {
Unknow,
Open,
Flag
};
vector<vector<int>>v;//雷分布
vector<vector<int>>vsum;//雷计数
vector<vector<int>>vs;//实时界面
bool isWin;
int hseed;//棋谱中的种子
vector<int>hr, hc, ho;//棋谱中的行、列、操作数
void init(int seed)
{
srand(seed);
v.resize(ROW);
vsum.resize(ROW);
vs.resize(ROW);
for (auto& vi : v) {
vi.resize(COL);
for (auto& x : vi)x = EmptyGrid;
}
for (auto& vi : vsum) {
vi.resize(COL);
for (auto& x : vi)x = 0;
}
for (auto& vi : vs) {
vi.resize(COL);
for (auto& x : vi)x = Unknow;
}
int b = BOMB;
while (b) {
int r = rand() % ROW;
int c = rand() % COL;
if (v[r][c] == BombGrid)continue;
v[r][c] = BombGrid;
b--;
for (int i = max(0,r - 1); i <= min(r + 1,ROW-1); i++) {
for (int j = max(0, c - 1); j <= min(c + 1, COL - 1); j++){
vsum[i][j]++;
}
}
}
isWin = false;
}
//鼠标左键单击,是否阵亡
bool clickLeft(int r, int c)
{
if (v[r][c] == BombGrid) {
return true;
}
if (vs[r][c] == Open)return false;
vs[r][c] = Open;
if (vsum[r][c])return false;
for (int i = max(0, r - 1); i <= min(r + 1, ROW - 1); i++) {
for (int j = max(0, c - 1); j <= min(c + 1, COL - 1); j++) {
clickLeft(i, j);
}
}
return false;
}
//鼠标右键单击,是否阵亡
bool clickRight(int r, int c)
{
vs[r][c] = Flag;
return v[r][c] != BombGrid;
}
//鼠标左键双击,是否阵亡
bool clickDouble(int r, int c)
{
for (int i = max(0, r - 1); i <= min(r + 1, ROW - 1); i++) {
for (int j = max(0, c - 1); j <= min(c + 1, COL - 1); j++) {
if (vs[i][j] != Flag && clickLeft(i, j))return true;
}
}
return false;
}
//1左键单击,2右键单击,3左键双击,4显示棋谱,返回是否结束
bool opt(int r, int c, int optNum)
{
if (optNum >= 1 && optNum <= 3) {
hr.push_back(r);
hc.push_back(c);
ho.push_back(optNum);
}
if (optNum == 1 && clickLeft(r, c))return true;
if (optNum == 2 && clickRight(r, c))return true;
if (optNum == 3 && clickDouble(r, c))return true;
if(optNum==4){
cout << hseed << endl;
for (int i = 0; i < hr.size(); i++) {
cout << hr[i] << " " << hc[i] << " " << ho[i] << " ";
if (i % 5 == 4)cout << endl;
}
Sleep(5000);
return false;
}
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (vs[i][j] == Unknow && v[i][j]== EmptyGrid) {
return false;
}
}
}
return isWin = true;
}
void show()
{
system("cls");
cout << " 0 1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 ";
cout << "22 24 26 28 30 32 34 36 38" << endl;
int flagNum = 0;
for (int i = 0; i < ROW; i++) {
cout << i << " ";
if (i < 10)cout << " ";
for (int j = 0; j < COL; j++) {
if (vs[i][j] == Unknow)cout << "■";
else if (vs[i][j] == Open) {
if (vsum[i][j])cout << vsum[i][j]<<"_";
else cout << "□";
}
else {
cout << "●";
flagNum++;
}
}
cout << endl;
}
cout << "还剩" << BOMB - flagNum << "雷未标记";
cout << endl;
}
void showEnd()
{
cout << " 0 1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 ";
cout << "22 24 26 28 30 32 34 36 38" << endl;
for (int i = 0; i < ROW; i++) {
cout << i << " ";
if (i < 10)cout << " ";
for (int j = 0; j < COL; j++) {
if (v[i][j] == BombGrid)cout << "●";
else cout << "□";
}
cout << endl;
}
cout << endl;
opt(0, 0, 4);
}
bool run()
{
show();
cout << "输入行 列 操作数" << endl;
cout << "1左键单击,2右键单击,3左键双击,4显示棋谱" << endl;
int r, c, optNum;
cin >> r >> c >> optNum;
return opt(r, c, optNum);
}
int main()
{
cout << "输入随机数种子" << endl;
cin >> hseed;
init(hseed);
while(!run());
if (isWin)cout << "Win!!!" << endl;
else cout << "Lose..." << endl;
showEnd();
return 0;
}
3,运行效果
4,通关棋谱
我玩的第一个种子就是123456789,因为有棋谱功能,所以可以随时手动添加回溯点,不小心挂了可以恢复,这样很容易就得到一个通关的棋谱。
123456789
10 10 1 15 29 1 15 32 1 7 14 2 7 15 3
6 17 2 6 16 3 4 16 2 4 17 2 5 17 3
5 18 3 6 20 2 5 19 3 5 23 1 4 22 1
4 23 1 4 24 1 5 26 1 6 26 1 8 26 2
9 26 2 9 25 3 10 29 1 9 30 2 9 29 3
5 29 2 6 30 3 8 31 2 7 31 3 8 30 3
10 29 3 10 31 2 11 31 2 9 31 3 11 30 3
13 30 2 12 30 3 10 27 2 11 26 2 12 27 3
12 23 1 10 21 2 11 22 2 10 22 3 10 17 2
10 16 3 11 18 3 12 20 2 11 21 3 12 22 3
12 24 2 13 24 2 13 23 3 15 24 2 11 25 3
13 25 2 14 24 3 14 28 2 0 18 2 1 18 2
2 17 3 3 19 2 2 18 3 2 19 3 5 24 2
5 25 3 4 27 2 5 28 3 0 22 2 1 23 3
1 30 2 0 29 3 0 30 3 2 30 3 2 32 2
4 31 2 3 31 3 4 32 3 6 33 2 6 32 3
8 33 2 7 33 3 7 34 3 8 36 2 7 36 3
8 35 3 9 36 3 9 37 3 7 38 2 6 37 3
8 39 3 7 39 3 5 39 2 4 34 2 3 32 3
4 33 3 5 35 3 4 37 2 5 37 3 5 38 3
4 39 3 1 32 2 0 31 3 0 32 3 2 34 2
1 34 2 0 35 3 0 39 2 3 35 2 3 24 2
4 23 3 5 21 2 12 21 3 14 20 2 14 21 3
15 20 3 9 33 2 9 32 3 10 34 1 11 34 1
11 37 1 11 36 2 11 38 2 10 39 3 11 39 3
12 39 3 12 37 2 13 37 2 14 37 2 12 36 2
10 35 3 11 33 2 10 32 3 11 32 3 11 34 3
12 35 3 13 35 3 14 36 3 15 34 2 13 33 2
13 32 2 14 33 2 14 34 3 11 14 2 12 12 2
11 13 3 11 17 3 13 16 2 12 15 2 12 16 3
12 11 3 14 12 2 13 12 3 13 14 2 13 13 3
15 12 2 14 15 1 15 15 1 15 13 2 15 18 2
4 11 2 3 11 3 4 9 2 5 8 3 4 10 3
2 9 2 3 8 2 2 8 2 4 7 3 2 7 3
0 8 2 1 10 2 1 9 2 1 8 3 0 9 3
2 2 2 2 1 1 2 1 3 0 1 2 1 1 3
3 1 3 4 3 2 4 2 3 5 0 2 5 1 3
6 4 2 5 4 3 6 6 2 6 5 3 8 6 2
8 7 2 7 7 3 8 9 2 9 8 2 8 8 3
8 7 3 11 5 2 10 5 3 11 3 2 11 4 3
12 3 3 11 2 3 11 0 2 14 4 2 13 4 3
14 6 2 15 7 3 15 6 3 15 4 2 14 3 3
15 1 2 14 0 3 15 28 1 15 30 1 15 27 1
14 26 2 15 27 3 14 29 2 15 29 3 14 32 2
13 31 3 14 39 1 15 38 1 15 39 1
三,纯推理开局
1,定义
对于任意局面,只要开局的时候多点开几个格子,点到合适的格子,然后再开始推理,那么就一定可以做到,在推理过程中全程都是确定的推理结果,不需要概率运算。
即,纯推理,无猜测。
2,实例
对于扫雷代码V1+123456789这个种子来说,开局点开3个格子:10 10 、 15 29 、15 32,就能得到一个纯推理开局。