#!/usr/bin/perl -w
use strict;
=head1 life.pl
name : life.pl
desc : Conway's Game of Life
syntax : life.pl [delay]
SYNTAX
This script implements CGoL.
There is one optional runtime argument; namely,
the delay between iterations ('epochs').
THEORY
CGoL essentially is a cellular automaton. The playing
grid is a 2D matrix of cells with 2 possible states:
alive or dead. Cell state is dependent upon the 8
neighbors adjacent to the cell, and the following three
rules:
(1) A cell with exactly 3 live neighbors becomes alive.
(2) A live cell with 2 or 3 live neighbors remains alive.
(3) All other cells die.
INITIAL GRID
The starting pattern is specified in the __DATA__ section
at the end of this script. If there are no lines after
__DATA__, then input is read from stdin.
The length of the first line determines the number of
columns in the grid, and of course the number of lines
determine the number of rows in the grid. Initial 'live'
cells are denoted by x's; initial dead cells are anything
else (internally, dead cells are identified by the space
character, but the logic is tailored to treat anything
'not-alive' as dead).
GRID CAVEAT
The grid wraps around. This means cells on the boundary
can affect other cells on opposite boundaries.
=cut
my $delay = shift || 1;
my $alive = 'x';
my $dead = ' ';
my @grid = ();
my $rows = 0;
my $cols = 0;
#
# Read the grid...
#
my @in = ;
@in = <> unless @in;
foreach( @in ) {
chomp;
my @cells = split( '', $_ );
my $col = 0;
foreach( @cells )
{
$grid[ $rows ][ $col ] = $_;
$col++;
}
$cols = $col if $col > $cols;
$rows++;
}
#
# Now fill in the grid...
#
for( my $r=0; $r<$rows; $r++ )
{
for( my $c=0; $c<$cols; $c++ )
{
$grid[ $r ][ $c ] = $dead unless $grid[ $r ][ $c ];
}
}
print "$rows Rows, $cols Cols\n";
my $epochs = 0;
#
# Now loop forever!
#
{
`clear`;
&show;
$epochs++;
sleep $delay;
my @newgrid = ©( @grid );
for( my $r=0; $r<$rows; $r++ )
{
for( my $c=0; $c<$cols; $c++ )
{
my $cell = $grid[ $r ][ $c ];
my $neighbors = &countNeighbors( $r, $c );
# print "($r,$c) $cell => $neighbors\n";
#
# If a cell has exactly 3 live neighbors,
# then this is a live cell.
#
$newgrid[ $r ][ $c ] = $alive if $neighbors == 3;
#
# If a cell has 2 or 3 live neighbors,
# then this cell remains alive if it is alive.
#
next if $neighbors == 2
|| $neighbors == 3;
#
# rule: all other cells die.
#
$newgrid[ $r ][ $c ] = $dead;
}
}
@grid = @newgrid;
redo;
}
sub show
{
foreach( @grid )
{
if ( $_ )
{
my @row = @{$_};
print join( "", @row );
}
print "\n";
}
my $dashes = '-' x ($cols/2 - length( $epochs ));
print "$dashes $epochs $dashes\n";
}
sub copy
{
my @grid = @_;
my @newgrid = ();
for( my $r=0; $r<$rows; $r++ )
{
for( my $c=0; $c<$cols; $c++ )
{
$newgrid[ $r ][ $c ] = $grid[ $r ][ $c ];
}
}
return @newgrid;
}
sub countNeighbors
{
my ($r, $c) = @_;
my $count = 0;
$count += isAlive( $r-1, $c-1 );
$count += isAlive( $r-1, $c );
$count += isAlive( $r-1, $c+1 );
$count += isAlive( $r, $c-1 );
$count += isAlive( $r, $c+1 );
$count += isAlive( $r+1, $c-1 );
$count += isAlive( $r+1, $c );
$count += isAlive( $r+1, $c+1 );
return $count;
}
sub isAlive
{
my ($r, $c) = @_;
return 1 if $grid[ $r ][ $c ]
&& $grid[ $r ][ $c ] eq $alive;
return 0;
}
__DATA__
-first row-
x
xxx
x
last row