File Coverage

blib/lib/Cellular/Automata/Wolfram.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Cellular::Automata::Wolfram;
2              
3 1     1   6134 use 5.006;
  1         4  
  1         32  
4 1     1   4 use strict;
  1         1  
  1         32  
5 1     1   4 use warnings;
  1         1  
  1         26  
6 1     1   1495 use GD;
  0            
  0            
7             use Carp;
8             use Math::BaseCalc;
9             use Graphics::ColorNames 'hex2tuple';
10             use Class::MethodMaker
11             new_with_init => 'new',
12             get_set => [qw(rule rules colors radius width num_of_gens first_gen
13             random gens window draw_file)];
14              
15             use constant INSTANCE_DEFAULTS => (rule=>110,radius=>1,width=>80,num_of_gens=>100,random=>"",colors=>['white','black'],draw_file=>'wolfram.png');
16              
17             require Exporter;
18             require DynaLoader;
19             require GD;
20             require Carp;
21             require Math::BaseCalc;
22             require Graphics::ColorNames;
23              
24             use AutoLoader qw(AUTOLOAD);
25              
26             our @ISA = qw(Exporter DynaLoader);
27              
28             # Items to export into callers namespace by default. Note: do not export
29             # names by default without a very good reason. Use EXPORT_OK instead.
30             # Do not simply export all your public functions/methods/constants.
31              
32             # This allows declaration use Cellular::Automata::Wolfram ':all';
33             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
34             # will save memory.
35             our %EXPORT_TAGS = ( 'all' => [ qw(
36            
37             ) ] );
38              
39             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
40              
41             our @EXPORT = qw(
42            
43             );
44             our $VERSION = '1.1';
45              
46             bootstrap Cellular::Automata::Wolfram $VERSION;
47              
48             # Preloaded methods go here.
49              
50             # Autoload methods go after =cut, and are processed by the autosplit program.
51              
52             sub init {
53             my($self) = shift;
54             my %values = (INSTANCE_DEFAULTS,@_);
55             my $key;
56             if(!exists($values{first_gen})) {
57             $values{"first_gen"} =
58             $self->set_first_gen($values{"colors"},$values{"width"},$values{"random"});
59             $values{"width"} = (length($values{"first_gen"}));
60             } # if
61            
62             if(!exists($values{rules})) {
63             $values{rules} =
64             $self->get_rules($values{rule},$values{colors},$values{radius});
65             } # if
66             foreach my $key (keys %values) {
67             $self->$key($values{$key});
68             } # foreach
69             my $temp = $self->first_gen();
70             if(!defined($self->gens())) {
71             $self->generate();
72             } # if
73             return $self;
74             } # sub new
75              
76             sub draw {
77             my($self,$draw_file) = @_;
78             if(defined($draw_file)) {
79             $self->draw_file($draw_file);
80             } # if
81             else {
82             $draw_file = $self->draw_file();
83             } # else
84             my %COLORS;
85             tie %COLORS, 'Graphics::ColorNames';
86             my $width = $self->width();
87             my $num_of_gens = $self->num_of_gens();
88             my $outfile;
89             my $i;
90             my $j;
91             my $im = new GD::Image($width,$num_of_gens);
92             my $color;
93             open($outfile,">". $draw_file) or croak "Cannot open $draw_file\n";
94             my $colors = $self->colors();
95             my %color2tuple;
96             foreach $color (@{$colors}) {
97             if(exists($COLORS{$color})) {
98             $color2tuple{$color} =
99             $im->colorAllocate(hex2tuple($COLORS{$color}));
100             } # if
101             else {
102             croak("Cannot find:" . $color . "in Graphics::ColorNames\n");
103             } # else
104             } # foreach
105             $self->generate();
106             my $gens = $self->gens();
107             my $gen;
108             my @lines;
109             my $png_info;
110             for($i=0;$i<@{$gens};$i++) {
111             @lines = split(//,$gens->[$i]);
112             for($j=0;$j<=$#lines;$j++) {
113             $im->setPixel($j,$i,$color2tuple{$colors->[$lines[$j]]});
114             } # for
115             } # for
116             binmode $outfile;
117             $png_info = $im->png;
118             print $outfile $png_info;
119             close($outfile);
120             } # sub draw
121              
122              
123             sub next_gen {
124             my($self,$curr_gen) = @_;
125             my $next_gen;
126             my $i;
127             my $rules = $self->rules();
128             my $window_size = $self->window();
129             my $width = $self->width(length($self->first_gen()));
130             my $radius = $self->radius();
131             my $key;
132             my $curr_window;
133             my $state;
134             my $left_cells;
135             my $right_cells;
136             for($i=0;$i<=($width-$window_size);$i++) {
137             $curr_window = substr($curr_gen,$i,$window_size);
138             if(exists($rules->{$curr_window})) {
139             $next_gen .= $rules->{$curr_window};
140             } # if
141             else {
142             croak("There is no rule for:" . $curr_window);
143             } # else
144             } # for
145             my $temp1;
146             my $temp2;
147             for($i=1;$i<=$radius;$i++) {
148             $curr_window = substr($curr_gen,-$i,$i) .
149             substr($curr_gen,0,$window_size-$i);
150             if(exists($rules->{$curr_window})) {
151             $left_cells .= $rules->{$curr_window};
152             } # if
153             else {
154             croak("There is no rule for:" . $curr_window);
155             } # else
156             $temp1 = substr($curr_gen,-$window_size+$i);
157             $temp2 = substr($curr_gen,0,$i);
158             $curr_window = $temp1 . $temp2;
159             if(exists($rules->{$curr_window})) {
160             $right_cells .= $rules->{$curr_window};
161             } # if
162             else {
163             croak("There is no rule for:" . $curr_window);
164             } # else
165             } # for
166             $next_gen = $left_cells . $next_gen . $right_cells;
167             return $next_gen;
168             } # sub next_gen
169              
170             sub generate {
171             my($self) = @_;
172             my $num_of_gens = $self->num_of_gens();
173             my $curr_gen;
174             my $i;
175             if($self->random()) {
176             $curr_gen =
177             $self->set_first_gen($self->colors(),$self->width(),$self->random());
178             } # if
179             else {
180             $curr_gen = $self->first_gen();
181             } # else
182             my @gens;
183             push(@gens,$curr_gen);
184             for($i=0;$i<=$num_of_gens;$i++) {
185             $curr_gen = $self->next_gen($curr_gen);
186             push(@gens,$curr_gen);
187             } # for
188             $self->gens([@gens]);
189             } # sub generate
190              
191             sub set_first_gen {
192             my($self,$colors,$width,$random) = @_;
193             my $i;
194             my @first_gen;
195             my $temp;
196             my $num_o_colors = @{$colors};
197             if($random) {
198             srand($$);
199             for($i=0;$i<$width;$i++) {
200             push(@first_gen,int(rand($num_o_colors)));
201             } # for
202             $temp = join("",@first_gen);
203             } # if
204             else {
205             $num_o_colors--;
206             $temp = "0" x (int($width/2)-1) . "$num_o_colors" . "0" x
207             int($width/2);
208             } # else
209             return $temp;
210             } # sub set_first_gen
211              
212              
213             sub get_rules {
214             my($self,$rule,$colors,$radius) = @_;
215             my $i;
216             my @k_states = (0..9,'A'..'Z','a'..'z');
217             my @subk_states = splice(@k_states,0,@{$colors});
218             my $calc = new Math::BaseCalc(digits => \@subk_states); #up to base 36
219             my $rule_base = $calc->to_base($rule); # Convert $rule to given base
220             my $max_num = (@{$colors} ** (@{$colors}**($radius+2)))-1;
221             my $max_num_base = $calc->to_base($max_num);
222             my $max_num_base_len = length($max_num_base);
223             my $rule_base_len = length($rule_base);
224             my $zero_pad = "0" x ($max_num_base_len-$rule_base_len);
225             $rule_base = $zero_pad . $rule_base;
226             $rule_base = reverse($rule_base);
227             my $rule_num;
228             my $max_rule_num = (@{$colors}**($radius+2))-1;
229             my $max_rule_num_len = length($calc->to_base($max_rule_num));
230             $self->window($max_rule_num_len);
231             my %rules;
232             for($i=$max_rule_num;$i>=0;$i--) {
233             $rule_num = $calc->to_base($i);
234             $zero_pad = "0" x
235             ($max_rule_num_len-length($rule_num));
236             $rule_num = $zero_pad . $rule_num;
237             $rules{$rule_num} = chop($rule_base);
238             } # for
239             return \%rules;
240             } # get_rules
241              
242             1;
243             __END__