编写浏览器迷宫游戏的外挂(误..

背景

xiyoulinux兴趣小组 的免试题蛮有意思的,第三关要求不仅要有脑洞,还是得有代码编写的能力的.
第三关入口

Level_3

打开就是这个样子了…
迷宫这么大,时间肯定不够.
可以,写代码吧…用程序跑

回溯法

一说到走迷宫第一个想到就是回溯法了.(先解决问题,时间还是不够那再想其他办法)

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。


大致想法就是从起点出发,上,下,左,右搜索,哪个能走就走哪个.这条路不通就退回之前的点继续之前的步骤.用一个栈保存之前走过的点就可以了

地图的一部分大概长这样
map

// 地图放在map.txt中
#include <iostream>
#include <vector>
#include <stack>
#include <cstdio>
#include <utility>
#include <unistd.h>

using namespace std;

int main()
{
    vector<vector<char>> map(101, vector<char>(102));
    vector<vector<bool>> route(101, vector<bool>(102, false));

    FILE *fp = fopen("map.txt", "r");

    int ch;
    for(auto &i : map) {
        for(auto &j : i) {
            if((ch = fgetc(fp)) == EOF) {
                break;
            }
            j = ch;
        }
    }

    int x = 1;
    int y = 0;

    route[x][y] = true;
    stack<pair<int, int>> s;

    s.push(make_pair(x, y));
    while(!s.empty() && !(x == 100 && y == 99)) {
        // turn down
        if(x + 1 < 101 && map[x + 1][y] == '.' && route[x + 1][y] == false) {
            x++;
            route[x][y] = true;
            s.push(make_pair(x, y));
        } else if(y + 1 < 102 && map[x][y + 1] == '.' && route[x][y + 1] == false) {
            y++;
            route[x][y] = true;
            s.push(make_pair(x, y));
        } else if(x - 1 > 0 && map[x - 1][y] == '.' && route[x - 1][y] == false) {
            x--;
            route[x][y] = true;
            s.push(make_pair(x, y));
        } else if(y - 1 > 0 && map[x][y - 1] == '.' && route[x][y - 1] == false) {
            y--;
            route[x][y] = true;
            s.push(make_pair(x, y));
        } else {
        // 回溯
            s.pop();
            if(s.empty()) {
                break;
            }
            auto prev_pos = s.top();
            x = prev_pos.first;
            y = prev_pos.second;
        }
    }

// 看看走出来是啥样
    for(auto i: route) {
        for(auto j: i) {
            if(j) {
                cout << '.';
            } else {
                cout << ' ';
            }
        }
        cout << endl;
    }

    return 0;
}

路径打印出来
route

按键模拟

现在有走迷宫算法了,能够走迷宫了,但是我们不可能用手来,因为我们手速太慢了.这个时候希望能有模拟键盘摁下键盘的程序.

搜了搜,发现可以用将键盘摁下的事件写到/dev/input/event`X’ 中.这是个好办法,无论在图形界面下,还是字符模式下,都能模拟键盘.不过Linux每次重启后,内核绑定的event`X’文件都不一样,虽然可以用

cat /proc/bus/input/devices

来查看当前绑定的event`X’,不过还是要频繁改代码..
继续搜,发现可以用 XTextFakeKeyEvent 来模拟键盘输入.
这个玩意是 X11 里面的…


X11 下载开发包

sudo apt-get install xorg-dev -y

这样我们就可以使用X11 里面的Xtest了

#include <stdio.h>
#include <X11/extensions/XTest.h>
#include <X11/Xlib.h>

static Display *dsp;

int vk_init(void)
{
    dsp = XOpenDisplay(NULL);
    if(!dsp) {
        printf("open display failed\n");
        return -1;
    }

    return 0;
}

int vk_presskey(int s)
{
    if(dsp == NULL) {
        return -1;
    }

    KeyCode key = XKeysymToKeycode(dsp, s);
    if(key == NoSymbol)
        return -1;

    XTestFakeKeyEvent(dsp, key, 1, CurrentTime);
    XFlush(dsp);
    return 0;
}

int vk_releasekey(int s)
{
    if(dsp == NULL) {
        return -1;
    }

    KeyCode key = XKeysymToKeycode(dsp, s);

    if(key == NoSymbol) {
        return -1;
    }

    XTestFakeKeyEvent(dsp, key, 0, CurrentTime);
    XFlush(dsp);

    return 0;
}


int vk_kill(void)
{
    if(dsp != NULL) {
        XCloseDisplay(dsp);
        return 0;
    }
    return -1;
}

把它编译成库文件(当然直接编译最后一起链接也是没问题的.(其实就是命令太长… + 懒…))

gcc test.c -lX11 -lXtst -fPIC -shared -o libvirkey.so
# 安装!
sudo cp ./libvirkey.so /usr/lib/

最后我们改下之前的代码,加上摁键模拟就好了

#include <iostream>
#include <vector>
#include <stack>
#include <cstdio>
#include <utility>
#include <unistd.h>

using namespace std;

extern "C" {
    int vk_init();
    int vk_kill();
    int vk_presskey(int);
    int vk_releasekey(int);
}

#define press_W()       \
    vk_presskey('w');   \
    vk_releasekey('w');

#define press_S()       \
    vk_presskey('s');   \
    vk_releasekey('s');

#define press_A()       \
    vk_presskey('a');   \
    vk_releasekey('a');

#define press_D()       \
    vk_presskey('d');   \
    vk_releasekey('d');

const size_t S = 30000;

int main()
{
    vk_init();
    vector<vector<char>> map(101, vector<char>(102));
    vector<vector<bool>> route(101, vector<bool>(102, false));
    stack<pair<int, int>> s;

    FILE *fp = fopen("map.txt", "r");

    int ch;
    for(auto &i : map) {
        for(auto &j : i) {
            if((ch = fgetc(fp)) == EOF) {
                break;
            }
            j = ch;
        }
    }

    int x = 1;
    int y = 0;

    // 为了自己有时间切换到浏览器界面
    sleep(10);

    route[x][y] = true;
    s.push(make_pair(x, y));
    while(!s.empty() && !(x == 100 && y == 99)) {
        // turn down
        if(x + 1 < 101 && map[x + 1][y] == '.' && route[x + 1][y] == false) {
            x++;
            route[x][y] = true;
            s.push(make_pair(x, y));

            // 摁键必须有空隙.不能太快
            usleep(S);
            press_S();
        } else if(y + 1 < 102 && map[x][y + 1] == '.' && route[x][y + 1] == false) {
            y++;
            route[x][y] = true;
            s.push(make_pair(x, y));

            usleep(S);
            press_D();
        } else if(x - 1 > 0 && map[x - 1][y] == '.' && route[x - 1][y] == false) {
            x--;
            route[x][y] = true;
            s.push(make_pair(x, y));

            usleep(S);
            press_W();
        } else if(y - 1 > 0 && map[x][y - 1] == '.' && route[x][y - 1] == false) {
            y--;
            route[x][y] = true;
            s.push(make_pair(x, y));

            usleep(S);
            press_A();
        } else {
            auto curr_pos = s.top();
            s.pop();
            if(s.empty()) {
                break;
            }
            auto prev_pos = s.top();
            x = prev_pos.first;
            y = prev_pos.second;
            if(curr_pos.first - prev_pos.first > 0) {
                // up
                usleep(S);
                press_W();
            } else if(curr_pos.first - prev_pos.first < 0) {
                // down
                usleep(S);
                press_S();
            } else if(curr_pos.second - prev_pos.second > 0) {
                // left
                usleep(S);
                press_A();
            } else if(curr_pos.second - prev_pos.second < 0) {
                // right
                usleep(S);
                press_D();
            }
        }
    }

    vk_kill();
    return 0;
}

编译它

gcc test.cc -lvirkey

打怪途中
打怪升级

快跟我一起贪玩迷宫,打怪升级,装备不花一分钱

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值