扫雷游戏脚本(游戏脚本demo)

1.引言

扫雷作为家喻户晓的Windows游戏,作为经典游戏被广泛娱乐,那么能否做一个自动扫雷的小程序光速通关呢?答案是可以的。甚至可以做到无能能敌的地步,先上图,将记录设置为-8s,因此不存在有人能够突破这个记录了

2.实现方法

1. 实现逻辑

1)创建 WIN32 空项目;

2)打开扫雷进程,根据雷区的行和列所在内存地址,获取行、列的值;

3)根据雷区首地址和雷区范围,读取雷区数据;

4)将扫雷游戏界面置顶,读取雷区第i行第j列的数值,判断是否为地雷,如果不是则模拟鼠标左键单击操作,如果是地雷则模拟鼠标右键单击操作。

5)根据扫雷游戏界面的客户坐标系,计算第i行第j列的坐标位置,然后将鼠标移动到该位置,执行第3步的鼠标操作。

6)重复 3-4,直至雷区数据全部被扫描完成。

2. 相关API

读写内存地址中的内容使用的 WindowsAPI 函数为:

BOOL ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead);

BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten);

读写内存 API 函数需要目标进程的句柄作为参数,为此需要调用 OpenProcess 用于获取进程句柄:

HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);

OpenProcess 需要进程 ID 作为参数,可通过 GetWindowThreadProcessId

数得到:

DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);

GetWindowThreadProcessId 函数需要窗口句柄作为参数,为此可通过 FindWindow 获得,而该 API 函数用到的窗口类和窗口标题名两个参数,已在步骤7中通过 Spy++得到

HWND FindWindowA(LPCSTR lpClassName, LPCSTR lpWindowName);

模拟鼠标操作的 API 函数为 mouse_event,具体使用方式为,使用SetCursorPos API 函数将鼠标移动到指定位置,然后模拟鼠标按下和弹起操作。

函数声明如下:

void mouse_event(DWORD dwFlags, DWORD dx, DWORD dy, DWORD dwData, ULONG_PTR dwExtraInfo);

BOOL SetCursorPos(int X, int Y);

本实验中 mouse_event 函数仅需设置第一项参数即可,具体包括左键按下、弹起(MOUSEEVENTF_LEFTDOWNMOUSEEVENTF_LEFTUP),右键按下、弹起(MOUSEEVENTF_RIGHTDOWNMOUSEEVENTF_RIGHTUP)。 

将某个 GUI 程序界面置顶的 API 函数为:

BOOL SetForegroundWindow(HWND hWnd);

3.脚本实现

确定扫雷游戏格子大小

打开 Spy++,单击监视--日志消息,勾选隐藏 Spy++选项,拖动查找程序工具至扫雷游戏主界面,如图 8-4 所示,显示的内容包括扫雷窗口类(WNDCLASS)的名称以及标题名。

切换至Spy++消息窗口,先清除所有消息,然后勾选 “WM_LBUTTONDOWN WM_LBUTTONUP”两个消息,如图 8-5 所示,单击确定后返回 Spy++主界面。

返回扫雷游戏界面,依次单击左上角、紧邻左上角右方、紧邻左上角下方的三个格子,查看 Spy++记录的鼠标单击时的 xPos 和 yPos 的值,以此计算每个格子的宽度和高度。

首个雷区坐标为:

xPos =14

yPos =58

在Spy++界面,右键--清除消息日志,然后重复上述操作,也可间隔数个格 子单击,以此计算每个格子的平均值,作为每个格子的宽和高度值。格子的宽度为:16 ;格子的高度为:16

#include <windows.h>

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    const char* text = "扫雷";
    HWND hwnd = FindWindow(text, text);
    if (hwnd == NULL)
    {
        MessageBox(NULL, TEXT("Winmine is not found!"), TEXT("AutoMineSweeper"), MB_ICONSTOP);
        return 1;
    }

    DWORD pid;
    GetWindowThreadProcessId(hwnd, &pid);
    HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

    int width, height;
    width = -1;
    height = -1;
    ReadProcessMemory(handle, (LPVOID)0x01005334, &width, 4, NULL);
    ReadProcessMemory(handle, (LPVOID)0x01005338, &height, 4, NULL);

    /*BYTE **data;
    data=(BYTE**)malloc(32 * height * sizeof(BYTE));*/
    //BYTE data[] = new BYTE(32 * height);
    BYTE data[32*32] = { 0 };
    ReadProcessMemory(handle, (LPVOID)0x01005361, &data, 32 * height, NULL);
    
    ShowWindow(hwnd, SW_RESTORE);
    SetForegroundWindow(hwnd);
    Sleep(300);

    int width_num = 32;
    int x_pos = 14;		//雷区首个格子的位置
    int y_pos = 58;

    RECT rc;
BOOL bRet = GetClientRect(hwnd, &rc);
//更改扫雷时间,将其设置为-8
int time = -8;
   	WriteProcessMemory(handle, (LPVOID)0x0100579C, &time, 4, NULL);

    for (int y = 1; y <= height; y++)
    {
        for (int x = 1; x <= width; x++)
        {
            UINT downMsg = x_pos, upMsg = y_pos;
            if (data[width_num * (x - 1) + (y - 1)] == 0x10)
                break;
            else
            {
                if (data[(x - 1) + width_num * (y - 1)] == 0x0F)
                {
                    downMsg = MOUSEEVENTF_LEFTDOWN;
                    upMsg = MOUSEEVENTF_LEFTUP;
                }
                else
                {
                    downMsg = MOUSEEVENTF_RIGHTDOWN;
                    upMsg = MOUSEEVENTF_RIGHTUP;
                }
            }
            POINT curPos = { rc.left + x_pos + (x - 1) * 16, rc.top + y_pos + (y - 1) * 16 };
            ClientToScreen(hwnd, &curPos);
            SetCursorPos(curPos.x, curPos.y);
            mouse_event(downMsg, 0, 0, 0, 0);
            mouse_event(upMsg, 0, 0, 0, 0);
            Sleep(1);

        }
    }
    CloseHandle(handle);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ACanary

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值