=pod
-------------------------------------------------------------------------------
                             PERL SWEEPER version 3.633
                          - a marathon in boolean algebra
-------------------------------------------------------------------------------
                   A mine sweeper clone written in obfuscated Perl
                     by Andreas Hagelberg (aka The Logic Burner)
                            Copyright  A. Hagelberg 1999
-------------------------------------------------------------------------------


The central parts of the program are two one-dimensional arrays - '@m' and '@f'.
The '@m'-array contains all the mines, marked with '*', and the '@f'-array
contains all other characters displayed on the board (i.e. m/[\d#@ ]/). These
arrays are displayed on the terminal, using the subroutine '&o', as a grid of
32x20 in size. Note that the arrays are actually bigger than the actual
gaming-area (34x22) to accomodate the "frame".

   The program starts by generating the mine-array '@m' by setting some of
the cells in the mine-field-array to a '*'-character. The execution then enters
an infinite loop. The loop first displays the game-field ('@f') - by calling the
subroutine '&o' - and a small message with instructions. The next line then
fetches the input from the user via STDIN, split it up into seperate
characters, stores the first character in the standard-variable and the rest in
the array '@p', converts the first two characters from this array into
coordinates and calculates the correct array-index from these and performs a
check to make sure the array is within the boundaries of the game-field. If the
first entered character is an 'S' the line will continue by setting the cell
pointed at by the calculated index in the '@f' array to the return-value from
the '&f'-subroutine and check if this value is an '@', if this is the case the
infinite loop will be discontinued, if not and the return-value was not a digit
it will call the subroutine '&f' again, but this time with a true value as first
parameter (this performs the sweep). What this means is that if the specified
cell is empty and has no mine in it's vicinity the '&f'-subroutine will
recursively find all surrounding emtpty cells.

   If the first character is a 'Q', the loop will be exited; if it is an 'M'
the specified cell will be marked (the specified cell in array '@f' will be set
to a '#'); and if it is an 'U', the cell will be cleared.

   The subroutine '&o' will, as said before, display one of the arrays on the
terminal. The screen will be cleared via an escpae-sequence if required.
The for-loop will then print the entire game-field using the array sent to it
(as a reference). It will begin by setting the variable '$f' to the
x-coordinate; if the current cell happens to be the leftmost or the rightmost
cell, set it instead to the y-coordinate, otherwise and if it's not the first or
last row, set it to the corresponding cell from the array. Print '$f' at the
correct location on the terminal (once again using an escape-seqence). If the
variable '$f' happens to be empty, print instead the second parameter sent to
the subroutine (either empty or '+'); this is done to make a difference between
swept and unswept cells.

   Subroutine '&f' is used for two things - to count the number of mines in the
vicinity of a cell, and to sweep all cells surrounding an empty cell. If the
specified cell contains a mine it will return an '@', otherwise it will cycle
through all eight cell surrounding the specified cell. If the current cell
contains a mine, increase '$p'. If the mode is set to sweep (a true value as
first parameter), the current cell lies within the game-field, the current cell
has not been marked or been swept before and the current cell is not the same as
the one that was set to be swept, then sweep the cell. If the cell is then
discovered to be empty, call the subroutine again recursively. Return the number
of mines found in the cells vicinity or a space if no mines were found (the
latter is done to get nice, clean, empty areas and not a bunch of zeroes).



Below follows a more detailed description of the program:
--------------------------------------------------------------------------------
=cut


# Initialize the random number generator
srand;


# Sub used for making sure a given array-index lies within the game-field
# If it does, the sub returns the same number that was sent to it; if not
# zero is returned
sub d{
	$n if($n=pop)%34&&$n%34<33&$n>34&$n<713
}


# Sub used for converting coordinates given in ascii to the corresponding
# real coordinate. The variable '$m' is also set to zero here (must be reset
# befor sub '&o' is called, since this sub uses this variable.
sub p{
	$p-=($p=ord$_[$m=0])>58?55:48
}


# Generate the mine-field (sub d used to make sure no mines are put outside
# the actual gaming-area.
for(35..712){
	$m[$_]='*'if rand 8<1&&d$_
}


# This is the main-loop.
for(;;){

	# Call sub '&o' to draw the game-field. The second parameter tells
	# the sub that all unswept cells should be padded with a '+'-sign
	# rather than be left empty. Sub '&o' also checks wether all mines have
	# been marked or not, that not too many markes have been made and that
	# all empty cells have been swept. If all these conditions are met the
	# variable '$m' will contain zero. This line will then either print the
	# help message or exit the program, depending on the status of this
	# variable.
	o(\@f,'+',8)|$m&&print"m??=mrk;u??=unmrk;s??=sweep;q=quit$:"
	or die"Done$/";

	# Get the user input, split it up and store the characters in '$_' and
	# '@p'. Convert the coordinates using sub '&p', calculate the
	# corresponding array-index, store it in '$o', and make '$f' into a
	# reference pointing to the '$o':th cell of the '@f'-array. Check if the
	# content of this cell is not equal to '$0' ($0 is used here for two
	# two reasons - it must contain a true value before the mainloop is run
	# the first time, and it makes the code more obfuscated. It will
	# eventually contain a '#' rather than the program name), store this
	# comparsion in the variable '$p' for later use and then check if the
	# command ('$_') is 'S'. If these conditions are met, assign the return
	# value from the sub '&f' to the cell pointed to by '$f'. If this return
	# value is an '@' the player has hit a mine and therefor the loop should
	# be exited; if not, and the return value is not a digit (e.i. the cell
	# is empty and has no mines in its' vicinity) perform a sweep of the
	# cell by calling sub '&f' again, but with a true value as first
	# parameter this time.
	($$f=f(0,$o))eq'@'&&last or$$f!~/\d/&&f(7,$o)
	if($p=${$f=\$f[$o=d 34*p((($_,@p)=split//,uc<>)[2])+p@p]}ne$0)&/S/;

	# Is the command 'Q' then exit the loop.
	/Q/&&last;

	# If the command is 'M' or 'U' and the cell does not contain a '#'
	# (the comparsion from the previous line is reused here via the
	# variable '$p'), then if the command is 'M', assign '#' to '$0' and
	# then assign this to the cell pointed to by '$f', otherwise set this
	# cell to zero.
	$$f=/M/?$0='#':0if/M|U/&!$p|!$$f
}


# Since the main-loop has been exited the game is ended and therefore the mine-
# field is displayed useing sub '&o'. A second parameter is not given since no
# padding should be done.
o(\@m,,9);


# This subroutine does almost the opposite of sub '&p' - it converts a given
# coordinate into ascii to be shown on the terminal. The converted token is
# stored in '$f'.
sub k{
	$f=chr$_[0]+($_[0]<11?47:54)
}


# Subroutine for displaying the gameing-area, either using the 'symbol'-array
# '@f' or the mine-array '@m'. First parameter is a reference to the array to
# be drawn, second is the character to de printed on all unswept cells and the
# third tells whether the display should be cleared or not before the drawing
# begins.
sub o{

	# Clear the terminal window before drawing?
	pop&&print"\e[2J";

	# Loop through all cells of the array.
	for(1..747){

		# Calculate coordinates from the index and set '$f' to the
		# x-coordinate (via sub k). The second parameter is not used
		# by the sub, I just put it here rather than on a line of its
		# own to confuse; it doesn't take any extra space anyway.
		k($o=1+int$_/34,$p=$_%34+1);

		# Is the current index on the right or left border, then set
		# '$f' to the y-coordinate instead (via sub k), otherwise, if
		# the index resides inside the actual gameing-area, set '$f' to
		# the corresponding character from the array to be displayed
		# (first parameter) and if this cell is not empty, increase
		# '$m' (that was reset earlier), otherwise if the current cell
		# is marked but there is no mine here, increase '$m'.
		!($o^1&&$o^22)&&k$p or d$_
		and!($f=$_[0][$_])&&++$m||$f eq$0and$m[$_]||++$m;

		# Finally plot '$f' in the right place on the terminal, or if
		# '$f' is empty, plot the character sent as the second
		# parameter to the sub (either '+' or undef). Also, if we're on
		# the last row, print a line feed at the end to make the
		# help-message and the "Done"-message to be printed neatly
		# underneath the grid, rather than to the right of it. The
		# comparsion '$o>21' uses up an extra seven bytes and is not
		# really neccesary, but the speed-gain was so vast that I
		# decided to keep it.
		print"\e[$o;$p\H",$f||$_[1],$o>21&&$/
	}
}


# This subroutine is used for two things - to count the number of mines within
# the vicinity of a cell and to reveal empty areas. The latter is done
# recursively.
sub f{

	# If there is a mine at the given location, return an '@'. '$p' is set
	# to the return-value of this statement which will always be zero
	# unless there is a mine and in that case the sub will be exited anyway.
	# I could of course have written '$p=0;' instead, but that wouldn't be
	# as much fun and this construction doesn't take any extra space anyway.
	$p=($m[my$f=pop]&&return'@');

	# Loop through all eight cells surrounding the given cell.
	for(-4..4){

		# Calculate the actual index of the cell to be examined and
		# if there is a mine at the location, increase '$p'.
		$m[my$m=$_+($_<-1?-31:$_>1?31:0)+$f]&&$p++;

		# If the current cell has not been swept before and it lies
		# within the game-field and the mode of the subroutine is set
		# to "sweep" (true value as first parameter) then make a new
		# call to this sub (in "counting" mode) and assign the return
		# value to the current cell (in the '@'-array). If this value
		# is a space (no mines found near the cell) then call this sub
		# again recursivley (in "sweep mode") and the new cell will be
		# swept.
		$_[0]&$_&&($f[$m]=f(0,$m))eq$"&&f(7,$m)if!$f[$m]&&d$m
	}

	# Return the number of mines found in the vicinity of the given cell
	# or, if no mines were found, return a space (to make large empty areas
	# look neater - no zeroes).
	$p||$"
}


# This call is made immediately after the mine-array has been displayed on the
# terminal. This call will display the "symbol"-array, with no padding on
# unswept areas and without clearing the screen beforehand. This will cause the
# array to be displayed "ontop" of the mine-field, thus showing all swept areas
# and all markers while still showing all unmarked mines - and the exploded
# mine. This is done so that you can see where you went wrong.
o(\@f,0)
