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.