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             $Cellular::Automata::Wolfram::VERSION = '1.10_01';
3 1     1   1244 use 5.006;
  1         4  
  1         42  
4 1     1   7 use strict;
  1         1  
  1         37  
5 1     1   17 use warnings;
  1         1  
  1         29  
6 1     1   591 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 GD;
18             require Carp;
19             require Math::BaseCalc;
20             require Graphics::ColorNames;
21              
22             # Preloaded methods go here.
23              
24             # Autoload methods go after =cut, and are processed by the autosplit program.
25              
26             sub init {
27             my($self) = shift;
28             my %values = (INSTANCE_DEFAULTS,@_);
29             my $key;
30             if(!exists($values{first_gen})) {
31             $values{"first_gen"} =
32             $self->set_first_gen($values{"colors"},$values{"width"},$values{"random"});
33             $values{"width"} = (length($values{"first_gen"}));
34             } # if
35            
36             if(!exists($values{rules})) {
37             $values{rules} =
38             $self->get_rules($values{rule},$values{colors},$values{radius});
39             } # if
40             foreach my $key (keys %values) {
41             $self->$key($values{$key});
42             } # foreach
43             my $temp = $self->first_gen();
44             if(!defined($self->gens())) {
45             $self->generate();
46             } # if
47             return $self;
48             } # sub new
49              
50             sub draw {
51             my($self,$draw_file) = @_;
52             if(defined($draw_file)) {
53             $self->draw_file($draw_file);
54             } # if
55             else {
56             $draw_file = $self->draw_file();
57             } # else
58             my %COLORS;
59             tie %COLORS, 'Graphics::ColorNames';
60             my $width = $self->width();
61             my $num_of_gens = $self->num_of_gens();
62             my $outfile;
63             my $i;
64             my $j;
65             my $im = new GD::Image($width,$num_of_gens);
66             my $color;
67             open($outfile,">". $draw_file) or croak "Cannot open $draw_file\n";
68             my $colors = $self->colors();
69             my %color2tuple;
70             foreach $color (@{$colors}) {
71             if(exists($COLORS{$color})) {
72             $color2tuple{$color} =
73             $im->colorAllocate(hex2tuple($COLORS{$color}));
74             } # if
75             else {
76             croak("Cannot find:" . $color . "in Graphics::ColorNames\n");
77             } # else
78             } # foreach
79             $self->generate();
80             my $gens = $self->gens();
81             my $gen;
82             my @lines;
83             my $png_info;
84             for($i=0;$i<@{$gens};$i++) {
85             @lines = split(//,$gens->[$i]);
86             for($j=0;$j<=$#lines;$j++) {
87             $im->setPixel($j,$i,$color2tuple{$colors->[$lines[$j]]});
88             } # for
89             } # for
90             binmode $outfile;
91             $png_info = $im->png;
92             print $outfile $png_info;
93             close($outfile);
94             } # sub draw
95              
96              
97             sub next_gen {
98             my($self,$curr_gen) = @_;
99             my $next_gen;
100             my $i;
101             my $rules = $self->rules();
102             my $window_size = $self->window();
103             my $width = $self->width(length($self->first_gen()));
104             my $radius = $self->radius();
105             my $key;
106             my $curr_window;
107             my $state;
108             my $left_cells;
109             my $right_cells;
110             for($i=0;$i<=($width-$window_size);$i++) {
111             $curr_window = substr($curr_gen,$i,$window_size);
112             if(exists($rules->{$curr_window})) {
113             $next_gen .= $rules->{$curr_window};
114             } # if
115             else {
116             croak("There is no rule for:" . $curr_window);
117             } # else
118             } # for
119             my $temp1;
120             my $temp2;
121             for($i=1;$i<=$radius;$i++) {
122             $curr_window = substr($curr_gen,-$i,$i) .
123             substr($curr_gen,0,$window_size-$i);
124             if(exists($rules->{$curr_window})) {
125             $left_cells .= $rules->{$curr_window};
126             } # if
127             else {
128             croak("There is no rule for:" . $curr_window);
129             } # else
130             $temp1 = substr($curr_gen,-$window_size+$i);
131             $temp2 = substr($curr_gen,0,$i);
132             $curr_window = $temp1 . $temp2;
133             if(exists($rules->{$curr_window})) {
134             $right_cells .= $rules->{$curr_window};
135             } # if
136             else {
137             croak("There is no rule for:" . $curr_window);
138             } # else
139             } # for
140             $next_gen = $left_cells . $next_gen . $right_cells;
141             return $next_gen;
142             } # sub next_gen
143              
144             sub generate {
145             my($self) = @_;
146             my $num_of_gens = $self->num_of_gens();
147             my $curr_gen;
148             my $i;
149             if($self->random()) {
150             $curr_gen =
151             $self->set_first_gen($self->colors(),$self->width(),$self->random());
152             } # if
153             else {
154             $curr_gen = $self->first_gen();
155             } # else
156             my @gens;
157             push(@gens,$curr_gen);
158             for($i=0;$i<=$num_of_gens;$i++) {
159             $curr_gen = $self->next_gen($curr_gen);
160             push(@gens,$curr_gen);
161             } # for
162             $self->gens([@gens]);
163             } # sub generate
164              
165             no warnings 'redefine';
166              
167             sub set_first_gen {
168             my($self,$colors,$width,$random) = @_;
169             my $i;
170             my @first_gen;
171             my $temp;
172             my $num_o_colors = @{$colors};
173             if($random) {
174             srand($$);
175             for($i=0;$i<$width;$i++) {
176             push(@first_gen,int(rand($num_o_colors)));
177             } # for
178             $temp = join("",@first_gen);
179             } # if
180             else {
181             $num_o_colors--;
182             $temp = "0" x (int($width/2)-1) . "$num_o_colors" . "0" x
183             int($width/2);
184             } # else
185             return $temp;
186             } # sub set_first_gen
187              
188              
189             sub get_rules {
190             my($self,$rule,$colors,$radius) = @_;
191             my $i;
192             my @k_states = (0..9,'A'..'Z','a'..'z');
193             my @subk_states = splice(@k_states,0,@{$colors});
194             my $calc = new Math::BaseCalc(digits => \@subk_states); #up to base 36
195             my $rule_base = $calc->to_base($rule); # Convert $rule to given base
196             my $max_num = (@{$colors} ** (@{$colors}**($radius+2)))-1;
197             my $max_num_base = $calc->to_base($max_num);
198             my $max_num_base_len = length($max_num_base);
199             my $rule_base_len = length($rule_base);
200             my $zero_pad = "0" x ($max_num_base_len-$rule_base_len);
201             $rule_base = $zero_pad . $rule_base;
202             $rule_base = reverse($rule_base);
203             my $rule_num;
204             my $max_rule_num = (@{$colors}**($radius+2))-1;
205             my $max_rule_num_len = length($calc->to_base($max_rule_num));
206             $self->window($max_rule_num_len);
207             my %rules;
208             for($i=$max_rule_num;$i>=0;$i--) {
209             $rule_num = $calc->to_base($i);
210             $zero_pad = "0" x
211             ($max_rule_num_len-length($rule_num));
212             $rule_num = $zero_pad . $rule_num;
213             $rules{$rule_num} = chop($rule_base);
214             } # for
215             return \%rules;
216             } # get_rules
217              
218             1;
219             __END__