Minesweeper game script (game script demo)

1 Introduction

Minesweeper is a well-known Windows game and is widely entertained as a classic game. So, is it possible to make a small program that automatically clears mines and clear levels at light speed? The answer is yes. It can even reach the point of being invincible. First, the picture above sets the record to -8s, so no one can break this record.

2. Implementation method

1. Implement logic

1 ) Create a WIN32 empty project;

2 ) Open the minesweeper process and obtain the row and column values ​​according to the memory address of the rows and columns of the minefield;

3 ) Read the minefield data according to the minefield first address and minefield range;

4 ) Put the minesweeper game interface on top, read the value in row i and column j of the minefield, and determine whether it is a mine. If not, simulate a left-click operation of the mouse. If it is a mine, simulate a right-click operation of the mouse.

5 ) According to the customer coordinate system of the Minesweeper game interface, calculate the coordinate position of the i-th row and j-th column, then move the mouse to this position and perform the mouse operation in step 3 .

6 ) Repeat 3-4 until all minefield data are scanned.

2. Related APIs

The Windows API function used to read and write the contents of the memory address is:

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);

The read and write memory API function requires the handle of the target process as a parameter. For this purpose, OpenProcess needs to be called to obtain the process handle:

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

OpenProcess requires the process ID as a parameter, which can be obtained through the GetWindowThreadProcessId function .

The number is:

DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);

The GetWindowThreadProcessId function requires the window handle as a parameter, which can be obtained through FindWindow . The two parameters of the window class and window title name used by this API function have been obtained through Spy++ in step 7 .

HWND FindWindowA(LPCSTR lpClassName, LPCSTR lpWindowName);

The API function to simulate mouse operations is mouse_event . The specific usage method is to use the SetCursorPos API function to move the mouse to the specified position, and then simulate the mouse press and pop-up operations.

The function is declared as follows:

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

BOOL SetCursorPos(int X, int Y);

In this experiment, the mouse_event function only needs to set the first parameter, which specifically includes left-click pressing and pop-up ( MOUSEEVENTF_LEFTDOWN , MOUSEEVENTF_LEFTUP ), right-click pressing and pop-up ( MOUSEEVENTF_RIGHTDOWN , MOUSEEVENTF_RIGHTUP ). 

The API function to put a GUI program interface on top is:

BOOL SetForegroundWindow(HWND hWnd);

3. Script implementation

Determine the size of the minesweeper game grid

Open Spy++ , click " Monitor -- Log Message " , check " Hide Spy++ Options " , and drag the " Find Program Tool " to the main interface of the Minesweeper game, as shown in Figure 8-4 . The displayed content includes the Minesweeper window class ( WNDCLASS ) name and title name.

Switch to the " Message " window of Spy++ , first clear all messages, and then check the two messages "WM_LBUTTONDOWN and WM_LBUTTONUP" , as shown in Figure 8-5 . Click OK to return to the Spy++ main interface.

Return to the Minesweeper game interface, click on the three grids in the upper left corner, immediately to the right of the upper left corner, and immediately below the upper left corner. Check the xPos and yPos values ​​recorded by Spy++ when the mouse is clicked, and calculate the width and sum of each grid. high.

The coordinates of the first minefield are:

xPos =14

yPos =58

In the Spy++ interface, right-click - Clear Message Log, and then repeat the above operation. You can also click several grids apart to calculate the average value of each grid as the width and height value of each grid. The width of the grid is: 16 ; the height of the grid is: 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;
}

Guess you like

Origin blog.csdn.net/HUANGliang_/article/details/127354969