Log in

View Full Version : A Clean Sweep on Minesweeper (Botting)


L. Spiro
December 5th, 2006, 03:22

This was originally a tutorial posted on another site to demonstrate a new way of making bots.

Although this is in tutorial format, this doubles as a mini project—the script you get after following this (or just skipping to the code and copying/pasting) will give you a bot that will legally play Minesweeper for you.

Without modifying Minesweeper at all, the bot will move your mouse and correctly click on all valid squares in the Minesweeper field, as fast or as slow as you would like it to click.



If you opt to skip to the code without reading the setup, be sure to at least read the part explaining how to get the code to compile.

Demo #6 can be found here: http://www.memoryhacking.com/MemHack/MHSDemo6.rar

The unmodified tutorial follows.











This tutorial demonstrates the botting capabilities readily available in MHS Demo 6 and greater.  Minesweeper is a simple game with a point-click interface, which as luck would have it is the basis of every bot for MMORPG and RTS game out there.

This tutorial is advanced, and while it is simple to do, it takes a coder to actually understand.  If you are not adept in coding in C or C++, read on at your own risk.





Other tutorials show how to hack Minesweeper and win with a very low time score.

MHS is unique in that it has a full programming language built into it, which offers us a few alternative solutions that I will explore here because they are useful alternatives that can applied to many other types of situations, especially in the realm of botting.



This tutorial explains how to easily make a simple bot that literally plays Minesweeper for you, correctly clicking on the squares where there are no bombs.

This works without modifying the game RAM (freezing timers, for example, modifies game RAM and is highly detectable) and is the most “legal” way to cheat the game.

It is also impressive and fun to watch your cursor quickly move along the tiles and click the correct squares.





TERMINOLOGY

Botting: The use of an application to play your game for you, typically by simply sending mouse clicks that result in your in-game persona performing tedious or complicated actions undesirable or unattainable by humans.





TOOLS REQUIRED

MHS Demo 6 or greater: No other application can be used as a substitute; MHS is the only program currently available with L. Spiro Script, which is used in this example and makes things very simple.

Minesweeper: We hack this.  It comes with Windows®, typically in Start/Programs/Games/Minesweeper.





Let’s Get Started!

Open MHS and Minesweeper.

Select Minesweeper in MHS by going to File/Open Process (Ctrl-P) and selecting “winmine.exe”.  The list can be sorted by clicking the column header.



Finding What We Need

Our goal is to make a bot that can click the tiles and avoid the bombs.

One thing we know we will need is the width and height of the board.

It’s time to perform a search.

Go to Search/Data-Type Search.  It is assumed by this level you know the idea being searching, so we get to the meat of the process.

We guess the data is stored as a Long.

Go to Minesweeper and hit Game/Custom....  Set the width to 11 and hit OK.

Now we know the width of the board, so go back to MHS, select Exact Value in the Evaluation Type, and then enter 11 in the Value to Find field.  Check Aligned.

The standard search gives you around 375 results.



Go back to MinesweeperGame/Custom....

Set the width to 13 and hit OK.



Back to MHS and go to Search/Sub Search.

Select Exact Value and enter 13, then search again.

We have 2 results now.  One on <font color=&quot;Red&quot;>0x010056AC</font> and one on <font color=&quot;DarkGreen&quot;>0x01005334</font>.

Add them both to the main window by double-clicking them both (or selecting them both and hitting the Add button).



When they are in the main list, select them both and right-click one of them, then select View in Hex Editor.



The Hex Editor is real-time; anything that changes gets highlighted brighter blue.

