#!/usr/bin/perl
# 
# This was fun.  No fancy output here, but I managed to get it done on my
# Zaurus during a meeting.  One of the most productive meetings I've been
# in.  Something like ./frost 78 30 looks quite nice on my screen.  I'm
# not using warnings because I wanted undef to quietly behave like 0.
# Yes, I could have been more specific about this.  Here are my copious
# notes:
# 

use strict;

my (@G, $W, $H);                    # grid, width, height
my ($U, $V, $I) = (" ", ".", "*");  # vacuum, vapour, ice
$" = "";

sub usage
{
    die "usage: $0 width height [percent_vapour percent_ice ticks]\n@_\n";
}

sub show
{
    system "clear";
    my $l = "+" . "-" x $W . "+\n";
    print $l, map("|@$_|\n", @G), $l;
#    select undef, undef, undef, 0.1;  # delay between generations
}

sub grid
{
    my ($x1, $y1) = @_;
    my ($x2, $y2) = (($x1 + 1) % $W, ($y1 + 1) % $H);
    my @g = \($G[$y1][$x1], $G[$y1][$x2], $G[$y2][$x2], $G[$y2][$x1]);
    if (grep $$_ eq $I, @g) {  # turn to ice
        for (@g) { $$_ = $I if $$_ eq $V }
    }
    elsif (grep $$_ eq $V, @g) {  # rotate
        my @n = map ${$g[$_]}, int (rand 2) ? (3, 0, 1, 2) : (1, 2, 3, 0);
        ${$g[$_]} = $n[$_] for 0 .. 3;
        return 1;  # simulation not finished
    }
}

sub tick
{
    my ($t) = @_;
    my $done = 1;  # simulation finished
    for my $y (0 .. $H / 2 - 1) {
        for my $x (0 .. $W / 2 - 1) {
            $done = 0 if grid $x * 2 + $t, $y * 2 + $t;
        }
    }
    show;
    $done
}

sub main
{
    usage unless @ARGV > 1;
    ($W, $H, my $v, my $i, my $t) = @ARGV;
    usage "width and height must be even" if $W % 2 || $H % 2;
    usage "make those percentages sensible, please"
        unless $v >= 0 && $i >= 0 && $v + $i <= 100;
    @G = map [($U) x $W], 1 .. $H;
    $v = 20 unless defined $v;  # default to 20% vapour
    for ([$v, $V], [$i, $I]) {
        my $n = int($_->[0] * $W * $H / 100 + 0.5);
        $n ||= 1 unless defined $_->[0];  # at least one, unless we want none
        while ($n--) {
            my ($x, $y) = (rand $W, rand $H);
            redo if $G[$y][$x] ne $U;
            $G[$y][$x] = $_->[1];
        }
    }
    show;
    our $TICK;
    for (0 .. ($t ||= 1e9) - 1) { $TICK++; return if tick $_ % 2 }
}

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

main
