use warnings;
use strict;

{   package Local::Field;

    use constant {
        REAL  => 0,
        SHOW  => 1,
        COUNT => 2,
    };

    sub new {
        my ($class, $width, $height, $percent) = @_;
        my $field;
        for my $x (1 .. $width) {
            for my $y (1 .. $height) {
                $field->[$x - 1][$y - 1][REAL] = ' ';
                $field->[$x - 1][$y - 1][SHOW] = '.';
            }
        }
        for (1 .. $percent / 100 * $width * $height) {
            my ($x, $y) = map int rand $_, $width, $height;
            redo if 'm' eq $field->[$x][$y][REAL];
            $field->[$x][$y][REAL] = 'm';
            for my $i ($x - 1 .. $x + 1) {
                for my $j ($y - 1 .. $y + 1) {
                    $field->[$i][$j][COUNT]++
                        if $i >= 0 and $j >= 0
                        and $i <= $
                }
            }
        }
        bless $field, $class;
    }


    sub show {
        my ($self) = @_;
        print "\n  ";
        printf '%2d ', $_ + 1 for 0 .. $
        print "\n";

        for my $row (0 .. $
            printf '%2d ', 1 + $row;
            for my $column (0 .. $
                print $self->[$column][$row][SHOW], '  ';
            }
            print "\n";
        }
    }


    sub mark {
        my ($self, $x, $y) = @_;
        $_-- for $x, $y;

        if ('.' eq $self->[$x][$y][SHOW]) {
            $self->[$x][$y][SHOW] = '?';

        } elsif ('?' eq $self->[$x][$y][SHOW]) {
            $self->[$x][$y][SHOW] = '.';
        }
    }


    sub end {
        my $self = shift;
        for my $y (0 .. $
            for my $x (0 .. $
                $self->[$x][$y][SHOW] = '!' if '.' eq $self->[$x][$y][SHOW]
                    and 'm' eq $self->[$x][$y][REAL];
                $self->[$x][$y][SHOW] = 'x' if '?' eq $self->[$x][$y][SHOW]
                    and 'm' ne $self->[$x][$y][REAL];
            }
        }
        $self->show;
        exit;
    }

    sub _declassify {
        my ($self, $x, $y) = @_;
        return if '.' ne $self->[$x][$y][SHOW];
        if (' ' eq $self->[$x][$y][REAL] and '.' eq $self->[$x][$y][SHOW]) {
            $self->[$x][$y][SHOW] = $self->[$x][$y][COUNT] || ' ';
        }
        return if ' ' ne $self->[$x][$y][SHOW];

        for my $i ($x - 1 .. $x + 1) {
            next if $i < 0 or $i > $
            for my $j ($y - 1 .. $y + 1) {
                next if $j < 0 or $j > $
                no warnings 'recursion';
                $self->_declassify($i, $j);
            }
        }
    }


    sub clear {
        my ($self, $x, $y) = @_;
        $_-- for $x, $y;
        return unless '.' eq $self->[$x][$y][SHOW];

        print "You lost.\n" and $self->end if 'm' eq $self->[$x][$y][REAL];

        $self->_declassify($x, $y);
    }


    sub remain {
        my $self = shift;
        my $unclear = 0;
        for my $column (@$self) {
            for my $cell (@$column) {
                $unclear++ if '.' eq $cell->[SHOW];
            }
        }
        return $unclear;
    }

}

sub help {
    print << '__HELP__';
Commands:
h     ... help
q     ... quit
m X Y ... mark/unmark X Y
c X Y ... clear X Y
__HELP__
}


my ($width, $height, $percent) = @ARGV;
$width   ||= 6;
$height  ||= 4;
$percent ||= 15;

my $field = 'Local::Field'->new($width, $height, $percent);

my $help = 1;
while (1) {
    $field->show;
    help() if $help;
    $help = 0;
    my $remain = $field->remain;
    last if 0 == $remain;
    print "Cells remaining: $remain.\n";
    my $command = <STDIN>;
    exit if $command =~ /^q/i;

    if ($command =~ /^m.*?([0-9]+).*?([0-9]+)/i) {
        $field->mark($1, $2);

    } elsif ($command =~ /^c.*?([0-9]+).*?([0-9]+)/i) {
        $field->clear($1, $2);

    } elsif ($command =~ /^h/i) {
        $help = 1;

    } else {
        print "Huh?\n";
    }
}
print "You won!\n";