最快并行扫雷

一,正常扫雷

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,就能得到一个纯推理开局。

四,并行扫雷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值