line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package AI::Evolve::Befunge::Critter; |
2
|
5
|
|
|
5
|
|
95674
|
use strict; |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
172
|
|
3
|
5
|
|
|
5
|
|
28
|
use warnings; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
193
|
|
4
|
|
|
|
|
|
|
|
5
|
5
|
|
|
5
|
|
4428
|
use Language::Befunge; |
|
5
|
|
|
|
|
158193
|
|
|
5
|
|
|
|
|
58
|
|
6
|
5
|
|
|
5
|
|
5235
|
use Language::Befunge::Storage::Generic::Vec; |
|
5
|
|
|
|
|
58549
|
|
|
5
|
|
|
|
|
62
|
|
7
|
5
|
|
|
5
|
|
5680
|
use IO::File; |
|
5
|
|
|
|
|
11097
|
|
|
5
|
|
|
|
|
787
|
|
8
|
5
|
|
|
5
|
|
174
|
use Carp; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
260
|
|
9
|
5
|
|
|
5
|
|
937
|
use Perl6::Export::Attrs; |
|
5
|
|
|
|
|
11806
|
|
|
5
|
|
|
|
|
65
|
|
10
|
5
|
|
|
5
|
|
344
|
use Scalar::Util qw(weaken); |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
491
|
|
11
|
|
|
|
|
|
|
|
12
|
5
|
|
|
5
|
|
27
|
use base 'Class::Accessor::Fast'; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
1520
|
|
13
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors( |
14
|
|
|
|
|
|
|
# basic values |
15
|
|
|
|
|
|
|
qw{ boardsize codesize code color dims maxlen maxsize minsize }, |
16
|
|
|
|
|
|
|
# token currency stuff |
17
|
|
|
|
|
|
|
qw{ tokens codecost itercost stackcost repeatcost threadcost }, |
18
|
|
|
|
|
|
|
# other objects we manage |
19
|
|
|
|
|
|
|
qw{ blueprint physics interp } |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
5
|
|
|
5
|
|
4233
|
use AI::Evolve::Befunge::Util; |
|
5
|
|
|
|
|
15
|
|
|
5
|
|
|
|
|
61
|
|
23
|
5
|
|
|
5
|
|
12393
|
use aliased 'AI::Evolve::Befunge::Critter::Result' => 'Result'; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
53
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
AI::Evolve::Befunge::Critter - critter execution environment |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 DESCRIPTION |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
This module is where the actual execution of Befunge code occurs. It |
33
|
|
|
|
|
|
|
contains everything necessary to set up and run the code in a safe |
34
|
|
|
|
|
|
|
(sandboxed) Befunge universe. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This universe contains the Befunge code (obviously), as well as the |
37
|
|
|
|
|
|
|
current board game state (if any). The Befunge code exists in the |
38
|
|
|
|
|
|
|
negative vector space (with the origin at 0, Befunge code is below |
39
|
|
|
|
|
|
|
zero on all axes). Board game info, if any, exists as a square (or |
40
|
|
|
|
|
|
|
hypercube) which starts at the origin. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
The layout of befunge code space looks like this (for a 2d universe): |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|----------| | |
45
|
|
|
|
|
|
|
|1 | | |
46
|
|
|
|
|
|
|
|09876543210123456789| |
47
|
|
|
|
|
|
|
---+--------------------+--- |
48
|
|
|
|
|
|
|
-10|CCCCCCCCCC |-10 |
49
|
|
|
|
|
|
|
-9|CCCCCCCCCC| | -9 |
50
|
|
|
|
|
|
|
-8|CCCCCCCCCC | -8 |
51
|
|
|
|
|
|
|
-7|CCCCCCCCCC| | -7 |
52
|
|
|
|
|
|
|
-6|CCCCCCCCCC | -6 |
53
|
|
|
|
|
|
|
-5|CCCCCCCCCC| | -5 |
54
|
|
|
|
|
|
|
-4|CCCCCCCCCC | -4 |
55
|
|
|
|
|
|
|
-3|CCCCCCCCCC| | -3 |
56
|
|
|
|
|
|
|
-2|CCCCCCCCCC | -2 |
57
|
|
|
|
|
|
|
-1|CCCCCCCCCC| | -1 |
58
|
|
|
|
|
|
|
--0| - - - - -BBBB - - -|0-- |
59
|
|
|
|
|
|
|
1| BBBB | 1 |
60
|
|
|
|
|
|
|
2| BBBB | 2 |
61
|
|
|
|
|
|
|
3| BBBB | 3 |
62
|
|
|
|
|
|
|
4| | 4 |
63
|
|
|
|
|
|
|
5| | | 5 |
64
|
|
|
|
|
|
|
6| | 6 |
65
|
|
|
|
|
|
|
7| | | 7 |
66
|
|
|
|
|
|
|
8| | 8 |
67
|
|
|
|
|
|
|
9| | | 9 |
68
|
|
|
|
|
|
|
---+--------------------+--- |
69
|
|
|
|
|
|
|
|09876543210123456789| |
70
|
|
|
|
|
|
|
|1 | | |
71
|
|
|
|
|
|
|
|----------| | |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Where: |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
C is befunge code. This is the code under test. |
76
|
|
|
|
|
|
|
B is boardgame data. Each location is binary 0, 1 or 2 (or |
77
|
|
|
|
|
|
|
whatever tokens the game uses to represent |
78
|
|
|
|
|
|
|
unoccupied spaces, and the various player |
79
|
|
|
|
|
|
|
pieces). The B section only exists for |
80
|
|
|
|
|
|
|
board game applications. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
Everything else is free for local use. Note that none of this is |
83
|
|
|
|
|
|
|
write protected - a program is free to reorganize and/or overwrite |
84
|
|
|
|
|
|
|
itself, the game board, results table, or anything else within the |
85
|
|
|
|
|
|
|
space it was given. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
The universe is implemented as a hypercube of 1 or more dimensions. |
88
|
|
|
|
|
|
|
The universe size is simply the code size times two, or the board size |
89
|
|
|
|
|
|
|
times two, whichever is larger. If the board exists in 2 dimensions |
90
|
|
|
|
|
|
|
but the code exists in more, the board will be represented as a square |
91
|
|
|
|
|
|
|
starting at (0,0,...) and will only exist on plane 0 of the non-(X,Y) |
92
|
|
|
|
|
|
|
axes. |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Several attributes of the universe are pushed onto the initial stack, |
95
|
|
|
|
|
|
|
in the hopes that the critter can use this information to its |
96
|
|
|
|
|
|
|
advantage. The values pushed are (in order from the top of the stack |
97
|
|
|
|
|
|
|
(most accessible) to the bottom (least accessible)): |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
* the Physics token (implying the rules of the game/universe) |
100
|
|
|
|
|
|
|
* the number of dimensions this universe operates in |
101
|
|
|
|
|
|
|
* The number of tokens the critter has left (see LIMITS, below) |
102
|
|
|
|
|
|
|
* The iter cost (see LIMITS, below) |
103
|
|
|
|
|
|
|
* The repeat cost (see LIMITS, below) |
104
|
|
|
|
|
|
|
* The stack cost (see LIMITS, below) |
105
|
|
|
|
|
|
|
* The thread cost (see LIMITS, below) |
106
|
|
|
|
|
|
|
* The code size (a Vector) |
107
|
|
|
|
|
|
|
* The maximum storage size (a Vector) |
108
|
|
|
|
|
|
|
* The board size (a Vector) if operating in a boardgame universe |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
If a Critter instance will have it's ->invoke() method called more |
111
|
|
|
|
|
|
|
than once (for board game universes, it is called once per "turn"), |
112
|
|
|
|
|
|
|
the storage model is not re-created. The critter is responsible for |
113
|
|
|
|
|
|
|
preserving enough of itself to handle multiple invocations properly. |
114
|
|
|
|
|
|
|
The Language::Befunge Interpreter and Storage model are preserved, |
115
|
|
|
|
|
|
|
though a new IP is created each time, and (for board game universes) |
116
|
|
|
|
|
|
|
the board data segment is refreshed each time. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head1 LIMITS |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
This execution environment is sandboxed. Every attempt is made to |
122
|
|
|
|
|
|
|
keep the code under test from escaping the environment, or consuming |
123
|
|
|
|
|
|
|
an unacceptable amount of resources. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Escape is prevented by disabling all file operations, I/O operations, |
126
|
|
|
|
|
|
|
system commands like fork() and system(), and commands which load |
127
|
|
|
|
|
|
|
(potentially insecure) external Befunge semantics modules. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Resource consumption is limited through the use of a currency system. |
130
|
|
|
|
|
|
|
The way this works is, each critter starts out with a certain amount |
131
|
|
|
|
|
|
|
of "Tokens" (the critter form of currency), and every action (like an |
132
|
|
|
|
|
|
|
executed befunge instruction, a repeated command, a spawned thread, |
133
|
|
|
|
|
|
|
etc) incurs a cost. When the number of tokens drops to 0, the critter |
134
|
|
|
|
|
|
|
dies. This prevents the critter from getting itself (and the rest of |
135
|
|
|
|
|
|
|
the system) into trouble. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
For reference, the following things are specifically tested for: |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=over 4 |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item Size of stacks |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item Number of stacks |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item Storage size (electric fence) |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item Number of threads |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item "k" command repeat count |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=item "j" command jump count |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item "x" command dead IP checks (setting a null vector) |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=back |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Most of the above things will result in spending some tokens. There |
158
|
|
|
|
|
|
|
are a couple of exceptions to this: a storage write outside the |
159
|
|
|
|
|
|
|
confines of the critter's fence will result in the interpreter |
160
|
|
|
|
|
|
|
crashing and the critter dying with it; similarly, a huge "j" jump |
161
|
|
|
|
|
|
|
count will also kill the critter. |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
The following commands are removed entirely from the interpreter's Ops |
164
|
|
|
|
|
|
|
hash: |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
, (Output Character) |
167
|
|
|
|
|
|
|
. (Output Integer) |
168
|
|
|
|
|
|
|
~ (Input Character) |
169
|
|
|
|
|
|
|
& (Input Integer) |
170
|
|
|
|
|
|
|
i (Input File) |
171
|
|
|
|
|
|
|
o (Output File) |
172
|
|
|
|
|
|
|
= (Execute) |
173
|
|
|
|
|
|
|
( (Load Semantics) |
174
|
|
|
|
|
|
|
) (Unload Semantics) |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head2 new |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Critter->new(Blueprint => \$blueprint, Physics => \$physics, |
182
|
|
|
|
|
|
|
IterPerTurn => 10000, MaxThreads => 100, Config => \$config,\n" |
183
|
|
|
|
|
|
|
MaxStack => 1000,Color => 1, BoardSize => \$vector)"; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Create a new Critter object. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
The following arguments are required: |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=over 4 |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item Blueprint |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
The blueprint object, which contains the code for this critter. Also |
194
|
|
|
|
|
|
|
note, we also use the Blueprint object to cache a copy of the storage |
195
|
|
|
|
|
|
|
object, to speed up creation of subsequent Critter objects. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item Physics |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
The physics object controls the semantics of how the universe |
200
|
|
|
|
|
|
|
operates. Mainly it controls the size of the game board (if any). |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=item Config |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
The config object, see L. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=item Tokens |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Tokens are the basic form of life currency in this simulation. |
209
|
|
|
|
|
|
|
Critters have a certain amount of tokens at the beginning of a run |
210
|
|
|
|
|
|
|
(controlled by this value), and they spend tokens to perform tasks. |
211
|
|
|
|
|
|
|
(The amount of tokens required to perform a task depends on the |
212
|
|
|
|
|
|
|
various "Cost" values, below.) |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
When the number of tokens reaches 0, the critter dies (and the |
215
|
|
|
|
|
|
|
interpreter is killed). |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=back |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
The following arguments are optional: |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=over 4 |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=item CodeCost |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
This is the number of tokens the critter pays (up front, at birth |
228
|
|
|
|
|
|
|
time) for the codespace it inhabits. If the blueprint's CodeSize |
229
|
|
|
|
|
|
|
is (8,8,8), 8*8*8 = 512 spaces are taken up. If the CodeCost is 1, |
230
|
|
|
|
|
|
|
that means the critter pays 512 tokens just to be born. If CodeCost |
231
|
|
|
|
|
|
|
is 2, the critter pays 1024 tokens, and so on. |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
If not specified, this will be pulled from the variable "codecost" in |
234
|
|
|
|
|
|
|
the config file. If that can't be found, a default value of 1 is |
235
|
|
|
|
|
|
|
used. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item IterCost |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
This is the number of tokens the critter pays for each command it |
241
|
|
|
|
|
|
|
runs. It is a basic operational overhead, decremented for each clock |
242
|
|
|
|
|
|
|
tick for each running thread. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
If not specified, this will be pulled from the variable "itercost" in |
245
|
|
|
|
|
|
|
the config file. If that can't be found, a default value of 2 is |
246
|
|
|
|
|
|
|
used. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=item RepeatCost |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
This is the number of tokens the critter pays for each time a command |
252
|
|
|
|
|
|
|
is repeated (with the "k" instruction). It makes sense for this value |
253
|
|
|
|
|
|
|
to be lower than the IterCost setting, as it is somewhat more |
254
|
|
|
|
|
|
|
efficient. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
If not specified, this will be pulled from the variable "repeatcost" |
257
|
|
|
|
|
|
|
in the config file. If that can't be found, a default value of 1 is |
258
|
|
|
|
|
|
|
used. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=item StackCost |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
This is the number of tokens the critter pays for each time a value |
264
|
|
|
|
|
|
|
is pushed onto the stack. It also has an effect when the critter |
265
|
|
|
|
|
|
|
creates a new stack; the number of stack entries to be copied is |
266
|
|
|
|
|
|
|
multiplied by the StackCost to determine the total cost. |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
If not specified, this will be pulled from the variable "stackcost" |
269
|
|
|
|
|
|
|
in the config file. If that can't be found, a default value of 1 is |
270
|
|
|
|
|
|
|
used. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=item ThreadCost |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
This is a fixed number of tokens the critter pays for spawning a new |
276
|
|
|
|
|
|
|
thread. When a new thread is created, this cost is incurred, plus the |
277
|
|
|
|
|
|
|
cost of duplicating all of the thread's stacks (see StackCost, above). |
278
|
|
|
|
|
|
|
The new threads will begin incurring additional costs from the |
279
|
|
|
|
|
|
|
IterCost (also above), when it begins executing commands of its own. |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
If not specified, this will be pulled from the variable "threadcost" |
282
|
|
|
|
|
|
|
in the config file. If that can't be found, a default value of 10 is |
283
|
|
|
|
|
|
|
used. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=item Color |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
This determines the color of the player, which (for board games) |
289
|
|
|
|
|
|
|
indicates which type of piece the current player is able to play. It |
290
|
|
|
|
|
|
|
has no other effect, and thus, it is not necessary for non-boardgame |
291
|
|
|
|
|
|
|
physics models. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
If not specified, a default value of 1 is used. |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
=item BoardSize |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
If specified, a board game of the given size (specified as a Vector |
299
|
|
|
|
|
|
|
object) is created. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=back |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=cut |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub new { |
306
|
39
|
|
|
39
|
1
|
11816
|
my $package = shift; |
307
|
39
|
|
|
|
|
228
|
my %args = ( |
308
|
|
|
|
|
|
|
# defaults |
309
|
|
|
|
|
|
|
Color => 1, |
310
|
|
|
|
|
|
|
@_ |
311
|
|
|
|
|
|
|
); |
312
|
|
|
|
|
|
|
# args |
313
|
39
|
|
|
|
|
140
|
my $usage = |
314
|
|
|
|
|
|
|
"Usage: $package->new(Blueprint => \$blueprint, Physics => \$physics,\n" |
315
|
|
|
|
|
|
|
." Tokens => 2000, BoardSize => \$vector, Config => \$config)"; |
316
|
39
|
100
|
|
|
|
145
|
croak $usage unless exists $args{Config}; |
317
|
38
|
100
|
|
|
|
211
|
$args{Tokens} = $args{Config}->config('tokens' , 2000) unless defined $args{Tokens}; |
318
|
38
|
100
|
|
|
|
236
|
$args{CodeCost} = $args{Config}->config("code_cost" , 1 ) unless defined $args{CodeCost}; |
319
|
38
|
100
|
|
|
|
202
|
$args{IterCost} = $args{Config}->config("iter_cost" , 2 ) unless defined $args{IterCost}; |
320
|
38
|
100
|
|
|
|
196
|
$args{RepeatCost} = $args{Config}->config("repeat_cost", 1 ) unless defined $args{RepeatCost}; |
321
|
38
|
100
|
|
|
|
182
|
$args{StackCost} = $args{Config}->config("stack_cost" , 1 ) unless defined $args{StackCost}; |
322
|
38
|
100
|
|
|
|
176
|
$args{ThreadCost} = $args{Config}->config("thread_cost", 10 ) unless defined $args{ThreadCost}; |
323
|
|
|
|
|
|
|
|
324
|
38
|
100
|
|
|
|
200
|
croak $usage unless exists $args{Blueprint}; |
325
|
37
|
100
|
|
|
|
109
|
croak $usage unless exists $args{Physics}; |
326
|
36
|
100
|
|
|
|
131
|
croak $usage unless defined $args{Color}; |
327
|
|
|
|
|
|
|
|
328
|
35
|
|
|
|
|
54
|
my $codelen = 1; |
329
|
35
|
|
|
|
|
147
|
foreach my $d ($args{Blueprint}->size->get_all_components) { |
330
|
76
|
|
|
|
|
336
|
$codelen *= $d; |
331
|
|
|
|
|
|
|
} |
332
|
35
|
100
|
|
|
|
130
|
croak "CodeCost must be greater than 0!" unless $args{CodeCost} > 0; |
333
|
34
|
100
|
|
|
|
98
|
croak "IterCost must be greater than 0!" unless $args{IterCost} > 0; |
334
|
33
|
100
|
|
|
|
94
|
croak "RepeatCost must be greater than 0!" unless $args{RepeatCost} > 0; |
335
|
32
|
100
|
|
|
|
114
|
croak "StackCost must be greater than 0!" unless $args{StackCost} > 0; |
336
|
31
|
100
|
|
|
|
87
|
croak "ThreadCost must be greater than 0!" unless $args{ThreadCost} > 0; |
337
|
30
|
|
|
|
|
66
|
$args{Tokens} -= ($codelen * $args{CodeCost}); |
338
|
30
|
100
|
|
|
|
81
|
croak "Tokens must exceed the code size!" unless $args{Tokens} > 0; |
339
|
29
|
100
|
|
|
|
134
|
croak "Code must be freeform! (no newlines)" |
340
|
|
|
|
|
|
|
if $args{Blueprint}->code =~ /\n/; |
341
|
|
|
|
|
|
|
|
342
|
28
|
|
|
|
|
230
|
my $self = bless({}, $package); |
343
|
28
|
|
|
|
|
91
|
$$self{blueprint} = $args{Blueprint}; |
344
|
28
|
100
|
|
|
|
96
|
$$self{boardsize} = $args{BoardSize} if exists $args{BoardSize}; |
345
|
28
|
|
|
|
|
97
|
$$self{code} = $$self{blueprint}->code; |
346
|
28
|
|
|
|
|
167
|
$$self{codecost} = $args{CodeCost}; |
347
|
28
|
|
|
|
|
106
|
$$self{codesize} = $$self{blueprint}->size; |
348
|
28
|
|
|
|
|
145
|
$$self{config} = $args{Config}; |
349
|
28
|
|
|
|
|
89
|
$$self{dims} = $$self{codesize}->get_dims(); |
350
|
28
|
|
|
|
|
81
|
$$self{itercost} = $args{IterCost}; |
351
|
28
|
|
|
|
|
61
|
$$self{repeatcost} = $args{RepeatCost}; |
352
|
28
|
|
|
|
|
64
|
$$self{stackcost} = $args{StackCost}; |
353
|
28
|
|
|
|
|
54
|
$$self{threadcost} = $args{ThreadCost}; |
354
|
28
|
|
|
|
|
56
|
$$self{tokens} = $args{Tokens}; |
355
|
28
|
100
|
|
|
|
81
|
if(exists($$self{boardsize})) { |
356
|
23
|
100
|
|
|
|
131
|
$$self{dims} = $$self{boardsize}->get_dims() |
357
|
|
|
|
|
|
|
if($$self{dims} < $$self{boardsize}->get_dims()); |
358
|
|
|
|
|
|
|
} |
359
|
28
|
100
|
|
|
|
120
|
if($$self{codesize}->get_dims() < $$self{dims}) { |
360
|
|
|
|
|
|
|
# upgrade codesize (keep it hypercubical) |
361
|
17
|
|
|
|
|
232
|
$$self{codesize} = Language::Befunge::Vector->new( |
362
|
|
|
|
|
|
|
$$self{codesize}->get_all_components(), |
363
|
17
|
|
|
|
|
84
|
map { $$self{codesize}->get_component(0) } |
364
|
|
|
|
|
|
|
(1..$$self{dims}-$$self{codesize}->get_dims()) |
365
|
|
|
|
|
|
|
); |
366
|
|
|
|
|
|
|
} |
367
|
28
|
100
|
|
|
|
94
|
if(exists($$self{boardsize})) { |
368
|
23
|
100
|
|
|
|
97
|
if($$self{boardsize}->get_dims() < $$self{dims}) { |
369
|
|
|
|
|
|
|
# upgrade boardsize |
370
|
2
|
|
|
|
|
11
|
$$self{boardsize} = Language::Befunge::Vector->new( |
371
|
|
|
|
|
|
|
$$self{boardsize}->get_all_components(), |
372
|
1
|
|
|
|
|
8
|
map { 1 } (1..$$self{dims}-$$self{boardsize}->get_dims()) |
373
|
|
|
|
|
|
|
); |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
28
|
|
|
|
|
66
|
$$self{color} = $args{Color}; |
378
|
28
|
100
|
|
|
|
86
|
croak "Color must be greater than 0" unless $$self{color} > 0; |
379
|
27
|
|
|
|
|
58
|
$$self{physics} = $args{Physics}; |
380
|
27
|
100
|
|
|
|
103
|
croak "Physics must be a reference" unless ref($$self{physics}); |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# set up our corral to be twice the size of our code or our board, whichever |
383
|
|
|
|
|
|
|
# is bigger. |
384
|
26
|
|
|
|
|
139
|
my $maxpos = Language::Befunge::Vector->new_zeroes($$self{dims}); |
385
|
26
|
|
|
|
|
88
|
foreach my $dim (0..$$self{dims}-1) { |
386
|
57
|
100
|
100
|
|
|
389
|
if(!exists($$self{boardsize}) |
387
|
|
|
|
|
|
|
||($$self{codesize}->get_component($dim) > $$self{boardsize}->get_component($dim))) { |
388
|
35
|
|
|
|
|
161
|
$maxpos->set_component($dim, $$self{codesize}->get_component($dim)); |
389
|
|
|
|
|
|
|
} else { |
390
|
22
|
|
|
|
|
89
|
$maxpos->set_component($dim, $$self{boardsize}->get_component($dim)); |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} |
393
|
26
|
|
|
|
|
659
|
my $minpos = Language::Befunge::Vector->new_zeroes($$self{dims}) - $maxpos; |
394
|
26
|
|
|
|
|
448
|
my $maxlen = 0; |
395
|
26
|
|
|
|
|
77
|
foreach my $d (0..$$self{dims}-1) { |
396
|
57
|
|
|
|
|
178
|
my $this = $maxpos->get_component($d) - $minpos->get_component($d); |
397
|
57
|
100
|
|
|
|
178
|
$maxlen = $this if $this > $maxlen; |
398
|
|
|
|
|
|
|
} |
399
|
26
|
|
|
|
|
68
|
$$self{maxsize} = $maxpos; |
400
|
26
|
|
|
|
|
77
|
$$self{minsize} = $minpos; |
401
|
26
|
|
|
|
|
42
|
$$self{maxlen} = $maxlen; |
402
|
|
|
|
|
|
|
|
403
|
26
|
|
|
|
|
393
|
my $interp = Language::Befunge::Interpreter->new({ |
404
|
|
|
|
|
|
|
dims => $$self{dims}, |
405
|
|
|
|
|
|
|
storage => 'Language::Befunge::Storage::Generic::Vec' |
406
|
|
|
|
|
|
|
}); |
407
|
26
|
|
|
|
|
82910
|
$$self{interp} = $interp; |
408
|
26
|
|
|
|
|
55
|
$$self{codeoffset} = $minpos; |
409
|
26
|
|
|
|
|
87
|
my $cachename = "storagecache-".$$self{dims}; |
410
|
26
|
100
|
100
|
|
|
150
|
if(exists($$self{blueprint}{cache}) |
411
|
|
|
|
|
|
|
&& exists($$self{blueprint}{cache}{$cachename})) { |
412
|
3
|
|
|
|
|
19
|
$$interp{storage} = $$self{blueprint}{cache}{$cachename}->_copy; |
413
|
|
|
|
|
|
|
} else { |
414
|
23
|
100
|
|
|
|
71
|
if($$self{dims} > 1) { |
415
|
|
|
|
|
|
|
# split code into lines, pages, etc as necessary. |
416
|
22
|
|
|
|
|
28
|
my @lines; |
417
|
22
|
|
|
|
|
82
|
my $meas = $$self{codesize}->get_component(0); |
418
|
22
|
|
|
|
|
42
|
my $dims = $$self{dims}; |
419
|
22
|
|
|
|
|
63
|
my @terms = ("", "\n", "\f"); |
420
|
22
|
|
|
|
|
75
|
push(@terms, "\0" x ($_-2)) for(3..$dims); |
421
|
|
|
|
|
|
|
|
422
|
22
|
|
|
|
|
283
|
push(@lines, substr($$self{code}, 0, $meas, "")) while length $$self{code}; |
423
|
22
|
|
|
|
|
55
|
foreach my $dim (0..$dims-1) { |
424
|
46
|
|
|
|
|
68
|
my $offs = 1; |
425
|
46
|
|
|
|
|
94
|
$offs *= $meas for (1..$dim-1); |
426
|
46
|
|
|
|
|
118
|
for(my $i = $offs; $i <= scalar @lines; $i += $offs) { |
427
|
220
|
|
|
|
|
556
|
$lines[$i-1] .= $terms[$dim]; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
} |
430
|
22
|
|
|
|
|
89
|
$$self{code} = join("", @lines); |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
23
|
|
|
|
|
167
|
$interp->get_storage->store($$self{code}, $$self{codeoffset}); |
434
|
|
|
|
|
|
|
# assign our corral size to the befunge space |
435
|
23
|
|
|
|
|
4163
|
$interp->get_storage->expand($$self{minsize}); |
436
|
23
|
|
|
|
|
76
|
$interp->get_storage->expand($$self{maxsize}); |
437
|
|
|
|
|
|
|
# save off a copy of this befunge space for later reuse |
438
|
23
|
100
|
|
|
|
143
|
$$self{blueprint}{cache} = {} unless exists $$self{blueprint}{cache}; |
439
|
23
|
|
|
|
|
104
|
$$self{blueprint}{cache}{$cachename} = $interp->get_storage->_copy; |
440
|
|
|
|
|
|
|
} |
441
|
26
|
|
|
|
|
442
|
my $storage = $interp->get_storage; |
442
|
26
|
|
|
|
|
59
|
$$storage{maxsize} = $$self{maxsize}; |
443
|
26
|
|
|
|
|
54
|
$$storage{minsize} = $$self{minsize}; |
444
|
|
|
|
|
|
|
# store a copy of the Critter in the storage, so _expand (below) can adjust |
445
|
|
|
|
|
|
|
# the remaining tokens. |
446
|
26
|
|
|
|
|
70
|
$$storage{_ai_critter} = $self; |
447
|
26
|
|
|
|
|
108
|
weaken($$storage{_ai_critter}); |
448
|
|
|
|
|
|
|
# store a copy of the Critter in the interp, so various command callbacks |
449
|
|
|
|
|
|
|
# (below) can adjust the remaining tokens. |
450
|
26
|
|
|
|
|
164
|
$$interp{_ai_critter} = $self; |
451
|
26
|
|
|
|
|
67
|
weaken($$interp{_ai_critter}); |
452
|
|
|
|
|
|
|
|
453
|
26
|
|
|
|
|
93
|
$interp->get_ops->{'{'} = \&AI::Evolve::Befunge::Critter::_block_open; |
454
|
26
|
|
|
|
|
101
|
$interp->get_ops->{'j'} = \&AI::Evolve::Befunge::Critter::_op_flow_jump_to_wrap; |
455
|
26
|
|
|
|
|
62
|
$interp->get_ops->{'k'} = \&AI::Evolve::Befunge::Critter::_op_flow_repeat_wrap; |
456
|
26
|
|
|
|
|
67
|
$interp->get_ops->{'t'} = \&AI::Evolve::Befunge::Critter::_op_spawn_ip_wrap; |
457
|
|
|
|
|
|
|
|
458
|
26
|
|
|
|
|
72
|
my @invalid_meths = (',','.','&','~','i','o','=','(',')',map { chr } (ord('A')..ord('Z'))); |
|
676
|
|
|
|
|
1381
|
|
459
|
26
|
|
|
|
|
950
|
$$self{interp}{ops}{$_} = $$self{interp}{ops}{r} foreach @invalid_meths; |
460
|
|
|
|
|
|
|
|
461
|
26
|
100
|
|
|
|
98
|
if(exists($args{Commands})) { |
462
|
23
|
|
|
|
|
34
|
foreach my $command (sort keys %{$args{Commands}}) { |
|
23
|
|
|
|
|
118
|
|
463
|
42
|
|
|
|
|
76
|
my $cb = $args{Commands}{$command}; |
464
|
42
|
|
|
|
|
108
|
$$self{interp}{ops}{$command} = $cb; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
|
469
|
26
|
|
|
|
|
49
|
my @params; |
470
|
|
|
|
|
|
|
my @vectors; |
471
|
26
|
100
|
|
|
|
155
|
push(@vectors, $$self{boardsize}) if exists $$self{boardsize}; |
472
|
26
|
|
|
|
|
53
|
push(@vectors, $$self{maxsize}, $$self{codesize}); |
473
|
26
|
|
|
|
|
45
|
foreach my $vec (@vectors) { |
474
|
75
|
|
|
|
|
192
|
push(@params, $vec->get_all_components()); |
475
|
75
|
|
|
|
|
263
|
push(@params, 1) for($vec->get_dims()+1..$$self{dims}); |
476
|
|
|
|
|
|
|
} |
477
|
26
|
|
|
|
|
119
|
push(@params, $$self{threadcost}, $$self{stackcost}, $$self{repeatcost}, |
478
|
|
|
|
|
|
|
$$self{itercost}, $$self{tokens}, $$self{dims}); |
479
|
26
|
100
|
|
|
|
112
|
push(@params, $self->physics->token) if defined $self->physics->token; |
480
|
|
|
|
|
|
|
|
481
|
26
|
|
|
|
|
677
|
$$self{interp}->set_params([@params]); |
482
|
|
|
|
|
|
|
|
483
|
26
|
|
|
|
|
376
|
return $self; |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=head1 METHODS |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=head2 invoke |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
my $rv = $critter->invoke($board); |
492
|
|
|
|
|
|
|
my $rv = $critter->invoke(); |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
Run through a life cycle. If a board is specified, the board state |
495
|
|
|
|
|
|
|
is copied into the appropriate place before execution begins. |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
This should be run within an "eval"; if the critter causes an |
498
|
|
|
|
|
|
|
exception, it will kill this function. It is commonly invoked by |
499
|
|
|
|
|
|
|
L (see below), which handles exceptions properly. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=cut |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
sub invoke { |
504
|
31
|
|
|
31
|
1
|
457
|
my ($self, $board) = @_; |
505
|
31
|
|
|
|
|
75
|
delete($$self{move}); |
506
|
31
|
100
|
|
|
|
98
|
$self->populate($board) if defined $board; |
507
|
31
|
|
|
|
|
107
|
my $rv = Result->new(name => $self->blueprint->name); |
508
|
31
|
|
|
|
|
185
|
my $initial_ip = Language::Befunge::IP->new($$self{dims}); |
509
|
31
|
|
|
|
|
2534
|
$initial_ip->set_position($$self{codeoffset}); |
510
|
31
|
|
|
|
|
101
|
my $interp = $self->interp; |
511
|
31
|
|
|
|
|
144
|
push(@{$initial_ip->get_toss}, @{$interp->get_params}); |
|
31
|
|
|
|
|
78
|
|
|
31
|
|
|
|
|
190
|
|
512
|
31
|
|
|
|
|
122
|
$interp->set_ips([$initial_ip]); |
513
|
31
|
|
|
|
|
143
|
while($self->tokens > 0) { |
514
|
670
|
|
|
|
|
3938
|
my $ip = shift @{$interp->get_ips()}; |
|
670
|
|
|
|
|
1507
|
|
515
|
670
|
100
|
|
|
|
1715
|
unless(defined($ip)) { |
516
|
131
|
|
|
|
|
145
|
my @ips = @{$interp->get_newips}; |
|
131
|
|
|
|
|
406
|
|
517
|
131
|
100
|
|
|
|
303
|
last unless scalar @ips; |
518
|
116
|
|
|
|
|
140
|
$ip = shift @ips; |
519
|
116
|
|
|
|
|
424
|
$interp->set_ips([@ips]); |
520
|
|
|
|
|
|
|
} |
521
|
655
|
100
|
|
|
|
1699
|
unless(defined $$ip{_ai_critter}) { |
522
|
30
|
|
|
|
|
56
|
$$ip{_ai_critter} = $self; |
523
|
30
|
|
|
|
|
83
|
weaken($$ip{_ai_critter}); |
524
|
|
|
|
|
|
|
} |
525
|
655
|
50
|
|
|
|
1806
|
last unless $self->spend($self->itercost); |
526
|
655
|
|
|
|
|
1355
|
$interp->set_curip($ip); |
527
|
655
|
|
|
|
|
1605
|
$interp->process_ip(); |
528
|
652
|
100
|
|
|
|
234107
|
if(defined($$self{move})) { |
529
|
10
|
|
|
|
|
46
|
debug("move made: " . $$self{move} . "\n"); |
530
|
10
|
|
|
|
|
50
|
$rv->choice( $$self{move} ); |
531
|
10
|
|
|
|
|
140
|
return $rv; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
} |
534
|
18
|
|
|
|
|
101
|
debug("play timeout\n"); |
535
|
18
|
|
|
|
|
56
|
return $rv; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=head2 move |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
my $rv = $critter->move($board, $score); |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
Similar to invoke(), above. This function wraps invoke() in an |
544
|
|
|
|
|
|
|
eval block, updates a scoreboard afterwards, and creates a "dead" |
545
|
|
|
|
|
|
|
return value if the eval failed. |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=cut |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
sub move { |
550
|
29
|
|
|
29
|
1
|
1002
|
my ($self, $board) = @_; |
551
|
29
|
|
|
|
|
44
|
my $rv; |
552
|
29
|
|
|
|
|
46
|
local $@ = ''; |
553
|
29
|
|
|
|
|
56
|
eval { |
554
|
29
|
|
|
|
|
791
|
$rv = $self->invoke($board); |
555
|
|
|
|
|
|
|
}; |
556
|
29
|
100
|
|
|
|
4187
|
if($@ ne '') { |
557
|
3
|
|
|
|
|
24
|
debug("eval error $@\n"); |
558
|
3
|
|
|
|
|
15
|
$rv = Result->new(name => $self->blueprint->name, died => 1); |
559
|
3
|
|
|
|
|
8
|
my $reason = $@; |
560
|
3
|
|
|
|
|
12
|
chomp $reason; |
561
|
3
|
|
|
|
|
15
|
$rv->fate($reason); |
562
|
|
|
|
|
|
|
} |
563
|
29
|
|
|
|
|
187
|
$rv->tokens($self->tokens); |
564
|
29
|
|
|
|
|
392
|
return $rv; |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head2 populate |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
$critter->populate($board); |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
Writes the board game state into the Befunge universe. |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
=cut |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
sub populate { |
577
|
14
|
|
|
14
|
1
|
23
|
my ($self, $board) = @_; |
578
|
14
|
|
|
|
|
65
|
my $storage = $$self{interp}->get_storage; |
579
|
14
|
|
|
|
|
52
|
$storage->store($board->as_string); |
580
|
14
|
|
|
|
|
666
|
$$self{interp}{_ai_board} = $board; |
581
|
14
|
|
|
|
|
53
|
weaken($$self{interp}{_ai_board}); |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=head2 spend |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
return unless $critter->spend($tokens * $cost); |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
Attempts to spend a certain amount of the critter's tokens. Returns |
590
|
|
|
|
|
|
|
true on success, false on failure. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=cut |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
sub spend { |
595
|
953
|
|
|
953
|
1
|
4805
|
my ($self, $cost) = @_; |
596
|
953
|
|
|
|
|
1299
|
$cost = int($cost); |
597
|
953
|
|
|
|
|
2360
|
my $tokens = $self->tokens - $cost; |
598
|
|
|
|
|
|
|
#debug("spend: cost=$cost resulting tokens=$tokens\n"); |
599
|
953
|
100
|
|
|
|
5349
|
return 0 if $tokens < 0; |
600
|
947
|
|
|
|
|
4577
|
$self->tokens($tokens); |
601
|
947
|
|
|
|
|
14935
|
return 1; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
# sandboxing stuff |
606
|
|
|
|
|
|
|
{ |
607
|
5
|
|
|
5
|
|
16049
|
no warnings 'redefine'; |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
339
|
|
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
# override Storage->expand() to impose bounds checking |
610
|
|
|
|
|
|
|
my $_lbsgv_expand; |
611
|
5
|
|
|
5
|
|
955
|
BEGIN { $_lbsgv_expand = \&Language::Befunge::Storage::Generic::Vec::expand; }; |
612
|
|
|
|
|
|
|
sub _expand { |
613
|
124
|
|
|
124
|
|
5583
|
my ($storage, $v) = @_; |
614
|
124
|
100
|
|
|
|
323
|
if(exists($$storage{maxsize})) { |
615
|
38
|
|
|
|
|
63
|
my $min = $$storage{minsize}; |
616
|
38
|
|
|
|
|
55
|
my $max = $$storage{maxsize}; |
617
|
38
|
100
|
|
|
|
728
|
die "$v is out of bounds [$min,$max]!\n" |
618
|
|
|
|
|
|
|
unless $v->bounds_check($min, $max); |
619
|
|
|
|
|
|
|
} |
620
|
116
|
|
|
|
|
1172
|
my $rv = &$_lbsgv_expand(@_); |
621
|
116
|
|
|
|
|
4015
|
return $rv; |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
# redundant assignment avoids a "possible typo" warning |
624
|
|
|
|
|
|
|
*Language::Befunge::Storage::Generic::Vec::XS::expand = \&_expand; |
625
|
|
|
|
|
|
|
*Language::Befunge::Storage::Generic::Vec::XS::expand = \&_expand; |
626
|
|
|
|
|
|
|
*Language::Befunge::Storage::Generic::Vec::expand = \&_expand; |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
# override IP->spush() to impose stack size checking |
629
|
|
|
|
|
|
|
my $_lbip_spush; |
630
|
5
|
|
|
5
|
|
3199
|
BEGIN { $_lbip_spush = \&Language::Befunge::IP::spush; }; |
631
|
|
|
|
|
|
|
sub _spush { |
632
|
288
|
|
|
288
|
|
19903
|
my ($ip, @newvals) = @_; |
633
|
288
|
|
|
|
|
496
|
my $critter = $$ip{_ai_critter}; |
634
|
288
|
100
|
|
|
|
715
|
return $ip->dir_reverse unless $critter->spend($critter->stackcost * scalar @newvals); |
635
|
287
|
|
|
|
|
738
|
my $rv = &$_lbip_spush(@_); |
636
|
287
|
|
|
|
|
1951
|
return $rv; |
637
|
|
|
|
|
|
|
} |
638
|
|
|
|
|
|
|
*Language::Befunge::IP::spush = \&_spush; |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# override IP->ss_create() to impose stack count checking |
641
|
|
|
|
|
|
|
sub _block_open { |
642
|
2
|
|
|
2
|
|
85
|
my ($interp) = @_; |
643
|
2
|
|
|
|
|
6
|
my $ip = $interp->get_curip; |
644
|
2
|
|
|
|
|
4
|
my $critter = $$ip{_ai_critter}; |
645
|
2
|
|
|
|
|
7
|
my $count = $ip->svalue(1); |
646
|
2
|
100
|
|
|
|
15
|
return $ip->dir_reverse unless $critter->spend($critter->stackcost * $count); |
647
|
1
|
|
|
|
|
5
|
return Language::Befunge::Ops::block_open(@_); |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# override op_flow_jump_to to impose skip count checking |
651
|
|
|
|
|
|
|
sub _op_flow_jump_to_wrap { |
652
|
2
|
|
|
2
|
|
88
|
my ($interp) = @_; |
653
|
2
|
|
|
|
|
6
|
my $ip = $interp->get_curip; |
654
|
2
|
|
|
|
|
4
|
my $critter = $$interp{_ai_critter}; |
655
|
2
|
|
|
|
|
8
|
my $count = $ip->svalue(1); |
656
|
2
|
100
|
|
|
|
17
|
return $ip->dir_reverse unless $critter->spend($critter->repeatcost * abs($count)); |
657
|
1
|
|
|
|
|
4
|
return Language::Befunge::Ops::flow_jump_to(@_); |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# override op_flow_repeat to impose loop count checking |
661
|
|
|
|
|
|
|
sub _op_flow_repeat_wrap { |
662
|
4
|
|
|
4
|
|
218
|
my ($interp) = @_; |
663
|
4
|
|
|
|
|
13
|
my $ip = $interp->get_curip; |
664
|
4
|
|
|
|
|
8
|
my $critter = $$interp{_ai_critter}; |
665
|
4
|
|
|
|
|
18
|
my $count = $ip->svalue(1); |
666
|
4
|
100
|
|
|
|
45
|
return $ip->dir_reverse unless $critter->spend($critter->repeatcost * abs($count)); |
667
|
2
|
|
|
|
|
8
|
return Language::Befunge::Ops::flow_repeat(@_); |
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
# override op_spawn_ip to impose thread count checking |
671
|
|
|
|
|
|
|
sub _op_spawn_ip_wrap { |
672
|
2
|
|
|
2
|
|
119
|
my ($interp) = @_; |
673
|
2
|
|
|
|
|
6
|
my $ip = $interp->get_curip; |
674
|
2
|
|
|
|
|
3
|
my $critter = $$interp{_ai_critter}; |
675
|
2
|
|
|
|
|
4
|
my $cost = 0;$critter->threadcost; |
|
2
|
|
|
|
|
8
|
|
676
|
2
|
|
|
|
|
12
|
foreach my $stack ($ip->get_toss(), @{$ip->get_ss}) { |
|
2
|
|
|
|
|
8
|
|
677
|
2
|
|
|
|
|
6
|
$cost += scalar @$stack; |
678
|
|
|
|
|
|
|
} |
679
|
2
|
|
|
|
|
6
|
$cost *= $critter->stackcost; |
680
|
2
|
|
|
|
|
14
|
$cost += $critter->threadcost; |
681
|
2
|
100
|
|
|
|
12
|
return $ip->dir_reverse unless $critter->spend($cost); |
682
|
|
|
|
|
|
|
# This is a hack; Storable can't deep copy our data structure. |
683
|
|
|
|
|
|
|
# It will get re-added to both parent and child, next time around. |
684
|
1
|
|
|
|
|
2
|
delete($$ip{_ai_critter}); |
685
|
1
|
|
|
|
|
6
|
return Language::Befunge::Ops::spawn_ip(@_); |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
} |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
1; |