@kylecheng3146/agent-ops 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,999 @@
1
+ /**
2
+ * Office art: the agreed 24-colour palette and every sprite as a character
3
+ * matrix (one key per pixel, "." transparent). Sprites are drawn once on the
4
+ * server with small shape helpers and sent to the page as data, so the page
5
+ * never ships image files. Light comes from the top left; outlines are
6
+ * "sel-out": the lit side takes the material's dark ramp, the shadow side ink.
7
+ */
8
+ /** Room colours, keyed 0-9 and a-n. Ramps shift hue: shadows cooler, highlights warmer. */
9
+ export const ROOM_PALETTE = {
10
+ 0: "#2b241f", // ink, outlines
11
+ 1: "#5b3a2b", 2: "#8a5033", 3: "#b8743f", 4: "#d9aa72", 5: "#f0cf93", // wood
12
+ 6: "#e2cda6", 7: "#ead8b8", 8: "#f2e5c9", 9: "#fff7e6", // floor, wall, paper
13
+ a: "#2f3f4f", b: "#4d6683", c: "#729ead", d: "#a9d3d8", // slate blue
14
+ e: "#2e4a44", f: "#3f6b4e", g: "#6d9a5a", h: "#a8c46a", // leaves
15
+ i: "#6b5d50", j: "#9c8d7d", // warm metal
16
+ k: "#c86f4a", // alerts only
17
+ l: "#f2c95c", m: "#c9bfa8", n: "#d98a7a" // lamp yellow, carpet, pink
18
+ };
19
+ /** The alert colour key; only alert sprites may use it. */
20
+ export const ALERT_KEY = "k";
21
+ /** Next darker key of each material, used for shading and the lit-side outline. */
22
+ const DARK = {
23
+ 1: "0", 2: "1", 3: "2", 4: "3", 5: "4", 6: "4", 7: "6", 8: "6", 9: "8", a: "0", b: "a", c: "b", d: "c",
24
+ e: "0", f: "e", g: "f", h: "g", i: "0", j: "i", k: "2", l: "3", m: "j", n: "2",
25
+ O: "O", H: "O", I: "H", J: "I", S: "O", T: "S", U: "T", C: "O", D: "C", E: "D", P: "O", Q: "P", B: "O", K: "O", R: "S"
26
+ };
27
+ class Grid {
28
+ w;
29
+ h;
30
+ p;
31
+ constructor(w, h) {
32
+ this.w = w;
33
+ this.h = h;
34
+ this.p = Array.from({ length: h }, () => Array(w).fill(null));
35
+ }
36
+ get(x, y) { return x >= 0 && y >= 0 && x < this.w && y < this.h ? this.p[y][x] : null; }
37
+ set(x, y, c) { x = Math.round(x); y = Math.round(y); if (x >= 0 && y >= 0 && x < this.w && y < this.h)
38
+ this.p[y][x] = c; }
39
+ rect(x, y, w, h, c) { for (let j = y; j < y + h; j++)
40
+ for (let i = x; i < x + w; i++)
41
+ this.set(i, j, c); }
42
+ ell(cx, cy, rx, ry, c) {
43
+ for (let y = Math.floor(cy - ry - 1); y <= cy + ry + 1; y++)
44
+ for (let x = Math.floor(cx - rx - 1); x <= cx + rx + 1; x++) {
45
+ const dx = (x + .5 - cx) / rx, dy = (y + .5 - cy) / ry;
46
+ if (dx * dx + dy * dy <= 1)
47
+ this.set(x, y, c);
48
+ }
49
+ }
50
+ /** A blob shaded from the top left: [dark, mid, light]. */
51
+ ball(cx, cy, rx, ry, [dark, mid, light], hi = .55, lo = .45) {
52
+ for (let y = Math.floor(cy - ry - 1); y <= cy + ry + 1; y++)
53
+ for (let x = Math.floor(cx - rx - 1); x <= cx + rx + 1; x++) {
54
+ const dx = (x + .5 - cx) / rx, dy = (y + .5 - cy) / ry;
55
+ if (dx * dx + dy * dy > 1)
56
+ continue;
57
+ const s = dx * .6 + dy * .8;
58
+ this.set(x, y, s < -hi ? light : s > lo ? dark : mid);
59
+ }
60
+ }
61
+ line(x0, y0, x1, y1, c) {
62
+ const dx = Math.abs(x1 - x0), dy = -Math.abs(y1 - y0), sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1;
63
+ let e = dx + dy;
64
+ for (;;) {
65
+ this.set(x0, y0, c);
66
+ if (x0 === x1 && y0 === y1)
67
+ break;
68
+ const e2 = 2 * e;
69
+ if (e2 >= dy) {
70
+ e += dy;
71
+ x0 += sx;
72
+ }
73
+ if (e2 <= dx) {
74
+ e += dx;
75
+ y0 += sy;
76
+ }
77
+ }
78
+ }
79
+ box(x, y, w, h, c) { this.rect(x, y, w, 1, c); this.rect(x, y + h - 1, w, 1, c); this.rect(x, y, 1, h, c); this.rect(x + w - 1, y, 1, h, c); }
80
+ rows() { return this.p.map(row => row.map(c => c ?? ".").join("")); }
81
+ }
82
+ /** Luminance of the colour a key stands for; avatar keys use one reference identity. */
83
+ const REFERENCE = { ...ROOM_PALETTE, O: "#292633", H: "#51372f", I: "#80533d", J: "#b47c51",
84
+ S: "#c08762", T: "#e6ae87", U: "#f5cda4", C: "#3b6658", D: "#61947b", E: "#92bea1", P: "#404958", Q: "#626e7f", B: "#2e3443", K: "#292633", R: "#e39a86" };
85
+ function luminance(key) {
86
+ const n = parseInt((REFERENCE[key] ?? "#000000").slice(1), 16);
87
+ return (.299 * (n >> 16) + .587 * (n >> 8 & 255) + .114 * (n & 255)) / 255;
88
+ }
89
+ /** Adds a one-pixel sel-out outline around the shape, growing the sprite by 2 in each dimension. */
90
+ function outline(g) {
91
+ const o = new Grid(g.w + 2, g.h + 2);
92
+ for (let y = 0; y < g.h; y++)
93
+ for (let x = 0; x < g.w; x++)
94
+ o.p[y + 1][x + 1] = g.p[y][x];
95
+ const src = o.p.map(row => row.slice());
96
+ const at = (x, y) => src[y]?.[x] ?? null;
97
+ for (let y = 0; y < o.h; y++)
98
+ for (let x = 0; x < o.w; x++) {
99
+ if (at(x, y) !== null)
100
+ continue;
101
+ const right = at(x + 1, y), below = at(x, y + 1), left = at(x - 1, y), above = at(x, y - 1);
102
+ const near = right ?? below ?? left ?? above;
103
+ if (near === null)
104
+ continue;
105
+ const ink = /[A-Z]/u.test(near) ? "O" : "0";
106
+ if (right !== null || below !== null) { // the pixel sits on the lit, top-left side
107
+ const dark = DARK[DARK[near] ?? "0"] ?? "0";
108
+ o.p[y][x] = luminance(dark) > .5 ? ink : dark;
109
+ }
110
+ else
111
+ o.p[y][x] = ink;
112
+ }
113
+ return o.rows();
114
+ }
115
+ const drawn = (w, h, paint) => { const g = new Grid(w, h); paint(g); return outline(g); };
116
+ const flat = (w, h, paint) => { const g = new Grid(w, h); paint(g); return g.rows(); };
117
+ // ---------- people: 32x48 shapes keyed with avatar letters, coloured per identity ----------
118
+ function head(g, back) {
119
+ g.ball(15.5, 12.5, 7.2, 7.2, ["S", "T", "U"]);
120
+ if (back) {
121
+ g.ball(15.5, 12, 8, 7.8, ["H", "I", "J"], .6, .3);
122
+ g.rect(10, 18, 12, 1, "H");
123
+ return;
124
+ }
125
+ for (let y = 4; y <= 10; y++)
126
+ for (let x = 7; x <= 24; x++) {
127
+ const dx = (x + .5 - 15.5) / 8.2, dy = (y + .5 - 11) / 7.4;
128
+ if (dx * dx + dy * dy <= 1)
129
+ g.set(x, y, "I");
130
+ }
131
+ [10, 11, 13, 14, 17, 18, 19, 21].forEach(x => g.set(x, 11, "I"));
132
+ [11, 18].forEach(x => g.set(x, 12, "I"));
133
+ g.rect(8, 10, 2, 7, "I");
134
+ g.rect(22, 10, 2, 6, "H");
135
+ g.rect(21, 7, 3, 4, "H");
136
+ g.rect(11, 5, 4, 1, "J");
137
+ g.rect(10, 6, 2, 1, "J");
138
+ g.set(9, 8, "J");
139
+ g.rect(12, 13, 1, 2, "K");
140
+ g.rect(19, 13, 1, 2, "K");
141
+ g.set(11, 16, "R");
142
+ g.set(20, 16, "R");
143
+ g.rect(15, 17, 2, 1, "S");
144
+ }
145
+ function body(g, { back = false, hand = false, lift = 0, sit = false } = {}) {
146
+ // lift > 0 raises the left foot, lift < 0 the right one: two walking frames.
147
+ const left = Math.max(0, lift), right = Math.max(0, -lift);
148
+ if (sit) {
149
+ // Seated, seen from the front: a lap, short shins, feet on the floor.
150
+ g.rect(10, 33, 12, 4, "P");
151
+ g.rect(11, 34, 10, 1, "Q");
152
+ g.rect(11, 37, 4, 5, "P");
153
+ g.rect(17, 37, 4, 5, "P");
154
+ g.rect(12, 37, 1, 4, "Q");
155
+ g.rect(18, 37, 1, 4, "Q");
156
+ g.rect(10, 42, 5, 3, "B");
157
+ g.rect(17, 42, 5, 3, "B");
158
+ g.rect(11, 42, 3, 1, "Q");
159
+ g.rect(18, 42, 3, 1, "Q");
160
+ }
161
+ else {
162
+ g.rect(11, 34, 4, 9 - left, "P");
163
+ g.rect(17, 34, 4, 9 - right, "P");
164
+ g.rect(11, 33, 10, 3, "P");
165
+ g.rect(12, 35, 1, 6 - left, "Q");
166
+ g.rect(18, 35, 1, 6 - right, "Q");
167
+ g.rect(10, 43 - left, 5, 3, "B");
168
+ g.rect(17, 43 - right, 5, 3, "B");
169
+ g.rect(11, 43 - left, 3, 1, "Q");
170
+ g.rect(18, 43 - right, 3, 1, "Q");
171
+ }
172
+ g.rect(10, 21, 12, 13, "D");
173
+ g.set(10, 21, null);
174
+ g.set(21, 21, null);
175
+ g.rect(11, 22, 1, 9, "E");
176
+ g.rect(20, 22, 2, 11, "C");
177
+ g.rect(10, 32, 12, 2, "C");
178
+ if (back)
179
+ g.rect(15, 23, 1, 8, "C");
180
+ else {
181
+ g.rect(15, 21, 2, 2, "T");
182
+ g.set(14, 21, "E");
183
+ g.set(17, 21, "E");
184
+ }
185
+ g.rect(8, 22, 2, 7, "D");
186
+ g.set(8, 22, "E");
187
+ g.rect(8, 29, 2, 3, "T");
188
+ g.set(8, 31, "S");
189
+ if (hand) {
190
+ g.rect(22, 21, 2, 3, "C");
191
+ g.rect(24, 13, 2, 10, "C");
192
+ g.rect(24, 13, 1, 9, "D");
193
+ g.rect(24, 9, 2, 4, "T");
194
+ g.set(24, 9, "U");
195
+ }
196
+ else {
197
+ g.rect(22, 22, 2, 7, "C");
198
+ g.rect(22, 29, 2, 3, "S");
199
+ }
200
+ g.rect(14, 19, 4, 2, "S");
201
+ }
202
+ function side(stride) {
203
+ return drawn(32, 48, g => {
204
+ if (stride) {
205
+ g.rect(12, 33, 4, 10, "P");
206
+ g.rect(9, 43, 6, 3, "B");
207
+ g.rect(10, 43, 3, 1, "Q");
208
+ g.rect(16, 33, 4, 9, "P");
209
+ g.rect(17, 34, 1, 7, "Q");
210
+ g.rect(18, 41, 3, 2, "P");
211
+ g.rect(18, 43, 7, 3, "B");
212
+ g.rect(19, 43, 4, 1, "Q");
213
+ }
214
+ else {
215
+ g.rect(13, 33, 6, 10, "P");
216
+ g.rect(14, 34, 1, 8, "Q");
217
+ g.rect(12, 43, 9, 3, "B");
218
+ g.rect(13, 43, 5, 1, "Q");
219
+ }
220
+ g.rect(12, 21, 9, 13, "D");
221
+ g.rect(12, 22, 2, 10, "E");
222
+ g.rect(19, 22, 2, 11, "C");
223
+ g.rect(12, 32, 9, 2, "C");
224
+ if (stride) {
225
+ g.rect(15, 22, 1, 8, "C");
226
+ g.rect(16, 22, 3, 7, "D");
227
+ g.rect(16, 22, 1, 6, "E");
228
+ g.rect(17, 29, 3, 2, "D");
229
+ g.rect(18, 31, 3, 2, "T");
230
+ g.set(20, 31, "U");
231
+ }
232
+ else {
233
+ g.rect(15, 22, 1, 9, "C");
234
+ g.rect(16, 22, 3, 9, "D");
235
+ g.rect(16, 22, 1, 8, "E");
236
+ g.rect(16, 31, 3, 2, "T");
237
+ }
238
+ g.rect(15, 19, 4, 2, "S");
239
+ g.ball(16, 12.5, 7, 7.2, ["S", "T", "U"]);
240
+ for (let y = 3; y <= 19; y++)
241
+ for (let x = 7; x <= 24; x++) {
242
+ const dx = (x + .5 - 15.5) / 8, dy = (y + .5 - 11.5) / 7.8;
243
+ if (dx * dx + dy * dy > 1)
244
+ continue;
245
+ if (y < 9 || x < 16 || (y === 9 && x < 22))
246
+ g.set(x, y, y < 6 && x < 16 ? "J" : x < 11 ? "H" : "I");
247
+ }
248
+ [17, 18, 20].forEach(x => g.set(x, 10, "I"));
249
+ g.set(22, 9, "I");
250
+ g.rect(15, 12, 1, 3, "S");
251
+ g.rect(16, 13, 1, 2, "T"); // ear
252
+ g.rect(19, 11, 3, 1, "H");
253
+ g.rect(20, 12, 1, 2, "K"); // brow, eye
254
+ g.set(23, 13, "T");
255
+ g.set(23, 14, "T");
256
+ g.set(23, 15, "S"); // nose
257
+ g.rect(20, 17, 2, 1, "S");
258
+ g.set(19, 16, "R");
259
+ g.rect(18, 19, 3, 1, "S");
260
+ });
261
+ }
262
+ /**
263
+ * Person shapes (32x48 before the outline). Facing right is drawn; the page
264
+ * mirrors it for left. `down`/`up` walk by lifting alternate feet.
265
+ */
266
+ export function personSprites() {
267
+ const front = (options) => drawn(32, 48, g => { body(g, options); head(g, false); });
268
+ const back = (lift) => drawn(32, 48, g => { body(g, { back: true, lift }); head(g, true); });
269
+ return {
270
+ down0: front({}), down1: front({ lift: 2 }), down2: front({ lift: -2 }),
271
+ up0: back(0), up1: back(2), up2: back(-2),
272
+ right0: side(false), right1: side(true), right2: side(false),
273
+ hand: front({ hand: true }), sit: front({ sit: true })
274
+ };
275
+ }
276
+ /** Clothes say the role; hair and skin stay with the person. No entry is the alert red. */
277
+ const CLOTHES = {
278
+ coordinator: ["#37596d", "#5687a1", "#87b3c7"], worker: ["#3b6658", "#61947b", "#92bea1"],
279
+ reviewer: ["#625776", "#8c7da4", "#b7a9ca"], visitor: ["#5a5550", "#857d75", "#b0a89e"], viewer: ["#8a6a2b", "#bb893b", "#e3b64f"],
280
+ qa: ["#8d99a3", "#c9d2d9", "#eef2f5"], integrator: ["#76653f", "#a38f62", "#cdbb8c"]
281
+ };
282
+ const HAIR = [["#302c3b", "#514757", "#756575"], ["#51372f", "#80533d", "#b47c51"], ["#745338", "#a37c4f", "#d1ad72"]];
283
+ const SKIN = [["#bf8264", "#e6ae87", "#f5cda4"], ["#9b624b", "#c08762", "#e0ae82"], ["#654039", "#96624e", "#bf8c68"]];
284
+ /** Avatar letter key -> colour for one person. Self-contained: the page embeds it with toString. */
285
+ export function avatarColors(identity, role) {
286
+ const clothes = {
287
+ coordinator: ["#37596d", "#5687a1", "#87b3c7"], worker: ["#3b6658", "#61947b", "#92bea1"],
288
+ reviewer: ["#625776", "#8c7da4", "#b7a9ca"], visitor: ["#5a5550", "#857d75", "#b0a89e"], viewer: ["#8a6a2b", "#bb893b", "#e3b64f"],
289
+ qa: ["#8d99a3", "#c9d2d9", "#eef2f5"], integrator: ["#76653f", "#a38f62", "#cdbb8c"]
290
+ };
291
+ const hairs = [["#302c3b", "#514757", "#756575"], ["#51372f", "#80533d", "#b47c51"], ["#745338", "#a37c4f", "#d1ad72"]];
292
+ const skins = [["#bf8264", "#e6ae87", "#f5cda4"], ["#9b624b", "#c08762", "#e0ae82"], ["#654039", "#96624e", "#bf8c68"]];
293
+ let hash = 0;
294
+ for (let i = 0; i < identity.length; i++)
295
+ hash = (Math.imul(hash, 31) + identity.charCodeAt(i)) >>> 0;
296
+ const hair = hairs[hash % 3], skin = skins[Math.floor(hash / 3) % 3], shirt = clothes[role] ?? clothes.visitor;
297
+ return { O: "#292633", H: hair[0], I: hair[1], J: hair[2], S: skin[0], T: skin[1], U: skin[2],
298
+ C: shirt[0], D: shirt[1], E: shirt[2], P: "#404958", Q: "#626e7f", B: "#2e3443", K: "#292633", R: "#e39a86" };
299
+ }
300
+ /** Every colour any avatar can wear, for the palette rules. */
301
+ export const AVATAR_COLOURS = [...new Set([...Object.values(CLOTHES), ...HAIR, ...SKIN].flat()
302
+ .concat(["#292633", "#404958", "#626e7f", "#2e3443", "#e39a86"]))];
303
+ // ---------- props ----------
304
+ function monitor(g, x, y, lines) {
305
+ g.rect(x, y, 26, 17, "a");
306
+ g.rect(x + 2, y + 2, 22, 13, "b");
307
+ g.rect(x + 2, y + 2, 22, 1, "c");
308
+ lines.forEach(([dx, w, c], i) => g.rect(x + 3 + dx, y + 4 + i * 2, w, 1, c));
309
+ g.rect(x + 11, y + 17, 4, 5, "i");
310
+ g.rect(x + 11, y + 17, 1, 5, "j");
311
+ g.rect(x + 7, y + 21, 12, 2, "i");
312
+ g.rect(x + 7, y + 21, 12, 1, "j");
313
+ }
314
+ function crate(g, x, y, w, h) {
315
+ g.rect(x, y, w, 3, "5");
316
+ g.rect(x, y + 3, w, h - 3, "4");
317
+ g.rect(x + w - 2, y + 3, 2, h - 3, "3");
318
+ g.rect(x, y + h - 1, w, 1, "3");
319
+ const m = x + (w >> 1) - 1;
320
+ g.rect(m, y, 2, 3, "8");
321
+ g.rect(m, y + 3, 2, 3, "8");
322
+ g.rect(x + 2, y + 6, 4, 3, "9");
323
+ g.rect(x + 2, y + 8, 4, 1, "8");
324
+ g.rect(x, y + 3, w, 1, "3");
325
+ }
326
+ function plant() {
327
+ return drawn(32, 48, g => {
328
+ for (let y = 36; y <= 46; y++) {
329
+ const inset = Math.floor((y - 36) / 4);
330
+ g.rect(9 + inset, y, 14 - 2 * inset, 1, "3");
331
+ g.set(9 + inset, y, "4");
332
+ g.rect(20 - inset, y, 2, 1, "2");
333
+ }
334
+ g.rect(8, 34, 16, 3, "4");
335
+ g.rect(8, 34, 16, 1, "5");
336
+ g.rect(8, 36, 16, 1, "2");
337
+ g.rect(10, 35, 12, 1, "1");
338
+ g.rect(15, 20, 2, 15, "2");
339
+ g.rect(15, 20, 1, 15, "3");
340
+ g.line(16, 27, 20, 23, "2");
341
+ g.line(15, 25, 11, 21, "2");
342
+ const leaf = ["f", "g", "h"], shade = ["e", "f", "g"];
343
+ g.ball(9, 19, 5.5, 4.5, shade);
344
+ g.ball(23, 18, 5.5, 4.5, shade);
345
+ g.ball(16, 9, 8, 6.5, leaf);
346
+ g.ball(10, 14, 6, 5, leaf);
347
+ g.ball(22, 13, 6, 5, leaf);
348
+ g.ball(16, 17, 6, 4, shade);
349
+ [[13, 5], [18, 6], [11, 11], [20, 10], [15, 13], [8, 15], [24, 14]].forEach(([x, y]) => g.set(x, y, "h"));
350
+ [[17, 12], [12, 16], [21, 16], [14, 19], [19, 19]].forEach(([x, y]) => g.set(x, y, "e"));
351
+ });
352
+ }
353
+ function rack(fail) {
354
+ return drawn(26, 48, g => {
355
+ g.rect(0, 0, 26, 47, "a");
356
+ g.rect(0, 0, 26, 1, "b");
357
+ g.rect(0, 0, 1, 47, "b");
358
+ for (let u = 0; u < 6; u++) {
359
+ const y = 3 + u * 7;
360
+ g.rect(2, y, 22, 6, "b");
361
+ g.rect(2, y, 22, 1, "c");
362
+ g.rect(2, y + 5, 22, 1, "a");
363
+ for (let x = 4; x <= 12; x += 2)
364
+ g.set(x, y + 3, "a");
365
+ g.set(18, y + 2, "h");
366
+ g.set(20, y + 2, fail && u === 2 ? "k" : u % 2 ? "h" : "g");
367
+ g.set(22, y + 2, "g");
368
+ }
369
+ g.rect(1, 46, 4, 2, "0");
370
+ g.rect(21, 46, 4, 2, "0");
371
+ });
372
+ }
373
+ function testBench(fail) {
374
+ return drawn(72, 36, g => {
375
+ g.rect(0, 20, 72, 6, "j");
376
+ g.rect(0, 20, 72, 1, "9");
377
+ g.rect(0, 26, 72, 3, "i");
378
+ g.rect(2, 29, 3, 7, "i");
379
+ g.rect(67, 29, 3, 7, "i");
380
+ g.rect(2, 2, 26, 17, "a");
381
+ g.rect(4, 4, 22, 12, "0");
382
+ let py = 13;
383
+ for (let x = 5; x < 25; x++) {
384
+ const ny = 6 + Math.round(3 * Math.sin(x * .7)) + (x > 18 ? 2 : 0);
385
+ g.line(x - 1, py, x, ny, fail && x > 18 ? "k" : "h");
386
+ py = ny;
387
+ }
388
+ g.rect(13, 19, 4, 2, "i");
389
+ g.rect(34, 6, 18, 12, "a");
390
+ g.rect(35, 7, 16, 10, fail ? "b" : "f");
391
+ [8, 11, 14].forEach((y, i) => { g.set(37, y, fail && i === 1 ? "k" : "h"); g.rect(39, y, 9, 1, "d"); });
392
+ g.rect(32, 18, 22, 3, "j");
393
+ g.rect(32, 18, 22, 1, "9");
394
+ g.rect(57, 14, 12, 7, "f");
395
+ g.rect(57, 14, 12, 1, "g");
396
+ [59, 62, 65].forEach(x => g.set(x, 16, "l"));
397
+ g.rect(60, 18, 5, 2, "0");
398
+ g.rect(28, 12, 4, 8, "0");
399
+ g.rect(29, 13, 2, 5, "d");
400
+ g.line(10, 26, 8, 33, "0");
401
+ g.line(60, 26, 63, 32, "0");
402
+ });
403
+ }
404
+ function statusBoard(fail) {
405
+ return drawn(60, 28, g => {
406
+ g.rect(0, 0, 60, 28, "a");
407
+ g.rect(2, 2, 56, 22, "0");
408
+ g.rect(2, 2, 56, 1, "b");
409
+ for (let i = 0; i < 8; i++) {
410
+ const h = 4 + (i * 5) % 11;
411
+ g.rect(5 + i * 6, 20 - h, 4, h, fail && i === 5 ? "k" : i % 3 ? "g" : "h");
412
+ }
413
+ for (let i = 0; i < 6; i++)
414
+ g.rect(5 + i * 9, 21, 6, 1, "f");
415
+ g.rect(26, 25, 8, 3, "i");
416
+ });
417
+ }
418
+ function cubicle(mirror) {
419
+ return drawn(84, 56, g => {
420
+ g.rect(0, 0, 84, 2, "j");
421
+ g.rect(0, 0, 84, 1, "9");
422
+ g.rect(0, 2, 84, 14, "c");
423
+ g.rect(0, 14, 84, 2, "b");
424
+ for (let x = 3; x < 84; x += 6)
425
+ g.rect(x, 4, 1, 9, "b");
426
+ g.rect(0, 0, 4, 42, "j");
427
+ g.rect(0, 0, 1, 42, "9");
428
+ g.rect(0, 42, 4, 13, "c");
429
+ g.rect(0, 53, 4, 2, "b");
430
+ g.rect(80, 0, 4, 42, "j");
431
+ g.rect(83, 0, 1, 42, "i");
432
+ g.rect(80, 42, 4, 13, "c");
433
+ g.rect(80, 53, 4, 2, "b");
434
+ g.rect(8, 4, 5, 5, "l");
435
+ g.rect(14, 6, 5, 5, "n");
436
+ g.rect(66, 4, 9, 7, "9");
437
+ g.rect(67, 5, 7, 4, "d");
438
+ g.ball(70.5, 9, 3, 2, ["f", "g", "h"]);
439
+ g.rect(4, 16, 76, 10, "4");
440
+ g.rect(4, 16, 76, 1, "5");
441
+ g.rect(4, 26, 76, 3, "3");
442
+ const wing = mirror ? 56 : 4;
443
+ g.rect(wing, 16, 24, 26, "4");
444
+ g.rect(wing, 42, 24, 3, "3");
445
+ g.rect(wing + (mirror ? 0 : 23), 16, 1, 26, "5");
446
+ g.rect(mirror ? 57 : 6, 30, 10, 8, "9");
447
+ g.rect(mirror ? 58 : 7, 31, 8, 1, "j");
448
+ g.rect(mirror ? 58 : 7, 33, 6, 1, "j");
449
+ monitor(g, 30, 1, [[0, 8, "d"], [2, 11, "h"], [2, 6, "l"], [0, 10, "d"], [2, 4, "c"]]);
450
+ g.rect(31, 22, 22, 3, "j");
451
+ g.rect(31, 22, 22, 1, "9");
452
+ g.rect(mirror ? 12 : 64, 18, 5, 5, "9");
453
+ g.rect(mirror ? 12 : 64, 20, 5, 1, "n");
454
+ g.rect(mirror ? 70 : 8, 12, 6, 6, "3");
455
+ g.rect(mirror ? 70 : 8, 12, 6, 1, "4");
456
+ g.ball(mirror ? 73 : 11, 9, 4, 3.5, ["f", "g", "h"]);
457
+ });
458
+ }
459
+ function bubble(kind) {
460
+ return drawn(12, 14, g => {
461
+ g.rect(1, 0, 10, 10, "9");
462
+ g.rect(0, 1, 12, 8, "9");
463
+ g.rect(4, 10, 3, 1, "9");
464
+ g.set(5, 11, "9");
465
+ g.rect(1, 8, 10, 1, "8");
466
+ if (kind === "alert") {
467
+ g.rect(5, 2, 2, 4, "k");
468
+ g.rect(5, 7, 2, 1, "k");
469
+ }
470
+ else if (kind === "fail") {
471
+ g.line(3, 2, 8, 7, "k");
472
+ g.line(4, 2, 9, 7, "k");
473
+ g.line(8, 2, 3, 7, "k");
474
+ g.line(9, 2, 4, 7, "k");
475
+ }
476
+ else if (kind === "done") {
477
+ g.line(3, 5, 5, 7, "g");
478
+ g.line(5, 7, 9, 3, "g");
479
+ g.line(3, 4, 5, 6, "g");
480
+ g.line(5, 6, 8, 3, "g");
481
+ }
482
+ else {
483
+ g.set(3, 5, "i");
484
+ g.set(6, 5, "i");
485
+ g.set(9, 5, "i");
486
+ }
487
+ });
488
+ }
489
+ /** Door in a north-south wall seen from above (frames: 0 closed, 1 ajar, 2 open); the leaf swings into the right room. */
490
+ function sideDoor(frame) {
491
+ return drawn(18, 34, g => {
492
+ g.rect(0, 0, 8, 3, "1");
493
+ g.rect(0, 31, 8, 3, "1");
494
+ if (frame === 0) {
495
+ g.rect(1, 3, 6, 28, "3");
496
+ g.rect(1, 3, 1, 28, "5");
497
+ g.rect(6, 3, 1, 28, "2");
498
+ g.rect(3, 6, 2, 8, "4");
499
+ g.rect(3, 18, 2, 9, "4");
500
+ g.rect(5, 15, 2, 3, "l");
501
+ return;
502
+ }
503
+ g.rect(0, 3, 8, 28, "7");
504
+ g.rect(0, 3, 8, 1, "6");
505
+ g.rect(0, 30, 8, 1, "6");
506
+ if (frame === 1) {
507
+ for (let y = 0; y < 28; y++) {
508
+ const x = 8 + Math.floor(y / 5);
509
+ g.rect(x, 3 + y, 4, 1, "3");
510
+ g.set(x, 3 + y, "4");
511
+ }
512
+ g.set(11, 17, "l");
513
+ }
514
+ else {
515
+ g.rect(8, 3, 3, 28, "3");
516
+ g.rect(8, 3, 1, 28, "4");
517
+ g.rect(10, 3, 1, 28, "2");
518
+ g.set(10, 17, "l");
519
+ }
520
+ });
521
+ }
522
+ /** Door in an east-west wall seen from the front; the leaf swings towards the viewer, hinge on the left. */
523
+ function frontDoor(frame) {
524
+ return drawn(30, 36, g => {
525
+ g.rect(0, 0, 30, 36, "1");
526
+ g.rect(2, 2, 26, 34, "0");
527
+ if (frame === 0) {
528
+ g.rect(2, 2, 26, 34, "3");
529
+ g.rect(2, 2, 26, 1, "4");
530
+ g.box(5, 5, 20, 12, "2");
531
+ g.box(5, 20, 20, 13, "2");
532
+ g.rect(8, 7, 14, 7, "d");
533
+ g.rect(8, 7, 14, 1, "9");
534
+ g.rect(23, 20, 2, 3, "l");
535
+ return;
536
+ }
537
+ g.rect(2, 2, 26, 34, "6");
538
+ g.rect(2, 2, 26, 10, "8");
539
+ g.rect(2, 12, 26, 2, "4");
540
+ g.rect(2, 14, 26, 1, "2");
541
+ const w = frame === 1 ? 14 : 5;
542
+ g.rect(2, 2, w, 34, "3");
543
+ g.rect(2, 2, w, 1, "4");
544
+ g.rect(2 + w - 1, 2, 1, 34, "2");
545
+ if (frame === 1) {
546
+ g.rect(4, 6, 8, 6, "d");
547
+ g.rect(10, 20, 2, 3, "l");
548
+ }
549
+ });
550
+ }
551
+ /** Whiteboard without notes; notes are laid on it per room from the criteria. */
552
+ function boardBase() {
553
+ return drawn(64, 48, g => {
554
+ g.rect(9, 30, 2, 14, "i");
555
+ g.rect(53, 30, 2, 14, "i");
556
+ g.rect(9, 30, 1, 14, "j");
557
+ g.rect(53, 30, 1, 14, "j");
558
+ g.rect(4, 43, 12, 2, "i");
559
+ g.rect(48, 43, 12, 2, "i");
560
+ [5, 14, 49, 58].forEach(x => g.rect(x, 45, 2, 2, "a"));
561
+ g.rect(1, 1, 62, 32, "j");
562
+ g.rect(61, 1, 2, 32, "i");
563
+ g.rect(1, 31, 62, 2, "i");
564
+ g.rect(3, 3, 57, 27, "9");
565
+ g.rect(3, 27, 57, 3, "8");
566
+ g.line(5, 26, 12, 4, "8");
567
+ g.rect(6, 33, 52, 2, "j");
568
+ g.rect(6, 34, 52, 1, "i");
569
+ g.rect(14, 32, 5, 1, "b");
570
+ g.rect(21, 32, 5, 1, "g");
571
+ });
572
+ }
573
+ /** One sticky note (7x9 with its pin) per criterion status. */
574
+ function note(colour) {
575
+ return flat(7, 9, g => {
576
+ g.rect(0, 1, 7, 8, colour);
577
+ g.rect(0, 8, 7, 1, DARK[colour]);
578
+ g.rect(6, 2, 1, 7, DARK[colour]);
579
+ g.rect(1, 3, 4, 1, DARK[DARK[colour]]);
580
+ g.rect(1, 5, 3, 1, DARK[DARK[colour]]);
581
+ g.set(3, 0, "b");
582
+ });
583
+ }
584
+ export const NOTE_COLOURS = { PASS: "h", FAIL: "k", PENDING: "l", UNKNOWN: "j" };
585
+ /** Where note i sits on the board sprite (outline included); 12 slots, the 12th becomes "+" when more remain. */
586
+ export function notePosition(index) { return { x: 6 + (index % 6) * 9, y: 5 + Math.floor(index / 6) * 11 }; }
587
+ export const BOARD_SLOTS = 12;
588
+ function wallBlock(capped) {
589
+ // 24 wide so the stripe (8) and panel (24) rhythms tile seamlessly; 40 tall like every wall.
590
+ return flat(24, 40, g => {
591
+ if (capped) {
592
+ g.rect(0, 0, 24, 3, "2");
593
+ g.rect(0, 0, 24, 1, "1");
594
+ }
595
+ g.rect(0, 3, 24, 29, "8");
596
+ for (let x = 4; x < 24; x += 8)
597
+ g.rect(x, 3, 1, 29, "9");
598
+ g.rect(0, 32, 24, 2, "4");
599
+ g.rect(0, 32, 24, 1, "5");
600
+ g.rect(0, 34, 24, 3, "4");
601
+ g.rect(0, 34, 1, 3, "3");
602
+ g.rect(0, 37, 24, 3, "2");
603
+ });
604
+ }
605
+ /** Every room sprite by name. Generated once; the page receives it as JSON. */
606
+ export function roomSprites() {
607
+ const sprites = {
608
+ plant: plant(),
609
+ deskPlant: drawn(16, 16, g => { g.rect(4, 10, 8, 5, "3"); g.rect(4, 10, 8, 1, "4"); g.rect(4, 10, 1, 5, "4"); g.rect(10, 11, 2, 4, "2"); g.ball(8, 6, 5, 4, ["f", "g", "h"]); g.ball(4.5, 8, 3, 2.5, ["e", "f", "g"]); g.ball(11.5, 8, 3, 2.5, ["e", "f", "g"]); }),
610
+ chairBack: drawn(24, 28, g => {
611
+ g.rect(3, 1, 18, 14, "b");
612
+ g.rect(4, 0, 16, 1, "b");
613
+ g.rect(3, 1, 2, 13, "c");
614
+ g.rect(18, 2, 3, 13, "a");
615
+ g.rect(4, 14, 16, 1, "a");
616
+ g.rect(2, 16, 20, 4, "a");
617
+ g.rect(3, 16, 18, 1, "b");
618
+ g.rect(11, 20, 2, 4, "i");
619
+ g.rect(3, 23, 18, 2, "i");
620
+ g.rect(3, 23, 18, 1, "j");
621
+ [3, 11, 19].forEach(x => g.rect(x, 25, 2, 2, "0"));
622
+ }),
623
+ chairFront: drawn(18, 22, g => {
624
+ g.rect(2, 0, 14, 12, "b");
625
+ g.rect(3, 0, 12, 1, "c");
626
+ g.rect(2, 1, 2, 10, "c");
627
+ g.rect(13, 2, 3, 10, "a");
628
+ g.rect(1, 11, 16, 5, "c");
629
+ g.rect(1, 11, 16, 1, "d");
630
+ g.rect(1, 15, 16, 1, "a");
631
+ g.rect(8, 16, 2, 3, "i");
632
+ g.rect(2, 19, 14, 2, "i");
633
+ [2, 8, 14].forEach(x => g.rect(x, 21, 2, 1, "0"));
634
+ }),
635
+ confTable: drawn(112, 30, g => {
636
+ g.rect(2, 0, 108, 18, "3");
637
+ g.rect(0, 2, 112, 14, "3");
638
+ g.rect(2, 1, 108, 14, "4");
639
+ g.rect(4, 2, 104, 2, "5");
640
+ g.rect(0, 16, 112, 4, "2");
641
+ g.rect(2, 18, 108, 2, "1");
642
+ g.rect(14, 20, 4, 10, "1");
643
+ g.rect(94, 20, 4, 10, "1");
644
+ [[12, 5], [44, 6], [78, 5]].forEach(([x, y]) => { g.rect(x, y, 12, 7, "j"); g.rect(x, y, 12, 1, "9"); g.rect(x + 4, y + 3, 4, 2, "9"); });
645
+ [[30, 6], [64, 7], [96, 6]].forEach(([x, y]) => { g.rect(x, y, 7, 8, "9"); g.rect(x + 1, y + 2, 5, 1, "d"); g.rect(x + 1, y + 4, 4, 1, "d"); });
646
+ g.ell(58, 9, 5, 2.5, "a");
647
+ g.set(58, 8, "h");
648
+ [[26, 3], [90, 4]].forEach(([x, y]) => { g.rect(x, y, 2, 6, "d"); g.set(x, y, "9"); });
649
+ }),
650
+ cubicle: cubicle(false), cubicleMirror: cubicle(true),
651
+ rackPass: rack(false), rackFail: rack(true), benchPass: testBench(false), benchFail: testBench(true),
652
+ statusPass: statusBoard(false), statusFail: statusBoard(true),
653
+ filing: drawn(18, 36, g => {
654
+ g.rect(0, 0, 18, 36, "j");
655
+ g.rect(0, 0, 18, 1, "9");
656
+ g.rect(16, 0, 2, 36, "i");
657
+ for (let d = 0; d < 4; d++) {
658
+ const y = 2 + d * 8;
659
+ g.rect(2, y, 13, 7, "8");
660
+ g.rect(2, y + 6, 13, 1, "j");
661
+ g.rect(6, y + 2, 5, 1, "i");
662
+ g.rect(7, y + 4, 3, 1, "9");
663
+ }
664
+ g.rect(1, 34, 2, 2, "0");
665
+ g.rect(15, 34, 2, 2, "0");
666
+ }),
667
+ stool: drawn(12, 18, g => { g.ell(6, 3, 5.5, 2.8, "3"); g.ell(6, 2.5, 5, 2.2, "4"); g.rect(5, 5, 2, 10, "i"); g.rect(2, 15, 8, 2, "i"); }),
668
+ trash: drawn(10, 12, g => { g.rect(0, 0, 10, 2, "j"); g.rect(1, 2, 8, 10, "i"); g.rect(1, 2, 2, 10, "j"); g.rect(4, 0, 3, 1, "9"); }),
669
+ execDesk: drawn(64, 32, g => {
670
+ g.rect(0, 8, 64, 6, "3");
671
+ g.rect(0, 8, 64, 1, "4");
672
+ g.rect(0, 14, 64, 18, "2");
673
+ g.rect(0, 14, 64, 1, "1");
674
+ g.box(4, 17, 26, 12, "1");
675
+ g.box(34, 17, 26, 12, "1");
676
+ g.rect(15, 22, 4, 1, "l");
677
+ g.rect(45, 22, 4, 1, "l");
678
+ g.rect(4, 6, 8, 2, "l");
679
+ g.rect(3, 1, 10, 4, "f");
680
+ g.rect(3, 1, 10, 1, "g");
681
+ g.rect(7, 5, 2, 2, "i");
682
+ g.rect(24, 0, 18, 9, "j");
683
+ g.rect(24, 0, 18, 1, "9");
684
+ g.set(33, 4, "9");
685
+ g.rect(46, 5, 10, 4, "9");
686
+ g.rect(47, 4, 10, 4, "9");
687
+ g.rect(48, 5, 6, 1, "j");
688
+ g.rect(16, 6, 6, 2, "0");
689
+ g.rect(17, 6, 4, 1, "l");
690
+ }),
691
+ armchair: drawn(26, 26, g => {
692
+ g.rect(3, 0, 20, 12, "2");
693
+ g.rect(4, 0, 18, 2, "3");
694
+ g.rect(12, 2, 1, 9, "1");
695
+ g.rect(0, 8, 5, 15, "2");
696
+ g.rect(0, 8, 5, 2, "3");
697
+ g.rect(21, 8, 5, 15, "1");
698
+ g.rect(21, 8, 5, 2, "2");
699
+ g.rect(5, 12, 16, 7, "3");
700
+ g.rect(5, 12, 16, 2, "4");
701
+ g.rect(5, 19, 16, 4, "2");
702
+ g.rect(2, 23, 2, 3, "0");
703
+ g.rect(22, 23, 2, 3, "0");
704
+ }),
705
+ coffeeTable: drawn(38, 18, g => {
706
+ g.rect(0, 0, 38, 8, "4");
707
+ g.rect(0, 0, 38, 1, "5");
708
+ g.rect(0, 8, 38, 3, "3");
709
+ g.rect(2, 11, 3, 7, "2");
710
+ g.rect(33, 11, 3, 7, "2");
711
+ g.rect(5, 2, 9, 5, "9");
712
+ g.rect(6, 1, 9, 5, "9");
713
+ g.rect(7, 3, 6, 1, "j");
714
+ g.rect(22, 2, 5, 5, "9");
715
+ g.rect(22, 4, 5, 1, "n");
716
+ g.rect(30, 2, 4, 4, "l");
717
+ }),
718
+ lowBookcase: drawn(48, 26, g => {
719
+ g.rect(0, 0, 48, 26, "2");
720
+ g.rect(0, 0, 48, 2, "3");
721
+ g.rect(2, 4, 44, 9, "1");
722
+ g.rect(2, 15, 44, 9, "1");
723
+ g.rect(2, 13, 44, 2, "3");
724
+ const spines = ["b", "g", "n", "l", "c", "3", "h", "f", "d"];
725
+ [4, 15].forEach((y, s) => { let x = 3; for (let b = 0; x < 42; b++) {
726
+ const w = 2 + (b + s) % 2, h = 6 + (b * 3 + s) % 4, c = spines[(b * 2 + s) % spines.length];
727
+ if ((b + s) % 6 === 5) {
728
+ x += 3;
729
+ continue;
730
+ }
731
+ g.rect(x, y + 9 - h, w, h, c);
732
+ g.rect(x + w - 1, y + 9 - h, 1, h, DARK[c]);
733
+ x += w;
734
+ } });
735
+ }),
736
+ metalShelf: drawn(50, 46, g => {
737
+ g.rect(0, 0, 2, 46, "i");
738
+ g.rect(48, 0, 2, 46, "i");
739
+ for (let s = 0; s < 4; s++) {
740
+ const y = 2 + s * 14;
741
+ g.rect(0, y, 50, 2, "j");
742
+ g.rect(0, y + 1, 50, 1, "i");
743
+ }
744
+ [[3, 4, 12, 12], [16, 7, 14, 9], [32, 5, 13, 11], [4, 22, 18, 8], [24, 19, 10, 11], [36, 21, 10, 9], [3, 33, 13, 11], [18, 36, 26, 8]].forEach(([x, y, w, h]) => crate(g, x, y, w, h));
745
+ }),
746
+ copier: drawn(34, 32, g => {
747
+ g.rect(0, 4, 34, 28, "9");
748
+ g.rect(31, 4, 3, 28, "8");
749
+ g.rect(0, 4, 34, 1, "8");
750
+ g.rect(2, 0, 26, 5, "j");
751
+ g.rect(2, 0, 26, 1, "9");
752
+ g.rect(4, 2, 22, 2, "a");
753
+ g.rect(22, 7, 9, 5, "a");
754
+ g.rect(23, 8, 5, 3, "d");
755
+ g.set(30, 9, "h");
756
+ g.rect(2, 13, 28, 1, "j");
757
+ g.rect(2, 20, 28, 1, "j");
758
+ g.rect(10, 16, 10, 1, "i");
759
+ g.rect(10, 23, 10, 1, "i");
760
+ g.rect(0, 7, 3, 4, "9");
761
+ g.rect(1, 29, 3, 3, "0");
762
+ g.rect(28, 29, 3, 3, "0");
763
+ }),
764
+ sortTable: drawn(56, 26, g => {
765
+ g.rect(0, 8, 56, 6, "4");
766
+ g.rect(0, 8, 56, 1, "5");
767
+ g.rect(0, 14, 56, 3, "3");
768
+ g.rect(2, 17, 3, 9, "2");
769
+ g.rect(51, 17, 3, 9, "2");
770
+ [[2, 0], [16, 1], [30, 0]].forEach(([x, y]) => { g.rect(x, y + 3, 12, 6, "b"); g.rect(x, y + 3, 12, 1, "c"); g.rect(x + 2, y + 1, 8, 3, "9"); g.rect(x + 3, y, 6, 2, "8"); });
771
+ g.rect(44, 2, 9, 7, "4");
772
+ g.rect(44, 2, 9, 2, "5");
773
+ g.rect(48, 2, 1, 7, "8");
774
+ g.rect(16, 10, 8, 4, "9");
775
+ g.rect(18, 11, 4, 1, "j");
776
+ g.rect(28, 10, 7, 3, "n");
777
+ }),
778
+ handTruck: drawn(20, 42, g => {
779
+ g.rect(14, 0, 2, 38, "i");
780
+ g.rect(14, 0, 1, 38, "j");
781
+ g.rect(12, 0, 5, 2, "i");
782
+ g.rect(0, 37, 16, 2, "i");
783
+ crate(g, 0, 22, 13, 15);
784
+ crate(g, 1, 10, 12, 12);
785
+ g.ell(14, 38, 3.5, 3.5, "a");
786
+ g.set(13, 37, "j");
787
+ }),
788
+ crates: drawn(64, 48, g => {
789
+ crate(g, 1, 33, 15, 13);
790
+ crate(g, 16, 33, 15, 13);
791
+ crate(g, 31, 33, 15, 13);
792
+ crate(g, 8, 20, 15, 13);
793
+ crate(g, 23, 20, 15, 13);
794
+ crate(g, 16, 7, 15, 13);
795
+ g.rect(60, 8, 2, 37, "i");
796
+ g.rect(60, 8, 1, 37, "j");
797
+ g.rect(57, 6, 5, 2, "i");
798
+ g.rect(49, 44, 12, 2, "i");
799
+ crate(g, 48, 31, 12, 13);
800
+ g.ell(58, 45, 3, 3, "a");
801
+ }),
802
+ counter: drawn(80, 34, g => {
803
+ g.rect(0, 10, 80, 5, "9");
804
+ g.rect(0, 10, 80, 1, "8");
805
+ g.rect(0, 15, 80, 1, "j");
806
+ g.rect(0, 16, 80, 18, "4");
807
+ g.rect(78, 16, 2, 18, "3");
808
+ for (let d = 0; d < 5; d++) {
809
+ g.box(2 + d * 16, 18, 14, 14, "3");
810
+ g.rect(d % 2 ? 4 + d * 16 : 12 + d * 16, 23, 1, 4, "5");
811
+ }
812
+ g.rect(4, 0, 14, 12, "a");
813
+ g.rect(4, 0, 14, 1, "b");
814
+ g.rect(6, 3, 6, 3, "0");
815
+ g.set(14, 4, "h");
816
+ g.rect(8, 8, 5, 3, "9");
817
+ g.rect(9, 9, 3, 1, "2");
818
+ g.rect(26, 2, 22, 10, "j");
819
+ g.rect(26, 2, 22, 1, "9");
820
+ g.rect(28, 4, 13, 6, "a");
821
+ g.rect(29, 5, 5, 1, "b");
822
+ g.rect(43, 4, 3, 6, "i");
823
+ g.set(44, 5, "h");
824
+ g.rect(54, 10, 16, 3, "j");
825
+ g.rect(55, 11, 14, 1, "i");
826
+ g.rect(61, 5, 2, 6, "i");
827
+ g.rect(61, 5, 5, 1, "i");
828
+ g.rect(72, 6, 4, 5, "n");
829
+ g.rect(76, 7, 1, 2, "n");
830
+ g.rect(20, 7, 4, 4, "9");
831
+ g.rect(20, 8, 4, 1, "c");
832
+ }),
833
+ fridge: drawn(22, 44, g => {
834
+ g.rect(0, 0, 22, 44, "9");
835
+ g.rect(19, 0, 3, 44, "8");
836
+ g.rect(0, 14, 22, 1, "j");
837
+ g.rect(16, 4, 1, 8, "i");
838
+ g.rect(16, 18, 1, 12, "i");
839
+ g.rect(4, 20, 4, 4, "l");
840
+ g.rect(9, 24, 3, 3, "n");
841
+ g.rect(4, 27, 5, 6, "9");
842
+ g.box(4, 27, 5, 6, "j");
843
+ g.rect(1, 42, 3, 2, "0");
844
+ g.rect(18, 42, 3, 2, "0");
845
+ }),
846
+ vending: drawn(26, 46, g => {
847
+ g.rect(0, 0, 26, 46, "b");
848
+ g.rect(0, 0, 26, 2, "c");
849
+ g.rect(24, 0, 2, 46, "a");
850
+ g.rect(2, 4, 16, 30, "d");
851
+ const goods = ["n", "l", "h", "9", "3", "c"];
852
+ for (let r = 0; r < 5; r++) {
853
+ g.rect(2, 9 + r * 6, 16, 1, "j");
854
+ for (let c = 0; c < 4; c++)
855
+ g.rect(3 + c * 4, 5 + r * 6, 3, 4, goods[(r + c * 2) % goods.length]);
856
+ }
857
+ g.line(3, 31, 10, 5, "9");
858
+ g.rect(19, 6, 4, 10, "a");
859
+ for (let i = 0; i < 4; i++)
860
+ g.rect(20, 7 + i * 2, 2, 1, "j");
861
+ g.rect(20, 18, 2, 3, "0");
862
+ g.rect(3, 37, 14, 5, "a");
863
+ g.rect(4, 38, 12, 3, "0");
864
+ }),
865
+ cooler: drawn(14, 36, g => {
866
+ g.ball(7, 6, 6, 6, ["c", "d", "9"]);
867
+ g.rect(5, 11, 4, 2, "c");
868
+ g.rect(1, 13, 12, 22, "9");
869
+ g.rect(11, 13, 2, 22, "8");
870
+ g.rect(1, 13, 12, 1, "8");
871
+ g.rect(3, 17, 2, 2, "c");
872
+ g.rect(9, 17, 2, 2, "n");
873
+ g.rect(2, 21, 10, 1, "j");
874
+ g.rect(3, 28, 8, 1, "8");
875
+ }),
876
+ cafeTable: drawn(24, 22, g => {
877
+ g.ell(12, 6, 11.5, 5.5, "9");
878
+ g.ell(12, 7, 11.5, 5, "8");
879
+ g.ell(12, 5.5, 10.5, 4, "9");
880
+ g.rect(11, 11, 3, 8, "i");
881
+ g.rect(6, 19, 13, 2, "i");
882
+ g.rect(7, 3, 4, 3, "n");
883
+ g.rect(15, 4, 3, 3, "l");
884
+ }),
885
+ sofa: drawn(48, 28, g => {
886
+ g.rect(4, 2, 40, 12, "f");
887
+ g.rect(4, 2, 40, 2, "g");
888
+ g.rect(5, 1, 38, 1, "g");
889
+ g.rect(24, 3, 1, 11, "e");
890
+ g.rect(0, 8, 6, 16, "f");
891
+ g.rect(0, 8, 6, 2, "g");
892
+ g.rect(42, 8, 6, 16, "e");
893
+ g.rect(42, 8, 6, 2, "f");
894
+ g.rect(6, 13, 36, 7, "g");
895
+ g.rect(6, 13, 36, 2, "h");
896
+ g.rect(24, 13, 1, 7, "f");
897
+ g.rect(6, 20, 36, 4, "f");
898
+ g.rect(2, 24, 3, 3, "1");
899
+ g.rect(43, 24, 3, 3, "1");
900
+ }),
901
+ // wall decor
902
+ tv: drawn(52, 26, g => {
903
+ g.rect(0, 0, 52, 26, "a");
904
+ g.rect(2, 2, 48, 20, "9");
905
+ g.rect(2, 2, 48, 5, "b");
906
+ g.rect(4, 3, 18, 2, "9");
907
+ [[6, 8], [13, 11], [20, 6], [27, 13]].forEach(([x, h]) => g.rect(x, 20 - h, 5, h, x === 27 ? "g" : "c"));
908
+ g.rect(36, 10, 11, 1, "j");
909
+ g.rect(36, 13, 9, 1, "j");
910
+ g.rect(36, 16, 11, 1, "j");
911
+ g.rect(22, 23, 8, 3, "i");
912
+ }),
913
+ blinds: drawn(48, 24, g => {
914
+ g.rect(0, 0, 48, 22, "9");
915
+ g.rect(46, 0, 2, 22, "8");
916
+ g.rect(3, 3, 42, 16, "d");
917
+ g.rect(3, 12, 42, 7, "c");
918
+ g.ball(14, 19, 9, 4, ["f", "g", "h"]);
919
+ g.ball(38, 20, 7, 3, ["e", "f", "g"]);
920
+ for (let y = 3; y < 11; y += 2) {
921
+ g.rect(3, y, 42, 1, "8");
922
+ g.rect(3, y + 1, 42, 1, "6");
923
+ }
924
+ g.rect(3, 11, 42, 1, "j");
925
+ g.rect(40, 12, 1, 5, "i");
926
+ g.rect(0, 20, 48, 3, "9");
927
+ g.rect(0, 23, 48, 1, "6");
928
+ }),
929
+ poster: drawn(22, 28, g => {
930
+ g.rect(0, 0, 22, 28, "a");
931
+ g.rect(2, 2, 18, 18, "d");
932
+ g.rect(2, 12, 18, 8, "c");
933
+ g.line(2, 17, 9, 7, "f");
934
+ g.line(9, 7, 14, 13, "f");
935
+ g.line(12, 11, 16, 8, "f");
936
+ g.line(16, 8, 19, 12, "f");
937
+ for (let y = 8; y < 20; y++)
938
+ for (let x = 2; x < 20; x++)
939
+ if (g.get(x, y - 1) === "f" || g.get(x, y - 1) === "g")
940
+ g.set(x, y, "g");
941
+ g.set(9, 7, "9");
942
+ g.rect(8, 8, 3, 1, "9");
943
+ g.set(16, 8, "9");
944
+ g.rect(15, 4, 3, 3, "l");
945
+ g.rect(4, 22, 14, 2, "9");
946
+ g.rect(6, 25, 10, 1, "j");
947
+ }),
948
+ corkboard: drawn(36, 24, g => {
949
+ g.rect(0, 0, 36, 24, "3");
950
+ g.rect(2, 2, 32, 20, "4");
951
+ for (let i = 0; i < 30; i++)
952
+ g.set(3 + (i * 7) % 30, 3 + (i * 5) % 18, "5");
953
+ [[4, 4, "9"], [14, 5, "l"], [24, 3, "n"], [6, 13, "d"], [17, 13, "9"], [26, 12, "l"]].forEach(([x, y, c]) => { g.rect(+x, +y, 7, 7, String(c)); g.rect(+x + 1, +y + 3, 4, 1, "j"); g.set(+x + 3, +y, "b"); });
954
+ }),
955
+ frame: drawn(14, 11, g => { g.rect(0, 0, 14, 11, "2"); g.rect(2, 2, 10, 7, "d"); g.ball(7, 8, 5, 3, ["f", "g", "h"]); g.rect(9, 3, 2, 2, "l"); }),
956
+ diploma: drawn(16, 12, g => { g.rect(0, 0, 16, 12, "2"); g.rect(2, 2, 12, 8, "9"); g.rect(4, 4, 8, 1, "j"); g.rect(5, 6, 6, 1, "j"); g.ell(11, 8, 1.5, 1.5, "l"); }),
957
+ clock: drawn(16, 16, g => { g.ell(8, 8, 7.5, 7.5, "2"); g.ell(8, 8, 6.5, 6.5, "3"); g.ell(8, 8, 5.5, 5.5, "9"); g.set(7, 3, "0"); g.set(12, 7, "0"); g.set(7, 12, "0"); g.set(3, 7, "0"); g.rect(7, 4, 1, 4, "0"); g.rect(8, 7, 3, 1, "0"); }),
958
+ cubby: drawn(40, 18, g => {
959
+ g.rect(0, 0, 40, 18, "3");
960
+ g.rect(0, 0, 40, 1, "4");
961
+ for (let r = 0; r < 2; r++)
962
+ for (let c = 0; c < 5; c++) {
963
+ const x = 2 + Math.floor(c * 7.6), y = 2 + r * 8;
964
+ g.rect(x, y, 6, 6, "1");
965
+ if ((r * 5 + c) % 3 !== 1) {
966
+ g.rect(x + 1, y + 2, 4, 4, (r + c) % 4 ? "9" : "n");
967
+ g.rect(x + 1, y + 2, 4, 1, "8");
968
+ }
969
+ }
970
+ }),
971
+ // whiteboard driven by criteria
972
+ board: boardBase(), notePass: note("h"), noteFail: note("k"), notePending: note("l"), noteUnknown: note("j"),
973
+ notePlus: flat(7, 9, g => { g.rect(0, 1, 7, 8, "a"); g.rect(3, 3, 1, 4, "9"); g.rect(1, 5, 5, 1, "9"); g.set(3, 0, "b"); }),
974
+ // head bubbles and doors
975
+ bubbleAlert: bubble("alert"), bubbleDone: bubble("done"), bubbleBusy: bubble("busy"), bubbleFail: bubble("fail"),
976
+ sideDoor0: sideDoor(0), sideDoor1: sideDoor(1), sideDoor2: sideDoor(2), frontDoor0: frontDoor(0), frontDoor1: frontDoor(1), frontDoor2: frontDoor(2),
977
+ // tiles: walls (24x40, repeat in x), floors (repeat in both)
978
+ wall: wallBlock(true),
979
+ wood: flat(96, 16, g => { g.rect(0, 0, 96, 16, "7"); g.rect(0, 7, 96, 1, "6"); g.rect(0, 15, 96, 1, "6"); g.rect(30, 0, 1, 7, "6"); g.rect(78, 8, 1, 7, "6"); g.rect(8, 3, 14, 1, "8"); g.rect(50, 4, 9, 1, "8"); g.rect(40, 11, 18, 1, "8"); g.rect(84, 12, 6, 1, "8"); }),
980
+ carpet: flat(32, 32, g => {
981
+ g.rect(0, 0, 32, 32, "m");
982
+ for (let y = 0; y < 32; y++)
983
+ for (let x = 0; x < 32; x++) {
984
+ const r = (Math.imul(x, 73856093) ^ Math.imul(y, 19349663)) >>> 0;
985
+ if (r % 37 === 0)
986
+ g.set(x, y, "6");
987
+ else if (r % 71 === 0)
988
+ g.set(x, y, "j");
989
+ }
990
+ g.rect(0, 0, 32, 1, "6");
991
+ g.rect(0, 0, 1, 32, "6");
992
+ }),
993
+ vinyl: flat(16, 16, g => { g.rect(0, 0, 16, 16, "8"); g.rect(0, 0, 16, 1, "6"); g.rect(0, 0, 1, 16, "6"); }),
994
+ checker: flat(24, 24, g => { g.rect(0, 0, 24, 24, "9"); g.rect(12, 0, 12, 12, "7"); g.rect(0, 12, 12, 12, "7"); }),
995
+ rugBlue: flat(16, 16, g => { g.rect(0, 0, 16, 16, "b"); g.rect(0, 0, 16, 1, "c"); }),
996
+ rugGreen: flat(16, 16, g => { g.rect(0, 0, 16, 16, "f"); g.rect(0, 0, 16, 1, "g"); })
997
+ };
998
+ return sprites;
999
+ }