111 lines
2.9 KiB
Raku
111 lines
2.9 KiB
Raku
enum Tile-Type <Empty Mine>;
|
|
|
|
class Tile {
|
|
has Tile-Type $.type;
|
|
has $.face is rw;
|
|
method Str { with $!face { ~$!face } else { '.' } }
|
|
}
|
|
|
|
class Field {
|
|
has @.grid;
|
|
has Int $.width;
|
|
has Int $.height;
|
|
has Int $.mine-spots;
|
|
has Int $.empty-spots;
|
|
|
|
method new (Int $height, Int $width, Rat $mines-ratio=0.1) {
|
|
|
|
my $mine-spots = round $width*$height*$mines-ratio;
|
|
my $empty-spots = $width*$height - $mine-spots;
|
|
|
|
my @grid;
|
|
for ^$height X ^$width -> ($y, $x) {
|
|
@grid[$y][$x] = Tile.new(type => Empty);
|
|
}
|
|
for (^$height).pick($mine-spots) Z (^$width).pick($mine-spots) -> ($y, $x) {
|
|
@grid[$y][$x] = Tile.new( type => Mine);
|
|
}
|
|
self.bless(:$height, :$width, :@grid, :$mine-spots, :$empty-spots);
|
|
}
|
|
|
|
method open( $y, $x) {
|
|
return if @!grid[$y][$x].face.defined;
|
|
|
|
self.end-game("KaBoom") if @!grid[$y][$x].type ~~ Mine;
|
|
|
|
my @neighbors = gather do
|
|
take [$y+.[0],$x+.[1]]
|
|
if 0 <= $y + .[0] < $!height && 0 <= $x + .[1] < $!width
|
|
for [-1,-1],[+0,-1],[+1,-1],
|
|
[-1,+0], [+1,+0],
|
|
[-1,+1],[+0,+1],[+1,+1];
|
|
|
|
my $mines = +@neighbors.grep: { @!grid[.[0]][.[1]].type ~~ Mine };
|
|
|
|
$!empty-spots--;
|
|
@!grid[$y][$x].face = $mines || ' ';
|
|
|
|
if $mines == 0 {
|
|
self.open(.[0], .[1]) for @neighbors;
|
|
}
|
|
self.end-game("You won") if $!empty-spots == 0;
|
|
}
|
|
|
|
method end-game(Str $msg ) {
|
|
for ^$!height X ^$!width -> ($y, $x) {
|
|
@!grid[$y][$x].face = '*' if @!grid[$y][$x].type ~~ Mine
|
|
}
|
|
die $msg;
|
|
}
|
|
|
|
method mark ( $y, $x) {
|
|
if !@!grid[$y][$x].face.defined {
|
|
@!grid[$y][$x].face = "⚐";
|
|
$!mine-spots-- if @!grid[$y][$x].type ~~ Mine;
|
|
}
|
|
elsif !@!grid[$y][$x].face eq "⚐" {
|
|
undefine @!grid[$y][$x].face;
|
|
$!mine-spots++ if @!grid[$y][$x].type ~~ Mine;
|
|
}
|
|
self.end-game("You won") if $!mine-spots == 0;
|
|
}
|
|
|
|
constant @digs = |('a'..'z') xx *;
|
|
|
|
method Str {
|
|
[~] flat ' ', @digs[^$!width], "\n",
|
|
' ┌', '─' xx $!width, "┐\n",
|
|
join '', do for ^$!height -> $y {
|
|
$y, '│', @!grid[$y][*], "│\n"; },
|
|
' └', '─' xx $!width, '┘';
|
|
}
|
|
|
|
method valid ($y, $x) {
|
|
0 <= $y < $!height && 0 <= $x < $!width
|
|
}
|
|
}
|
|
|
|
sub a2n($a) { $a.ord > 64 ?? $a.ord % 32 - 1 !! +$a }
|
|
|
|
my $f = Field.new(6,10);
|
|
|
|
loop {
|
|
say ~$f;
|
|
my @w = prompt("[open loc|mark loc|loc]: ").words;
|
|
last unless @w;
|
|
unshift @w, 'open' if @w < 2;
|
|
my ($x,$y) = $0, $1 if @w[1] ~~ /(<alpha>)(<digit>)|$1=<digit>$0=<alpha>/;
|
|
$x = a2n($x);
|
|
given @w[0] {
|
|
when !$f.valid($y,$x) { say "invalid coordinates" }
|
|
when /^o/ { $f.open($y,$x) }
|
|
when /^m/ { $f.mark($y,$x) }
|
|
default { say "invalid cmd" }
|
|
}
|
|
CATCH {
|
|
say "$_: end of game.";
|
|
last;
|
|
}
|
|
}
|
|
|
|
say ~$f;
|