git @ Cat's Eye Technologies ALPACA / 6a6186d
Stick almost all of yoob/playfield.js inline in the JS backend. catseye 12 years ago
1 changed file(s) with 253 addition(s) and 58 deletion(s). Raw diff Collapse all Expand all
77 get_defined_playfield
88 )
99
10
11 # We'll just stick (almost) the entire yoob/playfield.js file inline here
12 # see https://github.com/catseye/yoob.js for where this came from (v0.2)
13 YOOB_PLAYFIELD_JS = r"""
14 var yoob = {};
15 if (typeof window !== 'undefined') {
16 if (!window.yoob) window.yoob = {};
17 yoob = window.yoob;
18 }
19
20 /*
21 * A two-dimensional Cartesian grid of values.
22 */
23 yoob.Playfield = function() {
24 this._store = {};
25 this.minX = undefined;
26 this.minY = undefined;
27 this.maxX = undefined;
28 this.maxY = undefined;
29 this._default = undefined;
30
31 /*
32 * Set the default value for this Playfield. This
33 * value is returned by get() for any cell that was
34 * never written to, or had `undefined` put() into it.
35 */
36 this.setDefault = function(v) {
37 this._default = v;
38 };
39
40 /*
41 * Obtain the value at (x, y). The default value will
42 * be returned if the cell was never written to.
43 */
44 this.get = function(x, y) {
45 var v = this._store[x+','+y];
46 if (v === undefined) return this._default;
47 return v;
48 };
49
50 /*
51 * Write a new value into (x, y). Note that writing
52 * `undefined` into a cell has the semantics of deleting
53 * the value at that cell; a subsequent get() for that
54 * location will return this Playfield's default value.
55 */
56 this.put = function(x, y, value) {
57 var key = x+','+y;
58 if (value === undefined || value === this._default) {
59 delete this._store[key];
60 return;
61 }
62 if (this.minX === undefined || x < this.minX) this.minX = x;
63 if (this.maxX === undefined || x > this.maxX) this.maxX = x;
64 if (this.minY === undefined || y < this.minY) this.minY = y;
65 if (this.maxY === undefined || y > this.maxY) this.maxY = y;
66 this._store[key] = value;
67 };
68
69 /*
70 * Like put(), but does not update the playfield bounds. Do
71 * this if you must do a batch of put()s in a more efficient
72 * manner; after doing so, call recalculateBounds().
73 */
74 this.putDirty = function(x, y, value) {
75 var key = x+','+y;
76 if (value === undefined || value === this._default) {
77 delete this._store[key];
78 return;
79 }
80 this._store[key] = value;
81 };
82
83 /*
84 * Recalculate the bounds (min/max X/Y) which are tracked
85 * internally to support methods like foreach(). This is
86 * not needed *unless* you've used putDirty() at some point.
87 * (In which case, call this immediately after your batch
88 * of putDirty()s.)
89 */
90 this.recalculateBounds = function() {
91 this.minX = undefined;
92 this.minY = undefined;
93 this.maxX = undefined;
94 this.maxX = undefined;
95
96 for (var cell in this._store) {
97 var pos = cell.split(',');
98 var x = parseInt(pos[0], 10);
99 var y = parseInt(pos[1], 10);
100 if (this.minX === undefined || x < this.minX) this.minX = x;
101 if (this.maxX === undefined || x > this.maxX) this.maxX = x;
102 if (this.minY === undefined || y < this.minY) this.minY = y;
103 if (this.maxY === undefined || y > this.maxY) this.maxY = y;
104 }
105 };
106
107 /*
108 * Clear the contents of this Playfield.
109 */
110 this.clear = function() {
111 this._store = {};
112 this.minX = undefined;
113 this.minY = undefined;
114 this.maxX = undefined;
115 this.maxX = undefined;
116 };
117
118 /*
119 * Load a string into this Playfield.
120 * The string may be multiline, with newline (ASCII 10)
121 * characters delimiting lines. ASCII 13 is ignored.
122 *
123 * If transformer is given, it should be a one-argument
124 * function which accepts a character and returns the
125 * object you wish to write into the playfield upon reading
126 * that character.
127 */
128 this.load = function(x, y, string, transformer) {
129 var lx = x;
130 var ly = y;
131 if (transformer === undefined) {
132 transformer = function(c) {
133 if (c === ' ') {
134 return undefined;
135 } else {
136 return c;
137 }
138 }
139 }
140 for (var i = 0; i < string.length; i++) {
141 var c = string.charAt(i);
142 if (c === '\n') {
143 lx = x;
144 ly++;
145 } else if (c === '\r') {
146 } else {
147 this.putDirty(lx, ly, transformer(c));
148 lx++;
149 }
150 }
151 this.recalculateBounds();
152 };
153
154 /*
155 * Convert this Playfield to a multi-line string. Each row
156 * is a line, delimited with a newline (ASCII 10).
157 *
158 * If transformer is given, it should be a one-argument
159 * function which accepts a playfield element and returns a
160 * character (or string) you wish to place in the resulting
161 * string for that element.
162 */
163 this.dump = function(transformer) {
164 var text = "";
165 if (transformer === undefined) {
166 transformer = function(c) { return c; }
167 }
168 for (var y = this.minY; y <= this.maxY; y++) {
169 var row = "";
170 for (var x = this.minX; x <= this.maxX; x++) {
171 row += transformer(this.get(x, y));
172 }
173 text += row + "\n";
174 }
175 return text;
176 };
177
178 /*
179 * Iterate over every defined cell in the Playfield.
180 * fun is a callback which takes three parameters:
181 * x, y, and value. If this callback returns a value,
182 * it is written into the Playfield at that position.
183 * This function ensures a particular order.
184 */
185 this.foreach = function(fun) {
186 for (var y = this.minY; y <= this.maxY; y++) {
187 for (var x = this.minX; x <= this.maxX; x++) {
188 var key = x+','+y;
189 var value = this._store[key];
190 if (value === undefined)
191 continue;
192 var result = fun(x, y, value);
193 if (result !== undefined) {
194 if (result === ' ') {
195 result = undefined;
196 }
197 this.put(x, y, result);
198 }
199 }
200 }
201 };
202
203 /*
204 * Analogous to (monoid) map in functional languages,
205 * iterate over this Playfield, transform each value using
206 * a supplied function, and write the transformed value into
207 * a destination Playfield.
208 *
209 * Supplied function should take a Playfield (this Playfield),
210 * x, and y, and return a value.
211 *
212 * The map source may extend beyond the internal bounds of
213 * the Playfield, by giving the min/max Dx/Dy arguments
214 * (which work like margin offsets.)
215 *
216 * Useful for evolving a cellular automaton playfield. In this
217 * case, min/max Dx/Dy should be computed from the neighbourhood.
218 */
219 this.map = function(destPf, fun, minDx, minDy, maxDx, maxDy) {
220 if (minDx === undefined) minDx = 0;
221 if (minDy === undefined) minDy = 0;
222 if (maxDx === undefined) maxDx = 0;
223 if (maxDy === undefined) maxDy = 0;
224 for (var y = this.minY + minDy; y <= this.maxY + maxDy; y++) {
225 for (var x = this.minX + minDx; x <= this.maxX + maxDx; x++) {
226 destPf.putDirty(x, y, fun(pf, x, y));
227 }
228 }
229 destPf.recalculateBounds();
230 };
231
232 this.getExtentX = function() {
233 if (this.maxX === undefined || this.minX === undefined) {
234 return 0;
235 } else {
236 return this.maxX - this.minX + 1;
237 }
238 };
239
240 this.getExtentY = function() {
241 if (this.maxY === undefined || this.minY === undefined) {
242 return 0;
243 } else {
244 return this.maxY - this.minY + 1;
245 }
246 };
247 };
248 """
10249
11250 class Compiler(object):
12251 def __init__(self, alpaca, file):
26265 * EDIT AT YOUR OWN RISK!
27266 */
28267
29 Playfield = function() {
30 this._store = {};
31 this.min_x = undefined;
32 this.min_y = undefined;
33 this.max_x = undefined;
34 this.max_y = undefined;
35 this.default = undefined;
36
37 this.get = function(x, y) {
38 var state = this._store[x+','+y];
39 return state === undefined ? this.default : state;
40 };
41
42 this.put = function(x, y, value) {
43 if (value === this.default) {
44 delete this._store[x+','+y];
45 } else {
46 this._store[x+','+y] = value;
47 }
48 };
49
50 this.recalculate_limits = function() {
51 this.min_x = undefined;
52 this.min_y = undefined;
53 this.max_x = undefined;
54 this.max_y = undefined;
55
56 for (var cell in this._store) {
57 var pos = cell.split(',');
58 var x = parseInt(pos[0], 10);
59 var y = parseInt(pos[1], 10);
60 if (this.min_x === undefined || this.min_x > x)
61 this.min_x = x;
62 if (this.max_x === undefined || this.max_x < x)
63 this.max_x = x;
64 if (this.min_y === undefined || this.min_y > y)
65 this.min_y = y;
66 if (this.max_y === undefined || this.max_y < y)
67 this.max_y = y;
68 }
69 };
70 };
71
268 """)
269 self.file.write(YOOB_PLAYFIELD_JS)
270 self.file.write("""
72271 function in_nbhd_pred(pf, x, y, pred, nbhd) {
73272 var count = 0;
74273 for (var i = 0; i < nbhd.length; i++) {
84283 }
85284
86285 function evolve_playfield(pf, new_pf) {
87 for (var y = pf.min_y - %d; y <= pf.max_y - %d; y++) {
88 for (var x = pf.min_x - %d; x <= pf.max_x - %d; x++) {
89 new_pf.put(x, y, evalState(pf, x, y));
90 }
91 }
92 new_pf.recalculate_limits();
286 pf.map(new_pf, evalState, %d, %d, %d, %d);
93287 }
94 """ % (bb.max_dy, bb.min_dy, bb.max_dx, bb.min_dx))
288 """ % (-1 * bb.max_dx, -1 * bb.max_dy, -1 * bb.min_dx, -1 * bb.min_dy,))
95289 class_map = get_class_map(self.alpaca)
96290 for (class_id, state_set) in class_map.iteritems():
97291 self.file.write("function is_%s(st) {\n" % class_id)
110304 pf = get_defined_playfield(self.alpaca)
111305 if pf is not None:
112306 self.file.write("""
113 pf = new Playfield();
114 pf.default = '%s';
307 pf = new yoob.Playfield();
308 pf.setDefault('%s');
115309 """ % pf.default)
116310 for (x, y, c) in pf.iteritems():
117 self.file.write("pf.put(%d, %d, '%s');\n" % (x, y, c))
118 self.file.write("pf.recalculate_limits();\n")
311 self.file.write("pf.putDirty(%d, %d, '%s');\n" % (x, y, c))
312 self.file.write("pf.recalculateBounds();\n")
313 # TODO: use pf.dump()
119314 self.file.write("""
120315 function dump_playfield(pf) {
121 for (var y = pf.min_y; y <= pf.max_y; y++) {
316 for (var y = pf.minY; y <= pf.maxY; y++) {
122317 var line = '';
123 for (var x = pf.min_x; x <= pf.max_x; x++) {
318 for (var x = pf.minX; x <= pf.maxX; x++) {
124319 s = pf.get(x, y);
125320 """)
126321 for (state_id, char) in pf.state_to_repr.iteritems():
134329 }
135330 """)
136331 self.file.write("""
137 new_pf = new Playfield();
138 new_pf.default = '%s';
332 new_pf = new yoob.Playfield();
333 new_pf.setDefault('%s');
139334 evolve_playfield(pf, new_pf);
140335 console.log('-----');
141336 dump_playfield(new_pf);