#!/usr/bin/perl




#################################################################################
# N-Dimensional Game Of Life
# Version 1.0 beta
#################################################################################
# By Steven Piantadosi
# GuidoPian@yahoo.com
#################################################################################
# This module is free under the GNU public liscense. You may use this for any non
# commerical purpose, so long as this message stays in the module. If you would 
# like to contribute to it's development or obtain a liscense for commercial use, 
# write to the author at the above address.
#################################################################################
# For more documentation, see the Central Perl Archive Network at http://cpan.org
#################################################################################




##############################################################
##############################################################
package Ndgol; #the package!!!!
##############################################################
##############################################################


##############################################################
# Provides a setup create interface to the module, making
# the grid, etc etc
# reads in ????
##############################################################
sub Create {
my $obj = {}; #obj is the object representing *this* simulation. 
bless $obj;
$obj->{'DimensionSize'} = 1;
$obj->{'DimensionNumber'} = 1;
return $obj;
} #end sub front end



##############################################################
# Runs the actual simulation. It loops through all the cells,
# determines what they touch, 
##############################################################
sub Run {
my $ob = shift;
$torun = $_[0];

@{$ob->{'Tvals'}} = $ob->Generate_Trans_Vals;

for(1 .. $torun) {
%NXT_CELL = ();
#runs the simulation for each cell in space; 
#returns %NXT_CELL as a hash of all thecell values for next turn
for($innum=0; $innum<=($ob->{'DimensionSize'} ** $ob->{'DimensionNumber'} - 1); $innum++) { #loop throgh all cells
$str = $ob->Count_Base_N($innum);

# RIGHT HERE $STR = THE NEXT SPACE VALUE#

$touching = $ob->Get_Cell_Touch_Count($str); 
#compensate for touching yourself
if($ob->{'CELL'}->{$str} eq "1") { #if it's on...
$touching--;
}

#touching now holds how many cells the cell is touching, non-self-inclusive

#now test for survival, etc
if(index(",".$ob->{'Death'}.",", ",".$touching.",", 0) > -1) { #if it's found
#KILL CELL
$NXT_CELL{$str} = 0;
}
elsif(index(",".$ob->{'Growth'}.",", ",".$touching.",", 0) > -1) { #if it's found
$NXT_CELL{$str} = 1;
}
else { #else it's stasis
#so do nothing
$NXT_CELL{$str} = $ob->{'CELL'}->{$str};
}
} #end loops, darling

%{$ob->{'CELL'}} = %NXT_CELL; #update the cells

} #end for 1 .. torun
return %NXT_CELL;
} #end sub run it



##############################################################
# Counts in base N -- used to loop through all cells
##############################################################
sub Count_Base_N {
my $ob = shift;
my $strval, $val, $s;
my $toconvert = $_[0];
for($s=1; $s<=$ob->{'DimensionNumber'}; $s++) { #this counts in base $DIMSIZE to get the next coords
$val = int(int($toconvert % ($ob->{'DimensionSize'} ** $s)) / ($ob->{'DimensionSize'} ** ($s - 1)));
$strval = $val.",".$strval;
}
$strval = substr($strval, 0, length($strval) - 1); #remove extra , !!!
return $strval;
} #end sub count in base N


##############################################################
# Takes in cell coordinates and returns the number of live 
# cells touching it
##############################################################
sub Get_Cell_Touch_Count {
my $ob = shift;
#returns thenumber of live cells touching a given one
#$_[0] is the coords of the cel
$cellcoord = $_[0]; #input cellcoord
#first, get a list of all touching
@Touching = $ob->Map_Trans_Vals($cellcoord);
$touch_count = 0; #reset count of number touching
foreach $touching_cell(@Touching) {
$touch_count = $touch_count + $ob->{CELL}->{$touching_cell}; #adds 1 if its alive
}
#now cell has determined number touching it; 
return $touch_count;
} #end Get_Cell_Touch_Count


##############################################################
# Takes in the string coordinates of the cell and returns 
# a list of the coords of all touching cells
##############################################################
sub Map_Trans_Vals { #maps a translation onto a specific cell
#returns an array of coords for all cells touching (inclusive)
#$_[0] = input cell coord
my $ob = shift;
$str_coords = $_[0];
@array_coords = split(",", $str_coords);
@Full_Transform = ();
foreach $str_transform(@{$ob->{'Tvals'}}) { #for each transformation, apply it
@array_transform = split(",", $str_transform);
@array_out_coord = (); #a single coordinate
for($i=0; $i<=$#array_coords; $i++) {
push @array_out_coord, ($array_coords[$i] + $array_transform[$i]); #do the actual transform,and save it 
}
$str_out_coord = join(",", @array_out_coord);
push @Full_Transform, $str_out_coord; #save the new coords w/ the list of all coords
}
return @Full_Transform;
} #end map trans vals


