# Frost qotw #7
# Tim and John Allwine

use Tk;
use Tk::PNG;
use strict;

my $mw = MainWindow->new(-title => "Freezing...");

# change these to what you want.
# a 50x50 with one ice seed is fast to watch
# a 400x200 took 1hr 20min on our twin 866 linux box
# note we get faster as the amount of vapor decreases

my $m = 100;
my $n = 100;
if (($m % 2) or ($n % 2)) {
     die "m and n have to be even\n";
}
my $grid = []; # the big picture

# *approximately* 20% humidity
for my $i (0..$m-1) {
     for my $j (0..$n-1) {
         $grid->[$i][$j] = int(rand(5))?" ":".";
     }
}
$grid->[$m/2][$n/2] = "*"; # center our seed

# uncomment these for more than one ice seed
#for(0..int(rand(4))+1) {
#    $grid->[int(rand($m))][int(rand($n))] = "*";
#}

my $pic = $mw->Photo(
             -format => "png",
             -width => $m,
             -height => $n
         ); # our output image

# define our colors
my $frost = {
     ' ' => "#000000", # vacuum
     '.' => "#0000ff", # vapor
     '*' => "#ffffff", # ice
};

# put vapor, vacuum, and our ice seed to the image
for my $i (0..$m-1) {
     for my $j (0..$n-1) {
         $pic->put(
                 [$frost->{$grid->[$i][$j]}],
                 -to => $i, $j, $i+1, $j+1
                 );
     }
}

$mw->Label(-image => $pic)->grid();

my $v = 0;
my $vapors = 1;
my $frame = 1;
$mw->update;

our $t;
$SIG{ALRM} = sub { warn "TICKS: $t\n"; exit };
alarm $ENV{TIMELIMIT} || 120;

# while there is still vapor
# do the problem
while($vapors) {
     $t++;
     $vapors = 0;
     for my $i (0..$m/2-1) {
         for my $j (0..$n/2-1) {
             my $x1 = 2*$i+$v;
             my $y1 = 2*$j+$v;
             my $x2 = (2*$i+1+$v)%$m;
             my $y2 = (2*$j+1+$v)%$n;
             my $square = join('',
                     $grid->[$x1][$y1],
                     $grid->[$x1][$y2],
                     $grid->[$x2][$y2],
                     $grid->[$x2][$y1]);
             my $isice = $square =~ /(\*)/;
             my $isvapor = $square =~ /(\.)/;
# we get faster as the amount of vapor
# decreases
             next unless $isvapor;
             $vapors = 1;
             unless($isice) {
# regex was faster than a for loop for
# neighborhood rotation
                 if(int(rand(2))) {
                     $square =~ s/(...)(.)/$2$1/;
                 } else {
                     $square =~ s/(.)(...)/$2$1/;
                 }
             } else {
                 $square =~ s/\./\*/g;
             }
# we have to split to get our coordinates back
             ($grid->[$x1][$y1],
              $grid->[$x1][$y2],
              $grid->[$x2][$y2],
              $grid->[$x2][$y1]) = split('', $square);
              $pic->put(
                      [$frost->{$grid->[$x1][$y1]}],
                      -to => $x1, $y1, $x1+1, $y1+1
                      );
              $pic->put(
                      [$frost->{$grid->[$x1][$y2]}],
                      -to => $x1, $y2, $x1+1, $y2+1
                      );
              $pic->put(
                      [$frost->{$grid->[$x2][$y1]}],
                      -to => $x2, $y1, $x2+1, $y1+1
                      );
              $pic->put(
                      [$frost->{$grid->[$x2][$y2]}],
                      -to => $x2, $y2, $x2+1, $y2+1
                      );
         }
     }
     $v = 1 - $v;
     $pic->write("frost_$frame.png", -format => "png");
     $frame++;
     $mw->update; # update the screen
}

# change the title of the window from "Freezing..."
# to "Frozen." when we are all done
$mw->configure(-title => "Frozen.");
MainLoop;

