arcadia / phase-runner

Phase Runner

chunks.js3.3 KB
1// Phase Runner - hand designed chunks + fairness solver.
2// Cell legend (one char per lane, rows listed nearest first):
3// . empty c coin
4// a day block b night block (solid only in that world)
5// g day gap h night gap
6// e day drone f night drone
7(function (root) {
8 "use strict";
9 var CH = [
10 // tier 0 : learn the two worlds
11 { t: 0, r: ["a..", "...", "..a", "...", ".a."] },
12 { t: 0, r: ["aaa", "...", "...", "bbb"] },
13 { t: 0, r: [".b.", "...", "b.b", "...", "..b"] },
14 { t: 0, r: ["g..", "...", ".g.", "...", "..g"] },
15 { t: 0, r: ["e..", "...", ".e.", "...", "..e"] },
16 { t: 0, r: ["hhh", "...", "...", "aaa"] },
17 { t: 0, r: ["cc.", ".cc", "c.c", ".cc", "cc."] },
18 // tier 1 : mixed worlds
19 { t: 1, r: ["aab", "...", "baa", "...", "aba"] },
20 { t: 1, r: ["a.b", "...", "b.a", "...", "a.b"] },
21 { t: 1, r: ["ggg", "...", "...", "bbb", "...", "hh."] },
22 { t: 1, r: ["e.f", "...", "f.e", "...", ".ef"] },
23 { t: 1, r: ["abb", "...", ".ab", "...", "ba."] },
24 { t: 1, r: ["aaa", "...", "bbb", "...", "aaa"] },
25 { t: 1, r: ["..a", "..b", ".a.", ".b.", "a..", "b.."] },
26 { t: 1, r: ["g.h", "...", "h.g", "...", "g.h"] },
27 // tier 2 : dense
28 { t: 2, r: ["aab", "baa", "aab", "baa"] },
29 { t: 2, r: ["abb", "bab", "bba", "abb"] },
30 { t: 2, r: ["g.h", ".e.", "h.g", "...", "f.e"] },
31 { t: 2, r: ["aab", "hgg", "bba", "ggh"] },
32 { t: 2, r: ["aaa", "...", "bbb", "aaa"] },
33 { t: 2, r: ["e.f", "f.e", "e.f", "...", "hhh", "...", "ggg"] },
34 // tier 3 : rapid phase work
35 { t: 3, r: ["aaa", ".c.", "bbb", ".c.", "aaa", ".c.", "bbb"] },
36 { t: 3, r: ["abb", "aba", "bba", "baa", "aab", "abb"] },
37 { t: 3, r: ["aab", "...", "bab", "...", "bba", "...", "aab"] },
38 { t: 3, r: ["ggg", "bbb", "hhh", "aaa"] },
39 { t: 3, r: ["abf", "bag", "hab", "abe", "fba"] }
40 ];
41 function safe(row, l, w) {
42 var ch = row.charAt(l);
43 if (w === 0) return ch !== "a" && ch !== "g" && ch !== "e";
44 return ch !== "b" && ch !== "h" && ch !== "f";
45 }
46 // Returns array of [lane, world] (one per row) or null when unsolvable.
47 // Player may change at most one lane and toggle world between consecutive rows.
48 function solve(rows, rnd) {
49 var layers = [], prev = [1, 1, 1, 1, 1, 1], i, l, w, pl, pw, cur, any;
50 for (i = 0; i < rows.length; i++) {
51 cur = [0, 0, 0, 0, 0, 0]; any = false;
52 for (l = 0; l < 3; l++) for (w = 0; w < 2; w++) {
53 if (!safe(rows[i], l, w)) continue;
54 for (pl = Math.max(0, l - 1); pl <= Math.min(2, l + 1) && !cur[l * 2 + w]; pl++)
55 for (pw = 0; pw < 2; pw++) if (prev[pl * 2 + pw]) { cur[l * 2 + w] = pl * 2 + pw + 1; any = true; break; }
56 }
57 if (!any) return null;
58 layers.push(cur);
59 prev = cur.map(function (x) { return x ? 1 : 0; });
60 }
61 var opts = [];
62 layers[layers.length - 1].forEach(function (x, k) { if (x) opts.push(k); });
63 var idx = opts[Math.floor((rnd ? rnd() : 0) * opts.length)];
64 var path = new Array(rows.length);
65 for (i = rows.length - 1; i >= 0; i--) { path[i] = [idx >> 1, idx & 1]; idx = layers[i][idx] - 1; }
66 return path;
67 }
68 root.PR_CHUNKS = CH; root.PR_solve = solve; root.PR_safe = safe;
69 if (typeof module !== "undefined") module.exports = { CH: CH, solve: solve, safe: safe };
70})(typeof window !== "undefined" ? window : globalThis);
71