##############################################################
# Generates an array of translational values to determine the
# cells touching a given based on the dimension size $B3_SIZE 
# This func counts in base 3, then uses Make_Trans_array
# to convert each base 3 number into a translation
##############################################################
sub Generate_Trans_Vals {
my $ob = shift;
my @TRANS_VALS, $raw_val, $real_size, $trans_val;
my $cur_num = 0;
while($cur_num < (3 ** $ob->{'DimensionNumber'})) { #while we're still counting IN this dimension
#this loop loops till we're done counting in the given dimension, and then produces a list of transformations
#to be put ont he coordinates of the target cell
$raw_val = $ob->ConvertB3($cur_num);
$real_size = $raw_val + 0; #clear other 0s
@trans = Make_Trans_Array($ob->ConvertB3($cur_num));
$trans_val = join(",", @trans);
push @TRANS_VALS, $trans_val;
$cur_num++;
}
return @TRANS_VALS;
} #end sub gen trans vals



##############################################################
# Takes in a base-3 number and translates it into +1,0,-1
# values
##############################################################
sub Make_Trans_Array {
#generate an array for transforming coordinates
$num = $_[0];
#now we must translate into a set of +1, 0, -1, values
@OUTARRAY = ();
@PARTS = split(//, $num);
foreach $part(@PARTS) {
$part = $part - 1;
push @OUTARRAY, $part;
}

return  @OUTARRAY;
} #end make trans array



###############################
# Converts a number to base 3 #
###############################
sub ConvertB3 { #returns the B3_SIZE digits of $_[0] in base 3
my $ob = shift;
$innum = $_[0];
$str = "";
for($s=1; $s<=$ob->{'DimensionNumber'}; $s++) {
$val = int(int($innum % (3 ** $s)) / (3 ** ($s - 1)));
$str = $val.$str;
}
return $str;
}



######## RETURN 1
1;
__END__
########






#################################################################################
#################################################################################
#################################################################################
=head1 NAME

Ndgol - N-Dimension Game Of Life - An implimentation of Conway's Game of Life which runs in any number of dimensions

=head1 SYNOPSIS

#!/usr/bin/perl

use Ndgol;

my $X = Create Ndgol; #create the object... lets call it X

#Specify the Death and Growth values. Any value left out (in this case, "2") is assumed to keep the cells in Stasis. Be sure to use a comma delimited string
$X->{'Death'} = ",1,4,5,6,7,8,9"; #if a cell touches exactly 1,4,5,6,7,8 or 9 others, it will DIE
$X->{'Growth'} = "3"; #if a cell touches exactly 3 others it will GROW (live)
# the number 2 is left out--a cell touching 2 others will stay the same

#Specify the number of dimensions and the size of them
$X->{'DimensionSize'} = 10;
$X->{'DimensionNumber'} = 2;

# Turn on whatever cells we want
# here we'll make a flip-flop, a vertical row that alternates with a horizontal 
# row as follows:
#step1:
#       010
#       010
#       010
#step2:
#       000
#       111
#       000
#step3:
#       010
#       010
#       010
#the below values can, of course, be anything withing the specified dimensions. 
# Note that we are using 2 dimensions and the coordinates here have 2 numbers, 
# separated by commas
# "1" signifies alive and "0" signifies dead 
$X->{'CELL'}->{'5,5'} = 1;
$X->{'CELL'}->{'5,6'} = 1;
$X->{'CELL'}->{'5,7'} = 1;

#run it 1 times (ie. update cells 1 times--this can be any number) 
# and save the results to a hash
my %out = $X->Run(1);

#display the results!
my $key;
foreach $k(keys %out) { #loop throuhg each hash key (coordinate)
if($out{$k} == 1) { #if it's a living cells
print "$k is ALIVE\n"; #print it out!
}
} #end foreach

# C'est tout.

=head1 DESCRIPTION

Ndgol is a ridiculously slow implimentation of Conway's Game of Life which will run in any number of dimensions. Users specify the number of dimensions and the dimension size (all must be the same. In addition, users specify rules of the Game of Life by telling the object how many life cells a given cell must touch in order for it to live, die, or stay the same.

The speed of the simulation depends not on merely the dimension size or the number of dimensions, but the number of cells. The number of cells is DimensionSize**DimensionNumber which increases exponentionally with the number of dimensions. The module works by looping through all cell coordinates, determining how many cells are touching each cell, and applying the rules specified by the user. The module treats cells not in "space" (ie, ones wiht negative coordinates, or coordinates out of the DimensionSize) as having a 0 value. 

As of now there is no user-friendly or graphical front-end, mostly due to the difficulty in displaying an arbitrary number of dimensions. As a result, the program only outputs coordinates of cells and their ON|OFF values (1|0). The Run function returns a hash of all cell coordinates and their values (those which have not yet been activated (switched either on or off) are NULL values, not 0). Coordinates are delimited by commas; for example, the point (5,5) in the xy plane is "5,5". Arbitrary numbers of dimensions can be added (ie. the point "5,5,5,5,5,5") but the program quickly becomes painfully slow, due to the large number of cells.

A Sample program is listed below. This program initiates the NDGOL module, tells it the rules, DimensionSize, DimensionNumber, and what cell are intially on. It then runs the simulation, and prints out a list of living cells:


=head1 COPYRIGHT

Distributed under the same terms as Perl.
Contact the author with any questions.

=head1 AUTHOR

Steve Piantadosi (GuidoPian@yahoo.com)

=head1 VERSION

Version 1.0 beta

=head1 PLATFORMS

All known

=head1 DEPENDENCIES

None

=cut

#################################################################################
#################################################################################
#################################################################################




