Perl Weekly Challenge 126: Minesweeper Game

by Abigail

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

Solution

This is pretty simple to solve. We first read in the entire input, putting the board into a two dimensional array. Then, for each cell (x, y), if it contains a mine (x), we print the mine, else for each of its neigbhouring cells, we tally the number of mines, and print this tally.

The get all the neigbhouring cells, we look at all the cells (x + dx, y + dy), with -1 <= dx <= 1 and -1 <= dy <= 1. To handle cells at the border, we check that x + dx and y + dy are inside the boundary values; if not, we skip those. We also skip counting ourselves, (that is, we require at least one of dx or dy to be non-zero), although that is not strictly required as we have already considered the case where the current cell contains a mine.

Perl

First, we read in the input, and initialize the sizes:

my @input = map {[/\S/g]} <>;

my $X =   @input;
my $Y = @{$input [0]};

We now loop over each of the cells:

for my $x (0 .. ($X - 1)) {
    foreach my $y (0 .. ($Y - 1)) {

If the cell contains a mine, we just print the mine:

if ($input  [$x] [$y] eq 'x') {
    print 'x';
    next;
}

Otherwise, we count the mines at our neighbours, at print the count. We have to take care that we're not indexing outside of the array, and we don't count ourselves.

my $mines = 0;
for my $dx (-1 .. 1) {
    next if $x + $dx < 0 || $x + $dx >= $X;     # Above or below board
    for my $dy (-1 .. 1) {
        next if $y + $dy < 0 || $y + $dy >= $Y; # Right or left of board
        next unless $dx || $dy;                 # Skip self
        $mines ++ if $input [$x + $dx] [$y + $dy] eq 'x';
    }
}
print $mines;

Find the full program on GitHub.

Other languages

We also have very similar solutions in AWK, C, Lua, and Node.js.


Please leave any comments as a GitHub issue.