#!/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(); } } }