Stick almost all of yoob/playfield.js inline in the JS backend.
catseye
12 years ago
7 | 7 | get_defined_playfield |
8 | 8 | ) |
9 | 9 | |
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 | """ | |
10 | 249 | |
11 | 250 | class Compiler(object): |
12 | 251 | def __init__(self, alpaca, file): |
26 | 265 | * EDIT AT YOUR OWN RISK! |
27 | 266 | */ |
28 | 267 | |
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(""" | |
72 | 271 | function in_nbhd_pred(pf, x, y, pred, nbhd) { |
73 | 272 | var count = 0; |
74 | 273 | for (var i = 0; i < nbhd.length; i++) { |
84 | 283 | } |
85 | 284 | |
86 | 285 | 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); | |
93 | 287 | } |
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,)) | |
95 | 289 | class_map = get_class_map(self.alpaca) |
96 | 290 | for (class_id, state_set) in class_map.iteritems(): |
97 | 291 | self.file.write("function is_%s(st) {\n" % class_id) |
110 | 304 | pf = get_defined_playfield(self.alpaca) |
111 | 305 | if pf is not None: |
112 | 306 | self.file.write(""" |
113 | pf = new Playfield(); | |
114 | pf.default = '%s'; | |
307 | pf = new yoob.Playfield(); | |
308 | pf.setDefault('%s'); | |
115 | 309 | """ % pf.default) |
116 | 310 | 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() | |
119 | 314 | self.file.write(""" |
120 | 315 | 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++) { | |
122 | 317 | var line = ''; |
123 | for (var x = pf.min_x; x <= pf.max_x; x++) { | |
318 | for (var x = pf.minX; x <= pf.maxX; x++) { | |
124 | 319 | s = pf.get(x, y); |
125 | 320 | """) |
126 | 321 | for (state_id, char) in pf.state_to_repr.iteritems(): |
134 | 329 | } |
135 | 330 | """) |
136 | 331 | self.file.write(""" |
137 | new_pf = new Playfield(); | |
138 | new_pf.default = '%s'; | |
332 | new_pf = new yoob.Playfield(); | |
333 | new_pf.setDefault('%s'); | |
139 | 334 | evolve_playfield(pf, new_pf); |
140 | 335 | console.log('-----'); |
141 | 336 | dump_playfield(new_pf); |