TL;DR

On with TASK #2 from The Weekly Challenge #126. Enjoy!

# The challenge

You are given a rectangle with points marked with either x or *. Please consider the x as a land mine.

Write a script to print a rectangle with numbers and x as in the Minesweeper game.

A number in a square of the minesweeper game indicates the number of mines within the neighbouring squares (usually 8), also implies that there are no bombs on that square.

Example

Input:
x * * * x * x x x x
* * * * * * * * * x
* * * * x * x * x *
* * * x x * * * * *
x * * * x * * * * x

Output:
x 1 0 1 x 2 x x x x
1 1 0 2 2 4 3 5 5 x
0 0 1 3 x 3 x 2 x 2
1 1 1 x x 4 1 2 2 2
x 1 1 3 x 2 0 0 1 x


# The questions

I think the puzzle is pretty clear, I would probably investigate a bit about the input and output formats and what to do with weird inputs, but nothing really insightful I guess.

# The solution

The basic idea of the approach in this solution is that we scan the input field, looking for mines. As soon as we find one, we add 1 unit to all surrounding positions in the output rendering, making sure to preserve previously set mines.

I started with a full solution in Perl also for this challenge, again for technical reasons.

sub reveal_solution ($field) { # this will keep the "revealed" field my @retval; # we need address cells in the @retval grid, so we have to iterate # over indices of the input array instead of on the rows directly for my$ri (0 .. $field->$#*) {
my $row =$field->[$ri]; # same for columns, we need the index and we get it in$ci
for my $ci (0 ..$row->$#*) { # make sure that the element is initialized.$retval[$ri][$ci] //= 0;

# after this, the only cell that is meaningful for us is the mine,
# as we will "propagate" its effects on the surrounding cells.
# This is efficient as long as there are *few* mines.
next if $row->[$ci] ne 'x';

# if the input field has a mine, the output has one too
$retval[$ri][$ci] = 'x'; # now we iterate over the 3x3 grid centered as ($ri, $ci), # making sure to ignore the central position (which cannot # influence itself) and that we don't go beyond the limits # of the input field.$rd is a "delta" for rows.
for my $rd (-1, 0, 1) { # This is a position in the output field that is influenced # by the mine we just found. Well, actually it's a row for # multiple positions. my$Ri = $ri +$rd;
next if $Ri < 0 ||$Ri > $field->$#*;

# similarly we do for column indexes
for my $cd (-1, 0, 1) { next unless$rd || $cd; # get rid of (0, 0) my$Ci = $ci +$cd;
next if $Ci < 0 ||$Ci > $row->$#*;
$retval[$Ri][$Ci] //= 0; # initialize if necessary next if$retval[$Ri][$Ci] eq 'x'; # don't overwrite mines
$retval[$Ri][$Ci]++; # increment close-by position } } } } return \@retval; }  Translating to Raku was… feasible:  sub reveal-solution (@field) { # this will keep the "revealed" field my @retval; # we need address cells in the @retval grid, so we have to iterate # over indices of the input array instead of on the rows directly for 0 .. @field.end ->$ri {
my @row := @field[$ri]; # same for columns, we need the index and we get it in$ci
for 0 .. @row.end -> $ci { # make sure that the element is initialized. @retval[$ri][$ci] //= 0; # after this, the only cell that is meaningful for us is the mine, # as we will "propagate" its effects on the surrounding cells. # This is efficient as long as there are *few* mines. next if @row[$ci] ne 'x';

# if the input field has a mine, the output has one too
@retval[$ri][$ci] = 'x';

# now we iterate over the 3x3 grid centered as ($ri,$ci),
# making sure to ignore the central position (which cannot
# influence itself) and that we don't go beyond the limits
# of the input field. $rd is a "delta" for rows. for -1, 0, 1 ->$rd {
# This is a position in the output field that is influenced
# by the mine we just found. Well, actually it's a row for
# multiple positions.
my $Ri =$ri + $rd; next if$Ri < 0 || $Ri > @field.end; # similarly we do for column indexes for -1, 0, 1 ->$cd {
next unless $rd ||$cd; # get rid of (0, 0)
my $Ci =$ci + $cd; next if$Ci < 0 || $Ci > @row.end; @retval[$Ri][$Ci] //= 0; # initialize if necessary next if @retval[$Ri][$Ci] eq 'x'; # don't overwrite mines @retval[$Ri][$Ci]++; # increment close-by position } } } } return @retval; }  It’s so much a translation that I also kept the comments. The main differences is that we’re never using references (which don’t exist in Raku) or with their counterparts as scalars holding sequential stuff. What was $field here is @field.

There is an interesting twist in the intermediate variable that goes through the rows. While in Perl we take the reference:

...
my $row =$field->[$ri]; ...  here in Raku we can use an array, provided that we bind it to the actual row. In other terms, not this: my @row = @field[$ri];  # THIS DOES NOT WORK PROPERLY


but this:

my @row := @field[\$ri]; # NOTE THE := INSTEAD OF =


I guess that something similar can be done with Perl too… but I’m happy like this!

Time’s up again, so thank you for reading and stay safe, folks!