@kylecheng3146/agent-ops 0.7.3 → 0.8.3

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.
Files changed (65) hide show
  1. package/README.md +2 -0
  2. package/dist/packages/cli/src/args.js +72 -1
  3. package/dist/packages/cli/src/bin.js +69 -10
  4. package/dist/packages/cli/src/cli.js +11 -3
  5. package/dist/packages/cli/src/codex-loop-process.js +6 -0
  6. package/dist/packages/cli/src/commands/batch.js +3 -1
  7. package/dist/packages/cli/src/commands/hook.js +4 -1
  8. package/dist/packages/cli/src/commands/review.js +9 -3
  9. package/dist/packages/cli/src/commands/task.js +44 -2
  10. package/dist/packages/cli/src/commands/update.js +18 -0
  11. package/dist/packages/cli/src/commands/verify.js +3 -1
  12. package/dist/packages/cli/src/debug-intake.js +245 -0
  13. package/dist/packages/cli/src/hook-process.js +155 -34
  14. package/dist/packages/cli/src/office-entry.js +34 -6
  15. package/dist/packages/cli/src/run-deps.js +8 -0
  16. package/dist/runtime/src/adapters/agy/config.js +3 -2
  17. package/dist/runtime/src/adapters/claude/config.js +7 -3
  18. package/dist/runtime/src/adapters/codex/config.js +10 -3
  19. package/dist/runtime/src/adapters/codex/events.js +2 -1
  20. package/dist/runtime/src/adapters/codex/input.js +20 -2
  21. package/dist/runtime/src/adapters/codex/output.js +4 -1
  22. package/dist/runtime/src/config/merge.js +1 -1
  23. package/dist/runtime/src/hooks/completion-gate.js +15 -0
  24. package/dist/runtime/src/install/harness.js +2 -0
  25. package/dist/runtime/src/install/plan.js +3 -3
  26. package/dist/runtime/src/install/profiles.js +2 -0
  27. package/dist/runtime/src/install/update.js +10 -2
  28. package/dist/runtime/src/office/activity.js +105 -0
  29. package/dist/runtime/src/office/art.js +1000 -0
  30. package/dist/runtime/src/office/collect.js +137 -10
  31. package/dist/runtime/src/office/layout.js +203 -0
  32. package/dist/runtime/src/office/page.js +883 -644
  33. package/dist/runtime/src/office/scene.js +38 -33
  34. package/dist/runtime/src/office/sessions.js +18 -1
  35. package/dist/runtime/src/office/snapshot.js +74 -1
  36. package/dist/runtime/src/parallel/finish.js +4 -2
  37. package/dist/runtime/src/parallel/manage.js +19 -2
  38. package/dist/runtime/src/review/attestation.js +4 -1
  39. package/dist/runtime/src/review/batch.js +10 -0
  40. package/dist/runtime/src/review/execute.js +10 -1
  41. package/dist/runtime/src/review/slots.js +59 -44
  42. package/dist/runtime/src/run/hosts/codex.js +1 -1
  43. package/dist/runtime/src/run/transport.js +3 -0
  44. package/dist/runtime/src/schema/validate.js +8 -2
  45. package/dist/runtime/src/security/permissions.js +3 -1
  46. package/dist/runtime/src/task/completion.js +20 -1
  47. package/dist/runtime/src/task/contract.js +1 -1
  48. package/dist/runtime/src/task/debug-intake.js +12 -0
  49. package/dist/runtime/src/task/debug-state.js +130 -0
  50. package/dist/runtime/src/task/debug-user-event.js +93 -0
  51. package/dist/runtime/src/task/debug.js +586 -0
  52. package/dist/runtime/src/task/service.js +46 -0
  53. package/dist/runtime/src/task/store.js +8 -1
  54. package/dist/runtime/src/verify/acceptance-replay.js +9 -4
  55. package/dist/runtime/src/verify/service.js +21 -2
  56. package/docs/en/guides/configuration.md +7 -0
  57. package/docs/en/guides/task-debug.md +111 -0
  58. package/docs/en/spec/harness-adapters.md +31 -4
  59. package/docs/zh-TW/guides/configuration.md +6 -0
  60. package/docs/zh-TW/guides/task-debug.md +66 -0
  61. package/docs/zh-TW/spec/harness-adapters.md +23 -4
  62. package/package.json +1 -1
  63. package/schemas/config.schema.json +30 -2
  64. package/schemas/manifest.schema.json +1 -0
  65. package/schemas/task.schema.json +62 -2
