#!/usr/bin/perl
#
# Like everyone else who's submitted, I enjoyed this one, even though my
# solution only does character graphics. I've been thinking about this all
# weekend, and I managed to get my program working in about 15 minutes,
# which was much quicker than I was expecting.
# 
# I don't think I've seen anyone else use my technique of alternating the
# Margolus neighbourhoods. On odd ticks of the clock, I preprocess the grid
# by rotating it across and up (or possibly down), and postprocess it by
# rotating it back to preserve the original layout. This keeps the processing
# of each neighbourhood simple, but I wish I'd thought of using perl's
# $array[-1] behaviour like one of the other posters did.
# 
# 
# 

use strict;

# Get the size of the grid, and the density of the vapour
# There isn't a check for a valid density, but there should be
# The default height isn't 24 because I always print a newline
my($m,$n,$density)=@ARGV;
$m||=80;
$n||=22;
$density||=0.2;
die "M and N must both be even\n"
  if $m%2 || $n%2;


# Initialise the grid with random vapour particles according to the density,
# and a central ice particle
# $x and $y indexes are reversed because that makes printing simpler
my @grid;
for my $x(0..$m-1){
  for my $y(0..$n-1){
    $grid[$y][$x]=rand()>$density ? ' ' : '.';
  }
}
$grid[$n/2][$m/2]='*';

# Now a big loop which does everything
my $tick; # This is the number of the current iteration
$SIG{ALRM} = sub { warn "TICKS: $tick\n"; exit };
alarm $ENV{TIMELIMIT} || 120;
my $vapour=1; # True if there's any vapour left
while($vapour){
  ++$tick;

  # Display the grid. I knew I should really use Term::Cap,
  # but it was easier to remember the common escape code
  print "\033[H";
  for my $y(0..$n-1){
    print join('',@{$grid[$y]}),"\n";
  }

  # If we're on an odd tick, rotate the grid one cell down and across.
  # This avoids the need for extra calculations during the next step.
  # Actually, someone else's trick of using 0 or -1 for the base is much simpler
  if($tick%2){
    my $row=shift @grid;
    push @grid,$row;
    for(0..$n-1){
      my $row=$grid[$_];
      my $cell=shift @$row;
      push @$row,$cell;
    }
  }

  # Process each neighbourhood
  $vapour=0;
  for(my $x=0;$x<$m;$x+=2){
    for(my $y=0;$y<$n;$y+=2){
      # Each neighbourhood is constructed as a string from the relevant cells
      # in the following order for easy rotation:
      #  01
      #  32
      my $margolus=join '',
        $grid[$y][$x],$grid[$y][$x+1],$grid[$y+1][$x+1],$grid[$y+1][$x];
      if($margolus=~/\*/){
        # Turn all vapour to ice if there's any ice
        # The 'next' avoids copying back into the grid if there's no vapour
        $margolus=~y/./*/
          or next;
      }elsif($margolus=~/\./){
        # If there's some vapour, remember we saw it, and rotate the cell
        ++$vapour;
        if(rand()<0.5){
          # Rotating strings is easy!
          $margolus=~s/(.)(...)/$2$1/;
        }else{
          $margolus=~s/(...)(.)/$2$1/;
        }
      }else{
        # There's no ice or vapour, so don't do anything
        next;
      }
      # Copy the neighbourhood back into the grid
      ($grid[$y][$x],$grid[$y][$x+1],$grid[$y+1][$x+1],$grid[$y+1][$x])
        =split //,$margolus;
    }
  }

  # Undo the rotation we did earlier for odd ticks to get back to the original
  # grid alignment
  if($tick%2){
    my $row=pop @grid;
    unshift @grid,$row;
    for(0..$n-1){
      my $row=$grid[$_];
      my $cell=pop @$row;
      unshift @$row,$cell;
    }
  }
}

__END__
-- 
	Peter Haworth	pmh@edison.ioppublishing.com
"Hey, set your dwarf on 'oscillating'."		-- MST3K
