git @ Cat's Eye Technologies REDGREEN / a47fa90
Generate JS with alpaca, add demo in HTML using yoob.js. catseye 12 years ago
4 changed file(s) with 901 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 <!DOCTYPE html>
1 <head>
2 <meta charset="utf-8">
3 <title>REDGREEN Demo</title>
4 </head>
5 <body>
6
7 <h1>REDGREEN Demo</h1>
8
9 <textarea id="input" rows="20" cols="80">
10 #####
11
12 # #
13 ...... # #
14 # ~ #
15 ####################### #
16 %# #
17 . . . T ##### #
18 ### # : #
19 # #
20 # . #
21 # #
22 # #
23 # . #
24 # #
25 # #
26 &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;##&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;############################
27 %
28 T
29 </textarea>
30
31 <pre id="output" style="border: 1px solid blue">
32 </pre>
33
34 <button id="start">Start</button>
35 <button id="stop">Stop</button>
36 <button id="step">Step</button>
37 <button id="load">Load</button>
38 Speed: <input id="speed" type="range" min="0" max="200" value="0" />
39
40 </body>
41 <script src="yoob/controller.js"></script>
42 <script src="yoob/playfield.js"></script>
43 <script src="../script/redgreen.js"></script>
44 <script>
45 var output = document.getElementById('output');
46 var c = new yoob.Controller();
47 var pf;
48
49 c.load = function(text) {
50 pf = new yoob.Playfield();
51 pf.setDefault('Air');
52 pf.load(0, 0, text, loadMapper);
53 output.innerHTML = pf.dump(dumpMapper);
54 };
55
56 c.step = function() {
57 var newPf = new yoob.Playfield();
58 newPf.setDefault('Air');
59 evolve_playfield(pf, newPf);
60 pf = newPf;
61 output.innerHTML = pf.dump(dumpMapper);
62 };
63
64 c.connect({
65 'start': 'start',
66 'stop': 'stop',
67 'step': 'step',
68 'load': 'load',
69 'source': 'input',
70 'speed': 'speed'
71 });
72 </script>
0 /*
1 * This file is part of yoob.js version 0.2-PRE
2 * This file is in the public domain. See http://unlicense.org/ for details.
3 */
4 if (window.yoob === undefined) yoob = {};
5
6 /*
7 * A controller for executing(/animating/evolving) states
8 * (such as esolang program states or cellular automaton
9 * configurations.)
10 *
11 * Can be connected to a UI in the DOM.
12 *
13 * Subclass this and override the following methods:
14 * - make it evolve the state by one tick in the step() method
15 * - make it load the state from a multiline string in the load() method
16 */
17 yoob.Controller = function() {
18 this.intervalId = undefined;
19 this.delay = 100;
20 this.source = undefined;
21 this.speed = undefined;
22
23 var makeOnClick = function(controller, key) {
24 if (controller['click_' + key] !== undefined)
25 key = 'click_' + key;
26 return function(e) { controller[key](); }
27 };
28
29 /*
30 * Single argument is a dictionary (object) where the keys
31 * are the actions a controller can undertake, and the values
32 * are either DOM elements or strings; if strings, DOM elements
33 * with those ids will be obtained from the document and used.
34 */
35 this.connect = function(dict) {
36 var self = this;
37 var keys = ["start", "stop", "step", "load"];
38 for (var i in keys) {
39 var key = keys[i];
40 var value = dict[key];
41 if (typeof value === 'string') {
42 value = document.getElementById(value);
43 }
44 if (value !== undefined) {
45 value.onclick = makeOnClick(this, key);
46 }
47 }
48
49 var source = dict.source;
50 if (typeof source === 'string') {
51 source = document.getElementById(source);
52 }
53 if (source !== undefined) {
54 this.source = source;
55 }
56
57 var speed = dict.speed;
58 if (typeof speed === 'string') {
59 speed = document.getElementById(speed);
60 }
61 if (speed !== undefined) {
62 this.speed = speed;
63 speed.value = self.delay;
64 speed.onchange = function(e) {
65 self.delay = speed.value;
66 if (self.intervalId !== undefined) {
67 self.stop();
68 self.start();
69 }
70 }
71 }
72 };
73
74 this.click_step = function(e) {
75 this.stop();
76 this.step();
77 };
78
79 this.step = function() {
80 alert("step() NotImplementedError");
81 };
82
83 this.click_load = function(e) {
84 this.stop();
85 this.load(this.source.value);
86 };
87
88 this.load = function(text) {
89 alert("load() NotImplementedError");
90 };
91
92 this.start = function() {
93 if (this.intervalId !== undefined)
94 return;
95 this.step();
96 var self = this;
97 this.intervalId = setInterval(function() { self.step(); }, this.delay);
98 };
99
100 this.stop = function() {
101 if (this.intervalId === undefined)
102 return;
103 clearInterval(this.intervalId);
104 this.intervalId = undefined;
105 };
106 };
0 /*
1 * This file is part of yoob.js version 0.2
2 * This file is in the public domain. See http://unlicense.org/ for details.
3 */
4 if (window.yoob === undefined) yoob = {};
5
6 /*
7 * A two-dimensional Cartesian grid of values.
8 */
9 yoob.Playfield = function() {
10 this._store = {};
11 this.minX = undefined;
12 this.minY = undefined;
13 this.maxX = undefined;
14 this.maxY = undefined;
15 this._default = undefined;
16
17 /*
18 * Set the default value for this Playfield. This
19 * value is returned by get() for any cell that was
20 * never written to, or had `undefined` put() into it.
21 */
22 this.setDefault = function(v) {
23 this._default = v;
24 };
25
26 /*
27 * Obtain the value at (x, y). The default value will
28 * be returned if the cell was never written to.
29 */
30 this.get = function(x, y) {
31 var v = this._store[x+','+y];
32 if (v === undefined) return this._default;
33 return v;
34 };
35
36 /*
37 * Write a new value into (x, y). Note that writing
38 * `undefined` into a cell has the semantics of deleting
39 * the value at that cell; a subsequent get() for that
40 * location will return this Playfield's default value.
41 */
42 this.put = function(x, y, value) {
43 var key = x+','+y;
44 if (value === undefined || value === this._default) {
45 delete this._store[key];
46 return;
47 }
48 if (this.minX === undefined || x < this.minX) this.minX = x;
49 if (this.maxX === undefined || x > this.maxX) this.maxX = x;
50 if (this.minY === undefined || y < this.minY) this.minY = y;
51 if (this.maxY === undefined || y > this.maxY) this.maxY = y;
52 this._store[key] = value;
53 };
54
55 /*
56 * Like put(), but does not update the playfield bounds. Do
57 * this if you must do a batch of put()s in a more efficient
58 * manner; after doing so, call recalculateBounds().
59 */
60 this.putDirty = function(x, y, value) {
61 var key = x+','+y;
62 if (value === undefined || value === this._default) {
63 delete this._store[key];
64 return;
65 }
66 this._store[key] = value;
67 };
68
69 /*
70 * Recalculate the bounds (min/max X/Y) which are tracked
71 * internally to support methods like foreach(). This is
72 * not needed *unless* you've used putDirty() at some point.
73 * (In which case, call this immediately after your batch
74 * of putDirty()s.)
75 */
76 this.recalculateBounds = function() {
77 this.minX = undefined;
78 this.minY = undefined;
79 this.maxX = undefined;
80 this.maxX = undefined;
81
82 for (var cell in this._store) {
83 var pos = cell.split(',');
84 var x = parseInt(pos[0], 10);
85 var y = parseInt(pos[1], 10);
86 if (this.minX === undefined || x < this.minX) this.minX = x;
87 if (this.maxX === undefined || x > this.maxX) this.maxX = x;
88 if (this.minY === undefined || y < this.minY) this.minY = y;
89 if (this.maxY === undefined || y > this.maxY) this.maxY = y;
90 }
91 };
92
93 /*
94 * Clear the contents of this Playfield.
95 */
96 this.clear = function() {
97 this._store = {};
98 this.minX = undefined;
99 this.minY = undefined;
100 this.maxX = undefined;
101 this.maxX = undefined;
102 };
103
104 /*
105 * Load a string into this Playfield.
106 * The string may be multiline, with newline (ASCII 10)
107 * characters delimiting lines. ASCII 13 is ignored.
108 *
109 * If transformer is given, it should be a one-argument
110 * function which accepts a character and returns the
111 * object you wish to write into the playfield upon reading
112 * that character.
113 */
114 this.load = function(x, y, string, transformer) {
115 var lx = x;
116 var ly = y;
117 if (transformer === undefined) {
118 transformer = function(c) {
119 if (c === ' ') {
120 return undefined;
121 } else {
122 return c;
123 }
124 }
125 }
126 for (var i = 0; i < string.length; i++) {
127 var c = string.charAt(i);
128 if (c === '\n') {
129 lx = x;
130 ly++;
131 } else if (c === '\r') {
132 } else {
133 this.putDirty(lx, ly, transformer(c));
134 lx++;
135 }
136 }
137 this.recalculateBounds();
138 };
139
140 /*
141 * Convert this Playfield to a multi-line string. Each row
142 * is a line, delimited with a newline (ASCII 10).
143 *
144 * If transformer is given, it should be a one-argument
145 * function which accepts a playfield element and returns a
146 * character (or string) you wish to place in the resulting
147 * string for that element.
148 */
149 this.dump = function(transformer) {
150 var text = "";
151 if (transformer === undefined) {
152 transformer = function(c) { return c; }
153 }
154 for (var y = this.minY; y <= this.maxY; y++) {
155 var row = "";
156 for (var x = this.minX; x <= this.maxX; x++) {
157 row += transformer(this.get(x, y));
158 }
159 text += row + "\n";
160 }
161 return text;
162 };
163
164 /*
165 * Iterate over every defined cell in the Playfield.
166 * fun is a callback which takes three parameters:
167 * x, y, and value. If this callback returns a value,
168 * it is written into the Playfield at that position.
169 * This function ensures a particular order.
170 */
171 this.foreach = function(fun) {
172 for (var y = this.minY; y <= this.maxY; y++) {
173 for (var x = this.minX; x <= this.maxX; x++) {
174 var key = x+','+y;
175 var value = this._store[key];
176 if (value === undefined)
177 continue;
178 var result = fun(x, y, value);
179 if (result !== undefined) {
180 if (result === ' ') {
181 result = undefined;
182 }
183 this.put(x, y, result);
184 }
185 }
186 }
187 };
188
189 /*
190 * Analogous to (monoid) map in functional languages,
191 * iterate over this Playfield, transform each value using
192 * a supplied function, and write the transformed value into
193 * a destination Playfield.
194 *
195 * Supplied function should take a Playfield (this Playfield),
196 * x, and y, and return a value.
197 *
198 * The map source may extend beyond the internal bounds of
199 * the Playfield, by giving the min/max Dx/Dy arguments
200 * (which work like margin offsets.)
201 *
202 * Useful for evolving a cellular automaton playfield. In this
203 * case, min/max Dx/Dy should be computed from the neighbourhood.
204 */
205 this.map = function(destPf, fun, minDx, minDy, maxDx, maxDy) {
206 if (minDx === undefined) minDx = 0;
207 if (minDy === undefined) minDy = 0;
208 if (maxDx === undefined) maxDx = 0;
209 if (maxDy === undefined) maxDy = 0;
210 for (var y = this.minY + minDy; y <= this.maxY + maxDy; y++) {
211 for (var x = this.minX + minDx; x <= this.maxX + maxDx; x++) {
212 destPf.putDirty(x, y, fun(pf, x, y));
213 }
214 }
215 destPf.recalculateBounds();
216 };
217
218 /*
219 * Draws elements of the Playfield in a drawing context.
220 * x and y are canvas coordinates, and width and height
221 * are canvas units of measure.
222 * The default implementation just renders them as text,
223 * in black.
224 * Override if you wish to draw them differently.
225 */
226 this.drawElement = function(ctx, x, y, cellWidth, cellHeight, elem) {
227 ctx.fillStyle = "black";
228 ctx.fillText(elem.toString(), x, y);
229 };
230
231 /*
232 * Draws the Playfield in a drawing context.
233 * cellWidth and cellHeight are canvas units of measure for each cell.
234 */
235 this.drawContext = function(ctx, offsetX, offsetY, cellWidth, cellHeight) {
236 var me = this;
237 this.foreach(function (x, y, elem) {
238 me.drawElement(ctx, offsetX + x * cellWidth, offsetY + y * cellHeight,
239 cellWidth, cellHeight, elem);
240 });
241 };
242
243 this.getExtentX = function() {
244 if (this.maxX === undefined || this.minX === undefined) {
245 return 0;
246 } else {
247 return this.maxX - this.minX + 1;
248 }
249 };
250
251 this.getExtentY = function() {
252 if (this.maxY === undefined || this.minY === undefined) {
253 return 0;
254 } else {
255 return this.maxY - this.minY + 1;
256 }
257 };
258
259 /*
260 * Draws the Playfield, and a set of Cursors, on a canvas element.
261 * Resizes the canvas to the needed dimensions.
262 * cellWidth and cellHeight are canvas units of measure for each cell.
263 */
264 this.drawCanvas = function(canvas, cellWidth, cellHeight, cursors) {
265 var ctx = canvas.getContext('2d');
266
267 var width = this.getExtentX();
268 var height = this.getExtentY();
269
270 if (cellWidth === undefined) {
271 ctx.textBaseline = "top";
272 ctx.font = cellHeight + "px monospace";
273 cellWidth = ctx.measureText("@").width;
274 }
275
276 canvas.width = width * cellWidth;
277 canvas.height = height * cellHeight;
278
279 ctx.clearRect(0, 0, canvas.width, canvas.height);
280
281 ctx.textBaseline = "top";
282 ctx.font = cellHeight + "px monospace";
283
284 var offsetX = this.minX * cellWidth * -1;
285 var offsetY = this.minY * cellHeight * -1;
286
287 for (var i = 0; i < cursors.length; i++) {
288 cursors[i].drawContext(
289 ctx,
290 cursors[i].x * cellWidth, cursors[i].y * cellHeight,
291 cellWidth, cellHeight
292 );
293 }
294
295 this.drawContext(ctx, offsetX, offsetY, cellWidth, cellHeight);
296 };
297
298 };
0 /*
1 * This file was AUTOMATICALLY generated from an ALPACA description.
2 * EDIT AT YOUR OWN RISK!
3 */
4
5
6 function in_nbhd_pred(pf, x, y, pred, nbhd) {
7 var count = 0;
8 for (var i = 0; i < nbhd.length; i++) {
9 if (pred(pf.get(x+nbhd[i][0], y+nbhd[i][1]))) {
10 count++;
11 }
12 }
13 return count;
14 }
15
16 function in_nbhd_eq(pf, x, y, stateId, nbhd) {
17 return in_nbhd_pred(pf, x, y, function(x) { return x === stateId; }, nbhd);
18 }
19
20 function evolve_playfield(pf, new_pf) {
21 pf.map(new_pf, evalState, -1, -1, 1, 1);
22 }
23 function loadMapper(c) {
24 if (c === ' ') return 'Air';
25 if (c === '#') return 'Earth';
26 if (c === '%') return 'Fire';
27 if (c === '&') return 'Magma';
28 if (c === '*') return 'Spark';
29 if (c === '-') return 'Tail';
30 if (c === '.') return 'OnePebble';
31 if (c === ':') return 'TwoPebble';
32 if (c === '=') return 'Wire';
33 if (c === '<') return 'ConveyorLeft';
34 if (c === '?') return 'Randomizer';
35 if (c === '>') return 'ConveyorRight';
36 if (c === '@') return 'Smoke';
37 if (c === 'D') return 'DuctTape';
38 if (c === 'O') return 'UnravelTape';
39 if (c === 'T') return 'Torch';
40 if (c === 'Z') return 'BigZappy';
41 if (c === 'f') return 'Fish';
42 if (c === 'l') return 'Twig';
43 if (c === 'o') return 'Bubble';
44 if (c === 's') return 'Steam';
45 if (c === 'z') return 'Zappy';
46 if (c === '~') return 'Water';
47 };
48 function dumpMapper(s) {
49 if (s === 'Air') return ' ';
50 if (s === 'Earth') return '#';
51 if (s === 'Fire') return '%';
52 if (s === 'Magma') return '&';
53 if (s === 'Spark') return '*';
54 if (s === 'Tail') return '-';
55 if (s === 'OnePebble') return '.';
56 if (s === 'TwoPebble') return ':';
57 if (s === 'Wire') return '=';
58 if (s === 'ConveyorLeft') return '<';
59 if (s === 'Randomizer') return '?';
60 if (s === 'ConveyorRight') return '>';
61 if (s === 'Smoke') return '@';
62 if (s === 'DuctTape') return 'D';
63 if (s === 'UnravelTape') return 'O';
64 if (s === 'Torch') return 'T';
65 if (s === 'BigZappy') return 'Z';
66 if (s === 'Fish') return 'f';
67 if (s === 'Twig') return 'l';
68 if (s === 'Bubble') return 'o';
69 if (s === 'Steam') return 's';
70 if (s === 'Zappy') return 'z';
71 if (s === 'Water') return '~';
72 };
73 function is_Passthru(st) {
74 return (st === 'Fire') || (st === 'Zappy') || (st === 'BigZappy') || (st === 'Air') || (st === 'Water') || (st === 'Smoke') || (st === 'Bubble') || (st === 'Steam') || 0;
75 }
76
77 function is_Flammable(st) {
78 return (st === 'UnravelTape') || (st === 'Twig') || (st === 'DuctTape') || 0;
79 }
80
81 function is_Support(st) {
82 return (st === 'Wire') || (st === 'DuctTape') || (st === 'Randomizer') || (st === 'TwoPebble') || (st === 'Magma') || (st === 'Tail') || (st === 'Earth') || (st === 'Spark') || (st === 'Twig') || 0;
83 }
84
85 function is_Burner(st) {
86 return (st === 'Fire') || (st === 'Zappy') || (st === 'Spark') || (st === 'Magma') || (st === 'BigZappy') || 0;
87 }
88
89 function is_Steamy(st) {
90 return (st === 'Bubble') || (st === 'Steam') || (st === 'Smoke') || 0;
91 }
92
93 function is_Fallable(st) {
94 return (st === 'UnravelTape') || (st === 'OnePebble') || (st === 'TwoPebble') || (st === 'Twig') || 0;
95 }
96
97 function evalClass_Fallable(pf, x, y) {
98 var id;
99 if (((!(is_Support(pf.get(x+0,y+1)))&&(in_nbhd_eq(pf, x, y, 'Air', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 2))&&!((in_nbhd_eq(pf, x, y, 'DuctTape', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)))) {
100 return 'Air';
101 }
102 if (((!(is_Support(pf.get(x+0,y+1)))&&(in_nbhd_eq(pf, x, y, 'Water', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 4))&&!((in_nbhd_eq(pf, x, y, 'DuctTape', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)))) {
103 return 'Water';
104 }
105 if ((((pf.get(x+0,y+1)==='ConveyorLeft')&&!(is_Passthru(pf.get(x+-1,y+0))))||((pf.get(x+0,y+1)==='ConveyorRight')&&!(is_Passthru(pf.get(x+1,y+0)))))) {
106 return pf.get(x+0,y+0);
107 }
108 return undefined;
109 }
110
111 function evalClass_Passthru(pf, x, y) {
112 var id;
113 if (is_Fallable(pf.get(x+0,y+-1))) {
114 return pf.get(x+0,y+-1);
115 }
116 if ((is_Fallable(pf.get(x+1,y+0))&&(pf.get(x+1,y+1)==='ConveyorLeft'))) {
117 return pf.get(x+1,y+0);
118 }
119 if ((is_Fallable(pf.get(x+-1,y+0))&&(pf.get(x+-1,y+1)==='ConveyorRight'))) {
120 return pf.get(x+-1,y+0);
121 }
122 return undefined;
123 }
124
125 function evalClass_Flammable(pf, x, y) {
126 var id;
127 if ((in_nbhd_pred(pf, x, y, is_Burner, [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)) {
128 return 'Fire';
129 }
130 return undefined;
131 }
132
133 function evalClass_Steamy(pf, x, y) {
134 var id;
135 if ((((((pf.get(x+-1,y+0)==='Water')||(pf.get(x+1,y+0)==='Water'))||(pf.get(x+-1,y+-1)==='Water'))||(pf.get(x+1,y+-1)==='Water'))||(pf.get(x+0,y+-1)==='Water'))) {
136 return 'Water';
137 }
138 if (true) {
139 return 'Air';
140 }
141 return undefined;
142 }
143
144 function evalClass_Burner(pf, x, y) {
145 var id;
146 return undefined;
147 }
148
149 function evalClass_Support(pf, x, y) {
150 var id;
151 return undefined;
152 }
153
154 function eval_Air(pf, x, y) {
155 var id;
156 if ((((((((pf.get(x+0,y+-1)==='Water')||(pf.get(x+-1,y+-1)==='Water'))||(pf.get(x+1,y+-1)==='Water'))||((pf.get(x+-1,y+0)==='Water')&&is_Support(pf.get(x+-1,y+1))))||(((pf.get(x+-1,y+0)==='Water')&&(pf.get(x+-1,y+1)==='Water'))&&(pf.get(x+0,y+1)==='Water')))||((pf.get(x+1,y+0)==='Water')&&is_Support(pf.get(x+1,y+1))))||(((pf.get(x+1,y+0)==='Water')&&(pf.get(x+1,y+1)==='Water'))&&(pf.get(x+0,y+1)==='Water')))) {
157 return 'Water';
158 }
159 if ((((pf.get(x+0,y+1)==='Steam')||(pf.get(x+-1,y+1)==='Steam'))||(pf.get(x+1,y+1)==='Steam'))) {
160 return 'Steam';
161 }
162 if ((((pf.get(x+0,y+1)==='Smoke')||(pf.get(x+-1,y+1)==='Smoke'))||(pf.get(x+1,y+1)==='Smoke'))) {
163 return 'Smoke';
164 }
165 if ((in_nbhd_eq(pf, x, y, 'Spark', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)) {
166 return 'Zappy';
167 }
168 id = evalClass_Passthru(pf, x, y);
169 if (id !== undefined) return id;
170 return 'Air';
171 }
172
173 function eval_Water(pf, x, y) {
174 var id;
175 if (((in_nbhd_eq(pf, x, y, 'Fire', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)||(in_nbhd_eq(pf, x, y, 'Magma', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1))) {
176 return 'Steam';
177 }
178 if ((((pf.get(x+0,y+1)==='Bubble')||(pf.get(x+0,y+1)==='Smoke'))||(pf.get(x+0,y+1)==='Steam'))) {
179 return 'Bubble';
180 }
181 if (((in_nbhd_eq(pf, x, y, 'Fish', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 3)&&(in_nbhd_eq(pf, x, y, 'Water', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 5))) {
182 return 'Fish';
183 }
184 id = evalClass_Passthru(pf, x, y);
185 if (id !== undefined) return id;
186 return 'Water';
187 }
188
189 function eval_Fire(pf, x, y) {
190 var id;
191 if ((((in_nbhd_eq(pf, x, y, 'Water', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)||!((in_nbhd_eq(pf, x, y, 'Air', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)))||((!((in_nbhd_eq(pf, x, y, 'Torch', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1))&&!((in_nbhd_eq(pf, x, y, 'DuctTape', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)))&&!((in_nbhd_eq(pf, x, y, 'Twig', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1))))) {
192 return 'Smoke';
193 }
194 id = evalClass_Passthru(pf, x, y);
195 if (id !== undefined) return id;
196 id = evalClass_Burner(pf, x, y);
197 if (id !== undefined) return id;
198 return 'Fire';
199 }
200
201 function eval_Earth(pf, x, y) {
202 var id;
203 if ((in_nbhd_eq(pf, x, y, 'Fire', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)) {
204 return 'Magma';
205 }
206 id = evalClass_Support(pf, x, y);
207 if (id !== undefined) return id;
208 return 'Earth';
209 }
210
211 function eval_Magma(pf, x, y) {
212 var id;
213 if ((!((in_nbhd_eq(pf, x, y, 'Fire', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1))&&!((in_nbhd_eq(pf, x, y, 'Magma', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 2)))) {
214 return 'Earth';
215 }
216 id = evalClass_Support(pf, x, y);
217 if (id !== undefined) return id;
218 id = evalClass_Burner(pf, x, y);
219 if (id !== undefined) return id;
220 return 'Magma';
221 }
222
223 function eval_Steam(pf, x, y) {
224 var id;
225 id = evalClass_Passthru(pf, x, y);
226 if (id !== undefined) return id;
227 id = evalClass_Steamy(pf, x, y);
228 if (id !== undefined) return id;
229 return 'Steam';
230 }
231
232 function eval_Smoke(pf, x, y) {
233 var id;
234 id = evalClass_Passthru(pf, x, y);
235 if (id !== undefined) return id;
236 id = evalClass_Steamy(pf, x, y);
237 if (id !== undefined) return id;
238 return 'Smoke';
239 }
240
241 function eval_Bubble(pf, x, y) {
242 var id;
243 id = evalClass_Passthru(pf, x, y);
244 if (id !== undefined) return id;
245 id = evalClass_Steamy(pf, x, y);
246 if (id !== undefined) return id;
247 return 'Bubble';
248 }
249
250 function eval_Fish(pf, x, y) {
251 var id;
252 if ((!((in_nbhd_eq(pf, x, y, 'Water', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1))&&!((in_nbhd_eq(pf, x, y, 'Fish', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 8)))) {
253 return 'Air';
254 }
255 if (((in_nbhd_eq(pf, x, y, 'Fish', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 4)||(in_nbhd_eq(pf, x, y, 'Water', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 7))) {
256 return 'Water';
257 }
258 return 'Fish';
259 }
260
261 function eval_OnePebble(pf, x, y) {
262 var id;
263 if (((pf.get(x+0,y+-1)==='OnePebble')&&is_Support(pf.get(x+0,y+1)))) {
264 return 'TwoPebble';
265 }
266 id = evalClass_Fallable(pf, x, y);
267 if (id !== undefined) return id;
268 return 'OnePebble';
269 }
270
271 function eval_TwoPebble(pf, x, y) {
272 var id;
273 id = evalClass_Fallable(pf, x, y);
274 if (id !== undefined) return id;
275 id = evalClass_Support(pf, x, y);
276 if (id !== undefined) return id;
277 return 'TwoPebble';
278 }
279
280 function eval_Spark(pf, x, y) {
281 var id;
282 if (true) {
283 return 'Tail';
284 }
285 id = evalClass_Support(pf, x, y);
286 if (id !== undefined) return id;
287 id = evalClass_Burner(pf, x, y);
288 if (id !== undefined) return id;
289 return 'Spark';
290 }
291
292 function eval_Tail(pf, x, y) {
293 var id;
294 if (true) {
295 return 'Wire';
296 }
297 id = evalClass_Support(pf, x, y);
298 if (id !== undefined) return id;
299 return 'Tail';
300 }
301
302 function eval_Wire(pf, x, y) {
303 var id;
304 if (((in_nbhd_eq(pf, x, y, 'Spark', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)&&!((in_nbhd_eq(pf, x, y, 'Spark', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 3)))) {
305 return 'Spark';
306 }
307 id = evalClass_Support(pf, x, y);
308 if (id !== undefined) return id;
309 return 'Wire';
310 }
311
312 function eval_DuctTape(pf, x, y) {
313 var id;
314 if ((!((in_nbhd_eq(pf, x, y, 'DuctTape', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 2))||!(((in_nbhd_eq(pf, x, y, 'DuctTape', [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1)&&(in_nbhd_pred(pf, x, y, is_Support, [[0,-1],[0,1],[-1,0],[-1,1],[-1,-1],[1,0],[1,1],[1,-1]]) >= 1))))) {
315 return 'UnravelTape';
316 }
317 id = evalClass_Support(pf, x, y);
318 if (id !== undefined) return id;
319 id = evalClass_Flammable(pf, x, y);
320 if (id !== undefined) return id;
321 return 'DuctTape';
322 }
323
324 function eval_UnravelTape(pf, x, y) {
325 var id;
326 id = evalClass_Fallable(pf, x, y);
327 if (id !== undefined) return id;
328 id = evalClass_Flammable(pf, x, y);
329 if (id !== undefined) return id;
330 return 'UnravelTape';
331 }
332
333 function eval_Twig(pf, x, y) {
334 var id;
335 id = evalClass_Fallable(pf, x, y);
336 if (id !== undefined) return id;
337 id = evalClass_Support(pf, x, y);
338 if (id !== undefined) return id;
339 id = evalClass_Flammable(pf, x, y);
340 if (id !== undefined) return id;
341 return 'Twig';
342 }
343
344 function eval_Zappy(pf, x, y) {
345 var id;
346 if (true) {
347 return 'BigZappy';
348 }
349 id = evalClass_Passthru(pf, x, y);
350 if (id !== undefined) return id;
351 id = evalClass_Burner(pf, x, y);
352 if (id !== undefined) return id;
353 return 'Zappy';
354 }
355
356 function eval_BigZappy(pf, x, y) {
357 var id;
358 if (true) {
359 return 'Air';
360 }
361 id = evalClass_Passthru(pf, x, y);
362 if (id !== undefined) return id;
363 id = evalClass_Burner(pf, x, y);
364 if (id !== undefined) return id;
365 return 'BigZappy';
366 }
367
368 function eval_Randomizer(pf, x, y) {
369 var id;
370 id = evalClass_Support(pf, x, y);
371 if (id !== undefined) return id;
372 return 'Randomizer';
373 }
374
375 function eval_ConveyorLeft(pf, x, y) {
376 var id;
377 if (((pf.get(x+0,y+1)==='Randomizer')&&guess)) {
378 return 'ConveyorRight';
379 }
380 return 'ConveyorLeft';
381 }
382
383 function eval_ConveyorRight(pf, x, y) {
384 var id;
385 if (((pf.get(x+0,y+1)==='Randomizer')&&guess)) {
386 return 'ConveyorLeft';
387 }
388 return 'ConveyorRight';
389 }
390
391 function eval_Torch(pf, x, y) {
392 var id;
393 return 'Torch';
394 }
395
396 function evalState(pf, x, y) {
397 var stateId = pf.get(x, y);
398 if (stateId === 'Air') return eval_Air(pf, x, y);
399 if (stateId === 'Water') return eval_Water(pf, x, y);
400 if (stateId === 'Fire') return eval_Fire(pf, x, y);
401 if (stateId === 'Earth') return eval_Earth(pf, x, y);
402 if (stateId === 'Magma') return eval_Magma(pf, x, y);
403 if (stateId === 'Steam') return eval_Steam(pf, x, y);
404 if (stateId === 'Smoke') return eval_Smoke(pf, x, y);
405 if (stateId === 'Bubble') return eval_Bubble(pf, x, y);
406 if (stateId === 'Fish') return eval_Fish(pf, x, y);
407 if (stateId === 'OnePebble') return eval_OnePebble(pf, x, y);
408 if (stateId === 'TwoPebble') return eval_TwoPebble(pf, x, y);
409 if (stateId === 'Spark') return eval_Spark(pf, x, y);
410 if (stateId === 'Tail') return eval_Tail(pf, x, y);
411 if (stateId === 'Wire') return eval_Wire(pf, x, y);
412 if (stateId === 'DuctTape') return eval_DuctTape(pf, x, y);
413 if (stateId === 'UnravelTape') return eval_UnravelTape(pf, x, y);
414 if (stateId === 'Twig') return eval_Twig(pf, x, y);
415 if (stateId === 'Zappy') return eval_Zappy(pf, x, y);
416 if (stateId === 'BigZappy') return eval_BigZappy(pf, x, y);
417 if (stateId === 'Randomizer') return eval_Randomizer(pf, x, y);
418 if (stateId === 'ConveyorLeft') return eval_ConveyorLeft(pf, x, y);
419 if (stateId === 'ConveyorRight') return eval_ConveyorRight(pf, x, y);
420 if (stateId === 'Torch') return eval_Torch(pf, x, y);
421 }