@@ -0,0 +1,1000 @@
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
+ // The viewer always wears the lightest skin; everyone else follows their identity.
297
+ const hair = hairs[hash % 3], skin = skins[role === "viewer" ? 0 : Math.floor(hash / 3) % 3], shirt = clothes[role] ?? clothes.visitor;
298
+ return { O: "#292633", H: hair[0], I: hair[1], J: hair[2], S: skin[0], T: skin[1], U: skin[2],
299
+ C: shirt[0], D: shirt[1], E: shirt[2], P: "#404958", Q: "#626e7f", B: "#2e3443", K: "#292633", R: "#e39a86" };
300
+ }
301
+ /** Every colour any avatar can wear, for the palette rules. */
302
+ export const AVATAR_COLOURS = [...new Set([...Object.values(CLOTHES), ...HAIR, ...SKIN].flat()
303
+ .concat(["#292633", "#404958", "#626e7f", "#2e3443", "#e39a86"]))];
304
+ // ---------- props ----------
305
+ function monitor(g, x, y, lines) {
306
+ g.rect(x, y, 26, 17, "a");
307
+ g.rect(x + 2, y + 2, 22, 13, "b");
308
+ g.rect(x + 2, y + 2, 22, 1, "c");
309
+ lines.forEach(([dx, w, c], i) => g.rect(x + 3 + dx, y + 4 + i * 2, w, 1, c));
310
+ g.rect(x + 11, y + 17, 4, 5, "i");
311
+ g.rect(x + 11, y + 17, 1, 5, "j");
312
+ g.rect(x + 7, y + 21, 12, 2, "i");
313
+ g.rect(x + 7, y + 21, 12, 1, "j");
314
+ }
315
+ function crate(g, x, y, w, h) {
316
+ g.rect(x, y, w, 3, "5");
317
+ g.rect(x, y + 3, w, h - 3, "4");
318
+ g.rect(x + w - 2, y + 3, 2, h - 3, "3");
319
+ g.rect(x, y + h - 1, w, 1, "3");
320
+ const m = x + (w >> 1) - 1;
321
+ g.rect(m, y, 2, 3, "8");
322
+ g.rect(m, y + 3, 2, 3, "8");
323
+ g.rect(x + 2, y + 6, 4, 3, "9");
324
+ g.rect(x + 2, y + 8, 4, 1, "8");
325
+ g.rect(x, y + 3, w, 1, "3");
326
+ }
327
+ function plant() {
328
+ return drawn(32, 48, g => {
329
+ for (let y = 36; y <= 46; y++) {
330
+ const inset = Math.floor((y - 36) / 4);
331
+ g.rect(9 + inset, y, 14 - 2 * inset, 1, "3");
332
+ g.set(9 + inset, y, "4");
333
+ g.rect(20 - inset, y, 2, 1, "2");
334
+ }
335
+ g.rect(8, 34, 16, 3, "4");
336
+ g.rect(8, 34, 16, 1, "5");
337
+ g.rect(8, 36, 16, 1, "2");
338
+ g.rect(10, 35, 12, 1, "1");
339
+ g.rect(15, 20, 2, 15, "2");
340
+ g.rect(15, 20, 1, 15, "3");
341
+ g.line(16, 27, 20, 23, "2");
342
+ g.line(15, 25, 11, 21, "2");
343
+ const leaf = ["f", "g", "h"], shade = ["e", "f", "g"];
344
+ g.ball(9, 19, 5.5, 4.5, shade);
345
+ g.ball(23, 18, 5.5, 4.5, shade);
346
+ g.ball(16, 9, 8, 6.5, leaf);
347
+ g.ball(10, 14, 6, 5, leaf);
348
+ g.ball(22, 13, 6, 5, leaf);
349
+ g.ball(16, 17, 6, 4, shade);
350
+ [[13, 5], [18, 6], [11, 11], [20, 10], [15, 13], [8, 15], [24, 14]].forEach(([x, y]) => g.set(x, y, "h"));
351
+ [[17, 12], [12, 16], [21, 16], [14, 19], [19, 19]].forEach(([x, y]) => g.set(x, y, "e"));
352
+ });
353
+ }
354
+ function rack(fail) {
355
+ return drawn(26, 48, g => {
356
+ g.rect(0, 0, 26, 47, "a");
357
+ g.rect(0, 0, 26, 1, "b");
358
+ g.rect(0, 0, 1, 47, "b");
359
+ for (let u = 0; u < 6; u++) {
360
+ const y = 3 + u * 7;
361
+ g.rect(2, y, 22, 6, "b");
362
+ g.rect(2, y, 22, 1, "c");
363
+ g.rect(2, y + 5, 22, 1, "a");
364
+ for (let x = 4; x <= 12; x += 2)
365
+ g.set(x, y + 3, "a");
366
+ g.set(18, y + 2, "h");
367
+ g.set(20, y + 2, fail && u === 2 ? "k" : u % 2 ? "h" : "g");
368
+ g.set(22, y + 2, "g");
369
+ }
370
+ g.rect(1, 46, 4, 2, "0");
371
+ g.rect(21, 46, 4, 2, "0");
372
+ });
373
+ }
374
+ function testBench(fail) {
375
+ return drawn(72, 36, g => {
376
+ g.rect(0, 20, 72, 6, "j");
377
+ g.rect(0, 20, 72, 1, "9");
378
+ g.rect(0, 26, 72, 3, "i");
379
+ g.rect(2, 29, 3, 7, "i");
380
+ g.rect(67, 29, 3, 7, "i");
381
+ g.rect(2, 2, 26, 17, "a");
382
+ g.rect(4, 4, 22, 12, "0");
383
+ let py = 13;
384
+ for (let x = 5; x < 25; x++) {
385
+ const ny = 6 + Math.round(3 * Math.sin(x * .7)) + (x > 18 ? 2 : 0);
386
+ g.line(x - 1, py, x, ny, fail && x > 18 ? "k" : "h");
387
+ py = ny;
388
+ }
389
+ g.rect(13, 19, 4, 2, "i");
390
+ g.rect(34, 6, 18, 12, "a");
391
+ g.rect(35, 7, 16, 10, fail ? "b" : "f");
392
+ [8, 11, 14].forEach((y, i) => { g.set(37, y, fail && i === 1 ? "k" : "h"); g.rect(39, y, 9, 1, "d"); });
393
+ g.rect(32, 18, 22, 3, "j");
394
+ g.rect(32, 18, 22, 1, "9");
395
+ g.rect(57, 14, 12, 7, "f");
396
+ g.rect(57, 14, 12, 1, "g");
397
+ [59, 62, 65].forEach(x => g.set(x, 16, "l"));
398
+ g.rect(60, 18, 5, 2, "0");
399
+ g.rect(28, 12, 4, 8, "0");
400
+ g.rect(29, 13, 2, 5, "d");
401
+ g.line(10, 26, 8, 33, "0");
402
+ g.line(60, 26, 63, 32, "0");
403
+ });
404
+ }
405
+ function statusBoard(fail) {
406
+ return drawn(60, 28, g => {
407
+ g.rect(0, 0, 60, 28, "a");
408
+ g.rect(2, 2, 56, 22, "0");
409
+ g.rect(2, 2, 56, 1, "b");
410
+ for (let i = 0; i < 8; i++) {
411
+ const h = 4 + (i * 5) % 11;
412
+ g.rect(5 + i * 6, 20 - h, 4, h, fail && i === 5 ? "k" : i % 3 ? "g" : "h");
413
+ }
414
+ for (let i = 0; i < 6; i++)
415
+ g.rect(5 + i * 9, 21, 6, 1, "f");
416
+ g.rect(26, 25, 8, 3, "i");
417
+ });
418
+ }
419
+ function cubicle(mirror) {
420
+ return drawn(84, 56, g => {
421
+ g.rect(0, 0, 84, 2, "j");
422
+ g.rect(0, 0, 84, 1, "9");
423
+ g.rect(0, 2, 84, 14, "c");
424
+ g.rect(0, 14, 84, 2, "b");
425
+ for (let x = 3; x < 84; x += 6)
426
+ g.rect(x, 4, 1, 9, "b");
427
+ g.rect(0, 0, 4, 42, "j");
428
+ g.rect(0, 0, 1, 42, "9");
429
+ g.rect(0, 42, 4, 13, "c");
430
+ g.rect(0, 53, 4, 2, "b");
431
+ g.rect(80, 0, 4, 42, "j");
432
+ g.rect(83, 0, 1, 42, "i");
433
+ g.rect(80, 42, 4, 13, "c");
434
+ g.rect(80, 53, 4, 2, "b");
435
+ g.rect(8, 4, 5, 5, "l");
436
+ g.rect(14, 6, 5, 5, "n");
437
+ g.rect(66, 4, 9, 7, "9");
438
+ g.rect(67, 5, 7, 4, "d");
439
+ g.ball(70.5, 9, 3, 2, ["f", "g", "h"]);
440
+ g.rect(4, 16, 76, 10, "4");
441
+ g.rect(4, 16, 76, 1, "5");
442
+ g.rect(4, 26, 76, 3, "3");
443
+ const wing = mirror ? 56 : 4;
444
+ g.rect(wing, 16, 24, 26, "4");
445
+ g.rect(wing, 42, 24, 3, "3");
446
+ g.rect(wing + (mirror ? 0 : 23), 16, 1, 26, "5");
447
+ g.rect(mirror ? 57 : 6, 30, 10, 8, "9");
448
+ g.rect(mirror ? 58 : 7, 31, 8, 1, "j");
449
+ g.rect(mirror ? 58 : 7, 33, 6, 1, "j");
450
+ monitor(g, 30, 1, [[0, 8, "d"], [2, 11, "h"], [2, 6, "l"], [0, 10, "d"], [2, 4, "c"]]);
451
+ g.rect(31, 22, 22, 3, "j");
452
+ g.rect(31, 22, 22, 1, "9");
453
+ g.rect(mirror ? 12 : 64, 18, 5, 5, "9");
454
+ g.rect(mirror ? 12 : 64, 20, 5, 1, "n");
455
+ g.rect(mirror ? 70 : 8, 12, 6, 6, "3");
456
+ g.rect(mirror ? 70 : 8, 12, 6, 1, "4");
457
+ g.ball(mirror ? 73 : 11, 9, 4, 3.5, ["f", "g", "h"]);
458
+ });
459
+ }
460
+ function bubble(kind) {
461
+ return drawn(12, 14, g => {
462
+ g.rect(1, 0, 10, 10, "9");
463
+ g.rect(0, 1, 12, 8, "9");
464
+ g.rect(4, 10, 3, 1, "9");
465
+ g.set(5, 11, "9");
466
+ g.rect(1, 8, 10, 1, "8");
467
+ if (kind === "alert") {
468
+ g.rect(5, 2, 2, 4, "k");
469
+ g.rect(5, 7, 2, 1, "k");
470
+ }
471
+ else if (kind === "fail") {
472
+ g.line(3, 2, 8, 7, "k");
473
+ g.line(4, 2, 9, 7, "k");
474
+ g.line(8, 2, 3, 7, "k");
475
+ g.line(9, 2, 4, 7, "k");
476
+ }
477
+ else if (kind === "done") {
478
+ g.line(3, 5, 5, 7, "g");
479
+ g.line(5, 7, 9, 3, "g");
480
+ g.line(3, 4, 5, 6, "g");
481
+ g.line(5, 6, 8, 3, "g");
482
+ }
483
+ else {
484
+ g.set(3, 5, "i");
485
+ g.set(6, 5, "i");
486
+ g.set(9, 5, "i");
487
+ }
488
+ });
489
+ }
490
+ /** Door in a north-south wall seen from above (frames: 0 closed, 1 ajar, 2 open); the leaf swings into the right room. */
491
+ function sideDoor(frame) {
492
+ return drawn(18, 34, g => {
493
+ g.rect(0, 0, 8, 3, "1");
494
+ g.rect(0, 31, 8, 3, "1");
495
+ if (frame === 0) {
496
+ g.rect(1, 3, 6, 28, "3");
497
+ g.rect(1, 3, 1, 28, "5");
498
+ g.rect(6, 3, 1, 28, "2");
499
+ g.rect(3, 6, 2, 8, "4");
500
+ g.rect(3, 18, 2, 9, "4");
501
+ g.rect(5, 15, 2, 3, "l");
502
+ return;
503
+ }
504
+ g.rect(0, 3, 8, 28, "7");
505
+ g.rect(0, 3, 8, 1, "6");
506
+ g.rect(0, 30, 8, 1, "6");
507
+ if (frame === 1) {
508
+ for (let y = 0; y < 28; y++) {
509
+ const x = 8 + Math.floor(y / 5);
510
+ g.rect(x, 3 + y, 4, 1, "3");
511
+ g.set(x, 3 + y, "4");
512
+ }
513
+ g.set(11, 17, "l");
514
+ }
515
+ else {
516
+ g.rect(8, 3, 3, 28, "3");
517
+ g.rect(8, 3, 1, 28, "4");
518
+ g.rect(10, 3, 1, 28, "2");
519
+ g.set(10, 17, "l");
520
+ }
521
+ });
522
+ }
523
+ /** Door in an east-west wall seen from the front; the leaf swings towards the viewer, hinge on the left. */
524
+ function frontDoor(frame) {
525
+ return drawn(30, 36, g => {
526
+ g.rect(0, 0, 30, 36, "1");
527
+ g.rect(2, 2, 26, 34, "0");
528
+ if (frame === 0) {
529
+ g.rect(2, 2, 26, 34, "3");
530
+ g.rect(2, 2, 26, 1, "4");
531
+ g.box(5, 5, 20, 12, "2");
532
+ g.box(5, 20, 20, 13, "2");
533
+ g.rect(8, 7, 14, 7, "d");
534
+ g.rect(8, 7, 14, 1, "9");
535
+ g.rect(23, 20, 2, 3, "l");
536
+ return;
537
+ }
538
+ g.rect(2, 2, 26, 34, "6");
539
+ g.rect(2, 2, 26, 10, "8");
540
+ g.rect(2, 12, 26, 2, "4");
541
+ g.rect(2, 14, 26, 1, "2");
542
+ const w = frame === 1 ? 14 : 5;
543
+ g.rect(2, 2, w, 34, "3");
544
+ g.rect(2, 2, w, 1, "4");
545
+ g.rect(2 + w - 1, 2, 1, 34, "2");
546
+ if (frame === 1) {
547
+ g.rect(4, 6, 8, 6, "d");
548
+ g.rect(10, 20, 2, 3, "l");
549
+ }
550
+ });
551
+ }
552
+ /** Whiteboard without notes; notes are laid on it per room from the criteria. */
553
+ function boardBase() {
554
+ return drawn(64, 48, g => {
555
+ g.rect(9, 30, 2, 14, "i");
556
+ g.rect(53, 30, 2, 14, "i");
557
+ g.rect(9, 30, 1, 14, "j");
558
+ g.rect(53, 30, 1, 14, "j");
559
+ g.rect(4, 43, 12, 2, "i");
560
+ g.rect(48, 43, 12, 2, "i");
561
+ [5, 14, 49, 58].forEach(x => g.rect(x, 45, 2, 2, "a"));
562
+ g.rect(1, 1, 62, 32, "j");
563
+ g.rect(61, 1, 2, 32, "i");
564
+ g.rect(1, 31, 62, 2, "i");
565
+ g.rect(3, 3, 57, 27, "9");
566
+ g.rect(3, 27, 57, 3, "8");
567
+ g.line(5, 26, 12, 4, "8");
568
+ g.rect(6, 33, 52, 2, "j");
569
+ g.rect(6, 34, 52, 1, "i");
570
+ g.rect(14, 32, 5, 1, "b");
571
+ g.rect(21, 32, 5, 1, "g");
572
+ });
573
+ }
574
+ /** One sticky note (7x9 with its pin) per criterion status. */
575
+ function note(colour) {
576
+ return flat(7, 9, g => {
577
+ g.rect(0, 1, 7, 8, colour);
578
+ g.rect(0, 8, 7, 1, DARK[colour]);
579
+ g.rect(6, 2, 1, 7, DARK[colour]);
580
+ g.rect(1, 3, 4, 1, DARK[DARK[colour]]);
581
+ g.rect(1, 5, 3, 1, DARK[DARK[colour]]);
582
+ g.set(3, 0, "b");
583
+ });
584
+ }
585
+ export const NOTE_COLOURS = { PASS: "h", FAIL: "k", PENDING: "l", UNKNOWN: "j" };
586
+ /** Where note i sits on the board sprite (outline included); 12 slots, the 12th becomes "+" when more remain. */
587
+ export function notePosition(index) { return { x: 6 + (index % 6) * 9, y: 5 + Math.floor(index / 6) * 11 }; }
588
+ export const BOARD_SLOTS = 12;
589
+ function wallBlock(capped) {
590
+ // 24 wide so the stripe (8) and panel (24) rhythms tile seamlessly; 40 tall like every wall.
591
+ return flat(24, 40, g => {
592
+ if (capped) {
593
+ g.rect(0, 0, 24, 3, "2");
594
+ g.rect(0, 0, 24, 1, "1");
595
+ }
596
+ g.rect(0, 3, 24, 29, "8");
597
+ for (let x = 4; x < 24; x += 8)
598
+ g.rect(x, 3, 1, 29, "9");
599
+ g.rect(0, 32, 24, 2, "4");
600
+ g.rect(0, 32, 24, 1, "5");
601
+ g.rect(0, 34, 24, 3, "4");
602
+ g.rect(0, 34, 1, 3, "3");
603
+ g.rect(0, 37, 24, 3, "2");
604
+ });
605
+ }
606
+ /** Every room sprite by name. Generated once; the page receives it as JSON. */
607
+ export function roomSprites() {
608
+ const sprites = {
609
+ plant: plant(),
610
+ 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"]); }),
611
+ chairBack: drawn(24, 28, g => {
612
+ g.rect(3, 1, 18, 14, "b");
613
+ g.rect(4, 0, 16, 1, "b");
614
+ g.rect(3, 1, 2, 13, "c");
615
+ g.rect(18, 2, 3, 13, "a");
616
+ g.rect(4, 14, 16, 1, "a");
617
+ g.rect(2, 16, 20, 4, "a");
618
+ g.rect(3, 16, 18, 1, "b");
619
+ g.rect(11, 20, 2, 4, "i");
620
+ g.rect(3, 23, 18, 2, "i");
621
+ g.rect(3, 23, 18, 1, "j");
622
+ [3, 11, 19].forEach(x => g.rect(x, 25, 2, 2, "0"));
623
+ }),
624
+ chairFront: drawn(18, 22, g => {
625
+ g.rect(2, 0, 14, 12, "b");
626
+ g.rect(3, 0, 12, 1, "c");
627
+ g.rect(2, 1, 2, 10, "c");
628
+ g.rect(13, 2, 3, 10, "a");
629
+ g.rect(1, 11, 16, 5, "c");
630
+ g.rect(1, 11, 16, 1, "d");
631
+ g.rect(1, 15, 16, 1, "a");
632
+ g.rect(8, 16, 2, 3, "i");
633
+ g.rect(2, 19, 14, 2, "i");
634
+ [2, 8, 14].forEach(x => g.rect(x, 21, 2, 1, "0"));
635
+ }),
636
+ confTable: drawn(112, 30, g => {
637
+ g.rect(2, 0, 108, 18, "3");
638
+ g.rect(0, 2, 112, 14, "3");
639
+ g.rect(2, 1, 108, 14, "4");
640
+ g.rect(4, 2, 104, 2, "5");
641
+ g.rect(0, 16, 112, 4, "2");
642
+ g.rect(2, 18, 108, 2, "1");
643
+ g.rect(14, 20, 4, 10, "1");
644
+ g.rect(94, 20, 4, 10, "1");
645
+ [[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"); });
646
+ [[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"); });
647
+ g.ell(58, 9, 5, 2.5, "a");
648
+ g.set(58, 8, "h");
649
+ [[26, 3], [90, 4]].forEach(([x, y]) => { g.rect(x, y, 2, 6, "d"); g.set(x, y, "9"); });
650
+ }),
651
+ cubicle: cubicle(false), cubicleMirror: cubicle(true),
652
+ rackPass: rack(false), rackFail: rack(true), benchPass: testBench(false), benchFail: testBench(true),
653
+ statusPass: statusBoard(false), statusFail: statusBoard(true),
654
+ filing: drawn(18, 36, g => {
655
+ g.rect(0, 0, 18, 36, "j");
656
+ g.rect(0, 0, 18, 1, "9");
657
+ g.rect(16, 0, 2, 36, "i");
658
+ for (let d = 0; d < 4; d++) {
659
+ const y = 2 + d * 8;
660
+ g.rect(2, y, 13, 7, "8");
661
+ g.rect(2, y + 6, 13, 1, "j");
662
+ g.rect(6, y + 2, 5, 1, "i");
663
+ g.rect(7, y + 4, 3, 1, "9");
664
+ }
665
+ g.rect(1, 34, 2, 2, "0");
666
+ g.rect(15, 34, 2, 2, "0");
667
+ }),
668
+ 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"); }),
669
+ 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"); }),
670
+ execDesk: drawn(64, 32, g => {
671
+ g.rect(0, 8, 64, 6, "3");
672
+ g.rect(0, 8, 64, 1, "4");
673
+ g.rect(0, 14, 64, 18, "2");
674
+ g.rect(0, 14, 64, 1, "1");
675
+ g.box(4, 17, 26, 12, "1");
676
+ g.box(34, 17, 26, 12, "1");
677
+ g.rect(15, 22, 4, 1, "l");
678
+ g.rect(45, 22, 4, 1, "l");
679
+ g.rect(4, 6, 8, 2, "l");
680
+ g.rect(3, 1, 10, 4, "f");
681
+ g.rect(3, 1, 10, 1, "g");
682
+ g.rect(7, 5, 2, 2, "i");
683
+ g.rect(24, 0, 18, 9, "j");
684
+ g.rect(24, 0, 18, 1, "9");
685
+ g.set(33, 4, "9");
686
+ g.rect(46, 5, 10, 4, "9");
687
+ g.rect(47, 4, 10, 4, "9");
688
+ g.rect(48, 5, 6, 1, "j");
689
+ g.rect(16, 6, 6, 2, "0");
690
+ g.rect(17, 6, 4, 1, "l");
691
+ }),
692
+ armchair: drawn(26, 26, g => {
693
+ g.rect(3, 0, 20, 12, "2");
694
+ g.rect(4, 0, 18, 2, "3");
695
+ g.rect(12, 2, 1, 9, "1");
696
+ g.rect(0, 8, 5, 15, "2");
697
+ g.rect(0, 8, 5, 2, "3");
698
+ g.rect(21, 8, 5, 15, "1");
699
+ g.rect(21, 8, 5, 2, "2");
700
+ g.rect(5, 12, 16, 7, "3");
701
+ g.rect(5, 12, 16, 2, "4");
702
+ g.rect(5, 19, 16, 4, "2");
703
+ g.rect(2, 23, 2, 3, "0");
704
+ g.rect(22, 23, 2, 3, "0");
705
+ }),
706
+ coffeeTable: drawn(38, 18, g => {
707
+ g.rect(0, 0, 38, 8, "4");
708
+ g.rect(0, 0, 38, 1, "5");
709
+ g.rect(0, 8, 38, 3, "3");
710
+ g.rect(2, 11, 3, 7, "2");
711
+ g.rect(33, 11, 3, 7, "2");
712
+ g.rect(5, 2, 9, 5, "9");
713
+ g.rect(6, 1, 9, 5, "9");
714
+ g.rect(7, 3, 6, 1, "j");
715
+ g.rect(22, 2, 5, 5, "9");
716
+ g.rect(22, 4, 5, 1, "n");
717
+ g.rect(30, 2, 4, 4, "l");
718
+ }),
719
+ lowBookcase: drawn(48, 26, g => {
720
+ g.rect(0, 0, 48, 26, "2");
721
+ g.rect(0, 0, 48, 2, "3");
722
+ g.rect(2, 4, 44, 9, "1");
723
+ g.rect(2, 15, 44, 9, "1");
724
+ g.rect(2, 13, 44, 2, "3");
725
+ const spines = ["b", "g", "n", "l", "c", "3", "h", "f", "d"];
726
+ [4, 15].forEach((y, s) => { let x = 3; for (let b = 0; x < 42; b++) {
727
+ const w = 2 + (b + s) % 2, h = 6 + (b * 3 + s) % 4, c = spines[(b * 2 + s) % spines.length];
728
+ if ((b + s) % 6 === 5) {
729
+ x += 3;
730
+ continue;
731
+ }
732
+ g.rect(x, y + 9 - h, w, h, c);
733
+ g.rect(x + w - 1, y + 9 - h, 1, h, DARK[c]);
734
+ x += w;
735
+ } });
736
+ }),
737
+ metalShelf: drawn(50, 46, g => {
738
+ g.rect(0, 0, 2, 46, "i");
739
+ g.rect(48, 0, 2, 46, "i");
740
+ for (let s = 0; s < 4; s++) {
741
+ const y = 2 + s * 14;
742
+ g.rect(0, y, 50, 2, "j");
743
+ g.rect(0, y + 1, 50, 1, "i");
744
+ }
745
+ [[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));
746
+ }),
747
+ copier: drawn(34, 32, g => {
748
+ g.rect(0, 4, 34, 28, "9");
749
+ g.rect(31, 4, 3, 28, "8");
750
+ g.rect(0, 4, 34, 1, "8");
751
+ g.rect(2, 0, 26, 5, "j");
752
+ g.rect(2, 0, 26, 1, "9");
753
+ g.rect(4, 2, 22, 2, "a");
754
+ g.rect(22, 7, 9, 5, "a");
755
+ g.rect(23, 8, 5, 3, "d");
756
+ g.set(30, 9, "h");
757
+ g.rect(2, 13, 28, 1, "j");
758
+ g.rect(2, 20, 28, 1, "j");
759
+ g.rect(10, 16, 10, 1, "i");
760
+ g.rect(10, 23, 10, 1, "i");
761
+ g.rect(0, 7, 3, 4, "9");
762
+ g.rect(1, 29, 3, 3, "0");
763
+ g.rect(28, 29, 3, 3, "0");
764
+ }),
765
+ sortTable: drawn(56, 26, g => {
766
+ g.rect(0, 8, 56, 6, "4");
767
+ g.rect(0, 8, 56, 1, "5");
768
+ g.rect(0, 14, 56, 3, "3");
769
+ g.rect(2, 17, 3, 9, "2");
770
+ g.rect(51, 17, 3, 9, "2");
771
+ [[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"); });
772
+ g.rect(44, 2, 9, 7, "4");
773
+ g.rect(44, 2, 9, 2, "5");
774
+ g.rect(48, 2, 1, 7, "8");
775
+ g.rect(16, 10, 8, 4, "9");
776
+ g.rect(18, 11, 4, 1, "j");
777
+ g.rect(28, 10, 7, 3, "n");
778
+ }),
779
+ handTruck: drawn(20, 42, g => {
780
+ g.rect(14, 0, 2, 38, "i");
781
+ g.rect(14, 0, 1, 38, "j");
782
+ g.rect(12, 0, 5, 2, "i");
783
+ g.rect(0, 37, 16, 2, "i");
784
+ crate(g, 0, 22, 13, 15);
785
+ crate(g, 1, 10, 12, 12);
786
+ g.ell(14, 38, 3.5, 3.5, "a");
787
+ g.set(13, 37, "j");
788
+ }),
789
+ crates: drawn(64, 48, g => {
790
+ crate(g, 1, 33, 15, 13);
791
+ crate(g, 16, 33, 15, 13);
792
+ crate(g, 31, 33, 15, 13);
793
+ crate(g, 8, 20, 15, 13);
794
+ crate(g, 23, 20, 15, 13);
795
+ crate(g, 16, 7, 15, 13);
796
+ g.rect(60, 8, 2, 37, "i");
797
+ g.rect(60, 8, 1, 37, "j");
798
+ g.rect(57, 6, 5, 2, "i");
799
+ g.rect(49, 44, 12, 2, "i");
800
+ crate(g, 48, 31, 12, 13);
801
+ g.ell(58, 45, 3, 3, "a");
802
+ }),
803
+ counter: drawn(80, 34, g => {
804
+ g.rect(0, 10, 80, 5, "9");
805
+ g.rect(0, 10, 80, 1, "8");
806
+ g.rect(0, 15, 80, 1, "j");
807
+ g.rect(0, 16, 80, 18, "4");
808
+ g.rect(78, 16, 2, 18, "3");
809
+ for (let d = 0; d < 5; d++) {
810
+ g.box(2 + d * 16, 18, 14, 14, "3");
811
+ g.rect(d % 2 ? 4 + d * 16 : 12 + d * 16, 23, 1, 4, "5");
812
+ }
813
+ g.rect(4, 0, 14, 12, "a");
814
+ g.rect(4, 0, 14, 1, "b");
815
+ g.rect(6, 3, 6, 3, "0");
816
+ g.set(14, 4, "h");
817
+ g.rect(8, 8, 5, 3, "9");
818
+ g.rect(9, 9, 3, 1, "2");
819
+ g.rect(26, 2, 22, 10, "j");
820
+ g.rect(26, 2, 22, 1, "9");
821
+ g.rect(28, 4, 13, 6, "a");
822
+ g.rect(29, 5, 5, 1, "b");
823
+ g.rect(43, 4, 3, 6, "i");
824
+ g.set(44, 5, "h");
825
+ g.rect(54, 10, 16, 3, "j");
826
+ g.rect(55, 11, 14, 1, "i");
827
+ g.rect(61, 5, 2, 6, "i");
828
+ g.rect(61, 5, 5, 1, "i");
829
+ g.rect(72, 6, 4, 5, "n");
830
+ g.rect(76, 7, 1, 2, "n");
831
+ g.rect(20, 7, 4, 4, "9");
832
+ g.rect(20, 8, 4, 1, "c");
833
+ }),
834
+ fridge: drawn(22, 44, g => {
835
+ g.rect(0, 0, 22, 44, "9");
836
+ g.rect(19, 0, 3, 44, "8");
837
+ g.rect(0, 14, 22, 1, "j");
838
+ g.rect(16, 4, 1, 8, "i");
839
+ g.rect(16, 18, 1, 12, "i");
840
+ g.rect(4, 20, 4, 4, "l");
841
+ g.rect(9, 24, 3, 3, "n");
842
+ g.rect(4, 27, 5, 6, "9");
843
+ g.box(4, 27, 5, 6, "j");
844
+ g.rect(1, 42, 3, 2, "0");
845
+ g.rect(18, 42, 3, 2, "0");
846
+ }),
847
+ vending: drawn(26, 46, g => {
848
+ g.rect(0, 0, 26, 46, "b");
849
+ g.rect(0, 0, 26, 2, "c");
850
+ g.rect(24, 0, 2, 46, "a");
851
+ g.rect(2, 4, 16, 30, "d");
852
+ const goods = ["n", "l", "h", "9", "3", "c"];
853
+ for (let r = 0; r < 5; r++) {
854
+ g.rect(2, 9 + r * 6, 16, 1, "j");
855
+ for (let c = 0; c < 4; c++)
856
+ g.rect(3 + c * 4, 5 + r * 6, 3, 4, goods[(r + c * 2) % goods.length]);
857
+ }
858
+ g.line(3, 31, 10, 5, "9");
859
+ g.rect(19, 6, 4, 10, "a");
860
+ for (let i = 0; i < 4; i++)
861
+ g.rect(20, 7 + i * 2, 2, 1, "j");
862
+ g.rect(20, 18, 2, 3, "0");
863
+ g.rect(3, 37, 14, 5, "a");
864
+ g.rect(4, 38, 12, 3, "0");
865
+ }),
866
+ cooler: drawn(14, 36, g => {
867
+ g.ball(7, 6, 6, 6, ["c", "d", "9"]);
868
+ g.rect(5, 11, 4, 2, "c");
869
+ g.rect(1, 13, 12, 22, "9");
870
+ g.rect(11, 13, 2, 22, "8");
871
+ g.rect(1, 13, 12, 1, "8");
872
+ g.rect(3, 17, 2, 2, "c");
873
+ g.rect(9, 17, 2, 2, "n");
874
+ g.rect(2, 21, 10, 1, "j");
875
+ g.rect(3, 28, 8, 1, "8");
876
+ }),
877
+ cafeTable: drawn(24, 22, g => {
878
+ g.ell(12, 6, 11.5, 5.5, "9");
879
+ g.ell(12, 7, 11.5, 5, "8");
880
+ g.ell(12, 5.5, 10.5, 4, "9");
881
+ g.rect(11, 11, 3, 8, "i");
882
+ g.rect(6, 19, 13, 2, "i");
883
+ g.rect(7, 3, 4, 3, "n");
884
+ g.rect(15, 4, 3, 3, "l");
885
+ }),
886
+ sofa: drawn(48, 28, g => {
887
+ g.rect(4, 2, 40, 12, "f");
888
+ g.rect(4, 2, 40, 2, "g");
889
+ g.rect(5, 1, 38, 1, "g");
890
+ g.rect(24, 3, 1, 11, "e");
891
+ g.rect(0, 8, 6, 16, "f");
892
+ g.rect(0, 8, 6, 2, "g");
893
+ g.rect(42, 8, 6, 16, "e");
894
+ g.rect(42, 8, 6, 2, "f");
895
+ g.rect(6, 13, 36, 7, "g");
896
+ g.rect(6, 13, 36, 2, "h");
897
+ g.rect(24, 13, 1, 7, "f");
898
+ g.rect(6, 20, 36, 4, "f");
899
+ g.rect(2, 24, 3, 3, "1");
900
+ g.rect(43, 24, 3, 3, "1");
901
+ }),
902
+ // wall decor
903
+ tv: drawn(52, 26, g => {
904
+ g.rect(0, 0, 52, 26, "a");
905
+ g.rect(2, 2, 48, 20, "9");
906
+ g.rect(2, 2, 48, 5, "b");
907
+ g.rect(4, 3, 18, 2, "9");
908
+ [[6, 8], [13, 11], [20, 6], [27, 13]].forEach(([x, h]) => g.rect(x, 20 - h, 5, h, x === 27 ? "g" : "c"));
909
+ g.rect(36, 10, 11, 1, "j");
910
+ g.rect(36, 13, 9, 1, "j");
911
+ g.rect(36, 16, 11, 1, "j");
912
+ g.rect(22, 23, 8, 3, "i");
913
+ }),
914
+ blinds: drawn(48, 24, g => {
915
+ g.rect(0, 0, 48, 22, "9");
916
+ g.rect(46, 0, 2, 22, "8");
917
+ g.rect(3, 3, 42, 16, "d");
918
+ g.rect(3, 12, 42, 7, "c");
919
+ g.ball(14, 19, 9, 4, ["f", "g", "h"]);
920
+ g.ball(38, 20, 7, 3, ["e", "f", "g"]);
921
+ for (let y = 3; y < 11; y += 2) {
922
+ g.rect(3, y, 42, 1, "8");
923
+ g.rect(3, y + 1, 42, 1, "6");
924
+ }
925
+ g.rect(3, 11, 42, 1, "j");
926
+ g.rect(40, 12, 1, 5, "i");
927
+ g.rect(0, 20, 48, 3, "9");
928
+ g.rect(0, 23, 48, 1, "6");
929
+ }),
930
+ poster: drawn(22, 28, g => {
931
+ g.rect(0, 0, 22, 28, "a");
932
+ g.rect(2, 2, 18, 18, "d");
933
+ g.rect(2, 12, 18, 8, "c");
934
+ g.line(2, 17, 9, 7, "f");
935
+ g.line(9, 7, 14, 13, "f");
936
+ g.line(12, 11, 16, 8, "f");
937
+ g.line(16, 8, 19, 12, "f");
938
+ for (let y = 8; y < 20; y++)
939
+ for (let x = 2; x < 20; x++)
940
+ if (g.get(x, y - 1) === "f" || g.get(x, y - 1) === "g")
941
+ g.set(x, y, "g");
942
+ g.set(9, 7, "9");
943
+ g.rect(8, 8, 3, 1, "9");
944
+ g.set(16, 8, "9");
945
+ g.rect(15, 4, 3, 3, "l");
946
+ g.rect(4, 22, 14, 2, "9");
947
+ g.rect(6, 25, 10, 1, "j");
948
+ }),
949
+ corkboard: drawn(36, 24, g => {
950
+ g.rect(0, 0, 36, 24, "3");
951
+ g.rect(2, 2, 32, 20, "4");
952
+ for (let i = 0; i < 30; i++)
953
+ g.set(3 + (i * 7) % 30, 3 + (i * 5) % 18, "5");
954
+ [[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"); });
955
+ }),
956
+ 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"); }),
957
+ 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"); }),
958
+ 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"); }),
959
+ cubby: drawn(40, 18, g => {
960
+ g.rect(0, 0, 40, 18, "3");
961
+ g.rect(0, 0, 40, 1, "4");
962
+ for (let r = 0; r < 2; r++)
963
+ for (let c = 0; c < 5; c++) {
964
+ const x = 2 + Math.floor(c * 7.6), y = 2 + r * 8;
965
+ g.rect(x, y, 6, 6, "1");
966
+ if ((r * 5 + c) % 3 !== 1) {
967
+ g.rect(x + 1, y + 2, 4, 4, (r + c) % 4 ? "9" : "n");
968
+ g.rect(x + 1, y + 2, 4, 1, "8");
969
+ }
970
+ }
971
+ }),
972
+ // whiteboard driven by criteria
973
+ board: boardBase(), notePass: note("h"), noteFail: note("k"), notePending: note("l"), noteUnknown: note("j"),
974
+ 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"); }),
975
+ // head bubbles and doors
976
+ bubbleAlert: bubble("alert"), bubbleDone: bubble("done"), bubbleBusy: bubble("busy"), bubbleFail: bubble("fail"),
977
+ sideDoor0: sideDoor(0), sideDoor1: sideDoor(1), sideDoor2: sideDoor(2), frontDoor0: frontDoor(0), frontDoor1: frontDoor(1), frontDoor2: frontDoor(2),
978
+ // tiles: walls (24x40, repeat in x), floors (repeat in both)
979
+ wall: wallBlock(true),
980
+ 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"); }),
981
+ carpet: flat(32, 32, g => {
982
+ g.rect(0, 0, 32, 32, "m");
983
+ for (let y = 0; y < 32; y++)
984
+ for (let x = 0; x < 32; x++) {
985
+ const r = (Math.imul(x, 73856093) ^ Math.imul(y, 19349663)) >>> 0;
986
+ if (r % 37 === 0)
987
+ g.set(x, y, "6");
988
+ else if (r % 71 === 0)
989
+ g.set(x, y, "j");
990
+ }
991
+ g.rect(0, 0, 32, 1, "6");
992
+ g.rect(0, 0, 1, 32, "6");
993
+ }),
994
+ 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"); }),
995
+ 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"); }),
996
+ rugBlue: flat(16, 16, g => { g.rect(0, 0, 16, 16, "b"); g.rect(0, 0, 16, 1, "c"); }),
997
+ rugGreen: flat(16, 16, g => { g.rect(0, 0, 16, 16, "f"); g.rect(0, 0, 16, 1, "g"); })
998
+ };
999
+ return sprites;
1000
+ }