App-SweeperBot
view release on metacpan or search on metacpan
lib/App/SweeperBot.pm view on Meta::CPAN
package App::SweeperBot;
# minesweeper.pl
#
# Win32::Screenshot, Win32::GuiTest, and Image::Magick are needed for this
# program. Use ActivePerl's PPM to install the first two:
# ppm> install Win32-GuiTest
# ppm> install http://theoryx5.uwinnipeg.ca/ppms/Win32-Screenshot.ppd
#
# The version of Image-Magick used by this code can be found at
# http://www.bribes.org/perl/ppmdir.html . Different ImageMagick
# distributions may result in different signature codes.
#
# 20050726, Matt Sparks (f0rked), http://f0rked.com
=head1 NAME
App::SweeperBot - Play windows minesweeper, automatically!
=head1 SYNOPSIS
C:\Path\To\Distribution> SweeperBot.exe
=head1 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.
=head1 METHODS
=cut
use strict;
use warnings;
use Carp;
use NEXT;
use 5.006;
our $VERSION = '0.03';
use Scalar::Util qw(looks_like_number);
use Win32::Process qw(NORMAL_PRIORITY_CLASS);
use constant DEBUG => 0;
use constant VERBOSE => 0;
use constant CHEAT => 1;
use constant UBER_CHEAT => 0;
use constant SMILEY_LENGTH => 26;
# The minimum and maximum top dressings define the range in which
# we'll look for a smiley, which we use to calibrate our board. Different
# windows themes put them in different places.
use constant MINIMUM_TOP_DRESSING => 56;
use constant MAXIMUM_TOP_DRESSING => 75;
my $Smiley_offset = 0;
use constant CHEAT_SAFE => "d0737abfd3abdacfeb15d559e28c2f0b3662a7aa03ac5b7a58afc422110db75a"; # Old 58
# use constant CHEAT_SAFE => "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e"; # Old 510
lib/App/SweeperBot.pm view on Meta::CPAN
# Click the left button of the mouse.
# Arguments: x, y as ABSOLUTE positions on the screen
sub click {
my($this, $x,$y,$button)=@_;
$button ||= "{LEFTCLICK}";
MouseMoveAbsPix($x,$y);
print "Button: $button ($x,$y)\n" if DEBUG;
SendMouse($button);
return;
}
=head2 new_game
$sweeperbot->new_game;
Starts a new game of minesweeper. C<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.
=cut
# TODO: Rather than using the reset variables, we should properly
# calculate the location of our reset button. We have calibration
# code elsewhere that essentially finds the smiley, we just have to
# click on it.
sub new_game {
my ($this) = @_;
our ($reset_x,$reset_y);
$this->click($reset_x,$reset_y);
return;
}
=head2 focus
$sweeperbot->focus;
Focuses on t he minesweeper window by clicking a little left of the
smiley. Does not check for success. Returns nothing.
=cut
# Focus on the Minesweeper window by clicking a little to the left of the game
# button.
sub focus {
my ($this) = @_;
our ($reset_x, $reset_y);
$this->click($reset_x - FOCUS_X_OFFSET ,$reset_y);
return;
}
=head2 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 L<Image::Magick>
object.
=head3 Bugs in capture_square
On failure to capture the image, this returns an empty
L<Image::Magick> object. This is considered a bug; in the future
C<capture_square> will throw an exception on error.
C<capture_square> depends upon calibration routines that are
currently implemented in the L</value> method; calling it before
the first call to L</value> can result in incorrect or inconsistent
results. In future releases C<capture_square> will automatically
calibrate itself if required.
=cut
# TODO GuiTest doesn't check the Image::Magick return codes, it
# just assumes everything works. We should consider writing our
# own code that _does_ test, since these diagnostics are very
# useful when things go wrong.
sub capture_square {
my($this, $sx,$sy)=@_;
our($l,$t);
my $image=CaptureRect(
$l+SQUARE1X+($sx-1)*SQUARE_W,
$t+$Square1Y+($sy-1)*SQUARE_H,
SQUARE_W,
SQUARE_H
);
return $image;
}
=head2 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.
=cut
sub value {
my($this, $sx,$sy)=@_;
if (not $Square1Y) {
# We haven't calibrated our board yet. Let's see if we can
# find a square we recognise.
CALIBRATION: {
for (my $i = MIN_SQUARE1Y; $i <= MAX_SQAURE1Y; $i++) {
$Square1Y = $i;
warn "Trying to calibrate board $i pixels down\n" if DEBUG;
my $sig = $this->capture_square(1,1)->Get("signature");
# Known signature, break out of calibration loop.
last CALIBRATION if ($contents_of_square{$sig});
}
# If we're here, we couldn't calibrate
die "Board calibration failed\n";
}
}
( run in 1.315 second using v1.01-cache-2.11-cpan-39bf76dae61 )