PsionicMan has asked for the wisdom of the Perl Monks concerning the following question:

Ok, I've got a question for you folks. While on a short weekend vacation with my family, I threw together a little minesweeper game to keep myself occupied. However, I've not been able to figure out how to code it so that when a square with no mines next to it is selected, it automagically uncovers all of the surrounding non-mine spaces.
Here's the program so you can see how I've set it all up:

#!/usr/bin/perl -w use strict; srand; # The coordinate system is row x col. # When I wrote all of this (the whole program), I forgot that the firs +t element of an array is 0, not one. # However, it appears to still work, since I used the 1-9 (not 0-8) in + the format etc.(i.e., I was # consistant). Still, it means there's a 0 element in each sitting ar +ound unused. Someday, perhaps, # I'll get around to fixing it. Maybe not, though, as it's obviously e +asier for my brain to comprehend it # this way--that's why I've messed up in the first place. # Oh, and it would cause trouble in the random placing of the mines ha +d I not fiddled with the values. my @grid; my @blankgrid; format minegrid = Wacky Fun Minesweeper!!!! 1 2 3 4 5 6 7 8 9 ----------------- 1| @ @ @ @ @ @ @ @ @ $blankgrid [1] [1], $blankgrid [1] [2], $blankgrid [1] [3], $blankgrid + [1] [4], $blankgrid [1] [5], $blankgrid [1] [6], $blankgrid [1] [7], + $blankgrid [1] [8], $blankgrid [1] [9] 2| @ @ @ @ @ @ @ @ @ $blankgrid [2] [1], $blankgrid [2] [2], $blankgrid [2] [3], $blankgrid + [2] [4], $blankgrid [2] [5], $blankgrid [2] [6], $blankgrid [2] [7], + $blankgrid [2] [8], $blankgrid [2] [9] 3| @ @ @ @ @ @ @ @ @ $blankgrid [3] [1], $blankgrid [3] [2], $blankgrid [3] [3], $blankgrid + [3] [4], $blankgrid [3] [5], $blankgrid [3] [6], $blankgrid [3] [7], + $blankgrid [3] [8], $blankgrid [3] [9] 4| @ @ @ @ @ @ @ @ @ $blankgrid [4] [1], $blankgrid [4] [2], $blankgrid [4] [3], $blankgrid + [4] [4], $blankgrid [4] [5], $blankgrid [4] [6], $blankgrid [4] [7], + $blankgrid [4] [8], $blankgrid [4] [9] 5| @ @ @ @ @ @ @ @ @ $blankgrid [5] [1], $blankgrid [5] [2], $blankgrid [5] [3], $blankgrid + [5] [4], $blankgrid [5] [5], $blankgrid [5] [6], $blankgrid [5] [7], + $blankgrid [5] [8], $blankgrid [5] [9] 6| @ @ @ @ @ @ @ @ @ $blankgrid [6] [1], $blankgrid [6] [2], $blankgrid [6] [3], $blankgrid + [6] [4], $blankgrid [6] [5], $blankgrid [6] [6], $blankgrid [6] [7], + $blankgrid [6] [8], $blankgrid [6] [9] 7| @ @ @ @ @ @ @ @ @ $blankgrid [7] [1], $blankgrid [7] [2], $blankgrid [7] [3], $blankgrid + [7] [4], $blankgrid [7] [5], $blankgrid [7] [6], $blankgrid [7] [7], + $blankgrid [7] [8], $blankgrid [7] [9] 8| @ @ @ @ @ @ @ @ @ $blankgrid [8] [1], $blankgrid [8] [2], $blankgrid [8] [3], $blankgrid + [8] [4], $blankgrid [8] [5], $blankgrid [8] [6], $blankgrid [8] [7], + $blankgrid [8] [8], $blankgrid [8] [9] 9| @ @ @ @ @ @ @ @ @ $blankgrid [9] [1], $blankgrid [9] [2], $blankgrid [9] [3], $blankgrid + [9] [4], $blankgrid [9] [5], $blankgrid [9] [6], $blankgrid [9] [7], + $blankgrid [9] [8], $blankgrid [9] [9] . $~ = "minegrid"; my $v; my $w; my $x; my $y; my $count = 0; my $mineletter = "*"; my $minehit = 0; my $guess; my $empty = 71; # Even though we have a total of 81 spaces, the amount needed to be cl +eared without hitting # a mine is 71. This is because 10 of the spaces are the mines themse +lves. # We will use "_" to mark a square that has not been uncovered, "*" to + mark a mine, "0" to # mark a blank, and the appropriate number for a square next to a mine +. The user can mark # what they think are mines; these are designated as "m" on the board. # @blankgrid is what we will be showing to the user. @grid is where w +e will store the values # that will be shown when the user selects a square. # When first run, all squares are set to "_" (unselected). for ($x=1;$x<=9;$x++) { for ($y=1;$y<=9;$y++) { $blankgrid [$x] [$y] = "_"; } } # To begin placing the different things, we set all to "0" (blank, not +hing). for ($x=1;$x<=9;$x++) { for ($y=1;$y<=9;$y++) { $grid [$x] [$y] = 0; } } # This little gem of a while loop is the mine placement code. while ($count < 10) { $v = (rand 7) + 2; $w = (rand 7) + 2; unless (($grid [$v] [$w]) eq $mineletter) { $grid [$v] [$w] = $mineletter; $grid [$v] [$w - 1] ++ unless $grid [$v] [$w - 1] eq $ +mineletter; $grid [$v] [$w + 1] ++ unless $grid [$v] [$w + 1] eq $ +mineletter; $grid [$v - 1] [$w] ++ unless $grid [$v - 1] [$w] eq $ +mineletter; $grid [$v - 1] [$w - 1] ++ unless $grid [$v - 1] [$w - + 1] eq $mineletter; $grid [$v - 1] [$w + 1] ++ unless $grid [$v - 1] [$w + + 1] eq $mineletter; $grid [$v + 1] [$w] ++ unless $grid [$v + 1] [$w] eq $ +mineletter; $grid [$v + 1] [$w - 1] ++ unless $grid [$v + 1] [$w - + 1] eq $mineletter; $grid [$v + 1] [$w + 1] ++ unless $grid [$v + 1] [$w + + 1] eq $mineletter; $count ++; } } # And here is the main program. Everything prior was set-up--here's w +here # we get the user's input, and adjust the display/score/etc appropriat +ely. while ($minehit == 0 && $empty > 0) { write; print "$empty non-mine spaces left uncovered.\ntype 'quit' to +quit\nEnter a coordinate, row first, seperated by a comma. (ex 1,1).\ +nprefix with m to mark a space\n: "; $guess = <STDIN>; chomp $guess; last if ($guess eq "quit"); if ($guess =~ /m(\d),(\d)/i) { if ("$blankgrid[$1][$2]" eq "_") { $blankgrid[$1][$2] = "m"; next; } if ("$blankgrid[$1][$2]" eq "m") { $blankgrid[$1][$2] = "_"; next; } print "Only uncovered spaces can be marked.\n"; next; } unless ($guess =~ /(^\d),(\d)/) { print "That's not a valid coordinate.\n"; next; } if (("$blankgrid[$1][$2]" eq "$grid[$1][$2]")) { next; } if ("$blankgrid[$1][$2]" eq "m") { print "You've marked that as a mine. You can't select + it until you unmark it\nby doing m$1,$2.\n"; next; } $blankgrid [$1] [$2] = $grid [$1] [$2]; if ($blankgrid [$1] [$2] eq $mineletter) { @blankgrid = @grid; write; print "Sorry, you lose.\n"; last; } $empty --; } # Don't want to congratulate them if they lost or quit... :) print "Congratulations, you've won the game!\n" if ($empty == 0);

