#!/usr/bin/perl -w # # A "deobfuscated" version of "Evolutionary Japh" # # 061128 liverpole # # Strict use strict; use warnings; # Libraries use Tk; use Data::Dumper; use FileHandle; # Global variables my @cells; # Current generation of cells my @nextgen; # Next generation of cells my @dimension = (0..63); # One dimension (X or Y) my @ids; # IDs of each "dot" in the canvas object my $off_color; # Color of a "dead" cell (canvas background) my $on_color = "blue"; # Color of a "living" cell my ($x, $y); # Current coordinates # Command-line my $filename = shift || 0; # Pattern filename (default = random screen) my $rulename = shift || '23/3'; # Which rule? (Conway's 23/3 is default) # Main program # Initialize the "rules hash". After this step, using the default rules, # %rules will be: # { # '1' => { '3' => 1, '2' => 1 }, # '0' => { '3' => 1 } # }; # my $living = 0; my %rules = ( ); # Initialize the "rules hash" foreach my $digits (split('/', $rulename)) { # Living first (living = 1), followed by dead (living = 0) $living ^= 1; foreach my $digit (split(//, $digits)) { $rules{$living}{$digit} = 1; } } # Setup the GUI my $mw = new MainWindow(-title => $rulename); my $canvas = $mw->Canvas(-width => 514, -height => 514)->pack(); $off_color = cget $canvas(-bg); $mw->after(1, \&setup_and_evolve_forever); MainLoop; # # The main program loop. # sub setup_and_evolve_forever { # Draw grid lines on the canvas foreach my $value (0..65) { my $a = 2 + 8 * $value; $canvas->createLine(2, $a, 515, $a); $canvas->createLine($a, 2, $a, 515); } if ($filename) { # Use a file for the initial pattern my $fh = new FileHandle; open($fh, "<", $filename); my $line; $y = 0; while ($line = <$fh>) { chomp $line; my $x = 0; foreach my $char (split(//, $line)) { my $is_living = ($char eq '@')? 1: 0; evolve_cell($x, $y, $is_living); ++$x; } ++$y; } } else { # Create a random initial pattern. # Chances of a cell being "alive" are 1 in 4. # foreach $x (@dimension) { foreach $y (@dimension) { evolve_cell($x, $y, (1 > rand 4)? 1: 0); } } } # Evolve forever ... while (1) { # First, plot the next generation of cells foreach my $p (@dimension) { foreach my $q (@dimension) { my $nneighbors = 0; my $this_cell = 0; foreach my $v (-1..1) { foreach my $w (-1..1) { $x = $p + $v; $y = $q + $w; my $is_living = evolve_cell($x, $y, -1); if (0 == $w && 0 == $v) { $this_cell = $is_living; } $nneighbors += $is_living; } } # Assign cell based on current state and number of neighbors $nneighbors = $rules{$this_cell}{$nneighbors-$this_cell}; $nextgen[64 * $p + $q] = $nneighbors; } } # Secondly, assign (and display) the next generation foreach $x (@dimension) { foreach $y (@dimension) { evolve_cell($x, $y, $nextgen[64 * $x+$y] || 0); } } $mw->update(); } } # Given (x, y) coordinates (x = $1, y = $2) and a state $3 (which can # be 0 (dead), 1 (alive), or -1 (don't modify, just return the state)), # assigns the new state to the given cell (if the state is not -1), and # returns the state of the cell. # sub evolve_cell { my ($x, $y, $z) = @_; # Get cell's state (0 = dead, 1 = alive) my $state = $cells[64 * $x + $y] || 0; if ($x < 0 || $x > 63 || $y < 0 || $y > 63) { # If the coordinates are out-of-bounds, call the cell "dead" $state = 0; } if ($z >= 0 && $state != $z) { # Only evolve if the state is 0 or 1, and not equal to # the previous state. # $cells[64 * $x + $y] = $z; # Insert new value my $index = 512 * $x + 8 * $y; # Calculate cell index my $id = $ids[$index]; # Lookup previous canvas ID, and $id and $canvas->delete($id); # if nonzero, delete it. # Setup the dimensions of the dot, and # put the dot in the canvas. # my @dot = (3 + 8 * $x, 3 + 8 * $y,9 + 8 * $x, 9 + 8 * $y); push @dot, $z? (-f => $on_color): (-f => $off_color); push @dot, -outline => $off_color; $ids[$index] = $canvas->createOval(@dot); } return $state; }