| 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; |