Also, any constructive criticism would be appreciated. But mostly, I would like to understand how to do the whole auto-uncover thing.

--Psi print(pack("h*","e4f64702566756e60236c6f637560247f602265696e676021602075627c602861636b65627e2")."\n");

Edit 2001-03-29 by tye to wrap the last line

Replies are listed 'Best First'.
Re: Need help with a minesweeper game
by ChOas (Curate) on Mar 29, 2001 at 13:56 UTC
    Hey there,

    Hint: Use recursion...

    I wrote minesweeper as my first perl program ever,
    you can find it here, check it for some maybe useful
    stuff

    It`s VERY simply set up... I wouldn`t code like that
    anymore, yet still... it might help

    GreetZ!,
      ChOas

    print "profeth still\n" if /bird|devil/;
Re: Need help with a minesweeper game
by greenFox (Vicar) on Mar 29, 2001 at 14:16 UTC

    I have always been a minesweeper junky... I played for 1/2 an hour before looking at your code <G>

    I think you have already partially solved this problem with your mine placement magic, walk through the grid and use the logic you used for incrementing the number of mines next to a particular location to see what the surrounding values are...

    some comments on the rest of your code if I may :)
    that format is waaaaay ugly :) you could replace it with a sub like this-
    sub draw_grid { print "# Wacky Fun Minesweeper!!!!\n"; for my $x (0..9) { print $x, ' '; for my $y (1..9) { if ($x == 0){ print $y, ' '; } else { print $blankgrid[$x][$y], ' '; } } print "\n"; } }

    now instead of using write call draw_grid();

    you can initialise both grids in the one double loop- (for modularity I would place the grid initialisation and the mine placement code in subroutines as well)

    for my $x (1..9) { for my $y (1..9) { $blankgrid[$x][$y] = "_"; $grid[$x][$y] = 0; } }

    your mainloop has a variable $minehit that you never use and I would replace $empty with something like $untested_squares so your main while loop becomes while ($untested_squares) { which IMO is nicer to read :)

    Further optimisation left to the monastry gurus... I am going to go play some more :-)

    --
    my $chainsaw = 'Perl';

Re: Need help with a minesweeper game
by dvergin (Monsignor) on Mar 29, 2001 at 14:19 UTC
    Another good strategy for games of this kind is to include in your data, a row of blank, never-used cells on each edge of the data map. This makes the check routine much cleaner. Checking an edge or corner cell does not require special code to not look at adjacent squares that are not there. They are there. They are always blank. They are never displayed. And the check-adjacent-cells routine doesn't care. It's just happy to be able to check edge cells as if they were any other cell.
Re: Need help with a minesweeper game
by suaveant (Parson) on Mar 29, 2001 at 20:00 UTC
    What I would do... store your point that is blank in an array @free = ([2,3]) Then do a while($ary = shift @free) Which will give you the first free point. Then write some code that looks at all the all the adjacent boxes. Open them all up, and if any of them have no mines near them, do a push @free, (new,point); What that will do is use @free as a stack, and basically recurse through it analyzing all necessary points until you are done.

    You should probably also use a hash to store which points you have visited around the stack and ignore them. something like

    # point is 3,5 $x = 3 $y = 5 next if $seen{"$x,$y")++; #checks and then increments in one line
    Otherwise you could just loop infinately :) - Ant