#!/usr/bin/perl
# a simple minesweeper implement, ported from $ruby_source/sample/mine.rb
use strict;
use warnings;
use Ruby -all;
my $bd = Board->new(
10, # hight
10, # width
10, # num of bombs
);
system "stty raw -echo";
while(true){
$_ = getc;
if($_ eq 'n'){
$bd->reset;
}
elsif($_ eq 'm'){
$bd->mark;
}
elsif($_ eq 'j'){
$bd->down;
}
elsif($_ eq 'k'){
$bd->up;
}
elsif($_ eq 'h'){
$bd->left;
}
elsif($_ eq 'l'){
$bd->right;
}
elsif($_ eq ' '){
$bd->open;
}
elsif($_ eq 'q'){
$bd->quit;
last;
}
if($bd->is_over){
my $c;
print "\nquit?(y/n) ";
1 while(($c = lc getc) !~ /^[yn]/);
$c eq 'y' and last;
$bd->reset;
}
}
puts;
END{
system "stty -raw echo";
}
BEGIN{
package Board;
use Ruby; # import symbols
use Ruby -base => 'Object';
Array->alias('aref', '[]');
Array->alias('aset', '[]=');
# colors
our $Default = 46;
our $Opened = 43;
our $Over = 45;
our @CHR = ('. ', '1 ', '2 ', '3 ', '4 ', '5 ', '6 ', '7 ', '8 ', 'M ', 'B ', '@ ');
__PACKAGE__->attr_accessor(qw(_hi _wi _m _ms _total
_cx _cy _mc _over _data _state));
sub info{
my($self) = @_;
$self->pos(0, $self->{'_hi'});
print "the rest: ", $self->{'_mc'}, "/", $self->{'_total'}, " ";
$self->pos();
}
sub clr{
my($self) = @_;
print "\e[2J";
}
sub pos{
my($self, $x, $y) = @_;
$x ||= $self->{'_cx'};
$y ||= $self->{'_cy'};
printf "\e[%d;%dH", $y+1, $x*2+1;
}
sub colorstr{
my($self, $id, $s) = @_;
printf "\e[%dm%s\e[0m", $id, $s;
}
sub put{
my($self, $x, $y, $col, $str) = @_;
$self->pos($x, $y);
$self->colorstr($col, $str);
$self->info();
}
sub new{
my($class, $h, $w, $m) = @_;
my $self = $class->SUPER::new();
$self->{'_hi'} = $h;
$self->{'_wi'} = $w;
$self->{'_m'} = $m;
$self->reset;
return $self;
}
sub reset{
my($self) = @_;
Kernel->srand();
$self->{'_cx'} = 0;
$self->{'_cy'} = 0;
$self->{'_mc'} = $self->{'_m'};
$self->{'_over'} = false;
$self->{'_data'} = Array->new($self->{'_hi'} * $self->{'_wi'});
$self->{'_state'} = Array->new($self->{'_hi'} * $self->{'_wi'});
$self->{'_total'} = $self->{'_hi'} * $self->{'_wi'};
$self->{'_total'}->times(sub{
my($i) = @_;
$self->{'_data'}->aset($i, 0);
});
$self->{'_m'}->times(sub{
while(true){
my $j = Kernel->rand($self->{'_total'} - 1);
if($self->{'_data'}->aref($j) == 0){
$self->{'_data'}->aset($j, 1);
last;
}
}
});
$self->clr;
$self->pos(0, 0);
$self->{'_hi'}->times(sub{
my($y) = @_;
$self->pos(0, $y);
$self->colorstr($Default, $CHR[0] * $self->{'_wi'});
});
$self->info();
}
sub mark{
my($self) = @_;
my $ix = $self->{'_wi'} * $self->{'_cy'} + $self->{'_cx'};
my $s = $self->{'_state'}->aref($ix);
if($s == nil){
$self->{'_state'}->aset($ix, "MARK");
$self->{'_mc'} -= 1;
$self->{'_total'} -= 1;
$self->put($self->{'_cx'}, $self->{'_cy'}, $Opened, $CHR[9]);
}
elsif($s == "MARK"){
$self->{'_state'}->aset($ix, nil);
$self->{'_mc'} += 1;
$self->{'_total'} += 1;
$self->put($self->{'_cx'}, $self->{'_cy'}, $Default, $CHR[0]);
}
elsif($s == "OPEN"){
return;
}
}
sub open{
my($self, $x, $y) = @_;
$x ||= $self->{'_cx'};
$y ||= $self->{'_cy'};
my $wi = $self->{'_wi'};
my $hi = $self->{'_hi'};
my $state = $self->{'_state'};
if($state->aref($wi * $y + $x) == "OPEN"){ return 0 }
if($state->aref($wi * $y + $x) == nil) {
$self->{'_total'} -= 1;
}
if($state->aref($wi * $y + $x) == "MARK"){
$self->{'_mc'} += 1;
}
$self->{'_state'}->aset($wi * $y + $x, "OPEN");
if($self->fetch($x, $y) == 1){
$self->{'_over'} = 1;
return;
}
my $c = $self->count($x, $y);
$self->put($x, $y, $Opened, $CHR[$c]);
return if $c != 0;
if($x > 0 && $y > 0) { $self->open($x-1, $y-1) }
if($y > 0) { $self->open($x, $y-1) }
if($x < $wi-1 && $y > 0) { $self->open($x+1, $y-1) }
if($x > 0) { $self->open($x-1, $y) }
if($x < $wi-1) { $self->open($x+1, $y) }
if($x > 0 && $y < $hi-1) { $self->open($x-1, $y+1) }
if($y < $hi-1) { $self->open($x, $y+1) }
if($x < $wi-1 && $y < $hi-1){ $self->open($x+1, $y+1) }
$self->pos();
}
sub fetch{
my($self, $x, $y) = @_;
if($x < 0) { return 0 }
elsif($x >= $self->{'_wi'}){ return 0 }
elsif($y < 0) { return 0 }
elsif($y >= $self->{'_hi'}){ return 0 }
else{
$self->{'_data'}->aref($y * $self->{'_wi'} + $x);
}
}
sub count{
my($self, $x, $y) = @_;
$self->fetch($x-1, $y-1) + $self->fetch($x, $y-1) + $self->fetch($x+1, $y-1) +
$self->fetch($x-1, $y) + + $self->fetch($x+1, $y) +
$self->fetch($x-1, $y+1) + $self->fetch($x, $y+1) + $self->fetch($x+1, $y+1);
}
sub over{
my($self, $win) = @_;
$self->quit();
unless($win){
$self->pos();
print $CHR[11];
}
$self->pos(0, $self->{'_hi'});
if($win){
print "*** YOU WIN ! ***";
}
else{
print "*** GAME OVER ***";
}
}
sub is_over{
my($self) = @_;
my $remain = ($self->{'_mc'} + $self->{'_total'} == 0);
if($self->{'_over'} || $remain){
$self->over($remain);
return true;
}
else{
return false;
}
}
sub quit{
my($self) = @_;
$self->{'_hi'}->times(sub{ my($y) = @_;
$self->pos(0, $y);
$self->{'_wi'}->times(sub{ my($x) = @_;
$self->colorstr(
$self->{'_state'}->aref($y*$self->{'_wi'}+$x) == "MARK"
? $Default : $Over,
$self->fetch($x, $y) == 1
? $CHR[10] : $CHR[ $self->count($x, $y) ]);
});
});
}
sub down
{
my($self) = @_;
if($self->{'_cy'} < $self->{'_hi'}-1){
$self->{'_cy'} += 1;
$self->pos();
}
}
sub up
{
my($self) = @_;
if($self->{'_cy'} > 0){
$self->{'_cy'} -= 1;
$self->pos();
}
}
sub left
{
my($self) = @_;
if($self->{'_cx'} > 0){
$self->{'_cx'} -= 1;
$self->pos();
}
}
sub right
{
my($self) = @_;
if($self->{'_cx'} < $self->{'_wi'}-1){
$self->{'_cx'} += 1;
$self->pos();
}
}
}