#!/usr/bin/perl -w

# frost.pl, simulate frost formation
# For Perl "Expert" QOTW #7
# Dan Sanderson, 2002/11/27

use strict;

sub do_usage {
    my ($msg) = (@_);
    if (defined($msg)) { print $msg, "\n"; }
    print <<'END_OF_USAGE';
Usage:
 frost.pl [density [width [height]]]
    density    Percent of cells that start as vapor, as a fractional
                 decimal number.  Default is 0.5.
    width      Number of cells across.  Must be an even integer > 0.
                 Default is 40.
    height     Number of cells wide.  Must be an even integer > 0.
                 Default is 20.
END_OF_USAGE

    exit(1);
}

sub initialize_region {
    my ($region_ref, $density, $region_width, $region_height) = (@_);

    @$region_ref = ();
    for (my $y = 0; $y < $region_height; $y++) {
	my $line = "";
	for (my $x = 0; $x < $region_width; $x++) {
	    if (rand() < $density) {
		$line .= '.';
	    } else {
		$line .= ' ';
	    }
	}
	if ($y == $region_height/2) {
	    substr($line, $region_width/2, 1, '*');
	}
	push @$region_ref, $line;
    }
}

sub increment_region {
    my ($region_ref, $is_odd_time) = (@_);
    my $region_width = length($$region_ref[0]);
    my $region_height = scalar(@$region_ref);
    for (my $y = 0; $y <= $region_height - 2; $y += 2) {
	for (my $x = 0; $x <= $region_width - 2; $x += 2) {
	    my $left = $x + $is_odd_time;
	    my $right = ($x == $region_width - 2 && $is_odd_time) ? 0 : $x + $is_odd_time + 1;
	    my $top = $y + $is_odd_time;
	    my $bottom = ($y == $region_height - 2 && $is_odd_time) ? 0 : $y + $is_odd_time + 1;
	    my $neighborhood =
		substr($$region_ref[$top], $left, 1)
		.substr($$region_ref[$top], $right, 1)
		.substr($$region_ref[$bottom], $left, 1)
		.substr($$region_ref[$bottom], $right, 1);

	    if ($neighborhood =~ m(\*)) {
		$neighborhood =~ tr(\.)(\*);
	    } else {
		if (rand() >= 0.5) {
		    $neighborhood =~ s((.)(.)(.)(.))($2$4$1$3);
		} else {
		    $neighborhood =~ s((.)(.)(.)(.))($3$1$4$2);
		}
	    }

	    substr($$region_ref[$top], $left, 1) = substr($neighborhood, 0, 1);
	    substr($$region_ref[$top], $right, 1) = substr($neighborhood, 1, 1);
	    substr($$region_ref[$bottom], $left, 1) = substr($neighborhood, 2, 1);
	    substr($$region_ref[$bottom], $right, 1) = substr($neighborhood, 3, 1);
	}
    }
}

sub main {

    my $density = $_[0] || 0.5;
    my $region_width = $_[1] || 40;
    my $region_height = $_[2] || 20;

    if ($density !~ /\d*\.?\d+/
	|| $density < 0
	|| $density > 1) {
	do_usage("Please specify density as a decimal fraction between 0 and 1.");
    }
    if ($region_width % 2 != 0
	|| $region_height % 2 != 0
	|| $region_width <= 0
	|| $region_height <= 0
	) {
	do_usage("Please specify width and height as even integers greater than 0.");
    }

    my @region;

    initialize_region(\@region, $density, $region_width, $region_height);

    our $t = 0;
    my $inkey = "y";
    my $auto_on = 0;
    while ($auto_on || lc($inkey) ne 'n') {
	print "T=$t\n", join("\n", @region), "\n\n";
	if (!$auto_on) {
	    while ($inkey ne 'y' && $inkey ne 'n' && $inkey ne 'auto') {
		print "Continue? [Y/n/auto] ";
		$inkey = <STDIN>;
		chomp $inkey;
		$inkey = lc($inkey);
	    }
	    if ($inkey eq 'auto') {
		$auto_on = 1;
	    }
	} else {
#	    sleep(1);
	}
	if ($auto_on || $inkey ne 'n') {
	    $t++;
	    increment_region(\@region, $t % 2);
	}
    }
}

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


main(@ARGV);
exit(0);

__END__




