C - 扫雷AI不点击瓷砖

时间:2014-08-30 18:58:43

标签: c cursor

游戏是扫雷。我试图实现的AI将采用已经在机器上运行的游戏Minesweeper实例(本例为Windows 7),获取窗口的矩形尺寸,计算窗口的位置,将其推送到前台在屏幕上,单击左上角的方块,然后通过花哨的算法来确定哪个方块是我的,哪个是清晰的。

最近,只有在点击第一个图块之前实现MessageBox之后,我才能正确处理窗口和光标。

使用消息框,程序将扫雷窗口推到前面,生成消息框,单击消息框上的回车按钮后,程序将光标定位在正确的左上方,然后停止..在消息框之后的代码中,有一个注释掉的部分,当取消注释时,遍历整个Minesweeper窗口并将其标记为我的(标志!) - 这告诉我它注册了{{1}正确地用于地雷 - 但不是明确的区域。在此之后,该程序假设单击左上角的第一个图块。发生的一切都是鼠标闪烁。

正确的行为是将扫雷窗口推到前面,生成消息框,点击消息框后,它应该点击游戏中的左上角。

问题是,为什么不在ClickTile中注册LEFTMOUSEDOWN而只注册RIGHTMOUSEDOWN

UiInfo.h

ClickTile

UiInfo.c

#ifndef UiInfo_
#define UiInfo_
#include <Windows.h>

struct UiInfo {
    float scale;
    POINT start;
    int tile_size;
};

extern struct UiInfo * ui_info;

void UiInfo_initialize(struct UiInfo * ui_info, 
                    float scale,
                    POINT start,
                    int tile_size);
#endif

的main.c

#include "UiInfo.h"

void UiInfo_initialize(struct UiInfo * ui_info, 
                    float scale,
                    POINT start,
                    int tile_size) {

    ui_info->scale = scale;
    ui_info->start = start;
    ui_info->tile_size = tile_size;
}

1 个答案:

答案 0 :(得分:0)

我将进行三次猜测。

猜数字1

您需要使用计时器才能让鼠标移动和鼠标点击时间生效。这包括在app启动时启动计时器,然后实现一个状态机,在计时器触发时生成新的鼠标事件,例如

// start the timer in some initialization function
SetTimer( 75, 5, NULL );   

// handler function to update the overall state
void CMainFrame::OnTimer(UINT nIDEvent)
{
    switch ( m_State )
    {
        case STATE_IDLE:    // do nothing until the user tells us to start
        case STATE_DONE:    // do nothing after the game is finished
            break;

        case STATE_START:   // the first click to get things started
            mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, m_Square[1][1].mousex, m_Square[1][1].mousey, 0, 0 );
            mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );
            mouse_event( MOUSEEVENTF_LEFTUP  , 0, 0, 0, 0 );
            m_State = STATE_MOVING;
            break;

        case STATE_MOVING:  // all the fun happens here
            if ( !MakeAMove() )
            {
                m_State = STATE_DONE;
                Invalidate();
            }
            break;
    }

    CFrameWnd::OnTimer(nIDEvent);
}

猜数字2

无论屏幕分辨率如何,鼠标坐标系统均为65536 x 65536。因此,您需要缩放鼠标坐标以转换为像素位置。这是计算比例因子的一种方法

HDC hdc = ::GetDC( NULL );
CDC *dc = CDC::FromHandle( hdc );
CWnd *wnd = dc->GetWindow();
wnd->GetClientRect( &rect );
xscale = 65535.0 / (double)rect.right;
yscale = 65535.0 / (double)rect.bottom;

猜数字3

在生成鼠标点击事件之前,您需要将鼠标移动到正确的屏幕坐标,例如

// the following code goes in some initialization function that knows the location of the minesweeper window
// note that screenx and screeny are the pixel coordinates of the center point of each square and these
// are converted to mouse coordinates for each square
for ( y = 1; y <= m_MaxY; y++ )
    for ( x = 1; x <= m_MaxX; x++ )
    {
        m_Square[y][x].mousex  = (int)(m_Square[y][x].screenx * xscale + 0.5);
        m_Square[y][x].mousey  = (int)(m_Square[y][x].screeny * yscale + 0.5);
    }

// this code generates a left click in a given square
mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, m_Square[y][x].mousex, m_Square[y][x].mousey, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTUP  , 0, 0, 0, 0 );