When we examine both addresses it becomes obvious that <font color=&quot;DarkGreen&quot;>0x01005334</font> is the one we need, because the next address (<font color=&quot;Blue&quot;>0x01005338</font> is the height of our board.



Now we have the width and height of our board already!



Get The Board Tiles

View the <font color=&quot;DarkGreen&quot;>0x01005334</font> address in the Hex Editor again.

The board height was right there, and the chances are good that the board array (the tiles on the playing field) are there too.

As mentioned, the Hex Editor will highlight bytes that change, so use this to our advantage.

Go to MinesweeperGame/New (or press the smiley face).

This makes a new game with a new board array.  If anything changed in our Hex Editor, it will be related to this change in our game.



Looking back at the Hex Editor shows that indeed several bytes have changed just below <font color=&quot;darkgreen&quot;>0x01005334</font>.

Guess what.  We just found the board array already.

Further investigation reveals that it starts on <font color=&quot;Indigo&quot;>0x01005361</font>.



Decode The Mine Array

This is quite simple and doesn&#8217;t even need its own section.

After playing with things a bit, you will find that mines have the highest bit set (they appear as values 0x80 and above in the Hex Editor).

In other words, if ([Square] &amp; 0x80) returns non-zero, there is a mine there, and we don&#8217;t want to click it.



Make The Bot Already!

It&#8217;s time to make use of the 3 things we&#8217;ve gathered.

We know the width and height of the board, and we know how to tell which squares should not be clicked.



Time to write some code.



MHS comes with its own language, compiler, and interpreter, so you need no extra tools.  Just go to Tools/Script Editor.

Place the floating windows where you like.



To begin a new script, press Ctrl-N.  A new empty file is created.

But this file isn&#8217;t related to MHS yet; it has no idea you want this file to be compiled and use by it.  We need to add it to MHS&#8217;s script set by pressing Ctrl-D.

It will prompt you to save it first, so think of a fitting name and save it.  My file is Minesweeper.lss.



Now the file is recognized by MHS as a file that you want it to compile and use in its scripting, but the file is empty.  Let&#8217;s solve that problem now.



It will be easier to show the code first and then go over it.

Paste this code into the file, then hit Ctrl-S to save it and F5 to compile it (very important).






VOID SolveMinesweeperBoard() {

    HWND hWnd = FindWindow( &quot;Minesweeper&quot; ;

    if ( !hWnd  { return; }

 

    // Put the Minesweeper board in front if possible.

    ShowWindowAsync( hWnd, SW_SHOW ;

    SetForegroundWindow( hWnd ;

 

    // Get its position.

    INT iX;

    INT iY;

    GetWindowPos( hWnd, &amp;iX, &amp;iY ;

    PrintF( &quot;Minesweeper board located at %d %d.&quot;, iX, iY ;

    if ( iX &lt; 0 || iY &lt; 0  {

        PrintF( &quot;Minesweeper board is out of range!&quot; ;

        return;

    }

 

 

    extern struct BOARDSIZE {

        INT iWidth;

        INT iHeight;

    }               e_bwSize            = { &quot;winmine.exe&quot;, 0x5334 };    // The width and height of the board.

    extern BYTE     e_pbBoard[32*32]    = { &quot;winmine.exe&quot;, 0x5361 };    // The board array itself.

                                                                        //  Note that 32*32 here is meaningless, except to show us how

                                                                        //  large the array is in the target process (it does not actually

                                                                        //  declare the array in the target process; it already exists).

 

 

 

 

    POINT pClick;

    // To calculate the final click positions we need the screen

    //  resolution.

    INT iScreenW = GetPrimScreenWidth();

    INT iScreenH = GetPrimScreenHeight();

 

    // System settings change the location of mines on the board, so

    //  get the settings needed to know where to click.

    INT iBorderHeight = GetSystemMetrics( SM_CYEDGE ;      // Window border (height).

    INT iCaptionHeight = GetSystemMetrics( SM_CYCAPTION ;  // Title bar height.

    INT iMenuHeight = GetSystemMetrics( SM_CYMENU ;        // Menu height.

    INT iFinalYOffset = iBorderHeight + iCaptionHeight + iMenuHeight;

    INT iFinalXOffset = GetSystemMetrics( SM_CXEDGE ;      // Window border (width).

 

    // Save the externs to locals.  This makes it faster to scan the

    //  board.

    INT iBoardWidth = e_bwSize.iWidth;

    INT iBoardHeight = e_bwSize.iHeight;

 

    // Run through the board and click on each square that has no mine.

    for ( INT J = 0; J &lt; iBoardHeight; J++  {

        for ( INT I = 0; I &lt; iBoardWidth; I++  {

            if ( (e_pbBoard[(32*J)+I] &amp; 0x80) == 0  {

                GetClickPos( iFinalXOffset, iFinalYOffset, iX, iY, &amp;pClick, I, J ;

                // Translate the click position into the actual screen coordinates (Windows® forces us to do this).

                pClick.x = pClick.x * 65535.0f / iScreenW;

                pClick.y = pClick.y * 65535.0f / iScreenH;

 

                // Move there.

                MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,

                    pClick.x, pClick.y,

                    0 ;

 

                // Click there (down and up).

                MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN,

                    0, 0,                           // X and Y positions aren&#8217;t used.

                    0 ;

                MouseEvent( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP,

                    0, 0,

                    0 ;

 

                // Optionally sleep for a bit in case you like to see the action unfold.

                //Sleep( 10 ;

            }

        }

    }

 

}

 

// Constants that tell us the dimensions of the board.

const INT iSquareSize = 16;

const INT iXOff = 14;

const INT iYOff = 56;

const INT iFinalX = iXOff + (iSquareSize / 2);

const INT iFinalY = iYOff + (iSquareSize / 2);

VOID GetClickPos( INT iScreenOffX, INT iScreenOffY, INT iPosX, INT iPosY, POINT * ppRet, INT iX, INT iY  {

    // The window is at (iPosX, iPosY).  From there, go to the corner of the board

    //  and then offset based on which square we are hitting.

    ppRet-&gt;x = iPosX + iX * iSquareSize + iFinalX + iScreenOffX;

    ppRet-&gt;y = iPosY + iY * iSquareSize + iFinalY + iScreenOffY;

}



Is That Just C Code?

Sure looks like it, doesn&#8217;t it?

L. Spiro Script isn&#8217;t actually a script at all.  It&#8217;s a full language on par with C.



You can use pointers and all C operators, including indirection (*) and address-of (&amp.

But at the top you may have noticed one major difference: <font color=&quot;Blue&quot;>extern</font>.



But first thing first.

We started by using the standard Windows® function FindWindow() to get a handle to the Minesweeper window.

From there, GetWindowPos() gets the actual position of the window.  This is important if we want to click on the window to play the game.



We then declare those <font color=&quot;Blue&quot;>extern</font> variables.

These are variables that are actually in Minesweeper itself, not in our script, or in MHS.  We can read and write to these variables the same way as we can with any normal variable, but the values when reading come from the RAM inside Minesweeper, and changing these variables will change the RAM inside Minesweeper.



We found the board width and height on &#8220;winmine.exe&#8221;+0x5334, and we declare a BOARDSIZE variable there.

The board array is on &#8220;winmine.exe&#8221;+0x5361, and we declare a byte array there.

From here out, reading the information from the game is a very simple task.



Next we get the screen width and height.  This is because we need it for translations required by Windows® when using MouseEvent() (same function as mouse_event() in the Windows® API).



Then we start the loop through the game board, and you can see how e_bsSize.x and e_bsSize.y are used to get the board width and height directly from winmine.exe itself.

There is no more need for the hassle involved with ReadProcessMemory() and WriteProcessMemory().



Then we use e_pbBoard to check the square on the Minesweeper board.

If the highest bit is set ([byte] &amp; 0x80), we do not want to click there.



If we do want to click there, we start by getting the actual position where we want to click.  We have written our own function to do this called GetClickPos() which is defined at the bottom.  This takes the X and Y of the Minesweeper window and the square we want to click, then returns the X and Y (in pixels on the screen) where we should click.  Unfortunately we have to perform a simple translation on it as required by Windows®, but then we are on our way.



We use MouseEvent() to put the mouse in position and then click.



We can optionally call Sleep() for a little bit to slow things down and make it fun to watch.  The cursor will glide across the board and gracefully click the correct squares, solving any board in no time at all.



That Was Advanced Yet Simple

And it has many other implications besides just quick Minesweeper boards.

You can use <font color=&quot;Blue&quot;>extern</font> to easily go inside any game and get/set any data you need to make any kind of interactive bot that you want.



But How Do I Make This Script Run?

Attach it to a hotkey!

First, write this function and place it at the bottom of the file:


VOID On_HK_0() {

    SolveMinesweeperBoard();

}

Now go to Tools/Hotkeys (Ctrl-K).

Hit New and select the Key and Mod of your choice.

Then select ScriptFunction in the Function field.

Our hotkey function is On_HK_0() so enter 0 into the Parm 1 field.  Parm 2 and Parm 3 are not used, so ignore them.

Uncheck the Poll Method check and hit OK.

WARNING: DO NOT USE SHIFT AS A MOD FOR YOUR HOTKEY.  DOING SO WILL PREVENT SOME MOUSE CLICKS FROM BEING SENT.



Now go to Minesweeper and hit the key and mod you selected to activate the hotkey.

Your script function will be called and your mouse will move across the board clicking all the correct squares.

It&#8217;s more than magic; it&#8217;s L. Spiro Script (/Me puts up flame shield).



Incredible, But What More?

Imagine what you can do to RTS and MMORPG games.

Mouse clicks aren&#8217;t the only things you can send; keyboard and joystick/controller events are supported as well, allowing you to make bots that can play all of your games for you, depending on your own level of coding and logical abilities.



You&#8217;ve all seen the video of Super Mario Bros.&#8482; 3 being beaten in 11 minutes.

Now you can make a bot that can beat the game in 10 minutes by sending perfectly timed button presses to the emulator.



Easily make bots to perform the dull routine mouse/key sequences in your favorite MMORPG games.



Your bots can even change behavior depending on different game states.



What you can do is up to you.

This tutorial just gets you started.



I hope you enjoyed.





L. Spiro


LLXX
December 5th, 2006, 06:23
Not really reversing and more towards game-hacking (which does involve a certain amount of RCE), but ok...

L. Spiro
December 5th, 2006, 06:35
I was requested to post here.
But if there is a better section for this, I would be happy to move it there.


(Update, modified the tutorial a bit. Code runs on more machines now.)


L. Spiro

dELTA
December 5th, 2006, 11:40
Nice project, thanks for sharing.

Silver
December 5th, 2006, 14:55
Take a look at SendInput rather than MouseEvent, I believe (at least MSDN says) that all input sending other than SendInput is now deprecated.

Your next challenge, write a bot for counterstrike

L. Spiro
December 5th, 2006, 17:20
My script’s “MouseEvent()” wraps around mouse_event() and is indeed deprecated.
My script supports SendInput() to cover all ground, but I prefer mouse_event() simply to avoid having to fill out a structure.



Before making a Counter Strike bot I have a few other ideas.

You’ve all seen the video of the guy beating Super Mario Bros. 3 in 11 minutes.
Well, a bot could be used to play it even faster, or to play another game at impressive speed. Just send timed input to the emulator. This is the most basic idea.

Since the bots are written in the script it means they can have logic and can react different ways depending on what is happening in the game, so the next level up from there would be to have a bot that can play Street Fighter on ZSNES. It can read the opponent’s moves and counter them perfectly.

Another idea is to make a bot that plays Tetris to infinity, which would require a bit more AI (but I have written a Tetris AI before so I have a basis), then allow the bot to play Windows Tetris, ZSNES Tetris, and/or Tetrinet 2 against actual humans online (that would be fun to watch).

Counter Strike bots are possible too, but I’m thinking of more simple games where, if you see them played at an inhuman level, it really makes you want to say, “Wow!”. Like the Super Mario Bros. 3 video.
I’m trying to think of what other games can be beaten super fast that way, and I think maybe Super Mario World can be.



Another alternative is to have the “bot” sit idle while you play, and just hit a hotkey to perform moves and combos for you.
This works very well in Killer Instinct—hit a button when ready and perform an ultra combo, or instantly perform any of Zangief’s moves in Street Fighter (as his moves are known to be finger breakers).




I still have a lot of work to do on the actual software (MHS) so I am only open to very simple ideas at the moment, but ideas are welcome.



L. Spiro

Anon
December 7th, 2006, 04:39
Quote:
[Originally Posted by L. Spiro]You’ve all seen the video of the guy beating Super Mario Bros. 3 in 11 minutes.
Well, a bot could be used to play it even faster

If you're referring to the video that you can view at http://www.youtube.com/watch?v=9QVYbDeHR5Q, then it might be hard to improve on it. The guy who made it used an emulator with slowdown features and a ton of savestates. You can get more info about these type of movies and how they are made at http://tasvideos.org/. There are quite a number at that site, including ones of Tetris and Super Mario World. The approach generally doesn't require much RCE, but there are some games for which major algorithms to determine "random" events and such things have been figured out.

L. Spiro
December 7th, 2006, 05:26
Quote:
The guy who made it used an emulator with slowdown features and a ton of savestates.

Not to mention a few invicibility cheats.

Of course, you’ll never get much faster than his video shows, but I’m still more interested in doing the same for another game that either hasn’t been done or isn’t known (widely) to be done.
Uniracers comes to mind.
Super Mario World is still a possibility because not many people have seen its videos; it isn’t as popular.

I’m also more interested in doing one for a fighting game though, as beating a popular fighting game with no damage on the hardest level would be quite the video.
Killer Instinct was my favorite and since I know it best I would probably do that one.


One step up over those other videos, however, is that at least I could rightfully claim that I did it at full speed, all in one sitting, live, and without in-game cheats such as invincibility. Though I would of course post my method as they too have done.


L. Spiro

Anon
December 7th, 2006, 14:25
Quote:
[Originally Posted by L. Spiro]Not to mention a few invicibility cheats.

I'm sorry if I'm going off-topic, but I must point out that invincibility cheats were not used. The Rules page (http://tasvideos.org/Rules.html) states, "Codes that manipulate ROM or RAM directly (e.g. Game genie codes) are not allowed." There were indeed several times when you think he should have gotten hit, but they were probably instances of hitbox abuse. As stated somewhere on the site, the idea is that anyone could play like in those movies using just an unmodified console and game cartridge if they could process all of the information and act quickly enough.

I'm just trying to clear up some common misconceptions about those so-called tool-assisted speedruns. Don't take this as hostile or anything. Your idea is quite interesting. I wouldn't quite say that you would have the right to claim that "you" were the one did the playing, though .