File: | blib/lib/Language/Homespring.pm |
Coverage: | 88.0% |
line | stmt | bran | cond | sub | time | code |
---|---|---|---|---|---|---|
1 | package Language::Homespring; | |||||
2 | ||||||
3 | $VERSION = 0.04; | |||||
4 | ||||||
5 | 4 4 4 | 17 8 30 | use strict; | |||
6 | 4 4 4 | 23 18 26 | use warnings; | |||
7 | ||||||
8 | 4 4 4 | 31 10 44 | use Language::Homespring::Node; | |||
9 | 4 4 4 | 39 8 37 | use Language::Homespring::Salmon; | |||
10 | 4 4 4 | 37 10 38 | use Language::Homespring::Snowmelt; | |||
11 | 4 4 4 | 32 10 35 | use Language::Homespring::River; | |||
12 | ||||||
13 | sub new { | |||||
14 | 26 | 70 | my $class = shift; | |||
15 | 26 | 98 | my $self = bless {}, $class; | |||
16 | ||||||
17 | 26 | 62 | my $options = shift; | |||
18 | 26 | 82 | $self->{root_node} = undef; | |||
19 | 26 | 77 | $self->{salmon} = []; | |||
20 | 26 | 81 | $self->{snowmelt} = []; | |||
21 | 26 | 74 | $self->{new_salmon} = []; | |||
22 | 26 | 79 | $self->{dead_salmon} = []; | |||
23 | 26 | 73 | $self->{output} = ''; | |||
24 | 26 | 69 | $self->{universe_ok} = 1; | |||
25 | ||||||
26 | 26 | 70 | return $self; | |||
27 | } | |||||
28 | ||||||
29 | sub parse { | |||||
30 | 26 | 77 | my ($self, $source) = @_; | |||
31 | ||||||
32 | 151 | 322 | my @tokens = | |||
33 | 151 151 | 363 272 | map{s/(\.$)|(^\.)/\n/g; $_} | |||
34 | 151 151 | 340 263 | map{s/\. / /g; $_} | |||
35 | 26 151 | 223 336 | map{s/ \././g; $_} | |||
36 | split /(?:(?<!\.) (?!\.))|(?:\n(?!\.))/, $source; | |||||
37 | ||||||
38 | #print((join '|', @tokens)."\n\n"); | |||||
39 | ||||||
40 | 26 | 201 | $self->{root_node} = new Language::Homespring::Node({ | |||
41 | 'interp' => $self, | |||||
42 | 'node_name' => shift @tokens, | |||||
43 | }); | |||||
44 | 26 | 86 | my $parent = $self->{root_node}; | |||
45 | ||||||
46 | 26 | 67 | for my $token(@tokens){ | |||
47 | 125 | 304 | if ($token){ | |||
48 | 95 | 504 | my $new_node = new Language::Homespring::Node({ | |||
49 | 'interp' => $self, | |||||
50 | 'node_name' => $token, | |||||
51 | 'parent_node' => $parent, | |||||
52 | }); | |||||
53 | 95 | 363 | $parent->add_child($new_node); | |||
54 | ||||||
55 | 95 | 522 | my $new_river = new Language::Homespring::River({ | |||
56 | 'interp' => $self, | |||||
57 | 'up_node' => $new_node, | |||||
58 | 'down_node' => $parent, | |||||
59 | }); | |||||
60 | 95 | 339 | $parent->add_river_up($new_river); | |||
61 | 95 | 302 | $new_node->add_river_down($new_river); | |||
62 | ||||||
63 | 95 | 309 | $parent = $new_node; | |||
64 | }else{ | |||||
65 | 30 | 114 | if (defined $parent->{parent_node}){ | |||
66 | 30 | 108 | $parent = $parent->{parent_node}; | |||
67 | } | |||||
68 | } | |||||
69 | } | |||||
70 | } | |||||
71 | ||||||
72 | sub tick { | |||||
73 | 155 | 383 | my ($self) = @_; | |||
74 | 155 | 239 | my @nodes; | |||
75 | ||||||
76 | 155 | 384 | $self->{output} = ''; | |||
77 | ||||||
78 | # has our universe been smashed? | |||||
79 | 155 | 536 | return if !$self->{universe_ok}; | |||
80 | ||||||
81 | # process snowmelts | |||||
82 | 151 | 447 | @nodes = $self->_get_nodes('snowmelt'); | |||
83 | 151 | 421 | for (@nodes){ | |||
84 | #spawn a new snowmelt | |||||
85 | 31 | 165 | my $snowmelt = new Language::Homespring::Snowmelt({'interp' => $self, 'location' => $_}); | |||
86 | 31 31 | 68 136 | push @{$self->{snowmelt}}, $snowmelt; | |||
87 | } | |||||
88 | 151 151 151 | 192 250 611 | $_->move() for (@{$self->{snowmelt}}); | |||
89 | ||||||
90 | # has our universe been smashed? | |||||
91 | 151 | 523 | return if !$self->{universe_ok}; | |||
92 | ||||||
93 | ||||||
94 | # process water | |||||
95 | ||||||
96 | # turn everything off | |||||
97 | 149 | 444 | $self->_set_all('water', 0); | |||
98 | ||||||
99 | # water from springs | |||||
100 | 149 | 437 | @nodes = $self->_get_all_nodes(); | |||
101 | 149 | 431 | for (@nodes){ | |||
102 | 700 | 2975 | $self->_water_downwards($_) if $_->{spring}; | |||
103 | } | |||||
104 | ||||||
105 | # process electricity | |||||
106 | ||||||
107 | # turn everything off | |||||
108 | 149 | 576 | $self->_set_recurse($self->{root_node}, 'power', 0); | |||
109 | ||||||
110 | # process "powers" | |||||
111 | 149 | 454 | @nodes = $self->_get_nodes('powers'); | |||
112 | 149 | 395 | for(@nodes){ | |||
113 | 116 | 443 | if (!$_->{destroyed}){ | |||
114 | 110 | 308 | $self->_power_downwards($_); | |||
115 | } | |||||
116 | } | |||||
117 | ||||||
118 | # process "hydro power" | |||||
119 | 149 | 452 | @nodes = $self->_get_nodes('hydro power'); | |||
120 | 149 | 403 | for (@nodes){ | |||
121 | 19 | 165 | $self->_power_downwards($_) if $_->{water} && !$_->{destroyed}; | |||
122 | } | |||||
123 | ||||||
124 | # process "power invert" | |||||
125 | 149 | 426 | @nodes = $self->_get_nodes('power invert'); | |||
126 | 149 | 418 | for (@nodes){ | |||
127 | 18 | 151 | $self->_power_downwards($_) if !$_->{power} && !$_->{destroyed}; | |||
128 | } | |||||
129 | ||||||
130 | # process salmon | |||||
131 | ||||||
132 | 149 149 149 | 191 248 709 | $_->move() for (@{$self->{salmon}}); | |||
133 | ||||||
134 | # sort out dead salmon | |||||
135 | 149 338 | 444 599 | @{$self->{salmon}} = grep{ | |||
136 | 149 | 477 | my $ok = 1; | |||
137 | 338 338 | 540 1131 | for my $dead(@{$self->{dead_salmon}}){ | |||
138 | 781 | 2822 | $ok = 0 if $_ == $dead; | |||
139 | } | |||||
140 | 338 | 781 | $ok; | |||
141 | 149 | 293 | }@{$self->{salmon}}; | |||
142 | ||||||
143 | # sort out new salmon | |||||
144 | 149 149 149 | 284 390 410 | push @{$self->{salmon}}, @{$self->{new_salmon}}; | |||
145 | 149 | 445 | $self->{new_salmon} = []; | |||
146 | ||||||
147 | # process others | |||||
148 | ||||||
149 | 149 | 477 | @nodes = $self->_get_nodes('hatchery'); | |||
150 | 149 | 385 | for (@nodes){ | |||
151 | 149 | 578 | if ($_->{power}){ | |||
152 | 103 103 | 193 342 | my $location = @{$_->{rivers_up}}[0]; | |||
153 | 103 | 642 | my $salmon = new Language::Homespring::Salmon({'interp' => $self,'mature' => 1, 'upstream' => 1, 'location' => $location}); | |||
154 | 103 103 | 227 482 | push @{$self->{salmon}}, $salmon; | |||
155 | } | |||||
156 | } | |||||
157 | ||||||
158 | # @nodes = $self->_get_nodes('bear'); | |||||
159 | # for (@nodes){ | |||||
160 | # for my $salmon($_->get_salmon()){ | |||||
161 | # $salmon->kill() if $salmon->{mature}; | |||||
162 | # } | |||||
163 | # } | |||||
164 | ||||||
165 | 149 | 533 | return $self->{output}; | |||
166 | } | |||||
167 | ||||||
168 | sub run{ | |||||
169 | 0 | 0 | my ($self, $max_ticks, $delimit) = @_; | |||
170 | 0 | 0 | my $tick = 0; | |||
171 | 0 | 0 | while(1){ | |||
172 | 0 | 0 | print $self->tick(); | |||
173 | 0 | 0 | print $delimit if defined($delimit); | |||
174 | 0 | 0 | $tick++; | |||
175 | 0 | 0 | return if (defined($max_ticks) && ($tick >= $max_ticks)); | |||
176 | 0 | 0 | return if !$self->{universe_ok}; | |||
177 | } | |||||
178 | } | |||||
179 | ||||||
180 | sub _set_all { | |||||
181 | 149 | 434 | my ($self, $prop, $value) = @_; | |||
182 | 149 | 546 | $self->_set_recurse($self->{root_node}, $prop, $value); | |||
183 | } | |||||
184 | ||||||
185 | sub _set_recurse { | |||||
186 | 1400 | 4149 | my ($self, $node, $prop, $value) = @_; | |||
187 | 1400 | 3287 | $node->{$prop} = $value; | |||
188 | 1400 1400 1400 | 1685 2267 8314 | $self->_set_recurse($_, $prop, $value) for @{$node->{child_nodes}}; | |||
189 | } | |||||
190 | ||||||
191 | sub _get_nodes { | |||||
192 | 747 | 1990 | my ($self, $name) = @_; | |||
193 | 747 | 2768 | return $self->_get_nodes_i($self->{root_node}, $name); | |||
194 | } | |||||
195 | ||||||
196 | sub _get_nodes_i { | |||||
197 | 3512 | 9708 | my ($self, $node, $name) = @_; | |||
198 | 3512 | 6770 | my @out = (); | |||
199 | 3512 | 12581 | push @out, $node if ($node->{node_name} eq $name); | |||
200 | 3512 3512 3512 | 4271 5576 16182 | push @out, $self->_get_nodes_i($_, $name) for @{$node->{child_nodes}}; | |||
201 | 3512 | 12635 | return @out; | |||
202 | } | |||||
203 | ||||||
204 | sub _get_all_nodes { | |||||
205 | 149 | 370 | my ($self) = @_; | |||
206 | 149 | 519 | return $self->_get_all_nodes_i($self->{root_node}); | |||
207 | } | |||||
208 | ||||||
209 | sub _get_all_nodes_i { | |||||
210 | 700 | 1793 | my ($self, $node) = @_; | |||
211 | 700 | 1344 | my @out = (); | |||
212 | 700 | 1383 | push @out, $node; | |||
213 | 700 700 700 | 851 1120 3147 | push @out, $self->_get_all_nodes_i($_) for @{$node->{child_nodes}}; | |||
214 | 700 | 2772 | return @out; | |||
215 | } | |||||
216 | ||||||
217 | sub _power_downwards { | |||||
218 | 337 | 883 | my ($self, $node) = @_; | |||
219 | ||||||
220 | 337 | 1360 | return if (!$node->{parent_node}); | |||
221 | ||||||
222 | 234 | 690 | $node->{parent_node}->{power} = 1; | |||
223 | ||||||
224 | 234 | 948 | return if ($node->{parent_node}->{node_name} eq 'power invert'); | |||
225 | 229 | 925 | return if ($node->{parent_node}->{node_name} eq 'insulated'); | |||
226 | 224 | 922 | return if ($node->{parent_node}->{node_name} eq 'force field'); | |||
227 | 211 | 1055 | return if (($node->{parent_node}->{node_name} eq 'bridge') && ($node->{parent_node}->{destroyed})); | |||
228 | ||||||
229 | # TODO: "sense" and "switch" | |||||
230 | ||||||
231 | 211 | 704 | $self->_power_downwards($node->{parent_node}); | |||
232 | } | |||||
233 | ||||||
234 | sub _water_downwards { | |||||
235 | 506 | 1285 | my ($self, $node) = @_; | |||
236 | ||||||
237 | 506 | 1960 | return if (!$node->{parent_node}); | |||
238 | ||||||
239 | 340 | 1005 | $node->{parent_node}->{water} = 1; | |||
240 | ||||||
241 | 340 | 1778 | return if (($node->{parent_node}->{node_name} eq 'force field') && ($node->{parent_node}->{power})); | |||
242 | 334 | 1671 | return if (($node->{parent_node}->{node_name} eq 'bridge') && ($node->{parent_node}->{destroyed})); | |||
243 | 334 | 1668 | return if (($node->{parent_node}->{node_name} eq 'evaporates') && ($node->{parent_node}->{power})); | |||
244 | ||||||
245 | 334 | 1095 | $self->_water_downwards($node->{parent_node}); | |||
246 | } | |||||
247 |