The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

App::SweeperBot - Play windows minesweeper, automatically!

SYNOPSIS

        C:\Path\To\Distribution> SweeperBot.exe

DESCRIPTION

This is alpha code, and released for testing and demonstration purposes only. It is still under active development.

Using this code for playing minesweeper on a production basis is strongly discouraged.

METHODS

new

        my $sweperbot = App::SweeperBot->new;

Creates a new App::SweeperBot object. Does not use any arguments passed, but will send them verbatim to an _init method if defined on a child class.

spawn_minesweeper

        $sweeperbot->spawn_minesweeper;

Attempts to spawn a new minesweeper instance. Returns the Win32::Process object on success, or throws an exception on error.

locate_minesweeper

        $sweeperbot->locate_minesweeper;

Locates the first minesweeper window that can be found, brings it into focus, and sets relevant state so that it can be acessed later. Must be used before a game can be started or played. Should be used if the minesweeper window changes size or position.

Returns the window ID on success. Throws an exception on failure.

click

        $sweeperbot->click($x,$y,$button);

Clicks on ($x,$y) as an absolute position on the screen. $button is any button as understood by Win32::GuiTest, usually {LEFTCLICK}, {MIDDLECLICK} or {RIGHTCLICK}.

If not specified, $button defaults to a left-click.

Returns nothing.

new_game

        $sweeperbot->new_game;

Starts a new game of minesweeper. locate_minesweeper() must have been called previously for this to work.

Does not return a value, nor does it check to see if a new game has been successfully started.

focus

        $sweeperbot->focus;

Focuses on t he minesweeper window by clicking a little left of the smiley. Does not check for success. Returns nothing.

capture_square

        my $image = $sweeperbot->capture_square($x,$y);

Captures the square ($x,$y) of the minesweeper board. (1,1) is the top-left of the grid. No checking is done to see if the square is actually on the board. Returns the image as an Image::Magick object.

Bugs in capture_square

On failure to capture the image, this returns an empty Image::Magick object. This is considered a bug; in the future capture_square will throw an exception on error.

capture_square depends upon calibration routines that are currently implemented in the "value" method; calling it before the first call to "value" can result in incorrect or inconsistent results. In future releases capture_square will automatically calibrate itself if required.

value

        my $value = $sweeperbot->value($x,$y);

Returns the value in position ($x,$y) of the board, square (1,1) is considered the top-left of the grid. Possible values are given below:

        0-8             # Number of adjacent mines (0 = empty)
        bomb            # A bomb (only when game lost)
        bomb_hilight    # The bomb we hit (only when game lost)
        flag            # A flag
        unpressed       # An unpressed square

Support of question-marks is not provided, but may be included in a future version.

Throws an exception on failure.

press

        $sweeperbot->press($x,$y, $button)

Clicks on the square with co-ordinates ($x,$y) using the mouse-button $button, or left-click by default. Square (1,1) is the top-left square. Does not return a value.

stomp

        $sweeperbot->stomp($x,$y);

Stomps (middle-clicks) on the square at ($x,$y), normally used to stand on all squares adjacent to the square specified. Square (1,1) is the top-left of the grid. Does not return a value.

flag_mines

        $sweeperbot->flag_mines($game_state,
                [2,3], [7,1], [8,3]
        );

Takes a game state, and a list of location tuples (array-refs), and marks all of those locations with flags.

The requirement to pass $game_state may be removed in a future version.

game_over

        if (my $state = $sweeperbot->game_over) {
                print $state > 0 ? "We won!\n" : "We lost!\n";
        }

Checks to see if the game is over by looking at the minesweeper smiley. Returns 1 for game over due to a win, -1 for game over due to a loss, and false if the game has not finished.

make_move

        $sweeperbot->make_move($game_state);

Given a game state, determines the next move(s) that should be made, and makes them. By default this uses a very simple process:

  • If UBER_CHEAT is set, then cheat.

  • If we find a square where the number of adjacent mines matches the number on the square, "stomp" on it.

  • If the number of adjacent unpressed squares matches the number of unknown adjacent mines, then flag them as mines.

  • If all else fails, pick a square at random. If CHEAT is defined, and we would have picked a square with a mine, then pick another.

If you want to inherit from this class to change the AI, overriding this method is the place to do it.

capture_game_state

        my $game_state = $sweeperbot->capture_game_state;

Walks over the entire board, capturing the value in each location and adding it to an array-of-arrays (game-state) structure. The value in a particular square can be accessed with:

        $value = $game_state->[$x][$y];

Where (1,1) is considered the top-left of the game board.

adjacent_mines_for

        my $mines = $sweeperbot->adjacent_mines_for($game_state, $x, $y);

Examines all the squares adjacent to ($x,$y) and returns an array-ref of tuples for those that have already been flagged as a mine.

adjacent_unpressed_for

        my $squares = $sweeperbot->adjacent_unpressed_for($game_state, $x, $y);

Examines all the squares adjacent to ($x,$y) and returns an array-ref of tuples for those that have not been pressed (and not flagged as a mine).

mines_at

        my $mines = $sweeperbot->mines_at($game_state, @locations);

Takes a game state and a list of locations, and returns an array-ref containing those locations from the list that have been flagged as a mine.

unpressed_list

        my $unpressed = $this->unpressed-list($game_state, @locations);

Identical to "mines_at" above, but returns any locations that have not been pressed (and not flagged as a mine).

enable_cheats

        $sweeperbot->enable_cheats;

Sends the magic xyzzy cheat to minesweeper, which allows us to determine the contents of a square by examining the top-left pixel of the entire display.

For this cheat to be used in the default AI, the CHEAT constant must be set to a true value in the App::SweeperBot source.

cheat_is_square_safe

        if ($sweeperbot->cheat_is_square_safe($x,$y) {
                print "($x,$y) looks safe!\n";
        } else {
                print "($x,$y) has a mine underneath.\n";
        }

If cheats are enabled, returns true if the given square looks safe to step on, or false if it appears to contain a mine.

Note that especially on fast, multi-core systems, it's possible for this to move the mouse and capture the required pixel before minesweeper has had a chance to update it. So if you cheat, you may sometimes be surprised.

BUGS

Plenty. The code is pretty awful right now. Anything that could go wrong probably will.

Use of this program may cause sweeperbot to take control of our mouse and keyboard, playing minesweeper endlessly for days on end, and forcing the user to go and do something productive instead.

All methods that require a game-state to be passed will be modified in the future to be usable without the game-state. The App::SweeperBot object itself should be able to retain state.

AUTHOR

Paul Fenwick <pjf@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2005-2008 by Paul Fenwick, <pjf@cpan.org>

Based upon original code Copyright (C) 2005 by Matt Sparks <root@f0rked.com>

This application is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.6.0 or, at your option, any later version of Perl 5 you may have available.