From 0a343ecbc50fe293fc9e7d1103bcd3a3912a41a2 Mon Sep 17 00:00:00 2001 From: 33333-33333 Date: Thu, 30 Jul 2026 15:13:53 +0900 Subject: [PATCH] auu --- app-logic.js | 201 + app.js | 5863 +++++++++++++++++ assets/flags/1f1e6-1f1e8.svg | 1 + assets/flags/1f1e6-1f1e9.svg | 1 + assets/flags/1f1e6-1f1ea.svg | 1 + assets/flags/1f1e6-1f1eb.svg | 1 + assets/flags/1f1e6-1f1ec.svg | 1 + assets/flags/1f1e6-1f1ee.svg | 1 + assets/flags/1f1e6-1f1f1.svg | 1 + assets/flags/1f1e6-1f1f2.svg | 1 + assets/flags/1f1e6-1f1f4.svg | 1 + assets/flags/1f1e6-1f1f6.svg | 1 + assets/flags/1f1e6-1f1f7.svg | 1 + assets/flags/1f1e6-1f1f8.svg | 1 + assets/flags/1f1e6-1f1f9.svg | 1 + assets/flags/1f1e6-1f1fa.svg | 1 + assets/flags/1f1e6-1f1fc.svg | 1 + assets/flags/1f1e6-1f1fd.svg | 1 + assets/flags/1f1e6-1f1ff.svg | 1 + assets/flags/1f1e7-1f1e6.svg | 1 + assets/flags/1f1e7-1f1e7.svg | 1 + assets/flags/1f1e7-1f1e9.svg | 1 + assets/flags/1f1e7-1f1ea.svg | 1 + assets/flags/1f1e7-1f1eb.svg | 1 + assets/flags/1f1e7-1f1ec.svg | 1 + assets/flags/1f1e7-1f1ed.svg | 1 + assets/flags/1f1e7-1f1ee.svg | 1 + assets/flags/1f1e7-1f1ef.svg | 1 + assets/flags/1f1e7-1f1f1.svg | 1 + assets/flags/1f1e7-1f1f2.svg | 1 + assets/flags/1f1e7-1f1f3.svg | 1 + assets/flags/1f1e7-1f1f4.svg | 1 + assets/flags/1f1e7-1f1f6.svg | 1 + assets/flags/1f1e7-1f1f7.svg | 1 + assets/flags/1f1e7-1f1f8.svg | 1 + assets/flags/1f1e7-1f1f9.svg | 1 + assets/flags/1f1e7-1f1fb.svg | 1 + assets/flags/1f1e7-1f1fc.svg | 1 + assets/flags/1f1e7-1f1fe.svg | 1 + assets/flags/1f1e7-1f1ff.svg | 1 + assets/flags/1f1e8-1f1e6.svg | 1 + assets/flags/1f1e8-1f1e8.svg | 1 + assets/flags/1f1e8-1f1e9.svg | 1 + assets/flags/1f1e8-1f1eb.svg | 1 + assets/flags/1f1e8-1f1ec.svg | 1 + assets/flags/1f1e8-1f1ed.svg | 1 + assets/flags/1f1e8-1f1ee.svg | 1 + assets/flags/1f1e8-1f1f0.svg | 1 + assets/flags/1f1e8-1f1f1.svg | 1 + assets/flags/1f1e8-1f1f2.svg | 1 + assets/flags/1f1e8-1f1f3.svg | 1 + assets/flags/1f1e8-1f1f4.svg | 1 + assets/flags/1f1e8-1f1f5.svg | 1 + assets/flags/1f1e8-1f1f6.svg | 1 + assets/flags/1f1e8-1f1f7.svg | 1 + assets/flags/1f1e8-1f1fa.svg | 1 + assets/flags/1f1e8-1f1fb.svg | 1 + assets/flags/1f1e8-1f1fc.svg | 1 + assets/flags/1f1e8-1f1fd.svg | 1 + assets/flags/1f1e8-1f1fe.svg | 1 + assets/flags/1f1e8-1f1ff.svg | 1 + assets/flags/1f1e9-1f1ea.svg | 1 + assets/flags/1f1e9-1f1ec.svg | 1 + assets/flags/1f1e9-1f1ef.svg | 1 + assets/flags/1f1e9-1f1f0.svg | 1 + assets/flags/1f1e9-1f1f2.svg | 1 + assets/flags/1f1e9-1f1f4.svg | 1 + assets/flags/1f1e9-1f1ff.svg | 1 + assets/flags/1f1ea-1f1e6.svg | 1 + assets/flags/1f1ea-1f1e8.svg | 1 + assets/flags/1f1ea-1f1ea.svg | 1 + assets/flags/1f1ea-1f1ec.svg | 1 + assets/flags/1f1ea-1f1ed.svg | 1 + assets/flags/1f1ea-1f1f7.svg | 1 + assets/flags/1f1ea-1f1f8.svg | 1 + assets/flags/1f1ea-1f1f9.svg | 1 + assets/flags/1f1ea-1f1fa.svg | 1 + assets/flags/1f1eb-1f1ee.svg | 1 + assets/flags/1f1eb-1f1ef.svg | 1 + assets/flags/1f1eb-1f1f0.svg | 1 + assets/flags/1f1eb-1f1f2.svg | 1 + assets/flags/1f1eb-1f1f4.svg | 1 + assets/flags/1f1eb-1f1f7.svg | 1 + assets/flags/1f1ec-1f1e6.svg | 1 + assets/flags/1f1ec-1f1e7.svg | 1 + assets/flags/1f1ec-1f1e9.svg | 1 + assets/flags/1f1ec-1f1ea.svg | 1 + assets/flags/1f1ec-1f1eb.svg | 1 + assets/flags/1f1ec-1f1ec.svg | 1 + assets/flags/1f1ec-1f1ed.svg | 1 + assets/flags/1f1ec-1f1ee.svg | 1 + assets/flags/1f1ec-1f1f1.svg | 1 + assets/flags/1f1ec-1f1f2.svg | 1 + assets/flags/1f1ec-1f1f3.svg | 1 + assets/flags/1f1ec-1f1f5.svg | 1 + assets/flags/1f1ec-1f1f6.svg | 1 + assets/flags/1f1ec-1f1f7.svg | 1 + assets/flags/1f1ec-1f1f8.svg | 1 + assets/flags/1f1ec-1f1f9.svg | 1 + assets/flags/1f1ec-1f1fa.svg | 1 + assets/flags/1f1ec-1f1fc.svg | 1 + assets/flags/1f1ec-1f1fe.svg | 1 + assets/flags/1f1ed-1f1f0.svg | 1 + assets/flags/1f1ed-1f1f2.svg | 1 + assets/flags/1f1ed-1f1f3.svg | 1 + assets/flags/1f1ed-1f1f7.svg | 1 + assets/flags/1f1ed-1f1f9.svg | 1 + assets/flags/1f1ed-1f1fa.svg | 1 + assets/flags/1f1ee-1f1e8.svg | 1 + assets/flags/1f1ee-1f1e9.svg | 1 + assets/flags/1f1ee-1f1ea.svg | 1 + assets/flags/1f1ee-1f1f1.svg | 1 + assets/flags/1f1ee-1f1f2.svg | 1 + assets/flags/1f1ee-1f1f3.svg | 1 + assets/flags/1f1ee-1f1f4.svg | 1 + assets/flags/1f1ee-1f1f6.svg | 1 + assets/flags/1f1ee-1f1f7.svg | 1 + assets/flags/1f1ee-1f1f8.svg | 1 + assets/flags/1f1ee-1f1f9.svg | 1 + assets/flags/1f1ef-1f1ea.svg | 1 + assets/flags/1f1ef-1f1f2.svg | 1 + assets/flags/1f1ef-1f1f4.svg | 1 + assets/flags/1f1ef-1f1f5.svg | 1 + assets/flags/1f1f0-1f1ea.svg | 1 + assets/flags/1f1f0-1f1ec.svg | 1 + assets/flags/1f1f0-1f1ed.svg | 1 + assets/flags/1f1f0-1f1ee.svg | 1 + assets/flags/1f1f0-1f1f2.svg | 1 + assets/flags/1f1f0-1f1f3.svg | 1 + assets/flags/1f1f0-1f1f5.svg | 1 + assets/flags/1f1f0-1f1f7.svg | 1 + assets/flags/1f1f0-1f1fc.svg | 1 + assets/flags/1f1f0-1f1fe.svg | 1 + assets/flags/1f1f0-1f1ff.svg | 1 + assets/flags/1f1f1-1f1e6.svg | 1 + assets/flags/1f1f1-1f1e7.svg | 1 + assets/flags/1f1f1-1f1e8.svg | 1 + assets/flags/1f1f1-1f1ee.svg | 1 + assets/flags/1f1f1-1f1f0.svg | 1 + assets/flags/1f1f1-1f1f7.svg | 1 + assets/flags/1f1f1-1f1f8.svg | 1 + assets/flags/1f1f1-1f1f9.svg | 1 + assets/flags/1f1f1-1f1fa.svg | 1 + assets/flags/1f1f1-1f1fb.svg | 1 + assets/flags/1f1f1-1f1fe.svg | 1 + assets/flags/1f1f2-1f1e6.svg | 1 + assets/flags/1f1f2-1f1e8.svg | 1 + assets/flags/1f1f2-1f1e9.svg | 1 + assets/flags/1f1f2-1f1ea.svg | 1 + assets/flags/1f1f2-1f1eb.svg | 1 + assets/flags/1f1f2-1f1ec.svg | 1 + assets/flags/1f1f2-1f1ed.svg | 1 + assets/flags/1f1f2-1f1f0.svg | 1 + assets/flags/1f1f2-1f1f1.svg | 1 + assets/flags/1f1f2-1f1f2.svg | 1 + assets/flags/1f1f2-1f1f3.svg | 1 + assets/flags/1f1f2-1f1f4.svg | 1 + assets/flags/1f1f2-1f1f5.svg | 1 + assets/flags/1f1f2-1f1f6.svg | 1 + assets/flags/1f1f2-1f1f7.svg | 1 + assets/flags/1f1f2-1f1f8.svg | 1 + assets/flags/1f1f2-1f1f9.svg | 1 + assets/flags/1f1f2-1f1fa.svg | 1 + assets/flags/1f1f2-1f1fb.svg | 1 + assets/flags/1f1f2-1f1fc.svg | 1 + assets/flags/1f1f2-1f1fd.svg | 1 + assets/flags/1f1f2-1f1fe.svg | 1 + assets/flags/1f1f2-1f1ff.svg | 1 + assets/flags/1f1f3-1f1e6.svg | 1 + assets/flags/1f1f3-1f1e8.svg | 1 + assets/flags/1f1f3-1f1ea.svg | 1 + assets/flags/1f1f3-1f1eb.svg | 1 + assets/flags/1f1f3-1f1ec.svg | 1 + assets/flags/1f1f3-1f1ee.svg | 1 + assets/flags/1f1f3-1f1f1.svg | 1 + assets/flags/1f1f3-1f1f4.svg | 1 + assets/flags/1f1f3-1f1f5.svg | 1 + assets/flags/1f1f3-1f1f7.svg | 1 + assets/flags/1f1f3-1f1fa.svg | 1 + assets/flags/1f1f3-1f1ff.svg | 1 + assets/flags/1f1f4-1f1f2.svg | 1 + assets/flags/1f1f5-1f1e6.svg | 1 + assets/flags/1f1f5-1f1ea.svg | 1 + assets/flags/1f1f5-1f1eb.svg | 1 + assets/flags/1f1f5-1f1ec.svg | 1 + assets/flags/1f1f5-1f1ed.svg | 1 + assets/flags/1f1f5-1f1f0.svg | 1 + assets/flags/1f1f5-1f1f1.svg | 1 + assets/flags/1f1f5-1f1f2.svg | 1 + assets/flags/1f1f5-1f1f3.svg | 1 + assets/flags/1f1f5-1f1f7.svg | 1 + assets/flags/1f1f5-1f1f8.svg | 1 + assets/flags/1f1f5-1f1f9.svg | 1 + assets/flags/1f1f5-1f1fc.svg | 1 + assets/flags/1f1f5-1f1fe.svg | 1 + assets/flags/1f1f6-1f1e6.svg | 1 + assets/flags/1f1f7-1f1ea.svg | 1 + assets/flags/1f1f7-1f1f4.svg | 1 + assets/flags/1f1f7-1f1f8.svg | 1 + assets/flags/1f1f7-1f1fa.svg | 1 + assets/flags/1f1f7-1f1fc.svg | 1 + assets/flags/1f1f8-1f1e6.svg | 1 + assets/flags/1f1f8-1f1e7.svg | 1 + assets/flags/1f1f8-1f1e8.svg | 1 + assets/flags/1f1f8-1f1e9.svg | 1 + assets/flags/1f1f8-1f1ea.svg | 1 + assets/flags/1f1f8-1f1ec.svg | 1 + assets/flags/1f1f8-1f1ed.svg | 1 + assets/flags/1f1f8-1f1ee.svg | 1 + assets/flags/1f1f8-1f1ef.svg | 1 + assets/flags/1f1f8-1f1f0.svg | 1 + assets/flags/1f1f8-1f1f1.svg | 1 + assets/flags/1f1f8-1f1f2.svg | 1 + assets/flags/1f1f8-1f1f3.svg | 1 + assets/flags/1f1f8-1f1f4.svg | 1 + assets/flags/1f1f8-1f1f7.svg | 1 + assets/flags/1f1f8-1f1f8.svg | 1 + assets/flags/1f1f8-1f1f9.svg | 1 + assets/flags/1f1f8-1f1fb.svg | 1 + assets/flags/1f1f8-1f1fd.svg | 1 + assets/flags/1f1f8-1f1fe.svg | 1 + assets/flags/1f1f8-1f1ff.svg | 1 + assets/flags/1f1f9-1f1e6.svg | 1 + assets/flags/1f1f9-1f1e8.svg | 1 + assets/flags/1f1f9-1f1e9.svg | 1 + assets/flags/1f1f9-1f1eb.svg | 1 + assets/flags/1f1f9-1f1ec.svg | 1 + assets/flags/1f1f9-1f1ed.svg | 1 + assets/flags/1f1f9-1f1ef.svg | 1 + assets/flags/1f1f9-1f1f0.svg | 1 + assets/flags/1f1f9-1f1f1.svg | 1 + assets/flags/1f1f9-1f1f2.svg | 1 + assets/flags/1f1f9-1f1f3.svg | 1 + assets/flags/1f1f9-1f1f4.svg | 1 + assets/flags/1f1f9-1f1f7.svg | 1 + assets/flags/1f1f9-1f1f9.svg | 1 + assets/flags/1f1f9-1f1fb.svg | 1 + assets/flags/1f1f9-1f1fc.svg | 1 + assets/flags/1f1f9-1f1ff.svg | 1 + assets/flags/1f1fa-1f1e6.svg | 1 + assets/flags/1f1fa-1f1ec.svg | 1 + assets/flags/1f1fa-1f1f2.svg | 1 + assets/flags/1f1fa-1f1f3.svg | 1 + assets/flags/1f1fa-1f1f8.svg | 1 + assets/flags/1f1fa-1f1fe.svg | 1 + assets/flags/1f1fa-1f1ff.svg | 1 + assets/flags/1f1fb-1f1e6.svg | 1 + assets/flags/1f1fb-1f1e8.svg | 1 + assets/flags/1f1fb-1f1ea.svg | 1 + assets/flags/1f1fb-1f1ec.svg | 1 + assets/flags/1f1fb-1f1ee.svg | 1 + assets/flags/1f1fb-1f1f3.svg | 1 + assets/flags/1f1fb-1f1fa.svg | 1 + assets/flags/1f1fc-1f1eb.svg | 1 + assets/flags/1f1fc-1f1f8.svg | 1 + assets/flags/1f1fd-1f1f0.svg | 1 + assets/flags/1f1fe-1f1ea.svg | 1 + assets/flags/1f1fe-1f1f9.svg | 1 + assets/flags/1f1ff-1f1e6.svg | 1 + assets/flags/1f1ff-1f1f2.svg | 1 + assets/flags/1f1ff-1f1fc.svg | 1 + ...f4-e0067-e0062-e0065-e006e-e0067-e007f.svg | 1 + ...f4-e0067-e0062-e0073-e0063-e0074-e007f.svg | 1 + ...f4-e0067-e0062-e0077-e006c-e0073-e007f.svg | 1 + assets/flags/LICENSE-TWEMOJI.txt | 393 ++ assets/fonts/DotGothic16-Regular.ttf | Bin 0 -> 2069236 bytes assets/fonts/OFL-DotGothic16.txt | 93 + .../2877fb2945abbc1bde895507.boards/B0.1.json | 1 + cloud-data/2877fb2945abbc1bde895507.json | 1 + docs/bend-field-performance-solutions.docx | Bin 0 -> 15666 bytes docs/client-guide.md | 91 + docs/field-save-load-design.md | 676 ++ docs/internal-system.md | 117 + docs/shared-world-complete.md | 34 + favicon.ico | Bin 0 -> 32038 bytes favicon.svg | 5 + field-persistence-worker.js | 32 + field-persistence.js | 257 + index.html | 159 + package.json | 11 + puzzle-core.js | 557 ++ puzzle-worker.js | 26 + realtime-server.js | 286 + scripts/fetch-flag-assets.mjs | 32 + server.js | 262 + store-catalog.json | 2186 ++++++ style.css | 685 ++ test/anomaly-smoke-test.js | 11 + test/browser-field-storage-benchmark.js | 110 + test/browser-performance-benchmark.js | 635 ++ test/cleanup-performance-smoke-test.js | 54 + test/concurrency-smoke-test.js | 235 + test/economy-simulation-test.js | 22 + test/expansion-repair-smoke-test.js | 38 + test/expansion-smoke-test.js | 91 + test/field-persistence-browser-test.html | 32 + test/field-persistence-smoke-test.js | 74 + test/field-save-load-v2-smoke-test.js | 38 + test/gameplay-simplification-smoke-test.js | 143 + test/helpers/app-source.js | 46 + test/helpers/realtime-client.js | 52 + test/interaction-smoke-test.js | 285 + test/mirror-chunk-smoke-test.js | 38 + test/performance-smoke-test.js | 174 + test/phase2-source-smoke-test.js | 24 + test/realtime-lease-unit-test.js | 33 + test/realtime-phase2-smoke-test.js | 70 + test/reset-smoke-test.js | 24 + test/run-all.js | 16 + test/save-pipeline-smoke-test.js | 143 + test/security-authority-smoke-test.js | 14 + test/server-smoke-test.js | 69 + test/shared-world-client-smoke-test.js | 72 + test/shared-world-complete-smoke-test.js | 47 + test/source-smoke-test.js | 163 + test/special-cell-smoke-test.js | 88 + test/stage34-smoke-test.js | 39 + test/storage-smoke-test.js | 101 + test/store-ui-browser-test.js | 119 + test/v4771-ui-input-smoke-test.js | 13 + ...4772-ownership-reaction-name-smoke-test.js | 29 + test/v4774-drag-overview-smoke-test.js | 11 + ...v4775-pan-cursor-performance-smoke-test.js | 12 + test/v4776-frame-pipeline-smoke-test.js | 25 + .../v4777-hud-input-performance-smoke-test.js | 20 + .../v4778-interaction-scheduler-smoke-test.js | 144 + 326 files changed, 15584 insertions(+) create mode 100644 app-logic.js create mode 100644 app.js create mode 100644 assets/flags/1f1e6-1f1e8.svg create mode 100644 assets/flags/1f1e6-1f1e9.svg create mode 100644 assets/flags/1f1e6-1f1ea.svg create mode 100644 assets/flags/1f1e6-1f1eb.svg create mode 100644 assets/flags/1f1e6-1f1ec.svg create mode 100644 assets/flags/1f1e6-1f1ee.svg create mode 100644 assets/flags/1f1e6-1f1f1.svg create mode 100644 assets/flags/1f1e6-1f1f2.svg create mode 100644 assets/flags/1f1e6-1f1f4.svg create mode 100644 assets/flags/1f1e6-1f1f6.svg create mode 100644 assets/flags/1f1e6-1f1f7.svg create mode 100644 assets/flags/1f1e6-1f1f8.svg create mode 100644 assets/flags/1f1e6-1f1f9.svg create mode 100644 assets/flags/1f1e6-1f1fa.svg create mode 100644 assets/flags/1f1e6-1f1fc.svg create mode 100644 assets/flags/1f1e6-1f1fd.svg create mode 100644 assets/flags/1f1e6-1f1ff.svg create mode 100644 assets/flags/1f1e7-1f1e6.svg create mode 100644 assets/flags/1f1e7-1f1e7.svg create mode 100644 assets/flags/1f1e7-1f1e9.svg create mode 100644 assets/flags/1f1e7-1f1ea.svg create mode 100644 assets/flags/1f1e7-1f1eb.svg create mode 100644 assets/flags/1f1e7-1f1ec.svg create mode 100644 assets/flags/1f1e7-1f1ed.svg create mode 100644 assets/flags/1f1e7-1f1ee.svg create mode 100644 assets/flags/1f1e7-1f1ef.svg create mode 100644 assets/flags/1f1e7-1f1f1.svg create mode 100644 assets/flags/1f1e7-1f1f2.svg create mode 100644 assets/flags/1f1e7-1f1f3.svg create mode 100644 assets/flags/1f1e7-1f1f4.svg create mode 100644 assets/flags/1f1e7-1f1f6.svg create mode 100644 assets/flags/1f1e7-1f1f7.svg create mode 100644 assets/flags/1f1e7-1f1f8.svg create mode 100644 assets/flags/1f1e7-1f1f9.svg create mode 100644 assets/flags/1f1e7-1f1fb.svg create mode 100644 assets/flags/1f1e7-1f1fc.svg create mode 100644 assets/flags/1f1e7-1f1fe.svg create mode 100644 assets/flags/1f1e7-1f1ff.svg create mode 100644 assets/flags/1f1e8-1f1e6.svg create mode 100644 assets/flags/1f1e8-1f1e8.svg create mode 100644 assets/flags/1f1e8-1f1e9.svg create mode 100644 assets/flags/1f1e8-1f1eb.svg create mode 100644 assets/flags/1f1e8-1f1ec.svg create mode 100644 assets/flags/1f1e8-1f1ed.svg create mode 100644 assets/flags/1f1e8-1f1ee.svg create mode 100644 assets/flags/1f1e8-1f1f0.svg create mode 100644 assets/flags/1f1e8-1f1f1.svg create mode 100644 assets/flags/1f1e8-1f1f2.svg create mode 100644 assets/flags/1f1e8-1f1f3.svg create mode 100644 assets/flags/1f1e8-1f1f4.svg create mode 100644 assets/flags/1f1e8-1f1f5.svg create mode 100644 assets/flags/1f1e8-1f1f6.svg create mode 100644 assets/flags/1f1e8-1f1f7.svg create mode 100644 assets/flags/1f1e8-1f1fa.svg create mode 100644 assets/flags/1f1e8-1f1fb.svg create mode 100644 assets/flags/1f1e8-1f1fc.svg create mode 100644 assets/flags/1f1e8-1f1fd.svg create mode 100644 assets/flags/1f1e8-1f1fe.svg create mode 100644 assets/flags/1f1e8-1f1ff.svg create mode 100644 assets/flags/1f1e9-1f1ea.svg create mode 100644 assets/flags/1f1e9-1f1ec.svg create mode 100644 assets/flags/1f1e9-1f1ef.svg create mode 100644 assets/flags/1f1e9-1f1f0.svg create mode 100644 assets/flags/1f1e9-1f1f2.svg create mode 100644 assets/flags/1f1e9-1f1f4.svg create mode 100644 assets/flags/1f1e9-1f1ff.svg create mode 100644 assets/flags/1f1ea-1f1e6.svg create mode 100644 assets/flags/1f1ea-1f1e8.svg create mode 100644 assets/flags/1f1ea-1f1ea.svg create mode 100644 assets/flags/1f1ea-1f1ec.svg create mode 100644 assets/flags/1f1ea-1f1ed.svg create mode 100644 assets/flags/1f1ea-1f1f7.svg create mode 100644 assets/flags/1f1ea-1f1f8.svg create mode 100644 assets/flags/1f1ea-1f1f9.svg create mode 100644 assets/flags/1f1ea-1f1fa.svg create mode 100644 assets/flags/1f1eb-1f1ee.svg create mode 100644 assets/flags/1f1eb-1f1ef.svg create mode 100644 assets/flags/1f1eb-1f1f0.svg create mode 100644 assets/flags/1f1eb-1f1f2.svg create mode 100644 assets/flags/1f1eb-1f1f4.svg create mode 100644 assets/flags/1f1eb-1f1f7.svg create mode 100644 assets/flags/1f1ec-1f1e6.svg create mode 100644 assets/flags/1f1ec-1f1e7.svg create mode 100644 assets/flags/1f1ec-1f1e9.svg create mode 100644 assets/flags/1f1ec-1f1ea.svg create mode 100644 assets/flags/1f1ec-1f1eb.svg create mode 100644 assets/flags/1f1ec-1f1ec.svg create mode 100644 assets/flags/1f1ec-1f1ed.svg create mode 100644 assets/flags/1f1ec-1f1ee.svg create mode 100644 assets/flags/1f1ec-1f1f1.svg create mode 100644 assets/flags/1f1ec-1f1f2.svg create mode 100644 assets/flags/1f1ec-1f1f3.svg create mode 100644 assets/flags/1f1ec-1f1f5.svg create mode 100644 assets/flags/1f1ec-1f1f6.svg create mode 100644 assets/flags/1f1ec-1f1f7.svg create mode 100644 assets/flags/1f1ec-1f1f8.svg create mode 100644 assets/flags/1f1ec-1f1f9.svg create mode 100644 assets/flags/1f1ec-1f1fa.svg create mode 100644 assets/flags/1f1ec-1f1fc.svg create mode 100644 assets/flags/1f1ec-1f1fe.svg create mode 100644 assets/flags/1f1ed-1f1f0.svg create mode 100644 assets/flags/1f1ed-1f1f2.svg create mode 100644 assets/flags/1f1ed-1f1f3.svg create mode 100644 assets/flags/1f1ed-1f1f7.svg create mode 100644 assets/flags/1f1ed-1f1f9.svg create mode 100644 assets/flags/1f1ed-1f1fa.svg create mode 100644 assets/flags/1f1ee-1f1e8.svg create mode 100644 assets/flags/1f1ee-1f1e9.svg create mode 100644 assets/flags/1f1ee-1f1ea.svg create mode 100644 assets/flags/1f1ee-1f1f1.svg create mode 100644 assets/flags/1f1ee-1f1f2.svg create mode 100644 assets/flags/1f1ee-1f1f3.svg create mode 100644 assets/flags/1f1ee-1f1f4.svg create mode 100644 assets/flags/1f1ee-1f1f6.svg create mode 100644 assets/flags/1f1ee-1f1f7.svg create mode 100644 assets/flags/1f1ee-1f1f8.svg create mode 100644 assets/flags/1f1ee-1f1f9.svg create mode 100644 assets/flags/1f1ef-1f1ea.svg create mode 100644 assets/flags/1f1ef-1f1f2.svg create mode 100644 assets/flags/1f1ef-1f1f4.svg create mode 100644 assets/flags/1f1ef-1f1f5.svg create mode 100644 assets/flags/1f1f0-1f1ea.svg create mode 100644 assets/flags/1f1f0-1f1ec.svg create mode 100644 assets/flags/1f1f0-1f1ed.svg create mode 100644 assets/flags/1f1f0-1f1ee.svg create mode 100644 assets/flags/1f1f0-1f1f2.svg create mode 100644 assets/flags/1f1f0-1f1f3.svg create mode 100644 assets/flags/1f1f0-1f1f5.svg create mode 100644 assets/flags/1f1f0-1f1f7.svg create mode 100644 assets/flags/1f1f0-1f1fc.svg create mode 100644 assets/flags/1f1f0-1f1fe.svg create mode 100644 assets/flags/1f1f0-1f1ff.svg create mode 100644 assets/flags/1f1f1-1f1e6.svg create mode 100644 assets/flags/1f1f1-1f1e7.svg create mode 100644 assets/flags/1f1f1-1f1e8.svg create mode 100644 assets/flags/1f1f1-1f1ee.svg create mode 100644 assets/flags/1f1f1-1f1f0.svg create mode 100644 assets/flags/1f1f1-1f1f7.svg create mode 100644 assets/flags/1f1f1-1f1f8.svg create mode 100644 assets/flags/1f1f1-1f1f9.svg create mode 100644 assets/flags/1f1f1-1f1fa.svg create mode 100644 assets/flags/1f1f1-1f1fb.svg create mode 100644 assets/flags/1f1f1-1f1fe.svg create mode 100644 assets/flags/1f1f2-1f1e6.svg create mode 100644 assets/flags/1f1f2-1f1e8.svg create mode 100644 assets/flags/1f1f2-1f1e9.svg create mode 100644 assets/flags/1f1f2-1f1ea.svg create mode 100644 assets/flags/1f1f2-1f1eb.svg create mode 100644 assets/flags/1f1f2-1f1ec.svg create mode 100644 assets/flags/1f1f2-1f1ed.svg create mode 100644 assets/flags/1f1f2-1f1f0.svg create mode 100644 assets/flags/1f1f2-1f1f1.svg create mode 100644 assets/flags/1f1f2-1f1f2.svg create mode 100644 assets/flags/1f1f2-1f1f3.svg create mode 100644 assets/flags/1f1f2-1f1f4.svg create mode 100644 assets/flags/1f1f2-1f1f5.svg create mode 100644 assets/flags/1f1f2-1f1f6.svg create mode 100644 assets/flags/1f1f2-1f1f7.svg create mode 100644 assets/flags/1f1f2-1f1f8.svg create mode 100644 assets/flags/1f1f2-1f1f9.svg create mode 100644 assets/flags/1f1f2-1f1fa.svg create mode 100644 assets/flags/1f1f2-1f1fb.svg create mode 100644 assets/flags/1f1f2-1f1fc.svg create mode 100644 assets/flags/1f1f2-1f1fd.svg create mode 100644 assets/flags/1f1f2-1f1fe.svg create mode 100644 assets/flags/1f1f2-1f1ff.svg create mode 100644 assets/flags/1f1f3-1f1e6.svg create mode 100644 assets/flags/1f1f3-1f1e8.svg create mode 100644 assets/flags/1f1f3-1f1ea.svg create mode 100644 assets/flags/1f1f3-1f1eb.svg create mode 100644 assets/flags/1f1f3-1f1ec.svg create mode 100644 assets/flags/1f1f3-1f1ee.svg create mode 100644 assets/flags/1f1f3-1f1f1.svg create mode 100644 assets/flags/1f1f3-1f1f4.svg create mode 100644 assets/flags/1f1f3-1f1f5.svg create mode 100644 assets/flags/1f1f3-1f1f7.svg create mode 100644 assets/flags/1f1f3-1f1fa.svg create mode 100644 assets/flags/1f1f3-1f1ff.svg create mode 100644 assets/flags/1f1f4-1f1f2.svg create mode 100644 assets/flags/1f1f5-1f1e6.svg create mode 100644 assets/flags/1f1f5-1f1ea.svg create mode 100644 assets/flags/1f1f5-1f1eb.svg create mode 100644 assets/flags/1f1f5-1f1ec.svg create mode 100644 assets/flags/1f1f5-1f1ed.svg create mode 100644 assets/flags/1f1f5-1f1f0.svg create mode 100644 assets/flags/1f1f5-1f1f1.svg create mode 100644 assets/flags/1f1f5-1f1f2.svg create mode 100644 assets/flags/1f1f5-1f1f3.svg create mode 100644 assets/flags/1f1f5-1f1f7.svg create mode 100644 assets/flags/1f1f5-1f1f8.svg create mode 100644 assets/flags/1f1f5-1f1f9.svg create mode 100644 assets/flags/1f1f5-1f1fc.svg create mode 100644 assets/flags/1f1f5-1f1fe.svg create mode 100644 assets/flags/1f1f6-1f1e6.svg create mode 100644 assets/flags/1f1f7-1f1ea.svg create mode 100644 assets/flags/1f1f7-1f1f4.svg create mode 100644 assets/flags/1f1f7-1f1f8.svg create mode 100644 assets/flags/1f1f7-1f1fa.svg create mode 100644 assets/flags/1f1f7-1f1fc.svg create mode 100644 assets/flags/1f1f8-1f1e6.svg create mode 100644 assets/flags/1f1f8-1f1e7.svg create mode 100644 assets/flags/1f1f8-1f1e8.svg create mode 100644 assets/flags/1f1f8-1f1e9.svg create mode 100644 assets/flags/1f1f8-1f1ea.svg create mode 100644 assets/flags/1f1f8-1f1ec.svg create mode 100644 assets/flags/1f1f8-1f1ed.svg create mode 100644 assets/flags/1f1f8-1f1ee.svg create mode 100644 assets/flags/1f1f8-1f1ef.svg create mode 100644 assets/flags/1f1f8-1f1f0.svg create mode 100644 assets/flags/1f1f8-1f1f1.svg create mode 100644 assets/flags/1f1f8-1f1f2.svg create mode 100644 assets/flags/1f1f8-1f1f3.svg create mode 100644 assets/flags/1f1f8-1f1f4.svg create mode 100644 assets/flags/1f1f8-1f1f7.svg create mode 100644 assets/flags/1f1f8-1f1f8.svg create mode 100644 assets/flags/1f1f8-1f1f9.svg create mode 100644 assets/flags/1f1f8-1f1fb.svg create mode 100644 assets/flags/1f1f8-1f1fd.svg create mode 100644 assets/flags/1f1f8-1f1fe.svg create mode 100644 assets/flags/1f1f8-1f1ff.svg create mode 100644 assets/flags/1f1f9-1f1e6.svg create mode 100644 assets/flags/1f1f9-1f1e8.svg create mode 100644 assets/flags/1f1f9-1f1e9.svg create mode 100644 assets/flags/1f1f9-1f1eb.svg create mode 100644 assets/flags/1f1f9-1f1ec.svg create mode 100644 assets/flags/1f1f9-1f1ed.svg create mode 100644 assets/flags/1f1f9-1f1ef.svg create mode 100644 assets/flags/1f1f9-1f1f0.svg create mode 100644 assets/flags/1f1f9-1f1f1.svg create mode 100644 assets/flags/1f1f9-1f1f2.svg create mode 100644 assets/flags/1f1f9-1f1f3.svg create mode 100644 assets/flags/1f1f9-1f1f4.svg create mode 100644 assets/flags/1f1f9-1f1f7.svg create mode 100644 assets/flags/1f1f9-1f1f9.svg create mode 100644 assets/flags/1f1f9-1f1fb.svg create mode 100644 assets/flags/1f1f9-1f1fc.svg create mode 100644 assets/flags/1f1f9-1f1ff.svg create mode 100644 assets/flags/1f1fa-1f1e6.svg create mode 100644 assets/flags/1f1fa-1f1ec.svg create mode 100644 assets/flags/1f1fa-1f1f2.svg create mode 100644 assets/flags/1f1fa-1f1f3.svg create mode 100644 assets/flags/1f1fa-1f1f8.svg create mode 100644 assets/flags/1f1fa-1f1fe.svg create mode 100644 assets/flags/1f1fa-1f1ff.svg create mode 100644 assets/flags/1f1fb-1f1e6.svg create mode 100644 assets/flags/1f1fb-1f1e8.svg create mode 100644 assets/flags/1f1fb-1f1ea.svg create mode 100644 assets/flags/1f1fb-1f1ec.svg create mode 100644 assets/flags/1f1fb-1f1ee.svg create mode 100644 assets/flags/1f1fb-1f1f3.svg create mode 100644 assets/flags/1f1fb-1f1fa.svg create mode 100644 assets/flags/1f1fc-1f1eb.svg create mode 100644 assets/flags/1f1fc-1f1f8.svg create mode 100644 assets/flags/1f1fd-1f1f0.svg create mode 100644 assets/flags/1f1fe-1f1ea.svg create mode 100644 assets/flags/1f1fe-1f1f9.svg create mode 100644 assets/flags/1f1ff-1f1e6.svg create mode 100644 assets/flags/1f1ff-1f1f2.svg create mode 100644 assets/flags/1f1ff-1f1fc.svg create mode 100644 assets/flags/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.svg create mode 100644 assets/flags/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.svg create mode 100644 assets/flags/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.svg create mode 100644 assets/flags/LICENSE-TWEMOJI.txt create mode 100644 assets/fonts/DotGothic16-Regular.ttf create mode 100644 assets/fonts/OFL-DotGothic16.txt create mode 100644 cloud-data/2877fb2945abbc1bde895507.boards/B0.1.json create mode 100644 cloud-data/2877fb2945abbc1bde895507.json create mode 100644 docs/bend-field-performance-solutions.docx create mode 100644 docs/client-guide.md create mode 100644 docs/field-save-load-design.md create mode 100644 docs/internal-system.md create mode 100644 docs/shared-world-complete.md create mode 100644 favicon.ico create mode 100644 favicon.svg create mode 100644 field-persistence-worker.js create mode 100644 field-persistence.js create mode 100644 index.html create mode 100644 package.json create mode 100644 puzzle-core.js create mode 100644 puzzle-worker.js create mode 100644 realtime-server.js create mode 100644 scripts/fetch-flag-assets.mjs create mode 100644 server.js create mode 100644 store-catalog.json create mode 100644 style.css create mode 100644 test/anomaly-smoke-test.js create mode 100644 test/browser-field-storage-benchmark.js create mode 100644 test/browser-performance-benchmark.js create mode 100644 test/cleanup-performance-smoke-test.js create mode 100644 test/concurrency-smoke-test.js create mode 100644 test/economy-simulation-test.js create mode 100644 test/expansion-repair-smoke-test.js create mode 100644 test/expansion-smoke-test.js create mode 100644 test/field-persistence-browser-test.html create mode 100644 test/field-persistence-smoke-test.js create mode 100644 test/field-save-load-v2-smoke-test.js create mode 100644 test/gameplay-simplification-smoke-test.js create mode 100644 test/helpers/app-source.js create mode 100644 test/helpers/realtime-client.js create mode 100644 test/interaction-smoke-test.js create mode 100644 test/mirror-chunk-smoke-test.js create mode 100644 test/performance-smoke-test.js create mode 100644 test/phase2-source-smoke-test.js create mode 100644 test/realtime-lease-unit-test.js create mode 100644 test/realtime-phase2-smoke-test.js create mode 100644 test/reset-smoke-test.js create mode 100644 test/run-all.js create mode 100644 test/save-pipeline-smoke-test.js create mode 100644 test/security-authority-smoke-test.js create mode 100644 test/server-smoke-test.js create mode 100644 test/shared-world-client-smoke-test.js create mode 100644 test/shared-world-complete-smoke-test.js create mode 100644 test/source-smoke-test.js create mode 100644 test/special-cell-smoke-test.js create mode 100644 test/stage34-smoke-test.js create mode 100644 test/storage-smoke-test.js create mode 100644 test/store-ui-browser-test.js create mode 100644 test/v4771-ui-input-smoke-test.js create mode 100644 test/v4772-ownership-reaction-name-smoke-test.js create mode 100644 test/v4774-drag-overview-smoke-test.js create mode 100644 test/v4775-pan-cursor-performance-smoke-test.js create mode 100644 test/v4776-frame-pipeline-smoke-test.js create mode 100644 test/v4777-hud-input-performance-smoke-test.js create mode 100644 test/v4778-interaction-scheduler-smoke-test.js diff --git a/app-logic.js b/app-logic.js new file mode 100644 index 0000000..673cbd1 --- /dev/null +++ b/app-logic.js @@ -0,0 +1,201 @@ +'use strict'; +(function(root,factory){ + const shared=root.BendPuzzle||(typeof module==='object'&&module.exports?require('./puzzle-core'):null),api=factory(shared); + if(typeof module==='object'&&module.exports)module.exports=api; + root.BendAppLogic=api; +})(typeof globalThis!=='undefined'?globalThis:this,function(shared){ + if(typeof shared?.key2!=='function')throw new Error('BendPuzzle coordinate helpers are not loaded'); + const key2=shared.key2; + function sectionCountRange(level){ + const max=Math.max(1,Math.min(10,Math.round(level)||1)); + return{min:Math.max(1,max-3),max}; + } + function normalizeGeneratedShape(chunks){ + const minX=Math.min(...chunks.map(cell=>cell[0])),minY=Math.min(...chunks.map(cell=>cell[1])); + return chunks.map(([x,y])=>[x-minX,y-minY]).sort((a,b)=>a[1]-b[1]||a[0]-b[0]); + } + function generatedShapeKey(chunks){return normalizeGeneratedShape(chunks).map(cell=>cell.join(',')).join(';')} + function transformedGeneratedShape(chunks,rotation,flip){ + let cells=chunks.map(([x,y])=>[flip?-x:x,y]); + for(let step=0;step[-y,x]); + return normalizeGeneratedShape(cells); + } + function generatedShapeFamilyKey(chunks){ + let best=null; + for(let flip=0;flip<2;flip++)for(let rotation=0;rotation<4;rotation++){ + const key=generatedShapeKey(transformedGeneratedShape(chunks,rotation,flip===1)); + if(best==null||key>>0)^0x6a09e667)),buckets=shuffle([...groups.values()],rng).map(group=>shuffle([...group],rng)),result=[]; + for(let round=0,remaining=true;remaining;round++){ + remaining=false; + for(const bucket of buckets)if(round{ + const ac=Math.abs(a[0])+Math.abs(a[1]),bc=Math.abs(b[0])+Math.abs(b[1]); + return ac-bc||rng()-.5; + }); + const compactPool=frontier.slice(0,Math.max(1,Math.ceil(frontier.length*.65))),chosen=compactPool[Math.floor(rng()*compactPool.length)]; + set.add(key2(...chosen));cells.push(chosen); + } + return normalizeGeneratedShape(cells); + } + function stableHash(parts){ + const text=(Array.isArray(parts)?parts:[parts]).map(value=>value==null?'':typeof value==='object'?JSON.stringify(value):String(value)).join('\u241f'); + let hash=0x811c9dc5; + for(let index=0;index>>16;hash=Math.imul(hash,0x7feb352d);hash^=hash>>>15;hash=Math.imul(hash,0x846ca68b);hash^=hash>>>16; + return hash>>>0; + } + function seededUnit(parts){return stableHash(parts)/0x100000000} + function boundedCoefficient(parts){return .8+seededUnit(parts)*.4} + function deterministicStorePrice(baseCost,worldSeed,x,y,priceVersion=1){ + const coefficient=boundedCoefficient(['store-price',worldSeed,x,y,priceVersion]); + return{coefficient,price:Math.max(1,Math.round(Math.max(0,Number(baseCost)||0)*coefficient))}; + } + function scoreLensMultiplier(activeCount){ + const count=Math.max(0,Math.floor(Number(activeCount)||0)); + return count<=0?1:count===1?1.25:count===2?1.4:1.5; + } + function completionIdentity(meta,state){ + const paths=(state?.paths||[]).map(path=>({ + startGate:path.startGate,endGate:path.endGate, + cells:(path.cells||[]).map(cell=>[cell[0],cell[1]]) + })).sort((left,right)=>left.startGate-right.startGate||left.endGate-right.endGate); + return stableHash(['completion',meta?.id,meta?.seed,meta?.generatorVersion,paths]); + } + function deterministicBoardReward(baseScore,{worldSeed=0,meta,state,timeAttackModifier=1,scoreLensCount=0}={}){ + const identity=completionIdentity(meta,state),coefficient=boundedCoefficient(['board-reward',worldSeed,meta?.id,meta?.seed,meta?.generatorVersion,identity]), + lensMultiplier=scoreLensMultiplier(scoreLensCount),timeMultiplier=Math.max(0,Number(timeAttackModifier)||1), + award=Math.max(0,Math.round(Math.max(0,Number(baseScore)||0)*coefficient*timeMultiplier*lensMultiplier)); + return{identity,coefficient,lensMultiplier,timeMultiplier,award}; + } + function specialSchedule(level,validCellCount,seed,encounteredTypes=[],recentTypes=[]){ + const boundedLevel=Math.max(1,Math.min(10,Math.floor(Number(level)||1))),cells=Math.max(1,Math.floor(Number(validCellCount)||1)); + if(boundedLevel<5)return{types:[],setCount:0,maxCells:0}; + const types=['warp','lock','crossing'],encountered=new Set(encounteredTypes||[]),recent=(recentTypes||[]).filter(type=>types.includes(type)).slice(-3), + unseen=types.filter(type=>!encountered.has(type)); + if(unseen.length){ + const introduced=[...unseen].sort((a,b)=>stableHash([seed,'introduction',a])-stableHash([seed,'introduction',b])||a.localeCompare(b))[0]; + return{types:[introduced],setCount:1,maxCells:introduced==='crossing'?1:2,introduction:true}; + } + const lastType=recent.at(-1),rotated=types.filter(type=>type!==lastType),pool=rotated.length?rotated:types, + ordered=[...pool].sort((a,b)=>stableHash([seed,b])-stableHash([seed,a])||a.localeCompare(b)), + typeCount=boundedLevel<=6?1:boundedLevel<=8?Math.min(2,1+(stableHash([seed,'type-count'])%2)):boundedLevel===9?2:2+(stableHash([seed,'type-count'])%2), + maxByArea=Math.max(1,Math.floor(cells/18)),setCount=Math.max(1,Math.min(maxByArea,boundedLevel<=6?1:boundedLevel===7?2:boundedLevel===8?3:boundedLevel===9?4:5)); + return{types:ordered.slice(0,Math.min(typeCount,ordered.length)),setCount,maxCells:Math.max(2,Math.min(cells,2+setCount*2)),introduction:false}; + } + function interactionBurden(puzzle){ + const paths=puzzle?.solution||[],totalPathLength=paths.reduce((sum,path)=>sum+(path.cells?.length||0),0),pickups=paths.length, + gateTravel=(puzzle?.g||[]).reduce((sum,gate,index,gates)=>index?sum+Math.abs(gate[0]-gates[index-1][0])+Math.abs(gate[1]-gates[index-1][1]):sum,0), + specialSwitches=(puzzle?.specialCells?.crossings?.length||0)+(puzzle?.specialCells?.warps?.length||0)+(puzzle?.specialCells?.locks?.length||0), + occupancy=new Map();let ambiguousAdjacency=0; + paths.forEach((path,pathIndex)=>(path.cells||[]).forEach(cell=>occupancy.set(key2(cell[0],cell[1]),pathIndex))); + for(const[key,pathIndex]of occupancy){const[row,column]=key.split(',').map(Number);for(const[dr,dc]of[[1,0],[0,1]]){const neighbor=occupancy.get(key2(row+dr,column+dc));if(neighbor!=null&&neighbor!==pathIndex)ambiguousAdjacency++}} + const raw=totalPathLength*.16+pickups*2+Math.min(20,gateTravel*.08)+ambiguousAdjacency*.35+specialSwitches*1.5; + return{totalPathLength,pickups,gateTravel,ambiguousAdjacency,specialSwitches,score:Math.min(40,raw)}; + } + function sectionCountWeight(level,size){ + const range=sectionCountRange(level),span=Math.max(1,range.max-range.min),position=(size-range.min)/span; + return 1+Math.max(0,Math.min(1,position))*(1.4+Math.max(1,Math.min(10,Math.round(level)||1))*.32); + } + function sectionSizeOrder(seed,level,deps){ + const{hash32,rngFrom}=deps||{}; + if(typeof hash32!=='function'||typeof rngFrom!=='function')throw new TypeError('sectionSizeOrder requires random dependencies'); + const range=sectionCountRange(level),rng=rngFrom(hash32(seed^0x5a17c9e3)),ranked=[]; + for(let size=range.min;size<=range.max;size++){ + const weight=sectionCountWeight(level,size),sample=Math.max(Number.EPSILON,rng()); + ranked.push({size,key:-Math.log(sample)/weight}); + } + return ranked.sort((a,b)=>a.key-b.key||b.size-a.size).map(entry=>entry.size); + } + function shapeCandidatesForLevel(seed,level,attemptsPerSize,deps){ + const{shapesBySize,hash32,rngFrom,shuffle}=deps||{}; + if(!shapesBySize||typeof shapesBySize.get!=='function'||typeof hash32!=='function'||typeof rngFrom!=='function'||typeof shuffle!=='function')throw new TypeError('shapeCandidatesForLevel requires shape-generation dependencies'); + const attempts=Number.isInteger(attemptsPerSize)&&attemptsPerSize>=0?attemptsPerSize:12, + sizes=sectionSizeOrder(seed,level,{hash32,rngFrom}),candidates=[],seen=new Set(); + for(const size of sizes){ + for(const known of shapesBySize.get(size)||[]){const key=generatedShapeKey(known);if(!seen.has(key)){seen.add(key);candidates.push(known)}} + for(let attempt=0;attempt>>0)^0x3c6ef372),{hash32,rngFrom,shuffle}); + } + + function analyzePathTurns(path,gates,warpPairs,includeEnd=true){ + if(!path?.cells?.length||includeEnd&&path.endGate==null)return{count:0,cells:[]}; + const sideDelta={N:[-1,0],S:[1,0],W:[0,-1],E:[0,1]},same=(a,b)=>a&&b&&a[0]===b[0]&&a[1]===b[1],warpMap=new Map(); + for(const pair of Array.isArray(warpPairs)?warpPairs:[]){if(pair?.a&&pair?.b){warpMap.set(pair.a.join(','),pair.b);warpMap.set(pair.b.join(','),pair.a)}} + const isWarp=(a,b)=>same(warpMap.get(a?.join(',')),b),outside=gate=>{const delta=sideDelta[gate?.[2]]||[0,0];return[(gate?.[0]||0)+delta[0],(gate?.[1]||0)+delta[1]]}; + const segments=[];let start=0; + for(let index=1;index{ + if(!segment.length)return;const first=segmentIndex===0,last=segmentIndex===segments.length-1,nodes=[]; + if(first)nodes.push(outside(gates?.[path.startGate]));nodes.push(...segment);if(last&&includeEnd)nodes.push(outside(gates?.[path.endGate])); + const offset=first?1:0; + for(let index=1;index=0&&sourceIndex=0?source.expansionRetryRound:0,solvedBy:source.solvedBy||null,solvedById:source.solvedById||null,solvedAt:Number.isFinite(source.solvedAt)&&source.solvedAt>0?source.solvedAt:null,scoreAwarded:source.scoreAwarded||0,scoreVersion:source.scoreVersion||scoreVersion,rewardIdentity:source.rewardIdentity||null,rewardCoefficient:source.rewardCoefficient||null,store:source.store||null,rev:source.rev||0,revAuthor:source.revAuthor||''}; + } + function collectConnectedLineComponent(meta,pathIndex,cache,environment){ + const targetCache=cache&&typeof cache.has==='function'&&typeof cache.set==='function'?cache:new Map(),data=environment?.data,metaState=environment?.metaState,matchingNeighborGate=environment?.matchingNeighborGate; + if(!data?.metas||!data?.states||typeof metaState!=='function'||typeof matchingNeighborGate!=='function')throw new TypeError('collectConnectedLineComponent requires line-graph dependencies'); + const startKey=`${meta?.id}:${pathIndex}`;if(targetCache.has(startKey))return targetCache.get(startKey); + const members=[],visited=new Set(),stack=[[meta?.id,pathIndex]];let length=0; + while(stack.length){ + const[id,index]=stack.pop(),key=`${id}:${index}`;if(visited.has(key))continue; + const currentMeta=data.metas[id],state=data.states[id];if(!currentMeta?.puzzle||!state)continue; + const path=metaState(id).paths[index];if(!path)continue; + visited.add(key);members.push([currentMeta,index]);length+=path.cells.length; + for(const gateIndex of[...(path.detachedStart?[]:[path.startGate]),path.endGate]){ + if(gateIndex==null)continue;let hit;try{hit=matchingNeighborGate(currentMeta,gateIndex)}catch(_){hit=null}if(!hit)continue; + const neighborState=metaState(hit.meta.id),neighborIndex=neighborState.paths.findIndex(candidate=>!candidate.detachedStart&&candidate.startGate===hit.gateIndex||candidate.endGate===hit.gateIndex); + if(neighborIndex>=0)stack.push([hit.meta.id,neighborIndex]); + } + } + const component={members,length:Math.max(1,length)}; + for(const key of visited)targetCache.set(key,component);targetCache.set(startKey,component);return component; + } + function connectedLineLength(meta,pathIndex,cache,environment){return collectConnectedLineComponent(meta,pathIndex,cache,environment).length} + function lineStrokeWidth(length){return 3+12*(1-Math.exp(-(Math.max(1,length)-1)/90))} + function renderedConnectedLineWidth(meta,pathIndex,widthCache,environment,componentCache){ + const targetCache=widthCache&&typeof widthCache.has==='function'&&typeof widthCache.set==='function'?widthCache:new Map(),startKey=`${meta?.id}:${pathIndex}`;if(targetCache.has(startKey))return targetCache.get(startKey); + const component=collectConnectedLineComponent(meta,pathIndex,componentCache,environment),width=lineStrokeWidth(component.length); + for(const[currentMeta,index]of component.members)targetCache.set(`${currentMeta.id}:${index}`,width);targetCache.set(startKey,width);return width; + } + return Object.freeze({ + sectionCountRange,normalizeGeneratedShape,generatedShapeKey,generatedShapeFamilyKey,balancedShapeCandidates,growConnectedShape,shapeCandidatesForLevel, + stableHash,deterministicStorePrice,deterministicBoardReward, + specialSchedule,interactionBurden, + analyzePathTurns,stateForStorage,collectConnectedLineComponent,connectedLineLength,lineStrokeWidth,renderedConnectedLineWidth + }); +}); diff --git a/app.js b/app.js new file mode 100644 index 0000000..c99ccff --- /dev/null +++ b/app.js @@ -0,0 +1,5863 @@ +'use strict'; +const AppLogic=globalThis.BendAppLogic; +if(!AppLogic)throw new Error('BendAppLogic is not loaded'); +const {key2,ckey,sameCell}=globalThis.BendPuzzle; +const FieldPersistence=globalThis.BendFieldPersistence; +if(!FieldPersistence)throw new Error('BendFieldPersistence is not loaded'); +const sectionCountRange=AppLogic.sectionCountRange,normalizeGeneratedShape=AppLogic.normalizeGeneratedShape,generatedShapeKey=AppLogic.generatedShapeKey,generatedShapeFamilyKey=AppLogic.generatedShapeFamilyKey,balancedShapeCandidates=AppLogic.balancedShapeCandidates,growConnectedShape=AppLogic.growConnectedShape,lineStrokeWidth=AppLogic.lineStrokeWidth; +const APP_VERSION='47.77',SAVE_SCHEMA=31,STORAGE_SCHEMA=30,IDB_LAYOUT_VERSION=8,FIELD_STORAGE_FORMAT=2,GAMEPLAY_DATA_VERSION=3,WORLD_GENERATION='v47-field-reset-20260728-interaction-fix',CELL=43,CHUNK=5,UNIT=CELL*CHUNK,PAD=26; +const GENERATOR_VERSION=5,MAX_SCORE=Number.MAX_SAFE_INTEGER,MAX_SOLVES=1000000000,MAX_WORLD_COORD=1000000000; +const MAX_BOARDS=200000,MAX_PATHS_PER_BOARD=512,MAX_IMPORT_BYTES=100*1024*1024,CLOCK_DRIFT_LIMIT=5*60*1000,LOCAL_MIRROR_MAX_BYTES=4.5*1024*1024,IDB_STARTUP_TIMEOUT=8000,MAX_FRONTIER_GENERATION_CYCLES=3,GC_BATCH_ROWS=500; +const AUXILIARY_FPS=30,AUXILIARY_FRAME_INTERVAL=1000/AUXILIARY_FPS,DRAG_TARGET_FPS=60,DRAG_FRAME_INTERVAL=1000/DRAG_TARGET_FPS,INTERACTION_FRAME_TOLERANCE_MS=1.25,DRAG_DISPLAY_WATCHDOG_MS=18,CAMERA_DISPLAY_WATCHDOG_MS=18,DRAG_MAX_POINTER_SAMPLES=12,DRAG_MAX_LOGICAL_SAMPLES_PER_FRAME=4,DRAG_MODEL_BUDGET_MS=.5,DRAG_MAX_CATCHUP_CELLS=24,LIGHTWEIGHT_DRAG_BOARD_CELLS=120,GENERATION_FAILURE_BONUS=2500,GENERATION_FAILURE_MIN_MS=8000,SHARED_EXPANSION_GRACE_MS=60000,SHARED_EXPANSION_JITTER_MS=30000,REALTIME_CURSOR_INTERVAL=50,REALTIME_CURSOR_HEARTBEAT_INTERVAL=5000,REALTIME_VIEWPORT_INTERVAL=250,REALTIME_PLAYER_STALE_MS=15000,REALTIME_CLAIM_REQUEST_TIMEOUT=5000,REALTIME_CLAIM_TOUCH_INTERVAL=20000,REALTIME_REACTION_DURATION=4500,REALTIME_REACTION_RATE_INTERVAL=500,REACTION_LONG_PRESS_MS=500,REACTION_MOVE_CANCEL_PX=18; +const INTERACTION_SCHEDULER_VARIANT=(()=>{try{return localStorage.getItem('bend-field-interaction-scheduler-variant')==='reduced'?'reduced':'full'}catch(_){return'full'}})(); +globalThis.BEND_INTERACTION_SCHEDULER=Object.freeze({enabled:true,version:2,variant:INTERACTION_SCHEDULER_VARIANT}); +const LEGACY_LOCAL_SOLVER='\u3042\u306a\u305f',DEFAULT_PLAYER_NAME='\u65c5\u4eba'; +const AUTO_NAME_ADJECTIVES=Object.freeze(['\u9752\u3044','\u8d64\u3044','\u767d\u3044','\u9ed2\u3044','\u91d1\u8272\u306e','\u9280\u8272\u306e','\u9759\u304b\u306a','\u7d20\u65e9\u3044','\u3084\u3055\u3057\u3044','\u3075\u3057\u304e\u306a']); +const AUTO_NAME_NOUNS=Object.freeze(['\u30ad\u30c4\u30cd','\u30cd\u30b3','\u30d5\u30af\u30ed\u30a6','\u30da\u30f3\u30ae\u30f3','\u30e9\u30c3\u30b3','\u30ab\u30e1','\u30af\u30b8\u30e9','\u30cf\u30ea\u30cd\u30ba\u30df','\u30ab\u30ef\u30a6\u30bd','\u30ed\u30dc\u30c3\u30c8']); +function createAutomaticPlayerName(){const values=new Uint32Array(2);if(globalThis.crypto?.getRandomValues)globalThis.crypto.getRandomValues(values);else{values[0]=(Math.random()*0xffffffff)>>>0;values[1]=(Math.random()*0xffffffff)>>>0}return`${AUTO_NAME_ADJECTIVES[values[0]%AUTO_NAME_ADJECTIVES.length]}${AUTO_NAME_NOUNS[values[1]%AUTO_NAME_NOUNS.length]}-${String(((values[0]^values[1])>>>0)%1000).padStart(3,'0')}`} +function currentPlayerName(){return typeof data?.playerName==='string'&&data.playerName.trim()?data.playerName.trim().slice(0,24):DEFAULT_PLAYER_NAME} +function currentPlayerId(){return typeof data?.cloudProfile?.playerId==='string'?data.cloudProfile.playerId:null} +const STARTER_SEED=0x51a7f00d; +const STARTER_PUZZLE=Object.freeze({"g":[[0,0,"W"],[0,1,"N"],[1,4,"E"],[0,4,"E"],[4,0,"W"],[4,3,"S"],[2,4,"E"],[4,4,"E"],[4,1,"S"],[4,2,"S"]],"n":[[1,0,3],[1,2,2],[2,0,3],[4,4,2],[3,1,2]],"valid":[[0,0],[0,1],[0,2],[0,3],[0,4],[1,0],[1,1],[1,2],[1,3],[1,4],[2,0],[2,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[3,3],[3,4],[4,0],[4,1],[4,2],[4,3],[4,4]],"bounds":{"w":5,"h":5},"axis":"MIX","solution":[{"startGate":0,"endGate":1,"cells":[[0,0],[1,0],[1,1],[0,1]]},{"startGate":2,"endGate":3,"cells":[[1,4],[1,3],[1,2],[0,2],[0,3],[0,4]]},{"startGate":4,"endGate":5,"cells":[[4,0],[3,0],[2,0],[2,1],[2,2],[2,3],[3,3],[4,3]]},{"startGate":6,"endGate":7,"cells":[[2,4],[3,4],[4,4]]},{"startGate":8,"endGate":9,"cells":[[4,1],[3,1],[3,2],[4,2]]}],"level":1,"maxTurns":3,"totalTurns":12,"style":"variable-world-gates","complexity":{"rating":1,"score":1,"areaUnits":1,"lineCount":5,"totalTurns":12,"avgTurns":2.4,"maxTurns":3,"avgLength":5,"maxLength":8},"difficulty":1,"regionalTarget":1}); +const SCORE_VERSION=6,STORE_PRICE_VERSION=1,MIN_STORE_ITEM_PRICE=3000,MIN_CURSOR_PRICE=500,MAX_FACE_CURSOR_PRICE=50000,FLAG_CURSOR_BASE_PRICE=10000,OECD_FLAG_CURSOR_BASE_PRICE=20000,STORE_CHANCE=1/30,DRAG_FLAG_CURSOR_SIZE=12.4,DRAG_FLAG_CLIP_RADIUS=5.8,MINIMAP_LONG_LINE=1024,MINIMAP_VIEW_CHUNKS_X=42; +const MIN_CAMERA_SCALE=.08,SCORE_LENS_ZOOM_THRESHOLD=.72,OVERVIEW_ZOOM_THRESHOLD=.54,SOUND_GAIN_MULTIPLIER=3.6,UNIQUE_SOLUTION_MIN_LEVEL=6; +const SPECIAL_CELL_MIN_LEVEL=5,SPECIAL_CELL_DEBUG_ALL_LEVELS=false; +const DRAG_EDGE_MARGIN=76,DRAG_EDGE_MAX_SPEED=.58; +const POINTER_SNAP_THRESHOLD=CELL*.45,POINTER_DOMINANT_RATIO=1.25; +const TIME_ATTACK_MINUTES=Object.freeze([3,5,10]); +const REACTION_EMOJIS=Object.freeze(['👍','👉🏻','🙏','🧠','🎉']); +const TIME_ATTACK_COOLDOWN_MINUTES=Object.freeze({3:30,5:50,10:100}); +const TIME_ATTACK_TIERS=Object.freeze([ + Object.freeze({score:1000,multiplier:3}), + Object.freeze({score:500,multiplier:2.5}), + Object.freeze({score:250,multiplier:2}), + Object.freeze({score:100,multiplier:1.5}), + Object.freeze({score:0,multiplier:1.25}) +]); +const STORE_ITEM_BASE=Object.freeze([ + Object.freeze({id:'score-lens',name:'ジェムレンズ',cost:200000,scoreLens:true,icon:'▦',effectLabel:'予想報酬表示 ON / OFF',toast:'予想報酬表示を切り替え',description:'未クリア盤面の予想報酬表示を切り替えます。'}) +]); + +const YELLOW_FACE_CURSOR_SOURCE=`1F600|grinning face +1F603|grinning face with big eyes +1F604|grinning face with smiling eyes +1F601|beaming face with smiling eyes +1F606|grinning squinting face +1F605|grinning face with sweat +1F923|rolling on the floor laughing +1F602|face with tears of joy +1F642|slightly smiling face +1F643|upside-down face +1FAE0|melting face +1F609|winking face +1F60A|smiling face with smiling eyes +1F607|smiling face with halo +1F970|smiling face with hearts +1F60D|smiling face with heart-eyes +1F929|star-struck +1F618|face blowing a kiss +1F617|kissing face +263A FE0F|smiling face +1F61A|kissing face with closed eyes +1F619|kissing face with smiling eyes +1F972|smiling face with tear +1F60B|face savoring food +1F61B|face with tongue +1F61C|winking face with tongue +1F92A|zany face +1F61D|squinting face with tongue +1F911|money-mouth face +1F917|smiling face with open hands +1F92D|face with hand over mouth +1FAE2|face with open eyes and hand over mouth +1FAE3|face with peeking eye +1F92B|shushing face +1F914|thinking face +1FAE1|saluting face +1F910|zipper-mouth face +1F928|face with raised eyebrow +1F610|neutral face +1F611|expressionless face +1F636|face without mouth +1FAE5|dotted line face +1F636 200D 1F32B FE0F|face in clouds +1F60F|smirking face +1F612|unamused face +1F644|face with rolling eyes +1F62C|grimacing face +1F62E 200D 1F4A8|face exhaling +1F925|lying face +1FAE8|shaking face +1F642 200D 2194 FE0F|head shaking horizontally +1F642 200D 2195 FE0F|head shaking vertically +1F60C|relieved face +1F614|pensive face +1F62A|sleepy face +1F924|drooling face +1F634|sleeping face +1FAE9|face with bags under eyes +1F637|face with medical mask +1F912|face with thermometer +1F915|face with head-bandage +1F927|sneezing face +1F974|woozy face +1F635|face with crossed-out eyes +1F635 200D 1F4AB|face with spiral eyes +1F92F|exploding head +1F920|cowboy hat face +1F973|partying face +1F978|disguised face +1F60E|smiling face with sunglasses +1F913|nerd face +1F9D0|face with monocle +1F615|confused face +1FAE4|face with diagonal mouth +1F61F|worried face +1F641|slightly frowning face +2639 FE0F|frowning face +1F62E|face with open mouth +1F62F|hushed face +1F632|astonished face +1F633|flushed face +1FAEA|distorted face +1F97A|pleading face +1F979|face holding back tears +1F626|frowning face with open mouth +1F627|anguished face +1F628|fearful face +1F630|anxious face with sweat +1F625|sad but relieved face +1F622|crying face +1F62D|loudly crying face +1F631|face screaming in fear +1F616|confounded face +1F623|persevering face +1F61E|disappointed face +1F613|downcast face with sweat +1F629|weary face +1F62B|tired face +1F971|yawning face +1F624|face with steam from nose +1F620|angry face`; +function titleEmojiName(name){return name.replace(/\b[a-z]/g,letter=>letter.toUpperCase())} +const YELLOW_FACE_CURSOR_ITEMS=Object.freeze(YELLOW_FACE_CURSOR_SOURCE.split('\n').map((row,index)=>{ + const[codes,name]=row.split('|'),codeKey=codes.toLowerCase().replace(/\s+/g,'-'), + emoji=String.fromCodePoint(...codes.split(' ').map(code=>Number.parseInt(code,16))), + cost=MIN_CURSOR_PRICE+((index*61+37)%100)*MIN_CURSOR_PRICE; + return Object.freeze({ + id:`cursor-face-${codeKey}`,name:titleEmojiName(name),cost, + cursorStyle:`emoji-${codeKey}`,cursorEmoji:emoji,icon:emoji, + effectLabel:'\u7d75\u6587\u5b57\u30ab\u30fc\u30bd\u30eb',toast:titleEmojiName(name), + description:'\u9ec4\u8272\u3044\u8868\u60c5\u7d75\u6587\u5b57\u306e\u30ab\u30fc\u30bd\u30eb\u3067\u3059\u3002' + }); +})); +const FLAG_REGION_CODES=`AC AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CP CQ CR CU CV CW CX CY CZ DE DG DJ DK DM DO DZ EA EC EE EG EH ER ES ET EU FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU IC ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TA TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM UN US UY UZ VA VC VE VG VI VN VU WF WS XK YE YT ZA ZM ZW`.split(' '); +const OECD_FLAG_CODES=new Set('AU AT BE CA CL CO CR CZ DK EE FI FR DE GR HU IS IE IL IT JP KR LV LT LU MX NL NZ NO PL PT SK SI ES SE CH TR GB US'.split(' ')); +function regionFlagEmoji(code){return String.fromCodePoint(...[...code].map(letter=>0x1f1e6+letter.charCodeAt(0)-65))} +function subdivisionFlagEmoji(tag){return String.fromCodePoint(0x1f3f4,...[...tag].map(letter=>0xe0000+letter.charCodeAt(0)),0xe007f)} +function emojiAssetKey(emoji){return[...emoji].map(character=>character.codePointAt(0).toString(16)).join('-')} +const FLAG_CURSOR_ITEMS=Object.freeze([ + ...FLAG_REGION_CODES.map(code=>({key:code.toLowerCase(),name:code,emoji:regionFlagEmoji(code),cost:OECD_FLAG_CODES.has(code)?OECD_FLAG_CURSOR_BASE_PRICE:FLAG_CURSOR_BASE_PRICE})), + ...[['gbeng','England'],['gbsct','Scotland'],['gbwls','Wales']].map(([key,name])=>({key,name,emoji:subdivisionFlagEmoji(key),cost:FLAG_CURSOR_BASE_PRICE})) +].map(source=>Object.freeze({ + id:`cursor-flag-${source.key}`,name:source.name,cost:source.cost, + cursorStyle:`flag-${source.key}`,cursorEmoji:source.emoji,icon:source.emoji,flagAsset:`assets/flags/${emojiAssetKey(source.emoji)}.svg`, + effectLabel:'国旗カーソル',toast:source.emoji,description:'' +}))); +const CURSOR_ITEMS=Object.freeze([...YELLOW_FACE_CURSOR_ITEMS,...FLAG_CURSOR_ITEMS]); +const STORE_ITEMS=Object.freeze([...STORE_ITEM_BASE,...CURSOR_ITEMS]); +const STORE_ITEM_CATALOG=new Map(STORE_ITEMS.map(item=>[item.id,item])); +const CURSOR_ITEM_BY_STYLE=new Map(CURSOR_ITEMS.map(item=>[item.cursorStyle,item])); +const STORE_ITEM_IDS=new Set(STORE_ITEMS.map(item=>item.id)); +const LINE_COLORS=['#5fd8ff','#ff709f','#ffd45f','#72e38f','#a98cff','#ff915f','#5f8dff','#ff5f62','#42d6c4','#e66cff']; +const DIFF_BACKGROUND_EASY=['#102b32','#17413f'],DIFF_BACKGROUND_HARD=['#43151f','#68202b']; +const REGIONS=[{name:'\u68ee',accent:'#72e38f'},{name:'\u6f6e',accent:'#5fd8ff'},{name:'\u5bb5',accent:'#a98cff'},{name:'\u706b',accent:'#ff915f'},{name:'\u865a',accent:'#ff709f'}]; +const OPP={N:'S',S:'N',W:'E',E:'W'}; +const SIDE_D={N:[-1,0],S:[1,0],W:[0,-1],E:[0,1]}; +const storageKey=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:compact`, + recoveryStorageKey=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:recovery`, + recoveryJournalKey=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:journal`, + recoveryJournalPrefix=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:journal:`, + storageRevisionKey=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:revision`, + worldEpochStorageKey=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:epoch`, + syncStorageKey=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:signal`, + worldDbName=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:world`, + worldLockName=`bend-field:v${STORAGE_SCHEMA}:${WORLD_GENERATION}:world`, + CHAIN_WINDOW=120000,SAVE_DELAY=180; +const mirrorChunkPrefix=`${storageKey}:chunk:`; +const MIRROR_IDLE_DELAY=5000,MIRROR_IDLE_TIMEOUT=15000,MIRROR_CHUNK_BYTES=64*1024,MIRROR_CHUNK_FORMAT='bend-field-chunked-v1',MINIMAP_CACHE_OVERSCAN_CHUNKS=4,LOD_CHANGES_PER_PASS=1,BOARD_RENDERS_PER_FRAME=6,HYDRATE_CONCURRENCY=4; +const RETIRED_WORLD_STORES=Object.freeze([ + Object.freeze({schema:31,generation:'v47-field-reset-20260728-bugfix'}), + Object.freeze({schema:30,generation:'v47-field-reset-20260727'}) +]); +const world=document.querySelector('#world'),viewport=document.querySelector('#viewport'),topbar=document.querySelector('#topbar'),overviewCanvas=document.querySelector('#overviewCanvas'),noiseCanvas=document.querySelector('#noiseCanvas'), + solvedCountEl=document.querySelector('#solvedCount'),scoreCountEl=document.querySelector('#scoreCount'),selectedInfo=document.querySelector('#selectedInfo'), + worldCountEl=document.querySelector('#worldCount'),fpsCounter=document.querySelector('#fpsCounter'),playerNameBtn=document.querySelector('#playerNameBtn'),settingsBtn=document.querySelector('#settingsBtn'), + minimapCanvas=document.querySelector('#minimapCanvas'),minimapStatus=document.querySelector('#minimapStatus'),clearFeed=document.querySelector('#clearFeed'),presenceCanvas=document.querySelector('#presenceCanvas'),reactionCanvas=document.querySelector('#reactionCanvas'),reactionRadial=document.querySelector('#reactionRadial'), + saveStatusEl=document.querySelector('#saveStatus'),statusPanel=document.querySelector('#statusPanel'), + statusMessage=document.querySelector('#statusMessage'),specialTooltip=document.querySelector('#specialTooltip'); +const timeAttackSuggestion=document.querySelector('#timeAttackSuggestion'); +const cloudApiEnabled=document.querySelector('meta[name="bend-field-cloud-api"]')?.content==='on'; +const sessionId=globalThis.crypto?.randomUUID?.()||`session-${Date.now()}-${Math.random().toString(36).slice(2)}`; +const sessionRecoveryJournalKey=recoveryJournalPrefix+sessionId; +const loadNotices=[]; +let volatileRecovery=null,storageAccessError=null,serverClockOffset=null; +const sessionWallClock=Date.now(),sessionMonotonic=globalThis.performance?.now?.()||0; +function deepClone(value){if(globalThis.structuredClone)try{return globalThis.structuredClone(value)}catch(_){}return JSON.parse(JSON.stringify(value))} +function trustedNow(){ + const monotonic=sessionWallClock+((globalThis.performance?.now?.()||sessionMonotonic)-sessionMonotonic),wall=Date.now(),serverWall=Number.isFinite(serverClockOffset)?wall+serverClockOffset:null,floor=Number.isFinite(data?.clockFloor)?data.clockFloor:0; + if(serverWall!=null)return Math.max(floor,serverWall); + return Math.max(floor,Math.abs(wall-monotonic)>CLOCK_DRIFT_LIMIT?monotonic:wall); +} +function safeLocalGet(key){try{return localStorage.getItem(key)}catch(error){storageAccessError=error;return null}} +function safeLocalSet(key,value){try{localStorage.setItem(key,value);storageAccessError=null;return true}catch(error){storageAccessError=error;return false}} +function safeLocalRemove(key){try{localStorage.removeItem(key);return true}catch(error){storageAccessError=error;return false}} +const UI_SETTINGS_KEY='bend-field:ui-settings:v1'; +function normalizeUiSettings(raw){return{lightweightRendering:raw?.lightweightRendering===true,soundEnabled:raw?.soundEnabled!==false}} +function readUiSettings(){try{return normalizeUiSettings(JSON.parse(safeLocalGet(UI_SETTINGS_KEY)||'{}'))}catch(_){return normalizeUiSettings(null)}} +let uiSettings=readUiSettings(),autoReducedEffects=INTERACTION_SCHEDULER_VARIANT==='reduced'; +function applyUiSettings(){document.body.dataset.interactionScheduler=INTERACTION_SCHEDULER_VARIANT;document.body.classList.toggle('lightweight-rendering',uiSettings.lightweightRendering);document.body.classList.toggle('reduced-effects',uiSettings.lightweightRendering||autoReducedEffects);if(uiSettings.lightweightRendering)pauseNoiseBackground?.();else scheduleNoiseBackground?.(true)} +function persistUiSettings(){safeLocalSet(UI_SETTINGS_KEY,JSON.stringify(uiSettings));applyUiSettings()} +function safeLocalKeys(prefix=''){ + try{const keys=[];for(let index=0;index=0?value.rev:0,revAuthor:typeof value?.revAuthor==='string'?value.revAuthor:typeof value?.author==='string'?value.author:''}} +function compareRevisionVersions(left,right){const a=revisionVersion(left),b=revisionVersion(right);return a.rev!==b.rev?a.rev-b.rev:a.revAuthor.localeCompare(b.revAuthor)} +function newerRevisionValue(left,right){return compareRevisionVersions(left,right)>=0?left:right} +function clearRecoveryJournalKeys(){let ok=safeLocalRemove(recoveryJournalKey);for(const key of safeLocalKeys(recoveryJournalPrefix))ok=safeLocalRemove(key)&&ok;return ok} +function deleteRetiredWorldData(){ + for(const retired of RETIRED_WORLD_STORES){ + const prefix=`bend-field:v${retired.schema}:${retired.generation}`; + for(const suffix of[':compact',':recovery',':journal',':revision',':signal',':world:lease'])safeLocalRemove(prefix+suffix); + for(const key of safeLocalKeys(`${prefix}:journal:`))safeLocalRemove(key); + if(typeof indexedDB!=='undefined')try{indexedDB.deleteDatabase(`${prefix}:world`)}catch(_){} + } +} +function manhattan(a,b){return Math.abs(a[0]-b[0])+Math.abs(a[1]-b[1])} +function isPlainObject(value){if(!value||typeof value!=='object'||Array.isArray(value))return false;const proto=Object.getPrototypeOf(value);return proto===Object.prototype||proto===null} +function timeAttackMultiplier(score){return TIME_ATTACK_TIERS.find(tier=>score>=tier.score)?.multiplier||1.25} +function normalizeTimeAttackRun(raw){ + if(!isPlainObject(raw)||!TIME_ATTACK_MINUTES.includes(raw.durationMinutes)||!Number.isFinite(raw.startedAt)||raw.startedAt<=0)return null; + const durationMinutes=raw.durationMinutes,startedAt=raw.startedAt,endsAt=startedAt+durationMinutes*60000, + collected=Number.isSafeInteger(raw.collected)&&raw.collected>0?Math.min(raw.collected,MAX_SCORE):0, + baseCollected=Number.isSafeInteger(raw.baseCollected)&&raw.baseCollected>0?Math.min(raw.baseCollected,MAX_SCORE):0; + return{ + id:typeof raw.id==='string'&&raw.id?raw.id.slice(0,64):`run-${Math.round(startedAt)}`, + durationMinutes,startedAt,endsAt, + collected,baseCollected,rewardPipelineVersion:2,scoreVersion:SCORE_VERSION, + solves:Number.isSafeInteger(raw.solves)&&raw.solves>0?Math.min(raw.solves,MAX_SOLVES):0 + }; +} +function normalizeTimeAttackResult(raw){ + if(!isPlainObject(raw)||!TIME_ATTACK_MINUTES.includes(raw.durationMinutes))return null; + const collected=Number.isSafeInteger(raw.collected)&&raw.collected>0?Math.min(raw.collected,MAX_SCORE):0, + solves=Number.isSafeInteger(raw.solves)&&raw.solves>0?Math.min(raw.solves,MAX_SOLVES):0, + baseCollected=Number.isSafeInteger(raw.baseCollected)&&raw.baseCollected>0?Math.min(raw.baseCollected,MAX_SCORE):0, + multiplier=baseCollected?collected/baseCollected:1,total=collected,bonus=0,modifierGain=Math.max(0,collected-baseCollected); + return{ + id:typeof raw.id==='string'&&raw.id?raw.id.slice(0,64):`result-${Math.round(raw.completedAt||Date.now())}`, + durationMinutes:raw.durationMinutes,collected,baseCollected,rewardPipelineVersion:2,scoreVersion:SCORE_VERSION,solves,multiplier,total,bonus,modifierGain, + completedAt:Number.isFinite(raw.completedAt)&&raw.completedAt>0?raw.completedAt:0 + }; +} +function normalizeTimeAttackCooldowns(raw){ + const cooldowns={3:0,5:0,10:0}; + if(isPlainObject(raw))for(const minutes of TIME_ATTACK_MINUTES){ + const value=raw[minutes]; + if(Number.isFinite(value)&&value>0)cooldowns[minutes]=value; + } + return cooldowns; +} +function normalizeBonusEvents(raw){ + const events={}; + if(isPlainObject(raw))for(const[id,value]of Object.entries(raw).slice(0,100000))if(typeof id==='string'&&id.length<=128&&Number.isSafeInteger(value)&&value>0)events[id]=Math.min(value,MAX_SCORE); + return events; +} +function bonusEventTotal(events=data?.bonusEvents){let total=0;for(const value of Object.values(events||{}))total=Math.min(MAX_SCORE,total+value);return total} +function normalizeCloudPending(raw){ + const ids=value=>[...new Set((Array.isArray(value)?value:[]).filter(id=>typeof id==='string'&&/^B(?:0|[1-9]\d*)$/.test(id)).slice(0,MAX_BOARDS))]; + return{metaIds:ids(raw?.metaIds),stateIds:ids(raw?.stateIds),deleted:ids(raw?.deleted),globalChanged:raw?.globalChanged===true}; +} +function normalizePlayerPurchases(raw){ + const purchases=[],seen=new Set(); + for(const source of Array.isArray(raw)?raw:[]){ + const purchaseId=typeof source?.purchaseId==='string'?source.purchaseId.slice(0,64):'',boardId=typeof source?.boardId==='string'?source.boardId.slice(0,32):'',item=storeItem(source?.itemId); + if(!purchaseId||!boardId||!item||seen.has(purchaseId))continue; + const boughtAt=Number(source.boughtAt); + seen.add(purchaseId);purchases.push({purchaseId,boardId,itemId:item.id,buyer:typeof source.buyer==='string'&&source.buyer.trim()?source.buyer.trim().slice(0,32):DEFAULT_PLAYER_NAME,boughtAt:Number.isFinite(boughtAt)&&boughtAt>0?boughtAt:0,paidCost:Number.isSafeInteger(source.paidCost)&&source.paidCost>0?Math.min(source.paidCost,MAX_SCORE):item.cost}); + } + return purchases; +} +function defaultData(){return{schema:SAVE_SCHEMA,gameplayVersion:GAMEPLAY_DATA_VERSION,worldGeneration:WORLD_GENERATION,worldEpoch:null,globalRev:0,globalRevAuthor:'',metas:{},states:{},quarantine:{},bonusEvents:{},specialMechanicsSeen:[],clockFloor:0,cloudProfile:null,cloudRevision:0,cloudSyncPaused:false,cloudPending:normalizeCloudPending(null),playerName:null,playerPurchases:[],playerEarnedScore:0,lastReaction:'👍',worldFeedRevision:0,nextId:1,solved:0,score:0,bonusScore:0,bonusScoreVersion:SCORE_VERSION,lastSolveAt:0,timeAttack:null,timeAttackRev:0,timeAttackCooldowns:{3:0,5:0,10:0},lastTimeAttack:null,timeAttackSuggestionsDisabled:false,cursorStyle:'default',scoreLensEnabled:false,debugAllItems:false,cameraAnchor:null,selectedBoardId:null,updatedAt:0}} +function validChunkShape(chunks){ + if(!Array.isArray(chunks)||chunks.length<1||chunks.length>50)return false; + const set=new Set(); + for(const q of chunks){ + if(!Array.isArray(q)||q.length!==2||!Number.isInteger(q[0])||!Number.isInteger(q[1])||q[0]<0||q[1]<0||q[0]>64||q[1]>64)return false; + const key=key2(q[0],q[1]);if(set.has(key))return false;set.add(key); + } + if(Math.min(...chunks.map(q=>q[0]))!==0||Math.min(...chunks.map(q=>q[1]))!==0)return false; + const reached=new Set([key2(...chunks[0])]),queue=[chunks[0]]; + while(queue.length){const[x,y]=queue.shift();for(const[dx,dy]of[[1,0],[-1,0],[0,1],[0,-1]]){const key=key2(x+dx,y+dy);if(set.has(key)&&!reached.has(key)){reached.add(key);queue.push([x+dx,y+dy])}}} + return reached.size===set.size; +} +function normalizePath(raw){ + if(!isPlainObject(raw)||!Number.isInteger(raw.startGate)||!Array.isArray(raw.cells)||raw.cells.length<1||raw.cells.length>5000)return null; + if(raw.endGate!=null&&!Number.isInteger(raw.endGate))return null; + const cells=[]; + for(const cell of raw.cells){ + if(!Array.isArray(cell)||cell.length!==2||!Number.isInteger(cell[0])||!Number.isInteger(cell[1]))return null; + cells.push([cell[0],cell[1]]); + } + const color=value=>Number.isInteger(value)&&value>=0&&value{ + if(!Array.isArray(cell)||cell.length!==2||!Number.isInteger(cell[0])||!Number.isInteger(cell[1]))return null; + const key=ckey(cell[0],cell[1]); + return validSet.has(key)?[cell[0],cell[1]]:null; + }; + for(const rawCell of Array.isArray(raw?.crossings)?raw.crossings:[]){ + const cell=readCell(rawCell);if(!cell)continue;const key=ckey(...cell);if(reserved.has(key))continue; + reserved.add(key);result.crossings.push(cell); + } + for(const rawPair of Array.isArray(raw?.warps)?raw.warps:[]){ + const a=readCell(rawPair?.a??rawPair?.[0]),b=readCell(rawPair?.b??rawPair?.[1]); + if(!a||!b||sameCell(a,b)||manhattan(a,b)===1)continue; + const ak=ckey(...a),bk=ckey(...b);if(reserved.has(ak)||reserved.has(bk))continue; + reserved.add(ak);reserved.add(bk);result.warps.push({a,b}); + } + for(const rawLock of Array.isArray(raw?.locks)?raw.locks:[]){ + const keyCell=readCell(rawLock?.key),door=readCell(rawLock?.door); + if(!keyCell||!door||sameCell(keyCell,door))continue; + const kk=ckey(...keyCell),dk=ckey(...door);if(reserved.has(kk)||reserved.has(dk))continue; + reserved.add(kk);reserved.add(dk);result.locks.push({key:keyCell,door}); + } + return result; +} +function normalizeStore(raw,paths){ + if(!isPlainObject(raw)||!Number.isInteger(raw.pathIndex)||raw.pathIndex<0||raw.pathIndex>=paths.length)return null; + const path=paths[raw.pathIndex]; + if(!path?.cells?.length)return null; + const purchases=[],seen=new Set(); + for(const purchase of Array.isArray(raw.purchases)?raw.purchases:[]){ + const id=typeof purchase==='string'?purchase:purchase?.id; + if(!STORE_ITEM_IDS.has(id)||seen.has(id))continue; const item=storeItem(id); + seen.add(id);purchases.push({ + id, + buyer:typeof purchase?.buyer==='string'&&purchase.buyer.trim()?purchase.buyer.trim().slice(0,32):DEFAULT_PLAYER_NAME, + boughtAt:Number.isFinite(purchase?.boughtAt)&&purchase.boughtAt>0?purchase.boughtAt:0, + paidCost:Number.isSafeInteger(purchase?.paidCost)&&purchase.paidCost>0?Math.min(purchase.paidCost,MAX_SCORE):item.cost + }); + } + const rawBonus=Number.isSafeInteger(raw.bonus)&&raw.bonus>0?Math.min(raw.bonus,MAX_SCORE):0; + const itemIds=normalizeStoreItemIds(raw.itemIds); + return{ + owner:typeof raw.owner==='string'&&raw.owner.trim()?raw.owner.trim().slice(0,32):DEFAULT_PLAYER_NAME, + pathIndex:raw.pathIndex, + cellIndex:Math.max(0,Math.min(path.cells.length-1,Number.isInteger(raw.cellIndex)?raw.cellIndex:Math.floor(path.cells.length/2))), + openedAt:Number.isFinite(raw.openedAt)&&raw.openedAt>0?raw.openedAt:0, + priceVersion:Number.isInteger(raw.priceVersion)&&raw.priceVersion>0?raw.priceVersion:STORE_PRICE_VERSION, + priceCoefficient:Number.isFinite(raw.priceCoefficient)&&raw.priceCoefficient>=.8&&raw.priceCoefficient<=1.2?raw.priceCoefficient:null, + bonus:rawBonus, + bonusVersion:SCORE_VERSION, + itemIds, + purchases + }; +} +function normalizeState(raw){ + const state={paths:[],specialProgress:{crossings:[]},solved:false,expanded:false,expansionRetryRound:0,solvedBy:null,solvedById:null,solvedAt:null,scoreAwarded:0,scoreVersion:SCORE_VERSION,rewardIdentity:null,rewardCoefficient:null,store:null,rev:0,revAuthor:''}; + if(!isPlainObject(raw))return state; + if(Array.isArray(raw.paths))for(const candidate of raw.paths.slice(0,MAX_PATHS_PER_BOARD)){const path=normalizePath(candidate);if(path)state.paths.push(path)} + state.specialProgress=normalizeSpecialProgress(raw.specialProgress); + state.solved=raw.solved===true; + state.expanded=raw.expanded===true; + state.expansionRetryRound=Number.isInteger(raw.expansionRetryRound)&&raw.expansionRetryRound>=0?Math.min(raw.expansionRetryRound,1000000):0; + if(typeof raw.solvedBy==='string'&&raw.solvedBy.trim())state.solvedBy=raw.solvedBy.trim().slice(0,32); + if(typeof raw.solvedById==='string'&&/^[a-f0-9]{16,64}$/i.test(raw.solvedById))state.solvedById=raw.solvedById.toLowerCase(); + if(state.solved&&!state.solvedBy)state.solvedBy=LEGACY_LOCAL_SOLVER; + if(state.solved&&Number.isFinite(raw.solvedAt)&&raw.solvedAt>0)state.solvedAt=raw.solvedAt; + if(state.solved&&Number.isSafeInteger(raw.scoreAwarded)&&raw.scoreAwarded>0){ + state.scoreAwarded=Math.min(raw.scoreAwarded,MAX_SCORE); + } + if(state.solved&&Number.isFinite(raw.rewardIdentity)&&raw.rewardIdentity>=0)state.rewardIdentity=raw.rewardIdentity>>>0; + if(state.solved&&Number.isFinite(raw.rewardCoefficient)&&raw.rewardCoefficient>=.8&&raw.rewardCoefficient<=1.2)state.rewardCoefficient=raw.rewardCoefficient; + if(state.solved)state.store=normalizeStore(raw.store,state.paths); + state.rev=Number.isFinite(raw.rev)&&raw.rev>=0?raw.rev:0;state.revAuthor=typeof raw.revAuthor==='string'?raw.revAuthor.slice(0,128):''; + return state; +} +function repairWarpNumberClues(puzzle,sourceNumbers){ + const warps=puzzle?.specialCells?.warps||[];if(!warps.length)return{numbers:sourceNumbers,repaired:false,maxTurns:puzzle.maxTurns||0,totalTurns:puzzle.totalTurns||0}; + const reserved=new Set();for(const cell of puzzle.specialCells.crossings||[])reserved.add(ckey(...cell));for(const pair of warps){reserved.add(ckey(...pair.a));reserved.add(ckey(...pair.b))}for(const lock of puzzle.specialCells.locks||[]){reserved.add(ckey(...lock.key));reserved.add(ckey(...lock.door))} + const used=new Set(),numbers=[];let maxTurns=0,totalTurns=0; + for(const path of puzzle.solution){ + const analysis=AppLogic.analyzePathTurns(path,puzzle.g,warps,true);if(!analysis.count||!analysis.cells.length)return null; + const bendKeys=new Set(analysis.cells.map(cell=>ckey(...cell))),existing=(sourceNumbers||[]).find(number=>path.cells.some(cell=>sameCell(cell,number))&&bendKeys.has(ckey(number[0],number[1]))&&!used.has(ckey(number[0],number[1]))), + pool=analysis.cells.filter(cell=>!reserved.has(ckey(...cell))&&!used.has(ckey(...cell))),fallback=analysis.cells.filter(cell=>!used.has(ckey(...cell))),cell=existing?[existing[0],existing[1]]:(pool[0]||fallback[0]); + if(!cell)return null;used.add(ckey(...cell));numbers.push([cell[0],cell[1],analysis.count]);maxTurns=Math.max(maxTurns,analysis.count);totalTurns+=analysis.count; + } + const repaired=JSON.stringify(numbers)!==JSON.stringify(sourceNumbers||[])||maxTurns!==(puzzle.maxTurns||0)||totalTurns!==(puzzle.totalTurns||0); + return{numbers,repaired,maxTurns,totalTurns}; +} +function normalizeStoredPuzzle(raw,chunks,targetLevel){ + if(!isPlainObject(raw)||!Array.isArray(raw.g)||!Array.isArray(raw.n)||!Array.isArray(raw.valid)||!Array.isArray(raw.solution))return null; + const maxRows=Math.max(...chunks.map(([,y])=>(y+1)*5)),maxCols=Math.max(...chunks.map(([x])=>(x+1)*5)),validSet=new Set(),valid=[]; + for(const cell of raw.valid){if(!Array.isArray(cell)||cell.length!==2||!Number.isInteger(cell[0])||!Number.isInteger(cell[1])||cell[0]<0||cell[1]<0||cell[0]>=maxRows||cell[1]>=maxCols)return null;const key=ckey(cell[0],cell[1]);if(validSet.has(key))return null;validSet.add(key);valid.push([cell[0],cell[1]])} + const obstacleSet=new Set(),obstacles=[]; + for(const cell of Array.isArray(raw.obstacles)?raw.obstacles:[]){if(!Array.isArray(cell)||cell.length!==2||!Number.isInteger(cell[0])||!Number.isInteger(cell[1])||cell[0]<0||cell[1]<0||cell[0]>=maxRows||cell[1]>=maxCols)return null;const key=ckey(cell[0],cell[1]);if(validSet.has(key)||obstacleSet.has(key))return null;obstacleSet.add(key);obstacles.push([cell[0],cell[1]])} + if(valid.length+obstacles.length!==chunks.length*25)return null; + const gates=[]; + for(const gate of raw.g){if(!Array.isArray(gate)||gate.length!==3||!Number.isInteger(gate[0])||!Number.isInteger(gate[1])||!validSet.has(ckey(gate[0],gate[1]))||!['N','S','W','E'].includes(gate[2]))return null;gates.push([gate[0],gate[1],gate[2]])} + if(!gates.length||gates.length%2!==0)return null; + const numbers=[]; + for(const number of raw.n){if(!Array.isArray(number)||number.length!==3||!Number.isInteger(number[0])||!Number.isInteger(number[1])||!Number.isInteger(number[2])||number[2]<0||number[2]>512||!validSet.has(ckey(number[0],number[1])))return null;numbers.push([number[0],number[1],number[2]])} + const specialCells=normalizeSpecialCells(raw.specialCells,validSet),warpPairs=new Map(); + for(const pair of specialCells.warps){warpPairs.set(ckey(...pair.a),ckey(...pair.b));warpPairs.set(ckey(...pair.b),ckey(...pair.a))} + const solution=[]; + for(const path of raw.solution){ + const normalized=normalizePath(path); + if(!normalized||normalized.endGate==null||normalized.startGate<0||normalized.endGate<0||normalized.startGate>=gates.length||normalized.endGate>=gates.length||normalized.cells.some(cell=>!validSet.has(ckey(...cell))))return null; + for(let index=1;indexckey(...cell))); + for(const path of solution)for(const cell of path.cells){const key=ckey(...cell),count=covered.get(key)||0;if(count&&(!crossingSet.has(key)||count>=2))return null;covered.set(key,count+1)} + if(covered.size!==valid.length)return null; + const bounds=isPlainObject(raw.bounds)&&Number.isInteger(raw.bounds.w)&&Number.isInteger(raw.bounds.h)?{w:raw.bounds.w,h:raw.bounds.h}:{w:maxCols,h:maxRows}; + const puzzle={g:gates,n:numbers,valid,obstacles,specialCells,bounds,axis:typeof raw.axis==='string'?raw.axis:'MIX',solution,level:Number.isInteger(raw.level)?raw.level:targetLevel,maxTurns:Number.isFinite(raw.maxTurns)?raw.maxTurns:0,totalTurns:Number.isFinite(raw.totalTurns)?raw.totalTurns:0,style:typeof raw.style==='string'?raw.style:'stored-procedural',regionalTarget:Number.isInteger(raw.regionalTarget)?raw.regionalTarget:targetLevel, + uniqueness:isPlainObject(raw.uniqueness)?deepClone(raw.uniqueness):null,solutionQuality:isPlainObject(raw.solutionQuality)?deepClone(raw.solutionQuality):null,interactionBurden:isPlainObject(raw.interactionBurden)?deepClone(raw.interactionBurden):null,regionalFallback:raw.regionalFallback===true,regionalOutlier:raw.regionalOutlier===true}; + for(const cell of specialCells.crossings)if(covered.get(ckey(...cell))!==2||!crossingStateAtCell({paths:solution},puzzle,cell))return null; + const warpClues=repairWarpNumberClues(puzzle,numbers);if(!warpClues)return null; + puzzle.n=warpClues.numbers;puzzle.maxTurns=warpClues.maxTurns;puzzle.totalTurns=warpClues.totalTurns;if(warpClues.repaired)puzzle._warpTurnsRepaired=true; + try{ + puzzle.difficulty=solverDifficulty(puzzle,targetLevel);puzzle.level=puzzle.difficulty;puzzle.complexity=isPlainObject(raw.complexity)?deepClone(raw.complexity):null;return puzzle + }catch(_){return null} +} +function puzzleForStorage(puzzle){ + return puzzle?{g:puzzle.g,n:puzzle.n,valid:puzzle.valid,obstacles:Array.isArray(puzzle.obstacles)?puzzle.obstacles:[],specialCells:puzzle.specialCells||{crossings:[],warps:[],locks:[]},bounds:puzzle.bounds,axis:puzzle.axis,solution:puzzle.solution,level:puzzle.level,difficulty:puzzle.difficulty,maxTurns:puzzle.maxTurns,totalTurns:puzzle.totalTurns,style:puzzle.style,complexity:puzzle.complexity,regionalTarget:puzzle.regionalTarget,regionalFallback:puzzle.regionalFallback===true,regionalOutlier:puzzle.regionalOutlier===true,uniqueness:puzzle.uniqueness||null,solutionQuality:puzzle.solutionQuality||null,interactionBurden:puzzle.interactionBurden||null}:null; +} +function normalizeMeta(id,raw){ + if(!/^B(?:0|[1-9]\d*)$/.test(id)||!isPlainObject(raw))return null; + const x=raw.x,y=raw.y,chunks=raw.chunks; + if(!Number.isSafeInteger(x)||!Number.isSafeInteger(y)||Math.abs(x)>MAX_WORLD_COORD||Math.abs(y)>MAX_WORLD_COORD||!validChunkShape(chunks))return null; + const target=Number.isInteger(raw.targetLevel)?raw.targetLevel:raw.level; + const level=Number.isInteger(raw.level)?raw.level:target; + if(!Number.isInteger(target)||target<1||target>10||!Number.isInteger(level)||level<1||level>10||!Number.isFinite(raw.seed))return null; + const sealedSides=[...new Set((Array.isArray(raw.sealedSides)?raw.sealedSides:[]).filter(side=>['N','S','W','E'].includes(side)))],puzzle=normalizeStoredPuzzle(raw.puzzle,chunks,target); + if(!puzzle)return null; + if(puzzle._warpTurnsRepaired)delete puzzle._warpTurnsRepaired; + return{id,x,y,chunks:chunks.map(q=>[q[0],q[1]]),level:puzzle.difficulty,targetLevel:target,seed:raw.seed>>>0,axis:puzzle.axis,entrySide:['N','S','W','E'].includes(raw.entrySide)?raw.entrySide:null,sealedSides,puzzle,generatorVersion:Number.isInteger(raw.generatorVersion)?raw.generatorVersion:GENERATOR_VERSION,rev:Number.isFinite(raw.rev)&&raw.rev>=0?raw.rev:0,revAuthor:typeof raw.revAuthor==='string'?raw.revAuthor.slice(0,128):''}; +} +function mechanicTypesForPuzzle(puzzle){ + const types=[];if(puzzle?.specialCells?.warps?.length)types.push('warp');if(puzzle?.specialCells?.locks?.length)types.push('lock');if(puzzle?.specialCells?.crossings?.length)types.push('crossing');return types; +} +function normalizeSnapshot(raw,{quiet=false}={}){ + const clean=defaultData(); + if(!isPlainObject(raw)){if(!quiet)loadNotices.push('\u4fdd\u5b58\u30c7\u30fc\u30bf\u3092\u521d\u671f\u5316\u3057\u307e\u3057\u305f\u3002');return clean} + if(raw.schema!==SAVE_SCHEMA||raw.worldGeneration!==WORLD_GENERATION){ + if(!quiet)loadNotices.push(`\u3053\u306e\u4fdd\u5b58\u5f62\u5f0f\u306b\u306f\u5bfe\u5fdc\u3057\u3066\u3044\u307e\u305b\u3093\uff1a${String(raw.schema)}`); + return clean; + } + const rawMetas=isPlainObject(raw.metas)?raw.metas:{}; + const metas=[]; + const metaEntries=Object.entries(rawMetas); + if(metaEntries.length>MAX_BOARDS&&!quiet)loadNotices.push('\u76e4\u9762\u6570\u304c\u4e0a\u9650\u3092\u8d85\u3048\u305f\u305f\u3081\u3001\u8d85\u904e\u5206\u3092\u9664\u5916\u3057\u307e\u3057\u305f\u3002'); + for(const[id,value]of metaEntries.slice(0,MAX_BOARDS)){const meta=normalizeMeta(id,value);if(meta)metas.push(meta);else if(!quiet)loadNotices.push(`\u7834\u640d\u3057\u305f\u76e4\u9762\u3092\u9664\u5916\u3057\u307e\u3057\u305f\uff1a${id}`)} + metas.sort((a,b)=>a.id==='B0'?-1:b.id==='B0'?1:(b.rev-a.rev)||a.id.localeCompare(b.id,undefined,{numeric:true})); + const occupied=new Set(); + for(const meta of metas){ + if(meta.chunks.some(([dx,dy])=>occupied.has(key2(meta.x+dx,meta.y+dy)))){if(!quiet)loadNotices.push(`\u91cd\u8907\u3057\u305f\u76e4\u9762\u3092\u9664\u5916\u3057\u307e\u3057\u305f\uff1a${meta.id}`);continue} + clean.metas[meta.id]=meta; + for(const[dx,dy]of meta.chunks)occupied.add(key2(meta.x+dx,meta.y+dy)); + } + const rawStates=isPlainObject(raw.states)?raw.states:{}; + for(const id of Object.keys(clean.metas))clean.states[id]=normalizeState(rawStates[id]); + const maxId=Math.max(0,...Object.keys(clean.metas).map(id=>Number(id.slice(1))||0)); + clean.nextId=Math.max(maxId+1,Number.isInteger(raw.nextId)&&raw.nextId>0?raw.nextId:1); + clean.lastSolveAt=Number.isFinite(raw.lastSolveAt)&&raw.lastSolveAt>0?raw.lastSolveAt:0; + clean.bonusEvents=normalizeBonusEvents(raw.bonusEvents);clean.bonusScore=bonusEventTotal(clean.bonusEvents);clean.bonusScoreVersion=SCORE_VERSION; + clean.timeAttack=normalizeTimeAttackRun(raw.timeAttack); + clean.timeAttackRev=Number.isFinite(raw.timeAttackRev)&&raw.timeAttackRev>=0?raw.timeAttackRev:0; + clean.timeAttackCooldowns=normalizeTimeAttackCooldowns(raw.timeAttackCooldowns); + clean.timeAttackSuggestionsDisabled=raw.timeAttackSuggestionsDisabled===true; + clean.cursorStyle=typeof raw.cursorStyle==='string'?raw.cursorStyle.slice(0,32):'default'; + clean.scoreLensEnabled=raw.scoreLensEnabled===true; + clean.debugAllItems=raw.debugAllItems===true; + const seen=new Set(Array.isArray(raw.specialMechanicsSeen)?raw.specialMechanicsSeen.filter(type=>['warp','lock','crossing'].includes(type)):[]); + for(const meta of Object.values(clean.metas))for(const type of mechanicTypesForPuzzle(meta.puzzle))seen.add(type); + clean.specialMechanicsSeen=[...seen].sort(); + clean.lastTimeAttack=normalizeTimeAttackResult(raw.lastTimeAttack); + clean.quarantine=isPlainObject(raw.quarantine)?raw.quarantine:{}; + clean.gameplayVersion=GAMEPLAY_DATA_VERSION; + clean.clockFloor=Number.isFinite(raw.clockFloor)&&raw.clockFloor>0?raw.clockFloor:0; + clean.cloudProfile=isPlainObject(raw.cloudProfile)&&typeof raw.cloudProfile.playerId==='string'&&typeof raw.cloudProfile.token==='string'?{playerId:raw.cloudProfile.playerId.slice(0,64),token:raw.cloudProfile.token.slice(0,128)}:null; + clean.cloudRevision=Number.isSafeInteger(raw.cloudRevision)&&raw.cloudRevision>=0?raw.cloudRevision:0; + clean.cloudSyncPaused=raw.cloudSyncPaused===true; + clean.playerName=typeof raw.playerName==='string'&&raw.playerName.trim()?raw.playerName.trim().slice(0,24):null; + clean.playerPurchases=normalizePlayerPurchases(raw.playerPurchases);clean.playerEarnedScore=Number.isSafeInteger(raw.playerEarnedScore)&&raw.playerEarnedScore>=0?raw.playerEarnedScore:0; + clean.lastReaction=REACTION_EMOJIS.includes(raw.lastReaction)?raw.lastReaction:'👍'; + clean.worldFeedRevision=Number.isSafeInteger(raw.worldFeedRevision)&&raw.worldFeedRevision>=0?raw.worldFeedRevision:0; + clean.cloudPending=normalizeCloudPending(raw.cloudPending); + clean.worldEpoch=validWorldEpoch(raw.worldEpoch)?raw.worldEpoch:null; + clean.globalRev=Number.isFinite(raw.globalRev)&&raw.globalRev>=0?raw.globalRev:0; + clean.globalRevAuthor=typeof raw.globalRevAuthor==='string'?raw.globalRevAuthor.slice(0,128):''; + clean.cameraAnchor=isPlainObject(raw.cameraAnchor)&&Number.isFinite(raw.cameraAnchor.centerX)&&Number.isFinite(raw.cameraAnchor.centerY)&&Number.isFinite(raw.cameraAnchor.scale)?{centerX:raw.cameraAnchor.centerX,centerY:raw.cameraAnchor.centerY,scale:Math.max(MIN_CAMERA_SCALE,Math.min(1.8,raw.cameraAnchor.scale))}:null; + clean.selectedBoardId=typeof raw.selectedBoardId==='string'&&/^B(?:0|[1-9]\d*)$/.test(raw.selectedBoardId)?raw.selectedBoardId:null; + clean.updatedAt=Number.isFinite(raw.updatedAt)&&raw.updatedAt>0?raw.updatedAt:0; + return clean; +} +function preserveRecovery(rawText,reason){ + if(!rawText)return false; + const envelope={savedAt:new Date().toISOString(),reason,raw:rawText};volatileRecovery=envelope;void persistRecoveryEnvelope(envelope); + const json=JSON.stringify(envelope); + if(safeLocalSet(recoveryStorageKey,json))return true; + try{sessionStorage.setItem(recoveryStorageKey,json);return true}catch(_){return false} +} +async function preserveRecoveryDurably(rawText,reason){ + if(!rawText)throw new Error('Recovery snapshot is empty'); + const envelope={savedAt:new Date().toISOString(),reason,raw:rawText},json=JSON.stringify(envelope);volatileRecovery=envelope; + let browserCopy=safeLocalSet(recoveryStorageKey,json);if(!browserCopy)try{sessionStorage.setItem(recoveryStorageKey,json);browserCopy=true}catch(_){} + const databaseCopy=await persistRecoveryEnvelope(envelope);if(!browserCopy&&!databaseCopy)throw new Error('\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002'); + return{browserCopy,databaseCopy,envelope}; +} +function mirrorChunkKey(generation,index){return`${mirrorChunkPrefix}${generation}:${index}`} +function parseMirrorManifest(raw){ + if(!raw||raw.charCodeAt(0)!==123)return null; + try{const value=JSON.parse(raw);return value?.format===MIRROR_CHUNK_FORMAT&&typeof value.generation==='string'&&Number.isSafeInteger(value.chunks)&&value.chunks>=0?value:null}catch(_){return null} +} +function readCompactMirrorRaw(){ + const manifestRaw=safeLocalGet(storageKey);if(manifestRaw==null)return null; + const manifest=parseMirrorManifest(manifestRaw);if(!manifest)return manifestRaw; + const maxChunks=Math.ceil(LOCAL_MIRROR_MAX_BYTES/MIRROR_CHUNK_BYTES)+8; + if(manifest.chunks>maxChunks||!Number.isFinite(manifest.bytes)||manifest.bytes<0||manifest.bytes>LOCAL_MIRROR_MAX_BYTES)return null; + const chunks=[];let bytes=0; + for(let index=0;indexLOCAL_MIRROR_MAX_BYTES)return null} + if(bytes!==manifest.bytes)return null;return chunks.join(''); +} +function clearCompactMirror(){let ok=safeLocalRemove(storageKey);for(const key of safeLocalKeys(mirrorChunkPrefix))ok=safeLocalRemove(key)&&ok;return ok} +function readInitialData(){ + const raw=readCompactMirrorRaw(); + if(raw!=null){ + if(raw.length>MAX_IMPORT_BYTES){preserveRecovery(raw,'Save exceeded safe import size');loadNotices.push('\u4fdd\u5b58\u30c7\u30fc\u30bf\u304c\u8aad\u307f\u8fbc\u307f\u4e0a\u9650\u3092\u8d85\u3048\u3066\u3044\u307e\u3059\u3002');return defaultData()} + try{ + const noticeCount=loadNotices.length,snapshot=normalizeSnapshot(JSON.parse(raw)); + if(loadNotices.length>noticeCount)preserveRecovery(raw,'Schema recovery'); + return snapshot; + }catch(error){preserveRecovery(raw,error?.message||'JSON parse error');loadNotices.push('\u4fdd\u5b58\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u3081\u306a\u3044\u305f\u3081\u3001\u65b0\u3057\u3044\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u958b\u59cb\u3057\u307e\u3059\u3002');return defaultData()} + } + if(storageAccessError)loadNotices.push('\u30d6\u30e9\u30a6\u30b6\u306e\u4fdd\u5b58\u6a5f\u80fd\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002'); + return defaultData(); +} +const indexedDbExpectedAtStartup=typeof indexedDB!=='undefined'; +let worldDbPromise=null,idbAvailable=indexedDbExpectedAtStartup,idbHealth=indexedDbExpectedAtStartup?'unverified':'unsupported',worldDbAbandoned=false,startupCloudPending=null,startupCloudOutboxLoaded=false,startupWorldEpoch=null,startupWorldControl=null,activeStorageFormat=FIELD_STORAGE_FORMAT,fieldIndexComplete=true,fieldIndexExpectedCount=0,fieldIndexLoadedCount=0,fieldIndexAfterNumber=-1,fieldIndexScanPromise=null,cloudOutboxReady=!cloudApiEnabled; +function requestValue(request){return new Promise((resolve,reject)=>{request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error||new Error('\u4fdd\u5b58\u30c7\u30fc\u30bf\u306e\u8aad\u307f\u53d6\u308a\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002'))})} +function transactionDone(tx){return new Promise((resolve,reject)=>{tx.oncomplete=()=>resolve();tx.onerror=()=>reject(tx.error||new Error('\u4fdd\u5b58\u51e6\u7406\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002'));tx.onabort=()=>reject(tx.error||new Error('\u4fdd\u5b58\u51e6\u7406\u304c\u4e2d\u65ad\u3055\u308c\u307e\u3057\u305f\u3002'))})} +function openWorldDb(){ + if(!idbAvailable||worldDbAbandoned)return Promise.reject(new Error('\u30d6\u30e9\u30a6\u30b6\u306e\u4fdd\u5b58\u6a5f\u80fd\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002')); + if(worldDbPromise)return worldDbPromise; + worldDbPromise=new Promise((resolve,reject)=>{ + const request=indexedDB.open(worldDbName,IDB_LAYOUT_VERSION);let blockedTimer=0,abandoned=false; + request.onupgradeneeded=event=>{ + const db=request.result; + for(const retired of['metas','states','global','recovery','tombstones','outbox'])if(db.objectStoreNames.contains(retired))db.deleteObjectStore(retired); + const controlStore=db.objectStoreNames.contains('control')?request.transaction.objectStore('control'):db.createObjectStore('control',{keyPath:'key'}), + worldsStore=db.objectStoreNames.contains('worlds')?request.transaction.objectStore('worlds'):db.createObjectStore('worlds',{keyPath:'epoch'}); + const ensureEpochStore=(name,keyPath)=>{ + const store=db.objectStoreNames.contains(name)?request.transaction.objectStore(name):db.createObjectStore(name,{keyPath}); + if(!store.indexNames.contains('epoch'))store.createIndex('epoch','epoch',{unique:false}); + return store; + }; + const boardIndexStore=ensureEpochStore('boardIndex',['epoch','id']);if(!boardIndexStore.indexNames.contains('epochNumber'))boardIndexStore.createIndex('epochNumber',['epoch','number'],{unique:true}); + ensureEpochStore('boardPuzzles',['epoch','id']);ensureEpochStore('boardStates',['epoch','id']); + ensureEpochStore('tombstonesV2',['epoch','id']);ensureEpochStore('recoveryV2',['epoch','key']);ensureEpochStore('outboxV2',['epoch','key']); + if(event.oldVersion{clearTimeout(blockedTimer);const db=request.result;if(abandoned||worldDbAbandoned){db.close();reject(new Error('IndexedDB startup was abandoned'));return}db.onversionchange=()=>db.close();idbHealth='ready';resolve(db)}; + request.onerror=()=>{clearTimeout(blockedTimer);reject(request.error||new Error('\u4fdd\u5b58\u30c7\u30fc\u30bf\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f\u3002'))}; + request.onblocked=()=>{console.warn('BEND FIELD: IndexedDB upgrade is blocked by another tab');if(!blockedTimer)blockedTimer=setTimeout(()=>{abandoned=true;reject(new Error('IndexedDB upgrade remained blocked by another tab'))},IDB_STARTUP_TIMEOUT)}; + }).catch(error=>{idbAvailable=false;idbHealth=worldDbAbandoned?'uncertain':'failed';cloudOutboxReady=!cloudApiEnabled;worldDbPromise=null;throw error}); + return worldDbPromise; +} +function cloudPendingFromOutboxRows(rows){ + const pending={metaIds:[],stateIds:[],deleted:[],globalChanged:false}; + for(const row of rows||[]){if(row?.type==='global')pending.globalChanged=true;else if(row?.id&&row.type==='meta')pending.metaIds.push(row.id);else if(row?.id&&row.type==='state')pending.stateIds.push(row.id);else if(row?.id&&row.type==='deleted')pending.deleted.push(row.id)} + return normalizeCloudPending(pending); +} +function epochKeyRange(epoch){return IDBKeyRange.bound([epoch,''],[epoch,'\uffff'])} +async function readActiveWorldControl(db=null){ + const target=db||await openWorldDb(),tx=target.transaction('control','readonly'),done=transactionDone(tx),row=await requestValue(tx.objectStore('control').get('active'));await done; + return row||null; +} +async function rollbackUnverifiedWorld(db,control){ + if(!control||control.activationVerified!==false||!validWorldEpoch(control.previousEpoch))return null; + const tx=db.transaction(['control','worlds'],'readwrite'),done=transactionDone(tx),controlStore=tx.objectStore('control'),worldsStore=tx.objectStore('worlds'), + [current,previous]=await Promise.all([requestValue(worldsStore.get(control.activeEpoch)),requestValue(worldsStore.get(control.previousEpoch))]); + if(!previous){try{tx.abort()}catch(_){}await done.catch(()=>{});return null} + if(current){current.status='garbage';worldsStore.put(current)}previous.status='active';worldsStore.put(previous); + const restored={key:'active',activeFormat:FIELD_STORAGE_FORMAT,activeEpoch:previous.epoch,activationId:`rollback:${sessionId}:${Date.now()}`,activationVerified:true,switchedAt:trustedNow()}; + controlStore.put(restored);await done;rememberWorldEpoch(previous.epoch);return restored; +} +async function loadV2SnapshotFromDb(db,control){ + const epoch=control?.activeEpoch;if(!validWorldEpoch(epoch))throw new Error('The active field epoch is invalid.'); + const tx=db.transaction(['worlds','boardIndex','outboxV2','recoveryV2','tombstonesV2'],'readonly'),done=transactionDone(tx), + [world,firstIndexRows,outboxRows,recoveryRows,tombstoneRows]=await Promise.all([ + requestValue(tx.objectStore('worlds').get(epoch)),requestValue(tx.objectStore('boardIndex').index('epochNumber').getAll(IDBKeyRange.bound([epoch,0],[epoch,Number.MAX_SAFE_INTEGER]),512)), + requestValue(tx.objectStore('outboxV2').getAll(epochKeyRange(epoch))),requestValue(tx.objectStore('recoveryV2').getAll(epochKeyRange(epoch))), + requestValue(tx.objectStore('tombstonesV2').getAll(epochKeyRange(epoch))) + ]);await done; + if(!world||world.status!=='active'&&world.status!=='rollback')throw new Error('The active field record is missing.'); + const selectedId=typeof world.global?.selectedBoardId==='string'&&/^B(?:0|[1-9]\d*)$/.test(world.global.selectedBoardId)?world.global.selectedBoardId:null,indexRows=[...firstIndexRows]; + if(selectedId&&!firstIndexRows.some(row=>row.id===selectedId)){ + const selectedTx=db.transaction('boardIndex','readonly'),selectedDone=transactionDone(selectedTx),selectedIndex=await requestValue(selectedTx.objectStore('boardIndex').get([epoch,selectedId]));await selectedDone;if(selectedIndex)indexRows.push(selectedIndex); + } + const tombstones=new Map(tombstoneRows.map(row=>[row.id,row])), + snapshot=normalizeSnapshot({...(world.global||{}),schema:SAVE_SCHEMA,worldGeneration:WORLD_GENERATION,worldEpoch:epoch,metas:{},states:{}},{quiet:true}); + boardIndexSummaries.clear(); + for(const index of indexRows){ + const tombstone=tombstones.get(index.id); + if(tombstone&&compareRevisionVersions(tombstone,{rev:Math.max(index.metaRev||0,index.stateRev||0),revAuthor:(index.metaRev||0)>=(index.stateRev||0)?index.metaRevAuthor||'':index.stateRevAuthor||''})>=0)continue; + if(!/^B(?:0|[1-9]\d*)$/.test(index.id)||!validChunkShape(index.chunks))throw new Error(`The stored field index is invalid for ${index.id}.`); + snapshot.metas[index.id]={id:index.id,x:index.x,y:index.y,chunks:index.chunks.map(chunk=>[chunk[0],chunk[1]]),level:index.level,targetLevel:index.targetLevel,seed:index.seed,axis:index.axis,entrySide:index.entrySide||null,sealedSides:[...(index.sealedSides||[])],puzzle:null,generatorVersion:GENERATOR_VERSION,rev:index.metaRev||0,revAuthor:index.metaRevAuthor||index.revAuthor||'',_summaryOnly:true}; + snapshot.states[index.id]=summaryStateFromIndex(index); + } + let maxId=0,scanAfterNumber=-1;for(const index of indexRows)maxId=Math.max(maxId,index.number||Number(index.id.slice(1))||0);for(const index of firstIndexRows)scanAfterNumber=Math.max(scanAfterNumber,index.number||Number(index.id.slice(1))||0); + snapshot.nextId=Math.max(snapshot.nextId||1,maxId+1);snapshot.solved=world.solvedCount||0;snapshot.score=world.score||0; + fieldIndexLoadedCount=Object.keys(snapshot.metas).length;fieldIndexExpectedCount=Math.max(fieldIndexLoadedCount,world.boardCount||0);fieldIndexAfterNumber=scanAfterNumber;fieldIndexComplete=fieldIndexLoadedCount>=fieldIndexExpectedCount; + startupCloudPending=cloudPendingFromOutboxRows(outboxRows);startupCloudOutboxLoaded=true;startupWorldEpoch=epoch;rememberWorldEpoch(epoch); + const coverage=new Map(),databaseJournals=[];for(const row of recoveryRows){if(typeof row?.key!=='string')continue;if(row.key.startsWith('covered:'))coverage.set(row.key.slice(8),row.value?.seq??-1);else if(row.key.startsWith('journal:')&&row.value)databaseJournals.push({...row.value,_dbKey:row.key})} + return{snapshot,coverage,tombstones,databaseJournals,worldEpoch:epoch}; +} +async function readV2IndexPage(epoch,afterNumber,limit=512){ + const db=await openWorldDb(),tx=db.transaction('boardIndex','readonly'),done=transactionDone(tx), + range=IDBKeyRange.bound([epoch,Math.max(0,afterNumber+1)],[epoch,Number.MAX_SAFE_INTEGER]), + rows=await requestValue(tx.objectStore('boardIndex').index('epochNumber').getAll(range,limit));await done;return rows; +} +function installV2IndexSummary(index){ + if(!index||!/^B(?:0|[1-9]\d*)$/.test(index.id)||!validChunkShape(index.chunks))throw new Error(`The stored field index is invalid for ${index?.id||'?'}.`); + if(data.metas[index.id])return false; + const meta={id:index.id,x:index.x,y:index.y,chunks:index.chunks.map(chunk=>[chunk[0],chunk[1]]),level:index.level,targetLevel:index.targetLevel,seed:index.seed,axis:index.axis,entrySide:index.entrySide||null,sealedSides:[...(index.sealedSides||[])],puzzle:null,generatorVersion:GENERATOR_VERSION,rev:index.metaRev||0,revAuthor:index.metaRevAuthor||index.revAuthor||'',_summaryOnly:true}, + state=summaryStateFromIndex(index); + data.metas[index.id]=meta;data.states[index.id]=state;fieldIndexLoadedCount++;normalizedStateObjects.add(state); + for(const[dx,dy]of meta.chunks){const key=key2(meta.x+dx,meta.y+dy),owner=occupancy.get(key);if(owner&&owner!==meta.id)throw new Error(`Stored field indexes overlap: ${owner} and ${meta.id}`);occupancy.set(key,meta.id)} + refreshClosedVoidAroundMeta(meta);worldGeometryRevision++;fieldBoundsCache=null;data.nextId=Math.max(data.nextId||1,(index.number||Number(index.id.slice(1))||0)+1);return true; +} +async function completeV2IndexScan(){ + if(fieldIndexComplete||activeStorageFormat!==FIELD_STORAGE_FORMAT)return 0; + if(fieldIndexScanPromise)return fieldIndexScanPromise; + fieldIndexScanPromise=(async()=>{ + let added=0,pages=0; + while(!fieldIndexComplete){ + const page=await readV2IndexPage(data.worldEpoch,fieldIndexAfterNumber,512);if(!page.length){fieldIndexComplete=fieldIndexLoadedCount>=fieldIndexExpectedCount;if(!fieldIndexComplete)throw new Error('The field index ended before its declared board count.');break} + for(const index of page){fieldIndexAfterNumber=Math.max(fieldIndexAfterNumber,index.number||Number(index.id.slice(1))||0);if(installV2IndexSummary(index))added++} + fieldIndexComplete=fieldIndexLoadedCount>=fieldIndexExpectedCount;pages++;worldCountEl.textContent=String(fieldIndexLoadedCount); + if(fieldIndexComplete||pages%8===0){scheduleMinimap(true);scheduleWorldOverview(true)} + if(!fieldIndexComplete)await new Promise(resolve=>setTimeout(resolve,0)); + } + if(fieldIndexComplete){orphanPruneDirty=true;invalidateEconomyCaches();statsDirty=true;pruneAndCount();invalidateWorldPresentation();perfCount('fieldIndexScans')} + return added; + })().finally(()=>{fieldIndexScanPromise=null}); + return fieldIndexScanPromise; +} +async function loadSnapshotFromDb(journals=[]){ + const db=await openWorldDb(),control=await readActiveWorldControl(db);startupWorldControl=control; + if(control?.activeFormat!==FIELD_STORAGE_FORMAT)throw new Error('この版では旧保存形式を読み込めません。'); + activeStorageFormat=FIELD_STORAGE_FORMAT; + try{return await loadV2SnapshotFromDb(db,control)} + catch(error){ + const restored=await rollbackUnverifiedWorld(db,control).catch(()=>null); + if(restored){startupWorldControl=restored;loadNotices.push('\u8aad\u307f\u8fbc\u307f\u306b\u5931\u6557\u3057\u305f\u305f\u3081\u3001\u524d\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u623b\u3057\u307e\u3057\u305f\u3002');return loadV2SnapshotFromDb(db,restored)} + throw error; + } +} +async function loadCloudOutboxFromDb(){ + if(!idbAvailable||!cloudApiEnabled)return normalizeCloudPending(null); + const epoch=validWorldEpoch(data?.worldEpoch)?data.worldEpoch:startupWorldEpoch;if(!validWorldEpoch(epoch))return normalizeCloudPending(null); + const db=await openWorldDb(),tx=db.transaction('outboxV2','readonly'),done=transactionDone(tx),rows=await requestValue(tx.objectStore('outboxV2').getAll(epochKeyRange(epoch)));await done; + return cloudPendingFromOutboxRows(rows); +} +function withStartupTimeout(promise,ms,label){ + let timer=null; + return Promise.race([ + Promise.resolve(promise), + new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error(label)),ms)}) + ]).finally(()=>clearTimeout(timer)); +} +let recoveryJournalsToCover=[]; +const recoveredDeletionTombstones=new Map(); +const startupRecoveredMetaIds=new Set(),startupRecoveredStateIds=new Set(),startupRecoveredDeletedIds=new Set(); +function setRecoveryJournalsToCover(journals){ + recoveryJournalsToCover=[...(journals||[])];recoveredDeletionTombstones.clear(); + for(const journal of recoveryJournalsToCover)for(const entry of journal.deleted||[]){const id=entry?.id,tombstone={id,rev:Number.isFinite(entry?.rev)?entry.rev:0,revAuthor:typeof entry?.revAuthor==='string'?entry.revAuthor:journal.sessionId},current=recoveredDeletionTombstones.get(id);if(id&&(!current||compareRevisionVersions(tombstone,current)>0))recoveredDeletionTombstones.set(id,tombstone)} +} +function readRecoveryJournals(){ + const keys=safeLocalKeys(recoveryJournalPrefix),journals=[]; + for(const key of new Set(keys)){const raw=safeLocalGet(key);if(!raw||raw.length>LOCAL_MIRROR_MAX_BYTES)continue;try{const journal=JSON.parse(raw);if(journal?.schema===SAVE_SCHEMA&&journal?.worldGeneration===WORLD_GENERATION&&Number.isFinite(journal.updatedAt))journals.push({...journal,_storageKey:key})}catch(_){}} + return journals.filter(journal=>typeof journal.sessionId==='string'&&Number.isSafeInteger(journal.seq)).sort((a,b)=>(a.updatedAt||0)-(b.updatedAt||0)); +} +function mergeRecoveryJournal(base,journal){ + if(!journal||!base)return base; + const global=isPlainObject(journal.global)?deepClone(journal.global):null,newerGlobal=(journal.updatedAt||0)>=(base.updatedAt||0), + merged={...deepClone(base),...(newerGlobal&&global?global:{}),metas:deepClone(base.metas||{}),states:deepClone(base.states||{})}; + if(!newerGlobal&&global){ + merged.nextId=Math.max(merged.nextId||1,global.nextId||1);merged.clockFloor=Math.max(merged.clockFloor||0,global.clockFloor||0);merged.cloudRevision=Math.max(merged.cloudRevision||0,global.cloudRevision||0); + merged.bonusEvents=deepClone(merged.bonusEvents||{});for(const[id,value]of Object.entries(global.bonusEvents||{}))merged.bonusEvents[id]=Math.max(merged.bonusEvents[id]||0,value||0); + const basePending=normalizeCloudPending(merged.cloudPending),journalPending=normalizeCloudPending(global.cloudPending);merged.cloudPending={metaIds:[...new Set([...basePending.metaIds,...journalPending.metaIds])],stateIds:[...new Set([...basePending.stateIds,...journalPending.stateIds])],deleted:[...new Set([...basePending.deleted,...journalPending.deleted])],globalChanged:basePending.globalChanged||journalPending.globalChanged}; + if((global.timeAttackRev||0)>(merged.timeAttackRev||0)){merged.timeAttack=global.timeAttack;merged.timeAttackRev=global.timeAttackRev;merged.timeAttackCooldowns=global.timeAttackCooldowns;merged.lastTimeAttack=global.lastTimeAttack||merged.lastTimeAttack} + } + for(const row of journal.metas||[]){ + if(!row?.id)continue;const current=merged.metas[row.id];if(!current||compareRevisionVersions(row,current)>=0)merged.metas[row.id]=deepClone(row); + } + for(const entry of journal.deleted||[]){const id=entry?.id,tombstone={rev:Number.isFinite(entry?.rev)?entry.rev:0,revAuthor:typeof entry?.revAuthor==='string'?entry.revAuthor:journal.sessionId},current=merged.metas[id],state=merged.states[id];if(id&&compareRevisionVersions(tombstone,current)>0&&compareRevisionVersions(tombstone,state)>0){delete merged.metas[id];delete merged.states[id]}} + for(const row of journal.states||[]){ + if(!row?.id||!merged.metas[row.id])continue;merged.states[row.id]=mergeBoardStates(merged.states[row.id],normalizeState(row.value)); + } + merged.updatedAt=Math.max(base.updatedAt||0,journal.updatedAt||0);return normalizeSnapshot(merged,{quiet:true}); +} +function mergeRecoveryJournals(base,journals){let merged=base;for(const journal of journals||[])merged=mergeRecoveryJournal(merged,journal);return merged} +function combineRecoveryJournals(...groups){ + const merged=new Map();for(const journal of groups.flat().filter(journal=>journal&&typeof journal.sessionId==='string'&&Number.isSafeInteger(journal.seq))){const key=`${journal.sessionId}:${journal.seq}`,current=merged.get(key);merged.set(key,current?{...current,...journal,_storageKey:current._storageKey||journal._storageKey,_dbKey:current._dbKey||journal._dbKey}:journal)} + return[...merged.values()].sort((a,b)=>(a.updatedAt||0)-(b.updatedAt||0)); +} +function mergeV2RecoveryJournals(base,journals){ + const merged=base; + for(const journal of journals||[]){ + if(journal.global&&(journal.updatedAt||0)>=(merged.updatedAt||0)){ + for(const key of['bonusEvents','clockFloor','cloudProfile','cloudRevision','cloudSyncPaused','nextId','lastSolveAt','timeAttack','timeAttackRev','timeAttackCooldowns','lastTimeAttack','specialMechanicsSeen','updatedAt'])if(Object.prototype.hasOwnProperty.call(journal.global,key))merged[key]=deepClone(journal.global[key]); + } + for(const row of journal.metas||[]){if(!row?.id)continue;const normalized=normalizeMeta(row.id,row),current=merged.metas[row.id];if(normalized&&(!current||compareRevisionVersions(normalized,current)>=0)){merged.metas[row.id]=normalized;startupRecoveredMetaIds.add(row.id)}} + for(const entry of journal.deleted||[]){const id=entry?.id;if(!id)continue;delete merged.metas[id];delete merged.states[id];startupRecoveredDeletedIds.add(id)} + for(const row of journal.states||[]){if(!row?.id||!merged.metas[row.id])continue;const normalized=normalizeState(row.value),current=merged.states[row.id];if(!current||compareRevisionVersions(normalized,current)>=0){merged.states[row.id]=normalized;startupRecoveredStateIds.add(row.id)}} + merged.updatedAt=Math.max(merged.updatedAt||0,journal.updatedAt||0); + } + return merged; +} +async function deleteRecoveryCoverageMarkers(sessionIds){ + const ids=[...new Set((sessionIds||[]).filter(Boolean))];if(!idbAvailable||!ids.length)return false; + try{ + if(!validWorldEpoch(data?.worldEpoch))return false; + const db=await openWorldDb(),tx=db.transaction('recoveryV2','readwrite'),done=transactionDone(tx),store=tx.objectStore('recoveryV2'); + for(const id of ids)store.delete([data.worldEpoch,`covered:${id}`]);await done;return true; + }catch(_){return false} +} +function attachWorldEpoch(snapshot,epoch){ + const target=snapshot||defaultData(),resolved=validWorldEpoch(epoch)?epoch:validWorldEpoch(target.worldEpoch)?target.worldEpoch:storedWorldEpoch()||createWorldEpoch(); + target.worldEpoch=resolved;rememberWorldEpoch(resolved);return target; +} +function abandonWorldDatabase(){ + if(!indexedDbExpectedAtStartup)return;worldDbAbandoned=true;idbAvailable=false;idbHealth='uncertain';cloudOutboxReady=!cloudApiEnabled;cloudAvailable=false; + const pending=worldDbPromise;if(pending)void pending.then(db=>{try{db.close()}catch(_){}},()=>{});worldDbPromise=null; +} +async function readInitialDataAsync(){ + const boardCount=snapshot=>Object.keys(snapshot?.metas||{}).length; + // The current IndexedDB format is authoritative. Per-session recovery + // journals cover writes that did not reach the active epoch. + const rawMirror=readInitialData(),allJournals=readRecoveryJournals(); + if(!idbAvailable){ + const epoch=rawMirror.worldEpoch||storedWorldEpoch()||createWorldEpoch(),journals=allJournals.filter(journal=>!journal.worldEpoch||journal.worldEpoch===epoch),fallbackMirror=attachWorldEpoch(mergeRecoveryJournals(rawMirror,journals),epoch),fallbackBoards=boardCount(fallbackMirror); + setRecoveryJournalsToCover(journals);return fallbackMirror + } + const dbLoad=loadSnapshotFromDb(allJournals); + try{ + const bundle=await withStartupTimeout(dbLoad,IDB_STARTUP_TIMEOUT,'IndexedDB startup timed out'),epoch=bundle?.worldEpoch||bundle?.snapshot?.worldEpoch||rawMirror.worldEpoch||storedWorldEpoch()||createWorldEpoch(), + journals=combineRecoveryJournals(allJournals,bundle?.databaseJournals||[]).filter(journal=>!journal.worldEpoch||journal.worldEpoch===epoch),coverage=bundle?.coverage||new Map(), + loaded=bundle&&Object.prototype.hasOwnProperty.call(bundle,'snapshot')?bundle.snapshot:bundle, + uncovered=journals.filter(journal=>!journal.sessionId||!Number.isSafeInteger(journal.seq)||(coverage.get(journal.sessionId)??-1)!journal.worldEpoch||journal.worldEpoch===epoch),fallbackMirror=attachWorldEpoch(mergeRecoveryJournals(rawMirror,journals),epoch),fallbackBoards=boardCount(fallbackMirror); + if(!fallbackBoards)try{const bundle=await withStartupTimeout(dbLoad,4000,'IndexedDB recovery timed out'),recovered=bundle&&Object.prototype.hasOwnProperty.call(bundle,'snapshot')?bundle.snapshot:bundle,dbJournals=combineRecoveryJournals(journals,bundle?.databaseJournals||[]);if(recovered){setRecoveryJournalsToCover(dbJournals);return attachWorldEpoch(mergeV2RecoveryJournals(recovered,dbJournals),bundle?.worldEpoch||epoch)}}catch(_){} + abandonWorldDatabase(); + loadNotices.push(fallbackBoards?'\u76f4\u8fd1\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u8aad\u307f\u8fbc\u307f\u307e\u3057\u305f\u3002':'\u4fdd\u5b58\u30c7\u30fc\u30bf\u3092\u78ba\u8a8d\u3067\u304d\u306a\u3044\u305f\u3081\u3001\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u3067\u8d77\u52d5\u3057\u307e\u3059\u3002'); + console.warn('BEND FIELD: IndexedDB startup fallback',error); + setRecoveryJournalsToCover(journals); + return fallbackMirror; + } +} +async function persistRecoveryEnvelope(envelope){ + if(!idbAvailable||!envelope||!validWorldEpoch(data?.worldEpoch))return false; + try{const db=await openWorldDb(),tx=db.transaction('recoveryV2','readwrite'),done=transactionDone(tx);tx.objectStore('recoveryV2').put({epoch:data.worldEpoch,key:'latest',value:envelope});await done;return true}catch(_){return false} +} +async function readRecoveryEnvelope(){ + if(idbAvailable&&validWorldEpoch(data?.worldEpoch))try{const db=await openWorldDb(),tx=db.transaction('recoveryV2','readonly'),done=transactionDone(tx),row=await requestValue(tx.objectStore('recoveryV2').get([data.worldEpoch,'latest']));await done;if(row?.value)return row.value}catch(_){} + const raw=safeLocalGet(recoveryStorageKey);return raw?parseJsonOrRaw(raw):volatileRecovery; +} +async function clearDatabaseWorld(newEpoch=createWorldEpoch()){ + if(!validWorldEpoch(newEpoch))throw new Error('Invalid world epoch');if(!idbAvailable){rememberWorldEpoch(newEpoch);return newEpoch} + const db=await openWorldDb(); + const expected=await activeWorldExpectation(),fresh=defaultData();fresh.worldEpoch=newEpoch;fresh.globalRev=nextRevision();fresh.globalRevAuthor=sessionId;fresh.updatedAt=trustedNow(); + try{ + const chunks=[[0,0]],puzzle=normalizeStoredPuzzle(deepClone(STARTER_PUZZLE),chunks,1);if(!puzzle)throw new Error('The origin puzzle could not be created.'); + const revision=nextRevision(),meta={id:'B0',x:0,y:0,chunks,level:puzzle.difficulty,targetLevel:1,seed:STARTER_SEED,axis:puzzle.axis,entrySide:null,sealedSides:[],puzzle,generatorVersion:GENERATOR_VERSION,rev:revision,revAuthor:sessionId},state=normalizeState(null);state.rev=nextRevision();state.revAuthor=sessionId;fresh.metas.B0=meta;fresh.states.B0=state;fresh.nextId=1; + await writeStagedBoardBatch(newEpoch,[{meta,state}]);const world={epoch:newEpoch,status:'ready',schema:SAVE_SCHEMA,worldGeneration:WORLD_GENERATION,global:globalForStorage(fresh),boardCount:1,solvedCount:0,score:0,bounds:{minX:0,minY:0,maxX:1,maxY:1},approximateBytes:JSON.stringify(meta).length+JSON.stringify(state).length,createdAt:trustedNow(),source:{kind:'reset'}}, + tx=db.transaction('worlds','readwrite'),done=transactionDone(tx);tx.objectStore('worlds').put(world);await done;await validateStagedWorldV2(newEpoch,{expectedBoardCount:1,strict:true});await activateReadyWorldV2({epoch:newEpoch,expected,world},{kind:'reset'});rememberWorldEpoch(newEpoch);return newEpoch; + }catch(error){await deleteV2Epoch(newEpoch).catch(()=>{});throw error} +} +let data=defaultData(); +const rendered=new Map(),staticRendered=new Map(); +const boardIndexSummaries=new Map(),hydratedBoardLru=new Map(); +let hydratedBoardBytes=0; +let occupancy=new Map(),closedVoidKeys=new Set(),activeBoard=null,hudBoardId=null,toastTimer=null,saveTimer=null,lastRevision=Date.now()*1000; +const dirtyMetaIds=new Set(),dirtyStateIds=new Set(),deletedBoardIds=new Set(),deletedBoardRevisions=new Map(),normalizedStateObjects=new WeakSet(); +const deletedBoardAuthors=new Map(); +const stateStatSignatures=new Map(),stateEconomySignatures=new Map(); +const cloudJournalMetaIds=new Set(),cloudJournalStateIds=new Set(),cloudJournalDeletedIds=new Set(),cloudOutboxDeleteKeys=new Set(); +let cloudJournalGlobalChanged=false,cloudJournalChangeSeq=0,cloudApplyingRemote=false,globalDirty=false,globalChangeSeq=0,worldSignalSeq=0,persistQueue=Promise.resolve(),lifecyclePersistenceSuppressed=false,statsDirty=true,cachedStats={solved:0,score:0,earned:0},inventoryCache=null,spentScoreCache=null; +let minimapFrame=0,minimapDirty=true,minimapLongSegments=0,minimapLastDraw=0,minimapDelayTimer=0,minimapRectCache=null; +let overviewFrame=0,overviewDirty=true,overviewLastDraw=0,overviewDelayTimer=0; +const minimapBase=document.createElement('canvas'),overviewBase=document.createElement('canvas'); +let overviewCache=null; +const OVERVIEW_CACHE_OVERSCAN_PX=192; +const adjacencyCache=new Map(); +let lineGraphRevision=0,lineGraphCacheRevision=-1,lineComponentCache=new Map(),lineWidthCache=new Map(),lineMinimapGeometryCache=new WeakMap(); +let minimapWorldRevision=0,worldGeometryRevision=0,minimapCache=null,minimapPointerState=null,fieldBoundsCache=null,orphanPruneDirty=true; +const perfSamples=new Map(),perfCounters=Object.create(null),perfGauges=Object.create(null); +function perfNow(){return globalThis.performance?.now?.()||Date.now()} +let perfResetAt=perfNow(); +function perfStart(){return perfNow()} +function perfObserve(name,value){ + if(!Number.isFinite(value)||value<0)return value; + const samples=perfSamples.get(name)||[];samples.push(value);if(samples.length>240)samples.splice(0,samples.length-240);perfSamples.set(name,samples);return value; +} +function perfEnd(name,started){ + return perfObserve(name,Math.max(0,perfNow()-started)); +} +function perfCount(name,amount=1){perfCounters[name]=(perfCounters[name]||0)+amount} +function perfGauge(name,value){perfGauges[name]=value} +function perfSnapshot(){ + const timings={}; + for(const[name,samples]of perfSamples){const sorted=[...samples].sort((a,b)=>a-b),pick=q=>sorted.length?sorted[Math.min(sorted.length-1,Math.floor((sorted.length-1)*q))]:0;timings[name]={count:sorted.length,p50:pick(.5),p95:pick(.95),max:sorted[sorted.length-1]||0}} + const elapsedSeconds=Math.max(.001,(perfNow()-perfResetAt)/1000),rates={};for(const[name,value]of Object.entries(perfCounters))rates[`${name}PerSecond`]=value/elapsedSeconds; + return{timings,counters:{...perfCounters},rates,gauges:{...perfGauges,interactionSchedulerVariant:INTERACTION_SCHEDULER_VARIANT,renderedBoards:rendered.size,staticBoards:staticRendered.size,domNodes:document.getElementsByTagName('*').length},elapsedSeconds,capturedAt:new Date().toISOString()}; +} +function resetPerf(){perfSamples.clear();for(const key of Object.keys(perfCounters))delete perfCounters[key];for(const key of Object.keys(perfGauges))delete perfGauges[key];interactionIntervals.splice(0);interactionActiveStartedAt=globalThis.document?.body?.classList?.contains?.('is-interacting')?perfNow():0;interactionLastFrame=0;interactionBestFrameGap=Infinity;interactionFrameOpportunities=0;interactionDroppedFrames=0;perfResetAt=perfNow()} +globalThis.BEND_PERF=Object.freeze({snapshot:perfSnapshot,reset:resetPerf}); +function observeSchedulerBattery(){ + if(typeof navigator.getBattery!=='function')return; + void navigator.getBattery().then(battery=>{ + const sample=()=>{perfGauge('batteryLevel',battery.level);perfGauge('batteryCharging',battery.charging?1:0);if(Number.isFinite(battery.dischargingTime))perfGauge('batteryDischargingTime',battery.dischargingTime)}; + sample();battery.addEventListener?.('levelchange',sample);battery.addEventListener?.('chargingchange',sample);battery.addEventListener?.('dischargingtimechange',sample); + }).catch(()=>{}); +} +observeSchedulerBattery(); +function longTaskOverlappedInteraction(entry){ + const start=entry.startTime,end=start+entry.duration;if(interactionActiveStartedAt&&end>=interactionActiveStartedAt)return true; + return interactionIntervals.some(([from,to])=>end>=from&&start<=to); +} +if(typeof PerformanceObserver!=='undefined')try{const observer=new PerformanceObserver(list=>{for(const entry of list.getEntries())if(entry.duration>=50){perfCount('longTasks');if(longTaskOverlappedInteraction(entry)){perfCount('interactionLongTasks');perfGauge('lastInteractionLongTaskMs',entry.duration)}perfGauge('lastLongTaskMs',entry.duration)}});observer.observe({type:'longtask',buffered:true})}catch(_){} +if(typeof PerformanceObserver!=='undefined'&&PerformanceObserver.supportedEntryTypes?.includes?.('long-animation-frame'))try{ + const observer=new PerformanceObserver(list=>{for(const entry of list.getEntries())if(longTaskOverlappedInteraction(entry)){const scripts=[...(entry.scripts||[])].sort((a,b)=>(b.duration||0)-(a.duration||0)).slice(0,5);perfGauge('lastInteractionLoafScripts',scripts.map(script=>`${script.sourceFunctionName||script.invoker||'anonymous'}@${script.sourceURL?.split('/').pop()||'?'}:${script.sourceCharPosition||0}:${(script.duration||0).toFixed(1)}`).join(','));perfGauge('lastInteractionLoafMs',entry.duration)}}); + observer.observe({type:'long-animation-frame',buffered:true}); +}catch(_){} +let fpsWindowStarted=perfNow(),fpsFrameCount=0,fpsLastBucket=-1,fpsLastValue=0,fpsLastFrameAt=0; +function markVisualFrame(timestamp=perfNow()){const bucket=Math.round(timestamp*10);if(bucket===fpsLastBucket)return;fpsLastBucket=bucket;fpsFrameCount++;fpsLastFrameAt=timestamp} +function refreshFpsCounter(){ + if(!fpsCounter)return;const now=perfNow(),elapsed=now-fpsWindowStarted;if(elapsed<450)return; + const interacting=globalThis.document?.body?.classList?.contains?.('is-interacting')===true,idle=!interacting&&fpsFrameCount<=2&&now-fpsLastFrameAt>180; + if(idle){fpsCounter.textContent='FPS 待機';fpsCounter.dataset.fps='idle'} + else{fpsLastValue=Math.max(0,Math.round(fpsFrameCount*1000/elapsed));fpsCounter.textContent=`FPS ${fpsLastValue}`;fpsCounter.dataset.fps=fpsLastValue>=55?'good':fpsLastValue>=40?'ok':'low'} + fpsFrameCount=0;fpsWindowStarted=now; +} +setInterval(refreshFpsCounter,500); +const reducedMotionQuery=globalThis.matchMedia?.('(prefers-reduced-motion: reduce)')||null; +let noiseTimer=0,noiseTick=0,noisePainted=false; +function noiseHash(value){value=Math.imul(value^(value>>>16),0x7feb352d);value=Math.imul(value^(value>>>15),0x846ca68b);return(value^(value>>>16))>>>0} +function paintNoiseBackground(staticFrame=false){ + if(!noiseCanvas||document.visibilityState==='hidden')return false; + const width=80,height=64;if(noiseCanvas.width!==width)noiseCanvas.width=width;if(noiseCanvas.height!==height)noiseCanvas.height=height; + const context=noiseCanvas.getContext('2d',{alpha:false}),image=context.createImageData(width,height),pixels=image.data, + phase=staticFrame?0:noiseTick++,seed=(data?.metas?.B0?.seed??STARTER_SEED)>>>0; + for(let y=0;y>2)+1,0x27d4eb2d)^Math.imul((y>>2)+1,0x165667b1)^Math.imul((phase>>1)+1,0x9e3779b9)), + value=((grain>>>25)+(slow>>>26))*.72,glow=((grain>>>29)===0?10:0); + pixels[index]=7+Math.min(18,value*.35);pixels[index+1]=12+Math.min(24,value*.52)+glow*.35;pixels[index+2]=17+Math.min(32,value*.72)+glow;pixels[index+3]=255; + } + context.putImageData(image,0,0);noisePainted=true;perfCount('noiseFrames');return true; +} +function pauseNoiseBackground(){if(noiseTimer){clearTimeout(noiseTimer);noiseTimer=0}} +function scheduleNoiseBackground(force=false){ + pauseNoiseBackground();if(document.visibilityState==='hidden')return;if(force||!noisePainted)paintNoiseBackground(true); +} +reducedMotionQuery?.addEventListener?.('change',()=>{noisePainted=false;scheduleNoiseBackground(true)}); +const interactionCommitAt=Object.create(null),interactionInputAt=Object.create(null); +const interactionIntervals=[]; +let interactionActiveStartedAt=0,interactionLastFrame=0,interactionFrames=0,interactionSlowFrames=0,interactionBestFrameGap=Infinity,interactionFrameOpportunities=0,interactionDroppedFrames=0,interactionQualityDowngradePending=false,wheelInteractionTimer=0,interactionSettleFrame=0; +function recordInteractionCommit(kind,timestamp=perfNow(),inputAt=timestamp,sampleInputAge=true){ + const previous=interactionCommitAt[kind]||0,previousInput=interactionInputAt[kind]||0,gap=previous?timestamp-previous:0,inputGap=previousInput&&Number.isFinite(inputAt)?inputAt-previousInput:0; + if(gap>0&&gap<=120&&inputGap<=25)perfObserve(`${kind}FrameGap`,gap);interactionCommitAt[kind]=timestamp;interactionInputAt[kind]=inputAt; + if(sampleInputAge&&Number.isFinite(inputAt)&&inputAt>0&&inputAt<=timestamp+1000)perfObserve(`${kind}InputAge`,Math.max(0,timestamp-inputAt)); +} +function refreshInteractionState(force=null){ + const drawingBoard=[...rendered.values()].find(board=>board.drawing?.pointerId!=null)||null,pendingClaimBoard=[...rendered.values()].find(board=>board.pendingClaimPointer)||null, + active=force==null?Boolean(pan||pinch||drawingBoard||pendingClaimBoard):Boolean(force); + const wasActive=document.body.classList.contains('is-interacting'); + document.body.classList.toggle('is-interacting',active); + document.body.classList.toggle('is-drawing',Boolean(drawingBoard)); + document.body.classList.toggle('is-claim-pending',Boolean(pendingClaimBoard)); + topbar?.classList.toggle('drawing-active',Boolean(drawingBoard));world?.classList.toggle('camera-interacting',Boolean(pan||pinch));noiseCanvas?.classList.toggle('interaction-muted',active); + const styledDrawingBoard=drawingBoard&&!usesLightweightDragOverlay(drawingBoard)?drawingBoard:null; + for(const board of rendered.values())board.card.classList.toggle('input-active',board===styledDrawingBoard); + if(active&&!wasActive)interactionActiveStartedAt=perfNow(); + else if(!active&&wasActive&&interactionActiveStartedAt){interactionIntervals.push([interactionActiveStartedAt,perfNow()]);if(interactionIntervals.length>64)interactionIntervals.splice(0,interactionIntervals.length-64);interactionActiveStartedAt=0} + if(!active){ + interactionLastFrame=0;interactionFrames=0;interactionSlowFrames=0;interactionBestFrameGap=Infinity;interactionFrameOpportunities=0;interactionDroppedFrames=0; + interactionCommitAt.camera=0;interactionCommitAt.pickupLogical=0;interactionCommitAt.pickupVisual=0;interactionCommitAt.pickupPreview=0; + interactionInputAt.camera=0;interactionInputAt.pickupLogical=0;interactionInputAt.pickupVisual=0;interactionInputAt.pickupPreview=0; + if(interactionQualityDowngradePending&&!autoReducedEffects){interactionQualityDowngradePending=false;autoReducedEffects=true;applyUiSettings()} + if(zoomDetailsDirty)updateZoomPresentation(true); + } +} +function scheduleInteractionSettlePresentation(){ + if(interactionSettleFrame)return; + interactionSettleFrame=requestAnimationFrame(()=>{ + interactionSettleFrame=0; + if(document.body.classList.contains('is-interacting')){scheduleInteractionSettlePresentation();return} + setPickupScenePresentation(null,false); + updateZoomPresentation(true);scheduleLodPass();scheduleMinimap();scheduleWorldOverview(); + }); +} +function observeInteractionFrame(timestamp=perfNow(),workDuration=0){ + if(interactionLastFrame){ + const gap=Math.max(0,timestamp-interactionLastFrame);perfObserve('interactionFrameGap',gap); + if(gap>=4&&gap<=50)interactionBestFrameGap=Math.min(interactionBestFrameGap,gap); + const interval=Number.isFinite(interactionBestFrameGap)?interactionBestFrameGap:16.67,opportunities=Math.max(1,Math.round(gap/interval)); + interactionFrameOpportunities+=opportunities;interactionDroppedFrames+=Math.max(0,opportunities-1);perfGauge('interactionDroppedFrameRatio',interactionDroppedFrames/Math.max(1,interactionFrameOpportunities)); + } + const observedInterval=Number.isFinite(interactionBestFrameGap)?interactionBestFrameGap:16.67,slowWorkThreshold=Math.max(6,observedInterval*.7); + interactionFrames++;if(workDuration>slowWorkThreshold)interactionSlowFrames++; + if(interactionFrames>=12){perfGauge('interactionSlowWorkRatio',interactionSlowFrames/interactionFrames);if(interactionSlowFrames>=6&&!autoReducedEffects)interactionQualityDowngradePending=true;interactionFrames=0;interactionSlowFrames=0} + interactionLastFrame=timestamp; +} +function pulseWheelInteraction(){ + refreshInteractionState(true);observeInteractionFrame();clearTimeout(wheelInteractionTimer);wheelInteractionTimer=setTimeout(()=>{refreshInteractionState();recordCameraAnchor();ensureBoards();repositionActiveBoardHud();scheduleMinimap(true);redrawOnlineLayersAfterCamera()},140); +} +function invalidateLineGraphCaches(boardId=null,pathIndex=null){ + lineGraphRevision++; + if(!boardId){lineGraphCacheRevision=lineGraphRevision;lineComponentCache=new Map();lineWidthCache=new Map();return} + const prefix=`${boardId}:`,components=new Set(); + if(Number.isInteger(pathIndex)){const component=lineComponentCache.get(linePathKey(boardId,pathIndex));if(component)components.add(component)} + else for(const[key,component]of lineComponentCache)if(key.startsWith(prefix))components.add(component); + const meta=data?.metas?.[boardId],paths=data?.states?.[boardId]?.paths||[],indexes=Number.isInteger(pathIndex)?[pathIndex]:paths.map((_,index)=>index); + if(meta?.puzzle)for(const index of indexes){const path=paths[index];if(!path)continue;for(const gateIndex of[...(path.detachedStart?[]:[path.startGate]),path.endGate]){if(gateIndex==null)continue;const hit=matchingNeighborGate(meta,gateIndex);if(!hit)continue;const neighborPaths=data.states?.[hit.meta.id]?.paths||[],neighborIndex=neighborPaths.findIndex(candidate=>pathUsesGate(candidate,hit.gateIndex)),component=neighborIndex>=0?lineComponentCache.get(linePathKey(hit.meta.id,neighborIndex)):null;if(component)components.add(component)}} + for(const component of components)for(const[meta,index]of component.members||[]){const key=linePathKey(meta.id,index);if(lineComponentCache.get(key)===component)lineComponentCache.delete(key);lineWidthCache.delete(key)} + if(Number.isInteger(pathIndex)){lineComponentCache.delete(linePathKey(boardId,pathIndex));lineWidthCache.delete(linePathKey(boardId,pathIndex))} + else for(const key of[...lineWidthCache.keys()])if(key.startsWith(prefix))lineWidthCache.delete(key); + lineGraphCacheRevision=lineGraphRevision; +} +function currentLineGraphCaches(){ + if(lineGraphCacheRevision!==lineGraphRevision){lineGraphCacheRevision=lineGraphRevision;lineComponentCache=new Map();lineWidthCache=new Map()} + return{components:lineComponentCache,widths:lineWidthCache,geometries:lineMinimapGeometryCache}; +} +function invalidateMinimapWorld(){minimapWorldRevision++;minimapDirty=true;minimapCache=null;scheduleMinimap()} +function invalidateWorldPresentation(){invalidateMinimapWorld();overviewDirty=true;overviewCache=null} +function svgEl(tag,attrs={}){const e=document.createElementNS('http://www.w3.org/2000/svg',tag);for(const[k,v]of Object.entries(attrs))e.setAttribute(k,v);return e} +let soundContext=null,lastStretchSoundAt=0; +function soundTone(frequency,duration=.08,{gain=.035,type='sine',slide=1,delay=0}={}){ + if(!uiSettings.soundEnabled)return; + try{ + const Audio=window.AudioContext||window.webkitAudioContext;if(!Audio)return; + soundContext=soundContext&&soundContext.state!=='closed'?soundContext:new Audio(); + if(soundContext.state==='suspended')void soundContext.resume(); + const now=soundContext.currentTime+delay,osc=soundContext.createOscillator(),amp=soundContext.createGain(); + osc.type=type;osc.frequency.setValueAtTime(frequency,now);osc.frequency.exponentialRampToValueAtTime(Math.max(40,frequency*slide),now+duration); + const outputGain=Math.min(.28,Math.max(.001,gain*SOUND_GAIN_MULTIPLIER)); + amp.gain.setValueAtTime(.0001,now);amp.gain.exponentialRampToValueAtTime(outputGain,now+.008);amp.gain.exponentialRampToValueAtTime(.0001,now+duration); + osc.connect(amp).connect(soundContext.destination);osc.start(now);osc.stop(now+duration+.02); + }catch(_){} +} +function playSound(kind){ + if(kind==='grab'){soundTone(190,.055,{gain:.028,type:'triangle',slide:1.18});soundTone(280,.045,{gain:.016,type:'sine',slide:.92,delay:.018})} + else if(kind==='stretch'){const now=performance.now();if(now-lastStretchSoundAt<48)return;lastStretchSoundAt=now;soundTone(145,.045,{gain:.014,type:'triangle',slide:1.08})} + else if(kind==='gate'){soundTone(210,.09,{gain:.035,type:'triangle',slide:1.7});soundTone(430,.08,{gain:.02,type:'sine',slide:1.22,delay:.045})} + else if(kind==='clear'){soundTone(260,.15,{gain:.04,type:'triangle',slide:1.65});soundTone(520,.18,{gain:.028,type:'sine',slide:1.45,delay:.08})} + else if(kind==='buy'){soundTone(330,.07,{gain:.03,type:'square',slide:1.22});soundTone(495,.09,{gain:.026,type:'triangle',slide:1.28,delay:.055})} + else if(kind==='reset'){soundTone(220,.09,{gain:.025,type:'triangle',slide:.58});soundTone(130,.11,{gain:.02,type:'sine',slide:.75,delay:.045})} + else soundTone(245,.045,{gain:.018,type:'triangle',slide:1.06}); +} +function toast(message,duration=2200){const e=document.querySelector('#toast');e.textContent=message;e.classList.add('show');clearTimeout(toastTimer);toastTimer=setTimeout(()=>e.classList.remove('show'),duration)} +function nextRevision(){lastRevision=Math.max(lastRevision+1,Date.now()*1000);return lastRevision} +function seedRevisionClock(snapshot=data){ + let maximum=Math.max(lastRevision,snapshot?.globalRev||0,(snapshot?.clockFloor||0)*1000); + for(const meta of Object.values(snapshot?.metas||{}))maximum=Math.max(maximum,meta?.rev||0); + for(const state of Object.values(snapshot?.states||{}))maximum=Math.max(maximum,state?.rev||0); + for(const tombstone of recoveredDeletionTombstones.values())maximum=Math.max(maximum,tombstone?.rev||0); + lastRevision=maximum;return maximum; +} +function showStatus(message,{retry=true,fresh=false}={}){ + statusMessage.textContent=message;statusPanel.hidden=false; + document.querySelector('#retryBtn').hidden=!retry; + document.querySelector('#freshBtn').hidden=!fresh; +} +function hideStatus(){statusPanel.hidden=true} +const {hash32,rngFrom,shuffle,macroDifficulty,difficultyFitsRegion,SHAPES,solverDifficulty,H_PORT_PROFILES,V_PORT_PROFILES,horizontalBoundaryKey,verticalBoundaryKey}=BendPuzzle; +let workerSeq=0; +const workerJobs=new Map(); +let puzzleWorker=null,workerRestartTimer=null,workerFailureCount=0,workerDisabledUntil=0,workerRetryAt=0; +function fallbackWorkerJob(id,error){ + const job=workerJobs.get(id);if(!job)return; + workerJobs.delete(id);clearTimeout(job.timeout); + job.reject(error); +} +function failWorkerJobs(error){for(const id of[...workerJobs.keys()])fallbackWorkerJob(id,error)} +function scheduleWorkerRestart(){ + clearTimeout(workerRestartTimer); + const delay=Math.min(30000,1000*2**Math.min(5,workerFailureCount++)); + workerRetryAt=Date.now()+delay;workerRestartTimer=setTimeout(()=>{workerRestartTimer=null;workerRetryAt=0;createPuzzleWorker()},delay); +} +function createPuzzleWorker(){ + if(puzzleWorker){clearTimeout(workerRestartTimer);workerRestartTimer=null;workerRetryAt=0;return puzzleWorker} + if(workerRestartTimer||Date.now(){ + if(worker!==puzzleWorker)return; + const job=workerJobs.get(message.id);if(!job)return; + if(message.generatorVersion!==GENERATOR_VERSION){const error=new Error('\u76e4\u9762\u751f\u6210\u6a5f\u80fd\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002');fallbackWorkerJob(message.id,error);restartPuzzleWorker(error);return} + workerJobs.delete(message.id);clearTimeout(job.timeout);workerFailureCount=0; + const deliver=()=>message.error?job.reject(new Error(message.error)):job.resolve(message.result??message.puzzle); + if(globalThis.document?.body?.classList?.contains?.('is-interacting')){perfCount('workerResultsDeferredDuringInteraction');waitForInteractionSettle().then(deliver)} + else deliver(); + }; + const failed=event=>{if(worker===puzzleWorker)restartPuzzleWorker(new Error(event?.message||'\u76e4\u9762\u751f\u6210\u51e6\u7406\u304c\u505c\u6b62\u3057\u307e\u3057\u305f\u3002'))}; + worker.onerror=failed;worker.onmessageerror=failed; + puzzleWorker=worker;return worker; + }catch(error){puzzleWorker=null;console.warn('BEND FIELD: worker unavailable',error);scheduleWorkerRestart();return null} +} +function restartPuzzleWorker(error,{cooldown=false}={}){ + if(error&&!error.code)error.code='WORKER_UNAVAILABLE'; + const old=puzzleWorker;puzzleWorker=null;if(old)old.terminate(); + if(cooldown)workerDisabledUntil=Date.now()+5000; + failWorkerJobs(error); + if(cooldown){clearTimeout(workerRestartTimer);workerRetryAt=Date.now()+5000;workerRestartTimer=setTimeout(()=>{workerRestartTimer=null;workerRetryAt=0;workerDisabledUntil=0;createPuzzleWorker()},5000)}else scheduleWorkerRestart(); +} +function generatePuzzleAsync(chunks,seed,level,originX=0,originY=0,timeoutMs=1500,generationOptions=null){ + const args={chunks,seed,level,originX,originY,generationOptions}; + if(!puzzleWorker)createPuzzleWorker(); + if(!puzzleWorker){const error=new Error('\u76e4\u9762\u751f\u6210\u6a5f\u80fd\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002');error.code='WORKER_UNAVAILABLE';perfCount('workerUnavailable');showStatus('\u76e4\u9762\u751f\u6210\u30ef\u30fc\u30ab\u30fc\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3002\u518d\u8a66\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002',{retry:true,fresh:false});return Promise.reject(error)} + return new Promise((resolve,reject)=>{ + const id=++workerSeq,job={resolve,reject,args,timeout:null}; + job.timeout=setTimeout(()=>{ + const error=new Error('\u76e4\u9762\u306e\u751f\u6210\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002'); + fallbackWorkerJob(id,error);restartPuzzleWorker(error,{cooldown:true}); + },Math.max(500,timeoutMs)); + workerJobs.set(id,job); + try{puzzleWorker.postMessage({id,...args,generatorVersion:GENERATOR_VERSION})} + catch(error){fallbackWorkerJob(id,error);restartPuzzleWorker(error)} + }); +} +function verifyPuzzleUniquenessAsync(puzzle,timeoutMs=650){ + const worker=createPuzzleWorker();if(!worker){const error=new Error('\u4e00\u610f\u89e3\u691c\u8a3c\u30ef\u30fc\u30ab\u30fc\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002');error.code='WORKER_UNAVAILABLE';return Promise.reject(error)} + return new Promise((resolve,reject)=>{ + const id=++workerSeq,job={resolve,reject,args:null,timeout:null,kind:'verify'}; + job.timeout=setTimeout(()=>{const current=workerJobs.get(id);if(!current)return;workerJobs.delete(id);const result={status:'timeout',solutions:0,nodes:0,elapsedMs:timeoutMs};resolve(result)},Math.max(500,timeoutMs)); + workerJobs.set(id,job); + try{worker.postMessage({id,action:'verify',puzzle,limits:{maxMs:450,nodeCap:250000},generatorVersion:GENERATOR_VERSION})} + catch(error){fallbackWorkerJob(id,error);restartPuzzleWorker(error)} + }); +} +function refreshClosedVoidAroundMeta(meta){ + for(const[dx,dy]of meta?.chunks||[]){const unitX=meta.x+dx,unitY=meta.y+dy;for(const[dr,dc]of[[0,0],...Object.values(SIDE_D)]){const x=unitX+dc,y=unitY+dr,key=key2(x,y);if(isClosedVoidUnit(x,y))closedVoidKeys.add(key);else closedVoidKeys.delete(key)}} +} +function addMetaToOccupancy(meta){ + if(!meta||!validChunkShape(meta.chunks))return false; + for(const[dx,dy]of meta.chunks)occupancy.set(key2(meta.x+dx,meta.y+dy),meta.id); + refreshClosedVoidAroundMeta(meta); + worldGeometryRevision++;fieldBoundsCache=null;adjacencyCache.clear();invalidateLineGraphCaches();invalidateWorldPresentation();return true; +} +function rebuildOccupancy(){ + occupancy=new Map();closedVoidKeys=new Set();worldGeometryRevision++;fieldBoundsCache=null; + for(const meta of Object.values(data.metas))if(meta&&validChunkShape(meta.chunks))for(const[dx,dy]of meta.chunks)occupancy.set(key2(meta.x+dx,meta.y+dy),meta.id); + const candidates=new Set();for(const key of occupancy.keys()){const[unitX,unitY]=key.split(',').map(Number);for(const[dr,dc]of Object.values(SIDE_D))candidates.add(key2(unitX+dc,unitY+dr))} + for(const key of candidates){const[unitX,unitY]=key.split(',').map(Number);if(isClosedVoidUnit(unitX,unitY))closedVoidKeys.add(key)} + adjacencyCache.clear();invalidateLineGraphCaches();invalidateWorldPresentation(); +} +function invalidateEconomyCaches(){inventoryCache=null;spentScoreCache=null;statsDirty=true} +function stateStatSignature(state){return`${state?.solved===true?1:0}:${Math.max(0,Number(state?.scoreAwarded)||0)}`} +function stateEconomySignature(state){return JSON.stringify((state?.store?.purchases||[]).map(purchase=>[purchase.id,purchase.paidCost||0,purchase.boughtAt||0]))} +function economySignatureSpend(signature){ + let rows=[];try{rows=JSON.parse(signature||'[]')}catch(_){} + return rows.reduce((sum,row)=>{const item=storeItem(row?.[0]);return sum+(item?row[1]||item.cost:0)},0); +} +function rememberStateSignatures(id,state){ + const previousStat=stateStatSignatures.get(id),previousEconomy=stateEconomySignatures.get(id),stat=stateStatSignature(state),economy=stateEconomySignature(state),statChanged=previousStat!==stat,economyChanged=previousEconomy!==economy; + stateStatSignatures.set(id,stat);stateEconomySignatures.set(id,economy); + if(statChanged&&!statsDirty){ + const[oldSolved,oldAward]=(previousStat||'0:0').split(':').map(Number),[newSolved,newAward]=stat.split(':').map(Number); + const awardDelta=newAward-oldAward,earned=Number.isFinite(cachedStats.earned)?Math.max(0,cachedStats.earned+awardDelta):null, + score=earned==null?Math.max(0,cachedStats.score+awardDelta):Math.max(0,earned-(spentScoreCache||0)); + cachedStats={solved:Math.max(0,cachedStats.solved+newSolved-oldSolved),earned,score}; + } + if(economyChanged&&previousEconomy!=null){ + const spentDelta=economySignatureSpend(economy)-economySignatureSpend(previousEconomy); + if(spentScoreCache!=null)spentScoreCache=Math.max(0,spentScoreCache+spentDelta); + if(!statsDirty)cachedStats.score=Math.max(0,cachedStats.score-spentDelta); + if(inventoryCache){ + inventoryCache=inventoryCache.filter(entry=>entry.meta.id!==id);const meta=data.metas[id],store=state?.store; + if(meta&&store)for(const purchase of store.purchases||[])inventoryCache.push({meta,st:state,store,purchase}); + inventoryCache.sort((a,b)=>(a.purchase.boughtAt||0)-(b.purchase.boughtAt||0)); + } + + } +} +function currentCloudPending(){ + return{metaIds:[...cloudJournalMetaIds],stateIds:[...cloudJournalStateIds].filter(id=>data?.states?.[id]?.solved===true),deleted:[...cloudJournalDeletedIds],globalChanged:cloudJournalGlobalChanged}; +} +function restoreCloudPending(pending=data.cloudPending){ + const clean=normalizeCloudPending(pending);cloudJournalMetaIds.clear();cloudJournalStateIds.clear();cloudJournalDeletedIds.clear(); + for(const id of clean.metaIds)cloudJournalMetaIds.add(id);for(const id of clean.stateIds){if(data?.states?.[id]?.solved===true)cloudJournalStateIds.add(id);else cloudOutboxDeleteKeys.add(`state:${id}`)}for(const id of clean.deleted)cloudJournalDeletedIds.add(id); + cloudJournalGlobalChanged=clean.globalChanged;cloudJournalChangeSeq=0;data.cloudPending=currentCloudPending(); +} +function noteCloudRow(kind,id){ + if(!id||!cloudApiEnabled)return; + if(kind==='state'&&data?.states?.[id]?.solved!==true){cloudJournalStateIds.delete(id);cloudOutboxDeleteKeys.add(`state:${id}`);return} + if(kind==='deleted'){cloudJournalMetaIds.delete(id);cloudJournalStateIds.delete(id);cloudJournalDeletedIds.add(id);cloudOutboxDeleteKeys.add(`meta:${id}`);cloudOutboxDeleteKeys.add(`state:${id}`)} + else{cloudJournalDeletedIds.delete(id);cloudOutboxDeleteKeys.add(`deleted:${id}`);(kind==='meta'?cloudJournalMetaIds:cloudJournalStateIds).add(id)} + cloudJournalChangeSeq++; +} +function markGlobalDirty(cloudRelevant=true){globalDirty=true;globalChangeSeq++;if(data){data.globalRev=nextRevision();data.globalRevAuthor=sessionId}if(cloudRelevant&&cloudApiEnabled){cloudJournalGlobalChanged=true;cloudJournalChangeSeq++}} +function hasPendingPersistence(){return globalDirty||dirtyMetaIds.size>0||dirtyStateIds.size>0||deletedBoardIds.size>0||cloudOutboxDeleteKeys.size>0} +function markMetaDirty(id){if(id){dirtyMetaIds.add(id);worldGeometryRevision++;fieldBoundsCache=null;if(data.metas[id]){if(!cloudApplyingRemote)data.metas[id].revAuthor=sessionId;deletedBoardIds.delete(id);deletedBoardRevisions.delete(id);deletedBoardAuthors.delete(id)}if(!cloudApplyingRemote)noteCloudRow('meta',id)}markGlobalDirty(!cloudApplyingRemote);invalidateWorldPresentation()} +function markStateDirty(id,pathIndex=null){if(id){dirtyStateIds.add(id);if(data.states[id]&&!cloudApplyingRemote)data.states[id].revAuthor=sessionId;if(!cloudApplyingRemote)noteCloudRow('state',id);rememberStateSignatures(id,data.states[id])}markGlobalDirty(!cloudApplyingRemote);invalidateLineGraphCaches(id,pathIndex);invalidateWorldPresentation()} +function markBoardDeleted(id){if(!id)return;dirtyMetaIds.delete(id);dirtyStateIds.delete(id);deletedBoardIds.add(id);deletedBoardRevisions.set(id,nextRevision());deletedBoardAuthors.set(id,sessionId);worldGeometryRevision++;fieldBoundsCache=null;if(!cloudApplyingRemote)noteCloudRow('deleted',id);stateStatSignatures.delete(id);stateEconomySignatures.delete(id);markGlobalDirty(!cloudApplyingRemote);statsDirty=true;invalidateEconomyCaches();invalidateLineGraphCaches(id);invalidateWorldPresentation()} +function replaceObjectContents(target,source){ + for(const key of Object.keys(target))if(!(key in source))delete target[key]; + Object.assign(target,source);return target; +} +function metaState(id){ + if(!data.states[id]||typeof data.states[id]!=='object'){ + data.states[id]=normalizeState(null);normalizedStateObjects.add(data.states[id]);markStateDirty(id);return data.states[id]; + } + const state=data.states[id]; + if(normalizedStateObjects.has(state))return state; + replaceObjectContents(state,normalizeState(state));normalizedStateObjects.add(state);markStateDirty(id);return state; +} +function storeItem(id){return STORE_ITEM_CATALOG.get(id)||null} +function normalizeStoreItemIds(raw){ + if(!Array.isArray(raw)||raw.length!==13||new Set(raw).size!==13)return null; + const items=raw.map(storeItem);return items.every(Boolean)&&items.filter(item=>item.cursorStyle).length===12&&items.filter(item=>!item.cursorStyle).length===1?[...raw]:null; +} +function seededStoreItemIds(seed){ + const cursorPool=shuffle([...CURSOR_ITEMS],rngFrom(hash32((seed>>>0)^0x5f356495))), + otherPool=shuffle(STORE_ITEMS.filter(item=>!item.cursorStyle),rngFrom(hash32((seed>>>0)^0x2c9277b5))), + selected=[...cursorPool.slice(0,12),...otherPool.slice(0,1)]; + return shuffle(selected,rngFrom(hash32((seed>>>0)^0x6d2b79f5))).map(item=>item.id); +} +function storeInventoryItems(meta,store=null){ + if(!meta)return[];const ids=normalizeStoreItemIds(store?.itemIds)||seededStoreItemIds(meta.seed); + return ids.map(storeItem).filter(Boolean); +} +let playerEconomyLoaded=false; +function onlinePlayerEconomy(){return Boolean(cloudApiEnabled&&cloudAvailable&&data.cloudProfile&&playerEconomyLoaded)} +function playerPurchaseForStore(boardId,itemId){return normalizePlayerPurchases(data.playerPurchases).find(purchase=>purchase.boardId===boardId&&purchase.itemId===itemId)||null} +function inventoryEntries(itemId=null){ + if(!inventoryCache){ + inventoryCache=[]; + if(onlinePlayerEconomy())for(const source of normalizePlayerPurchases(data.playerPurchases)){const item=storeItem(source.itemId);if(!item)continue;const meta=data.metas[source.boardId]||null,st=meta?metaState(meta.id):null,store=st?.store||null,purchase={...source,id:source.itemId};inventoryCache.push({meta,st,store,purchase,personal:true})} + else for(const meta of Object.values(data.metas)){const st=metaState(meta.id),store=st.store;if(!store)continue;for(const purchase of store.purchases)inventoryCache.push({meta,st,store,purchase,personal:false})} + inventoryCache.sort((a,b)=>(a.purchase.boughtAt||0)-(b.purchase.boughtAt||0)); + } + return itemId?inventoryCache.filter(entry=>entry.purchase.id===itemId):[...inventoryCache]; +} +function inventoryCount(itemId=null){return inventoryEntries(itemId).length} +function activeScoreLensCount(){return data.scoreLensEnabled===true?Math.max(debugAllItemsEnabled()?1:0,inventoryCount('score-lens')):0} +function spentScoreTotal(){ + if(spentScoreCache!=null)return spentScoreCache; + let spent=0;if(onlinePlayerEconomy())for(const purchase of normalizePlayerPurchases(data.playerPurchases)){const item=storeItem(purchase.itemId);if(item)spent+=purchase.paidCost||item.cost} + else for(const id of Object.keys(data.metas)){const store=metaState(id).store;if(!store)continue;for(const purchase of store.purchases){const item=storeItem(purchase.id);if(item)spent+=purchase.paidCost||item.cost}} + spentScoreCache=spent;return spent; +} +function pruneAndCount(){ + if(orphanPruneDirty){for(const id of Object.keys(data.states))if(!data.metas[id]){delete data.states[id];markBoardDeleted(id)}orphanPruneDirty=false} + if(statsDirty){ + let solved=0,earned=onlinePlayerEconomy()?Math.max(0,Number(data.playerEarnedScore)||0):bonusEventTotal();data.bonusScore=onlinePlayerEconomy()?0:earned; + for(const id of Object.keys(data.metas)){const st=metaState(id);if(st.solved)solved++;if(!onlinePlayerEconomy())earned+=st.scoreAwarded} + cachedStats={solved,earned,score:Math.max(0,earned-spentScoreTotal())};statsDirty=false; + } + data.solved=cachedStats.solved;data.score=cachedStats.score;solvedCountEl.textContent=String(data.solved);scoreCountEl.textContent=formatScore(data.score); +} +function snapshotForStorage(updatedAt=trustedNow()){ + const started=perfStart(); + const metas=Object.fromEntries(metaRowsForStorage().map(meta=>[meta.id,meta])); + const states=Object.fromEntries(stateRowsForStorage().map(row=>[row.id,row.value])); + const snapshot={...globalForStorage(data,updatedAt),cloudPending:currentCloudPending(),metas,states};perfGauge('snapshotBoards',Object.keys(metas).length);perfEnd('snapshotForStorage',started);return snapshot; +} +function compactSnapshot(updatedAt=trustedNow()){ + pruneAndCount(); + return snapshotForStorage(updatedAt); +} +function mergePurchases(a=[],b=[]){const map=new Map();for(const purchase of[...a,...b]){const current=map.get(purchase.id);if(!current||(purchase.boughtAt||0)>(current.boughtAt||0))map.set(purchase.id,deepClone(purchase))}return[...map.values()]} +function mergeBoardStates(current,incoming){ + if(!current)return deepClone(incoming);if(!incoming)return current; + if(current.solved||incoming.solved){ + const selected=current.solved&&!incoming.solved?current:incoming.solved&&!current.solved?incoming:compareRevisionVersions(incoming,current)>=0?incoming:current, + other=selected===current?incoming:current,chosen=deepClone(selected); + if(chosen.store&&other.store)chosen.store.purchases=mergePurchases(chosen.store.purchases,other.store.purchases); + chosen.specialProgress=mergeSpecialProgress(current.specialProgress,incoming.specialProgress); + const version=newerRevisionValue(current,incoming);chosen.rev=version.rev||0;chosen.revAuthor=version.revAuthor||'';return chosen; + } + const newer=compareRevisionVersions(incoming,current)>=0?incoming:current,older=newer===incoming?current:incoming,result=deepClone(newer),usedGates=new Set(),usedCells=new Set(); + for(const path of result.paths||[]){if(!path.detachedStart)usedGates.add(path.startGate);if(path.endGate!=null)usedGates.add(path.endGate);if(path.openGate!=null)usedGates.add(path.openGate);for(const cell of path.cells)usedCells.add(ckey(...cell))} + for(const path of older.paths||[]){ + const gates=[...(path.detachedStart?[]:[path.startGate]),...(path.endGate==null?[]:[path.endGate]),...(path.openGate==null?[]:[path.openGate])],conflict=gates.some(g=>usedGates.has(g))||path.cells.some(cell=>usedCells.has(ckey(...cell)));if(conflict)continue; + result.paths.push(deepClone(path));for(const g of gates)usedGates.add(g);for(const cell of path.cells)usedCells.add(ckey(...cell)); + } + result.specialProgress=mergeSpecialProgress(current.specialProgress,incoming.specialProgress); + result.rev=newer.rev||0;result.revAuthor=newer.revAuthor||'';return result; +} +function sameMetaGeometry(a,b){return a&&b&&a.x===b.x&&a.y===b.y&&a.seed===b.seed&&JSON.stringify(a.chunks)===JSON.stringify(b.chunks)&&JSON.stringify(a.sealedSides||[])===JSON.stringify(b.sealedSides||[])} +function resolveMergedOverlaps(){ + const metas=Object.values(data.metas).sort((a,b)=>a.id==='B0'?-1:b.id==='B0'?1:(b.rev||0)-(a.rev||0)||a.id.localeCompare(b.id,undefined,{numeric:true})); + const occupied=new Set(),removed=[]; + for(const meta of metas){ + if(meta.chunks.some(([dx,dy])=>occupied.has(key2(meta.x+dx,meta.y+dy)))){removed.push(meta.id);continue} + for(const[dx,dy]of meta.chunks)occupied.add(key2(meta.x+dx,meta.y+dy)); + } + for(const id of removed){delete data.metas[id];delete data.states[id];markBoardDeleted(id);destroyBoard(rendered.get(id))} + return removed; +} +function clearSharedWorldJournalRow(kind,id){ + if(kind==='meta'){cloudJournalMetaIds.delete(id);cloudOutboxDeleteKeys.add(`meta:${id}`)} + else if(kind==='state'){cloudJournalStateIds.delete(id);cloudOutboxDeleteKeys.add(`state:${id}`)} + cloudJournalDeletedIds.delete(id);cloudOutboxDeleteKeys.add(`deleted:${id}`); +} +function applyAuthoritativeSharedGlobal(external){ + if(!external)return; + if(Number.isSafeInteger(external.nextId)&&external.nextId>0)data.nextId=external.nextId; + if(Number.isSafeInteger(external.solved)&&external.solved>=0)data.solved=external.solved; + if(Number.isFinite(external.lastSolveAt)&&external.lastSolveAt>=0)data.lastSolveAt=external.lastSolveAt; + if(Array.isArray(external.specialMechanicsSeen))data.specialMechanicsSeen=[...new Set(external.specialMechanicsSeen)].filter(type=>['warp','lock','crossing'].includes(type)).sort(); + if(isPlainObject(external.quarantine))data.quarantine=deepClone(external.quarantine); + statsDirty=true; +} +function mergeSnapshotIntoData(external,{finalize=true,authoritativeWorld=false}={}){ + if(!external||!isPlainObject(external.metas))return[]; + const added=[]; + for(const[id,incoming]of Object.entries(external.metas)){ + lastRevision=Math.max(lastRevision,incoming.rev||0);const current=data.metas[id]; + if(authoritativeWorld){ + clearSharedWorldJournalRow('meta',id); + if(!current){data.metas[id]=incoming;added.push(id)} + else if(sameMetaGeometry(current,incoming))Object.assign(current,incoming); + else{destroyBoard(rendered.get(id));destroyStaticBoard(staticRendered.get(id));data.metas[id]=incoming;added.push(id)} + continue; + } + if(!current){data.metas[id]=incoming;added.push(id);continue} + if(compareRevisionVersions(incoming,current)>0){ + if(sameMetaGeometry(current,incoming))Object.assign(current,incoming); + else{destroyBoard(rendered.get(id));data.metas[id]=incoming;added.push(id)} + } + } + for(const[id,incoming]of Object.entries(external.states||{})){ + lastRevision=Math.max(lastRevision,incoming.rev||0);if(!data.metas[id])continue; + const current=data.states[id];let merged; + if(authoritativeWorld){ + clearSharedWorldJournalRow('state',id); + merged=current?.solved&&!incoming.solved?deepClone(incoming):mergeBoardStates(current,incoming); + }else merged=mergeBoardStates(current,incoming); + if(!current||JSON.stringify(merged)!==JSON.stringify(current)){ + if(current&&!authoritativeWorld){merged.rev=nextRevision();merged.revAuthor=sessionId} + data.states[id]=merged;normalizedStateObjects.add(merged);if(current)markStateDirty(id); + const board=rendered.get(id);if(board){board.drawing=null;board.solvedPathsRendered=false} + if(data.metas[id].puzzle)sanitizeStateForPuzzle(data.metas[id],{quiet:true}); + } + } + if(authoritativeWorld)applyAuthoritativeSharedGlobal(external);else mergeGlobalFields(external); + if(finalize)resolveMergedOverlaps() + return added.filter(id=>data.metas[id]); +} +function setSaveStatus(state,message){saveStatusEl.dataset.state=state;saveStatusEl.textContent=message;saveStatusEl.setAttribute('aria-label',`\u4fdd\u5b58\u72b6\u614b\uff1a${message}`)} +function metaForStorage(m){return{id:m.id,x:m.x,y:m.y,chunks:m.chunks,level:m.level,targetLevel:m.targetLevel,seed:m.seed,axis:m.axis,entrySide:m.entrySide||null,sealedSides:[...new Set((m.sealedSides||[]).filter(side=>['N','S','W','E'].includes(side)))],puzzle:puzzleForStorage(m.puzzle),generatorVersion:m.generatorVersion||GENERATOR_VERSION,rev:m.rev||0,revAuthor:m.revAuthor||''}} +function globalForStorage(source=data,updatedAt=trustedNow()){ + const anchor=source===data&&typeof currentCameraAnchor==='function'?currentCameraAnchor():source.cameraAnchor||null; + return{ + schema:SAVE_SCHEMA,gameplayVersion:GAMEPLAY_DATA_VERSION,worldGeneration:WORLD_GENERATION,worldEpoch:source.worldEpoch, + globalRev:source.globalRev||0,globalRevAuthor:source.globalRevAuthor||'',appVersion:APP_VERSION,generatorVersion:GENERATOR_VERSION, + quarantine:source.quarantine||{},bonusEvents:source.bonusEvents||{},clockFloor:Math.max(source.clockFloor||0,updatedAt), + cloudProfile:source.cloudProfile,cloudRevision:source.cloudRevision||0,cloudSyncPaused:source.cloudSyncPaused===true,playerName:source.playerName||null,playerPurchases:normalizePlayerPurchases(source.playerPurchases),playerEarnedScore:Number.isSafeInteger(source.playerEarnedScore)&&source.playerEarnedScore>=0?source.playerEarnedScore:0,lastReaction:REACTION_EMOJIS.includes(source.lastReaction)?source.lastReaction:'👍',worldFeedRevision:source.worldFeedRevision||0, + nextId:source.nextId,solved:source.solved||0,score:source.score||0,bonusScore:source.bonusScore||0,bonusScoreVersion:SCORE_VERSION, + lastSolveAt:source.lastSolveAt||0,timeAttack:source.timeAttack,timeAttackRev:source.timeAttackRev||0,timeAttackCooldowns:source.timeAttackCooldowns, + lastTimeAttack:source.lastTimeAttack,timeAttackSuggestionsDisabled:source.timeAttackSuggestionsDisabled===true,cursorStyle:source.cursorStyle||'default', + scoreLensEnabled:source.scoreLensEnabled===true, + debugAllItems:source.debugAllItems===true, + specialMechanicsSeen:[...new Set(source.specialMechanicsSeen||[])].filter(type=>['warp','lock','crossing'].includes(type)).sort(), + cameraAnchor:anchor,selectedBoardId:source===data?(activeBoard||source.selectedBoardId||null):source.selectedBoardId||null,updatedAt,sessionId + }; +} +function mergeGlobalRecords(current,incoming){ + if(!current)return deepClone(incoming);if(!incoming)return deepClone(current); + if(validWorldEpoch(current.worldEpoch)&&validWorldEpoch(incoming.worldEpoch)&¤t.worldEpoch!==incoming.worldEpoch){const error=new Error('World epoch changed in another tab');error.code='STALE_WORLD_EPOCH';throw error} + const newer=compareRevisionVersions({rev:incoming.globalRev,revAuthor:incoming.globalRevAuthor},{rev:current.globalRev,revAuthor:current.globalRevAuthor})>=0?incoming:current, + older=newer===incoming?current:incoming,merged=deepClone(newer); + merged.worldEpoch=incoming.worldEpoch||current.worldEpoch||null;merged.nextId=Math.max(current.nextId||1,incoming.nextId||1); + merged.clockFloor=Math.max(current.clockFloor||0,incoming.clockFloor||0);merged.cloudRevision=Math.max(current.cloudRevision||0,incoming.cloudRevision||0);merged.updatedAt=Math.max(current.updatedAt||0,incoming.updatedAt||0); + merged.bonusEvents={};for(const source of[current.bonusEvents||{},incoming.bonusEvents||{}])for(const[id,value]of Object.entries(source))merged.bonusEvents[id]=Math.max(merged.bonusEvents[id]||0,value||0); + merged.bonusScore=bonusEventTotal(merged.bonusEvents); + const personalPurchases=new Map();for(const purchase of[...(current.playerPurchases||[]),...(incoming.playerPurchases||[])]){if(!purchase?.purchaseId)continue;const prior=personalPurchases.get(purchase.purchaseId);if(!prior||(purchase.boughtAt||0)>=(prior.boughtAt||0))personalPurchases.set(purchase.purchaseId,deepClone(purchase))}merged.playerPurchases=[...personalPurchases.values()]; + merged.quarantine={};for(const source of[current.quarantine||{},incoming.quarantine||{}])for(const[id,value]of Object.entries(source)){const prior=merged.quarantine[id];if(!prior||(value?.failedAt||0)>=(prior?.failedAt||0))merged.quarantine[id]=deepClone(value)} + if((older.lastSolveAt||0)>(merged.lastSolveAt||0))merged.lastSolveAt=older.lastSolveAt; + if((older.timeAttackRev||0)>(merged.timeAttackRev||0)){merged.timeAttack=deepClone(older.timeAttack);merged.timeAttackRev=older.timeAttackRev;merged.timeAttackCooldowns=deepClone(older.timeAttackCooldowns);merged.lastTimeAttack=deepClone(older.lastTimeAttack)} + else if((older.lastTimeAttack?.completedAt||0)>(merged.lastTimeAttack?.completedAt||0))merged.lastTimeAttack=deepClone(older.lastTimeAttack); + merged.specialMechanicsSeen=[...new Set([...(current.specialMechanicsSeen||[]),...(incoming.specialMechanicsSeen||[])])].filter(type=>['warp','lock','crossing'].includes(type)).sort(); + for(const retired of['combo','comboUntil','comboExpiresAt','comboTimer','bestCombo','undo','undoStack','redoStack'])delete merged[retired]; + merged.globalRev=newer.globalRev||0;merged.globalRevAuthor=newer.globalRevAuthor||'';return merged; +} +function applyGlobalRecordToData(record){ + if(!record)return; + const priorBonuses=JSON.stringify(data.bonusEvents||{}); + for(const key of['worldEpoch','globalRev','globalRevAuthor','gameplayVersion','quarantine','bonusEvents','clockFloor','cloudProfile','cloudRevision','cloudSyncPaused','playerName','playerPurchases','playerEarnedScore','lastReaction','worldFeedRevision','nextId','solved','score','bonusScore','lastSolveAt','timeAttack','timeAttackRev','timeAttackCooldowns','lastTimeAttack','timeAttackSuggestionsDisabled','cursorStyle','scoreLensEnabled','debugAllItems','specialMechanicsSeen','cameraAnchor','selectedBoardId','updatedAt'])if(Object.prototype.hasOwnProperty.call(record,key))data[key]=deepClone(record[key]); + data.bonusScore=bonusEventTotal(data.bonusEvents);lastRevision=Math.max(lastRevision,data.globalRev||0,data.clockFloor?data.clockFloor*1000:0); + if(priorBonuses!==JSON.stringify(data.bonusEvents||{}))statsDirty=true; +} +function staleWorldEpochError(){const error=new Error('\u5225\u306e\u30bf\u30d6\u3067\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u7f6e\u304d\u63db\u3048\u3089\u308c\u307e\u3057\u305f\u3002');error.code='STALE_WORLD_EPOCH';return error} +function stateForStorage(state){return AppLogic.stateForStorage(state,{scoreVersion:SCORE_VERSION,normalizeState})} +function summarizeBoardV2(meta,state,epoch=data.worldEpoch){ + const puzzle=meta?.puzzle,special=puzzle?.specialCells||{},store=state?.store; + let storeCell=null; + if(store&&Number.isInteger(store.pathIndex)&&Number.isInteger(store.cellIndex)){ + const cell=state.paths?.[store.pathIndex]?.cells?.[store.cellIndex];if(cell)storeCell=[cell[0],cell[1]]; + } + return{ + epoch,id:meta.id,number:Number(meta.id.slice(1))||0,x:meta.x,y:meta.y,chunks:meta.chunks.map(chunk=>[chunk[0],chunk[1]]),level:meta.level,targetLevel:meta.targetLevel, + seed:meta.seed,axis:meta.axis,entrySide:meta.entrySide||null,sealedSides:[...(meta.sealedSides||[])],metaRev:meta.rev||0,stateRev:state?.rev||0,metaRevAuthor:meta.revAuthor||'',stateRevAuthor:state?.revAuthor||'', + solved:state?.solved===true,expanded:state?.expanded===true,solvedBy:state?.solvedBy||null,solvedById:state?.solvedById||null,solvedAt:Number.isFinite(state?.solvedAt)?state.solvedAt:null,scoreAwarded:Math.max(0,Number(state?.scoreAwarded)||0), + hasProgress:Boolean(state?.paths?.length||state?.specialProgress?.crossings?.length), + specialFlags:{crossing:Boolean(special.crossings?.length),warp:Boolean(special.warps?.length),lock:Boolean(special.locks?.length)}, + shop:store?{cell:storeCell,itemIds:Array.isArray(store.itemIds)?[...store.itemIds]:[],purchases:(store.purchases||[]).map(purchase=>({id:purchase.id,paidCost:purchase.paidCost||0,boughtAt:purchase.boughtAt||0}))}:null + }; +} +function puzzleRecordV2(meta,epoch=data.worldEpoch){return{epoch,id:meta.id,metaRev:meta.rev||0,revAuthor:meta.revAuthor||'',generatorVersion:meta.generatorVersion||GENERATOR_VERSION,puzzle:puzzleForStorage(meta.puzzle)}} +function stateRecordV2(id,state,epoch=data.worldEpoch){return{epoch,id,stateRev:state?.rev||0,revAuthor:state?.revAuthor||'',value:stateForStorage(state)}} +function metaFromV2Records(index,puzzle){ + if(!index||!puzzle||index.id!==puzzle.id||index.metaRev!==puzzle.metaRev)return null; + return normalizeMeta(index.id,{id:index.id,x:index.x,y:index.y,chunks:index.chunks,level:index.level,targetLevel:index.targetLevel,seed:index.seed,axis:index.axis,entrySide:index.entrySide||null,sealedSides:index.sealedSides||[],puzzle:puzzle.puzzle,generatorVersion:puzzle.generatorVersion||GENERATOR_VERSION,rev:puzzle.metaRev,revAuthor:puzzle.revAuthor||''}); +} +function stateFromV2Record(index,state){ + if(!index||!state||index.id!==state.id||index.stateRev!==state.stateRev)return null; + return normalizeState(state.value); +} +function summaryStateFromIndex(index){ + const shop=index?.shop; + return{ + paths:[],specialProgress:{crossings:[]},solved:index?.solved===true,expanded:index?.expanded===true,expansionRetryRound:0,solvedBy:typeof index?.solvedBy==='string'?index.solvedBy:null,solvedById:typeof index?.solvedById==='string'?index.solvedById:null,solvedAt:Number.isFinite(index?.solvedAt)?index.solvedAt:null, + scoreAwarded:Math.max(0,Number(index?.scoreAwarded)||0),scoreVersion:SCORE_VERSION,rewardIdentity:null,rewardCoefficient:null, + store:shop?{owner:'',pathIndex:-1,cellIndex:-1,itemIds:[...(shop.itemIds||[])],purchases:deepClone(shop.purchases||[]),summaryCell:Array.isArray(shop.cell)?[...shop.cell]:null}:null, + rev:index?.stateRev||0,revAuthor:index?.stateRevAuthor||index?.revAuthor||'',_summaryOnly:true + }; +} +function fieldBoundsFromMetas(metas){ + if(metas===data?.metas&&fieldBoundsCache?.revision===worldGeometryRevision)return{...fieldBoundsCache.bounds}; + let minX=0,minY=0,maxX=1,maxY=1,found=false; + for(const meta of Object.values(metas||{}))for(const[dx,dy]of meta?.chunks||[]){ + const x=meta.x+dx,y=meta.y+dy;if(!found){minX=x;minY=y;maxX=x+1;maxY=y+1;found=true}else{minX=Math.min(minX,x);minY=Math.min(minY,y);maxX=Math.max(maxX,x+1);maxY=Math.max(maxY,y+1)} + } + const bounds={minX,minY,maxX,maxY};if(metas===data?.metas)fieldBoundsCache={revision:worldGeometryRevision,bounds};return{...bounds}; +} +function metaRowsForStorage(ids=Object.keys(data.metas)){return ids.map(id=>data.metas[id]&&metaForStorage(data.metas[id])).filter(Boolean)} +function stateRowsForStorage(ids=Object.keys(data.states)){return ids.map(id=>data.states[id]&&({id,value:stateForStorage(data.states[id])})).filter(Boolean)} +async function cloudRowsForStorage(metaIds,stateIds){ + const metaSet=new Set(metaIds||[]),stateSet=new Set(stateIds||[]),allIds=[...new Set([...metaSet,...stateSet])],metas=[],states=[]; + if(!allIds.length)return{metas,states}; + if(activeStorageFormat!==FIELD_STORAGE_FORMAT||!idbAvailable)return{metas:metaRowsForStorage([...metaSet]),states:stateRowsForStorage([...stateSet])}; + const db=await openWorldDb(),tx=db.transaction(['control','boardIndex','boardPuzzles','boardStates'],'readonly'),done=transactionDone(tx),key=id=>[data.worldEpoch,id], + [control,indexes,puzzles,stateRecords]=await Promise.all([ + requestValue(tx.objectStore('control').get('active')), + Promise.all(allIds.map(id=>requestValue(tx.objectStore('boardIndex').get(key(id))))), + Promise.all(allIds.map(id=>requestValue(tx.objectStore('boardPuzzles').get(key(id))))), + Promise.all(allIds.map(id=>requestValue(tx.objectStore('boardStates').get(key(id))))) + ]);await done; + if(!control||control.activeEpoch!==data.worldEpoch||control.activeFormat!==FIELD_STORAGE_FORMAT)throw staleWorldEpochError(); + for(let position=0;position{ + if(!validWorldEpoch(data?.worldEpoch))return false; + const db=await openWorldDb(),tx=db.transaction('recoveryV2','readwrite'),done=transactionDone(tx),key=`journal:${sessionId}`; + tx.objectStore('recoveryV2').put({epoch:data.worldEpoch,key,value:journal});await done;recoveryWalFailed=false;perfCount('recoveryWalWrites');return true; + }; + const result=recoveryWalPromise.then(run,run).catch(error=>{recoveryWalFailed=true;console.warn('BEND FIELD: recovery WAL failed',error);if(document.visibilityState==='visible')showStatus('\u7dca\u6025\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002',{retry:true,fresh:false});return false});recoveryWalPromise=result.then(()=>{},()=>{});return result; +} +function writeDirtyRecoveryJournal(){ + if(!hasPendingPersistence())return false; + const updatedAt=Math.max(trustedNow(),(data.updatedAt||0)+1),global=globalForStorage(data,updatedAt),journal={ + schema:SAVE_SCHEMA,worldGeneration:WORLD_GENERATION,worldEpoch:data.worldEpoch,updatedAt,sessionId,seq:globalChangeSeq,global:{...global,cloudPending:currentCloudPending()}, + metas:metaRowsForStorage([...dirtyMetaIds]),states:stateRowsForStorage([...dirtyStateIds]),deleted:[...deletedBoardIds].map(id=>({id,rev:deletedBoardRevisions.get(id)||updatedAt*1000,revAuthor:deletedBoardAuthors.get(id)||sessionId})) + }; + try{ + void persistRecoveryJournalToDb(journal); + const json=JSON.stringify(journal);if(json.length>LOCAL_MIRROR_MAX_BYTES){recoveryJournalSeq=globalChangeSeq;perfCount('recoveryJournalOversize');return idbAvailable} + if(!safeLocalSet(sessionRecoveryJournalKey,json))return false;recoveryJournalSeq=globalChangeSeq;perfCount('recoveryJournalWrites');return true; + }catch(_){return false} +} +function clearRecoveryJournalIfCovered(committedSeq,coveredJournals=[]){ + let cleared=false;const cleanupSessions=[]; + if(recoveryJournalSeq>=0&&recoveryJournalSeq<=committedSeq){if(safeLocalRemove(sessionRecoveryJournalKey)){recoveryJournalSeq=-1;cleanupSessions.push(sessionId);cleared=true}} + for(const journal of coveredJournals)if(journal?._storageKey&&safeLocalRemove(journal._storageKey)){if(journal.sessionId)cleanupSessions.push(journal.sessionId);cleared=true} + if(coveredJournals.length){const coveredKeys=new Set(coveredJournals.map(journal=>journal._storageKey));recoveryJournalsToCover=recoveryJournalsToCover.filter(journal=>!coveredKeys.has(journal._storageKey))} + if(cleanupSessions.length)void deleteRecoveryCoverageMarkers(cleanupSessions); + return cleared; +} +let mirrorRetryAt=0,mirrorCleanupHandle=0,mirrorCleanupQueue=[],mirrorBuildSerial=0; +function scheduleMirrorChunkCleanup(keepGeneration=null){ + const keepPrefix=keepGeneration?`${mirrorChunkPrefix}${keepGeneration}:`:null; + mirrorCleanupQueue=safeLocalKeys(mirrorChunkPrefix).filter(key=>!keepPrefix||!key.startsWith(keepPrefix)); + if(mirrorCleanupHandle||!mirrorCleanupQueue.length)return; + const run=deadline=>{ + mirrorCleanupHandle=0;let removed=0; + while(mirrorCleanupQueue.length&&(deadline?.didTimeout?removed<1:typeof deadline?.timeRemaining==='function'?deadline.timeRemaining()>4:removed<1)){safeLocalRemove(mirrorCleanupQueue.shift());removed++} + if(mirrorCleanupQueue.length)mirrorCleanupHandle=typeof requestIdleCallback==='function'?requestIdleCallback(run,{timeout:MIRROR_IDLE_TIMEOUT}):setTimeout(()=>run({didTimeout:true,timeRemaining:()=>0}),0); + }; + mirrorCleanupHandle=typeof requestIdleCallback==='function'?requestIdleCallback(run,{timeout:MIRROR_IDLE_TIMEOUT}):setTimeout(()=>run({didTimeout:true,timeRemaining:()=>0}),0); +} +function writeCompactMirror(snapshot=snapshotForStorage(data.updatedAt||trustedNow()),{authoritative=false}={}){ + const started=perfStart(); + try{ + const head=readStorageRevision(),updatedAt=snapshot?.updatedAt||0; + if(Math.max(head.databaseUpdatedAt||0,head.mirrorUpdatedAt||0)>updatedAt){perfCount('mirrorStaleSkips');if(authoritative){mirrorRetryAt=Date.now()+1000;return false}return true} + const json=JSON.stringify(snapshot); + perfGauge('mirrorBytes',json.length); + if(json.length>LOCAL_MIRROR_MAX_BYTES){perfCount('mirrorOversize');mirrorRetryAt=Date.now()+5*60*1000;return false} + const written=safeLocalSet(storageKey,json);if(written){mirrorRetryAt=0;updateStorageRevision({mirrorUpdatedAt:updatedAt});scheduleMirrorChunkCleanup();perfCount('mirrorWrites')}else mirrorRetryAt=Date.now()+60*1000;return written; + }catch(error){mirrorRetryAt=Date.now()+60*1000;console.warn('BEND FIELD: compact mirror failed',error);return false} + finally{perfEnd('writeCompactMirror',started)} +} +function* jsonValueFragments(value){ + if(value===null){yield'null';return} + const type=typeof value; + if(type==='string'){yield JSON.stringify(value);return} + if(type==='number'){yield Number.isFinite(value)?String(value):'null';return} + if(type==='boolean'){yield value?'true':'false';return} + if(type!=='object'){yield'null';return} + if(Array.isArray(value)){ + yield'[';for(let index=0;index=MIRROR_CHUNK_BYTES)return offset} + return offset; +} +function writeMirrorBuildChunk(build){ + if(!build.pendingChunk)return true; + const started=perfStart(),key=mirrorChunkKey(build.generation,build.chunkIndex),chunk=build.pendingChunk; + if(!safeLocalSet(key,chunk)){perfEnd('mirrorChunkWrite',started);return false} + build.pendingChunk='';build.chunkIndex++;build.bytes+=chunk.length;perfGauge('mirrorChunkWriteMs',perfNow()-started);perfEnd('mirrorChunkWrite',started);perfCount('mirrorChunkWrites');return true; +} +function commitMirrorBuild(build){ + const head=readStorageRevision(); + if(Math.max(head.databaseUpdatedAt||0,head.mirrorUpdatedAt||0)>build.updatedAt){perfCount('mirrorStaleSkips');discardMirrorBuild(build);return true} + const manifest={format:MIRROR_CHUNK_FORMAT,generation:build.generation,chunks:build.chunkIndex,bytes:build.bytes,updatedAt:build.updatedAt}; + if(!safeLocalSet(storageKey,JSON.stringify(manifest)))return false; + mirrorRetryAt=0;updateStorageRevision({mirrorUpdatedAt:build.updatedAt});perfGauge('mirrorBytes',build.bytes);perfGauge('mirrorChunks',build.chunkIndex);perfCount('mirrorWrites');scheduleMirrorChunkCleanup(build.generation);return true; +} +let mirrorIdleHandle=0,mirrorDelayTimer=0,mirrorCheckpointDirty=false,mirrorBuild=null; +function* storageEntryIterator(source){for(const id in source)if(Object.prototype.hasOwnProperty.call(source,id))yield[id,source[id]]} +function cancelMirrorCheckpoint(clearDirty=false){ + if(mirrorDelayTimer){clearTimeout(mirrorDelayTimer);mirrorDelayTimer=0} + if(mirrorIdleHandle){if(typeof cancelIdleCallback==='function')cancelIdleCallback(mirrorIdleHandle);else clearTimeout(mirrorIdleHandle);mirrorIdleHandle=0} + discardMirrorBuild();if(clearDirty)mirrorCheckpointDirty=false; +} +function runMirrorCheckpoint(deadline=null){ + mirrorIdleHandle=0; + if(!idbAvailable||!mirrorCheckpointDirty)return; + const interacting=Boolean(pan||pinch||[...rendered.values()].some(board=>board.drawing?.pointerId!=null)); + if(interacting||hasPendingPersistence()){discardMirrorBuild();scheduleMirrorCheckpoint();return} + if(Date.now()deadline?.didTimeout?processed<64:typeof deadline?.timeRemaining==='function'?deadline.timeRemaining()>4:processed<64;let processed=0;build.slices++; + if(build.stage==='rows'){ + while(!build.metaDone&&canContinue(processed++)){const next=build.metaIterator.next();if(next.done){build.metaDone=true;break}const[id,meta]=next.value;if(meta)build.metas[id]=metaForStorage(meta)} + while(build.metaDone&&!build.stateDone&&canContinue(processed++)){const next=build.stateIterator.next();if(next.done){build.stateDone=true;break}const[id,state]=next.value;if(state)build.states[id]=stateForStorage(state)} + if(build.metaDone&&build.stateDone)initializeMirrorSerialization(build); + } + if(build.stage==='cleanup'){ + if(build.cleanupKeys.length){safeLocalRemove(build.cleanupKeys.shift());perfCount('mirrorChunkRemovals');perfGauge('mirrorBuildSliceMs',perfNow()-sliceStarted);scheduleMirrorCheckpoint(0);return} + build.stage='serialize'; + } + if(build.stage==='serialize'){ + let fragmentOffset=build.fragmentOffset||0,fragment=build.fragment||''; + while(canContinue(processed++)){ + if(fragmentOffset=MIRROR_CHUNK_BYTES){build.fragment=fragment;build.fragmentOffset=fragmentOffset;if(!writeMirrorBuildChunk(build)){mirrorRetryAt=Date.now()+60*1000;discardMirrorBuild(build);scheduleMirrorCheckpoint(mirrorRetryAt-Date.now());return}perfGauge('mirrorBuildSliceMs',perfNow()-sliceStarted);scheduleMirrorCheckpoint(0);return}continue} + const next=build.serializer.next();if(next.done){build.serializerDone=true;break}fragment=next.value;fragmentOffset=0; + } + build.fragment=fragment;build.fragmentOffset=fragmentOffset; + if(build.serializerDone){if(build.pendingChunk){if(!writeMirrorBuildChunk(build)){mirrorRetryAt=Date.now()+60*1000;discardMirrorBuild(build);scheduleMirrorCheckpoint(mirrorRetryAt-Date.now());return}perfGauge('mirrorBuildSliceMs',perfNow()-sliceStarted);scheduleMirrorCheckpoint(0);return}build.stage='commit'} + } + perfGauge('mirrorBuildSliceMs',perfNow()-sliceStarted);perfGauge('mirrorBuildSlices',build.slices); + if(hasPendingPersistence()||data.updatedAt!==build.updatedAt||globalChangeSeq!==build.seq){discardMirrorBuild(build);scheduleMirrorCheckpoint();return} + if(build.stage!=='commit'){scheduleMirrorCheckpoint(0);return} + mirrorCheckpointDirty=false; + if(!commitMirrorBuild(build)){mirrorCheckpointDirty=true;mirrorRetryAt=Date.now()+60*1000;discardMirrorBuild(build)}else mirrorBuild=null; + if(mirrorCheckpointDirty)scheduleMirrorCheckpoint(Math.max(MIRROR_IDLE_DELAY,mirrorRetryAt-Date.now())); +} +function scheduleMirrorCheckpoint(delay=MIRROR_IDLE_DELAY){ + if(!idbAvailable||activeStorageFormat===FIELD_STORAGE_FORMAT)return;mirrorCheckpointDirty=true; + if(mirrorDelayTimer||mirrorIdleHandle)return; + mirrorDelayTimer=setTimeout(()=>{ + mirrorDelayTimer=0; + if(typeof requestIdleCallback==='function')mirrorIdleHandle=requestIdleCallback(runMirrorCheckpoint,{timeout:MIRROR_IDLE_TIMEOUT}); + else mirrorIdleHandle=setTimeout(()=>runMirrorCheckpoint({didTimeout:true,timeRemaining:()=>0}),0); + },Math.max(0,delay)); +} +async function persistDirtyToDb(options={}){ + const{skipCloud=false}=options; + if(options.lifecycle!==true&&globalThis.document?.body?.classList?.contains?.('is-interacting')){perfCount('savesDeferredDuringInteraction');await waitForInteractionSettle(null)} + if(globalThis.document?.body?.classList?.contains?.('is-interacting'))perfCount('persistenceDuringInteraction'); + const started=perfStart();pruneAndCount(); + if(!hasPendingPersistence()){perfEnd('persistDirtyToDb',started);return{updatedAt:data.updatedAt||data.clockFloor||0,count:0,skipped:true}} + data.worldEpoch=validWorldEpoch(data.worldEpoch)?data.worldEpoch:storedWorldEpoch()||createWorldEpoch();rememberWorldEpoch(data.worldEpoch); + const metaIds=[...dirtyMetaIds],stateIds=[...dirtyStateIds],deleted=[...deletedBoardIds], + deletedVersions=new Map(deleted.map(id=>[id,{rev:deletedBoardRevisions.get(id)||0,revAuthor:deletedBoardAuthors.get(id)||sessionId}])), + outboxDeletes=[...cloudOutboxDeleteKeys],coveredJournals=[...recoveryJournalsToCover],currentRecoverySeq=recoveryJournalSeq,globalSeq=globalChangeSeq,globalChanged=cloudJournalGlobalChanged, + metaVersions=new Map(metaIds.map(id=>[id,revisionVersion(data.metas[id])])),stateVersions=new Map(stateIds.map(id=>[id,revisionVersion(data.states[id])])), + updatedAt=trustedNow(),global=globalForStorage(data,updatedAt),metaRows=metaRowsForStorage(metaIds),stateRows=stateRowsForStorage(stateIds), + metaRowMap=new Map(metaRows.map(row=>[row.id,row])),stateRowMap=new Map(stateRows.map(row=>[row.id,row])),committedMetas=new Map(),committedStates=new Map(),committedDeleted=new Set(); + let committedGlobal=global,reconciledPersistenceConflict=false; + if(idbAvailable&¤tRecoverySeq>=0)await recoveryWalPromise; + if(!idbAvailable){ + const mirror=snapshotForStorage(updatedAt); + if(!writeCompactMirror(mirror,{authoritative:true}))throw storageAccessError||new Error('\u30d6\u30e9\u30a6\u30b6\u306e\u4fdd\u5b58\u6a5f\u80fd\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002'); + for(const id of deleted)committedDeleted.add(id); + }else{ + const db=await openWorldDb(),tx=db.transaction(['control','worlds','boardIndex','boardPuzzles','boardStates','outboxV2','recoveryV2','tombstonesV2'],'readwrite'),done=transactionDone(tx), + controlStore=tx.objectStore('control'),worldsStore=tx.objectStore('worlds'),indexStore=tx.objectStore('boardIndex'),puzzleStore=tx.objectStore('boardPuzzles'),stateV2Store=tx.objectStore('boardStates'), + outboxV2Store=tx.objectStore('outboxV2'),recoveryV2Store=tx.objectStore('recoveryV2'),tombstoneV2Store=tx.objectStore('tombstonesV2'), + affectedIds=[...new Set([...metaIds,...stateIds,...deleted])], + rowReads=affectedIds.map(id=>{const key=[data.worldEpoch,id];return Promise.all([requestValue(indexStore.get(key)),requestValue(puzzleStore.get(key)),requestValue(stateV2Store.get(key)),requestValue(tombstoneV2Store.get(key))])}), + [controlRow,storedWorldRow,existingRows]=await Promise.all([requestValue(controlStore.get('active')),requestValue(worldsStore.get(data.worldEpoch)),Promise.all(rowReads)]), + v2Active=controlRow?.activeFormat===FIELD_STORAGE_FORMAT&&controlRow.activeEpoch===data.worldEpoch; + if(!v2Active||!storedWorldRow||storedWorldRow.status!=='active'){try{tx.abort()}catch(_){}await done.catch(()=>{});throw staleWorldEpochError()} + committedGlobal=mergeGlobalRecords(storedWorldRow.global,global); + for(let index=0;index=0&&compareRevisionVersions(tombstone,existingState)>=0){ + indexStore.delete(key);puzzleStore.delete(key);stateV2Store.delete(key);tombstoneV2Store.put({...tombstone,epoch:data.worldEpoch}); + committedMetas.set(id,null);committedStates.set(id,null);committedDeleted.add(id); + }else{committedMetas.set(id,existingMeta||null);committedStates.set(id,existingState||null)} + continue; + } + const metaWinner=localMeta&&(!existingMeta||compareRevisionVersions(localMeta,existingMeta)>=0)?localMeta:existingMeta||null, + tombstoneBlocksMeta=existingTombstone&&compareRevisionVersions(existingTombstone,metaWinner)>=0; + if(tombstoneBlocksMeta||!metaWinner){ + if(tombstoneBlocksMeta){indexStore.delete(key);puzzleStore.delete(key);stateV2Store.delete(key);committedDeleted.add(id)} + committedMetas.set(id,null);committedStates.set(id,null);continue; + } + committedMetas.set(id,metaWinner); + const stateWinner=localState&&(!existingState||compareRevisionVersions(localState,existingState)>=0)?localState:existingState||null; + if(stateWinner&&(!existingTombstone||compareRevisionVersions(stateWinner,existingTombstone)>0)){committedStates.set(id,stateWinner);tombstoneV2Store.delete(key)} + else{stateV2Store.delete(key);committedStates.set(id,null)} + const normalizedMeta=normalizeMeta(id,metaWinner),normalizedState=normalizeState(stateWinner); + if(!normalizedMeta){try{tx.abort()}catch(_){}await done.catch(()=>{});throw new Error(`Cannot persist invalid board ${id}.`)} + if(localMeta||!existingPuzzle)puzzleStore.put(puzzleRecordV2(normalizedMeta,data.worldEpoch)); + if(localStateRow||!existingStateRecord)stateV2Store.put(stateRecordV2(id,normalizedState,data.worldEpoch)); + indexStore.put(summarizeBoardV2(normalizedMeta,normalizedState,data.worldEpoch));tombstoneV2Store.delete(key); + } + for(const key of outboxDeletes)outboxV2Store.delete([data.worldEpoch,key]); + if(cloudApiEnabled){ + for(const id of metaIds)if(cloudJournalMetaIds.has(id)){const key=`meta:${id}`,row={key,type:'meta',id,epoch:data.worldEpoch};outboxV2Store.delete([data.worldEpoch,`deleted:${id}`]);outboxV2Store.put(row)} + for(const id of stateIds)if(cloudJournalStateIds.has(id)){const key=`state:${id}`,row={key,type:'state',id,epoch:data.worldEpoch};outboxV2Store.delete([data.worldEpoch,`deleted:${id}`]);outboxV2Store.put(row)} + for(const id of committedDeleted)if(cloudJournalDeletedIds.has(id)){const version=deletedVersions.get(id)||{},key=`deleted:${id}`,row={epoch:data.worldEpoch,key,type:'deleted',id,rev:version.rev||0,revAuthor:version.revAuthor||'',worldEpoch:data.worldEpoch};outboxV2Store.delete([data.worldEpoch,`meta:${id}`]);outboxV2Store.delete([data.worldEpoch,`state:${id}`]);outboxV2Store.put(row)} + for(const id of deleted)if(!committedDeleted.has(id))outboxV2Store.delete([data.worldEpoch,`deleted:${id}`]); + if(globalChanged)outboxV2Store.put({epoch:data.worldEpoch,key:'global',type:'global'}); + } + const nextWorld={...storedWorldRow,epoch:data.worldEpoch,status:'active',schema:SAVE_SCHEMA,worldGeneration:WORLD_GENERATION,global:committedGlobal,boardCount:Object.keys(data.metas).length,solvedCount:data.solved||0,score:data.score||0,bounds:fieldBoundsFromMetas(data.metas),createdAt:storedWorldRow.createdAt||updatedAt,activatedAt:storedWorldRow.activatedAt||updatedAt,progress:null}; + worldsStore.put(nextWorld); + if(currentRecoverySeq>=0){const key=`covered:${sessionId}`,value={seq:Math.min(currentRecoverySeq,globalSeq),updatedAt,worldEpoch:data.worldEpoch};recoveryV2Store.put({epoch:data.worldEpoch,key,value});recoveryV2Store.delete([data.worldEpoch,`journal:${sessionId}`])} + for(const journal of coveredJournals)if(journal.sessionId&&Number.isSafeInteger(journal.seq)){const key=`covered:${journal.sessionId}`,value={seq:journal.seq,updatedAt,worldEpoch:data.worldEpoch};recoveryV2Store.put({epoch:data.worldEpoch,key,value});if(journal._dbKey)recoveryV2Store.delete([data.worldEpoch,journal._dbKey])} + await done; + updateStorageRevision({databaseUpdatedAt:updatedAt}); + scheduleMirrorCheckpoint(); + } + for(const[id,version]of metaVersions)if(compareRevisionVersions(data.metas[id],version)===0){ + const committed=committedMetas.has(id)?committedMetas.get(id):data.metas[id]; + if(committed){if(compareRevisionVersions(committed,version)!==0)reconciledPersistenceConflict=true;data.metas[id]=normalizeMeta(id,committed)||data.metas[id]} + else{reconciledPersistenceConflict=true;delete data.metas[id];delete data.states[id]} + dirtyMetaIds.delete(id); + } + for(const[id,version]of stateVersions)if(compareRevisionVersions(data.states[id],version)===0){ + const committed=committedStates.has(id)?committedStates.get(id):data.states[id]; + if(committed&&data.metas[id]){if(compareRevisionVersions(committed,version)!==0)reconciledPersistenceConflict=true;data.states[id]=normalizeState(committed);normalizedStateObjects.add(data.states[id])} + else{reconciledPersistenceConflict=true;delete data.states[id]} + dirtyStateIds.delete(id); + } + for(const id of deleted){ + const version=deletedVersions.get(id); + if(deletedBoardRevisions.get(id)!==version.rev||deletedBoardAuthors.get(id)!==version.revAuthor)continue; + if(!committedDeleted.has(id)){ + const committedMeta=committedMetas.get(id),committedState=committedStates.get(id); + if(committedMeta){ + data.metas[id]=normalizeMeta(id,committedMeta)||committedMeta; + if(committedState){data.states[id]=normalizeState(committedState);normalizedStateObjects.add(data.states[id])} + reconciledPersistenceConflict=true; + } + } + deletedBoardIds.delete(id);deletedBoardRevisions.delete(id);deletedBoardAuthors.delete(id); + } + for(const id of deleted)if(!committedDeleted.has(id))cloudJournalDeletedIds.delete(id); + applyGlobalRecordToData(committedGlobal);data.updatedAt=Math.max(data.updatedAt||0,updatedAt);data.clockFloor=Math.max(data.clockFloor||0,updatedAt); + if(activeStorageFormat===FIELD_STORAGE_FORMAT)for(const id of new Set([...metaIds,...stateIds,...deleted])){ + if(data.metas[id]&&data.states[id])boardIndexSummaries.set(id,summarizeBoardV2(data.metas[id],data.states[id],data.worldEpoch));else boardIndexSummaries.delete(id); + } + if(reconciledPersistenceConflict)refreshWorldView({rebuild:true,syncConnections:false,persist:false}); + for(const key of outboxDeletes)cloudOutboxDeleteKeys.delete(key); + if(globalChangeSeq===globalSeq)globalDirty=false; + clearRecoveryJournalIfCovered(globalSeq,coveredJournals); + const signal={commitId:`${sessionId}:${++worldSignalSeq}`,updatedAt,sessionId,worldEpoch:data.worldEpoch,metaIds,stateIds,deleted:[...committedDeleted],globalChanged};broadcastWorldSignal(signal);if(!skipCloud)scheduleCloudPush(signal); + perfGauge('lastPersistRows',metaRows.length+stateRows.length+deleted.length);perfEnd('persistDirtyToDb',started); + return{updatedAt,count:metaRows.length+stateRows.length+deleted.length,skipped:false}; +} +async function persistNow(options={}){ + if(lifecyclePersistenceSuppressed)return false; + if(options.lifecycle!==true&&globalThis.document?.body?.classList?.contains?.('is-interacting')){perfCount('savesDeferredDuringInteraction');await waitForInteractionSettle(null)} + clearTimeout(saveTimer);saveTimer=null; + const run=async()=>{if(lifecyclePersistenceSuppressed)return false;try{ + const result=await(options.lockHeld===true?persistDirtyToDb(options):withWorldMutationLock(()=>persistDirtyToDb(options))); + if(!result.skipped){setSaveStatus('saved','\u4fdd\u5b58\u3057\u307e\u3057\u305f');hideStatus();if(result.count>0&&startupWorldControl?.activationVerified===false){try{await verifyActiveWorldActivation()}catch(error){const restored=await rollbackUnverifiedWorld(await openWorldDb(),startupWorldControl).catch(()=>null);if(restored){lifecyclePersistenceSuppressed=true;rememberWorldEpoch(restored.activeEpoch);announceWorldReplacement(restored.activeEpoch);location.reload();return false}throw error}}} + if(hasPendingPersistence()){clearTimeout(saveTimer);saveTimer=setTimeout(runDeferredSave,SAVE_DELAY)} + return true; + }catch(error){ + if(error?.code==='STALE_WORLD_EPOCH'){lifecyclePersistenceSuppressed=true;setSaveStatus('error','\u5225\u306e\u30bf\u30d6\u3067\u66f4\u65b0\u3055\u308c\u307e\u3057\u305f');showStatus(error.message,{retry:false,fresh:false});setTimeout(()=>location.reload(),80);return false} + setSaveStatus('error','\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002');showStatus('\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002',{retry:true,fresh:false});console.warn('BEND FIELD: save failed',error);return false}}; + if(options.lockHeld===true)return run(); + const result=persistQueue.then(run,run);persistQueue=result.then(()=>{},()=>{});return result; +} +function flushSave(options={}){return persistNow(options)} +function runDeferredSave(){ + if(document.body?.classList?.contains('is-interacting')){clearTimeout(saveTimer);saveTimer=setTimeout(runDeferredSave,SAVE_DELAY);perfCount('savesDeferredDuringInteraction');return} + void persistNow(); +} +function save(immediate=false,options={}){ + if(lifecyclePersistenceSuppressed)return immediate?Promise.resolve(false):false; + if(!hasPendingPersistence())return immediate?Promise.resolve(true):true; + if(immediate)return persistNow(options); + setSaveStatus('saving','\u4fdd\u5b58\u4e2d');clearTimeout(saveTimer);saveTimer=setTimeout(runDeferredSave,SAVE_DELAY);return true; +} +function updatePlayerNameUi(){if(!playerNameBtn)return;const name=currentPlayerName();playerNameBtn.textContent=name;playerNameBtn.title=`プレイヤー名を変更:${name}`} +function updateHud(){pruneAndCount();worldCountEl.textContent=Object.keys(data.metas).length;updatePlayerNameUi();applyUiSettings();syncCursorAppearance(data.cursorStyle||'default');document.body.dataset.debugItems=data.debugAllItems?'on':'off';updateInventoryUi();updateTimeAttackUi()} +function changed(id,pathIndex=null){const st=metaState(id);st.rev=nextRevision();st.revAuthor=sessionId;markStateDirty(id,pathIndex);save()} +const SHAPES_BY_SIZE=new Map();for(const s of SHAPES){const n=s.length;if(!SHAPES_BY_SIZE.has(n))SHAPES_BY_SIZE.set(n,[]);SHAPES_BY_SIZE.get(n).push(s)} +function shapeCandidatesForLevel(seed,level,attemptsPerSize=12){return AppLogic.shapeCandidatesForLevel(seed,level,attemptsPerSize,{shapesBySize:SHAPES_BY_SIZE,hash32,rngFrom,shuffle})} +function gateConnectionAllowed(meta,gateIndex){ + if(!meta?.puzzle||!Number.isInteger(gateIndex))return false; + const gate=puzzleOf(meta).g[gateIndex];return Boolean(gate&&!(meta.sealedSides||[]).includes(gate[2])); +} +function boardMeta(id){return data.metas[id]} +function puzzleOf(meta){ + if(!meta?.puzzle)throw new Error(`\u76e4\u9762${meta?.id||'?'}\u306e\u8aad\u307f\u8fbc\u307f\u304c\u5b8c\u4e86\u3057\u3066\u3044\u307e\u305b\u3093\u3002`); + if(meta.puzzle.difficulty==null) + meta.puzzle.difficulty=solverDifficulty(meta.puzzle,meta.targetLevel??meta.level??1); + if(meta.level!==meta.puzzle.difficulty){meta.level=meta.puzzle.difficulty;markMetaDirty(meta.id)} + return meta.puzzle +} +function validateHydratedMeta(meta){ + if(!meta)throw new Error('\u76e4\u9762\u60c5\u5831\u304c\u3042\u308a\u307e\u305b\u3093\u3002'); + if(!meta.puzzle)throw new Error(`\u76e4\u9762${meta.id}\u306e\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093\u3002`); + puzzleOf(meta);sanitizeStateForPuzzle(meta); + if(data.quarantine)delete data.quarantine[meta.id]; + return meta; +} +function rememberHydratedBoard(id,bytes){ + const previous=hydratedBoardLru.get(id);if(previous)hydratedBoardBytes-=previous.bytes; + hydratedBoardLru.delete(id);hydratedBoardLru.set(id,{bytes});hydratedBoardBytes+=bytes;evictHydratedBoardDetails(); +} +function hydratedBoardPinned(id){return id===activeBoard||dirtyMetaIds.has(id)||dirtyStateIds.has(id)||rendered.has(id)||staticRendered.has(id)||pendingVisibleHydrations?.has?.(id)||openStoreBoardId===id} +function evictHydratedBoardDetails(){ + let attempts=0; + while((hydratedBoardLru.size>32||hydratedBoardBytes>8*1024*1024)&&attemptspath&&Array.isArray(path.cells)&&pathUsesGate(path,gi))||null} +function pathColorIndexAtGate(path,gi){ + if(!path)return null; + if(!path.detachedStart&&path.startGate===gi)return path.startColorIndex??path.colorIndex??0; + if(path.endGate===gi||path.openGate===gi)return path.endColorIndex??path.colorIndex??path.startColorIndex??0; + return path.colorIndex??0; +} +function setPathColorAtGate(path,gi,colorIndex){ + if(!path||colorIndex==null)return false; + const key=!path.detachedStart&&path.startGate===gi?'startColorIndex':path.endGate===gi||path.openGate===gi?'endColorIndex':null; + if(!key||path[key]===colorIndex)return false; + path[key]=colorIndex; + if(key==='startColorIndex')path.colorIndex=colorIndex; + return true; +} +function normalizeLineColorIndex(value){return Number.isInteger(value)?((value%LINE_COLORS.length)+LINE_COLORS.length)%LINE_COLORS.length:null} +function boundaryColorSource(meta,gi,hit=matchingNeighborGate(meta,gi)){ + if(!hit)return null; + const st=metaState(meta.id),neighborState=metaState(hit.meta.id),own=gateUsedPath(st,gi),other=gateUsedPath(neighborState,hit.gateIndex),candidates=[]; + if(own)candidates.push({meta,gateIndex:gi,state:st,path:own,colorIndex:normalizeLineColorIndex(pathColorIndexAtGate(own,gi))}); + if(other)candidates.push({meta:hit.meta,gateIndex:hit.gateIndex,state:neighborState,path:other,colorIndex:normalizeLineColorIndex(pathColorIndexAtGate(other,hit.gateIndex))}); + const usable=candidates.filter(candidate=>candidate.colorIndex!=null);if(!usable.length)return null; + usable.sort((a,b)=>Number(b.state.solved)-Number(a.state.solved)||(a.state.rev||0)-(b.state.rev||0)||String(a.meta.id).localeCompare(String(b.meta.id))||a.gateIndex-b.gateIndex); + return usable[0]; +} +function confirmedBoundaryColorIndex(meta,gi){ + const hit=matchingNeighborGate(meta,gi);if(!hit)return null; + const st=metaState(meta.id),neighborState=metaState(hit.meta.id),own=gateUsedPath(st,gi),other=gateUsedPath(neighborState,hit.gateIndex); + if(!own&&!other)return null; + if(!st.solved&&!neighborState.solved)return null; + return boundaryColorSource(meta,gi,hit)?.colorIndex??null; +} +function sharedBoundaryColorIndex(meta,gi){const hit=matchingNeighborGate(meta,gi);return hit?boundaryColorSource(meta,gi,hit)?.colorIndex??null:null} +function displayedEndpointColorIndex(meta,path,side='end'){if(!path)return 0;let gateIndex=null;if(side==='start'&&!path.detachedStart)gateIndex=path.startGate;else if(side==='end'){if(Number.isInteger(path.openGate))gateIndex=path.openGate;else if(Number.isInteger(path.endGate))gateIndex=path.endGate;else if(path.cells?.length===1&&!path.detachedStart)gateIndex=path.startGate}const shared=Number.isInteger(gateIndex)?sharedBoundaryColorIndex(meta,gateIndex):null;if(shared!=null)return shared;return side==='start'?(path.startColorIndex??path.colorIndex??0):(path.endColorIndex??path.startColorIndex??path.colorIndex??0)} +function neighborColor(meta,gi){return sharedBoundaryColorIndex(meta,gi)} +function addInheritedBoundaryPath(meta,gi,colorIndex){ + const st=metaState(meta.id);if(st.solved||gateUsedPath(st,gi))return false; + const p=puzzleOf(meta),g=gateObj(p,gi),occ=occupiedMap(st);if(occ.has(ckey(...g.cell)))return false; + st.paths.push({startGate:gi,endGate:null,openGate:null,cells:[[g.cell[0],g.cell[1]]],colorIndex,startColorIndex:colorIndex,endColorIndex:null});return true; +} +function syncBoundaryConnections({rebuild=true,persist=true,updateProgress=true}={}){ + const touched=new Set(),processed=new Set(); + if(rebuild)rebuildOccupancy(); + const candidateIds=new Set(); + if(!rebuild&&dirtyStateIds.size){ + for(const id of dirtyStateIds){ + const meta=data.metas[id];if(!meta)continue;candidateIds.add(id); + for(const[dx,dy]of meta.chunks||[]){const unitX=meta.x+dx,unitY=meta.y+dy;for(const[dr,dc]of Object.values(SIDE_D)){const neighborId=occupancy.get(key2(unitX+dc,unitY+dr));if(neighborId)candidateIds.add(neighborId)}} + } + }else for(const id of Object.keys(data.metas))candidateIds.add(id); + for(const id of candidateIds){ + const meta=data.metas[id]; + if(!meta?.puzzle)continue; + const st=metaState(meta.id),p=puzzleOf(meta); + for(let gi=0;girefreshWorldView(options)); + const{rebuild=true,syncConnections=true,markStats=false,resumeTimer=false,hide=false,persist=false,immediate=false,lockHeld=false}=options; + if(rebuild)rebuildOccupancy(); + if(syncConnections)syncBoundaryConnections({rebuild:false,persist:false,updateProgress:false}); + if(markStats)statsDirty=true; + ensureBoards();renderAll();updateHud();updateSelectedProgress(rendered.get(activeBoard)); + if(resumeTimer)resumeTimeAttackTimer();if(hide)hideStatus(); + return persist?save(immediate,{lockHeld}):true; +} +function waitForInteractionSettle(counter='worldRefreshesDeferredDuringInteraction'){ + if(!globalThis.document?.body?.classList?.contains?.('is-interacting'))return Promise.resolve(); + if(counter)perfCount(counter); + return new Promise(resolve=>{const check=()=>globalThis.document?.body?.classList?.contains?.('is-interacting')?setTimeout(check,40):resolve();check()}); +} + +function fitsMeta(x,y,chunks){ + for(const[dx,dy]of chunks)if(occupancy.has(key2(x+dx,y+dy)))return false; + return true; +} +function placedShapeKeys(x,y,chunks){return new Set(chunks.map(([dx,dy])=>key2(x+dx,y+dy)))} +function unitOccupiedWithExtra(unitX,unitY,extraOccupied=null){const key=key2(unitX,unitY);return occupancy.has(key)||extraOccupied?.has(key)} +function isClosedVoidWithExtra(unitX,unitY,extraOccupied=null){ + if(unitOccupiedWithExtra(unitX,unitY,extraOccupied))return false; + let count=0;for(const[dr,dc]of Object.values(SIDE_D))if(unitOccupiedWithExtra(unitX+dc,unitY+dr,extraOccupied))count++; + return count===4; +} +function openUnitFrontiers(meta,extraOccupied=null){ + if(!meta?.chunks?.length)return[]; + const own=new Set(meta.chunks.map(([dx,dy])=>key2(meta.x+dx,meta.y+dy))),frontiers=new Map(); + for(const[dx,dy]of meta.chunks){ + const unitX=meta.x+dx,unitY=meta.y+dy; + for(const side of['N','S','W','E']){ + const[dr,dc]=SIDE_D[side],targetX=unitX+dc,targetY=unitY+dr,key=key2(targetX,targetY); + if(own.has(key)||unitOccupiedWithExtra(targetX,targetY,extraOccupied)||isClosedVoidWithExtra(targetX,targetY,extraOccupied))continue; + if(!frontiers.has(key))frontiers.set(key,{unitX:targetX,unitY:targetY,side,targetSide:OPP[side]}); + } + } + return[...frontiers.values()]; +} +function shapeFitsWithExtra(x,y,chunks,extraOccupied=null){ + for(const[dx,dy]of chunks){const key=key2(x+dx,y+dy);if(occupancy.has(key)||extraOccupied?.has(key))return false} + return true; +} +function viableFuturePlacementAt(unitX,unitY,extraOccupied=null){ + if(unitOccupiedWithExtra(unitX,unitY,extraOccupied)||isClosedVoidWithExtra(unitX,unitY,extraOccupied))return false; + const targetLevel=macroDifficulty(unitX,unitY), + seed=hash32(Math.imul(unitX+0x3191,73856093)^Math.imul(unitY-0x47b5,19349663)^0x51ed270b), + candidates=shapeCandidatesForLevel(seed,targetLevel,5); + for(const shape of candidates)for(const[dx,dy]of shape)if(shapeFitsWithExtra(unitX-dx,unitY-dy,shape,extraOccupied))return true; + return false; +} +function viableExpansionExists(meta,extraOccupied=null){ + for(const frontier of openUnitFrontiers(meta,extraOccupied))if(viableFuturePlacementAt(frontier.unitX,frontier.unitY,extraOccupied))return true; + return false; +} +function prospectiveShapeHasFrontier(x,y,chunks){ + const own=placedShapeKeys(x,y,chunks),prospective={x,y,chunks}; + return viableExpansionExists(prospective,own); +} +function placementPreservesUnsolvedFrontiers(x,y,chunks,sourceId=null,{allowTerminal=false}={}){ + const extra=placedShapeKeys(x,y,chunks); + const terminalFill=allowTerminal&&chunks.length===1&&isClosedVoidUnit(x,y), + connectedMetaIds=terminalFill?new Set(placementConnectionRequirements(x,y,chunks).map(requirement=>requirement.metaId)):null; + if(!terminalFill&&!prospectiveShapeHasFrontier(x,y,chunks))return false; + const affectedIds=new Set(); + for(const key of extra){const[unitX,unitY]=key.split(',').map(Number);for(const[dr,dc]of Object.values(SIDE_D)){const id=occupancy.get(key2(unitX+dc,unitY+dr));if(id)affectedIds.add(id)}} + for(const id of affectedIds){ + const meta=data.metas[id]; + if(!meta?.chunks?.length||meta.id===sourceId)continue;const state=metaState(meta.id); + if(state.solved&&state.expanded)continue; + const viableBefore=viableExpansionExists(meta); + if(viableBefore&&!viableExpansionExists(meta,extra)&&!connectedMetaIds?.has(meta.id))return false; + } + return true; +} +function storeWorldCenter(meta){ + const count=Math.max(1,meta.chunks.length), + x=meta.x+.5+meta.chunks.reduce((sum,chunk)=>sum+chunk[0],0)/count, + y=meta.y+.5+meta.chunks.reduce((sum,chunk)=>sum+chunk[1],0)/count; + return[x,y]; +} +function worldSeedValue(){return data.metas.B0?.seed??STARTER_SEED} +function storePriceLocation(meta,store){ + const path=store&&metaState(meta.id).paths[store.pathIndex],cell=path?.cells?.[store.cellIndex]; + return cell?[meta.x+(cell[1]+.5)/CHUNK,meta.y+(cell[0]+.5)/CHUNK]:storeWorldCenter(meta); +} +function storeCellForMeta(meta){ + const state=meta&&metaState(meta.id),store=state?.store,path=store&&state.paths[store.pathIndex],cell=path?.cells?.[store.cellIndex]; + return Array.isArray(cell)?cell:Array.isArray(store?.summaryCell)?store.summaryCell:null; +} +function storePriceDetails(meta,store){ + const[x,y]=storePriceLocation(meta,store),priceVersion=store?.priceVersion||STORE_PRICE_VERSION, + computed=AppLogic.deterministicStorePrice(1,worldSeedValue(),x,y,priceVersion), + coefficient=Number.isFinite(store?.priceCoefficient)?store.priceCoefficient:computed.coefficient; + return{coefficient,priceVersion,x,y}; +} +function storeItemPrice(meta,store,item){ + const coefficient=storePriceDetails(meta,store).coefficient,baseCost=Math.max(0,Number(item?.cost)||0); + const adjusted=Math.round(baseCost*coefficient); + if(item?.id?.startsWith('cursor-face-'))return Math.max(MIN_CURSOR_PRICE,Math.min(MAX_FACE_CURSOR_PRICE,adjusted)); + return Math.max(item?.cursorStyle?MIN_CURSOR_PRICE:MIN_STORE_ITEM_PRICE,adjusted); +} +let storeEffectSourcesCache=null,storeEffectSpatialCache=null; +function targetLevelForNewBoard(x,y,chunks){ + const count=Math.max(1,chunks.length),centerX=x+.5+chunks.reduce((sum,chunk)=>sum+chunk[0],0)/count,centerY=y+.5+chunks.reduce((sum,chunk)=>sum+chunk[1],0)/count; + return macroDifficulty(centerX,centerY); +} +let worldMutationQueue=Promise.resolve(); +function sleep(ms){return new Promise(resolve=>setTimeout(resolve,ms))} +async function withStorageLease(name,task){ + const key=`${name}:lease`,token=`${sessionId}:${Math.random().toString(36).slice(2)}`,deadline=Date.now()+20000; + if(storageAccessError)return task(); + while(Date.now(){let current=null;try{current=JSON.parse(safeLocalGet(key)||'null')}catch(_){}if(current?.token===token)safeLocalSet(key,JSON.stringify({token,expiresAt:Date.now()+15000}))},5000); + try{return await task()}finally{clearInterval(renew);let current=null;try{current=JSON.parse(safeLocalGet(key)||'null')}catch(_){}if(current?.token===token)safeLocalRemove(key)} + }} + await sleep(35+Math.random()*90); + } + throw new Error('\u5225\u306e\u30bf\u30d6\u3067\u66f4\u65b0\u3057\u3066\u3044\u307e\u3059\u3002'); +} +function withWorldMutationLock(task){ + return navigator.locks?.request?navigator.locks.request(worldLockName,{mode:'exclusive'},task):withStorageLease(worldLockName,task); +} +function enqueueWorldMutation(task){ + const run=()=>withWorldMutationLock(task); + const result=worldMutationQueue.then(run,run);worldMutationQueue=result.catch(()=>{});return result; +} +function occupiedNeighborCount(unitX,unitY){ + let count=0; + for(const[dx,dy]of[[0,-1],[0,1],[-1,0],[1,0]])if(occupancy.has(key2(unitX+dx,unitY+dy)))count++; + return count; +} +function isClosedVoidUnit(unitX,unitY){return!occupancy.has(key2(unitX,unitY))&&occupiedNeighborCount(unitX,unitY)===4} +function frontierCandidates(meta){ + if(!meta)return[]; + const own=new Set(meta.chunks.map(([dx,dy])=>key2(meta.x+dx,meta.y+dy))),map=new Map(); + for(const[dx,dy]of meta.chunks){ + const unitX=meta.x+dx,unitY=meta.y+dy; + for(const side of['N','S','W','E']){ + const[dr,dc]=SIDE_D[side],targetX=unitX+dc,targetY=unitY+dr,key=key2(targetX,targetY); + if(own.has(key)||occupancy.has(key)||isClosedVoidUnit(targetX,targetY))continue; + const existing=map.get(key)||{unitX:targetX,unitY:targetY,side,targetSide:OPP[side],contacts:[]}; + existing.contacts.push({unitX,unitY,side});map.set(key,existing); + } + } + return[...map.values()].sort((a,b)=>{ + const ah=hash32(meta.seed^Math.imul(a.unitX+8191,73856093)^Math.imul(a.unitY-12289,19349663)),bh=hash32(meta.seed^Math.imul(b.unitX+8191,73856093)^Math.imul(b.unitY-12289,19349663)); + return(ah>>>0)-(bh>>>0)||a.unitY-b.unitY||a.unitX-b.unitX; + }); +} +function repairFacingGateConnections(meta){ + if(!meta?.puzzle)return 0; + const p=puzzleOf(meta);let changed=0; + for(let gateIndex=0;gateIndexside!==OPP[g[2]]); + neighbor.rev=nextRevision();markMetaDirty(neighbor.id);changed++; + } + if(changed)adjacencyCache.clear(); + return changed; +} +function gateFrontierCandidates(meta){ + if(!meta?.puzzle)return[]; + const p=puzzleOf(meta),map=new Map(); + for(let gateIndex=0;gateIndex{ + const ai=Math.min(...a.contacts.map(contact=>contact.gateIndex)),bi=Math.min(...b.contacts.map(contact=>contact.gateIndex)); + return ai-bi||a.unitY-b.unitY||a.unitX-b.unitX; + }); +} +function unresolvedExpansionCandidates(meta){return gateFrontierCandidates(meta)} +function closedVoidRepairCandidates(){ + const candidates=[]; + for(const key of closedVoidKeys){ + const[unitX,unitY]=key.split(',').map(Number);if(!isClosedVoidUnit(unitX,unitY)){closedVoidKeys.delete(key);continue} + let candidate=null; + for(const side of['N','S','W','E']){ + const[dr,dc]=SIDE_D[side],sourceId=occupancy.get(key2(unitX-dc,unitY-dr)),source=data.metas[sourceId]; + if(!source?.chunks?.length||!metaState(source.id).solved)continue; + const gateFrontier=gateFrontierCandidates(source).find(frontier=>frontier.unitX===unitX&&frontier.unitY===unitY); + candidate={source,frontier:gateFrontier||{unitX,unitY,side,targetSide:OPP[side],contacts:[],gateDriven:false,terminalFill:true,closedVoidRepair:true}};break; + } + if(candidate)candidates.push(candidate); + } + return candidates.sort((left,right)=>left.frontier.unitY-right.frontier.unitY||left.frontier.unitX-right.frontier.unitX||left.source.id.localeCompare(right.source.id,undefined,{numeric:true})); +} +function missingGateConnections(meta){ + if(!meta?.puzzle)return[]; + const p=puzzleOf(meta),missing=[]; + for(let gateIndex=0;gateIndexvalue===b[index])} +function fixedPortProfilesForRequirements(requirements){ + const groups=new Map(); + for(const requirement of requirements){ + const unitX=Math.floor(requirement.neighborCol/5),unitY=Math.floor(requirement.neighborRow/5),side=requirement.targetSide; + let key,offset,profiles; + if(side==='N'||side==='S'){ + key=horizontalBoundaryKey(unitX,side==='N'?unitY:unitY+1);offset=requirement.neighborCol-unitX*5;profiles=H_PORT_PROFILES; + }else{ + key=verticalBoundaryKey(side==='W'?unitX:unitX+1,unitY);offset=requirement.neighborRow-unitY*5;profiles=V_PORT_PROFILES; + } + const group=groups.get(key)||{profiles,offsets:new Set()};group.offsets.add(offset);groups.set(key,group); + } + const fixed={}; + for(const[key,{profiles,offsets}]of groups){ + const values=[...offsets].sort((a,b)=>a-b),index=profiles.findIndex(profile=>sameNumberList(profile,values)); + if(index<0)return null; + fixed[key]=index; + } + return fixed; +} +function generatedPuzzleIssue(puzzle){ + if(!puzzle||!Array.isArray(puzzle.valid)||!Array.isArray(puzzle.g)||!Array.isArray(puzzle.n)||!Array.isArray(puzzle.solution))return'\u76e4\u9762\u30c7\u30fc\u30bf\u4e0d\u8db3'; + if(!puzzle.valid.length||!puzzle.g.length||puzzle.g.length%2||puzzle.solution.length!==puzzle.n.length)return'\u7dda\u3068\u30b2\u30fc\u30c8\u306e\u6570\u304c\u4e0d\u6b63'; + const special=specialCellSet(puzzle),crossingCoverage=new Set(special.crossings.map(cell=>ckey(...cell))),valid=new Set(),coverage=new Map(),usedGates=new Set(); + for(const cell of puzzle.valid){const key=ckey(...cell);if(valid.has(key))return'\u6709\u52b9\u30bb\u30eb\u91cd\u8907';valid.add(key)} + for(const path of puzzle.solution){ + if(!path||!Array.isArray(path.cells)||!path.cells.length||!Number.isInteger(path.startGate)||!Number.isInteger(path.endGate)||path.startGate===path.endGate)return'\u89e3\u7b54\u7dda\u4e0d\u6b63'; + if(path.startGate<0||path.endGate<0||path.startGate>=puzzle.g.length||path.endGate>=puzzle.g.length||usedGates.has(path.startGate)||usedGates.has(path.endGate))return'\u30b2\u30fc\u30c8\u91cd\u8907'; + usedGates.add(path.startGate);usedGates.add(path.endGate); + if(!sameCell(path.cells[0],puzzle.g[path.startGate])||!sameCell(path.cells[path.cells.length-1],puzzle.g[path.endGate]))return'\u89e3\u7b54\u7dda\u3068\u30b2\u30fc\u30c8\u306e\u4e0d\u6574\u5408'; + const own=new Set(); + for(let index=0;index=maxObstacles)break; + const adjacent=[[1,0],[-1,0],[0,1],[0,-1]].some(([dr,dc])=>planned.has(ckey(r+dr,c+dc))),rate=Math.min(.18,baseRate+(adjacent?.09:0)); + if(rng()candidate.cells.length<=remaining); + if(!candidates.length)break; + candidates.sort((a,b)=>{ + const aPlanned=a.cells.reduce((sum,cell)=>sum+(planned.has(ckey(...cell))?1:0),0),bPlanned=b.cells.reduce((sum,cell)=>sum+(planned.has(ckey(...cell))?1:0),0), + aAdjacent=a.cells.some(([r,c])=>[[1,0],[-1,0],[0,1],[0,-1]].some(([dr,dc])=>obstacles.has(ckey(r+dr,c+dc))))?1:0, + bAdjacent=b.cells.some(([r,c])=>[[1,0],[-1,0],[0,1],[0,-1]].some(([dr,dc])=>obstacles.has(ckey(r+dr,c+dc))))?1:0; + return(bPlanned*100+bAdjacent*12-b.cells.length)-(aPlanned*100+aAdjacent*12-a.cells.length)||a.cells.length-b.cells.length||rng()-.5; + }); + const chosen=candidates[0],path=puzzle.solution[chosen.pathIndex];path.cells=[...path.cells.slice(0,chosen.start),...path.cells.slice(chosen.end+1)];for(const cell of chosen.cells)obstacles.set(ckey(...cell),cell); + } + if(!obstacles.size){puzzle.obstacles=[];return puzzle} + puzzle.obstacles=[...obstacles.values()];puzzle.valid=puzzle.valid.filter(cell=>!obstacles.has(ckey(...cell)));puzzle.n=[];let maxTurns=0,totalTurns=0; + const clueRng=rngFrom(hash32(seed^0x2c1b3c6d)); + for(const path of puzzle.solution){const analysis=turnAnalysis(path,puzzle);if(!analysis.count||!analysis.cells.length)return sourcePuzzle;const clue=analysis.cells[Math.floor(clueRng()*analysis.cells.length)];puzzle.n.push([clue[0],clue[1],analysis.count]);maxTurns=Math.max(maxTurns,analysis.count);totalTurns+=analysis.count} + puzzle.maxTurns=maxTurns;puzzle.totalTurns=totalTurns;puzzle.style=`${puzzle.style||'procedural'}-obstacles`; + puzzle.difficulty=sourcePuzzle.difficulty;puzzle.level=sourcePuzzle.level??sourcePuzzle.difficulty + return puzzle; +} +function specialCellSet(p){ + if(!p.specialCells)p.specialCells={crossings:[],warps:[],locks:[]}; + if(!Array.isArray(p.specialCells.crossings))p.specialCells.crossings=[]; + if(!Array.isArray(p.specialCells.warps))p.specialCells.warps=[]; + if(!Array.isArray(p.specialCells.locks))p.specialCells.locks=[]; + return p.specialCells; +} +function invalidateSpecialCellCaches(p){if(p){delete p._warpMap}} +function reservedSpecialKeys(p){ + const keys=new Set(),special=specialCellSet(p); + for(const cell of special.crossings)keys.add(ckey(...cell)); + for(const pair of special.warps){keys.add(ckey(...pair.a));keys.add(ckey(...pair.b))} + for(const lock of special.locks){keys.add(ckey(...lock.key));keys.add(ckey(...lock.door))} + return keys; +} +function rebuildSolutionClues(p,seed){ + const reserved=reservedSpecialKeys(p),rng=rngFrom(hash32(seed^0x3c6ef372));p.n=[];let maxTurns=0,totalTurns=0; + for(const path of p.solution){ + const analysis=turnAnalysis(path,p);if(!analysis.count||!analysis.cells.length)return false; + const pool=analysis.cells.filter(cell=>!reserved.has(ckey(...cell))),candidates=pool.length?pool:analysis.cells, + clue=candidates[Math.floor(rng()*candidates.length)]; + p.n.push([clue[0],clue[1],analysis.count]);maxTurns=Math.max(maxTurns,analysis.count);totalTurns+=analysis.count; + } + p.maxTurns=maxTurns;p.totalTurns=totalTurns;return true; +} +function addWarpSpecial(p,rng,reserved){ + const candidates=[]; + p.solution.forEach((path,pathIndex)=>{ + const cells=path.cells; + for(let i=1;ib.distance-a.distance||rng()-.5);const chosen=candidates[Math.floor(rng()*Math.min(8,candidates.length))],path=p.solution[chosen.pathIndex],cells=path.cells; + path.cells=[...cells.slice(0,chosen.i+1),...cells.slice(chosen.i+1,chosen.j+1).reverse(),...cells.slice(chosen.j+1)]; + specialCellSet(p).warps.push({a:chosen.a,b:chosen.b});invalidateSpecialCellCaches(p);reserved.add(ckey(...chosen.a));reserved.add(ckey(...chosen.b));return true; +} +function addLockSpecial(p,rng,reserved){ + const candidates=[]; + p.solution.forEach((path,pathIndex)=>{ + const cells=path.cells; + for(let keyIndex=1;keyIndexb.span-a.span||rng()-.5);const chosen=candidates[Math.floor(rng()*Math.min(10,candidates.length))]; + specialCellSet(p).locks.push({key:chosen.key,door:chosen.door});reserved.add(ckey(...chosen.key));reserved.add(ckey(...chosen.door));return true; +} +function buildCrossingTemplate(p,reserved){ + if(reserved.size)return false; + const allCells=[...(p.valid||[]),...(p.obstacles||[])],chunkSet=new Set(allCells.map(([r,c])=>key2(Math.floor(c/5),Math.floor(r/5)))),chunks=[...chunkSet].map(key=>key.split(',').map(Number)); + const directions=[['N',0,-1],['E',1,0],['S',0,1],['W',-1,0]],rotateSide=(side,turns)=>directions[(directions.findIndex(item=>item[0]===side)+turns)%4][0],rotateCell=([r,c],turns)=>{let row=r,col=c;for(let i=0;i!chunkSet.has(key2(chunkX+dx,chunkY+dy))).map(item=>item[0])); + for(let turns=0;turns<4;turns++)if(external.has(rotateSide('N',turns))&&external.has(rotateSide('W',turns))){chosen={chunkX,chunkY,turns};break} + if(chosen)break; + } + if(!chosen)return false; + const basePaths=[ + {startGate:0,endGate:1,openGate:null,cells:[[2,0],[1,0],[1,1],[2,1],[2,2],[2,3],[3,3],[4,3],[4,4],[3,4],[2,4],[1,4],[1,3],[0,3]]}, + {startGate:2,endGate:3,openGate:null,cells:[[0,1],[0,2],[1,2],[2,2],[3,2],[4,2],[4,1],[4,0]]} + ],baseGates=[[2,0,'W'],[0,3,'N'],[0,1,'N'],[4,0,'W']],offsetCell=cell=>{const[row,col]=rotateCell(cell,chosen.turns);return[chosen.chunkY*5+row,chosen.chunkX*5+col]}; + p.solution=basePaths.map(path=>({...path,cells:path.cells.map(offsetCell)})); + p.g=baseGates.map(([r,c,side])=>{const[row,col]=offsetCell([r,c]);return[row,col,rotateSide(side,chosen.turns)]}); + const crossing=offsetCell([2,2]),validKeys=new Set();for(const path of p.solution)for(const cell of path.cells)validKeys.add(ckey(...cell)); + const nextValid=allCells.filter(cell=>validKeys.has(ckey(...cell))),nextObstacles=allCells.filter(cell=>!validKeys.has(ckey(...cell))); + if(nextObstacles.length>obstacleCellLimit(p))return false; + p.valid=nextValid;p.obstacles=nextObstacles;p.n=[];p.specialCells={crossings:[crossing],warps:[],locks:[]};invalidateSpecialCellCaches(p);reserved.add(ckey(...crossing));return true; +} +function addCrossingSpecial(p,rng,reserved){ + const valid=cellSet(p),gateCells=new Set(p.g.map(g=>ckey(g[0],g[1]))),candidates=[]; + p.solution.forEach((basePath,basePathIndex)=>{ + for(let baseIndex=1;baseIndex!valid.has(ckey(...candidate))))continue; + const baseAxis=previous[0]===cell[0]&&next[0]===cell[0]?'H':previous[1]===cell[1]&&next[1]===cell[1]?'V':null; + if(!baseAxis)continue; + const [entry,exit]=baseAxis==='H'?[north,south]:[west,east]; + p.solution.forEach((crossPath,crossPathIndex)=>{ + if(crossPathIndex===basePathIndex||crossPath.cells.some(candidate=>sameCell(candidate,cell)))return; + const entryIndex=crossPath.cells.findIndex(candidate=>sameCell(candidate,entry)),exitIndex=crossPath.cells.findIndex(candidate=>sameCell(candidate,exit)); + if(entryIndex<0||exitIndex<0||Math.abs(entryIndex-exitIndex)<2)return; + const lo=Math.min(entryIndex,exitIndex),hi=Math.max(entryIndex,exitIndex),removed=crossPath.cells.slice(lo+1,hi); + if(!removed.length||(p.obstacles?.length||0)+removed.length>obstacleCellLimit(p)||removed.some(candidate=>sameCell(candidate,cell)||reserved.has(ckey(...candidate))||gateCells.has(ckey(...candidate))))return; + for(let index=lo+1;index<=hi;index++)if(isWarpTransition(p,crossPath.cells[index-1],crossPath.cells[index]))return; + candidates.push({cell:[...cell],basePathIndex,crossPathIndex,lo,hi,removed:removed.map(candidate=>[...candidate]),score:removed.length+Math.min(baseIndex,basePath.cells.length-1-baseIndex)}); + }); + } + }); + if(!candidates.length)return buildCrossingTemplate(p,reserved); + candidates.sort((a,b)=>a.removed.length-b.removed.length||b.score-a.score||rng()-.5);const chosen=candidates[Math.floor(rng()*Math.min(12,candidates.length))],crossPath=p.solution[chosen.crossPathIndex]; + crossPath.cells=[...crossPath.cells.slice(0,chosen.lo+1),[...chosen.cell],...crossPath.cells.slice(chosen.hi)]; + const removedKeys=new Set(chosen.removed.map(cell=>ckey(...cell))); + p.valid=p.valid.filter(cell=>!removedKeys.has(ckey(...cell)));p.obstacles=[...(p.obstacles||[]),...chosen.removed]; + specialCellSet(p).crossings.push(chosen.cell);invalidateSpecialCellCaches(p);reserved.add(ckey(...chosen.cell)); + return Boolean(crossingStateAtCell({paths:p.solution},p,chosen.cell)); +} + +function recentSpecialMechanicTypes(limit=3){ + const result=[],metas=Object.values(data.metas||{}).sort((left,right)=>(Number(right.id?.slice(1))||0)-(Number(left.id?.slice(1))||0)); + for(const meta of metas)for(const type of mechanicTypesForPuzzle(meta.puzzle)){result.push(type);if(result.length>=limit)return result.reverse()} + return result.reverse(); +} +function addScheduledSpecial(puzzle,type,rng,reserved){ + if(type==='warp')return addWarpSpecial(puzzle,rng,reserved); + if(type==='lock')return addLockSpecial(puzzle,rng,reserved); + if(type==='crossing')return addCrossingSpecial(puzzle,rng,reserved); + return false; +} +function specialCellUsage(puzzle){ + const special=specialCellSet(puzzle); + return(special.crossings?.length||0)+(special.warps?.length||0)*2+(special.locks?.length||0)*2; +} +function addSpecialCellPattern(sourcePuzzle,seed,level){ + const effectiveLevel=SPECIAL_CELL_DEBUG_ALL_LEVELS?Math.max(SPECIAL_CELL_MIN_LEVEL,level):level, + schedule=AppLogic.specialSchedule(effectiveLevel,sourcePuzzle.valid?.length||0,seed,data.specialMechanicsSeen||[],recentSpecialMechanicTypes()); + if(!schedule.types.length)return sourcePuzzle; + const puzzle=deepClone(sourcePuzzle),special=specialCellSet(puzzle),reserved=reservedSpecialKeys(puzzle); + special.crossings=[];special.warps=[];special.locks=[];invalidateSpecialCellCaches(puzzle);reserved.clear(); + const plan=[...schedule.types];for(let index=plan.length;indexschedule.maxCells){if(indextargetRange.max))return null; + const connectionRequirements=placementConnectionRequirements(x,y,shape),required=new Set((frontier.contacts||[]).map(contact=>contact.gateIndex).filter(Number.isInteger)); + if(frontier.gateDriven&&[...required].some(gateIndex=>!connectionRequirements.some(requirement=>requirement.metaId===source.id&&requirement.gateIndex===gateIndex)))return null; + if(!puzzleSupportsConnectionRequirements(puzzle,x,y,connectionRequirements))return null; + const id='B'+data.nextId++,meta={id,x,y,chunks:shape,level:prepared.level,targetLevel:prepared.targetLevel,seed:prepared.generationSeed,axis:puzzle.axis,entrySide:prepared.targetSide,sealedSides:[...prepared.sealedSides],puzzle,justRevealed:true,generatorVersion:GENERATOR_VERSION,rev:nextRevision()}; + data.metas[id]=meta; + const introduced=mechanicTypesForPuzzle(puzzle),seen=new Set(data.specialMechanicsSeen||[]);let encounterChanged=false; + for(const type of introduced)if(!seen.has(type)){seen.add(type);encounterChanged=true} + if(encounterChanged)data.specialMechanicsSeen=[...seen].sort(); + markMetaDirty(id);metaState(id);markStateDirty(id);addMetaToOccupancy(meta);return meta; +} +function nearbyShapeFamilyCounts(unitX,unitY,radius=7){ + const ids=new Set(),counts=new Map(); + for(let y=unitY-radius;y<=unitY+radius;y++)for(let x=unitX-radius;x<=unitX+radius;x++){const id=occupancy.get(key2(x,y));if(id)ids.add(id)} + for(const id of ids){const chunks=data.metas[id]?.chunks;if(!chunks?.length)continue;const family=generatedShapeFamilyKey(chunks);counts.set(family,(counts.get(family)||0)+1)} + return counts; +} +function shapeCandidatesForArea(candidates,seed,unitX,unitY){ + const balanced=balancedShapeCandidates(candidates,seed,{hash32,rngFrom,shuffle}),nearby=nearbyShapeFamilyCounts(unitX,unitY); + return balanced.map((shape,index)=>({shape,index,count:nearby.get(generatedShapeFamilyKey(shape))||0})).sort((left,right)=>left.count-right.count||left.index-right.index).map(entry=>entry.shape); +} +async function placeChildAtFrontierAttempt(source,frontier,attemptBase=0,{prepareOnly=false,futureBoardNumber=data.nextId}={}){ + const targetUnitX=frontier.unitX,targetUnitY=frontier.unitY; + const terminalFill=frontier.terminalFill===true&&isClosedVoidUnit(targetUnitX,targetUnitY); + if(occupancy.has(key2(targetUnitX,targetUnitY))||isClosedVoidUnit(targetUnitX,targetUnitY)&&!terminalFill)return null; + const targetSide=frontier.targetSide,preliminaryLevel=macroDifficulty(targetUnitX,targetUnitY), + candidateSeed=hash32(source.seed^Math.imul(targetUnitX+101,49999)^Math.imul(targetUnitY-211,31337)^attemptBase); + const tryShape=async(shape,seed,rng,{allowRegionalFallback=false}={})=>{ + const alignments=shuffle([...shape],rng); + for(const[dx,dy]of alignments){ + const x=targetUnitX-dx,y=targetUnitY-dy,placementIsTerminal=terminalFill&&shape.length===1&&x===targetUnitX&&y===targetUnitY; + if(!fitsMeta(x,y,shape)||!placementPreservesUnsolvedFrontiers(x,y,shape,source.id,{allowTerminal:placementIsTerminal}))continue; + const targetLevel=targetLevelForNewBoard(x,y,shape,futureBoardNumber),actualRange=sectionCountRange(targetLevel); + if(!placementIsTerminal&&(shape.lengthactualRange.max))continue; + const connectionRequirements=placementConnectionRequirements(x,y,shape),requiredFrontierGates=new Set((frontier.contacts||[]).map(contact=>contact.gateIndex).filter(Number.isInteger)); + if(frontier.gateDriven&&[...requiredFrontierGates].some(gateIndex=>!connectionRequirements.some(requirement=>requirement.metaId===source.id&&requirement.gateIndex===gateIndex)))continue; + const fixedPortProfiles=fixedPortProfilesForRequirements(connectionRequirements);if(fixedPortProfiles==null)continue; + let accepted=null,lastGenerationIssue=null; + const generationAttempts=shape.length===1?5:4; + for(let variant=0;variant=7?9000:4000,generationOptions)} + catch(error){lastGenerationIssue=error?.message||String(error);continue} + let puzzle=addObstaclePattern(generated,obstacleSeed);puzzle=addSpecialCellPattern(puzzle,specialSeed,targetLevel); + if(!puzzle){lastGenerationIssue='\u7279\u6b8a\u30bb\u30eb\u5272\u308a\u5f53\u3066\u4e0d\u6210\u7acb';continue} + let issue=generatedPuzzleIssue(puzzle); + if(!issue&&!puzzleSupportsConnectionRequirements(puzzle,x,y,connectionRequirements))issue='\u65e2\u5b58\u30b2\u30fc\u30c8\u3068\u4e0d\u4e00\u81f4'; + if(!issue&&!placementIsTerminal&&(shape.lengthactualRange.max))issue='\u30ec\u30d9\u30eb\u5225\u306e\u533a\u753b\u6570\u7bc4\u56f2\u5916'; + const intrinsicLevel=solverDifficulty(puzzle,targetLevel),intrinsicInBand=difficultyFitsRegion(intrinsicLevel,targetLevel); + const level=intrinsicLevel,inBand=intrinsicInBand; + puzzle.difficulty=level;puzzle.level=level;puzzle.regionalTarget=targetLevel; + if(!issue&&!inBand&&!allowRegionalFallback)issue='\u5730\u57df\u96e3\u6613\u5ea6\u7bc4\u56f2\u5916'; + const burden=AppLogic.interactionBurden(puzzle);puzzle.interactionBurden=burden; + if(!issue&&burden.score>=39)issue='\u64cd\u4f5c\u8ca0\u62c5\u304c\u4e0a\u9650\u3092\u8d85\u904e'; + if(!issue&&level>=UNIQUE_SOLUTION_MIN_LEVEL){ + let verification;try{verification=await verifyPuzzleUniquenessAsync(puzzle)}catch(error){lastGenerationIssue=error?.message||String(error);continue} + if(verification.status!=='unique')issue=`\u4e00\u610f\u89e3\u691c\u8a3c\uff1a${verification.status}`; + else{ + const boundedBurdenPenalty=Math.min(14,burden.score*.35),qualityScore=Math.max(0,(verification.quality?.score||0)-boundedBurdenPenalty); + puzzle.uniqueness={status:'unique',signature:verification.signature,ruleVersion:verification.ruleVersion,nodes:verification.nodes}; + puzzle.solutionQuality={...verification.quality,interactionBurdenPenalty:boundedBurdenPenalty,score:qualityScore,accepted:verification.quality?.accepted===true&&qualityScore>=10}; + if(!puzzle.solutionQuality.accepted)issue='\u89e3\u6cd5\u54c1\u8cea\u30b9\u30b3\u30a2\u4e0d\u8db3'; + } + } + if(issue){lastGenerationIssue=issue;continue} + accepted={puzzle,generationSeed,level,inBand,portSeed,obstacleSeed,specialSeed};break; + } + if(!accepted){if(globalThis.BEND_DEBUG_GENERATION&&lastGenerationIssue)console.debug('BEND FIELD: regenerated rejected board candidate',lastGenerationIssue);continue} + const{puzzle,generationSeed,level,inBand}=accepted; + if(!fitsMeta(x,y,shape)||!placementPreservesUnsolvedFrontiers(x,y,shape,source.id,{allowTerminal:placementIsTerminal})||occupancy.has(key2(targetUnitX,targetUnitY))||isClosedVoidUnit(targetUnitX,targetUnitY)&&!placementIsTerminal)continue; + if(!inBand)puzzle.regionalFallback=true; + const requiredOpenSides=new Set([...(frontier.gateDriven||!placementIsTerminal?[targetSide]:[]),...connectionRequirements.map(requirement=>requirement.targetSide)]); + const sealedSides=(placementIsTerminal?['N','S','W','E']:[]).filter(side=>!requiredOpenSides.has(side)), + prepared={sourceId:source.id,frontier:deepClone(frontier),futureBoardNumber,x,y,chunks:shape.map(cell=>[...cell]),level,targetLevel,generationSeed,targetSide,sealedSides,puzzle}; + return prepareOnly?prepared:installPreparedChild(prepared); + } + return null; + }; + const candidateLevels=new Set([preliminaryLevel,macroDifficulty(targetUnitX,targetUnitY)]); + const candidates=[],candidateKeys=new Set(); + for(const levelCandidate of candidateLevels)for(const shape of shapeCandidatesForLevel(hash32(candidateSeed^Math.imul(levelCandidate,0x45d9f3b)),levelCandidate,12)){const key=generatedShapeKey(shape);if(!candidateKeys.has(key)){candidateKeys.add(key);candidates.push(shape)}} + for(const shape of shuffle([...SHAPES],rngFrom(hash32(candidateSeed^0x3c6ef372)))){const key=generatedShapeKey(shape);if(!candidateKeys.has(key)){candidateKeys.add(key);candidates.push(shape)}} + const orderedCandidates=shapeCandidatesForArea(candidates,hash32(candidateSeed^0xbb67ae85),targetUnitX,targetUnitY); + for(let pass=0;pass<2;pass++)for(let index=0;indexrange.max))continue; + const requirements=placementConnectionRequirements(x,y,shape),required=new Set((frontier.contacts||[]).map(contact=>contact.gateIndex).filter(Number.isInteger)); + if(frontier.gateDriven&&[...required].some(gateIndex=>!requirements.some(requirement=>requirement.metaId===source.id&&requirement.gateIndex===gateIndex)))continue; + if(fixedPortProfilesForRequirements(requirements)!=null)return true; + } + } + return false; +} +async function placeChildAtFrontier(source,frontier,attemptBase=0,options=null){ + let cycle=0; + while(cycle0)return 0; + rebuildOccupancy(); + const metas=[];if(preferredMeta)metas.push(preferredMeta); + for(const meta of Object.values(data.metas))if(meta!==preferredMeta&&metaState(meta.id).solved)metas.push(meta); + metas.sort((a,b)=>(a===preferredMeta?-1:b===preferredMeta?1:0)||(b.rev||0)-(a.rev||0)); + for(const meta of metas){ + if(!meta.puzzle){try{await hydrateMeta(meta)}catch(_){continue}} + await hydrateAdjacentMetas(meta); + const gateCandidates=gateFrontierCandidates(meta),candidates=gateCandidates.length?gateCandidates:frontierCandidates(meta); + for(let offset=0;offset{for(const frontier of frontiers){if(await placeChildAtFrontier(meta,frontier,attemptOffset))made++;attemptOffset+=1000}}; + await placeTargets(unresolvedExpansionCandidates(meta)); + for(const retryBase of[500000,900000,1400000]){const remaining=unresolvedExpansionCandidates(meta);if(!remaining.length)break;attemptOffset=Math.max(attemptOffset,retryBase);await placeTargets(remaining)} + if(unsolvedBoardCount()===0)made+=await ensureUnsolvedFrontier(meta); + const missingConnections=missingGateConnections(meta); + st.expanded=missingConnections.length===0;st.expansionRetryRound=st.expanded?0:Math.min(1000000,retryRound+1);st.rev=nextRevision();markStateDirty(meta.id); + const saved=await refreshWorldView({rebuild:false,syncConnections:true,hide:true,persist:true,immediate:true,lockHeld:true});await nextPaint(); + if(!saved)showStatus('\u76e4\u9762\u306f\u751f\u6210\u6e08\u307f\u3067\u3059\u3002\u4fdd\u5b58\u3092\u518d\u8a66\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002',{retry:true,fresh:false}); + if(unsolvedBoardCount()===0)st.expanded=false; + return made; +} +function expandMeta(meta,prepared=null){ + const id=meta?.id; + return enqueueWorldMutation(async()=>{ + const current=data.metas[id];if(!current)return 0; + if(!current.puzzle)await hydrateMeta(current);return expandMetaNow(current,prepared); + }); +} +async function repairMetaFrontierNow(meta){ + const st=metaState(meta.id);if(!st.solved||st.expanded)return 0; + if(!meta.puzzle)await hydrateMeta(meta);await hydrateAdjacentMetas(meta);rebuildOccupancy();repairFacingGateConnections(meta); + const frontiers=unresolvedExpansionCandidates(meta),round=Math.max(0,Number.isInteger(st.expansionRetryRound)?st.expansionRetryRound:0); + let made=0; + if(frontiers.length){ + const frontier=frontiers[round%frontiers.length],attemptBase=310000003+Math.imul(round+1,1000003); + if(await placeChildAtFrontierAttempt(meta,frontier,attemptBase))made=1; + } + const missing=missingGateConnections(meta);st.expanded=missing.length===0; + st.expansionRetryRound=st.expanded?0:Math.min(1000000,round+1);st.rev=nextRevision();markStateDirty(meta.id);return made; +} +function repairMetaFrontier(meta){ + const id=meta?.id; + return enqueueWorldMutation(async()=>{const current=data.metas[id];return current?repairMetaFrontierNow(current):0}); +} +function sharedExpansionRepairDelay(state,now=trustedNow()){ + if(!cloudAvailable||!data.cloudProfile||!state?.solved)return 0;const owner=state.solvedById,playerId=currentPlayerId();if(!owner||owner===playerId)return 0; + const solvedAt=Number(state.solvedAt)||0;if(!solvedAt)return 0;const jitter=playerId?AppLogic.stableHash(['shared-expansion-takeover',playerId,owner,solvedAt])%SHARED_EXPANSION_JITTER_MS:0;return Math.max(0,solvedAt+SHARED_EXPANSION_GRACE_MS+jitter-now); +} +function nextSharedExpansionRepairDelay(){ + let wait=Infinity,found=false;for(const meta of Object.values(data.metas)){const st=metaState(meta.id);if(!st.solved||st.expanded)continue;found=true;wait=Math.min(wait,sharedExpansionRepairDelay(st))}return found?Math.max(0,wait):null; +} +function hasSharedExpansionRepairAuthority(){return Object.values(data.metas).some(meta=>{const st=metaState(meta.id);return st.solved&&!st.expanded&&sharedExpansionRepairDelay(st)<=0})} +async function repairExpansions(){ + if(typeof fieldArchiveBusy!=='undefined'&&fieldArchiveBusy){scheduleExpansionRepair(1000);return 0} + let made=0; + for(let pass=0;pass<3;pass++){ + let passMade=0;const active=data.metas[activeBoard],pending=Object.values(data.metas).filter(meta=>{const st=metaState(meta.id);return st.solved&&!st.expanded&&sharedExpansionRepairDelay(st)<=0}); + pending.sort((left,right)=>{ + const leftDistance=active?Math.hypot(left.x-active.x,left.y-active.y):0,rightDistance=active?Math.hypot(right.x-active.x,right.y-active.y):0; + return leftDistance-rightDistance||(metaState(left.id).expansionRetryRound||0)-(metaState(right.id).expansionRetryRound||0)||left.id.localeCompare(right.id,undefined,{numeric:true}); + }); + for(const meta of pending)passMade+=await repairMetaFrontier(meta)||0; + made+=passMade; + } + let closedOffset=0; + for(const{source,frontier}of closedVoidRepairCandidates().filter(({source})=>sharedExpansionRepairDelay(metaState(source.id))<=0).slice(0,2)){ + if(await placeChildAtFrontierAttempt(source,frontier,470000003+closedOffset*1000003))made++; + closedOffset++; + } + if(unsolvedBoardCount()===0&&hasSharedExpansionRepairAuthority())made+=await ensureUnsolvedFrontier(data.metas.B0||Object.values(data.metas)[0]); + await refreshWorldView({rebuild:true,syncConnections:true,hide:true,persist:true,immediate:true});if(pendingExpansionCount())scheduleExpansionRepair(700);return made; +} +let expansionRepairTimer=0; +function scheduleExpansionRepair(delay=700){if(expansionRepairTimer)return;const sharedWait=nextSharedExpansionRepairDelay();if(sharedWait==null&&!pendingExpansionCount())return;expansionRepairTimer=setTimeout(()=>{expansionRepairTimer=0;void repairExpansions().catch(handleExpansionError)},Math.max(150,delay,sharedWait||0))} +function reopenMissingGateExpansions(){ + rebuildOccupancy();let reopened=0; + for(const meta of Object.values(data.metas)){ + const st=metaState(meta.id);if(!st.solved||!meta.puzzle)continue; + repairFacingGateConnections(meta); + if(st.expanded&&missingGateConnections(meta).length){st.expanded=false;st.rev=nextRevision();markStateDirty(meta.id);reopened++} + } + return reopened; +} +function pendingExpansionCount(){ + let count=0; + for(const meta of Object.values(data.metas)){const st=metaState(meta.id);if(st.solved&&!st.expanded)count++} + return count+closedVoidRepairCandidates().length; +} +function cellSet(p){return p._validSet||(p._validSet=new Set(p.valid.map(c=>ckey(...c))))} +function warpMap(p){ + if(p._warpMap)return p._warpMap;const map=new Map(); + for(const pair of specialCellSet(p).warps){map.set(ckey(...pair.a),pair.b);map.set(ckey(...pair.b),pair.a)} + return p._warpMap=map; +} +function warpPairForCell(p,cell){return cell?warpMap(p).get(ckey(...cell))||null:null} +function isWarpTransition(p,a,b){const pair=warpPairForCell(p,a);return!!pair&&sameCell(pair,b)} +function pathCellsAdjacent(p,a,b){return manhattan(a,b)===1||isWarpTransition(p,a,b)} +function lockForDoor(p,cell){return specialCellSet(p).locks.find(lock=>sameCell(lock.door,cell))||null} +function pathHasLockKey(path,lock){return!!lock&&path.cells.some(cell=>sameCell(cell,lock.key))} +function crossingKeys(p){return specialCellSet(p).crossings.map(cell=>ckey(...cell))} +function crossingsSatisfied(st,p){return crossingKeys(p).every(key=>Boolean(crossingStateAtCell(st,p,key)))} +function pathIndexesAtCell(st,cellOrKey){ + const key=typeof cellOrKey==='string'?cellOrKey:ckey(...cellOrKey),indexes=[]; + st.paths.forEach((path,index)=>{if(path?.cells?.some(cell=>ckey(...cell)===key))indexes.push(index)});return indexes; +} +function crossingStateAtCell(st,p,cellOrKey){ + const key=typeof cellOrKey==='string'?cellOrKey:ckey(...cellOrKey);if(!crossingKeys(p).includes(key))return null; + const cell=key.split(',').map(Number),entries=[]; + st.paths.forEach((path,index)=>{if(!path?.cells?.some(candidate=>sameCell(candidate,cell)))return;const axis=pathAxisAtCell(path,p,cell);if(axis)entries.push({index,axis})}); + if(entries.length!==2||entries[0].axis===entries[1].axis)return null; + return{key,cell,horizontal:entries.find(entry=>entry.axis==='H')?.index,vertical:entries.find(entry=>entry.axis==='V')?.index}; +} +function crossingExitDirection(st,p,pathIndex){ + const path=st.paths[pathIndex];if(!path?.cells||path.cells.length<2)return null;const cell=path.cells[path.cells.length-1],key=ckey(...cell); + if(!crossingKeys(p).includes(key)||pathIndexesAtCell(st,key).length<2)return null; + const previous=path.cells[path.cells.length-2];return[cell[0]-previous[0],cell[1]-previous[1]]; +} +function gateObj(p,i){const g=p.g[i];return{i,cell:[g[0],g[1]],side:g[2]}} +function outsidePoint(g){const[dR,dC]=SIDE_D[g.side];return[g.cell[0]+dR,g.cell[1]+dC]} +function detachedTurnAnalysis(path,p){ + const cells=[],segments=[];let start=0; + for(let index=1;indexpath.cells.some(c=>c[0]===n[0]&&c[1]===n[1])).map(n=>({cell:[n[0],n[1]],value:n[2]}))} +function specialPathValid(path,p){ + for(const pair of specialCellSet(p).warps){ + const ai=path.cells.findIndex(cell=>sameCell(cell,pair.a)),bi=path.cells.findIndex(cell=>sameCell(cell,pair.b)); + if((ai>=0)!==(bi>=0)||ai>=0&&Math.abs(ai-bi)!==1)return false; + } + for(const lock of specialCellSet(p).locks){ + const keyIndex=path.cells.findIndex(cell=>sameCell(cell,lock.key)),doorIndex=path.cells.findIndex(cell=>sameCell(cell,lock.door)); + if(doorIndex>=0&&(keyIndex<0||keyIndex>doorIndex))return false; + } + return true; +} +function pathValid(path,p){const nums=numbersForPath(path,p),ta=turnAnalysis(path,p);return specialPathValid(path,p)&&nums.length===1&&nums[0].value===ta.count&&ta.cells.some(c=>sameCell(c,nums[0].cell))} +function usedGateSet(st,except=-1){const s=new Set();st.paths.forEach((p,i)=>{if(i===except||!p)return;if(!p.detachedStart)s.add(p.startGate);if(p.endGate!=null)s.add(p.endGate);if(p.openGate!=null)s.add(p.openGate)});return s} +function occupiedMap(st){const m=new Map();st.paths.forEach((p,pi)=>p.cells.forEach(c=>m.set(ckey(...c),pi)));return m} +function isSolved(st,p){if(!crossingsSatisfied(st,p)||st.paths.length!==p.n.length||st.paths.some(x=>x.endGate==null||x.detachedStart))return false;if(usedGateSet(st).size!==p.g.length)return false;if(occupiedMap(st).size!==p.valid.length)return false;return st.paths.every(x=>pathValid(x,p))} +function sanitizeStateForPuzzle(meta,{quiet=false}={}){ + const p=puzzleOf(meta),st=metaState(meta.id),validCells=cellSet(p),crossingSet=new Set(crossingKeys(p)),warpKeys=new Set([...warpMap(p).keys()]),usedCells=new Map(),usedGates=new Set(),kept=[]; + let removed=0; + for(const rawPath of st.paths){ + const path=normalizePath(rawPath); + let valid=!!path&&path.startGate>=0&&path.startGate=0&&path.endGate0&&!crossingSet.has(key))||owners>=2||(i>0&&!pathCellsAdjacent(p,path.cells[i-1],cell))){valid=false;break} + ownCells.add(key); + } + if(valid&&path.detachedStart&&path.endGate==null&&path.cells.length>1&&warpKeys.has(ckey(...path.cells[0]))&&warpKeys.has(ckey(...path.cells[path.cells.length-1])))valid=false; + if(valid&&(!path.detachedStart&&usedGates.has(path.startGate)||path.endGate!=null&&usedGates.has(path.endGate)||path.openGate!=null&&usedGates.has(path.openGate)))valid=false; + if(!valid){removed++;continue} + for(const key of ownCells)usedCells.set(key,(usedCells.get(key)||0)+1); + if(!path.detachedStart)usedGates.add(path.startGate);if(path.endGate!=null)usedGates.add(path.endGate);if(path.openGate!=null)usedGates.add(path.openGate); + Object.assign(rawPath,path);kept.push(rawPath); + } + if(removed){st.paths.splice(0,st.paths.length,...kept);st.rev=nextRevision();markStateDirty(meta.id);if(!quiet)loadNotices.push(`${meta.id}\u306e\u7834\u640d\u3057\u305f\u7dda\u3092${removed}\u672c\u524a\u9664\u3057\u307e\u3057\u305f\u3002`)} + if(st.specialProgress?.crossings?.length){st.specialProgress={crossings:[]};st.rev=nextRevision();markStateDirty(meta.id)} + const solved=isSolved(st,p); + if(st.solved!==solved){st.solved=solved;st.rev=nextRevision();markStateDirty(meta.id)} + if(solved&&!st.solvedBy){st.solvedBy=LEGACY_LOCAL_SOLVER;st.rev=nextRevision();markStateDirty(meta.id)} + if(!solved&&(st.solvedBy||st.scoreAwarded||st.store)){st.solvedBy=null;st.scoreAwarded=0;st.store=null;st.rev=nextRevision();markStateDirty(meta.id)} + return removed; +} +function chooseColor(meta,gi){const inherited=neighborColor(meta,gi);if(inherited!=null)return inherited;const used=new Set(metaState(meta.id).paths.map(p=>p.colorIndex).filter(v=>v!=null));for(let i=0;i>16)&255,(value>>8)&255,value&255]} +function mixHex(a,b,t){const aa=hexRgb(a),bb=hexRgb(b),q=Math.max(0,Math.min(1,t)),value=aa.map((v,i)=>Math.round(v+(bb[i]-v)*q));return`#${value.map(v=>v.toString(16).padStart(2,'0')).join('')}`} +function levelBackground(level){const t=(Math.max(1,Math.min(10,level||1))-1)/9;return[ mixHex(DIFF_BACKGROUND_EASY[0],DIFF_BACKGROUND_HARD[0],t),mixHex(DIFF_BACKGROUND_EASY[1],DIFF_BACKGROUND_HARD[1],t) ]} +function pathDistanceProfile(path,p){ + const samples=[];let total=0,previousPoint=path.detachedStart?boardCellCenter(path.cells[0]):gatePoint(gateObj(p,path.startGate)),previousCell=null; + for(const cell of path.cells||[]){ + const point=boardCellCenter(cell); + if(!previousCell||!isWarpTransition(p,previousCell,cell))total+=Math.hypot(point[0]-previousPoint[0],point[1]-previousPoint[1]); + samples.push({cell,distance:total});previousPoint=point;previousCell=cell; + } + if(path.endGate!=null){const end=gatePoint(gateObj(p,path.endGate));total+=Math.hypot(end[0]-previousPoint[0],end[1]-previousPoint[1])} + return{samples,total}; +} +function pathProgressAtCell(path,p,cell){ + const profile=pathDistanceProfile(path,p),sample=profile.samples.find(entry=>sameCell(entry.cell,cell)); + return sample&&profile.total>0?Math.max(0,Math.min(1,sample.distance/profile.total)):0; +} +function pathColorAtCell(path,p,cell){ + const startIndex=path.startColorIndex??path.colorIndex??0,endIndex=path.endColorIndex??startIndex; + const colorEndGate=path.endGate??path.openGate;if(uiSettings.lightweightRendering||colorEndGate==null||startIndex===endIndex)return LINE_COLORS[startIndex%LINE_COLORS.length]; + return mixHex(LINE_COLORS[startIndex%LINE_COLORS.length],LINE_COLORS[endIndex%LINE_COLORS.length],pathProgressAtCell(path,p,cell)); +} +function straightNumberWarningKeys(st,p){ + const numberKeys=new Set(p.n.map(number=>ckey(number[0],number[1]))),warnings=new Set(); + for(const path of st.paths){ + if(!path.cells.length)continue; + const points=[...(path.detachedStart?[]:[outsidePoint(gateObj(p,path.startGate))]),...path.cells],offset=path.detachedStart?0:1; + if(path.endGate!=null)points.push(outsidePoint(gateObj(p,path.endGate))); + path.cells.forEach((cell,index)=>{ + const key=ckey(...cell),pointIndex=index+offset; + if(!numberKeys.has(key)||!points[pointIndex-1]||!points[pointIndex+1])return; + const before=[points[pointIndex][0]-points[pointIndex-1][0],points[pointIndex][1]-points[pointIndex-1][1]], + after=[points[pointIndex+1][0]-points[pointIndex][0],points[pointIndex+1][1]-points[pointIndex][1]]; + if(before[0]===after[0]&&before[1]===after[1])warnings.add(key); + }); + } + return warnings; +} +function multipleNumberWarningKeys(st,p){ + const numberKeys=new Set(p.n.map(number=>ckey(number[0],number[1]))),warnings=new Set(); + for(const path of st.paths){ + const hits=path.cells.map(cell=>ckey(...cell)).filter(key=>numberKeys.has(key)); + if(hits.length>1)for(const key of hits)warnings.add(key); + } + return warnings; +} +function formatScore(value){return Math.max(0,Math.round(Number(value)||0)).toLocaleString('en-US')} +function roundToThreeSignificantDigits(value){const amount=Math.max(0,Number(value)||0);if(amount<1000)return Math.round(amount);const unit=10**Math.max(0,Math.floor(Math.log10(amount))-2);return Math.round(amount/unit)*unit} +function formatProjectedScore(value){return formatScore(roundToThreeSignificantDigits(value))} +function lineGraphEnvironment(){return{data,metaState,matchingNeighborGate}} +function collectConnectedLineComponent(meta,pathIndex,cache=null){return AppLogic.collectConnectedLineComponent(meta,pathIndex,cache||currentLineGraphCaches().components,lineGraphEnvironment())} +function connectedLineLength(meta,pathIndex,cache=null){return AppLogic.connectedLineLength(meta,pathIndex,cache||currentLineGraphCaches().components,lineGraphEnvironment())} +function renderedConnectedLineWidth(meta,pathIndex,cache=null,componentCache=null){const caches=currentLineGraphCaches();return AppLogic.renderedConnectedLineWidth(meta,pathIndex,cache||caches.widths,lineGraphEnvironment(),componentCache||caches.components)} +function minimapGeometryForComponent(component,cache=currentLineGraphCaches().geometries){ + if(cache.has(component))return cache.get(component); + const segments=[]; + for(const[member,memberIndex]of component.members||[]){ + if(!member?.puzzle)continue;const memberPath=metaState(member.id).paths[memberIndex];if(!memberPath?.cells?.length)continue;const memberPuzzle=puzzleOf(member), + gateMap=gateIndex=>{const g=gateObj(memberPuzzle,gateIndex),row=g.cell[0],col=g.cell[1];return[member.x+(col+(g.side==='E'?1:g.side==='W'?0:.5))/CHUNK,member.y+(row+(g.side==='S'?1:g.side==='N'?0:.5))/CHUNK]},turns=[]; + for(let pointIndex=0;pointIndex{ + if(!minimapFrame)return; + if(globalThis.document?.body?.classList?.contains?.('is-interacting')){minimapFrame=0;return} + if(timestamp-minimapLastDraw{if(!rects.length)return;context.beginPath();for(const[x,y,w,h]of rects)context.rect(x,y,w,h);context.fillStyle=color;context.fill()}; + fillRects(unsolvedRects,'#30373c');fillRects(solvedRects,'#477a5d'); + if(showLevels&&levelLabels.length){const fontSize=Math.max(8,Math.min(22,unitSize*.42));context.save();context.font=`900 ${fontSize}px DotGothic16Local,monospace`;context.textAlign='center';context.textBaseline='middle';context.lineJoin='round';context.lineWidth=Math.max(2,fontSize*.18);context.strokeStyle='rgba(7,10,12,.88)';context.fillStyle='rgba(245,249,252,.96)';for(const label of levelLabels){context.strokeText(label.text,label.x,label.y);context.fillText(label.text,label.x,label.y)}context.restore()} +} +function drawMapLongLines(context,metas,visibleIds,mapX,mapY,lineWidth=1){ + if(metas.length>96)return 0;let longSegments=0;const caches=currentLineGraphCaches(),seenComponents=new Set();context.lineCap='square';context.lineJoin='miter'; + for(const meta of metas){if(!meta.puzzle)continue;const st=metaState(meta.id);for(let index=0;index(i?context.lineTo(mapX(point[0]),mapY(point[1])):context.moveTo(mapX(point[0]),mapY(point[1]))));context.strokeStyle=segment.color;context.lineWidth=lineWidth;context.globalAlpha=.9;context.stroke();context.globalAlpha=1} + }}return longSegments +} +function rebuildMinimapWorld(width,height,dpr,centerX,centerY){ + const started=perfStart(),spanX=MINIMAP_VIEW_CHUNKS_X,spanY=Math.max(10,spanX*height/Math.max(1,width)),scale=Math.min(width/spanX,height/spanY),overscan=MINIMAP_CACHE_OVERSCAN_CHUNKS,overscanPixels=overscan*scale, + baseWidth=width+overscanPixels*2,baseHeight=height+overscanPixels*2,minChunkX=centerX-spanX/2-overscan,minChunkY=centerY-spanY/2-overscan,maxChunkX=centerX+spanX/2+overscan,maxChunkY=centerY+spanY/2+overscan, + mapX=value=>baseWidth/2+(value-centerX)*scale,mapY=value=>baseHeight/2+(value-centerY)*scale,pixelWidth=Math.round(baseWidth*dpr),pixelHeight=Math.round(baseHeight*dpr); + if(minimapBase.width!==pixelWidth||minimapBase.height!==pixelHeight){minimapBase.width=pixelWidth;minimapBase.height=pixelHeight} + const base=minimapBase.getContext('2d');base.setTransform(dpr,0,0,dpr,0,0);base.clearRect(0,0,baseWidth,baseHeight);base.fillStyle='#101315';base.fillRect(0,0,baseWidth,baseHeight); + const visibleIds=new Set();for(let y=Math.floor(minChunkY)-1;y<=Math.ceil(maxChunkY)+1;y++)for(let x=Math.floor(minChunkX)-1;x<=Math.ceil(maxChunkX)+1;x++){const id=occupancy.get(key2(x,y));if(id)visibleIds.add(id)} + const visibleMetas=[...visibleIds].map(id=>data.metas[id]).filter(Boolean);drawMapBoardCells(base,visibleMetas,mapX,mapY,scale);const longSegments=drawMapLongLines(base,visibleMetas,visibleIds,mapX,mapY,1); + minimapCache={revision:minimapWorldRevision,width,height,dpr,anchorX:centerX,anchorY:centerY,scale,overscanPixels,baseWidth,baseHeight,longSegments};perfCount('minimapWorldBuilds');perfEnd('rebuildMinimapWorld',started); +} +function drawMinimap(){ + if(globalThis.document?.body?.classList?.contains?.('is-interacting')){minimapDirty=true;perfCount('minimapDrawsDeferredDuringInteraction');return false} + const started=perfStart();perfCount('minimapDraws');const rect=getMinimapRect(),width=Math.max(1,Math.round(rect.width)),height=Math.max(1,Math.round(rect.height)),dpr=Math.min(2,Math.max(1,window.devicePixelRatio||1)),pixelWidth=Math.round(width*dpr),pixelHeight=Math.round(height*dpr),[centerX,centerY]=cameraCenterInChunks(); + if(minimapCanvas.width!==pixelWidth||minimapCanvas.height!==pixelHeight){minimapCanvas.width=pixelWidth;minimapCanvas.height=pixelHeight} + const stale=!minimapCache||minimapCache.revision!==minimapWorldRevision||minimapCache.width!==width||minimapCache.height!==height||minimapCache.dpr!==dpr|| + Math.abs(centerX-minimapCache.anchorX)*minimapCache.scale>minimapCache.overscanPixels*.75||Math.abs(centerY-minimapCache.anchorY)*minimapCache.scale>minimapCache.overscanPixels*.75; + if(stale)rebuildMinimapWorld(width,height,dpr,centerX,centerY); + minimapDirty=false;minimapLongSegments=minimapCache.longSegments; + const sourceX=(minimapCache.overscanPixels+(centerX-minimapCache.anchorX)*minimapCache.scale)*dpr,sourceY=(minimapCache.overscanPixels+(centerY-minimapCache.anchorY)*minimapCache.scale)*dpr, + context=minimapCanvas.getContext('2d');context.setTransform(1,0,0,1,0,0);context.clearRect(0,0,pixelWidth,pixelHeight);context.drawImage(minimapBase,sourceX,sourceY,pixelWidth,pixelHeight,0,0,pixelWidth,pixelHeight); + context.setTransform(dpr,0,0,dpr,0,0);context.strokeStyle='rgba(239,248,255,.82)';context.lineWidth=1;context.beginPath();context.moveTo(width/2-5,height/2);context.lineTo(width/2+5,height/2);context.moveTo(width/2,height/2-5);context.lineTo(width/2,height/2+5);context.stroke(); + let nearbyPlayers=0;for(const player of(typeof remotePlayers==='undefined'?[]:remotePlayers.values())){const worldX=Number.isFinite(player.currentX)?player.currentX:player.targetX,worldY=Number.isFinite(player.currentY)?player.currentY:player.targetY;if(!Number.isFinite(worldX)||!Number.isFinite(worldY))continue;const x=width/2+(worldX-centerX)*minimapCache.scale,y=height/2+(worldY-centerY)*minimapCache.scale;if(x<-4||y<-4||x>width+4||y>height+4)continue;nearbyPlayers++;context.beginPath();context.arc(x,y,2.6,0,Math.PI*2);context.fillStyle='#d9f06f';context.fill();context.strokeStyle='rgba(8,12,15,.9)';context.lineWidth=1;context.stroke()} + const spanX=MINIMAP_VIEW_CHUNKS_X,spanY=Math.max(10,spanX*height/Math.max(1,width)),bounds={minX:Math.floor(centerX-spanX/2)-1,maxX:Math.ceil(centerX+spanX/2)+1,minY:Math.floor(centerY-spanY/2)-1,maxY:Math.ceil(centerY+spanY/2)+1},visibleIds=visibleMetaIdsForBounds(bounds),visibleMetas=[...visibleIds].map(id=>data.metas[id]).filter(Boolean), + solved=visibleMetas.reduce((count,meta)=>count+(metaState(meta.id).solved?1:0),0),status=`${solved} / ${visibleMetas.length} · ${nearbyPlayers+1}人`,label=`\u8996\u70b9\u5468\u8fba\u306e\u76e4\u9762\u3002\u30af\u30ea\u30a2\u6e08\u307f\uff1a${solved}\u3001\u672a\u30af\u30ea\u30a2\uff1a${visibleMetas.length-solved}\u3001\u8fd1\u304f\u306e\u30d7\u30ec\u30a4\u30e4\u30fc\uff1a${nearbyPlayers+1}\u4eba\u3002`; + if(minimapStatus.textContent!==status)minimapStatus.textContent=status;if(minimapCanvas.getAttribute('aria-label')!==label)minimapCanvas.setAttribute('aria-label',label); + perfCount(stale?'minimapCacheMisses':'minimapCacheHits');perfEnd('drawMinimap',started); +} +function worldNavigationBounds(){ + const bounds=fieldBoundsFromMetas(data.metas),margin=3;return{minX:bounds.minX-margin,maxX:bounds.maxX+margin,minY:bounds.minY-margin,maxY:bounds.maxY+margin}; +} +function centerWorldUnit(worldX,worldY,{persist=true}={}){ + if(!Number.isFinite(worldX)||!Number.isFinite(worldY))return false;const bounds=worldNavigationBounds(),rect=getViewportRect(); + worldX=Math.max(bounds.minX,Math.min(bounds.maxX,worldX));worldY=Math.max(bounds.minY,Math.min(bounds.maxY,worldY)); + worldOverviewActive=false;cam.x=rect.width/2-(worldX-renderOriginX)*UNIT*cam.scale;cam.y=rect.height/2-(worldY-renderOriginY)*UNIT*cam.scale;applyCamera(true);ensureBoards();if(persist)recordCameraAnchor();return true; +} +function minimapPoint(event){ + const rect=getMinimapRect();return{x:event.clientX-rect.left,y:event.clientY-rect.top,width:rect.width,height:rect.height}; +} +function minimapWorldPoint(event){ + const point=minimapPoint(event),center=cameraCenterInChunks(),scale=minimapCache?.scale||Math.max(1,point.width/MINIMAP_VIEW_CHUNKS_X); + return{x:center[0]+(point.x-point.width/2)/scale,y:center[1]+(point.y-point.height/2)/scale,scale}; +} +function beginMinimapPointer(event){ + if(event.button!==0)return;event.preventDefault();event.stopPropagation();const target=minimapWorldPoint(event); + minimapPointerState={id:event.pointerId,startClientX:event.clientX,startClientY:event.clientY,targetX:target.x,targetY:target.y,scale:target.scale}; + try{minimapCanvas.setPointerCapture(event.pointerId)}catch(_){} + centerWorldUnit(target.x,target.y,{persist:false});minimapCanvas.focus({preventScroll:true}); +} +function moveMinimapPointer(event){ + const state=minimapPointerState;if(!state||state.id!==event.pointerId)return;event.preventDefault(); + centerWorldUnit(state.targetX+(event.clientX-state.startClientX)/state.scale,state.targetY+(event.clientY-state.startClientY)/state.scale,{persist:false}); +} +function endMinimapPointer(event){ + if(!minimapPointerState||minimapPointerState.id!==event.pointerId)return;minimapPointerState=null; + try{if(minimapCanvas.hasPointerCapture?.(event.pointerId))minimapCanvas.releasePointerCapture(event.pointerId)}catch(_){} + recordCameraAnchor(); +} +function keyMinimap(event){ + const directions={ArrowUp:[0,-1],ArrowDown:[0,1],ArrowLeft:[-1,0],ArrowRight:[1,0]},direction=directions[event.key]; + if(!direction)return;event.preventDefault();const center=cameraCenterInChunks(),step=event.shiftKey?5:1;centerWorldUnit(center[0]+direction[0]*step,center[1]+direction[1]*step); +} +function scoreFromThickness(meta,totalThickness){ + const level=Math.max(1,Math.min(10,meta.level||1)), + sections=Math.max(1,meta.chunks?.length||Math.ceil((((meta.puzzle?.valid?.length||0)+(meta.puzzle?.obstacles?.length||0))||25)/25)), + hardMultiplier=1+Math.max(0,level-4)*.18, + largeMultiplier=1+Math.pow(Math.max(0,sections-1),.82)*(.18+level*.018), + effortMultiplier=hardMultiplier*largeMultiplier, + raw=level*level*100+totalThickness*level*20, + originalAward=Math.max(100,Math.round(raw/10)*10), + quarter=Math.max(25,Math.round(originalAward/4)), + halved=Math.max(13,Math.round(quarter/2)); + return Math.min(MAX_SCORE,Math.max(13,Math.round(halved*effortMultiplier))); +} +function scoreForBoard(meta){ + const st=metaState(meta.id); + if(!st.solved||!st.paths.length)return 0; + const cache=new Map(),totalThickness=st.paths.reduce((sum,_,index)=>sum+lineStrokeWidth(connectedLineLength(meta,index,cache)),0); + return scoreFromThickness(meta,totalThickness); +} +function scoreLensCountForMeta(){return activeScoreLensCount()} +function timeAttackRewardModifier(){ + const run=data.timeAttack;if(!run||trustedNow()>=run.endsAt)return 1; + return timeAttackMultiplier(run.baseCollected||0); +} +function rewardDetailsForBoard(meta,state=metaState(meta.id)){ + const baseScore=scoreForBoard(meta),options={worldSeed:worldSeedValue(),meta,state,scoreLensCount:scoreLensCountForMeta(meta)}, + beforeTime=AppLogic.deterministicBoardReward(baseScore,{...options,timeAttackModifier:1}), + reward=AppLogic.deterministicBoardReward(baseScore,{...options,timeAttackModifier:timeAttackRewardModifier()}); + return{...reward,preTimeAward:beforeTime.award}; +} +function projectedScoreForBoard(meta){ + const solution=puzzleOf(meta).solution||[],baseScore=scoreFromThickness(meta,solution.reduce((sum,path)=>sum+lineStrokeWidth(path.cells.length),0)), + projectedState={paths:solution,solved:true}; + return AppLogic.deterministicBoardReward(baseScore,{worldSeed:worldSeedValue(),meta,state:projectedState,timeAttackModifier:timeAttackRewardModifier(),scoreLensCount:scoreLensCountForMeta(meta)}).award; +} +function maybeOpenStore(meta,st,solveAward,roll=null){ + const deterministicRoll=roll==null?(hash32(meta.seed^0x7f4a7c15)>>>0)/4294967296:roll; + if(!st.solved||st.store||!st.paths.length||deterministicRoll>=STORE_CHANCE)return 0; + const pathIndex=st.paths.reduce((best,path,index)=>path.cells.length>st.paths[best].cells.length?index:best,0), + path=st.paths[pathIndex], + cellIndex=Math.max(0,Math.min(path.cells.length-1,Math.floor(path.cells.length*.55))), + bonus=0; + st.store={owner:currentPlayerName(),pathIndex,cellIndex,openedAt:trustedNow(),priceVersion:STORE_PRICE_VERSION,priceCoefficient:null,bonus,bonusVersion:SCORE_VERSION,itemIds:seededStoreItemIds(meta.seed),purchases:[]}; + st.store.priceCoefficient=storePriceDetails(meta,st.store).coefficient; + + return bonus; +} +function gatePoint(g){const r=g.cell[0],c=g.cell[1],x=PAD+(c+.5)*CELL,y=PAD+(r+.5)*CELL;return g.side==='N'?[x,PAD+r*CELL]:g.side==='S'?[x,PAD+(r+1)*CELL]:g.side==='W'?[PAD+c*CELL,y]:[PAD+(c+1)*CELL,y]} +function gateMarkerPathAt(point,side,sealed=false,scale=1){ + const[dr,dc]=SIDE_D[side],ox=dc,oy=dr,tx=-dr,ty=dc,stem=11*scale,back=4.5*scale,wing=4.5*scale, + tip=[point[0]+ox*stem,point[1]+oy*stem]; + if(sealed){ + const center=[point[0]+ox*7*scale,point[1]+oy*7*scale], + left=[center[0]+tx*wing,center[1]+ty*wing],right=[center[0]-tx*wing,center[1]-ty*wing]; + return`M ${point[0]} ${point[1]} L ${center[0]} ${center[1]} M ${left[0]} ${left[1]} L ${right[0]} ${right[1]}`; + } + const base=[tip[0]-ox*back,tip[1]-oy*back], + left=[base[0]+tx*wing,base[1]+ty*wing],right=[base[0]-tx*wing,base[1]-ty*wing]; + return`M ${point[0]} ${point[1]} L ${tip[0]} ${tip[1]} M ${left[0]} ${left[1]} L ${tip[0]} ${tip[1]} L ${right[0]} ${right[1]}`; +} +function gateHitBox(point,side){ + // Keep hit regions inside the owning board. Outward hit boxes from adjacent + // boards overlapped and could steal a pointer from the opposite gate. + const inward=Math.round(CELL*.42),outward=0,half=Math.round(CELL*.30),depth=inward+outward; + if(side==='N')return{x:point[0]-half,y:point[1],width:half*2,height:depth}; + if(side==='S')return{x:point[0]-half,y:point[1]-inward,width:half*2,height:depth}; + if(side==='W')return{x:point[0],y:point[1]-half,width:depth,height:half*2}; + return{x:point[0]-inward,y:point[1]-half,width:depth,height:half*2}; +} +function gateAtCell(p,cell,exclude=null,side=null){if(!p._gateCells){p._gateCells=new Map();p.g.forEach((g,i)=>{const key=ckey(g[0],g[1]);if(!p._gateCells.has(key))p._gateCells.set(key,[]);p._gateCells.get(key).push(i)})}for(const i of p._gateCells.get(ckey(...cell))||[])if(i!==exclude&&(!side||p.g[i][2]===side))return i;return null} +function regionFor(level){return REGIONS[Math.min(REGIONS.length-1,Math.floor((Math.max(1,level)-1)/2))]} +function hudPlacementCandidates(meta){ + const own=new Set(meta.chunks.map(([dx,dy])=>key2(meta.x+dx,meta.y+dy))),priority={N:0,S:1,W:2,E:3},candidates=[]; + for(const[dx,dy]of meta.chunks)for(const side of['N','S','W','E']){ + const[dr,dc]=SIDE_D[side],localKey=key2(dx+dc,dy+dr);if(own.has(key2(meta.x+dx+dc,meta.y+dy+dr)))continue; + const neighbor=occupancy.get(key2(meta.x+dx+dc,meta.y+dy+dr));if(neighbor&&neighbor!==meta.id)continue; + candidates.push({side,dx,dy,priority:priority[side],distance:Math.abs(dx+.5-(puzzleOf(meta).bounds.w/CHUNK)/2)+Math.abs(dy+.5-(puzzleOf(meta).bounds.h/CHUNK)/2),localKey}); + } + return candidates.sort((a,b)=>a.priority-b.priority||a.distance-b.distance||a.dy-b.dy||a.dx-b.dx); +} +function positionBoardLabel(b){ + const candidates=hudPlacementCandidates(b.meta),fallback={side:'N',dx:0,dy:0},viewportRect=viewport.getBoundingClientRect(),boardRect=boardScreenRect(b),scale=Math.max(MIN_CAMERA_SCALE,cam.scale),margin=8, + innerWidth=b.p.bounds.w*CELL,estimatedHeight=Math.max(28,b.label.offsetHeight||32)*scale; + const boundsFor=placement=>{ + const horizontal=placement.side==='N'||placement.side==='S',labelWidth=(horizontal?innerWidth:Math.max(150,b.label.offsetWidth||190))*scale, + edgeX=horizontal?PAD+innerWidth/2:PAD+(placement.dx+(placement.side==='E'?1:0))*UNIT, + edgeY=horizontal?PAD+(placement.side==='S'?b.p.bounds.h*CELL:0):PAD+(placement.dy+.5)*UNIT, + x=boardRect.left+edgeX/b.w*boardRect.width,y=boardRect.top+edgeY/b.h*boardRect.height,gap=(placement.side==='N'?22:8)*scale; + if(placement.side==='N')return{left:x-labelWidth/2,right:x+labelWidth/2,top:y-gap-estimatedHeight,bottom:y-gap}; + if(placement.side==='S')return{left:x-labelWidth/2,right:x+labelWidth/2,top:y+gap,bottom:y+gap+estimatedHeight}; + if(placement.side==='W')return{left:x-gap-labelWidth,right:x-gap,top:y-estimatedHeight/2,bottom:y+estimatedHeight/2}; + return{left:x+gap,right:x+gap+labelWidth,top:y-estimatedHeight/2,bottom:y+estimatedHeight/2}; + }; + const overflow=rect=>Math.max(0,viewportRect.left+margin-rect.left)+Math.max(0,rect.right-(viewportRect.right-margin))+Math.max(0,viewportRect.top+margin-rect.top)+Math.max(0,rect.bottom-(viewportRect.bottom-margin)); + const placement=(candidates.length?candidates:[fallback]).map(candidate=>({candidate,overflow:overflow(boundsFor(candidate))})).sort((a,c)=>a.overflow-c.overflow||a.candidate.priority-c.candidate.priority)[0].candidate; + b.label.dataset.side=placement.side;b.label.style.removeProperty('right');b.label.style.removeProperty('bottom');b.label.style.removeProperty('width'); + if(placement.side==='N'){b.label.style.left=PAD+'px';b.label.style.width=innerWidth+'px';b.label.style.top=(PAD-22)+'px';b.label.style.transform='translate(0,-100%)'} + else if(placement.side==='S'){b.label.style.left=PAD+'px';b.label.style.width=innerWidth+'px';b.label.style.top=(PAD+b.p.bounds.h*CELL+8)+'px';b.label.style.transform='translate(0,0)'} + else if(placement.side==='W'){const edgeX=PAD+placement.dx*UNIT,edgeY=PAD+(placement.dy+.5)*UNIT;b.label.style.left=(edgeX-8)+'px';b.label.style.top=edgeY+'px';b.label.style.transform='translate(-100%,-50%)'} + else{const edgeX=PAD+(placement.dx+1)*UNIT,edgeY=PAD+(placement.dy+.5)*UNIT;b.label.style.left=(edgeX+8)+'px';b.label.style.top=edgeY+'px';b.label.style.transform='translate(0,-50%)'} +} +const dirtyBoards=new Set(),lineWidthDirtyPathKeys=new Set();let renderFrame=0,lineWidthFrame=0; +function applyLineWidth(node,width){const value=width.toFixed(2);if(node.style.getPropertyValue('--line-width')!==value)node.style.setProperty('--line-width',value)} +function linePathKey(id,index){return`${id}:${index}`} +function splitLinePathKey(key){const split=key.lastIndexOf(':');return split<0?[key,NaN]:[key.slice(0,split),Number(key.slice(split+1))]} +function refreshRenderedLineWidths(pathKeys=null){ + const started=perfStart(),caches=currentLineGraphCaches(),refreshAll=pathKeys==null,targetKeys=pathKeys?new Set(pathKeys):new Set(); + if(refreshAll)for(const[id,board]of rendered)for(let index=0;indexsplitLinePathKey(key)[0])); + for(const id of targetIds){ + const board=rendered.get(id);if(!board)continue; + const pathNodes=board.pathStrokeNodes||[]; + for(const node of pathNodes){ + const index=Number(node.dataset.pathIndex);if(!targetKeys.has(linePathKey(id,index)))continue; + const width=renderedConnectedLineWidth(board.meta,index,caches.widths,caches.components);applyLineWidth(node,width); + } + const connectorNodes=board.connectorNodes||[]; + for(const node of connectorNodes){ + const ownerId=node.dataset.ownerBoard||board.id,index=Number(node.dataset.pathIndex),owner=data.metas[ownerId]; + if(!owner||!Number.isInteger(index)||!targetKeys.has(linePathKey(ownerId,index)))continue;applyLineWidth(node,renderedConnectedLineWidth(owner,index,caches.widths,caches.components)); + } + } + perfGauge('lineWidthBoards',targetIds.size);perfGauge('lineWidthPaths',targetKeys.size);perfEnd('refreshRenderedLineWidths',started); +} +function queueLineWidthRefresh(boardId=null,pathIndex=null){ + if(boardId){if(Number.isInteger(pathIndex))lineWidthDirtyPathKeys.add(linePathKey(boardId,pathIndex));else for(let index=0;index{lineWidthFrame=0;const keys=new Set(lineWidthDirtyPathKeys);lineWidthDirtyPathKeys.clear();refreshRenderedLineWidths(keys)}); +} +function scoreLensVisibleForMeta(meta){return cam.scale<=SCORE_LENS_ZOOM_THRESHOLD&&!metaState(meta.id).solved&&activeScoreLensCount()>0} +function updateScoreLensBadge(b){ + if(!b?.scoreLensBadge)return;const visible=scoreLensVisibleForMeta(b.meta);b.scoreLensBadge.hidden=!visible; + if(visible){const score=formatProjectedScore(projectedScoreForBoard(b.meta));b.scoreLensBadge.textContent=`◆ ${score}`;b.scoreLensBadge.setAttribute('aria-label',`予想報酬 ${score}`);b.scoreLensBadge.setAttribute('aria-hidden','false')}else{b.scoreLensBadge.removeAttribute('aria-label');b.scoreLensBadge.setAttribute('aria-hidden','true')} +} +let lastPresentedScale=NaN,worldOverviewActive=false,zoomDetailsDirty=false; +function inWorldOverview(){ + const drawingActive=document.body.classList.contains('is-drawing')||Boolean(rendered.get(activeBoard)?.drawing?.keyboardActive); + if(drawingActive){worldOverviewActive=false;return false} + worldOverviewActive=cam.scale<=OVERVIEW_ZOOM_THRESHOLD; + return worldOverviewActive; +} +function updateZoomPresentation(force=false){ + if(!force&&Math.abs(cam.scale-lastPresentedScale)<1e-6)return inWorldOverview();lastPresentedScale=cam.scale; + const overview=inWorldOverview();world.classList.toggle('world-overview',overview);world.classList.toggle('score-lens-zoom',cam.scale<=SCORE_LENS_ZOOM_THRESHOLD);viewport.classList.toggle('canvas-overview',overview); + world.style.setProperty('--inverse-camera-scale',String(1/Math.max(MIN_CAMERA_SCALE,cam.scale)));world.style.setProperty('--camera-scale',String(cam.scale)); + if(document.body.classList.contains('is-interacting'))zoomDetailsDirty=true;else{for(const board of rendered.values())updateScoreLensBadge(board);zoomDetailsDirty=false} + if(overview){overviewDirty=true;scheduleWorldOverview(true)}else if(overviewCanvas){overviewCanvas.hidden=true} + return overview; +} +function flushBoardRenderQueue(timestamp){ + renderFrame=0;if(!dirtyBoards.size)return;markVisualFrame(timestamp);let count=0; + for(const board of [...dirtyBoards]){dirtyBoards.delete(board);if(board.card.isConnected)renderBoardNow(board);if(++count>=BOARD_RENDERS_PER_FRAME)break} + if(dirtyBoards.size)renderFrame=requestAnimationFrame(flushBoardRenderQueue); +} +function renderBoard(b){ + if(!b||!b.card.isConnected)return;dirtyBoards.add(b);perfCount('boardRenderRequests');if(!renderFrame)renderFrame=requestAnimationFrame(flushBoardRenderQueue); +} +function boardCellCenter(cell){return[PAD+(cell[1]+.5)*CELL,PAD+(cell[0]+.5)*CELL]} +function pathRenderSegments(path,p){ + if(!path?.cells?.length)return[];const segments=[],first=boardCellCenter(path.cells[0]);let current=path.detachedStart?[first]:[gatePoint(gateObj(p,path.startGate)),first]; + for(let index=1;indexsegment.length>0); +} +function pathStrokePieces(segments,startColor,endColor){ + const pieces=[];let total=0; + for(const points of segments)for(let i=1;i[x+(col+.5)*tutorialCell,y+(row+.5)*tutorialCell], + gate:(side,row)=>side==='W'?[x,y+(row+.5)*tutorialCell]:[x+cols*tutorialCell,y+(row+.5)*tutorialCell] + }; + } + function addGate(svg,point,side){ + svg.append( + svgEl('circle',{cx:point[0],cy:point[1],r:4.5,class:'gate-dot',fill:'#72e38f'}), + svgEl('path',{d:gateMarkerPathAt(point,side,false,.82),class:'gate-marker tutorial-gate-marker'}) + ); + } + function addPath(svg,points,color,className){ + const line=svgEl('polyline',{points:points.map(point=>point.join(',')).join(' '),class:`path tutorial-path ${className}`,stroke:color,pathLength:1}); + svg.append(line);return line; + } + for(const host of document.querySelectorAll('[data-tutorial]')){ + const type=host.dataset.tutorial,fill=type==='fill', + width=tutorialPad*2+tutorialCell*4, + height=tutorialPad*2+tutorialCell*3, + svg=svgEl('svg',{viewBox:`0 0 ${width} ${height}`,class:'tutorial-board','aria-hidden':'true'}); + svg.style.setProperty('--line-width','8px'); + if(fill){ + const grid=addGrid(svg,{bg:levelBackground(4)[0]}); + for(let row=0;row<3;row++){ + const fromLeft=row!==1,start=grid.gate(fromLeft?'W':'E',row),end=grid.gate(fromLeft?'E':'W',row), + cells=fromLeft?[0,1,2,3]:[3,2,1,0],points=[start,...cells.map(col=>grid.center(row,col)),end]; + addGate(svg,start,fromLeft?'W':'E');addGate(svg,end,fromLeft?'E':'W'); + addPath(svg,points,LINE_COLORS[[2,0,3][row]],`tutorial-fill-path tutorial-fill-${row+1}`); + } + }else{ + const grid=addGrid(svg),startRow=type==='draw'?0:1,endRow=type==='draw'?2:type==='bend'?0:1, + start=grid.gate('W',startRow),end=grid.gate('E',endRow); + addGate(svg,start,'W');addGate(svg,end,'E'); + if(type==='draw'){ + addPath(svg,[start,grid.center(0,0),grid.center(0,1),grid.center(1,1),grid.center(1,2),grid.center(2,2),grid.center(2,3),end],LINE_COLORS[2],'tutorial-draw-path'); + }else if(type==='bend'){ + const numberCell=grid.center(1,1),number=svgEl('text',{x:numberCell[0],y:numberCell[1],class:'num tutorial-number-pulse'}); + addPath(svg,[start,grid.center(1,0),numberCell,grid.center(0,1),grid.center(0,2),grid.center(0,3),end],LINE_COLORS[3],'tutorial-bend-path'); + number.textContent='2';svg.append(number); + }else{ + const points=[start,grid.center(1,0),grid.center(1,1),grid.center(1,2),grid.center(1,3),end]; + addPath(svg,points,LINE_COLORS[2],'tutorial-shop-path'); + const markerPoint=grid.center(1,2),marker=svgEl('g',{class:'tutorial-shop-marker'}); + marker.append(svgEl('rect',{x:markerPoint[0]-19,y:markerPoint[1]-17,width:38,height:34,class:'tutorial-shop-building'}),svgEl('text',{x:markerPoint[0],y:markerPoint[1]-1,class:'tutorial-shop-gem'}),svgEl('text',{x:markerPoint[0],y:markerPoint[1]+12,class:'tutorial-shop-label'})); + marker.children[1].textContent='\u25a6';marker.children[2].textContent='\u5e97';svg.append(marker); + const score=svgEl('text',{x:markerPoint[0]+31,y:markerPoint[1]-19,class:'tutorial-shop-score'});score.textContent='+ ◆';svg.append(score); + } + } + host.replaceChildren(svg); + } +} +buildTutorialBoards(); +function boardCellsPath(cells){return(cells||[]).map(([r,c])=>{const x=PAD+c*CELL,y=PAD+r*CELL;return`M${x} ${y}h${CELL}v${CELL}h-${CELL}Z`}).join('')} +function outerEdgesPath(p){const set=cellSet(p),parts=[];for(const[r,c]of p.valid){const x=PAD+c*CELL,y=PAD+r*CELL;if(!set.has(ckey(r-1,c)))parts.push(`M${x} ${y}h${CELL}`);if(!set.has(ckey(r+1,c)))parts.push(`M${x} ${y+CELL}h${CELL}`);if(!set.has(ckey(r,c-1)))parts.push(`M${x} ${y}v${CELL}`);if(!set.has(ckey(r,c+1)))parts.push(`M${x+CELL} ${y}v${CELL}`)}return parts.join('')} +function drawOuterEdges(layer,p){layer.append(svgEl('path',{d:outerEdgesPath(p),class:'outer-edge'}))} +function overviewChunkPath(meta){return meta.chunks.map(([dx,dy])=>{const x=PAD+dx*UNIT,y=PAD+dy*UNIT;return`M${x} ${y}h${UNIT}v${UNIT}h-${UNIT}Z`}).join('')} +function staticBoardSignature(meta){const state=metaState(meta.id);return`${meta.rev||0}:${state.rev||0}:${state.solved?1:0}:${state.paths.length}`} +function destroyStaticBoard(board){ + if(!board)return;board.card.remove();staticRendered.delete(board.id);perfCount('staticBoardsDestroyed'); +} +function boardTimeout(board,callback,delay){ + const timer=setTimeout(()=>{board?.timers?.delete(timer);if(board?.card?.isConnected)callback()},delay);board?.timers?.add(timer);return timer; +} + +function makeStaticBoard(meta){ + const p=puzzleOf(meta),w=p.bounds.w*CELL+PAD*2,h=p.bounds.h*CELL+PAD*2,card=document.createElement('div'); + card.className=`board-card board-static${metaState(meta.id).solved?' solved':''}`;card.style.left=((meta.x-renderOriginX)*UNIT-PAD)+'px';card.style.top=((meta.y-renderOriginY)*UNIT-PAD)+'px';card.style.width=w+'px';card.style.height=h+'px';card.dataset.id=meta.id;card.tabIndex=-1; + const svg=svgEl('svg',{viewBox:`0 0 ${w} ${h}`,width:w,height:h,class:'board-static-svg','aria-hidden':'true'}),[background]=levelBackground(meta.level), + shape=svgEl('path',{d:overviewChunkPath(meta),class:'static-summary-fill',fill:background}); + svg.append(shape);const state=metaState(meta.id); + state.paths.forEach((path,index)=>{if(!path?.cells?.length)return;const color=LINE_COLORS[(path.startColorIndex??path.colorIndex??index)%LINE_COLORS.length],width=Math.min(15,Math.max(3,renderedConnectedLineWidth(meta,index)));for(const points of pathRenderSegments(path,p)){const node=svgEl('polyline',{points:points.map(point=>point.join(',')).join(' '),class:'static-summary-path',stroke:color});node.style.setProperty('--static-line-width',width.toFixed(2));svg.append(node)}}); + card.append(svg); + const storeCell=storeCellForMeta(meta);if(storeCell){const shop=document.createElement('button'),[x,y]=boardCellCenter(storeCell);shop.type='button';shop.className='static-shop-icon';shop.innerHTML='';shop.style.left=x+'px';shop.style.top=y+'px';shop.setAttribute('aria-label','\u30b7\u30e7\u30c3\u30d7\u3092\u958b\u304f');shop.addEventListener('pointerdown',event=>{if(event.button!==0)return;event.preventDefault();event.stopPropagation();playSound('click');openStoreMeta(meta)});card.append(shop)} + world.append(card);const board={id:meta.id,meta,card,svg,signature:staticBoardSignature(meta)};staticRendered.set(meta.id,board);refreshClaimPresentation(meta.id);perfCount('staticBoardsCreated');return board; +} + +function makeBoard(meta){ + const makeStarted=perfStart(); + const p=puzzleOf(meta),w=p.bounds.w*CELL+PAD*2,h=p.bounds.h*CELL+PAD*2; + const card=document.createElement('div');card.className='board-card';card.style.left=((meta.x-renderOriginX)*UNIT-PAD)+'px';card.style.top=((meta.y-renderOriginY)*UNIT-PAD)+'px';card.style.width=w+'px';card.style.height=h+'px';card.dataset.id=meta.id; + const region=regionFor(meta.targetLevel??meta.level);card.dataset.region=region.name.toLowerCase();card.dataset.difficulty=String(meta.level);card.style.setProperty('--region',region.accent); + const label=document.createElement('div');label.className='board-label'; + const solverBadge=document.createElement('div');solverBadge.className='solver-badge';solverBadge.setAttribute('aria-hidden','true'); + const claimBadge=document.createElement('div');claimBadge.className='board-claim-badge';claimBadge.hidden=true;claimBadge.setAttribute('aria-live','polite'); + const solverCaption=document.createElement('span');solverCaption.textContent='\u30af\u30ea\u30a2'; + const solverName=document.createElement('strong');solverBadge.append(solverCaption,solverName); + const boardActions=document.createElement('div');boardActions.className='board-actions'; + const boardReset=document.createElement('button');boardReset.type='button';boardReset.className='board-action board-reset';boardReset.textContent='\u21ba \u30ea\u30bb\u30c3\u30c8';boardReset.setAttribute('aria-label','\u3053\u306e\u76e4\u9762\u306e\u7dda\u3092\u3059\u3079\u3066\u6d88\u3059');boardActions.append(boardReset);label.append(boardActions); + const storeButton=document.createElement('button');storeButton.type='button';storeButton.className='line-store';storeButton.hidden=true;storeButton.innerHTML='SHOP'; + const scoreLensBadge=document.createElement('div');scoreLensBadge.className='score-lens-badge';scoreLensBadge.hidden=true;scoreLensBadge.setAttribute('aria-hidden','true'); + const svg=svgEl('svg',{viewBox:`0 0 ${w} ${h}`,width:w,height:h,class:'board-svg',tabindex:0,role:'group','aria-label':`\u30ec\u30d9\u30eb${meta.level}`}); + const overviewLayer=svgEl('g',{class:'overview-layer'}),staticLayer=svgEl('g',{class:'static-layer'}),specialLayer=svgEl('g',{class:'special-cell-layer'}),connectorLayer=svgEl('g',{class:'connector-layer'}),pathLayer=svgEl('g',{class:'path-layer'}),dragLayer=svgEl('g',{class:'drag-layer'}),claimPreviewLayer=svgEl('g',{class:'claim-preview-layer'}),numberLayer=svgEl('g',{class:'number-layer'}),gateLayer=svgEl('g',{class:'gate-layer'}); + overviewLayer.append(svgEl('path',{d:overviewChunkPath(meta),class:'overview-fill'})); + const[bgStart,bgEnd]=levelBackground(meta.level),bgId=`board-level-bg-${meta.id}`,bgDefs=svgEl('defs'),bgGradient=svgEl('linearGradient',{id:bgId,x1:'0%',y1:'0%',x2:'100%',y2:'100%'}); + bgGradient.append(svgEl('stop',{offset:'0%','stop-color':bgStart}),svgEl('stop',{offset:'100%','stop-color':bgEnd}));bgDefs.append(bgGradient);staticLayer.append(bgDefs); + const validCellPath=boardCellsPath(p.valid),inputClipId=`board-input-clip-${meta.id}`,inputClip=svgEl('clipPath',{id:inputClipId,clipPathUnits:'userSpaceOnUse'}),inputClipShape=svgEl('path',{d:validCellPath});inputClip.append(inputClipShape);bgDefs.append(inputClip); + const cellShape=svgEl('path',{d:validCellPath,class:'cell-shape',fill:`url(#${bgId})`}),unfilledWarningShape=svgEl('path',{d:'',class:'unfilled-warning-cells'}),boardInputSurface=svgEl('path',{d:validCellPath,class:'board-input-surface','aria-hidden':'true'});staticLayer.append(cellShape,unfilledWarningShape,boardInputSurface) + for(const[r,c]of p.obstacles||[]){const x=PAD+c*CELL,y=PAD+r*CELL,group=svgEl('g',{class:'obstacle-mark'});group.append(svgEl('rect',{x,y,width:CELL,height:CELL,class:'obstacle-cell'}),svgEl('line',{x1:x+10,y1:y+10,x2:x+CELL-10,y2:y+CELL-10,class:'obstacle-cross'}),svgEl('line',{x1:x+CELL-10,y1:y+10,x2:x+10,y2:y+CELL-10,class:'obstacle-cross'}));staticLayer.append(group)} + drawOuterEdges(staticLayer,p); + const specialInfo=specialCellInfoMap(p),specialSets=specialCellSet(p),specialNodes=new Map(); + for(const[key,info]of specialInfo){const[r,c]=key.split(',').map(Number),marker=makeSpecialMarker(info,r,c);specialNodes.set(key,marker);specialLayer.append(marker)} + const numberNodes=new Map(),numberWarningNodes=new Map();for(const[r,c,v]of p.n){ + const[x,y]=boardCellCenter([r,c]),key=ckey(r,c),node=svgEl('text',{x,y,class:'num'}), + warning=svgEl('g',{class:'number-turn-warning','aria-hidden':'true'}), + frame=svgEl('rect',{x:x-CELL*.39,y:y-CELL*.39,width:CELL*.78,height:CELL*.78,class:'number-warning-frame'}), + caption=svgEl('text',{x,y:y+14,class:'number-warning-caption'}); + node.textContent=v;caption.textContent='\u66f2\u304c\u308b';warning.append(frame,caption);numberNodes.set(key,node);numberWarningNodes.set(key,warning);numberLayer.append(node,warning); + } + const gateDots=[],gateKnobs=[],gateMarkers=[],gateHits=[],gateIndexesByCell=new Map(); + p.g.forEach((_,i)=>{ + const g=gateObj(p,i),gp=gatePoint(g),sealed=!gateConnectionAllowed(meta,i), + dot=svgEl('circle',{cx:gp[0],cy:gp[1],r:4.5,class:`gate-dot${sealed?' sealed':''}`,fill:'#7d878e'}), + knob=svgEl('circle',{cx:gp[0],cy:gp[1],r:7,class:'gate-knob','data-gate-knob':i}), + marker=svgEl('path',{d:gateMarkerPathAt(gp,g.side,sealed),class:`gate-marker${sealed?' sealed':''}`}), + hit=svgEl('rect',{...gateHitBox(gp,g.side),rx:3,class:'gate-hit','clip-path':`url(#${inputClipId})`,'data-gate':i,tabindex:0,role:'button','aria-label':`${sealed?'\u7d42\u7aef':'\u30b2\u30fc\u30c8 '+(i+1)}`}); + gateDots.push(dot);gateKnobs.push(knob);gateMarkers.push(marker);gateHits.push(hit);const gateCellKey=ckey(...g.cell),cellGates=gateIndexesByCell.get(gateCellKey)||[];cellGates.push(i);gateIndexesByCell.set(gateCellKey,cellGates); + gateLayer.append(marker,dot,knob,hit); + }); + svg.append(overviewLayer,staticLayer,specialLayer,connectorLayer,gateLayer,pathLayer,dragLayer,claimPreviewLayer,numberLayer);card.append(label,svg,solverBadge,claimBadge,storeButton,scoreLensBadge);world.append(card); + const b={id:meta.id,meta,p,card,svg,label,solverBadge,solverName,claimBadge,storeButton,drawing:null,pendingClaimPointer:null,pendingClaimFrame:0,armedGate:null,w,h,connectorLayer,pathLayer,dragLayer,claimPreviewLayer,numberLayer,specialLayer,dragPathIndex:null,pathStrokeNodes:[],pathVisualNodes:new Map(),connectorNodes:[],numberNodes,numberWarningNodes,numberWarningKeys:new Set(),multipleWarningKeys:new Set(),gateDots,gateKnobs,gateMarkers,gateHits,gateIndexesByCell,endpointIndexesByCell:new Map(),gateLayer,boardActions,boardReset,scoreLensBadge,cellShape,boardInputSurface,inputClipId,unfilledWarningShape,cellFlashAnimations:new Map(),specialNodes,specialInfo,specialSets,crossingKeySet:new Set(specialSets.crossings.map(cell=>ckey(...cell))),dragSpecialRevision:0,dragSpecialAppliedRevision:-1,unfilledWarningActive:false,solvedPathsRendered:false,specialCrossTriggered:false,timers:new Set()}; + positionBoardLabel(b);rendered.set(meta.id,b);bindBoard(b);renderBoardNow(b); + if(meta.justRevealed){card.classList.add('revealing');boardTimeout(b,()=>card.classList.remove('revealing'),900);delete meta.justRevealed} + perfCount('boardsCreated');perfEnd('makeBoard',makeStarted);return b; +} +function destroyBoard(board){ + if(!board)return;finishCompletionVisual(board.id,true);const pointerId=board.drawing?.pointerId;board.drawing=null;cancelBoardDragFrame(board);dirtyBoards.delete(board);for(const key of[...lineWidthDirtyPathKeys])if(key.startsWith(`${board.id}:`))lineWidthDirtyPathKeys.delete(key); + if(pointerId!=null)safeRelease(board.svg,pointerId); + for(const timer of board.timers||[])clearTimeout(timer);board.timers?.clear?.(); + for(const animation of board.cellFlashAnimations?.values?.()||[])try{animation.cancel()}catch(_){} + board.cellFlashAnimations?.clear?.();board.pathStrokeNodes=[];board.connectorNodes=[];board.pathVisualNodes?.clear?.();board.numberNodes?.clear?.();board.numberWarningNodes?.clear?.();board.specialNodes?.clear?.();board.dragLayer?.replaceChildren?.();board.pathLayer?.replaceChildren?.();board.card.remove();rendered.delete(board.id);perfCount('boardsDestroyed'); +} +function pointerPointForDrawing(drawing,point){const offset=drawing?.pointerOffset||[0,0];return[point[0]+offset[0],point[1]+offset[1]]} +const POINTER_DIRECTION_DELTAS=Object.freeze({N:Object.freeze([-1,0]),S:Object.freeze([1,0]),W:Object.freeze([0,-1]),E:Object.freeze([0,1])}); +function setPoint(target,x,y){if(Array.isArray(target)){target[0]=x;target[1]=y;return target}return[x,y]} +function setSvgAttr(node,name,value){const text=String(value);if(node.getAttribute(name)!==text)node.setAttribute(name,text)} +function drawingForPath(b,pathIndex,pointerId=null,lastPoint=null){ + const path=metaState(b.id).paths[pathIndex],tip=path?.cells?.[path.cells.length-1]||[0,0],center=boardCellCenter(tip); + return{ + phase:'dragging',pointerId,keyboardActive:pointerId==null,pathIndex,lastPoint, + rawPointerPosition:lastPoint?[lastPoint[0],lastPoint[1]]:null,currentConfirmedCell:[tip[0],tip[1]], + candidateDirection:null,currentCandidateCell:null, + renderedHandlePosition:[center[0],center[1]],originGate:null,originGateCell:null,gateSnapArmed:false,leftOriginGateCell:false, + warpDirection:null,segmentOccupancy:occupiedMap(metaState(b.id)),pathCellIndex:new Map((path?.cells||[]).map((cell,index)=>[ckey(...cell),index])),logicalRevision:0,lastModelProbeKey:null,lastRenderedTip:null + }; +} +function syncDrawingConfirmedState(b,drawing=b?.drawing){ + const path=drawing&&metaState(b.id).paths[drawing.pathIndex],tip=path?.cells?.[path.cells.length-1];if(!tip)return false; + drawing.currentConfirmedCell=setPoint(drawing.currentConfirmedCell,tip[0],tip[1]); + const center=boardCellCenter(tip);drawing.renderedHandlePosition=setPoint(drawing.renderedHandlePosition,center[0],center[1]);return true; +} +function clearDrawingPointerState(drawing){ + if(!drawing)return;drawing.rawPointerPosition=null;drawing.candidateDirection=null;drawing.currentCandidateCell=null; +} +function directionFromDelta(dx,dy,preferredAxis=null){ + const adx=Math.abs(dx),ady=Math.abs(dy),largest=Math.max(adx,ady); + if(largestady; + return horizontal?(dx<0?'W':'E'):(dy<0?'N':'S'); +} +function updateDrawingPointerState(b,drawing,point){ + if(!drawing||!point||!syncDrawingConfirmedState(b,drawing))return null; + drawing.rawPointerPosition=setPoint(drawing.rawPointerPosition,point[0],point[1]); + const center=boardCellCenter(drawing.currentConfirmedCell),dx=point[0]-center[0],dy=point[1]-center[1],direction=directionFromDelta(dx,dy,pathTailAxis(activePath(b),b.p)); + drawing.candidateDirection=direction||null; + if(direction){ + const delta=POINTER_DIRECTION_DELTAS[direction],cell=drawing.currentConfirmedCell; + drawing.currentCandidateCell=setPoint(drawing.currentCandidateCell,cell[0]+delta[0],cell[1]+delta[1]); + }else drawing.currentCandidateCell=null; + updateDrawingHandlePosition(b,drawing,point); + return direction; +} +function updateDrawingHandlePosition(b,drawing,point){ + if(!drawing||!point||!syncDrawingConfirmedState(b,drawing))return false; + // Keep the model snapped to the confirmed cell; only the visual handle follows the pointer. + const center=boardCellCenter(drawing.currentConfirmedCell),dx=point[0]-center[0],dy=point[1]-center[1], + distance=Math.hypot(dx,dy),limit=CELL*.44,scale=distance>limit?limit/distance:1; + drawing.renderedHandlePosition=setPoint(drawing.renderedHandlePosition,center[0]+dx*scale,center[1]+dy*scale); + return true; +} +function liveEndpointPoint(b,pathIndex,actualTip){ + const drawing=b.drawing; + if(!drawing||drawing.pointerId==null||drawing.pathIndex!==pathIndex)return actualTip; + return drawing.renderedHandlePosition||actualTip; +} +function clearDragRender(b){ + if(!b)return; + if(Number.isInteger(b.dragPathIndex))for(const node of b.pathVisualNodes?.get(b.dragPathIndex)||[])node.style.removeProperty('display'); + if(Number.isInteger(b.dragGateIndex))b.gateKnobs?.[b.dragGateIndex]?.classList.remove('drag-target'); + if(b.dragLayer)b.dragLayer.style.display='none';if(b.dragCache){b.dragCache.logicalRevision=-1;b.dragCache.geometryRevision=-1}b.dragPathIndex=null;b.dragGateIndex=null;b.dragSpecialAppliedRevision=-1; +} +function buildDragCache(b,index,blended,count){ + const layer=b.dragLayer,defs=svgEl('defs'),strokes=[],gradients=[];layer.replaceChildren(defs); + for(let pieceIndex=0;pieceIndexpath.cells.some(cell=>sameCell(cell,lock.key))),doorUnlocked=st.paths.some(path=>pathHasLockKey(path,lock));b.specialNodes.get(ckey(...lock.key))?.classList.toggle('activated',keyTouched);b.specialNodes.get(ckey(...lock.door))?.classList.toggle('unlocked',doorUnlocked)} +} +function matchingNumberKeys(path,p){ + if(!path?.cells?.length)return new Set(); + const turns=partialTurnCount(path,p),occupied=new Set(path.cells.map(cell=>ckey(...cell))); + return new Set(p.n.filter(([r,c,value])=>value===turns&&occupied.has(ckey(r,c))).map(([r,c])=>ckey(r,c))); +} +function showNumberMatchEffect(b,key,cache){ + const cell=key.split(',').map(Number),[cx,cy]=boardCellCenter(cell),pulse=svgEl('circle',{cx,cy,r:9,class:'number-match-pulse'}); + b.numberLayer.append(pulse);boardTimeout(b,()=>pulse.remove(),420); +} +function updateNumberMatchFeedback(b,path,cache){ + const drawing=b?.drawing;if(!drawing)return; + const current=matchingNumberKeys(path,b.p),previous=drawing.matchingNumberKeys; + if(previous)for(const key of current)if(!previous.has(key))showNumberMatchEffect(b,key,cache); + cache.knob.classList.toggle('number-match',current.size>0);cache.halo.classList.toggle('number-match',current.size>0);cache.matchOrbit.classList.toggle('show',current.size>0); + drawing.matchingNumberKeys=current; +} +function drawingLineWidth(b,pathIndex){const drawing=b?.drawing;if(drawing?.pathIndex===pathIndex&&Number.isFinite(drawing.lineWidth))return drawing.lineWidth;const width=renderedConnectedLineWidth(b.meta,pathIndex);if(drawing?.pathIndex===pathIndex)drawing.lineWidth=width;return width} +function renderDragFrame(b){ + const started=perfStart(),drawing=b?.drawing,path=activePath(b);if(!drawing||drawing.pointerId==null||!path?.cells?.length){clearDragRender(b);return false} + if(!drawing.pickupStartMeasured){ + drawing.pickupStartMeasured=true;perfGauge('pickupStartFullBoardRenders',Math.max(0,(perfCounters.fullBoardRenders||0)-(drawing.fullBoardRendersAtPointerDown||0))); + if(Number.isFinite(drawing.pointerDownAt))perfObserve('pickupStartLatency',Math.max(0,perfNow()-drawing.pointerDownAt)); + } + dirtyBoards.delete(b); + const index=drawing.pathIndex,baseNodes=b.pathVisualNodes?.get(index)||[];if(!baseNodes.length&&!drawing.createdPath){renderBoardNow(b);return false} + if(b.dragPathIndex!==index){clearDragRender(b);b.dragPathIndex=index;for(const node of baseNodes)node.style.display='none'} + b.dragLayer.style.display=''; + const logicalRevision=drawing.logicalRevision||0,startIndex=path.startColorIndex??path.colorIndex??index,endIndex=path.endColorIndex??startIndex, + startColor=LINE_COLORS[startIndex%LINE_COLORS.length],endColor=LINE_COLORS[endIndex%LINE_COLORS.length], + blended=false,lineWidth=drawingLineWidth(b,index); + if(drawing.renderGeometryRevision!==logicalRevision||drawing.renderBlended!==blended){ + drawing.renderGeometryRevision=logicalRevision;drawing.renderBlended=blended;drawing.renderSegments=pathRenderSegments(path,b.p);drawing.renderTurnCount=String(partialTurnCount(path,b.p)); + drawing.renderPieces=blended?pathStrokePieces(drawing.renderSegments,startColor,endColor):drawing.renderSegments.map(points=>({points})); + } + const pieces=drawing.renderPieces||[],cache=!b.dragCache||b.dragCache.index!==index||b.dragCache.blended!==blended||b.dragCache.count!==pieces.length?buildDragCache(b,index,blended,pieces.length):b.dragCache; + const geometryChanged=cache.geometryRevision!==logicalRevision; + if(geometryChanged){ + pieces.forEach((piece,pieceIndex)=>{ + const node=cache.strokes[pieceIndex],points=piece.points,pointsText=points.map(point=>point.join(',')).join(' ');if(node.getAttribute('points')!==pointsText)node.setAttribute('points',pointsText);applyLineWidth(node,lineWidth); + if(blended){const first=points[0],last=points[points.length-1],gradient=cache.gradients[pieceIndex];gradient.gradient.setAttribute('x1',first[0]);gradient.gradient.setAttribute('y1',first[1]);gradient.gradient.setAttribute('x2',last[0]);gradient.gradient.setAttribute('y2',last[1]);gradient.start.setAttribute('stop-color',piece.startColor);gradient.end.setAttribute('stop-color',piece.endColor)} + else if(node.getAttribute('stroke')!==startColor)node.setAttribute('stroke',startColor); + });cache.geometryRevision=logicalRevision; + } + const actualTip=boardCellCenter(path.cells[path.cells.length-1]),tip=liveEndpointPoint(b,index,actualTip),displayColorIndex=displayedEndpointColorIndex(b.meta,path,'end'),color=LINE_COLORS[displayColorIndex%LINE_COLORS.length],turnCount=drawing.renderTurnCount||'0'; + const previousTip=drawing.lastRenderedTip;if(previousTip&&cache.logicalRevision===logicalRevision&&Math.abs(previousTip[0]-tip[0])<.45&&Math.abs(previousTip[1]-tip[1])<.45){perfEnd('renderDragFrame',started);return true}drawing.lastRenderedTip=setPoint(drawing.lastRenderedTip,tip[0],tip[1]); + const tailNow=perfNow(),tailDue=geometryChanged||!drawing.lastTailRenderAt||tailNow-drawing.lastTailRenderAt>=32; + if(tailDue){drawing.lastTailRenderAt=tailNow;setSvgAttr(cache.liveTail,'x1',actualTip[0]);setSvgAttr(cache.liveTail,'y1',actualTip[1]);setSvgAttr(cache.liveTail,'x2',tip[0]);setSvgAttr(cache.liveTail,'y2',tip[1]);setSvgAttr(cache.liveTail,'stroke',startColor);applyLineWidth(cache.liveTail,lineWidth)} + if(path.detachedStart){const startTip=boardCellCenter(path.cells[0]);for(const node of[cache.startHalo,cache.startKnob]){node.style.display='';node.setAttribute('cx',startTip[0]);node.setAttribute('cy',startTip[1])}cache.startKnob.setAttribute('fill',startColor)}else for(const node of[cache.startHalo,cache.startKnob])node.style.display='none'; + const tipTransform=`translate3d(${tip[0]}px,${tip[1]}px,0)`;if(cache.tipGroup.style.transform!==tipTransform)cache.tipGroup.style.transform=tipTransform; + setSvgAttr(cache.knob,'fill',color);if(data.cursorStyle!=='default')updateDragCursorDesign(cache);if(cache.turns.textContent!==turnCount)cache.turns.textContent=turnCount;refreshDragSpecialState(b); + if(cache.logicalRevision!==logicalRevision){updateNumberMatchFeedback(b,path,cache);refreshDragNumberColors(b);cache.logicalRevision=logicalRevision} + const gateIndex=gateAtCell(b.p,path.cells[path.cells.length-1],path.startGate),nextGateIndex=Number.isInteger(gateIndex)?gateIndex:null; + if(b.dragGateIndex!==nextGateIndex){if(Number.isInteger(b.dragGateIndex))b.gateKnobs[b.dragGateIndex]?.classList.remove('drag-target');b.dragGateIndex=nextGateIndex;if(Number.isInteger(nextGateIndex))b.gateKnobs[nextGateIndex]?.classList.add('drag-target')} + perfCount('dragRenders');perfEnd('renderDragFrame',started);return true; +} +function refreshDragNumberColors(b){ + const st=metaState(b.id),drawing=b.drawing,path=drawing&&st.paths[drawing.pathIndex];if(!drawing||!path)return; + const current=new Set(path.cells.map(cell=>ckey(...cell)).filter(key=>b.numberNodes.has(key))),affected=new Set([...(drawing.dragNumberKeys||[]),...current]),occupancy=drawing.segmentOccupancy||occupiedMap(st); + for(const key of affected){ + const node=b.numberNodes.get(key);if(!node)continue;const pathIndex=occupancy.get(key),owner=Number.isInteger(pathIndex)?st.paths[pathIndex]:null,cell=key.split(',').map(Number),lineColor=owner?pathColorAtCell(owner,b.p,cell):null; + node.classList.toggle('on-line',Boolean(lineColor));if(lineColor)node.style.setProperty('--number-line-color',lineColor);else node.style.removeProperty('--number-line-color'); + } + drawing.dragNumberKeys=current; +} +function boardPlayHudVisible(b,solved=metaState(b.id).solved){return !solved&&hudBoardId===b?.id} +function renderBoardNow(b){ + const renderStarted=perfStart();perfCount('fullBoardRenders'); + clearDragRender(b); + const{p,meta,card,label,pathLayer,connectorLayer}=b,st=metaState(meta.id); + rebuildEndpointIndexes(b,st); + const solved=!!st.solved,validation=new Map(),lineLengthCache=currentLineGraphCaches().widths; + connectorLayer.replaceChildren();b.connectorNodes=[];card.classList.toggle('solved',solved);applyClaimPresentationToBoard(b);updateScoreLensBadge(b);const hudVisible=boardPlayHudVisible(b,solved);card.classList.toggle('hud-current',hudVisible);if(hudVisible)positionBoardLabel(b);b.boardActions.hidden=solved; + const solver=st.solvedBy&&st.solvedBy!==LEGACY_LOCAL_SOLVER?st.solvedBy:currentPlayerName(); + b.solverName.textContent=solver;b.solverBadge.setAttribute('aria-hidden',solved?'false':'true'); + const store=solved?st.store:null,storePath=store?st.paths[store.pathIndex]:null,storeCell=storePath?.cells?.[store.cellIndex]; + b.storeButton.hidden=!storeCell; + if(storeCell){ + const[x,y]=boardCellCenter(storeCell);b.storeButton.style.left=x+'px';b.storeButton.style.top=y+'px'; + b.storeButton.setAttribute('aria-label',`${store.owner}\u306e\u30b7\u30e7\u30c3\u30d7`); + b.storeButton.title=`${store.owner}\u306e\u30b7\u30e7\u30c3\u30d7`; + } + card.tabIndex=solved?0:-1;if(solved)card.setAttribute('aria-label',`\u30ec\u30d9\u30eb${meta.level}\u3002\u30af\u30ea\u30a2\u6e08\u307f\u3002`);else card.removeAttribute('aria-label'); + b.svg.setAttribute('tabindex',solved?-1:0); + if(!solved)for(const path of st.paths)if(path.endGate!=null)validation.set(path,pathValid(path,p)); + const invalidPaths=solved?0:[...validation.values()].filter(valid=>!valid).length; + card.classList.toggle('has-errors',invalidPaths>0);b.hadInvalidPaths=invalidPaths>0; + const occupiedKeys=new Set();for(const route of st.paths)for(const cell of route?.cells||[])occupiedKeys.add(ckey(...cell)); + const special=specialCellSet(p); + for(const cell of special.crossings)b.specialNodes.get(ckey(...cell))?.classList.toggle('activated',Boolean(crossingStateAtCell(st,p,cell))); + for(const pair of special.warps)for(const cell of[pair.a,pair.b])b.specialNodes.get(ckey(...cell))?.classList.toggle('activated',occupiedKeys.has(ckey(...cell))); + for(const lock of special.locks){ + const keyTouched=st.paths.some(path=>path.cells.some(cell=>sameCell(cell,lock.key))),doorUnlocked=st.paths.some(path=>pathHasLockKey(path,lock)); + b.specialNodes.get(ckey(...lock.key))?.classList.toggle('activated',keyTouched);b.specialNodes.get(ckey(...lock.door))?.classList.toggle('unlocked',doorUnlocked); + } + const allLinesConnected=st.paths.length===p.n.length&&st.paths.length>0&&st.paths.every(route=>route?.endGate!=null),emptyKeys=new Set(p.valid.map(cell=>ckey(...cell)).filter(key=>!occupiedKeys.has(key))),unfilledWarning=!solved&&allLinesConnected&&emptyKeys.size>0; + if(b.unfilledWarningShape)b.unfilledWarningShape.setAttribute('d',unfilledWarning?boardCellsPath(p.valid.filter(cell=>emptyKeys.has(ckey(...cell)))):''); + card.classList.toggle('has-unfilled-warning',unfilledWarning); + const showUnfilledWarning=unfilledWarning&&!b.unfilledWarningActive&&activeBoard===meta.id; + b.unfilledWarningActive=unfilledWarning; + label.replaceChildren(Object.assign(document.createElement('b'),{textContent:`\u30ec\u30d9\u30eb${meta.level}`}),b.boardActions); + if(!solved||!b.solvedPathsRendered){ + pathLayer.replaceChildren();b.pathStrokeNodes=[];b.pathVisualNodes=new Map(); + const defs=svgEl('defs');pathLayer.append(defs); + st.paths.forEach((path,index)=>{ + if(!path.cells.length)return; + const visualNodes=[];b.pathVisualNodes.set(index,visualNodes); + const pointerDragging=b.drawing?.pathIndex===index&&b.drawing.pointerId!=null,segments=pathRenderSegments(path,p),actualTip=boardCellCenter(path.cells[path.cells.length-1]),visualTip=pointerDragging?liveEndpointPoint(b,index,actualTip):actualTip; + if(pointerDragging&&path.endGate==null&&segments.length){const liveSegment=segments[segments.length-1];if(liveSegment.length)liveSegment[liveSegment.length-1]=visualTip} + const startIndex=path.startColorIndex??path.colorIndex??index,endIndex=path.endColorIndex??startIndex,colorEndGate=path.endGate??path.openGate, + startColor=LINE_COLORS[startIndex%LINE_COLORS.length],endColor=LINE_COLORS[endIndex%LINE_COLORS.length],invalid=path.endGate!=null&&!validation.get(path)&&!solved, + lineWidth=renderedConnectedLineWidth(meta,index,lineLengthCache),blended=!uiSettings.lightweightRendering&&colorEndGate!=null&&endIndex!==startIndex; + if(blended){ + for(const[pieceIndex,piece]of pathStrokePieces(segments,startColor,endColor).entries()){ + const first=piece.points[0],last=piece.points[piece.points.length-1],gradientId=`path-gradient-${b.id}-${index}-${pieceIndex}`,gradient=svgEl('linearGradient',{id:gradientId,gradientUnits:'userSpaceOnUse',x1:first[0],y1:first[1],x2:last[0],y2:last[1]}); + gradient.append(svgEl('stop',{offset:'0%','stop-color':piece.startColor}),svgEl('stop',{offset:'100%','stop-color':piece.endColor}));defs.append(gradient); + const pathNode=svgEl('polyline',{points:piece.points.map(q=>q.join(',')).join(' '),class:`path${invalid?' invalid':''}`,stroke:`url(#${gradientId})`,'data-path-index':index,'data-path-progress-start':piece.startProgress.toFixed(6),'data-path-progress-end':piece.endProgress.toFixed(6)}); + applyLineWidth(pathNode,lineWidth);b.pathStrokeNodes.push(pathNode);visualNodes.push(pathNode);pathLayer.append(pathNode); + } + }else for(const points of segments){ + const pathNode=svgEl('polyline',{points:points.map(q=>q.join(',')).join(' '),class:`path${invalid?' invalid':''}`,stroke:startColor,'data-path-index':index}); + applyLineWidth(pathNode,lineWidth);b.pathStrokeNodes.push(pathNode);visualNodes.push(pathNode);pathLayer.append(pathNode); + } + if(path.endGate==null){ + const tip=visualTip,isActive=b.drawing?.pathIndex===index,turns=partialTurnCount(path,p),bx=tip[0]+19,by=tip[1]-19,endPickupColor=LINE_COLORS[displayedEndpointColorIndex(meta,path,'end')%LINE_COLORS.length],startPickupColor=LINE_COLORS[displayedEndpointColorIndex(meta,path,'start')%LINE_COLORS.length]; + const + halo=svgEl('circle',{cx:tip[0],cy:tip[1],r:7,class:'endpoint-halo'}),knob=svgEl('circle',{cx:tip[0],cy:tip[1],r:4.8,class:'endpoint-knob',fill:endPickupColor}),hitNode=svgEl('circle',{cx:tip[0],cy:tip[1],r:24,class:'endpoint-hit','clip-path':`url(#${b.inputClipId})`,'data-path-index':index,'data-endpoint-side':'end',tabindex:0,role:'button','aria-label':`\u7dda\u306e\u7d9a\u304d\u3092\u5f15\u304f: ${index+1}`}); + if(path.detachedStart){const startTip=boardCellCenter(path.cells[0]),startHalo=svgEl('circle',{cx:startTip[0],cy:startTip[1],r:7,class:'endpoint-halo'}),startKnob=svgEl('circle',{cx:startTip[0],cy:startTip[1],r:4.8,class:'endpoint-knob',fill:startPickupColor}),startHit=svgEl('circle',{cx:startTip[0],cy:startTip[1],r:24,class:'endpoint-hit','clip-path':`url(#${b.inputClipId})`,'data-path-index':index,'data-endpoint-side':'start',tabindex:0,role:'button','aria-label':`\u7dda\u306e\u53cd\u5bfe\u5074\u3092\u5f15\u304f: ${index+1}`});visualNodes.push(startHalo,startKnob,startHit);pathLayer.append(startHalo,startKnob,startHit)} + visualNodes.push(halo,knob,hitNode);pathLayer.append(halo,knob,hitNode); + if(isActive){const badge=svgEl('circle',{cx:bx,cy:by,r:10.5,class:'turn-badge'}),t=svgEl('text',{x:bx,y:by+.5,class:'turn-count'});t.textContent=turns;visualNodes.push(badge,t);pathLayer.append(badge,t)} + } + }); + b.solvedPathsRendered=solved; + } + if(!solved)b.solvedPathsRendered=false; + p.g.forEach((_,gi)=>{ + const hit=matchingNeighborGate(meta,gi);if(!hit)return; + const neighborState=metaState(hit.meta.id),own=gateUsedPath(st,gi),other=gateUsedPath(neighborState,hit.gateIndex); + if(!own&&!other)return; + const localColorIndex=confirmedBoundaryColorIndex(meta,gi)??pathColorIndexAtGate(own,gi)??pathColorIndexAtGate(other,hit.gateIndex)??0, + localColor=LINE_COLORS[localColorIndex%LINE_COLORS.length], + g=gateObj(p,gi),gp=gatePoint(g),[dr,dc]=SIDE_D[g.side],end=[gp[0]+dc*CELL*.5,gp[1]+dr*CELL*.5]; + const connector=svgEl('line',{x1:gp[0],y1:gp[1],x2:end[0],y2:end[1],class:'path connector',stroke:localColor}); + if(own){connector.dataset.ownerBoard=meta.id;connector.dataset.pathIndex=String(st.paths.indexOf(own))} + else{connector.dataset.ownerBoard=hit.meta.id;connector.dataset.pathIndex=String(neighborState.paths.indexOf(other))} + const owner=data.metas[connector.dataset.ownerBoard],ownerIndex=Number(connector.dataset.pathIndex); + if(owner&&Number.isInteger(ownerIndex)){const width=renderedConnectedLineWidth(owner,ownerIndex,lineLengthCache);applyLineWidth(connector,width)} + b.connectorNodes.push(connector);connectorLayer.append(connector); + }); + if(!solved){ + const bad=new Set(),turnWarnings=straightNumberWarningKeys(st,p),multipleWarnings=multipleNumberWarningKeys(st,p), + warningKeys=new Set([...turnWarnings,...multipleWarnings]);st.paths.forEach(path=>{if(path.endGate==null)return;const nums=numbersForPath(path,p),ta=turnAnalysis(path,p);for(const num of nums)if(nums.length!==1||num.value!==ta.count||!ta.cells.some(c=>sameCell(c,num.cell)))bad.add(ckey(...num.cell))}); + const numberOccupancy=occupiedMap(st); + for(const[key,node]of b.numberNodes){ + const multiple=multipleWarnings.has(key),warning=b.numberWarningNodes.get(key),pathIndex=numberOccupancy.get(key),path=Number.isInteger(pathIndex)?st.paths[pathIndex]:null, + cell=key.split(',').map(Number),lineColor=path?pathColorAtCell(path,p,cell):null; + node.classList.toggle('bad',bad.has(key));node.classList.toggle('turn-warning',warningKeys.has(key));node.classList.toggle('multiple-warning',multiple);node.classList.toggle('on-line',Boolean(lineColor)); + if(lineColor)node.style.setProperty('--number-line-color',lineColor);else node.style.removeProperty('--number-line-color'); + warning?.classList.toggle('show',warningKeys.has(key));warning?.classList.toggle('multiple',multiple); + const caption=warning?.querySelector('.number-warning-caption');if(caption)caption.textContent=multiple?'1\u7dda1\u500b':'\u66f2\u304c\u308b'; + } + if(activeBoard===meta.id&&[...multipleWarnings].some(key=>!b.multipleWarningKeys.has(key)))toast('\u6570\u5b57\u306f1\u672c\u306e\u7dda\u306b1\u3064\u3067\u3059\u3002'); + else if(activeBoard===meta.id&&[...turnWarnings].some(key=>!b.numberWarningKeys.has(key)))toast('\u6570\u5b57\u306e\u30de\u30b9\u3067\u3001\u8868\u793a\u56de\u6570\u3068\u66f2\u304c\u308a\u6570\u3092\u5408\u308f\u305b\u307e\u3059\u3002'); + b.numberWarningKeys=turnWarnings;b.multipleWarningKeys=multipleWarnings; + if(showUnfilledWarning)toast('\u307e\u3060\u7a7a\u3044\u3066\u3044\u308b\u30de\u30b9\u304c\u3042\u308a\u307e\u3059\u3002'); + + } + const gateOwnColors=new Map(); + st.paths.forEach(path=>{ + if(!path.detachedStart)gateOwnColors.set(path.startGate,LINE_COLORS[(pathColorIndexAtGate(path,path.startGate)??0)%LINE_COLORS.length]); + if(path.endGate!=null)gateOwnColors.set(path.endGate,LINE_COLORS[(pathColorIndexAtGate(path,path.endGate)??0)%LINE_COLORS.length]); + if(path.openGate!=null)gateOwnColors.set(path.openGate,LINE_COLORS[(pathColorIndexAtGate(path,path.openGate)??0)%LINE_COLORS.length]); + }); + p.g.forEach((g,i)=>{ + const inherited=sharedBoundaryColorIndex(meta,i),path=gateUsedPath(st,i), + color=inherited!=null?LINE_COLORS[inherited]:(gateOwnColors.get(i)||'#f4f5f5'), + sealed=!gateConnectionAllowed(meta,i),[gr,gc]=globalCell(meta,[g[0],g[1]]),[dr,dc]=SIDE_D[g[2]], + frontier=!sealed&&!matchingNeighborGate(meta,i)&&!unitOccupiedAtGlobalCell(gr+dr,gc+dc,meta.id), + tip=path&&path.endGate==null&&path.cells.length?path.cells[path.cells.length-1]:null, + connected=Boolean(path&&path.endGate!=null), + armed=b.armedGate===i||path?.openGate===i||(path&&path.endGate==null&&sameCell(tip,[g[0],g[1]])), + gate=gateObj(p,i),gp=gatePoint(gate),gateCellKey=ckey(g[0],g[1]),available=!solved&&!path&&!occupiedKeys.has(gateCellKey); + b.gateDots[i].setAttribute('fill',color); + b.gateDots[i].classList.toggle('sealed',sealed); + b.gateDots[i].classList.toggle('frontier',frontier); + b.gateKnobs[i]?.setAttribute('fill',color); + b.gateKnobs[i]?.classList.toggle('connected',connected&&!sealed); + b.gateKnobs[i]?.classList.toggle('show',(Boolean(armed)||connected||available||Boolean(path?.openGate===i))&&!sealed); + b.gateMarkers[i]?.setAttribute('d',gateMarkerPathAt(gp,gate.side,sealed)); + b.gateMarkers[i]?.setAttribute('stroke',sealed?'#7d878e':color); + b.gateMarkers[i]?.classList.toggle('sealed',sealed); + }); + // Pointer dragging redraws the active polyline every frame. Its width was + // already assigned synchronously above, so defer whole-world work until the + if(b.drawing?.pointerId==null)queueLineWidthRefresh(b.id);else renderDragFrame(b); + if(minimapDirty)scheduleMinimap();if(overviewDirty&&inWorldOverview())scheduleWorldOverview(); + perfEnd('renderBoardNow',renderStarted); +} +function activePath(b){return b.drawing?metaState(b.id).paths[b.drawing.pathIndex]||null:null} +function nextPaint(){return new Promise(resolve=>requestAnimationFrame(()=>resolve()))} +const activeCompletionVisuals=new Map(); +function finishCompletionVisual(boardId,skipped=false){ + const visual=activeCompletionVisuals.get(boardId);if(!visual)return false;activeCompletionVisuals.delete(boardId);clearTimeout(visual.timer); + visual.board?.timers?.delete?.(visual.timer);visual.board?.card?.classList?.remove('completing');visual.flash?.remove?.();visual.burst?.remove?.(); + visual.resolve?.({skipped});return true; +} +function completionEffect(b,award){ + finishCompletionVisual(b.id,true); + let resolve;const finished=new Promise(done=>{resolve=done}),visual={board:b,flash:null,burst:null,timer:0,resolve,finished}; + activeCompletionVisuals.set(b.id,visual); + try{ + if(reducedMotionQuery?.matches){finishCompletionVisual(b.id,false);return visual} + b.card.classList.add('completing'); + visual.flash=document.createElement('div');visual.flash.className='completion-flash';b.card.append(visual.flash); + visual.burst=document.createElement('div');visual.burst.className='completion-burst';visual.burst.textContent=`◆ +${formatScore(award)}`;b.card.append(visual.burst); + visual.timer=setTimeout(()=>finishCompletionVisual(b.id,false),1800);b.timers?.add?.(visual.timer); + }catch(error){console.warn('BEND FIELD: completion effect skipped',error);finishCompletionVisual(b.id,true)} + return visual; +} +function gemCollectionSources(b,count){ + const cells=b?.p?.valid||[],rect=b?.svg?.getBoundingClientRect?.(),sources=[];if(!cells.length||!rect?.width)return sources; + const sx=rect.width/b.w,sy=rect.height/b.h,seed=b.meta.seed>>>0; + for(let index=0;index{ + const particle=document.createElement('i');particle.className='gem-particle';particle.setAttribute('aria-hidden','true');particle.style.left=`${x}px`;particle.style.top=`${y}px`;document.body.append(particle); + const dx=target[0]-x,dy=target[1]-y,arc=(index%2?-1:1)*(24+(index%5)*9),delay=reduced?0:index*48,duration=reduced?520:1450+(index%4)*110;latest=Math.max(latest,delay+duration); + try{ + const flight=particle.animate([ + {transform:'translate3d(0,10px,0) scale(.25)',opacity:0}, + {transform:`translate3d(${arc*.18}px,${-18-Math.abs(arc)*.16}px,0) scale(1.28)`,opacity:1,offset:.16}, + {transform:`translate3d(${arc*.28}px,${-25-Math.abs(arc)*.12}px,0) scale(1.05)`,opacity:1,offset:.56}, + {transform:`translate3d(${dx*.48+arc}px,${dy*.3-Math.abs(arc)}px,0) scale(.95)`,opacity:1,offset:.76}, + {transform:`translate3d(${dx}px,${dy}px,0) scale(.26)`,opacity:.15} + ],{duration,delay,easing:'cubic-bezier(.2,.72,.18,1)',fill:'forwards'}); + flight.finished.catch(()=>{}).finally(()=>particle.remove()); + }catch(_){setTimeout(()=>particle.remove(),delay+duration)} + }); + setTimeout(()=>scoreCountEl.closest?.('.stat')?.classList.remove('gem-receiving'),latest+120);return true; +} +function skipCompletionVisuals(){for(const boardId of[...activeCompletionVisuals.keys()])finishCompletionVisual(boardId,true)} +document.addEventListener('keydown',event=>{if(event.key==='Escape')skipCompletionVisuals()},true); +function updateSelectedProgress(b){if(b)selectedInfo.textContent=`\u30ec\u30d9\u30eb${b.meta.level}`} +let selectedProgressFrame=0,pendingProgressBoard=null; +function queueSelectedProgress(b){pendingProgressBoard=b;if(selectedProgressFrame)return;selectedProgressFrame=requestAnimationFrame(()=>{selectedProgressFrame=0;const pending=pendingProgressBoard;pendingProgressBoard=null;if(pending)updateSelectedProgress(pending)})} +const expansionErrorCounts=new Map(); +function handleExpansionError(error){ + const message=error?.message||String(error),signature=`${error?.name||'Error'}:${message}`,count=(expansionErrorCounts.get(signature)||0)+1; + expansionErrorCounts.set(signature,count); + if(count%20===0)console.warn(`BEND FIELD: expansion is still retrying (${count})`,message); + const workerDelay=error?.code==='WORKER_UNAVAILABLE'?Math.max(1000,workerRetryAt-Date.now()+50):700;scheduleExpansionRepair(workerDelay); +} +function checkSolvedWhenInteractionSettles(b){ + if(globalThis.document?.body?.classList?.contains?.('is-interacting')){requestAnimationFrame(()=>checkSolvedWhenInteractionSettles(b));return} + checkSolvedAndExpand(b).catch(handleExpansionError); +} +const pendingBoardCommandSettlements=new Map(); +let boardCommandSettleFrame=0; +function scheduleBoardCommandSettlement(b,{persist=false,solve=false,paint=true,invalidate=true,pathIndex=null}={}){ + if(!b)return; + const pending=pendingBoardCommandSettlements.get(b.id)||{b,persist:false,solve:false,paint:false,invalidate:false,pathIndexes:new Set()}; + pending.persist||=persist;pending.solve||=solve;pending.paint||=paint;pending.invalidate||=invalidate;if(Number.isInteger(pathIndex))pending.pathIndexes.add(pathIndex); + pendingBoardCommandSettlements.set(b.id,pending); + if(boardCommandSettleFrame)return; + boardCommandSettleFrame=requestAnimationFrame(()=>{ + boardCommandSettleFrame=0; + if(document.body.classList.contains('is-interacting')){scheduleBoardCommandSettlement(b,{paint:false});return} + const settlements=[...pendingBoardCommandSettlements.values()];pendingBoardCommandSettlements.clear(); + for(const item of settlements){ + const index=item.pathIndexes.size===1?[...item.pathIndexes][0]:null; + if(item.invalidate)invalidateLineGraphCaches(item.b.id,index); + if(item.paint&&item.b.card?.isConnected){renderBoard(item.b);updateSelectedProgress(item.b)} + if(item.persist)changed(item.b.id,index); + if(item.solve)checkSolvedWhenInteractionSettles(item.b); + } + }); +} +function applyBoardCommand(b,mutate,{persist=false,solve=false,paint=true,invalidate=true,deferSettlement=false}={}){ + const st=metaState(b.id),pathIndex=b.drawing?.pathIndex,pointerInteraction=b.drawing?.pointerId!=null,result=mutate(st);if(result===false)return false; + if(pointerInteraction&&b.drawing?.pointerId==null)refreshInteractionState(); + touchBoardClaim(b.id); + if(deferSettlement){scheduleBoardCommandSettlement(b,{persist,solve,paint,invalidate,pathIndex});return result??true} + if(invalidate)invalidateLineGraphCaches(b.id,pathIndex); + if(paint){renderBoard(b);updateSelectedProgress(b)} + if(persist)changed(b.id,pathIndex); + if(solve)checkSolvedWhenInteractionSettles(b); + return result??true; +} +function generationFailureBonusKey(boardId){return`bend-field:generation-failure:${data.worldEpoch||WORLD_GENERATION}:${boardId}`} +async function maybeGrantGenerationFailureBonus(meta,elapsedMs){ + if(!meta||elapsedMs{if(globalThis.BEND_DEBUG_GENERATION)console.debug('BEND FIELD: speculative expansion preparation skipped',error);return null}); + if(!await persistence){ + finishCompletionVisual(b.id,true);if(data.metas[b.id]===b.meta&&data.states[b.id]===st){data.states[b.id]=previous.state;data.lastSolveAt=previous.lastSolveAt;data.timeAttack=previous.timeAttack;data.timeAttackRev=previous.timeAttackRev;data.specialMechanicsSeen=previous.specialMechanicsSeen;b.meta.sealedSides=previous.sealedSides;b.meta.rev=previous.metaRev} + renderBoard(rendered.get(b.id));updateHud();throw new Error('保存できなかったため、クリアを取り消しました。'); + } + if(cloudAvailable&&data.cloudProfile){ + const published=await pushCloudPending(); + if(!published){ + finishCompletionVisual(b.id,true);if(data.metas[b.id]===b.meta&&data.states[b.id]===st){data.states[b.id]=previous.state;data.lastSolveAt=previous.lastSolveAt;data.timeAttack=previous.timeAttack;data.timeAttackRev=previous.timeAttackRev;data.specialMechanicsSeen=previous.specialMechanicsSeen;b.meta.sealedSides=previous.sealedSides;b.meta.rev=previous.metaRev;markStateDirty(b.id);markMetaDirty(b.id);markGlobalDirty(false);await persistNow({skipCloud:true})} + await pullCloudWorld(true);renderBoard(rendered.get(b.id));updateHud();toast('共有ワールドで先に更新されたため、最新状態を取得しました。');return false; + } + } + let durableMeta=data.metas[b.id],durableState=data.states[b.id];if(!durableMeta||!durableState?.solved){scheduleExpansionRepair();return false}scheduleTimeAttackSuggestionAfterCompletion(); + const prepared=await preparation;durableMeta=data.metas[b.id];durableState=data.states[b.id];if(!durableMeta||!durableState?.solved){scheduleExpansionRepair();return false} + const expansionStarted=perfNow();let made=0;try{made=await expandMeta(durableMeta,prepared)||0}catch(error){await maybeGrantGenerationFailureBonus(durableMeta,perfNow()-expansionStarted);handleExpansionError(error);return true} + await maybeGrantGenerationFailureBonus(durableMeta,perfNow()-expansionStarted); + if(cloudAvailable&&data.cloudProfile&&made){const shared=await pushCloudPending();if(!shared){await pullCloudWorld(true);scheduleExpansionRepair(500)}} + scheduleExpansionRepair();if(made){expansionErrorCounts.clear();toast(`新しい盤面を${made}枚生成しました。`)}else scheduleExpansionRepair(350);return true; +} +function gateConnectEffect(b,gi,color){ + if(!b?.gateLayer||!Number.isInteger(gi))return;const gp=gatePoint(gateObj(b.p,gi)); + const pulse=svgEl('circle',{cx:gp[0],cy:gp[1],r:7,class:'gate-connect-pulse',stroke:color||'#fff'}); + b.gateLayer.append(pulse);boardTimeout(b,()=>pulse.remove(),720); + b.card.classList.add('gate-connected');boardTimeout(b,()=>b.card.classList.remove('gate-connected'),320); + try{navigator.vibrate?.(18)}catch(_){} +} +function reconcileDrawingPresentation(){refreshInteractionState();scheduleInteractionSettlePresentation()} +function finalizeAtGate(b,gi){ + const st=metaState(b.id),pi=b.drawing?.pathIndex,path=pi!=null?st.paths[pi]:null;if(!path||gi==null||!path.detachedStart&&gi===path.startGate||usedGateSet(st,pi).has(gi))return false; + const g=b.p.g[gi],last=path.cells[path.cells.length-1];if(g[0]!==last[0]||g[1]!==last[1])return false; + const color=LINE_COLORS[(path.startColorIndex??path.colorIndex??0)%LINE_COLORS.length],done=applyBoardCommand(b,()=>{ + if(path.detachedStart){ + path.cells.reverse();path.startGate=gi;path.endGate=null;path.openGate=null;path.detachedStart=false; + const gateColor=canonicalGateColorIndex(b.meta,gi,path);path.colorIndex=gateColor;path.startColorIndex=gateColor;path.endColorIndex=null; + }else{path.endGate=gi;path.openGate=null;const inherited=neighborColor(b.meta,gi);path.endColorIndex=inherited??path.startColorIndex??path.colorIndex} + b.drawing=null;b.armedGate=null; + },{persist:true,solve:true,deferSettlement:true}); + if(done){playSound('gate');if(usesLightweightDragOverlay(b))requestAnimationFrame(()=>gateConnectEffect(b,gi,color));else gateConnectEffect(b,gi,color);queueMicrotask(reconcileDrawingPresentation)}return done; +} +function openTipMergePlan(b,ai,oi,otherSide='end',enteredOtherTipCell=false){ + const st=metaState(b.id);if(ai===oi||!Number.isInteger(ai)||!Number.isInteger(oi))return null; + const a=st.paths[ai],o=st.paths[oi]; + if(!a?.cells?.length||!o?.cells?.length||a.endGate!=null||o.endGate!=null||otherSide==='start'&&!o.detachedStart||!a.detachedStart&&!o.detachedStart&&a.startGate===o.startGate)return null; + const orientedOther=otherSide==='start'?[...o.cells].reverse():o.cells,al=a.cells[a.cells.length-1],ol=orientedOther[orientedOther.length-1],tipDistance=manhattan(al,ol); + if(tipDistance!==0&&!(enteredOtherTipCell&&tipDistance===1))return null; + const otherTail=[...orientedOther].reverse().map(cell=>[cell[0],cell[1]]);if(tipDistance===0)otherTail.shift(); + const joined=[...a.cells.map(cell=>[cell[0],cell[1]]),...otherTail],seen=new Set(); + let cells=joined,merged; + for(const cell of cells){const key=ckey(...cell);if(seen.has(key)||!cellSet(b.p).has(key))return null;seen.add(key)} + const aColor=a.startColorIndex??a.colorIndex,oStartColor=o.startColorIndex??o.colorIndex,oEndColor=o.endColorIndex??oStartColor,oRemainingColor=otherSide==='start'?oEndColor:oStartColor; + if(!a.detachedStart&&!o.detachedStart)merged={startGate:a.startGate,endGate:o.startGate,openGate:null,cells,colorIndex:aColor,startColorIndex:aColor,endColorIndex:oStartColor}; + else if(!a.detachedStart&&o.detachedStart)merged={startGate:a.startGate,endGate:null,openGate:null,detachedStart:false,cells,colorIndex:aColor,startColorIndex:aColor,endColorIndex:oRemainingColor}; + else if(a.detachedStart&&!o.detachedStart){cells=[...joined].reverse();merged={startGate:o.startGate,endGate:null,openGate:null,detachedStart:false,cells,colorIndex:oStartColor,startColorIndex:oStartColor,endColorIndex:aColor}} + else merged={startGate:a.startGate,endGate:null,openGate:null,detachedStart:true,cells,colorIndex:aColor,startColorIndex:aColor,endColorIndex:oRemainingColor}; + return{a,o,lo:Math.min(ai,oi),hi:Math.max(ai,oi),merged}; +} +function joinTips(b,ai,oi,otherSide='end',enteredOtherTipCell=false){ + if(b?.joiningPaths)return false;const plan=openTipMergePlan(b,ai,oi,otherSide,enteredOtherTipCell);if(!plan)return false; + const st=metaState(b.id),pointerId=b.drawing?.pointerId;b.joiningPaths=true;cancelBoardDragFrame(b); + let done=false; + try{done=applyBoardCommand(b,()=>{ + if(st.paths[ai]!==plan.a||st.paths[oi]!==plan.o)return false; + st.paths.splice(plan.hi,1);st.paths.splice(plan.lo,1);st.paths.push(plan.merged);b.drawing=null; + },{persist:true,solve:true,deferSettlement:true})}finally{b.joiningPaths=false} + if(done){if(pointerId!=null)safeRelease(b.svg,pointerId);playSound('gate');queueMicrotask(reconcileDrawingPresentation)} + return done; +} +function pathAxisAtCell(path,p,cell){ + const index=path?.cells?.findIndex(candidate=>sameCell(candidate,cell))??-1;if(index<0)return null; + const previous=index?path.cells[index-1]:path.detachedStart?null:outsidePoint(gateObj(p,path.startGate)),next=index{path.cells.push(cell);path.openGate=null},{paint:!deferRender,persist:!deferRender,invalidate:!deferRender}); + if(segmentOccupancy)segmentOccupancy.set(key,pi);b.drawing?.pathCellIndex?.set(key,path.cells.length-1);playSound('gate');return true; +} +function extendOne(b,cell,deferRender=false,segmentOccupancy=null,suppressMotion=false){ + const activeBefore=activePath(b),lastBefore=activeBefore?.cells?.[activeBefore.cells.length-1]; + if(b.drawing?.warpDirection&&lastBefore){const dr=cell[0]-lastBefore[0],dc=cell[1]-lastBefore[1];if(dr!==b.drawing.warpDirection[0]||dc!==b.drawing.warpDirection[1])return false;b.drawing.warpDirection=null} + + let st=metaState(b.id),path=activePath(b),key=ckey(...cell),occupancyMap=segmentOccupancy||b.drawing?.segmentOccupancy||occupiedMap(st); + if(!path||!cellSet(b.p).has(key))return false; + const pi=b.drawing.pathIndex,last=path.cells[path.cells.length-1]; + if(sameCell(cell,last))return true; + if(!pathCellsAdjacent(b.p,cell,last))return false; + const occ=occupancyMap,isCrossing=b.crossingKeySet.has(key),owners=isCrossing?pathIndexesAtCell(st,key):null,currentOwner=isCrossing?(owners.includes(pi)?pi:occ.get(key)):occ.get(key),owner=isCrossing?owners.find(index=>index!==pi):(currentOwner!==pi?currentOwner:null); + if(currentOwner===pi){ + let rewind=b.drawing?.pathCellIndex?.get(key)??path.cells.findIndex(candidate=>sameCell(candidate,cell)); + if(rewind<0)return false; + const pair=warpPairForCell(b.p,path.cells[rewind]),pairIndex=pair?(b.drawing?.pathCellIndex?.get(ckey(...pair))??path.cells.findIndex(candidate=>sameCell(candidate,pair))):-1; + if(pairIndex===rewind+1)rewind--; + if(rewind<0||path.detachedStart&&rewind===0)return false; + const removed=path.cells.slice(rewind+1),removedWarp=removed.some((candidate,index)=>index&&isWarpTransition(b.p,removed[index-1],candidate)); + if(removedWarp&&b.drawing?.pointerId!=null)b.warpedDuringExtend=true; + const result=applyBoardCommand(b,()=>{ + path.cells.splice(rewind+1); + if(path.openGate!=null&&!sameCell(path.cells[path.cells.length-1],[b.p.g[path.openGate][0],b.p.g[path.openGate][1]]))path.openGate=null; + },{paint:!deferRender,persist:!deferRender,invalidate:!deferRender}); + if(result&&occupancyMap)for(const removedCell of removed){ + const removedKey=ckey(...removedCell),remaining=b.crossingKeySet.has(removedKey)?pathIndexesAtCell(st,removedKey).find(index=>index!==pi):null; + if(remaining==null)occupancyMap.delete(removedKey);else occupancyMap.set(removedKey,remaining); + b.drawing?.pathCellIndex?.delete(removedKey); + } + return Boolean(result); + } + const exitDirection=crossingExitDirection(st,b.p,pi); + if(exitDirection){const direction=[cell[0]-last[0],cell[1]-last[1]];if(direction[0]!==exitDirection[0]||direction[1]!==exitDirection[1])return false} + if(owner!=null){ + if(activateCrossing(b,cell,owner,last,deferRender,segmentOccupancy,suppressMotion))return true; + const other=st.paths[owner]; + if(other?.endGate==null){ + const otherSide=other.detachedStart&&sameCell(other.cells[0],cell)?'start':sameCell(other.cells[other.cells.length-1],cell)?'end':null; + if(otherSide)return joinTips(b,pi,owner,otherSide,true); + } + return false; + } + const lock=lockForDoor(b.p,cell); + if(lock&&!pathHasLockKey(path,lock))return false; + const warpExit=warpPairForCell(b.p,cell); + if(warpExit){ + const exitKey=ckey(...warpExit),exitOwner=occ.get(exitKey);if(exitOwner!=null&&exitOwner!==pi||path.cells.some(candidate=>sameCell(candidate,warpExit)))return false; + const pointerId=b.drawing?.pointerId; + const beforeWarp=path.cells[path.cells.length-1],warpDirection=[cell[0]-beforeWarp[0],cell[1]-beforeWarp[1]]; + applyBoardCommand(b,()=>{path.cells.push(cell,[...warpExit]);path.openGate=null},{paint:!deferRender,persist:!deferRender,invalidate:!deferRender}); + if(segmentOccupancy){segmentOccupancy.set(key,pi);segmentOccupancy.set(exitKey,pi)}if(b.drawing?.pathCellIndex){b.drawing.pathCellIndex.set(key,path.cells.length-2);b.drawing.pathCellIndex.set(exitKey,path.cells.length-1)} + if(pointerId!=null)b.warpedDuringExtend=true;if(b.drawing)b.drawing.warpDirection=warpDirection + const gi=gateAtCell(b.p,warpExit,path.detachedStart?null:path.startGate);if(gi!=null&&!usedGateSet(st,pi).has(gi))return finalizeAtGate(b,gi); + return true; + } + applyBoardCommand(b,()=>{path.cells.push(cell);path.openGate=null},{paint:!deferRender,persist:!deferRender,invalidate:!deferRender}); + if(segmentOccupancy)segmentOccupancy.set(key,pi);b.drawing?.pathCellIndex?.set(key,path.cells.length-1); + if(!suppressMotion){flashConfirmedCell(b,cell);playConfirmTone(path.cells.length)} + const gi=gateAtCell(b.p,cell,path.detachedStart?null:path.startGate); + if(gi!=null&&!usedGateSet(st,pi).has(gi))return finalizeAtGate(b,gi); + return true; +} +function extendToward(b,target){ + const path=activePath(b);if(!path||!target)return false; + const last=path.cells[path.cells.length-1]; + if(sameCell(last,target))return true; + // A direct cell click may only advance to a neighboring cell. Long pointer + // movement is handled by segment traversal below, never by a guessed route. + if(manhattan(last,target)!==1)return false; + return extendOne(b,target); +} +function pathTailAxis(path,p){ + if(!path?.cells?.length)return null; + const last=path.cells[path.cells.length-1],previous=path.cells.length>1?path.cells[path.cells.length-2]:path.detachedStart?null:outsidePoint(gateObj(p,path.startGate));if(!previous)return null; + if(isWarpTransition(p,previous,last))return null; + return previous[0]===last[0]?'H':previous[1]===last[1]?'V':null; +} +function cellsCrossedBySegment(from,to,{preferredAxis=null,validCells=null}={}){ + const x0=(from[0]-PAD)/CELL,y0=(from[1]-PAD)/CELL,x1=(to[0]-PAD)/CELL,y1=(to[1]-PAD)/CELL; + let c=Math.floor(x0),r=Math.floor(y0); + const endC=Math.floor(x1),endR=Math.floor(y1),dc=x1-x0,dr=y1-y0,sx=Math.sign(dc),sy=Math.sign(dr); + const deltaC=sx?1/Math.abs(dc):Infinity,deltaR=sy?1/Math.abs(dr):Infinity; + let maxC=sx>0?(c+1-x0)/Math.abs(dc):sx<0?(x0-c)/Math.abs(dc):Infinity, + maxR=sy>0?(r+1-y0)/Math.abs(dr):sy<0?(y0-r)/Math.abs(dr):Infinity; + const cells=[],limit=600; + for(let guard=0;guard=Math.abs(dr)); + if(chooseHorizontal){c+=sx;maxC+=deltaC}else{r+=sy;maxR+=deltaR} + }else if(maxC=0&&c>=0&&rsameCell(cell,target));if(index<0||index===path.cells.length-1)return false; + const pair=warpPairForCell(b.p,path.cells[index]),pairIndex=pair?(drawing?.pathCellIndex?.get(ckey(...pair))??path.cells.findIndex(cell=>sameCell(cell,pair))):-1; + if(pairIndex===index+1)index--;if(index<0||path.detachedStart&&index===0)return false; + const removed=path.cells.slice(index+1),removedWarp=removed.some((cell,removedIndex)=>removedIndex&&isWarpTransition(b.p,removed[removedIndex-1],cell)); + const result=applyBoardCommand(b,()=>{path.cells.splice(index+1);if(path.openGate!=null&&!sameCell(path.cells[path.cells.length-1],[b.p.g[path.openGate][0],b.p.g[path.openGate][1]]))path.openGate=null},{paint:false,persist:false,invalidate:false}); + if(!result)return false;if(removedWarp&&drawing?.pointerId!=null)b.warpedDuringExtend=true; + for(const removedCell of removed){const removedKey=ckey(...removedCell),remaining=b.crossingKeySet.has(removedKey)?pathIndexesAtCell(metaState(b.id),removedKey).find(owner=>owner!==drawing.pathIndex):null;if(remaining==null)segmentOccupancy?.delete(removedKey);else segmentOccupancy?.set(removedKey,remaining);drawing?.pathCellIndex?.delete(removedKey)} + return true; +} +function removeDetachedPathAtOwnEndpoint(b,point,pixels=24){ + const drawing=b?.drawing,st=drawing&&metaState(b.id),path=st?.paths?.[drawing.pathIndex]; + if(!point||!path?.detachedStart||path.endGate!=null||path.cells.length<2)return false; + const fixed=boardCellCenter(path.cells[0]),radius=Math.min(pointerLocalRadius(b,pixels),CELL*.6); + if((fixed[0]-point[0])**2+(fixed[1]-point[1])**2>radius*radius)return false; + const pointerId=drawing.pointerId,index=drawing.pathIndex;cancelBoardDragFrame(b); + const done=applyBoardCommand(b,()=>{if(st.paths[index]!==path)return false;st.paths.splice(index,1);b.drawing=null;b.armedGate=null},{persist:true,solve:true,deferSettlement:true}); + if(done){if(pointerId!=null)safeRelease(b.svg,pointerId);playSound('reset');queueMicrotask(reconcileDrawingPresentation)} + return Boolean(done); +} +function extendPointerTo(b,rawPoint,deferFrameRender=false,catchupLimit=DRAG_MAX_CATCHUP_CELLS){ + const drawing=b.drawing,path=activePath(b);if(!drawing||!path||!rawPoint)return; + const pathIndex=drawing.pathIndex;b.specialCrossTriggered=false;b.warpedDuringExtend=false; + const mappedPoint=pointerPointForDrawing(drawing,rawPoint);updateDrawingPointerState(b,drawing,mappedPoint); + const point=mappedPoint,targetCell=gridCellAt(b,point),candidate=drawing.currentCandidateCell,probeStep=6,probeKey=`${drawing.currentConfirmedCell?.join(',')||''}|${targetCell?.join(',')||''}|${candidate?.join(',')||''}|${Math.round(point[0]/probeStep)},${Math.round(point[1]/probeStep)}`; + if(drawing.lastModelProbeKey===probeKey&&!drawing.catchupPending)return;drawing.lastModelProbeKey=probeKey;drawing.catchupPending=false; + const from=boardCellCenter(path.cells[path.cells.length-1]),segmentOccupancy=drawing.segmentOccupancy||(drawing.segmentOccupancy=occupiedMap(metaState(b.id)));let changedPath=false,blocked=false,feedbackCell=null,feedbackLength=0; + const emitBatchedFeedback=()=>{if(!feedbackCell)return;const now=perfNow();if(now-(b.lastDragToneAt||0)>=90){b.lastDragToneAt=now;playConfirmTone(feedbackLength)}if(uiSettings.lightweightRendering||autoReducedEffects||now-(b.lastDragFeedbackAt||0)<180)return;b.lastDragFeedbackAt=now;flashConfirmedCell(b,feedbackCell)}; + updateGateSnapArming(b,drawing); + if(removeDetachedPathAtOwnEndpoint(b,point)){queueSelectedProgress(b);return} + const overlap=overlappingOpenTipAtPoint(b,point); + if(overlap&&joinTips(b,pathIndex,overlap.index,overlap.side)){queueSelectedProgress(b);return} + if(targetCell&&(drawing.pathCellIndex?.has(ckey(...targetCell))||path.cells.some(cell=>sameCell(cell,targetCell))))changedPath=rewindActivePathToCell(b,targetCell,segmentOccupancy)||changedPath; + else{ + const crossed=cellsCrossedBySegment(from,point,{preferredAxis:pathTailAxis(activePath(b),b.p),validCells:cellSet(b.p)}), + batch=Number.isFinite(catchupLimit)?crossed.slice(0,Math.max(1,catchupLimit)):crossed; + let catchupHalted=false; + for(const cell of batch){ + const key=ckey(...cell),active=activePath(b);if(!active){catchupHalted=true;break} + if(!cellSet(b.p).has(key)){blocked=true;continue} + if(blocked){if(active.cells.some(existing=>sameCell(existing,cell)))changedPath=rewindActivePathToCell(b,cell,segmentOccupancy)||changedPath;continue} + if(!extendOne(b,cell,true,segmentOccupancy,true)){catchupHalted=true;break} + changedPath=true;const updated=activePath(b)||active,tip=updated?.cells?.[updated.cells.length-1];feedbackCell=tip?[...tip]:[...cell];feedbackLength=updated?.cells?.length||1;if(b.specialCrossTriggered||b.warpedDuringExtend)break; + } + if(b.drawing&&crossed.length>batch.length&&!catchupHalted&&!blocked&&!b.specialCrossTriggered&&!b.warpedDuringExtend){b.drawing.catchupPending=true;perfCount('pickupCatchupBatches');perfGauge('pickupCatchupCellsRemaining',crossed.length-batch.length)} + } + if(b.warpedDuringExtend&&b.drawing){ + const active=activePath(b),tip=active?.cells?.[active.cells.length-1]; + if(tip){const exitPoint=boardCellCenter(tip);b.drawing.pointerOffset=[exitPoint[0]-rawPoint[0],exitPoint[1]-rawPoint[1]];b.drawing.lastPoint=exitPoint;syncDrawingConfirmedState(b,b.drawing)} + if(changedPath){b.dragSpecialRevision++;if(b.drawing)b.drawing.logicalRevision=(b.drawing.logicalRevision||0)+1} + emitBatchedFeedback(); + if(!deferFrameRender||b.drawing?.pointerId==null){if(b.drawing?.pointerId!=null)renderDragFrame(b);else renderBoard(b)}if(changedPath)queueSelectedProgress(b);return; + } + if(b.drawing){ + updateGateSnapArming(b,drawing); + const active=activePath(b),tip=active?.cells?.[active.cells.length-1]||null;let snappedGate=gateFromPointOrCell(b,mappedPoint); + if(active&&tip){const tipGate=gateFromCell(b,tip,mappedPoint,active.detachedStart?null:active.startGate,20);if(Number.isInteger(tipGate))snappedGate=tipGate} + if(active&&!active.detachedStart&&snappedGate===active.startGate&&active.openGate!==snappedGate)snappedGate=null; + if(active&&snappedGate===drawing.originGate&&!drawing.leftOriginGateCell)snappedGate=null; + if(active&&Number.isInteger(snappedGate)){ + const gateCell=[b.p.g[snappedGate][0],b.p.g[snappedGate][1]],currentTip=active.cells[active.cells.length-1]; + if(sameCell(gateCell,currentTip)){const snap=boardPointForGate(b,snappedGate).point;b.drawing.lastPoint=snap;finalizeAtGate(b,snappedGate)} + else b.drawing.lastPoint=point; + }else b.drawing.lastPoint=point; + if(b.drawing){syncDrawingConfirmedState(b,b.drawing);updateDrawingPointerState(b,b.drawing,mappedPoint)} + } + if(changedPath){b.dragSpecialRevision++;if(b.drawing)b.drawing.logicalRevision=(b.drawing.logicalRevision||0)+1} + emitBatchedFeedback(); + if(!deferFrameRender||b.drawing?.pointerId==null){if(b.drawing?.pointerId!=null)renderDragFrame(b);else renderBoard(b)}if(changedPath)queueSelectedProgress(b) +} + + +function reopenConnectedFromGate(b,pi,gi,pointerId=null){ + const st=metaState(b.id),path=st.paths[pi]; + if(!path||path.endGate==null)return false; + if(gi!==path.endGate&&gi!==path.startGate)return false; + const openedColor=canonicalGateColorIndex(b.meta,gi,path); + return applyBoardCommand(b,()=>{ + if(gi===path.endGate){ + path.endColorIndex=openedColor;path.endGate=null;path.openGate=gi; + }else{ + const oldStart=path.startGate,oldEnd=path.endGate,oldEndColor=canonicalGateColorIndex(b.meta,oldEnd,path); + path.cells=[...path.cells].reverse();path.startGate=oldEnd;path.endGate=null;path.openGate=oldStart; + path.startColorIndex=oldEndColor;path.colorIndex=oldEndColor;path.endColorIndex=openedColor; + } + b.armedGate=gi;b.drawing=drawingFromGate(b,pi,gi,pointerId); + },{persist:pointerId==null,paint:pointerId==null}); +} +function reopenPathAtCell(b,pi,cell,pointerId=null){ + const st=metaState(b.id),path=st.paths[pi]; + if(!path)return false; + const idx=path.cells.findIndex(c=>sameCell(c,cell)); + if(idx<0)return false; + return applyBoardCommand(b,()=>{ + if(path.endGate==null)path.cells.splice(idx+1); + else{const n=path.cells.length,distFromStart=idx,distFromEnd=n-1-idx;if(distFromEnd<=distFromStart){path.cells.splice(idx+1);path.endGate=null;path.endColorIndex=null}else{const oldEnd=path.endGate,oldEndColor=path.endColorIndex,reversed=[...path.cells].reverse(),newIdx=n-1-idx;path.cells=reversed.slice(0,newIdx+1);path.startGate=oldEnd;path.endGate=null;path.startColorIndex=oldEndColor??path.colorIndex;path.colorIndex=path.startColorIndex;path.endColorIndex=null}} + b.drawing=drawingForPath(b,pi,pointerId,null); + },{persist:pointerId==null,paint:pointerId==null}); +} +function reverseDetachedPath(path){ + if(!path?.detachedStart)return false; + path.cells.reverse();const startColor=path.startColorIndex??path.colorIndex??0,endColor=path.endColorIndex??startColor; + path.startColorIndex=endColor;path.colorIndex=endColor;path.endColorIndex=startColor;path.openGate=null;return true; +} +function detachPathFromStartGate(b,pi,gi,pointerId=null){ + const path=metaState(b.id).paths[pi];if(!path||path.detachedStart||path.endGate!=null||path.startGate!==gi||path.cells.length<2)return false; + return applyBoardCommand(b,()=>{ + path.cells.reverse();const startColor=path.startColorIndex??path.colorIndex??0,endColor=path.endColorIndex??startColor; + path.startColorIndex=endColor;path.colorIndex=endColor;path.endColorIndex=startColor;path.detachedStart=true;path.openGate=gi; + b.armedGate=gi;b.drawing=drawingFromGate(b,pi,gi,pointerId); + },{persist:pointerId==null,paint:pointerId==null}); +} +function orientDetachedEndpoint(b,pi,side,pointerId=null,point=null){ + const path=metaState(b.id).paths[pi];if(!path?.detachedStart)return false; + if(side==='start')applyBoardCommand(b,()=>reverseDetachedPath(path),{persist:pointerId==null,paint:pointerId==null}); + b.armedGate=null;b.drawing=drawingForPath(b,pi,pointerId,point);return true; +} +function startGate(b,gi,pointerId=null){ + const st=metaState(b.id);playSound('grab'); + if(st.solved||!Number.isInteger(gi)||!gateConnectionAllowed(b.meta,gi))return; + + const existing=st.paths.findIndex(path=>pathUsesGate(path,gi)); + if(existing>=0){ + const path=st.paths[existing],gateColor=canonicalGateColorIndex(b.meta,gi,path);setPathColorAtGate(path,gi,gateColor); + if(path.endGate!=null){if(reopenConnectedFromGate(b,existing,gi,pointerId)&&pointerId==null)renderBoardNow(b);return} + if(!path.detachedStart&&path.startGate===gi&&path.cells.length>1){if(detachPathFromStartGate(b,existing,gi,pointerId)&&pointerId==null)renderBoardNow(b);return} + if(path.openGate===gi){ + path.endColorIndex=canonicalGateColorIndex(b.meta,gi,path);b.armedGate=gi;b.drawing=drawingFromGate(b,existing,gi,pointerId);if(pointerId==null)renderBoardNow(b); + }else if(path.cells.length===1){b.drawing=drawingFromGate(b,existing,gi,pointerId);if(pointerId==null)renderBoardNow(b)} + else toast('\u7dda\u306e\u5148\u7aef\u3092\u3064\u304b\u3093\u3067\u304f\u3060\u3055\u3044\u3002'); + return; + } + + const g=gateObj(b.p,gi),occ=occupiedMap(st); + if(occ.has(ckey(...g.cell))){renderBoard(b);return} + + const colorIndex=canonicalGateColorIndex(b.meta,gi,null); + const started=applyBoardCommand(b,()=>{ + st.paths.push({startGate:gi,endGate:null,openGate:null,cells:[[g.cell[0],g.cell[1]]],colorIndex,startColorIndex:colorIndex,endColorIndex:null}); + b.drawing=drawingFromGate(b,st.paths.length-1,gi,pointerId); + b.drawing.createdPath=pointerId!=null; + },{persist:pointerId==null,paint:pointerId==null}); + if(started&&pointerId==null)renderBoardNow(b); +} +function safeCapture(el,id){try{el.setPointerCapture(id)}catch(_){}finally{refreshInteractionState()}} +function safeRelease(el,id){try{if(el.hasPointerCapture?.(id))el.releasePointerCapture(id)}catch(_){}finally{refreshInteractionState();scheduleInteractionSettlePresentation()}} +function eventToSvg(b,e){ + const r=boardScreenRect(b),vb=b.svg.viewBox.baseVal; + if(!r.width||!r.height)return null; + return[(e.clientX-r.left)*vb.width/r.width,(e.clientY-r.top)*vb.height/r.height] +} +function cellAt(b,pt){if(!pt)return null;const c=Math.floor((pt[0]-PAD)/CELL),r=Math.floor((pt[1]-PAD)/CELL);return cellSet(b.p).has(ckey(r,c))?[r,c]:null} +function setActiveBoard(id){const previous=rendered.get(activeBoard);let endedDrawing=false;activeBoard=id;if(previous&&previous.id!==id&&previous.drawing?.pointerId==null){endedDrawing=Boolean(previous.drawing);previous.drawing=null;previous.armedGate=null;if(endedDrawing)renderBoard(previous)}if(endedDrawing)queueMicrotask(reconcileDrawingPresentation);return previous} +function selectBoard(b,{paint=true}={}){ + if(!b)return; + const alreadySelected=activeBoard===b.id&&hudBoardId===b.id&&data.selectedBoardId===b.id; + const previousHud=rendered.get(hudBoardId);setActiveBoard(b.id);hudBoardId=b.id;data.selectedBoardId=b.id;markGlobalDirty(false); + if(previousHud&&previousHud!==b){previousHud.card.classList.remove('hud-current');if(paint)renderBoard(previousHud)} + b.card.classList.add('hud-current');if(!alreadySelected&&paint)renderBoard(b);updateSelectedProgress(b) +} +function metasAreAdjacent(a,b){ + if(!a?.chunks||!b?.chunks||a.id===b.id)return false; + const cells=new Set(b.chunks.map(([dx,dy])=>key2(b.x+dx,b.y+dy))); + for(const[dx,dy]of a.chunks){ + const x=a.x+dx,y=a.y+dy; + if(cells.has(key2(x+1,y))||cells.has(key2(x-1,y))||cells.has(key2(x,y+1))||cells.has(key2(x,y-1)))return true; + } + return false; +} +function shouldTeleportToUnsolvedBoard(_meta){return false} +function pointerLocalRadius(_board,pixels=50){return pixels/Math.max(MIN_CAMERA_SCALE,cam.scale)} +function pointerFacesGate(b,index,point){ + const gate=gateObj(b.p,index),center=boardCellCenter(gate.cell),[dr,dc]=SIDE_D[gate.side]; + return(point[0]-center[0])*dc+(point[1]-center[1])*dr>=CELL*.06; +} +function gateCandidatesInCell(b,cell,{exclude=null,interactiveOnly=true}={}){ + if(!cell)return[]; + const candidates=[],indexes=b.gateIndexesByCell?(b.gateIndexesByCell.get(ckey(...cell))||[]):b.p.g.keys(); + for(const index of indexes){ + const gate=b.p.g[index]; + if(index===exclude||gate[0]!==cell[0]||gate[1]!==cell[1])continue; + if(interactiveOnly&&!gateConnectionAllowed(b.meta,index))continue; + candidates.push(index); + } + return candidates; +} +function gateCandidateAtPoint(b,point,{cellHint=null,directGate=null,exclude=null,interactiveOnly=true,maxPixels=null,requireFacing=false}={}){ + if(!point)return Number.isInteger(directGate)?directGate:null; + const cell=cellHint||cellAt(b,point),inCell=gateCandidatesInCell(b,cell,{exclude,interactiveOnly}); + if(Number.isInteger(directGate)&&directGate!==exclude&&b.p.g[directGate]&&(!interactiveOnly||gateConnectionAllowed(b.meta,directGate)))return directGate; + let candidates=inCell; + if(!candidates.length){ + if(maxPixels===0)return null; + const radius=pointerLocalRadius(b,Number.isFinite(maxPixels)?maxPixels:34),limit=radius*radius; + candidates=[]; + for(let index=0;index{if(!cell)return;const key=ckey(...cell),entries=indexByCell.get(key)||[];entries.push({index,side,cell});indexByCell.set(key,entries)}; + st.paths.forEach((path,index)=>{if(path?.endGate!=null||!path?.cells?.length)return;add(path.cells[path.cells.length-1],index,'end');if(path.detachedStart&&path.cells.length>1)add(path.cells[0],index,'start')}); + b.endpointIndexesByCell=indexByCell;return indexByCell; +} +function endpointCandidatesNearPoint(b,point){ + const cell=gridCellAt(b,point);if(!cell)return[]; + const index=b.endpointIndexesByCell?.size?b.endpointIndexesByCell:rebuildEndpointIndexes(b),candidates=[]; + for(let dr=-1;dr<=1;dr++)for(let dc=-1;dc<=1;dc++)for(const entry of index.get(ckey(cell[0]+dr,cell[1]+dc))||[])candidates.push(entry); + return candidates; +} +function nearestEndpointAtPoint(b,point){ + if(!point)return null;const radius=Math.min(pointerLocalRadius(b,30),CELL*.48),limit=radius*radius; + let best=null,bestD=limit; + for(const entry of endpointCandidatesNearPoint(b,point)){const tip=boardCellCenter(entry.cell),d=(tip[0]-point[0])**2+(tip[1]-point[1])**2;if(dvaluemax-margin?1-(max-value)/margin:0; + return[axis(clientX,rect.left,rect.right)*DRAG_EDGE_MAX_SPEED,axis(clientY,rect.top,rect.bottom)*DRAG_EDGE_MAX_SPEED]; +} +function pointerInsideBoardScreen(b,move){const rect=boardScreenRect(b);return Boolean(move&&rect.width&&rect.height&&move.clientX>=rect.left&&move.clientX<=rect.right&&move.clientY>=rect.top&&move.clientY<=rect.bottom)} +function hidePickupHandleOverlay(){pickupHandleOverlay?.classList.remove('visible')} +function updatePickupHandleOverlay(b,drawing,clientX,clientY){ + if(!pickupHandleOverlay||!drawing||!Number.isFinite(clientX)||!Number.isFinite(clientY))return false; + const path=activePath(b),color=LINE_COLORS[(path?.endColorIndex??path?.startColorIndex??path?.colorIndex??0)%LINE_COLORS.length],transform=`translate3d(${clientX}px,${clientY}px,0) translate(-50%,-50%)`; + if(pickupHandleOverlay.style.transform!==transform)pickupHandleOverlay.style.transform=transform; + if(pickupHandleOverlay.style.getPropertyValue('--pickup-color')!==color)pickupHandleOverlay.style.setProperty('--pickup-color',color); + pickupHandleOverlay.classList.add('visible');return true; +} +function usesLightweightDragOverlay(b){return(b?.p?.valid?.length||0)>=LIGHTWEIGHT_DRAG_BOARD_CELLS} +function setLightweightDragPresentation(b,active){ + if(!b||!usesLightweightDragOverlay(b))return false;const next=Boolean(active);if(b.lightweightDragPresentation===next)return false;b.lightweightDragPresentation=next; + for(const layer of[b.connectorLayer,b.pathLayer,b.specialLayer,b.gateLayer,b.numberLayer])if(layer)layer.style.opacity=next?'0':''; + return true; +} +function setPickupScenePresentation(activeBoard=null,active=false){ + const next=Boolean(active); + for(const board of rendered.values())setLightweightDragPresentation(board,next&&board===activeBoard); +} +function recordPickupStartFrame(b,drawing){ + if(!drawing||drawing.pickupStartMeasured)return false;drawing.pickupStartMeasured=true; + perfGauge('pickupStartFullBoardRenders',Math.max(0,(perfCounters.fullBoardRenders||0)-(drawing.fullBoardRendersAtPointerDown||0))); + if(Number.isFinite(drawing.pointerDownAt))perfObserve('pickupStartLatency',Math.max(0,perfNow()-drawing.pointerDownAt));return true; +} +function cancelBoardDragFrame(b){if(!b)return;if(b.pointerMoveFrame)cancelAnimationFrame(b.pointerMoveFrame);if(b.pointerMoveDelayTimer)clearTimeout(b.pointerMoveDelayTimer);b.pointerMoveTaskController?.abort();b.pointerMoveFrame=0;b.pointerMoveDelayTimer=0;b.pointerMoveTaskController=null;b.pendingPointerMove=null;b.pointerMoveSamples=[];b.lastProcessedPointerMove=null;b.dragFrameAt=0;b.dragNextFrameAt=0;b.dragVisualActiveUntil=0;b.dragVisualX=NaN;b.dragVisualY=NaN;b.dragVisualLastFrameAt=0;b.dragVisualCommitAt=0;b.dragSvgVisualAt=0;b.dragTrailRevision=-1;b.dragInputRevision=0;b.dragVisualCommittedInputRevision=0;b.dragLogicalCommittedInputRevision=0;hidePickupHandleOverlay()} +function scheduleBoardDragFrame(b){ + if(!b||b.pointerMoveFrame||b.pointerMoveDelayTimer||b.pointerMoveTaskController)return; + let committed=false; + const step=timestamp=>{ + if(committed)return;committed=true; + if(b.dragVisualLastFrameAt&×tamp+INTERACTION_FRAME_TOLERANCE_MS{ + b.pointerMoveDelayTimer=0; + b.pointerMoveFrame=requestAnimationFrame(step); + const watchdog=()=>{perfCount('pickupDisplayWatchdogFrames');step(perfNow())}; + if(globalThis.scheduler?.postTask){const controller=new AbortController();b.pointerMoveTaskController=controller;scheduler.postTask(watchdog,{priority:'user-blocking',delay:DRAG_DISPLAY_WATCHDOG_MS,signal:controller.signal}).catch(()=>{})} + else b.pointerMoveDelayTimer=setTimeout(watchdog,DRAG_DISPLAY_WATCHDOG_MS); + }; + arm(); +} +function pointerEventSamples(event){ + let sample=event;if(event?.pointerType!=='mouse'&&typeof event?.getCoalescedEvents==='function'){const coalesced=event.getCoalescedEvents();if(coalesced?.length)sample=coalesced[coalesced.length-1]} + return sample&&Number.isFinite(sample.clientX)&&Number.isFinite(sample.clientY)?{pointerId:event.pointerId,clientX:sample.clientX,clientY:sample.clientY,inputAt:Number(sample.timeStamp)||Number(event.timeStamp)||perfNow()}:null; +} +function trimBoardPointerSamples(samples){ + while(samples.length>DRAG_MAX_POINTER_SAMPLES){ + let removeIndex=1,leastTurn=Infinity; + for(let index=1;index=b.dragNextFrameAt, + freshVisualInput=b.dragVisualCommittedInputRevision!==b.dragInputRevision, + freshLogicalInput=b.dragLogicalCommittedInputRevision!==b.dragInputRevision; + if(freshVisualInput){recordInteractionCommit('pickupVisual',timestamp,move.inputAt,true);b.dragVisualCommitAt=timestamp;b.dragVisualCommittedInputRevision=b.dragInputRevision} + else if(timestamp<=b.dragVisualActiveUntil)recordInteractionCommit('pickupVisual',timestamp,move.inputAt,false); + b.lastProcessedPointerMove=move; + if(logicalSlotDue){let nextFrame=b.dragNextFrameAt||timestamp;do nextFrame+=DRAG_FRAME_INTERVAL;while(nextFrame<=timestamp+2);b.dragNextFrameAt=nextFrame} + const visualElapsed=b.dragVisualLastFrameAt?Math.min(50,Math.max(1,timestamp-b.dragVisualLastFrameAt)):16.67,visualBlend=1-Math.exp(-visualElapsed/8); + if(!Number.isFinite(b.dragVisualX)){b.dragVisualX=move.clientX;b.dragVisualY=move.clientY} + else{b.dragVisualX+=(move.clientX-b.dragVisualX)*visualBlend;b.dragVisualY+=(move.clientY-b.dragVisualY)*visualBlend} + b.dragVisualLastFrameAt=timestamp; + touchBoardClaim(b.id);const velocity=edgePanVelocity(move.clientX,move.clientY),dt=Math.min(32,Math.max(8,timestamp-(b.dragFrameAt||timestamp-16)));b.dragFrameAt=timestamp; + if(velocity[0]||velocity[1]){cam.x-=velocity[0]*dt;cam.y-=velocity[1]*dt;applyCamera(true)} + const logicalDue=logicalSlotDue&&(freshLogicalInput||velocity[0]||velocity[1]||b.drawing?.catchupPending||b.pointerMoveSamples?.length); + if(logicalDue){ + const modelStarted=perfStart();let processed=0,logicalMove=move; + do{ + logicalMove=freshLogicalInput&&b.pointerMoveSamples?.length?b.pointerMoveSamples.shift():move;processed++; + const point=pointerInsideBoardScreen(b,logicalMove)?eventToSvg(b,logicalMove):null,traceCell=point?cellAt(b,point):null; + if(traceCell){const trace=b.logicalPointerCellTrace||(b.logicalPointerCellTrace=[]);trace.push(traceCell);if(trace.length>48)trace.shift()} + if(point&&b.drawing?.pointerId===logicalMove.pointerId)extendPointerTo(b,point,true); + }while(freshLogicalInput&&b.pointerMoveSamples?.length&&processed48)trace.shift()}if(point&&b.drawing?.pointerId===move.pointerId)extendPointerTo(b,point,true,index===moves.length-1?Infinity:DRAG_MAX_CATCHUP_CELLS)} + if(b.drawing&&!usesLightweightDragOverlay(b))renderDragFrame(b); +} +function reconnectOpenGateOnRelease(b){ + const drawing=b?.drawing,path=activePath(b),gateIndex=path?.openGate; + if(!drawing||!path||!Number.isInteger(gateIndex)||drawing.leftOriginGateCell)return false; + const gateCell=[b.p.g[gateIndex][0],b.p.g[gateIndex][1]],tip=path.cells[path.cells.length-1]; + if(!sameCell(gateCell,tip))return false; + return finalizeAtGate(b,gateIndex); +} +function discardUnmovedCreatedPath(b,drawing=b?.drawing){ + if(!drawing?.createdPath)return false; + const st=metaState(b.id),path=st.paths[drawing.pathIndex];if(!path||path.endGate!=null||path.cells.length!==1)return false; + return applyBoardCommand(b,()=>{st.paths.splice(drawing.pathIndex,1);b.drawing=null;b.armedGate=null},{persist:true,deferSettlement:true}); +} +function schedulePendingClaimPreview(b){ + if(!b?.pendingClaimPointer||b.pendingClaimFrame)return; + b.pendingClaimFrame=requestAnimationFrame(timestamp=>{ + b.pendingClaimFrame=0;const pending=b.pendingClaimPointer;if(!pending)return;const point=eventToSvg(b,pending);if(!point)return; + pending.preview.setAttribute('transform',`translate(${point[0]} ${point[1]})`);recordInteractionCommit('pickupPreview',timestamp,pending.inputAt);markVisualFrame(timestamp);perfCount('pickupPreviewFrames'); + }); +} +function beginPendingClaimPointer(b,event,targetInfo){ + const started=perfStart(); + if(b.pendingClaimPointer)clearPendingClaimPointer(b,b.pendingClaimPointer.pointerId); + const preview=svgEl('g',{class:'claim-pending-preview'}),halo=svgEl('circle',{r:9,class:'claim-pending-halo'}),knob=svgEl('circle',{r:5.5,class:'claim-pending-knob'}); + preview.append(halo,knob);b.claimPreviewLayer.replaceChildren(preview); + const initialSample=pointerEventSamples(event); + b.pendingClaimPointer={pointerId:event.pointerId,clientX:event.clientX,clientY:event.clientY,inputAt:Number(event.timeStamp)||perfNow(),samples:initialSample?[initialSample]:[],startedAt:perfNow(),fullBoardRendersAtDown:perfCounters.fullBoardRenders||0,preview,...targetInfo}; + try{b.svg.setPointerCapture(event.pointerId)}catch(_){}refreshInteractionState();schedulePendingClaimPreview(b);perfEnd('pickupPointerDownPreview',started);return b.pendingClaimPointer; +} +function updatePendingClaimPointer(b,event){ + const pending=b?.pendingClaimPointer;if(!pending||pending.pointerId!==event.pointerId)return false; + const sample=pointerEventSamples(event);if(sample){pending.clientX=sample.clientX;pending.clientY=sample.clientY;pending.inputAt=sample.inputAt;(pending.samples||(pending.samples=[])).push(sample);trimBoardPointerSamples(pending.samples);schedulePendingClaimPreview(b)}return true; +} +function clearPendingClaimPointer(b,pointerId,{release=true}={}){ + const pending=b?.pendingClaimPointer;if(!pending||pointerId!=null&&pending.pointerId!==pointerId)return null; + if(b.pendingClaimFrame)cancelAnimationFrame(b.pendingClaimFrame);b.pendingClaimFrame=0;b.pendingClaimPointer=null;b.claimPreviewLayer.replaceChildren(); + if(release)try{if(b.svg.hasPointerCapture?.(pending.pointerId))b.svg.releasePointerCapture(pending.pointerId)}catch(_){} + refreshInteractionState();return pending; +} +function activateBoardPointerDrag(b,pending,point){ + const started=perfStart(); + const drawing=b?.drawing;if(!drawing||!pending||!point){perfEnd('pickupPointerDownCommit',started);return false} + cancelBoardDragFrame(b); + b.logicalPointerCellTrace=[];b.lastFlushPointerCells=[]; + drawing.lastPoint=point;updateDrawingPointerState(b,drawing,point);drawing.pointerDownAt=pending.startedAt;drawing.fullBoardRendersAtPointerDown=pending.fullBoardRendersAtDown; + setPickupScenePresentation(b,true); + safeCapture(b.svg,pending.pointerId); + const samples=pending.samples?.length?pending.samples:[{pointerId:pending.pointerId,clientX:pending.clientX,clientY:pending.clientY,inputAt:pending.inputAt}]; + for(const sample of samples)setBoardPointerSample(b,sample); + scheduleBoardDragFrame(b);perfEnd('pickupPointerDownCommit',started);return true; +} +function bindBoard(b){ + b.card.addEventListener('focus',()=>selectBoard(b)); + b.svg.addEventListener('pointerdown',async e=>{ + if(e.button!==0||b.joiningPaths)return; + e.preventDefault(); + e.stopPropagation(); + selectBoard(b,{paint:false}); + let st=metaState(b.id); + if(st.solved)return; + const endpointTarget=e.target.closest?.('.endpoint-hit'),directGate=Number(e.target.closest?.('.gate-hit')?.dataset.gate), + targetEndpoint=Number(endpointTarget?.dataset.pathIndex),targetEndpointSide=endpointTarget?.dataset.endpointSide||'end', + pending=beginPendingClaimPointer(b,e,{directGate,targetEndpoint,targetEndpointSide}); + const claimApproved=await ensureBoardClaimForInput(b); + if(!claimApproved||b.pendingClaimPointer!==pending||!realtimeHeldPointers.has(e.pointerId)){clearPendingClaimPointer(b,e.pointerId);return} + clearPendingClaimPointer(b,e.pointerId,{release:false}); + if(!b.card.isConnected||(st=metaState(b.id)).solved){safeRelease(b.svg,e.pointerId);return}touchBoardClaim(b.id,true); + const point=eventToSvg(b,pending); + if(Number.isInteger(directGate)&&b.p.g[directGate]){ + const directCell=[b.p.g[directGate][0],b.p.g[directGate][1]],occupiedPath=occupiedMap(st).get(ckey(...directCell)),gatePath=st.paths.findIndex(path=>pathUsesGate(path,directGate)); + if(gatePath>=0||occupiedPath==null){armGate(b,directGate);startGate(b,directGate,e.pointerId);if(!activateBoardPointerDrag(b,pending,point)){b.armedGate=null;renderBoard(b)}return} + } + if(Number.isInteger(targetEndpoint)&&st.paths[targetEndpoint]?.endGate==null){ + if(st.paths[targetEndpoint].detachedStart)orientDetachedEndpoint(b,targetEndpoint,targetEndpointSide,e.pointerId,point); + else{b.armedGate=null;b.drawing=drawingForPath(b,targetEndpoint,e.pointerId,point)} + activateBoardPointerDrag(b,pending,point);return; + } + const nearestEndpoint=nearestEndpointAtPoint(b,point); + if(Number.isInteger(nearestEndpoint)&&st.paths[nearestEndpoint]?.endGate==null){ + b.armedGate=null;b.drawing=drawingForPath(b,nearestEndpoint,e.pointerId,point); + activateBoardPointerDrag(b,pending,point);return; + } + const hintCell=cellAt(b,point), + targetGate=gateStartCandidate(b,point,hintCell,directGate),occupiedPath=hintCell?occupiedMap(st).get(ckey(...hintCell)):null, + gatePath=Number.isInteger(targetGate)?st.paths.findIndex(path=>pathUsesGate(path,targetGate)):-1; + if(Number.isInteger(targetGate)&&(gatePath>=0||occupiedPath==null)){ + armGate(b,targetGate);startGate(b,targetGate,e.pointerId); + if(!activateBoardPointerDrag(b,pending,point)){b.armedGate=null;renderBoard(b)} + return; + } + if(Number.isInteger(occupiedPath)){ + if(reopenPathAtCell(b,occupiedPath,hintCell,e.pointerId)){playSound('grab');activateBoardPointerDrag(b,pending,point);return} + } + if(Number.isInteger(targetGate)){ + armGate(b,targetGate);startGate(b,targetGate,e.pointerId); + if(!activateBoardPointerDrag(b,pending,point)){b.armedGate=null;renderBoard(b)} + return; + } + const cell=cellAt(b,point); + if(!cell)return; + const pi=occupiedMap(st).get(ckey(...cell)); + if(pi!=null){ + if(reopenPathAtCell(b,pi,cell,e.pointerId)){ + activateBoardPointerDrag(b,pending,point);return; + } + } + + }); + b.svg.addEventListener('pointermove',e=>{ + if(updatePendingClaimPointer(b,e)){e.preventDefault();return} + if(!b.drawing||b.drawing.pointerId!==e.pointerId)return; + e.preventDefault();queueBoardPointerMove(b,e); + }); + const finishPointer=(e,flush=true)=>{ + const started=perfStart();try{ + if(b.pendingClaimPointer?.pointerId===e.pointerId){clearPendingClaimPointer(b,e.pointerId);return} + if(!b.drawing||b.drawing.pointerId!==e.pointerId)return; + const release=()=>{clearDragRender(b);safeRelease(b.svg,e.pointerId);queueMicrotask(()=>updateCustomCursorFromPointer(e))}; + if(flush)flushBoardPointerMove(b,e);else cancelBoardDragFrame(b); + if(!b.drawing||b.drawing.pointerId!==e.pointerId){release();return} + if(flush&&reconnectOpenGateOnRelease(b)){release();return} + if(discardUnmovedCreatedPath(b)){release();return} + const finishedPathIndex=b.drawing.pathIndex;b.drawing=null;b.armedGate=null;refreshInteractionState(); + const removedWarpKnobPaths=sanitizeStateForPuzzle(b.meta,{quiet:true}); + changed(b.id);if(removedWarpKnobPaths)toast('両端がワープセルにある未接続の線を削除しました。'); + scheduleBoardCommandSettlement(b,{paint:true,invalidate:true,pathIndex:finishedPathIndex}); + release(); + }finally{perfEnd('pickupPointerFinish',started)} + }; + b.svg.addEventListener('pointerup',e=>finishPointer(e,true)); + b.svg.addEventListener('pointercancel',e=>finishPointer(e,false)); + b.svg.addEventListener('lostpointercapture',e=>{if(b.pendingClaimPointer?.pointerId===e.pointerId)clearPendingClaimPointer(b,e.pointerId,{release:false});else if(b.drawing?.pointerId===e.pointerId)finishPointer(e,false);else refreshInteractionState()}); + b.svg.addEventListener('contextmenu',e=>e.preventDefault()); + b.svg.addEventListener('focusin',()=>selectBoard(b)); + b.svg.addEventListener('keydown',async e=>{ + if(document.querySelector('#modal').classList.contains('show')||document.querySelector('#storeModal').classList.contains('show')||document.querySelector('#inventoryModal').classList.contains('show')||document.querySelector('#timeAttackModal').classList.contains('show'))return; + const st=metaState(b.id);if(st.solved)return; + const gateTarget=e.target.closest?.('.gate-hit'),endpointTarget=e.target.closest?.('.endpoint-hit'),claimKey=e.key==='Enter'||e.key===' '||e.key==='Backspace'||['ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.key); + if(claimKey&&!await ensureBoardClaimForInput(b))return;if(claimKey)touchBoardClaim(b.id); + if((e.key==='Enter'||e.key===' ')&&gateTarget){ + e.preventDefault();selectBoard(b);startGate(b,Number(gateTarget.dataset.gate),null);b.svg.focus({preventScroll:true});return; + } + if((e.key==='Enter'||e.key===' ')&&endpointTarget){ + e.preventDefault();selectBoard(b);const pi=Number(endpointTarget.dataset.pathIndex),side=endpointTarget.dataset.endpointSide||'end'; + if(st.paths[pi]?.endGate==null){if(st.paths[pi].detachedStart)orientDetachedEndpoint(b,pi,side,null,null);else b.drawing=drawingForPath(b,pi,null,null);b.drawing.keyboardActive=true;renderBoard(b);b.svg.focus({preventScroll:true})}return; + } + const moves={ArrowUp:[-1,0],ArrowDown:[1,0],ArrowLeft:[0,-1],ArrowRight:[0,1]}; + if(moves[e.key]&&activePath(b)){ + e.preventDefault();b.drawing.keyboardActive=true;const path=activePath(b),last=path.cells[path.cells.length-1],move=moves[e.key]; + extendOne(b,[last[0]+move[0],last[1]+move[1]]);b.svg.focus({preventScroll:true});return; + } + if(e.key==='Backspace'&&activePath(b)){ + e.preventDefault();b.drawing.keyboardActive=true;const path=activePath(b); + if(path.cells.length>1)extendOne(b,path.cells[path.cells.length-2]); + else toast('\u59cb\u70b9\u306f\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3002'); + } + }); +} +let renderOriginX=0,renderOriginY=0; +let viewportRectCache=null,viewportResizeFrame=0; +function getViewportRect(force=false){if(force||!viewportRectCache)viewportRectCache=viewport.getBoundingClientRect();return viewportRectCache} +function getMinimapRect(force=false){if(force||!minimapRectCache)minimapRectCache=minimapCanvas.getBoundingClientRect();return minimapRectCache} +function boardScreenRect(board){ + const viewportRect=getViewportRect(),scale=cam.scale,left=viewportRect.left+cam.x+((board.meta.x-renderOriginX)*UNIT-PAD)*scale,top=viewportRect.top+cam.y+((board.meta.y-renderOriginY)*UNIT-PAD)*scale; + return{left,top,right:left+board.w*scale,bottom:top+board.h*scale,width:board.w*scale,height:board.h*scale}; +} +function boardWorldBounds(meta){const p=puzzleOf(meta);return{x:(meta.x-renderOriginX)*UNIT-PAD,y:(meta.y-renderOriginY)*UNIT-PAD,w:p.bounds.w*CELL+PAD*2,h:p.bounds.h*CELL+PAD*2}} +function viewportChunkBounds(screenMargin=0){ + const r=getViewportRect(),margin=screenMargin/Math.max(MIN_CAMERA_SCALE,cam.scale),left=-cam.x/cam.scale-margin,top=-cam.y/cam.scale-margin,right=-cam.x/cam.scale+r.width/cam.scale+margin,bottom=-cam.y/cam.scale+r.height/cam.scale+margin; + return{minX:renderOriginX+Math.floor(left/UNIT)-1,maxX:renderOriginX+Math.ceil(right/UNIT)+1,minY:renderOriginY+Math.floor(top/UNIT)-1,maxY:renderOriginY+Math.ceil(bottom/UNIT)+1}; +} +function visibleMetaIdsForBounds(bounds){const ids=new Set();for(let y=bounds.minY;y<=bounds.maxY;y++)for(let x=bounds.minX;x<=bounds.maxX;x++){const id=occupancy.get(key2(x,y));if(id)ids.add(id)}return ids} +const pendingVisibleHydrations=new Set(); +function visibleMetaIds(){ + const ids=visibleMetaIdsForBounds(viewportChunkBounds(24)); + for(const[id,b]of rendered)if(b.drawing?.pointerId!=null)ids.add(id);return ids; +} +function scheduleWorldOverview(markDirty=false){ + if(markDirty)overviewDirty=true; + if(document.body.classList.contains('is-interacting'))return; + if(!overviewCanvas||!inWorldOverview()||!overviewDirty||overviewFrame||overviewDelayTimer)return; + const step=timestamp=>{ + if(!overviewFrame)return; + if(timestamp-overviewLastDraw{overviewDelayTimer=0;if(document.body.classList.contains('is-interacting'))return;drawWorldOverview()}; + overviewDelayTimer=typeof requestIdleCallback==='function'?requestIdleCallback(rebuild,{timeout:180}):setTimeout(rebuild,0); + }; + overviewFrame=requestAnimationFrame(step); +} +function positionCachedWorldOverview(){ + if(!overviewCanvas||!overviewCache||!inWorldOverview())return false; + const rect=getViewportRect(),width=Math.max(1,Math.round(rect.width)),height=Math.max(1,Math.round(rect.height)),[centerX,centerY]=cameraCenterInChunks(), + left=width/2-overviewCache.baseWidth/2-(centerX-overviewCache.anchorX)*overviewCache.unit, + top=height/2-overviewCache.baseHeight/2-(centerY-overviewCache.anchorY)*overviewCache.unit; + overviewCanvas.hidden=false;overviewCanvas.style.transform=`translate3d(${left.toFixed(2)}px,${top.toFixed(2)}px,0)`; + perfCount('overviewTransformUpdates');return true; +} +function rebuildWorldOverviewCache(width,height,dpr,centerX,centerY){ + if(globalThis.document?.body?.classList?.contains?.('is-interacting'))perfCount('overviewBuildsDuringInteraction'); + const started=perfStart(),unit=UNIT*cam.scale,overscan=Math.max(96,Math.min(OVERVIEW_CACHE_OVERSCAN_PX,Math.max(width,height)*.18)),baseWidth=width+overscan*2,baseHeight=height+overscan*2, + pixelWidth=Math.max(1,Math.round(baseWidth*dpr)),pixelHeight=Math.max(1,Math.round(baseHeight*dpr)); + if(overviewBase.width!==pixelWidth||overviewBase.height!==pixelHeight){overviewBase.width=pixelWidth;overviewBase.height=pixelHeight} + const context=overviewBase.getContext('2d',{alpha:false});context.setTransform(dpr,0,0,dpr,0,0);context.fillStyle='#101315';context.fillRect(0,0,baseWidth,baseHeight); + const halfX=baseWidth/(2*unit),halfY=baseHeight/(2*unit),bounds={minX:Math.floor(centerX-halfX)-1,maxX:Math.ceil(centerX+halfX)+1,minY:Math.floor(centerY-halfY)-1,maxY:Math.ceil(centerY+halfY)+1},ids=visibleMetaIdsForBounds(bounds),visibleMetas=[...ids].map(id=>data.metas[id]).filter(Boolean), + mapX=value=>baseWidth/2+(value-centerX)*unit,mapY=value=>baseHeight/2+(value-centerY)*unit; + drawMapBoardCells(context,visibleMetas,mapX,mapY,unit,{showLevels:true});const longSegments=drawMapLongLines(context,visibleMetas,ids,mapX,mapY,Math.max(1,cam.scale*1.5)); + overviewCache={revision:minimapWorldRevision,width,height,dpr,scale:cam.scale,anchorX:centerX,anchorY:centerY,unit,overscan,baseWidth,baseHeight,longSegments,boardCount:ids.size}; + perfCount('overviewCacheBuilds');perfGauge('overviewBoards',ids.size);perfGauge('overviewPaths',longSegments);perfEnd('rebuildWorldOverviewCache',started); +} +function drawWorldOverview(){ + const started=perfStart();if(!overviewCanvas||!inWorldOverview())return; + const rect=getViewportRect(),width=Math.max(1,Math.round(rect.width)),height=Math.max(1,Math.round(rect.height)),dpr=1,[centerX,centerY]=cameraCenterInChunks(); + const stale=!overviewCache||overviewCache.revision!==minimapWorldRevision||overviewCache.width!==width||overviewCache.height!==height||overviewCache.dpr!==dpr||Math.abs(overviewCache.scale-cam.scale)>1e-6|| + Math.abs(centerX-overviewCache.anchorX)*overviewCache.unit>overviewCache.overscan*.82||Math.abs(centerY-overviewCache.anchorY)*overviewCache.unit>overviewCache.overscan*.82; + if(stale){ + rebuildWorldOverviewCache(width,height,dpr,centerX,centerY); + const bitmapWidth=Math.max(1,Math.round(overviewCache.baseWidth*dpr)),bitmapHeight=Math.max(1,Math.round(overviewCache.baseHeight*dpr)); + if(overviewCanvas.width!==bitmapWidth||overviewCanvas.height!==bitmapHeight){overviewCanvas.width=bitmapWidth;overviewCanvas.height=bitmapHeight} + const context=overviewCanvas.getContext('2d',{alpha:false});context.setTransform(1,0,0,1,0,0);context.drawImage(overviewBase,0,0,bitmapWidth,bitmapHeight); + overviewCanvas.style.width=`${overviewCache.baseWidth}px`;overviewCanvas.style.height=`${overviewCache.baseHeight}px`;perfCount('overviewBitmapCopies'); + } + overviewCanvas.hidden=false;overviewDirty=false; + // Move the oversized cached bitmap as one compositor layer. Previous builds + // copied the entire viewport canvas on every pan frame, which dominated low- + // end devices even though the map geometry itself was cached. + positionCachedWorldOverview(); + perfGauge('overviewBoards',overviewCache.boardCount);perfGauge('overviewPaths',overviewCache.longSegments);perfGauge('overviewShops',0);perfEnd('drawWorldOverview',started); +} +async function hydrateVisibleMetas(){ + if(inWorldOverview())return 0;const queue=[]; + for(const id of visibleMetaIds()){const meta=data.metas[id];if(!meta||meta.puzzle||pendingVisibleHydrations.has(id))continue;pendingVisibleHydrations.add(id);queue.push(meta)} + let cursor=0,completed=0;async function worker(){while(cursor{lodFrame=0;lodIdleHandle=0;ensureBoards()},interacting=globalThis.document?.body?.classList?.contains?.('is-interacting')===true;if(interacting&&typeof requestIdleCallback==='function')lodIdleHandle=requestIdleCallback(run,{timeout:500});else if(interacting)lodIdleHandle=setTimeout(run,180);else lodFrame=requestAnimationFrame(run)} +function desiredInteractiveBoardIds(ids){ + const desired=new Set(); + for(const id of ids)if(data.metas[id]?.puzzle)desired.add(id); + return desired; +} +function ensureBoards(){ + const started=perfStart();perfCount('ensureBoardsRuns'); + if(globalThis.document?.body?.classList?.contains?.('is-interacting')===true){perfCount('lodPassesDeferredDuringInteraction');scheduleLodPass();if(inWorldOverview())scheduleWorldOverview(true);perfEnd('ensureBoards',started);return} + let changes=0,pending=false; + if(inWorldOverview()){ + for(const[,b]of rendered)if(b.drawing?.pointerId==null){if(changesdata.metas[id]&&!data.metas[id].puzzle))void hydrateVisibleMetas().then(count=>{if(count)ensureBoards()}); + const ids=visibleMetaIds(),desiredInteractive=desiredInteractiveBoardIds(ids); + for(const id of desiredInteractive){ + const meta=data.metas[id];if(!meta?.puzzle||rendered.has(id))continue; + if(changesdata.metas[id]?.puzzle&&!metaState(id).solved).length; + perfGauge('visibleBoardIds',ids.size);perfGauge('visibleUnsolvedBoards',visibleUnsolvedBoards);perfGauge('interactiveBoardBudget',desiredInteractive.size);perfEnd('ensureBoards',started); +} +function renderAll(){for(const b of rendered.values())renderBoard(b)} +let cam={x:0,y:0,scale:1},pan=null,pinch=null,cameraFrame=0,cameraFrameDelayTimer=0,cameraLastDraw=0,cameraInteractionFrame=0,cameraInteractionDelayTimer=0,cameraInteractionLastDraw=0,pendingCameraInteraction=null,visibilityTimer=0,previousViewportSize=null; +function currentCameraAnchor(){const r=getViewportRect(),scale=Math.max(MIN_CAMERA_SCALE,Math.min(1.8,cam.scale||1));return{centerX:renderOriginX+(r.width/2-cam.x)/(UNIT*scale),centerY:renderOriginY+(r.height/2-cam.y)/(UNIT*scale),scale}} +function recordCameraAnchor(){ + const next=currentCameraAnchor(),previous=data.cameraAnchor; + if(previous&&Math.abs(previous.centerX-next.centerX)<1e-9&&Math.abs(previous.centerY-next.centerY)<1e-9&&Math.abs(previous.scale-next.scale)<1e-9)return false; + data.cameraAnchor=next;markGlobalDirty(false);save();return true; +} +function restoreSavedCamera(){const anchor=data.cameraAnchor;if(!anchor||!Number.isFinite(anchor.centerX)||!Number.isFinite(anchor.centerY)||!Number.isFinite(anchor.scale))return false;const r=getViewportRect();cam.scale=Math.max(MIN_CAMERA_SCALE,Math.min(1.8,anchor.scale));cam.x=r.width/2-(anchor.centerX-renderOriginX)*UNIT*cam.scale;cam.y=r.height/2-(anchor.centerY-renderOriginY)*UNIT*cam.scale;applyCamera(true);previousViewportSize={width:r.width,height:r.height};return true} +const touchPoints=new Map(); +function rebaseWorldOrigin(){ + const r=getViewportRect(),worldX=renderOriginX+(r.width/2-cam.x)/(UNIT*cam.scale),worldY=renderOriginY+(r.height/2-cam.y)/(UNIT*cam.scale); + if(Math.abs(worldX-renderOriginX)<2048&&Math.abs(worldY-renderOriginY)<2048)return; + const nextX=Math.trunc(worldX),nextY=Math.trunc(worldY),dx=nextX-renderOriginX,dy=nextY-renderOriginY;renderOriginX=nextX;renderOriginY=nextY;cam.x+=dx*UNIT*cam.scale;cam.y+=dy*UNIT*cam.scale; + for(const b of[...rendered.values(),...staticRendered.values()]){b.card.style.left=((b.meta.x-renderOriginX)*UNIT-PAD)+'px';b.card.style.top=((b.meta.y-renderOriginY)*UNIT-PAD)+'px'}overviewDirty=true; +} +function repositionActiveBoardHud(){const board=rendered.get(hudBoardId);if(board?.card?.isConnected&&boardPlayHudVisible(board))positionBoardLabel(board)} +function applyCamera(immediate=false,frameTimestamp=null){ + const cameraGestureActive=Boolean(pan||pinch||document.body.classList.contains('is-interacting'));if(!cameraGestureActive)rebaseWorldOrigin();const overview=updateZoomPresentation(); + const paint=timestamp=>{cameraFrame=0;cameraFrameDelayTimer=0;cameraLastDraw=timestamp;markVisualFrame(timestamp);if(overview)positionCachedWorldOverview();else world.style.transform=`translate3d(${cam.x}px,${cam.y}px,0) scale(${cam.scale})`}; + if(immediate){if(cameraFrame)cancelAnimationFrame(cameraFrame);if(cameraFrameDelayTimer)clearTimeout(cameraFrameDelayTimer);paint(frameTimestamp??perfNow())} + else if(!cameraFrame)cameraFrame=requestAnimationFrame(paint); + if(overview){overviewDirty=true;if(!cameraGestureActive)scheduleWorldOverview()} + if(cameraGestureActive){if(visibilityTimer){clearTimeout(visibilityTimer);visibilityTimer=0}}else{clearTimeout(visibilityTimer);visibilityTimer=setTimeout(()=>{visibilityTimer=0;ensureBoards();repositionActiveBoardHud()},90)} + minimapDirty=true; + if(cameraGestureActive)shiftOnlineLayersForCamera();else{scheduleMinimap();redrawOnlineLayersAfterCamera()} + scheduleRealtimeViewport(); +} +function zoomAt(clientX,clientY,nextScale){ + const r=getViewportRect(),mx=clientX-r.left,my=clientY-r.top,wx=(mx-cam.x)/cam.scale,wy=(my-cam.y)/cam.scale; + cam.scale=Math.max(MIN_CAMERA_SCALE,Math.min(1.8,nextScale));cam.x=mx-wx*cam.scale;cam.y=my-wy*cam.scale;applyCamera(); +} +function centerMeta(meta,{preserveScale=false,select=true}={}){ + if(!meta?.puzzle)return false; + const bounds=boardWorldBounds(meta),r=getViewportRect(); + worldOverviewActive=false;if(!preserveScale)cam.scale=Math.min(1.15,Math.max(.55,Math.min((r.width-90)/bounds.w,(r.height-90)/bounds.h))); + cam.x=r.width/2-(bounds.x+bounds.w/2)*cam.scale;cam.y=r.height/2-(bounds.y+bounds.h/2)*cam.scale;updateZoomPresentation(true);applyCamera(); + previousViewportSize={width:r.width,height:r.height};ensureBoards();const board=rendered.get(meta.id);if(select&&board)selectBoard(board);recordCameraAnchor();return true; +} +function centerOrigin(){return centerMeta(data.metas.B0||Object.values(data.metas).find(meta=>meta.puzzle))} +async function centerRandomBoard(){ + let candidates=Object.values(data.metas).filter(Boolean); + if(candidates.length>1)candidates=candidates.filter(meta=>meta.id!==activeBoard); + if(!candidates.length){toast('\u79fb\u52d5\u3067\u304d\u308b\u76e4\u9762\u306f\u3042\u308a\u307e\u305b\u3093\u3002');return false} + const random=new Uint32Array(1);try{if(!globalThis.crypto?.getRandomValues)throw new Error('secure random unavailable');globalThis.crypto.getRandomValues(random)}catch(_){random[0]=Math.floor(Math.random()*0xffffffff)} + const meta=candidates[random[0]%candidates.length]; + if(!meta.puzzle)try{await hydrateMeta(meta)}catch(error){toast('\u76e4\u9762\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3002');return false} + return centerMeta(meta); +} +function cancelPointerGestures(){ + for(const b of rendered.values())if(b.drawing?.pointerId!=null){const pointerId=b.drawing.pointerId;cancelBoardDragFrame(b);safeRelease(b.svg,pointerId);if(discardUnmovedCreatedPath(b)){renderBoard(b);continue}b.drawing.pointerId=null;b.drawing.lastPoint=null;b.drawing.pointerOffset=null;clearDrawingPointerState(b.drawing);changed(b.id);renderBoard(b)} +} +function touchPair(){ + const points=[...touchPoints.values()];if(points.length<2)return null; + const[a,b]=points,center=[(a.x+b.x)/2,(a.y+b.y)/2],distance=Math.hypot(a.x-b.x,a.y-b.y); + return{center,distance}; +} +function commitCameraInteraction(timestamp=perfNow()){ + const started=perfStart();cameraInteractionFrame=0;cameraInteractionDelayTimer=0;cameraInteractionLastDraw=timestamp;const next=pendingCameraInteraction;pendingCameraInteraction=null;if(!next){perfEnd('commitCameraInteraction',started);return false} + cam.x=next.x;cam.y=next.y;if(Number.isFinite(next.scale))cam.scale=next.scale;applyCamera(true,timestamp);recordInteractionCommit('camera',timestamp,next.inputAt);perfCount('cameraInteractionFrames');const workDuration=perfEnd('commitCameraInteraction',started);observeInteractionFrame(timestamp,workDuration);return true; +} +function queueCameraInteraction(next){ + pendingCameraInteraction=next;if(cameraInteractionFrame||cameraInteractionDelayTimer)return; + const step=timestamp=>{ + if(!cameraInteractionFrame&&!cameraInteractionDelayTimer)return; + if(cameraInteractionLastDraw&×tamp+INTERACTION_FRAME_TOLERANCE_MS{ + cameraInteractionDelayTimer=0; + cameraInteractionFrame=requestAnimationFrame(step); + cameraInteractionDelayTimer=setTimeout(()=>{perfCount('cameraDisplayWatchdogFrames');step(perfNow())},CAMERA_DISPLAY_WATCHDOG_MS); + }; + arm(); +} +function flushCameraInteraction(){ + if(cameraInteractionFrame){cancelAnimationFrame(cameraInteractionFrame);cameraInteractionFrame=0}if(cameraInteractionDelayTimer){clearTimeout(cameraInteractionDelayTimer);cameraInteractionDelayTimer=0} + const committed=commitCameraInteraction();recordCameraAnchor();return committed; +} +function leftFieldPanAllowed(event){ + if(event.button!==0)return false; + const target=event.target;if(target?.closest?.('button,a,input,select,textarea,[role="button"]'))return false; + const card=target?.closest?.('.board-card');if(!card)return true; + const id=card.dataset?.id; + return card.classList?.contains('solved')||Boolean(id&&data.metas[id]&&metaState(id).solved); +} +function beginPan(e){ + if(e.pointerType==='touch'){ + touchPoints.set(e.pointerId,{x:e.clientX,y:e.clientY}); + if(touchPoints.size===2){ + e.preventDefault();e.stopPropagation();cancelPointerGestures(); + const pair=touchPair(),r=getViewportRect(),mx=pair.center[0]-r.left,my=pair.center[1]-r.top; + pinch={startDistance:Math.max(1,pair.distance),startScale:cam.scale,worldX:(mx-cam.x)/cam.scale,worldY:(my-cam.y)/cam.scale,lastCenter:pair.center,lastAt:performance.now()}; + pan=null;viewport.classList.add('panning');refreshInteractionState();return; + } + if(touchPoints.size===1&&!e.target.closest?.('.board-svg,.board-static,.board-card button')){ + e.preventDefault();e.stopPropagation();pan={id:e.pointerId,x:e.clientX,y:e.clientY,cx:cam.x,cy:cam.y,lastX:e.clientX,lastY:e.clientY,lastAt:performance.now(),touch:true};viewport.classList.add('panning'); + try{viewport.setPointerCapture(e.pointerId)}catch(_){}refreshInteractionState(); + } + return; + } + if(e.button!==2&&!leftFieldPanAllowed(e))return; + e.preventDefault();e.stopPropagation();pan={id:e.pointerId,x:e.clientX,y:e.clientY,cx:cam.x,cy:cam.y,lastX:e.clientX,lastY:e.clientY,lastAt:performance.now(),touch:false,button:e.button};viewport.classList.add('panning'); + try{viewport.setPointerCapture(e.pointerId)}catch(_){}refreshInteractionState(); +} +function movePan(e){ + if(e.pointerType==='touch'&&touchPoints.has(e.pointerId))touchPoints.set(e.pointerId,{x:e.clientX,y:e.clientY}); + if(pinch&&touchPoints.size>=2){ + e.preventDefault();e.stopPropagation();const pair=touchPair(),r=getViewportRect(),mx=pair.center[0]-r.left,my=pair.center[1]-r.top, + nextScale=Math.max(.35,Math.min(1.8,pinch.startScale*pair.distance/pinch.startDistance)); + queueCameraInteraction({scale:nextScale,x:mx-pinch.worldX*nextScale,y:my-pinch.worldY*nextScale,inputAt:Number(e.timeStamp)||perfNow()});return; + } + if(!pan||pan.id!==e.pointerId)return; + e.preventDefault();if(pan.touch)e.stopPropagation(); + queueCameraInteraction({scale:cam.scale,x:pan.cx+e.clientX-pan.x,y:pan.cy+e.clientY-pan.y,inputAt:Number(e.timeStamp)||perfNow()}); +} +function stopPan(e){ + touchPoints.delete(e.pointerId); + let ended=false; + if(pinch&&touchPoints.size<2){flushCameraInteraction();pinch=null;ended=true} + if(pan&&pan.id===e.pointerId){flushCameraInteraction();pan=null;ended=true;try{viewport.releasePointerCapture(e.pointerId)}catch(_){}} + if(ended){viewport.classList.remove('panning');refreshInteractionState();clearTimeout(visibilityTimer);visibilityTimer=setTimeout(()=>{visibilityTimer=0;rebaseWorldOrigin();ensureBoards();repositionActiveBoardHud();scheduleWorldOverview(true)},60);scheduleMinimap(true);redrawOnlineLayersAfterCamera();scheduleRealtimeViewport(true)} +} +let tooltipFrame=0,pendingTooltipEvent=null,tooltipSize=null,tooltipText='',tooltipLastProbe=0; +function hideSpecialTooltip(){pendingTooltipEvent=null;if(tooltipFrame){cancelAnimationFrame(tooltipFrame);tooltipFrame=0}if(specialTooltip)specialTooltip.hidden=true} +function positionSpecialTooltip(event){ + if(!specialTooltip||specialTooltip.hidden)return; + const view=globalThis.visualViewport,gap=14,pad=10,viewLeft=view?.offsetLeft||0,viewTop=view?.offsetTop||0,viewWidth=view?.width||innerWidth,viewHeight=view?.height||innerHeight, + minLeft=viewLeft+pad,minTop=viewTop+pad,maxRight=viewLeft+viewWidth-pad,maxBottom=viewTop+viewHeight-pad; + if(!tooltipSize)tooltipSize={width:specialTooltip.offsetWidth||260,height:specialTooltip.offsetHeight||42}; + const{width,height}=tooltipSize; + let left=event.clientX+gap,top=event.clientY+gap; + if(left+width>maxRight)left=event.clientX-width-gap; + if(top+height>maxBottom)top=event.clientY-height-gap; + left=Math.max(minLeft,Math.min(left,maxRight-width));top=Math.max(minTop,Math.min(top,maxBottom-height)); + specialTooltip.style.left=left+'px';specialTooltip.style.top=top+'px'; +} +function specialInfoFromEvent(event){ + const card=event.target?.closest?.('.board-card'),b=card?rendered.get(card.dataset.id):null;if(!b||metaState(b.id).solved)return null; + let cell=null; + const directCell=event.target?.closest?.('[data-cell]')?.dataset.cell; + if(directCell){const parts=directCell.split(',').map(Number);if(parts.length===2&&parts.every(Number.isFinite))cell=parts} + if(!cell){const directGate=Number(event.target?.closest?.('.gate-hit')?.dataset.gate);if(Number.isInteger(directGate)&&b.p.g[directGate]){const gate=gateObj(b.p,directGate);cell=[gate.cell[0],gate.cell[1]]}} + const point=eventToSvg(b,event); + if(!cell)cell=cellAt(b,point); + let info=cell?b.specialInfo?.get(ckey(...cell)):null; + if(!info&&point&&b.specialInfo?.size){ + let nearest=null,nearestDistance=(CELL*.8)**2; + for(const[key,candidate]of b.specialInfo){const rc=key.split(',').map(Number),center=boardCellCenter(rc),distance=(center[0]-point[0])**2+(center[1]-point[1])**2;if(distance<=nearestDistance){nearestDistance=distance;nearest={cell:rc,info:candidate}}} + if(nearest){cell=nearest.cell;info=nearest.info} + } + return info?{b,cell,info}:null; +} +function updateSpecialTooltip(event){if(pan||pinch){hideSpecialTooltip();return}const target=specialInfoFromEvent(event);if(!target||!specialTooltip){hideSpecialTooltip();return}if(tooltipText!==target.info.description){tooltipText=target.info.description;specialTooltip.textContent=tooltipText;tooltipSize=null}specialTooltip.hidden=false;positionSpecialTooltip(event)} +function queueSpecialTooltip(event){ + if(pan||pinch||document.body.classList.contains('is-drawing')){if(!specialTooltip?.hidden)hideSpecialTooltip();return} + pendingTooltipEvent={target:event.target,clientX:event.clientX,clientY:event.clientY}; + if(tooltipFrame)return; + const step=timestamp=>{if(timestamp-tooltipLastProbe<80){tooltipFrame=requestAnimationFrame(step);return}tooltipFrame=0;tooltipLastProbe=timestamp;const pending=pendingTooltipEvent;pendingTooltipEvent=null;if(pending)updateSpecialTooltip(pending)}; + tooltipFrame=requestAnimationFrame(step); +} +viewport.addEventListener('pointermove',queueSpecialTooltip,{passive:true}); +viewport.addEventListener('pointerleave',hideSpecialTooltip,{passive:true}); +viewport.addEventListener('pointerdown',hideSpecialTooltip,true); +viewport.addEventListener('pointerdown',beginReactionGesture,true); +viewport.addEventListener('pointerdown',beginPan,true); +window.addEventListener('pointermove',moveReactionGesture,true); +viewport.addEventListener('pointermove',movePan,true); +window.addEventListener('pointerup',endReactionGesture,true); +viewport.addEventListener('pointerup',stopPan,true); +window.addEventListener('pointercancel',cancelReactionGesture,true); +viewport.addEventListener('pointercancel',stopPan,true); +viewport.addEventListener('contextmenu',e=>e.preventDefault()); +viewport.addEventListener('wheel',e=>{e.preventDefault();pulseWheelInteraction();zoomAt(e.clientX,e.clientY,cam.scale*Math.exp(-e.deltaY*.0012))},{passive:false}); +minimapCanvas.addEventListener('pointerdown',beginMinimapPointer); +minimapCanvas.addEventListener('pointermove',moveMinimapPointer); +minimapCanvas.addEventListener('pointerup',endMinimapPointer); +minimapCanvas.addEventListener('pointercancel',endMinimapPointer); +minimapCanvas.addEventListener('lostpointercapture',endMinimapPointer); +minimapCanvas.addEventListener('keydown',keyMinimap); +function handleViewportResize(){ + viewportResizeFrame=0;viewportRectCache=null;minimapRectCache=null;const r=getViewportRect(true); + if(previousViewportSize){ + const worldCenterX=(previousViewportSize.width/2-cam.x)/cam.scale,worldCenterY=(previousViewportSize.height/2-cam.y)/cam.scale; + cam.x=r.width/2-worldCenterX*cam.scale;cam.y=r.height/2-worldCenterY*cam.scale; + } + previousViewportSize={width:r.width,height:r.height};applyCamera(); +} +function scheduleViewportResize(){viewportRectCache=null;minimapRectCache=null;if(!viewportResizeFrame)viewportResizeFrame=requestAnimationFrame(handleViewportResize)} +window.addEventListener('resize',scheduleViewportResize); +globalThis.visualViewport?.addEventListener?.('resize',scheduleViewportResize); +if(typeof ResizeObserver!=='undefined')new ResizeObserver(scheduleViewportResize).observe(viewport); +if(typeof ResizeObserver!=='undefined')new ResizeObserver(()=>{minimapRectCache=null;invalidateMinimapWorld()}).observe(minimapCanvas); +const modal=document.querySelector('#modal'),helpBtn=document.querySelector('#helpBtn'),helpPanel=modal.querySelector('.panel'), + storeModal=document.querySelector('#storeModal'),storePanel=storeModal.querySelector('.store-panel'), + storeTitle=document.querySelector('#storeTitle'),storeWallet=document.querySelector('#storeWallet'), + storeMeta=document.querySelector('#storeMeta'),storeInventory=document.querySelector('#storeInventory'), + inventoryModal=document.querySelector('#inventoryModal'),inventoryPanel=inventoryModal.querySelector('.inventory-panel'), + inventoryBtn=document.querySelector('#inventoryBtn'),inventoryCountEl=document.querySelector('#inventoryCount'), + inventoryTotal=document.querySelector('#inventoryTotal'),inventoryTarget=document.querySelector('#inventoryTarget'), + inventoryList=document.querySelector('#inventoryList'),debugAllItemsToggle=document.querySelector('#debugAllItemsToggle'),debugAllItemsState=document.querySelector('#debugAllItemsState'), + timeAttackModal=document.querySelector('#timeAttackModal'),timeAttackPanel=timeAttackModal.querySelector('.time-attack-panel'), + timeAttackBtn=document.querySelector('#timeAttackBtn'),timeAttackButtonLabel=document.querySelector('#timeAttackButtonLabel'), + timeAttackHeadingScore=document.querySelector('#timeAttackHeadingScore'),timeAttackSetup=document.querySelector('#timeAttackSetup'), + timeAttackLive=document.querySelector('#timeAttackLive'),timeAttackResultEl=document.querySelector('#timeAttackResult'), + timeAttackClockEl=document.querySelector('#timeAttackClock'),timeAttackCollectedEl=document.querySelector('#timeAttackCollected'), + timeAttackMultiplierEl=document.querySelector('#timeAttackMultiplier'),timeAttackProjectedEl=document.querySelector('#timeAttackProjected'), + timeAttackResultLimit=document.querySelector('#timeAttackResultLimit'),timeAttackResultSolves=document.querySelector('#timeAttackResultSolves'), + timeAttackResultCollected=document.querySelector('#timeAttackResultCollected'),timeAttackResultMultiplier=document.querySelector('#timeAttackResultMultiplier'), + timeAttackResultBonus=document.querySelector('#timeAttackResultBonus'),timeAttackResultTotal=document.querySelector('#timeAttackResultTotal'), + timeAttackShareText=document.querySelector('#timeAttackShareText'),copyTimeAttackBtn=document.querySelector('#copyTimeAttack'), + closeTimeAttackBtn=document.querySelector('#closeTimeAttack'), + settingsModal=document.querySelector('#settingsModal'),settingsPanel=settingsModal.querySelector('.settings-panel'),settingsPlayerName=document.querySelector('#settingsPlayerName'),lightweightRenderingToggle=document.querySelector('#lightweightRenderingToggle'),soundEnabledToggle=document.querySelector('#soundEnabledToggle'),saveSettingsBtn=document.querySelector('#saveSettings'),closeSettingsBtn=document.querySelector('#closeSettings'),customEmojiCursor=document.querySelector('#customEmojiCursor'),pickupHandleOverlay=document.querySelector('#pickupHandleOverlay'); +let openStoreBoardId=null; +let timeAttackTimer=null,entryChoicePending=false; +function trapDialogFocus(panel,e){ + if(e.key!=='Tab')return; + const focusable=[...panel.querySelectorAll('button,[href],[tabindex]:not([tabindex="-1"])')].filter(el=>!el.disabled&&!el.hidden); + if(!focusable.length){e.preventDefault();panel.focus();return} + const first=focusable[0],last=focusable[focusable.length-1]; + if(e.shiftKey&&document.activeElement===first){e.preventDefault();last.focus()} + else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first.focus()} +} +function setDialogInert(root,inert){ + root.inert=Boolean(inert); + if(inert)root.setAttribute('inert',''); + else root.removeAttribute('inert'); +} +function focusOutsideDialog(root,preferred=null){ + const active=document.activeElement; + if(!active||!root.contains(active))return; + const target=preferred?.isConnected&&!preferred.disabled?preferred:viewport; + try{target?.focus?.({preventScroll:true})}catch(_){target?.focus?.()} + if(root.contains(document.activeElement))active.blur?.(); +} +function openDialogRoot(root){ + setDialogInert(root,false); + root.setAttribute('aria-hidden','false'); + root.classList.add('show'); +} +function closeDialogRoot(root,preferredFocus=null){ + focusOutsideDialog(root,preferredFocus); + root.classList.remove('show'); + setDialogInert(root,true); + root.setAttribute('aria-hidden','true'); +} +function debugAllItemsEnabled(){return data.debugAllItems===true} +function setItemIcon(node,item){ + node.classList.toggle('emoji-glyph',Boolean(item?.cursorEmoji&&!item?.flagAsset)); + if(item?.flagAsset){ + const image=document.createElement('img');image.className='item-flag-image';image.src=item.flagAsset;image.alt='';image.draggable=false;node.replaceChildren(image); + }else node.textContent=item?.icon||''; +} +function renderInventoryPanel(){ + const debug=debugAllItemsEnabled(),total=debug?STORE_ITEMS.length:inventoryCount(); + inventoryCountEl.textContent=debug?'ALL':String(total); + inventoryTotal.textContent=debug?'DEBUG':String(total); + if(debugAllItemsToggle)debugAllItemsToggle.checked=debug; + if(debugAllItemsState)debugAllItemsState.textContent=debug?'ON · 全アイテム使用可能':'OFF · 通常所持数を使用'; + inventoryTarget.textContent='カーソルはデザインをクリックして切り替えます。同じデザインをもう一度クリックすると標準へ戻ります。'; + inventoryList.replaceChildren(); + if(!total){ + const empty=document.createElement('div');empty.className='inventory-item empty';empty.textContent='所持アイテムはありません。';inventoryList.append(empty);return; + } + const available=STORE_ITEMS.filter(item=>debug||inventoryCount(item.id)>0),categories=[ + {title:'アイテム',items:available.filter(item=>!item.cursorStyle),cursor:false}, + {title:'カーソル',items:available.filter(item=>item.cursorStyle),cursor:true} + ]; + for(const category of categories){ + if(!category.items.length)continue; + const section=document.createElement('section'),title=document.createElement('h3'),list=document.createElement('div'); + section.className=`inventory-section inventory-${category.cursor?'cursors':'items'}-section`; + title.className='inventory-section-title';title.textContent=category.title; + list.className=category.cursor?'inventory-cursor-grid':'inventory-item-list'; + for(const item of category.items){ + const count=inventoryCount(item.id); + if(category.cursor){ + const option=document.createElement('button');option.type='button';option.className='inventory-cursor-option';setItemIcon(option,item);option.classList.toggle('selected',data.cursorStyle===item.cursorStyle); + option.setAttribute('aria-pressed',String(data.cursorStyle===item.cursorStyle));option.setAttribute('aria-label',`${item.name}カーソル`); + option.addEventListener('click',()=>useInventoryItem(item.id));list.append(option);continue; + } + const card=document.createElement('article'),icon=document.createElement('div'),copy=document.createElement('div'), + name=document.createElement('h3'),description=document.createElement('p'),use=document.createElement('button'); + card.className=`inventory-item${debug?' debug-available':''}`;icon.className='inventory-item-icon';icon.textContent=item.icon; + name.textContent=debug?`${item.name} · ∞`:`${item.name} · ×${count}`;description.textContent=item.description;copy.append(name,description); + const lensActive=item.scoreLens&&data.scoreLensEnabled===true; + use.type='button';use.className='inventory-use';use.textContent=item.scoreLens?(lensActive?'ON':'OFF'):'使用';use.disabled=false; + if(item.scoreLens){use.classList.toggle('selected',lensActive);use.setAttribute('aria-pressed',String(lensActive))} + use.addEventListener('click',()=>useInventoryItem(item.id));card.append(icon,copy,use);list.append(card); + } + section.append(title,list);inventoryList.append(section); + } +} +function updateInventoryUi(){ + inventoryCountEl.textContent=debugAllItemsEnabled()?'ALL':String(inventoryCount()); + if(inventoryModal.classList.contains('show'))renderInventoryPanel(); +} + +function syncCursorAppearance(style){ + const selected=CURSOR_ITEM_BY_STYLE.get(style); + document.body.dataset.cursorStyle=style||'default';document.body.dataset.cursorEmoji=selected?'on':'off'; + let nativeCursor=''; + if(selected?.flagAsset)nativeCursor=`url("${selected.flagAsset}") 16 16, auto`; + else if(selected?.cursorEmoji){ + const svg=`${selected.cursorEmoji}`; + nativeCursor=`url("data:image/svg+xml,${encodeURIComponent(svg)}") 16 16, auto`; + } + let forceDomFollower=false;try{forceDomFollower=localStorage.getItem('bend-field-cursor-renderer')==='dom'}catch(_){} + const nativeSupported=Boolean(selected&&nativeCursor&&!forceDomFollower&&globalThis.CSS?.supports?.('cursor',nativeCursor)); + document.body.dataset.cursorMode=selected?(nativeSupported?'native':'dom'):'default'; + if(nativeSupported)document.body.style.setProperty('--active-native-cursor',nativeCursor);else document.body.style.removeProperty('--active-native-cursor'); + customEmojiCursor.classList.toggle('flag-cursor',Boolean(selected?.flagAsset)); + if(selected?.flagAsset){ + const image=document.createElement('img');image.src=selected.flagAsset;image.alt='';image.draggable=false;customEmojiCursor.replaceChildren(image); + }else customEmojiCursor.textContent=selected?.cursorEmoji||''; + if(!selected||nativeSupported){customEmojiCursor.classList.remove('visible');if(!selected)customEmojiCursor.style.transform=''} +} +function applyCursorStyle(style){data.cursorStyle=style||'default';syncCursorAppearance(data.cursorStyle);if(realtimePendingCursor){realtimePendingCursor.cursorStyle=data.cursorStyle;realtimeSend({type:'cursor',...realtimePendingCursor})}markGlobalDirty();} +let customCursorFrame=0,customCursorX=0,customCursorY=0,customCursorInputAt=0,customCursorActiveUntil=0,customCursorRenderedX=NaN,customCursorRenderedY=NaN,customCursorLastFrameAt=0,customCursorInputRevision=0,customCursorCommittedRevision=0; +function updateCustomCursorFromPointer(event){ + const customActive=document.body.dataset.cursorMode==='dom',insideDocument=event.target?.isConnected!==false,finePointer=event.pointerType!=='touch'; + if(!customActive||!insideDocument||!finePointer){customCursorRenderedX=customCursorRenderedY=NaN;customCursorLastFrameAt=0;customCursorActiveUntil=0;if(customEmojiCursor.classList.contains('visible'))customEmojiCursor.classList.remove('visible');return} + const wasVisible=customEmojiCursor.classList.contains('visible'); + customCursorX=event.clientX;customCursorY=event.clientY;customCursorInputAt=Number(event.timeStamp)||perfNow();customCursorInputRevision++;customCursorActiveUntil=perfNow()+40; + if(!wasVisible){customCursorRenderedX=customCursorX;customCursorRenderedY=customCursorY;customEmojiCursor.classList.add('visible')} + if(customCursorFrame)return; + const step=timestamp=>{ + if(!customCursorFrame)return;customCursorFrame=0; + if(document.body.dataset.cursorMode!=='dom'||!customEmojiCursor.classList.contains('visible'))return; + const elapsed=customCursorLastFrameAt?Math.min(50,Math.max(1,timestamp-customCursorLastFrameAt)):16.67,blend=1-Math.exp(-elapsed/8); + if(!Number.isFinite(customCursorRenderedX)){customCursorRenderedX=customCursorX;customCursorRenderedY=customCursorY} + else{customCursorRenderedX+=(customCursorX-customCursorRenderedX)*blend;customCursorRenderedY+=(customCursorY-customCursorRenderedY)*blend} + customCursorLastFrameAt=timestamp; + customEmojiCursor.style.transform=`translate3d(${customCursorRenderedX}px,${customCursorRenderedY}px,0) translate(-50%,-50%)`;markVisualFrame(timestamp); + const freshInput=customCursorCommittedRevision!==customCursorInputRevision;recordInteractionCommit('cursor',timestamp,customCursorInputAt,freshInput);if(freshInput)customCursorCommittedRevision=customCursorInputRevision;perfCount('cursorCommits'); + if(perfNow(){if(!event.relatedTarget)customEmojiCursor.classList.remove('visible')},{passive:true}); +function flashConfirmedCell(b,cell){ + if(!b?.svg?.isConnected||!Array.isArray(cell))return; + const key=ckey(...cell),previous=b.cellFlashAnimations.get(key);if(previous)try{previous.cancel()}catch(_){} + const node=svgEl('rect',{x:PAD+cell[1]*CELL,y:PAD+cell[0]*CELL,width:CELL,height:CELL,class:'cell-confirm-flash'});b.dragLayer.append(node); + let animation=null,timer=0;const cleanup=()=>{node.remove();if(b.cellFlashAnimations.get(key)===controller)b.cellFlashAnimations.delete(key)}; + const controller={cancel(){if(animation)try{animation.cancel()}catch(_){}if(timer)clearTimeout(timer);cleanup()}};b.cellFlashAnimations.set(key,controller); + if(typeof node.animate==='function'){try{animation=node.animate([{opacity:.75},{opacity:0}],{duration:260,easing:'ease-out'});animation.finished.catch(()=>{}).finally(cleanup)}catch(_){animation=null}} + if(!animation)timer=setTimeout(cleanup,280); +} +function playConfirmTone(pathLength){const step=Math.min(7,Math.max(0,(pathLength||1)-1));soundTone(250*Math.pow(2,step/12),.055,{gain:.014,type:'sine',slide:1.03})} +function finalizeExitGates(meta){const p=puzzleOf(meta),st=metaState(meta.id);if(!p||!st.solved)return;meta.sealedSides=[];meta.rev=nextRevision();markMetaDirty(meta.id)} +let sessionCompletionCount=0,timeAttackSuggestionShown=false,timeAttackSuggestionPending=false,timeAttackSuggestionTimer=0; +function timeAttackSuggestionReady(){ + if(data.timeAttackSuggestionsDisabled||data.timeAttack||timeAttackSuggestionShown||!timeAttackSuggestionPending)return false; + const dialogOpen=[modal,storeModal,inventoryModal,timeAttackModal,settingsModal].some(root=>root?.classList.contains('show')), + drawing=[...rendered.values()].some(board=>board.drawing?.pointerId!=null||board.drawing?.keyboardActive); + return!dialogOpen&&!drawing; +} +function dismissTimeAttackSuggestion(){ + clearTimeout(timeAttackSuggestionTimer);timeAttackSuggestionTimer=0;if(timeAttackSuggestion)timeAttackSuggestion.hidden=true; +} +function tryShowTimeAttackSuggestion(){ + clearTimeout(timeAttackSuggestionTimer);timeAttackSuggestionTimer=0; + if(!timeAttackSuggestionPending||timeAttackSuggestionShown)return false; + if(data.timeAttackSuggestionsDisabled||data.timeAttack){timeAttackSuggestionPending=false;return false} + if(!timeAttackSuggestionReady()){timeAttackSuggestionTimer=setTimeout(tryShowTimeAttackSuggestion,250);return false} + timeAttackSuggestionPending=false;timeAttackSuggestionShown=true;timeAttackSuggestion.hidden=false; + timeAttackSuggestionTimer=setTimeout(dismissTimeAttackSuggestion,7000);return true; +} +function scheduleTimeAttackSuggestionAfterCompletion(){ + sessionCompletionCount++;if(sessionCompletionCount!==10||timeAttackSuggestionShown)return false; + timeAttackSuggestionPending=true;clearTimeout(timeAttackSuggestionTimer);timeAttackSuggestionTimer=setTimeout(tryShowTimeAttackSuggestion,700);return true; +} +function worldUnitAtClient(clientX,clientY){ + const rect=getViewportRect(),localX=(clientX-rect.left-cam.x)/cam.scale,localY=(clientY-rect.top-cam.y)/cam.scale; + return[renderOriginX+localX/UNIT,renderOriginY+localY/UNIT]; +} +async function useInventoryItemLoaded(itemId){ + const item=storeItem(itemId),debug=debugAllItemsEnabled(),entry=inventoryEntries(itemId)[0]; + if(!item||(!debug&&!entry)){toast('そのアイテムは所持していません。');renderInventoryPanel();return false} + if(item.cursorStyle){ + const previousCursor=data.cursorStyle,nextCursor=previousCursor===item.cursorStyle?'default':item.cursorStyle; + applyCursorStyle(nextCursor); + if(!await save(true)){applyCursorStyle(previousCursor);renderInventoryPanel();toast('切り替えできませんでした。');return false} + updateHud();renderInventoryPanel();toast(nextCursor==='default'?'標準カーソルへ戻しました。':`${item.name}へ切り替えました。`);return true; + } + if(item.scoreLens){ + const previous=data.scoreLensEnabled===true;data.scoreLensEnabled=!previous;markGlobalDirty();invalidateEconomyCaches(); + if(!await save(true)){data.scoreLensEnabled=previous;markGlobalDirty();invalidateEconomyCaches();renderInventoryPanel();toast('切り替えできませんでした。');return false} + updateHud();renderAll();updateZoomPresentation(true);renderInventoryPanel();toast(`${item.name} ${data.scoreLensEnabled?'ON':'OFF'}`);return true; + } + const previous=entry?deepClone(entry.purchase):null,previousRev=entry?.st?.rev; + if(!await save(true)){ + if(entry&&previous){Object.assign(entry.purchase,previous);entry.st.rev=previousRev;markStateDirty(entry.meta.id)} + renderInventoryPanel();toast('使用できませんでした。');return false; + } + updateHud();renderAll();renderInventoryPanel();toast(`${item.name}を装備しました。`);return true; +} +async function useInventoryItem(itemId){ + const entry=inventoryEntries(itemId)[0]; + if(entry?.meta&&!entry.meta.puzzle)await hydrateMeta(entry.meta); + return useInventoryItemLoaded(itemId); +} +function openInventory(){ + if(modal.classList.contains('show'))closeHelp(false); + if(storeModal.classList.contains('show'))closeStore(false); + if(timeAttackModal.classList.contains('show'))closeTimeAttack(false); + if(settingsModal.classList.contains('show'))closeSettings(false); + renderInventoryPanel(); + openDialogRoot(inventoryModal);inventoryBtn.setAttribute('aria-expanded','true'); + requestAnimationFrame(()=>inventoryPanel.focus()); +} +function closeInventory(restoreFocus=true){ + closeDialogRoot(inventoryModal,restoreFocus?inventoryBtn:viewport);inventoryBtn.setAttribute('aria-expanded','false'); +} +function formatTimeAttackClock(milliseconds){ + const seconds=Math.max(0,Math.ceil(milliseconds/1000)),minutes=Math.floor(seconds/60); + return`${String(minutes).padStart(2,'0')}:${String(seconds%60).padStart(2,'0')}`; +} +function timeAttackResultText(result=data.lastTimeAttack){ + if(!result)return''; + const lines=[ + '⏱️ 曲線フィールド|タイムアタック', + `🏁 ${result.durationMinutes}分 🧩 ${result.solves}枚クリア`, + `💎 獲得 ${formatScore(result.collected)} ✨ 実効倍率 ×${result.multiplier.toFixed(2)}`, + `🎁 タイム加算 +${formatScore(result.modifierGain||0)}`, + `🏆 合計 ${formatScore(result.total)}` + ]; + // lines.push('https://example.com/bend-field/time-attack/demo-result'); + return lines.join('\n'); +} +function timeAttackCooldownRemaining(durationMinutes,now=trustedNow()){ + return Math.max(0,(data.timeAttackCooldowns?.[durationMinutes]||0)-now); +} +function hasActiveTimeAttackCooldown(){return TIME_ATTACK_MINUTES.some(minutes=>timeAttackCooldownRemaining(minutes)>0)} +function renderTimeAttackPanel(){ + const run=data.timeAttack,last=data.lastTimeAttack; + timeAttackSetup.hidden=!!run; + timeAttackLive.hidden=!run; + timeAttackResultEl.hidden=!!run||!last; + copyTimeAttackBtn.hidden=!!run||!last; + closeTimeAttackBtn.textContent='\u9589\u3058\u308b'; + for(const button of timeAttackModal.querySelectorAll('[data-time-minutes]')){ + const minutes=Number(button.dataset.timeMinutes),remaining=timeAttackCooldownRemaining(minutes),label=button.querySelector('.duration-status'),idleLabel=minutes===3?'短時間で集中':minutes===5?'標準コース':'じっくり挑戦'; + button.disabled=remaining>0; + if(label)label.textContent=remaining?`待機 ${formatTimeAttackClock(remaining)}`:idleLabel; + button.title=remaining?`\u518d\u6311\u6226\u307e\u3067\uff1a${formatTimeAttackClock(remaining)}`:`${minutes}\u5206\u306e\u30bf\u30a4\u30e0\u30a2\u30bf\u30c3\u30af\u3092\u958b\u59cb`; + } + if(run){ + const remaining=Math.max(0,run.endsAt-trustedNow()),multiplier=timeAttackMultiplier(run.baseCollected||0); + timeAttackClockEl.textContent=formatTimeAttackClock(remaining); + timeAttackClockEl.classList.toggle('urgent',remaining<=30000); + timeAttackCollectedEl.textContent=formatScore(run.collected); + timeAttackMultiplierEl.textContent=`\u00d7${multiplier.toFixed(2)}`; + timeAttackProjectedEl.textContent=formatScore(run.collected); + timeAttackHeadingScore.textContent=`${run.solves}\u679a\u30af\u30ea\u30a2`; + }else{ + timeAttackClockEl.classList.remove('urgent'); + timeAttackHeadingScore.textContent=last?`\u30bf\u30a4\u30e0\u52a0\u7b97 +${formatScore(last.modifierGain||last.bonus)}`:'\u30bf\u30a4\u30e0\u52a0\u7b97'; + } + if(last){ + timeAttackResultLimit.textContent=`${last.durationMinutes}\u5206`; + timeAttackResultSolves.textContent=String(last.solves); + timeAttackResultCollected.textContent=formatScore(last.collected); + timeAttackResultMultiplier.textContent=`\u00d7${last.multiplier.toFixed(2)}`; + timeAttackResultBonus.textContent=`+${formatScore(last.modifierGain||last.bonus)}`; + timeAttackResultTotal.textContent=formatScore(last.total); + timeAttackShareText.textContent=timeAttackResultText(last); + }else timeAttackShareText.textContent=''; +} +function updateTimeAttackUi(){ + const run=data.timeAttack,now=trustedNow(); + if(run&&now>=run.endsAt){void finishTimeAttack();return} + if(!run&&!hasActiveTimeAttackCooldown())clearTimeAttackTimer() + timeAttackBtn.classList.toggle('active',!!run); + timeAttackBtn.classList.toggle('starting',Boolean(run&&now-run.startedAt<5000)); + timeAttackBtn.setAttribute('aria-label',run?`\u6b8b\u308a\u6642\u9593 ${formatTimeAttackClock(run.endsAt-now)}`:'\u30bf\u30a4\u30e0\u30a2\u30bf\u30c3\u30af\u3092\u958b\u304f'); + timeAttackButtonLabel.textContent=run?`${formatTimeAttackClock(run.endsAt-now)}`:'\u30bf\u30a4\u30e0\u30a2\u30bf\u30c3\u30af'; + if(timeAttackModal.classList.contains('show'))renderTimeAttackPanel(); +} +function clearTimeAttackTimer(){if(timeAttackTimer){clearInterval(timeAttackTimer);clearTimeout(timeAttackTimer);timeAttackTimer=null}} +function resumeTimeAttackTimer(){ + clearTimeAttackTimer();updateTimeAttackUi(); + if(data.timeAttack){timeAttackTimer=setInterval(updateTimeAttackUi,250);return} + const remaining=TIME_ATTACK_MINUTES.map(minutes=>timeAttackCooldownRemaining(minutes)).filter(Boolean); + if(!remaining.length)return; + if(timeAttackModal.classList.contains('show'))timeAttackTimer=setInterval(updateTimeAttackUi,1000); + else timeAttackTimer=setTimeout(resumeTimeAttackTimer,Math.min(...remaining)+50); +} +async function startTimeAttack(durationMinutes){ + if(data.timeAttack||!TIME_ATTACK_MINUTES.includes(durationMinutes))return false; + const cooldown=timeAttackCooldownRemaining(durationMinutes); + if(cooldown){toast(`\u518d\u6311\u6226\u307e\u3067\uff1a${formatTimeAttackClock(cooldown)}`);return false} + const startedAt=trustedNow(); + const previous={timeAttack:data.timeAttack,timeAttackRev:data.timeAttackRev}; + data.timeAttack={id:globalThis.crypto?.randomUUID?.()||`run-${startedAt}`,durationMinutes,startedAt,endsAt:startedAt+durationMinutes*60000,collected:0,baseCollected:0,rewardPipelineVersion:2,scoreVersion:SCORE_VERSION,solves:0}; + data.timeAttackRev=nextRevision();markGlobalDirty(); + if(!await save(true)){data.timeAttack=previous.timeAttack;data.timeAttackRev=previous.timeAttackRev;toast('\u30bf\u30a4\u30e0\u30a2\u30bf\u30c3\u30af\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3002');return false} + resumeTimeAttackTimer();closeTimeAttack(false);updateHud(); + toast(`${durationMinutes}\u5206\u306e\u30bf\u30a4\u30e0\u30a2\u30bf\u30c3\u30af\u3092\u958b\u59cb\u3057\u307e\u3057\u305f\u3002 \u500d\u7387 \u00d71.25`); + return true; +} +function recordTimeAttackScore(reward){ + const run=data.timeAttack; + if(!run)return false; + if(trustedNow()>=run.endsAt){void finishTimeAttack();return false} + const award=Math.max(0,Math.round(typeof reward==='object'?reward.award:reward||0)); + const baseAward=Math.max(0,Math.round(typeof reward==='object'?reward.preTimeAward:award)); + run.baseCollected=Math.min(MAX_SCORE,(run.baseCollected||0)+baseAward); + run.collected=Math.min(MAX_SCORE,run.collected+award); + run.solves=Math.min(MAX_SOLVES,run.solves+1); + data.timeAttackRev=nextRevision(); + updateTimeAttackUi(); + return true; +} +let finishingTimeAttack=false; +async function finishTimeAttack(){ + if(finishingTimeAttack)return null; + const run=data.timeAttack;if(!run)return null;finishingTimeAttack=true; + const previous={run:deepClone(run),lastTimeAttack:deepClone(data.lastTimeAttack),cooldowns:deepClone(data.timeAttackCooldowns),bonusScore:data.bonusScore,bonusEvents:deepClone(data.bonusEvents),timeAttackRev:data.timeAttackRev}; + try{ + const result=normalizeTimeAttackResult({...run,completedAt:trustedNow()});data.timeAttack=null;data.lastTimeAttack=result; + data.timeAttackCooldowns=normalizeTimeAttackCooldowns(data.timeAttackCooldowns);data.timeAttackCooldowns[result.durationMinutes]=result.completedAt+TIME_ATTACK_COOLDOWN_MINUTES[result.durationMinutes]*60000; + data.bonusEvents=data.bonusEvents||{};if(result.bonus>0)data.bonusEvents[`time-attack:${result.id}`]=result.bonus;data.bonusScore=bonusEventTotal();statsDirty=true;data.timeAttackRev=nextRevision();markGlobalDirty();clearTimeAttackTimer(); + if(!await save(true)){data.timeAttack=previous.run;data.lastTimeAttack=previous.lastTimeAttack;data.timeAttackCooldowns=previous.cooldowns;data.bonusScore=previous.bonusScore;data.bonusEvents=previous.bonusEvents;data.timeAttackRev=previous.timeAttackRev;statsDirty=true;resumeTimeAttackTimer();showStatus('\u4fdd\u5b58\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002',{retry:true,fresh:false});return null} + updateHud();resumeTimeAttackTimer();openTimeAttack();toast(`\u30bf\u30a4\u30e0\u30a2\u30bf\u30c3\u30af\u7d42\u4e86 \u00b7 \u30bf\u30a4\u30e0\u52a0\u7b97 +${formatScore(result.modifierGain||result.bonus)}`,3000);return result; + }finally{finishingTimeAttack=false} +} +async function copyTimeAttackResult(){ + const text=timeAttackResultText(); + if(!text)return false; + let copied=false; + try{await navigator.clipboard.writeText(text);copied=true} + catch(_){ + const area=document.createElement('textarea'); + area.value=text;area.setAttribute('readonly','');area.style.position='fixed';area.style.opacity='0'; + document.body.append(area);area.select(); + try{copied=document.execCommand('copy')}catch(__){copied=false} + area.remove(); + } + toast(copied?'\u7d50\u679c\u3092\u30b3\u30d4\u30fc\u3057\u307e\u3057\u305f\u3002':'\u7d50\u679c\u3092\u30b3\u30d4\u30fc\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002'); + return copied; +} +function openTimeAttack(){ + if(modal.classList.contains('show'))closeHelp(false); + if(storeModal.classList.contains('show'))closeStore(false); + if(inventoryModal.classList.contains('show'))closeInventory(false); + if(settingsModal.classList.contains('show'))closeSettings(false); + renderTimeAttackPanel(); + openDialogRoot(timeAttackModal);timeAttackBtn.setAttribute('aria-expanded','true'); + resumeTimeAttackTimer();requestAnimationFrame(()=>timeAttackPanel.focus()); +} +function closeTimeAttack(restoreFocus=true){ + closeDialogRoot(timeAttackModal,restoreFocus?timeAttackBtn:viewport);timeAttackBtn.setAttribute('aria-expanded','false'); + resumeTimeAttackTimer(); +} +function openHelp(entry=false){ + if(storeModal.classList.contains('show'))closeStore(false); + if(inventoryModal.classList.contains('show'))closeInventory(false); + if(timeAttackModal.classList.contains('show'))closeTimeAttack(false); + if(settingsModal.classList.contains('show'))closeSettings(false); + entryChoicePending=entry===true;modal.classList.toggle('entry-choice',entryChoicePending); + openDialogRoot(modal);helpBtn.setAttribute('aria-expanded','true'); + requestAnimationFrame(()=>helpPanel.focus()); +} +function closeHelp(restoreFocus=true,force=false){ + if(entryChoicePending&&!force)return false; + closeDialogRoot(modal,restoreFocus?helpBtn:viewport);modal.classList.remove('entry-choice');helpBtn.setAttribute('aria-expanded','false'); + return true; +} +function chooseNormalPlay(){entryChoicePending=false;closeHelp(false,true);viewport.focus?.()} +function chooseTimeAttack(){entryChoicePending=false;closeHelp(false,true);openTimeAttack()} + +function openSettings(){ + if(modal.classList.contains('show'))closeHelp(false,true); + if(storeModal.classList.contains('show'))closeStore(false); + if(inventoryModal.classList.contains('show'))closeInventory(false); + if(timeAttackModal.classList.contains('show'))closeTimeAttack(false); + settingsPlayerName.value=currentPlayerName();lightweightRenderingToggle.checked=uiSettings.lightweightRendering;soundEnabledToggle.checked=uiSettings.soundEnabled; + openDialogRoot(settingsModal);settingsBtn?.setAttribute('aria-expanded','true');requestAnimationFrame(()=>{settingsPanel.focus();settingsPlayerName.select()}); +} +function closeSettings(restoreFocus=true){closeDialogRoot(settingsModal,restoreFocus?(settingsBtn||playerNameBtn):viewport);settingsBtn?.setAttribute('aria-expanded','false')} +async function saveSettings(){ + const name=settingsPlayerName.value.replace(/[\u0000-\u001f\u007f]/g,'').replace(/\s+/g,' ').trim().slice(0,24);if(!name){toast('プレイヤー名を入力してください。');settingsPlayerName.focus();return false} + if(name!==currentPlayerName()){ + if(cloudAvailable&&data.cloudProfile)try{const result=await fetchJson('/api/cloud/profile',{method:'POST',headers:cloudAuthHeaders(),body:JSON.stringify({name})});data.playerName=result.name||name;setCloudStatus('共有中','saved')}catch(error){toast(`名前を変更できませんでした。 ${error.message}`);return false} + else data.playerName=name;markGlobalDirty(false);await persistNow({skipCloud:true});updatePlayerNameUi();renderAll();schedulePresenceRender(true); + } + const previousLightweight=uiSettings.lightweightRendering;uiSettings=normalizeUiSettings({lightweightRendering:lightweightRenderingToggle.checked,soundEnabled:soundEnabledToggle.checked});persistUiSettings();if(previousLightweight!==uiSettings.lightweightRendering){for(const board of rendered.values())board.solvedPathsRendered=false;renderAll();}closeSettings();toast('設定を保存しました。');return true +} + +function renderStorePanel(){ + pruneAndCount(); + const meta=data.metas[openStoreBoardId],st=meta?metaState(meta.id):null,store=st?.store; + if(!meta||!store){closeStore(false);return} + storeTitle.textContent='\u30a2\u30a4\u30c6\u30e0\u30b7\u30e7\u30c3\u30d7'; + storeWallet.textContent=formatScore(data.score); + storeMeta.textContent=`店主:${store.owner}`; + storeInventory.replaceChildren(); + const available=storeInventoryItems(meta,store),categories=[ + {title:'アイテム',items:available.filter(item=>!item.cursorStyle),cursor:false}, + {title:'カーソル',items:available.filter(item=>item.cursorStyle),cursor:true} + ]; + for(const category of categories){ + const section=document.createElement('section'),title=document.createElement('h3'),list=document.createElement('div'); + section.className=`store-section store-${category.cursor?'cursors':'items'}-section`;title.className='store-section-title';title.textContent=category.title; + list.className=`store-section-list ${category.cursor?'store-cursor-list':'store-item-list'}`; + for(const item of category.items){ + const purchased=onlinePlayerEconomy()?Boolean(playerPurchaseForStore(meta.id,item.id)):store.purchases.some(purchase=>purchase.id===item.id),price=storeItemPrice(meta,store,item), + card=document.createElement('article'),icon=document.createElement('div'),buy=document.createElement('button'); + card.className=`store-item ${category.cursor?'store-cursor':'store-other'}${purchased?' purchased':''}`; + icon.className='store-item-icon';setItemIcon(icon,item); + buy.type='button';buy.className='store-buy'; + buy.textContent=purchased?'\u8cfc\u5165\u6e08\u307f':`購入 ${formatScore(price)}`; + buy.disabled=purchased||data.scorepurchaseStoreItem(meta.id,item.id)); + if(category.cursor)card.append(icon,buy); + else{ + const copy=document.createElement('div'),name=document.createElement('h4'),effect=document.createElement('strong'),description=document.createElement('p'); + copy.className='store-item-copy';name.textContent=item.name;effect.textContent=item.effectLabel;description.textContent=item.description; + copy.append(name,effect,description);card.append(icon,copy,buy); + } + list.append(card); + } + section.append(title,list);storeInventory.append(section); + } +} +function openStoreMeta(meta){ + if(meta&&!meta.puzzle){void hydrateMeta(meta).then(()=>openStoreMeta(meta)).catch(error=>{console.warn('BEND FIELD: shop hydration failed',error);toast('\u30b7\u30e7\u30c3\u30d7\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002')});return true} + const store=meta&&metaState(meta.id).store;if(!store)return false; + if(modal.classList.contains('show'))closeHelp(false); + if(inventoryModal.classList.contains('show'))closeInventory(false); + if(timeAttackModal.classList.contains('show'))closeTimeAttack(false); + if(settingsModal.classList.contains('show'))closeSettings(false); + openStoreBoardId=meta.id;renderStorePanel();openDialogRoot(storeModal);requestAnimationFrame(()=>storePanel.focus());return true; +} +function openStore(board){return openStoreMeta(board?.meta)} +function closeStore(restoreFocus=true){ + const board=rendered.get(openStoreBoardId); + closeDialogRoot(storeModal,restoreFocus&&board?.storeButton?.isConnected?board.storeButton:viewport);openStoreBoardId=null; +} +async function purchaseStoreItem(boardId,itemId){ + const item=storeItem(itemId);if(!item)return false; + pruneAndCount(); + const meta=data.metas[boardId],st=meta?metaState(boardId):null,store=st?.store;if(!store)return false; + if(!storeInventoryItems(meta,store).some(available=>available.id===item.id)){toast('この店では取り扱っていません。');return false} + const already=onlinePlayerEconomy()?playerPurchaseForStore(boardId,itemId):store.purchases.find(purchase=>purchase.id===item.id);if(already){toast('購入済みです。');return false} + const price=storeItemPrice(meta,store,item);if(data.score{ + if(settingsModal.classList.contains('show')){ + if(e.key==='Escape'){e.preventDefault();closeSettings();return} + if(e.key==='Enter'&&document.activeElement===settingsPlayerName){e.preventDefault();void saveSettings();return} + trapDialogFocus(settingsPanel,e);return; + } + if(inventoryModal.classList.contains('show')){ + if(e.key==='Escape'){e.preventDefault();closeInventory();return} + trapDialogFocus(inventoryPanel,e);return; + } + if(timeAttackModal.classList.contains('show')){ + if(e.key==='Escape'){e.preventDefault();closeTimeAttack();return} + trapDialogFocus(timeAttackPanel,e);return; + } + if(storeModal.classList.contains('show')){ + if(e.key==='Escape'){e.preventDefault();closeStore();return} + trapDialogFocus(storePanel,e);return; + } + if(modal.classList.contains('show')){ + if(e.key==='Escape'){e.preventDefault();closeHelp();return} + trapDialogFocus(helpPanel,e);return; + } + if(e.key!=='Escape')return; + const b=rendered.get(activeBoard); + if(b?.drawing){e.preventDefault();b.drawing=null;changed(b.id);renderBoard(b);updateSelectedProgress(b);queueMicrotask(reconcileDrawingPresentation);toast('\u7dda\u306e\u7de8\u96c6\u3092\u7d42\u4e86\u3057\u307e\u3057\u305f\u3002')} +}); +helpBtn.onclick=openHelp; +settingsBtn.onclick=openSettings; +playerNameBtn.onclick=openSettings; +saveSettingsBtn.onclick=()=>{void saveSettings()}; +closeSettingsBtn.onclick=()=>closeSettings(); +settingsModal.addEventListener('pointerdown',e=>{if(e.target===settingsModal)closeSettings()}); +inventoryBtn.onclick=openInventory; +document.querySelector('#closeInventory').onclick=()=>closeInventory(); +inventoryModal.addEventListener('pointerdown',e=>{if(e.target===inventoryModal)closeInventory()}); +if(debugAllItemsToggle)debugAllItemsToggle.addEventListener('change',()=>{data.debugAllItems=debugAllItemsToggle.checked;markGlobalDirty();updateHud();void save(true);toast(data.debugAllItems?'デバッグON · 全アイテムを使用できます。':'デバッグOFF · 通常所持数へ戻りました。')}); +document.querySelector('#acceptTimeAttackSuggestion').onclick=()=>{dismissTimeAttackSuggestion();openTimeAttack()}; +document.querySelector('#dismissTimeAttackSuggestion').onclick=dismissTimeAttackSuggestion; +timeAttackBtn.onclick=openTimeAttack; +for(const button of timeAttackModal.querySelectorAll('[data-time-minutes]'))button.addEventListener('click',()=>startTimeAttack(Number(button.dataset.timeMinutes))); +copyTimeAttackBtn.onclick=copyTimeAttackResult; +closeTimeAttackBtn.onclick=()=>closeTimeAttack(); +timeAttackModal.addEventListener('pointerdown',e=>{if(e.target===timeAttackModal)closeTimeAttack()}); +document.querySelector('#normalPlayBtn').onclick=chooseNormalPlay; +document.querySelector('#helpTimeAttackBtn').onclick=chooseTimeAttack; +modal.addEventListener('pointerdown',e=>{if(e.target===modal&&!entryChoicePending)closeHelp()}); +document.querySelector('#closeStore').onclick=()=>closeStore(); +storeModal.addEventListener('pointerdown',e=>{if(e.target===storeModal)closeStore()}); +document.querySelector('#minimapOriginBtn').onclick=()=>centerOrigin(); +document.querySelector('#minimapRandomBtn').onclick=()=>{void centerRandomBoard()}; +function resetSelectedBoard(selected=rendered.get(hudBoardId)){ + const b=selected;if(!b)return false;const st=metaState(b.id); + const hasSpecialProgress=!!st.specialProgress?.crossings?.length; + if(st.solved||!st.paths.length&&!hasSpecialProgress){toast(st.solved?'\u30af\u30ea\u30a2\u6e08\u307f':'\u7dda\u304c\u3042\u308a\u307e\u305b\u3093\u3002');return false} + const performReset=()=>{if(typeof touchBoardClaim==='function')touchBoardClaim(b.id,true);const pointerId=b.drawing?.pointerId;cancelBoardDragFrame(b);if(pointerId!=null)try{if(b.svg.hasPointerCapture?.(pointerId))b.svg.releasePointerCapture(pointerId)}catch(_){} + applyBoardCommand(b,state=>{b.drawing=null;b.armedGate=null;b.solvedPathsRendered=false;state.paths=[];state.specialProgress={crossings:[]}},{persist:true,paint:false}); + renderBoardNow(b);updateSelectedProgress(b);queueMicrotask(reconcileDrawingPresentation);toast('\u76e4\u9762\u3092\u30ea\u30bb\u30c3\u30c8\u3057\u307e\u3057\u305f\u3002');return true}; + if(typeof cloudApiEnabled==='undefined'||typeof cloudAvailable==='undefined'||!cloudApiEnabled||!cloudAvailable)return performReset(); + return ensureBoardClaimForInput(b).then(ok=>ok?performReset():false); +} +world.addEventListener('click',event=>{ + const control=event.target.closest?.('.board-reset,.line-store');if(!control)return; + const card=control.closest('.board-card'),board=card?rendered.get(card.dataset.id):null;if(!board)return; + event.preventDefault();event.stopPropagation();selectBoard(board); + if(control.classList.contains('board-reset')){playSound('reset');void resetSelectedBoard(board)} + else{playSound('click');openStore(board)} +}); +saveStatusEl.onclick=async()=>{if(await flushSave())toast('\u4fdd\u5b58\u3057\u307e\u3057\u305f')}; +let activeArchiveController=null,fieldArchiveBusy=false; +const dismissStatusBtn=document.querySelector('#dismissStatusBtn'),exportBtn=document.querySelector('#exportBtn'); +function setFieldArchiveBusy(busy,message=''){ + fieldArchiveBusy=busy;document.body.classList.toggle('archive-busy',busy); + for(const button of[exportBtn,document.querySelector('#importBtn'),document.querySelector('#freshBtn')])if(button)button.disabled=busy; + if(dismissStatusBtn)dismissStatusBtn.textContent=busy?'\u30ad\u30e3\u30f3\u30bb\u30eb':'\u9589\u3058\u308b'; + if(message)showStatus(message,{retry:false,fresh:false}); +} +dismissStatusBtn.onclick=()=>{if(activeArchiveController)activeArchiveController.abort();else hideStatus()}; +document.addEventListener('keydown',event=>{if(!fieldArchiveBusy)return;if(event.key==='Escape'&&activeArchiveController)activeArchiveController.abort();if(event.key!=='Tab'){event.preventDefault();event.stopImmediatePropagation()}},true); +function parseJsonOrRaw(text){if(!text)return null;try{return JSON.parse(text)}catch(_){return{raw:text,parseError:true}}} +function portableGlobalForArchive(source=data){ + return{ + gameplayVersion:GAMEPLAY_DATA_VERSION, + bonusEvents:deepClone(source.bonusEvents||{}), + specialMechanicsSeen:[...new Set(source.specialMechanicsSeen||[])].filter(type=>['warp','lock','crossing'].includes(type)).sort(), + clockFloor:Math.max(0,Number(source.clockFloor)||0), + lastSolveAt:Math.max(0,Number(source.lastSolveAt)||0), + timeAttack:deepClone(source.timeAttack), + timeAttackCooldowns:deepClone(source.timeAttackCooldowns), + lastTimeAttack:deepClone(source.lastTimeAttack), + timeAttackSuggestionsDisabled:source.timeAttackSuggestionsDisabled===true, + cursorStyle:typeof source.cursorStyle==='string'?source.cursorStyle.slice(0,32):'default', + scoreLensEnabled:source.scoreLensEnabled===true + }; +} +function portableMetaForArchive(meta){ + const value=metaForStorage(meta);delete value.rev;delete value.revAuthor;return value; +} +function portableStateForArchive(state){ + const value=stateForStorage(state);delete value.rev;delete value.revAuthor;return value; +} +async function loadV2ArchiveBoardPage(epoch,afterNumber=-1,limit=32){ + const db=await openWorldDb(),tx=db.transaction('boardIndex','readonly'),done=transactionDone(tx), + range=IDBKeyRange.bound([epoch,Math.max(0,afterNumber+1)],[epoch,Number.MAX_SAFE_INTEGER]), + indexes=await requestValue(tx.objectStore('boardIndex').index('epochNumber').getAll(range,limit));await done; + if(!indexes.length)return[]; + const detailsTx=db.transaction(['boardPuzzles','boardStates'],'readonly'),detailsDone=transactionDone(detailsTx), + puzzleStore=detailsTx.objectStore('boardPuzzles'),stateStore=detailsTx.objectStore('boardStates'), + [puzzles,states]=await Promise.all([Promise.all(indexes.map(index=>requestValue(puzzleStore.get([epoch,index.id])))),Promise.all(indexes.map(index=>requestValue(stateStore.get([epoch,index.id]))))]);await detailsDone; + return indexes.map((index,position)=>{ + const puzzle=puzzles[position],state=states[position];if(!puzzle||!state||puzzle.metaRev!==index.metaRev||state.stateRev!==index.stateRev)throw new Error(`Stored board revisions do not match for ${index.id}.`); + const meta={id:index.id,x:index.x,y:index.y,chunks:index.chunks,level:index.level,targetLevel:index.targetLevel,seed:index.seed,axis:index.axis,entrySide:index.entrySide||null,sealedSides:[...(index.sealedSides||[])],puzzle:puzzle.puzzle,generatorVersion:puzzle.generatorVersion||GENERATOR_VERSION,rev:puzzle.metaRev,revAuthor:puzzle.revAuthor||index.metaRevAuthor||''}; + return{id:index.id,number:index.number,meta:portableMetaForArchive(meta),state:portableStateForArchive(state.value)}; + }); +} +async function* portableBoardsForExport(epoch){ + if(activeStorageFormat===FIELD_STORAGE_FORMAT){ + let after=-1; + while(true){const page=await loadV2ArchiveBoardPage(epoch,after);if(!page.length)break;for(const board of page){after=board.number;yield{id:board.id,meta:board.meta,state:board.state}}} + return; + } + for(const id of Object.keys(data.metas).sort((a,b)=>(Number(a.slice(1))||0)-(Number(b.slice(1))||0)))yield{id,meta:portableMetaForArchive(data.metas[id]),state:portableStateForArchive(data.states[id]||normalizeState(null))}; +} +async function activeWorldArchiveHeader(epoch){ + const db=await openWorldDb(),tx=db.transaction(['control','worlds'],'readonly'),done=transactionDone(tx),controlStore=tx.objectStore('control'),worldsStore=tx.objectStore('worlds'),[control,world]=await Promise.all([requestValue(controlStore.get('active')),requestValue(worldsStore.get(epoch))]);await done; + if(!control||control.activeFormat!==FIELD_STORAGE_FORMAT||control.activeEpoch!==epoch||!world||world.status!=='active')throw staleWorldEpochError();return world; +} +async function downloadSaveExport(destination){ + const controller=new AbortController();activeArchiveController=controller;setFieldArchiveBusy(true,'\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u66f8\u304d\u51fa\u3057\u3066\u3044\u307e\u3059\u3002'); + try{ + if(!fieldIndexComplete)await completeV2IndexScan(); + if(!await flushSave())throw new Error('The field could not be checkpointed before export.'); + return await withWorldMutationLock(async()=>{ + if(hasPendingPersistence())await persistDirtyToDb({skipCloud:true}); + const epoch=data.worldEpoch,world=await activeWorldArchiveHeader(epoch),boardCount=world.boardCount||Object.keys(data.metas).length,portableGlobal=portableGlobalForArchive(),compression=typeof CompressionStream==='function'?'gzip':'identity',estimatedRawBytes=Math.max(boardCount*1024,(world.approximateBytes||0)+JSON.stringify(portableGlobal).length+boardCount*256), + manifest={saveSchema:SAVE_SCHEMA,gameplayVersion:GAMEPLAY_DATA_VERSION,worldGeneration:WORLD_GENERATION,appVersion:APP_VERSION,generatorVersion:GENERATOR_VERSION,exportedAt:new Date().toISOString(),boardCount,estimatedRawBytes,encoding:'ndjson',compression}, + result=await FieldPersistence.writeArchive({writable:destination.writable,manifest,globalState:portableGlobal,boards:portableBoardsForExport(epoch),signal:controller.signal,onProgress:progress=>setSaveStatus('saving',`${progress.boardsDone}/${progress.boardsTotal}`)}); + await destination.finish();setSaveStatus('saved','\u66f8\u304d\u51fa\u3057\u5b8c\u4e86');toast('\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u66f8\u304d\u51fa\u3057\u307e\u3057\u305f\u3002');return result; + }); + }catch(error){ + await destination.abort(error);if(error?.name==='AbortError')toast('\u66f8\u304d\u51fa\u3057\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u307e\u3057\u305f\u3002');else throw error; + }finally{activeArchiveController=null;setFieldArchiveBusy(false);hideStatus()} +} +exportBtn.onclick=async()=>{ + const filename=`bend-field-save-${new Date().toISOString().slice(0,10)}.bfsave`; + try{ + const destination=await FieldPersistence.createArchiveDestination(filename,Math.max(1024,Object.keys(data.metas).length*1024)); + await downloadSaveExport(destination); + }catch(error){if(error?.name!=='AbortError'){showStatus(`\u66f8\u304d\u51fa\u3057\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 ${error?.message||error}`,{retry:false,fresh:false});toast('\u66f8\u304d\u51fa\u3057\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002')}} +}; +async function beginWorldReplacement(reason){ + if(!await flushSave())throw new Error('The current field could not be checkpointed.'); + lifecyclePersistenceSuppressed=true;clearTimeout(saveTimer);saveTimer=null;cancelMirrorCheckpoint(true);await persistQueue; + if(activeStorageFormat!==FIELD_STORAGE_FORMAT)await preserveRecoveryDurably(JSON.stringify(compactSnapshot()),reason); + return createWorldEpoch(); +} +document.querySelector('#freshBtn').onclick=async()=>{ + if(!confirm('\u73fe\u5728\u306e\u9032\u884c\u3092\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3057\u3001\u6700\u521d\u304b\u3089\u59cb\u3081\u307e\u3059\u304b\uff1f'))return; + try{const newEpoch=await beginWorldReplacement('User started fresh');await clearDatabaseWorld(newEpoch);clearCompactMirror();clearRecoveryJournalKeys();safeLocalRemove(storageRevisionKey);announceWorldReplacement(newEpoch);location.reload()} + catch(error){lifecyclePersistenceSuppressed=false;showStatus(`\u65b0\u3057\u3044\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u958b\u59cb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 ${error?.message||error}`,{retry:true,fresh:true})} +}; +function validateImportedStateStrict(meta,raw){ + if(!isPlainObject(raw)||!Array.isArray(raw.paths)||raw.paths.length>MAX_PATHS_PER_BOARD)throw new Error(`Invalid board state: ${meta.id}`); + const state=normalizeState(raw);if(state.paths.length!==raw.paths.length)throw new Error(`Invalid path record: ${meta.id}`); + const puzzle=meta.puzzle,validCells=cellSet(puzzle),crossingSet=new Set(crossingKeys(puzzle)),usedCells=new Map(),usedGates=new Set(); + for(const path of state.paths){ + let valid=path.startGate>=0&&path.startGate=0&&path.endGate0&&!crossingSet.has(key))||owners>=2||(index>0&&!pathCellsAdjacent(puzzle,path.cells[index-1],cell))){valid=false;break} + ownCells.add(key); + } + if(valid&&(!path.detachedStart&&usedGates.has(path.startGate)||path.endGate!=null&&usedGates.has(path.endGate)||path.openGate!=null&&usedGates.has(path.openGate)))valid=false; + if(!valid)throw new Error(`Invalid path geometry: ${meta.id}`); + for(const key of ownCells)usedCells.set(key,(usedCells.get(key)||0)+1); + if(!path.detachedStart)usedGates.add(path.startGate);if(path.endGate!=null)usedGates.add(path.endGate);if(path.openGate!=null)usedGates.add(path.openGate); + } + const solved=isSolved(state,puzzle);if(state.solved!==solved)throw new Error(`Solved state does not match the puzzle: ${meta.id}`); + if(!solved&&(state.solvedBy||state.scoreAwarded||state.store))throw new Error(`Unsolved board contains solved-only data: ${meta.id}`); + return state; +} +async function deleteV2Epoch(epoch){ + if(!idbAvailable||!validWorldEpoch(epoch))return false; + const db=await openWorldDb(),stores=['boardIndex','boardPuzzles','boardStates','tombstonesV2','recoveryV2','outboxV2'],tx=db.transaction(['worlds',...stores],'readwrite'),done=transactionDone(tx),range=epochKeyRange(epoch); + tx.objectStore('worlds').delete(epoch);for(const name of stores)tx.objectStore(name).delete(range);await done;return true; +} +async function verifyActiveWorldActivation(){ + if(!idbAvailable||activeStorageFormat!==FIELD_STORAGE_FORMAT)return false; + const db=await openWorldDb(),control=await readActiveWorldControl(db); + if(!control||control.activationVerified!==false||control.activeEpoch!==data.worldEpoch)return false; + await validateActiveWorldReadiness(control.activeEpoch); + const tx=db.transaction(['control','worlds'],'readwrite'),done=transactionDone(tx),controlStore=tx.objectStore('control'),worldsStore=tx.objectStore('worlds'), + [fresh,active,previous]=await Promise.all([requestValue(controlStore.get('active')),requestValue(worldsStore.get(control.activeEpoch)),control.previousEpoch?requestValue(worldsStore.get(control.previousEpoch)):Promise.resolve(null)]); + if(!fresh||fresh.activeEpoch!==control.activeEpoch||fresh.activationVerified!==false||!active){try{tx.abort()}catch(_){}await done.catch(()=>{});throw staleWorldEpochError()} + if(previous){previous.status='garbage';previous.progress={phase:'cleanup',store:'boardIndex',rows:0};worldsStore.put(previous)} + active.status='active';worldsStore.put(active); + const verified={...fresh,previousEpoch:undefined,activationVerified:true};controlStore.put(verified);await done;startupWorldControl=verified;return true; +} + +function stableJson(value){if(Array.isArray(value))return`[${value.map(stableJson).join(',')}]`;if(value&&typeof value==='object')return`{${Object.keys(value).sort().map(key=>`${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;return JSON.stringify(value)} +function boardIndexMatchesDetails(index,meta,state,epoch){ + const expected=summarizeBoardV2(meta,state,epoch),keys=['epoch','id','number','x','y','chunks','level','targetLevel','seed','axis','entrySide','metaRev','stateRev','revAuthor','solved','expanded','scoreAwarded','hasProgress','specialFlags','shop']; + return keys.every(key=>stableJson(index?.[key])===stableJson(expected[key])); +} +async function validateStagedWorldV2(epoch,{expectedBoardCount=null,strict=true,allowStaging=false}={}){ + if(!validWorldEpoch(epoch))throw new Error('The staged field epoch is invalid.'); + const db=await openWorldDb();let after=-1,boardCount=0,solvedCount=0,boardScore=0,maxNumber=0,approximateBytes=0,bounds=null,originOwnedByB0=false;const occupied=new Set(); + while(true){ + const tx=db.transaction(['boardIndex','boardPuzzles','boardStates'],'readonly'),done=transactionDone(tx),indexStore=tx.objectStore('boardIndex'),puzzleStore=tx.objectStore('boardPuzzles'),stateStore=tx.objectStore('boardStates'), + indexes=await requestValue(indexStore.index('epochNumber').getAll(IDBKeyRange.bound([epoch,Math.max(0,after+1)],[epoch,Number.MAX_SAFE_INTEGER]),512)); + if(!indexes.length){await done;break} + const [puzzles,states]=await Promise.all([Promise.all(indexes.map(index=>requestValue(puzzleStore.get([epoch,index.id])))),Promise.all(indexes.map(index=>requestValue(stateStore.get([epoch,index.id]))))]);await done; + for(let position=0;position0&&position%32===0)await new Promise(resolve=>setTimeout(resolve,0)); + } + await new Promise(resolve=>setTimeout(resolve,0)); + } + if(boardCount<1||!occupied.size||!bounds)throw new Error('The staged field is empty.'); + if(!originOwnedByB0||after<0)throw new Error('B0 does not own the staged field origin.'); + const countTx=db.transaction(['worlds','boardIndex','boardPuzzles','boardStates'],'readonly'),countDone=transactionDone(countTx),worldsStore=countTx.objectStore('worlds'), + [world,indexCount,puzzleCount,stateCount]=await Promise.all([requestValue(worldsStore.get(epoch)),requestValue(countTx.objectStore('boardIndex').index('epoch').count(IDBKeyRange.only(epoch))),requestValue(countTx.objectStore('boardPuzzles').index('epoch').count(IDBKeyRange.only(epoch))),requestValue(countTx.objectStore('boardStates').index('epoch').count(IDBKeyRange.only(epoch)))]);await countDone; + const allowedStatus=allowStaging?world?.status==='staging'||world?.status==='ready':world?.status==='ready'; + if(!world||!allowedStatus||indexCount!==boardCount||puzzleCount!==boardCount||stateCount!==boardCount)throw new Error('The staged field row counts do not match.'); + if(expectedBoardCount!=null&&boardCount!==expectedBoardCount)throw new Error('The staged field board count changed during validation.'); + const expectedScore=Math.min(MAX_SCORE,boardScore+bonusEventTotal(world.global?.bonusEvents||{})); + if((world.global?.nextId||0)!==maxNumber+1||(world.global?.solved||0)!==solvedCount||(world.global?.score||0)!==expectedScore)throw new Error('The staged global aggregates do not match the board rows.'); + return{boardCount,solvedCount,score:expectedScore,boardScore,nextId:maxNumber+1,bounds,approximateBytes}; +} +async function validateActiveWorldReadiness(epoch){ + const db=await openWorldDb(),tx=db.transaction(['control','worlds','boardIndex','boardPuzzles','boardStates'],'readonly'),done=transactionDone(tx),controlStore=tx.objectStore('control'),worldsStore=tx.objectStore('worlds'),indexStore=tx.objectStore('boardIndex'), + [control,world,indexes,b0Index,b0Puzzle,b0State]=await Promise.all([requestValue(controlStore.get('active')),requestValue(worldsStore.get(epoch)),requestValue(indexStore.index('epochNumber').getAll(IDBKeyRange.bound([epoch,0],[epoch,Number.MAX_SAFE_INTEGER]),512)),requestValue(indexStore.get([epoch,'B0'])),requestValue(tx.objectStore('boardPuzzles').get([epoch,'B0'])),requestValue(tx.objectStore('boardStates').get([epoch,'B0']))]);await done; + if(!control||control.activeEpoch!==epoch||control.activeFormat!==FIELD_STORAGE_FORMAT||!world||world.status!=='active'||!indexes.length||indexes[0].id!=='B0')throw new Error('The activated field header or first index page is invalid.'); + const occupied=new Set();for(const index of indexes){if(!/^B(?:0|[1-9]\d*)$/.test(index.id)||!validChunkShape(index.chunks))throw new Error(`The activated field index is invalid: ${index.id}`);for(const[dx,dy]of index.chunks){const key=key2(index.x+dx,index.y+dy);if(occupied.has(key))throw new Error('The activated field first index page overlaps.');occupied.add(key)}} + if(!b0Index||!b0Puzzle||!b0State||b0Index.metaRev!==b0Puzzle.metaRev||b0Index.stateRev!==b0State.stateRev)throw new Error('The activated field failed its B0 revision check.'); + const meta=normalizeMeta('B0',{id:'B0',x:b0Index.x,y:b0Index.y,chunks:b0Index.chunks,level:b0Index.level,targetLevel:b0Index.targetLevel,seed:b0Index.seed,axis:b0Index.axis,entrySide:b0Index.entrySide||null,sealedSides:b0Index.sealedSides||[],puzzle:b0Puzzle.puzzle,generatorVersion:b0Puzzle.generatorVersion||GENERATOR_VERSION,rev:b0Puzzle.metaRev,revAuthor:b0Puzzle.revAuthor||b0Index.metaRevAuthor||''}); + if(!meta||!meta.chunks.some(([dx,dy])=>meta.x+dx===0&&meta.y+dy===0))throw new Error('The activated field contains an invalid B0 puzzle or origin.');validateImportedStateStrict(meta,b0State.value);return true; +} +async function deleteV2EpochBatch(epoch,limit=GC_BATCH_ROWS){ + const stores=['boardIndex','boardPuzzles','boardStates','tombstonesV2','recoveryV2','outboxV2'],db=await openWorldDb(),worldTx=db.transaction('worlds','readonly'),worldDone=transactionDone(worldTx),world=await requestValue(worldTx.objectStore('worlds').get(epoch));await worldDone;if(!world)return{done:true,rows:0}; + let storeIndex=Math.max(0,stores.indexOf(world.progress?.store));if(storeIndex<0)storeIndex=0; + for(;storeIndexString(row?.key||'').startsWith('pin:')&&validWorldEpoch(row?.value?.epoch||row?.value?.worldEpoch)).map(row=>row.value.epoch||row.value.worldEpoch)),cutoff=trustedNow()-24*60*60*1000, + target=worlds.find(world=>world.epoch!==control?.activeEpoch&&world.epoch!==control?.previousEpoch&&!pinned.has(world.epoch)&&(world.status==='garbage'||(world.status==='staging'||world.status==='ready')&&(world.createdAt||0){}); + if(!target)return 0;const result=await deleteV2EpochBatch(target.epoch,GC_BATCH_ROWS);if(result.rows)perfCount('fieldEpochRowsCollected',result.rows);if(result.done)perfCount('fieldEpochsCollected'); + setTimeout(()=>{void collectV2Garbage().catch(error=>console.warn('BEND FIELD: epoch cleanup deferred',error))},0);return result.rows; +} +async function writeStagedBoardBatch(epoch,batch){ + if(!batch.length)return; + const db=await openWorldDb(),tx=db.transaction(['boardIndex','boardPuzzles','boardStates'],'readwrite'),done=transactionDone(tx), + indexStore=tx.objectStore('boardIndex'),puzzleStore=tx.objectStore('boardPuzzles'),stateStore=tx.objectStore('boardStates'); + for(const{meta,state}of batch){indexStore.put(summarizeBoardV2(meta,state,epoch));puzzleStore.put(puzzleRecordV2(meta,epoch));stateStore.put(stateRecordV2(meta.id,state,epoch))} + await done; +} +function importedGlobalFromPortable(raw,epoch,{nextId,solved,boardScore}){ + const result=defaultData();result.worldEpoch=epoch;result.gameplayVersion=GAMEPLAY_DATA_VERSION; + result.bonusEvents=normalizeBonusEvents(raw?.bonusEvents);result.bonusScore=bonusEventTotal(result.bonusEvents);result.bonusScoreVersion=SCORE_VERSION; + result.specialMechanicsSeen=[...new Set(raw?.specialMechanicsSeen||[])].filter(type=>['warp','lock','crossing'].includes(type)).sort(); + result.clockFloor=Math.max(0,Number(raw?.clockFloor)||0);result.lastSolveAt=Math.max(0,Number(raw?.lastSolveAt)||0); + result.timeAttack=normalizeTimeAttackRun(raw?.timeAttack);result.timeAttackCooldowns=normalizeTimeAttackCooldowns(raw?.timeAttackCooldowns);result.lastTimeAttack=normalizeTimeAttackResult(raw?.lastTimeAttack); + result.timeAttackSuggestionsDisabled=raw?.timeAttackSuggestionsDisabled===true;result.cursorStyle=typeof raw?.cursorStyle==='string'?raw.cursorStyle.slice(0,32):'default';result.scoreLensEnabled=raw?.scoreLensEnabled===true; + result.nextId=nextId;result.solved=solved;result.score=Math.min(MAX_SCORE,boardScore+result.bonusScore);result.globalRev=nextRevision();result.globalRevAuthor=sessionId; + result.cloudProfile=null;result.cloudRevision=0;result.cloudSyncPaused=true;result.cloudPending=normalizeCloudPending(null);result.updatedAt=trustedNow();return result; +} +async function activeWorldExpectation(){ + const db=await openWorldDb(),control=await readActiveWorldControl(db); + if(!control||!validWorldEpoch(control.activeEpoch))throw new Error('The active field control record is missing.'); + const tx=db.transaction('worlds','readonly'),done=transactionDone(tx),world=await requestValue(tx.objectStore('worlds').get(control.activeEpoch));await done; + return{epoch:control.activeEpoch,globalRev:world?.global?.globalRev||0,format:control.activeFormat}; +} +async function preflightArchiveStorage(estimatedRawBytes){ + if(!navigator.storage?.estimate)return true; + const estimate=await navigator.storage.estimate(),required=Math.max(32*1024*1024,Math.max(0,Number(estimatedRawBytes)||0)*2+32*1024*1024), + available=Math.max(0,(estimate.quota||0)-(estimate.usage||0)); + if(estimate.quota&&available{if(!batch.length)return;await writeStagedBoardBatch(epoch,batch);const tx=db.transaction('worlds','readwrite'),done=transactionDone(tx),row=await requestValue(tx.objectStore('worlds').get(epoch));tx.objectStore('worlds').put({...row,progress:{phase:'stage',lastKey:batch[batch.length-1].meta.id,rows:boardCount}});await done;batch.length=0;batchBytes=0}; + try{ + const result=await FieldPersistence.readArchive(file,{ + signal:controller.signal,onProgress:progress=>{setSaveStatus('saving',`${progress.boardsDone}/${progress.boardsTotal}`);showStatus(`フィールドを検証中 ${progress.boardsDone}/${progress.boardsTotal}`,{retry:false,fresh:false})}, + onManifest:async value=>{manifest=assertCurrentArchive(value);if(value.boardCount<1||value.boardCount>MAX_BOARDS)throw new Error('The archive board count is outside the supported range.');await preflightArchiveStorage(value.estimatedRawBytes)}, + onGlobal:value=>{globalRaw=value}, + onBoard:async record=>{ + if(!isPlainObject(record.meta)||!isPlainObject(record.state)||record.meta.id&&record.meta.id!==record.id)throw new Error(`Invalid board record: ${record.id}`); + const meta=normalizeMeta(record.id,{...record.meta,id:record.id,rev:0,revAuthor:''});if(!meta)throw new Error(`Invalid puzzle metadata: ${record.id}`); + if(stableJson(portableMetaForArchive(meta))!==stableJson(record.meta))throw new Error(`The board metadata is not canonical: ${record.id}`); + for(const[dx,dy]of meta.chunks){const x=meta.x+dx,y=meta.y+dy,key=key2(x,y);if(occupied.has(key))throw new Error(`Overlapping board geometry: ${record.id}`);occupied.add(key);if(!boundsReady){minX=x;minY=y;maxX=x+1;maxY=y+1;boundsReady=true}else{minX=Math.min(minX,x);minY=Math.min(minY,y);maxX=Math.max(maxX,x+1);maxY=Math.max(maxY,y+1)}} + const state=validateImportedStateStrict(meta,{...record.state,rev:0,revAuthor:''});if(stableJson(portableStateForArchive(state))!==stableJson(record.state))throw new Error(`The board state is not canonical: ${record.id}`); + const revision=Math.min(Number.MAX_SAFE_INTEGER-2,Date.now()*1000+boardCount*2);meta.rev=revision;meta.revAuthor=sessionId;state.rev=revision+1;state.revAuthor=sessionId; + boardCount++;maxNumber=Math.max(maxNumber,Number(record.id.slice(1))||0);if(state.solved){solved++;boardScore=Math.min(MAX_SCORE,boardScore+state.scoreAwarded)} + const bytes=JSON.stringify(record).length;approximateBytes+=bytes;batchBytes+=bytes;batch.push({meta,state});if(batch.length>=batchLimit||batchBytes>=4*1024*1024)await flush();else if(boardCount%32===0)await new Promise(resolve=>setTimeout(resolve,0)); + } + }); + await flush();if(!manifest||!globalRaw||boardCount!==manifest.boardCount||!occupied.size)throw new Error('The archive did not produce a complete field.'); + const imported=importedGlobalFromPortable(globalRaw,epoch,{nextId:maxNumber+1,solved,boardScore});if(stableJson(portableGlobalForArchive(imported))!==stableJson(globalRaw))throw new Error('The archive global record is not canonical.'); + const provisional={epoch,status:'staging',schema:SAVE_SCHEMA,worldGeneration:WORLD_GENERATION,global:globalForStorage(imported),boardCount,solvedCount:solved,score:imported.score,bounds:{minX,minY,maxX,maxY},approximateBytes:result.decodedBytes||approximateBytes,createdAt:trustedNow(),source:{kind:'import',archiveCrc32:result.footer.crc32},progress:{phase:'verify',rows:boardCount}}, + tx=db.transaction('worlds','readwrite'),done=transactionDone(tx);tx.objectStore('worlds').put(provisional);await done; + const verified=await validateStagedWorldV2(epoch,{expectedBoardCount:manifest.boardCount,strict:true,allowStaging:true});await verifyStagedStorageEstimate();const world={...provisional,status:'ready',boardCount:verified.boardCount,solvedCount:verified.solvedCount,score:verified.score,bounds:verified.bounds,approximateBytes:verified.approximateBytes,progress:null}; + const readyTx=db.transaction('worlds','readwrite'),readyDone=transactionDone(readyTx);readyTx.objectStore('worlds').put(world);await readyDone;return{epoch,expected,world,manifest}; + }catch(error){await deleteV2Epoch(epoch).catch(()=>{});throw error} +} +async function stageRecoverySnapshotV2(snapshot,expected,controller){ + const epoch=createWorldEpoch(),ids=Object.keys(snapshot.metas).sort((a,b)=>(Number(a.slice(1))||0)-(Number(b.slice(1))||0));await preflightArchiveStorage(Math.max(1,ids.length)*1024); + const db=await openWorldDb(),portableGlobal=portableGlobalForArchive(snapshot),occupied=new Set();let solved=0,boardScore=0,maxNumber=0,approximateBytes=0; + try{ + const initialTx=db.transaction('worlds','readwrite'),initialDone=transactionDone(initialTx);initialTx.objectStore('worlds').put({epoch,status:'staging',schema:SAVE_SCHEMA,worldGeneration:WORLD_GENERATION,global:null,boardCount:0,createdAt:trustedNow(),source:{kind:'import'},progress:{phase:'stage',rows:0}});await initialDone; + for(let offset=0;offset{const meta=normalizeMeta(id,{...metaForStorage(snapshot.metas[id]),rev:0,revAuthor:''});if(!meta)throw new Error(`Invalid recovery board: ${id}`);for(const[dx,dy]of meta.chunks){const key=key2(meta.x+dx,meta.y+dy);if(occupied.has(key))throw new Error(`Overlapping recovery board: ${id}`);occupied.add(key)}const state=normalizeState(stateForStorage(snapshot.states[id]||normalizeState(null))),revision=Math.min(Number.MAX_SAFE_INTEGER-2,Date.now()*1000+(offset+position)*2);meta.rev=revision;meta.revAuthor=sessionId;state.rev=revision+1;state.revAuthor=sessionId;if(state.solved){solved++;boardScore=Math.min(MAX_SCORE,boardScore+state.scoreAwarded)}maxNumber=Math.max(maxNumber,Number(id.slice(1))||0);approximateBytes+=JSON.stringify(meta).length+JSON.stringify(state).length;return{meta,state}}); + await writeStagedBoardBatch(epoch,batch);const progressTx=db.transaction('worlds','readwrite'),progressDone=transactionDone(progressTx),row=await requestValue(progressTx.objectStore('worlds').get(epoch));progressTx.objectStore('worlds').put({...row,progress:{phase:'stage',lastKey:batch[batch.length-1]?.meta.id||null,rows:Math.min(ids.length,offset+batch.length)}});await progressDone;setSaveStatus('saving',`${Math.min(ids.length,offset+batch.length)}/${ids.length}`);await new Promise(resolve=>setTimeout(resolve,0)); + } + const imported=importedGlobalFromPortable(portableGlobal,epoch,{nextId:maxNumber+1,solved,boardScore}),provisional={epoch,status:'staging',schema:SAVE_SCHEMA,worldGeneration:WORLD_GENERATION,global:globalForStorage(imported),boardCount:ids.length,solvedCount:solved,score:imported.score,bounds:fieldBoundsFromMetas(snapshot.metas),approximateBytes,createdAt:trustedNow(),source:{kind:'import'},progress:{phase:'verify',rows:ids.length}}, + tx=db.transaction('worlds','readwrite'),done=transactionDone(tx);tx.objectStore('worlds').put(provisional);await done; + const verified=await validateStagedWorldV2(epoch,{expectedBoardCount:ids.length,strict:false,allowStaging:true});await verifyStagedStorageEstimate();const world={...provisional,status:'ready',boardCount:verified.boardCount,solvedCount:verified.solvedCount,score:verified.score,bounds:verified.bounds,approximateBytes:verified.approximateBytes,progress:null}; + const readyTx=db.transaction('worlds','readwrite'),readyDone=transactionDone(readyTx);readyTx.objectStore('worlds').put(world);await readyDone;return{epoch,expected,world,recovery:true}; + }catch(error){await deleteV2Epoch(epoch).catch(()=>{});throw error} +} +async function activateReadyWorldV2(staged,{kind='import'}={}){ + return withWorldMutationLock(async()=>{ + if(hasPendingPersistence())await persistDirtyToDb({skipCloud:true}); + const db=await openWorldDb(),tx=db.transaction(['control','worlds'],'readwrite'),done=transactionDone(tx),controlStore=tx.objectStore('control'),worldsStore=tx.objectStore('worlds'), + [control,currentWorld,readyWorld]=await Promise.all([requestValue(controlStore.get('active')),requestValue(worldsStore.get(staged.expected.epoch)),requestValue(worldsStore.get(staged.epoch))]); + const olderPrevious=control?.previousEpoch&&control.previousEpoch!==staged.expected.epoch?await requestValue(worldsStore.get(control.previousEpoch)):null; + if(!control||control.activeEpoch!==staged.expected.epoch||control.activeFormat!==FIELD_STORAGE_FORMAT||(currentWorld?.global?.globalRev||0)!==staged.expected.globalRev){try{tx.abort()}catch(_){}await done.catch(()=>{});throw new Error('The active field changed while the replacement was being validated. Please try again.')} + if(!readyWorld||readyWorld.status!=='ready'){try{tx.abort()}catch(_){}await done.catch(()=>{});throw new Error('The staged field is not ready to activate.')} + if(olderPrevious){olderPrevious.status='garbage';olderPrevious.progress={phase:'cleanup',store:'boardIndex',rows:0};worldsStore.put(olderPrevious)}if(currentWorld){currentWorld.status='rollback';currentWorld.progress=null;worldsStore.put(currentWorld)}readyWorld.status='active';readyWorld.activatedAt=trustedNow(); + const nextControl={key:'active',activeFormat:FIELD_STORAGE_FORMAT,activeEpoch:staged.epoch,previousEpoch:staged.expected.epoch,activationId:`${kind}:${sessionId}:${Date.now()}`,activationVerified:false,switchedAt:trustedNow()}; + worldsStore.put(readyWorld);controlStore.put(nextControl); + await done;return nextControl; + }); +} +async function activateStagedWorldV2(staged,{kind='import'}={}){ + let control;try{control=await activateReadyWorldV2(staged,{kind})}catch(error){await deleteV2Epoch(staged?.epoch).catch(()=>{});throw error}lifecyclePersistenceSuppressed=true;clearTimeout(saveTimer);saveTimer=null;cancelMirrorCheckpoint(true);clearCompactMirror();clearRecoveryJournalKeys();safeLocalRemove(recoveryStorageKey);try{sessionStorage.removeItem(recoveryStorageKey)}catch(_){}volatileRecovery=null;safeLocalRemove(storageRevisionKey);rememberWorldEpoch(control.activeEpoch);announceWorldReplacement(control.activeEpoch);location.reload();return true; +} +async function importSaveFile(file){ + if(!file)return false; + if(!idbAvailable)throw new Error('IndexedDB is required to restore a field.'); + const controller=new AbortController();activeArchiveController=controller;setFieldArchiveBusy(true,'\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u8aad\u307f\u8fbc\u3093\u3067\u3044\u307e\u3059\u3002'); + try{ + if(!await flushSave())throw new Error('The current field could not be checkpointed before import.'); + if(activeStorageFormat!==FIELD_STORAGE_FORMAT)throw new Error('現行の保存形式ではありません。'); + const expected=await activeWorldExpectation(),summary=assertCurrentArchive(await FieldPersistence.inspectArchive(file,{signal:controller.signal})); + if(!Number.isSafeInteger(summary.boardCount)||summary.boardCount<1||summary.boardCount>MAX_BOARDS)throw new Error('The archive board count is outside the supported range.'); + if(!confirm(`\u73fe\u5728\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3092${summary.boardCount}\u76e4\u9762\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3067\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b\uff1f`))return false; + const staged=await stageArchiveV2(file,controller,expected); + return await activateStagedWorldV2(staged); + }finally{if(!lifecyclePersistenceSuppressed){activeArchiveController=null;setFieldArchiveBusy(false)}} +} +const importFileEl=document.querySelector('#importFile'),importBtn=document.querySelector('#importBtn'); +if(importBtn&&importFileEl){ + importBtn.onclick=()=>{importFileEl.value='';importFileEl.click()}; + importFileEl.onchange=async()=>{try{await importSaveFile(importFileEl.files?.[0])}catch(error){showStatus(`\u4fdd\u5b58\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 ${error?.message||error}`,{retry:false,fresh:false});toast('\u4fdd\u5b58\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002')}}; +} +async function retryRecovery(){ + hideStatus();if(!puzzleWorker)createPuzzleWorker(); + try{ + const envelope=await readRecoveryEnvelope();if(!envelope?.raw)throw new Error('復元用バックアップがありません。'); + const parsed=JSON.parse(envelope.raw),candidate=parsed?.current??parsed,snapshot=normalizeSnapshot(candidate,{quiet:true}); + if(!Object.keys(snapshot.metas).length)throw new Error('復元用バックアップに有効な盤面がありません。'); + if(activeStorageFormat!==FIELD_STORAGE_FORMAT)throw new Error('現行の保存形式ではありません。'); + const expected=await activeWorldExpectation(),controller=new AbortController(),staged=await stageRecoverySnapshotV2(snapshot,expected,controller); + await activateStagedWorldV2(staged,{kind:'recovery'}); + } + catch(error){console.warn('BEND FIELD: recovery retry failed',error);showStatus(`保存データを復元できませんでした。 ${error?.message||error}`,{retry:true,fresh:false})} +} +document.querySelector('#retryBtn').onclick=retryRecovery; +const worldChannel=typeof BroadcastChannel!=='undefined'?new BroadcastChannel(`bend-field-v${SAVE_SCHEMA}-${WORLD_GENERATION}`):null; +const seenWorldCommitIds=new Set(),seenWorldCommitOrder=[],pendingWorldCommitIds=new Set(); +let worldInitReady=false,deferredWorldSignals=[]; +function worldCommitId(signal){return typeof signal?.commitId==='string'?signal.commitId:signal?.sessionId&&signal?.updatedAt?`${signal.sessionId}:${signal.updatedAt}`:null} +function rememberWorldCommit(signal){ + const id=worldCommitId(signal);if(!id)return true;if(seenWorldCommitIds.has(id))return false; + seenWorldCommitIds.add(id);seenWorldCommitOrder.push(id);if(seenWorldCommitOrder.length>512)seenWorldCommitIds.delete(seenWorldCommitOrder.shift());return true; +} +function broadcastWorldSignal(signal){rememberWorldCommit(signal);try{worldChannel?.postMessage(signal)}catch(_){}safeLocalSet(syncStorageKey,JSON.stringify(signal))} +function announceWorldReplacement(worldEpoch){const signal={type:'world-replaced',commitId:`${sessionId}:replacement:${++worldSignalSeq}`,updatedAt:trustedNow(),sessionId,worldEpoch,metaIds:[],stateIds:[],deleted:[],globalChanged:true};broadcastWorldSignal(signal)} +let syncQueue=Promise.resolve(); +async function loadDbChanges(signal){ + if(!idbAvailable||!validWorldEpoch(data?.worldEpoch))return null; + const ids=[...new Set([...(signal.metaIds||[]),...(signal.stateIds||[]),...(signal.deleted||[])])],db=await openWorldDb(),tx=db.transaction(['control','worlds','boardIndex','boardPuzzles','boardStates','tombstonesV2'],'readonly'),done=transactionDone(tx), + controlStore=tx.objectStore('control'),worldsStore=tx.objectStore('worlds'),indexStore=tx.objectStore('boardIndex'),puzzleStore=tx.objectStore('boardPuzzles'),stateStore=tx.objectStore('boardStates'),tombstoneStore=tx.objectStore('tombstonesV2'), + keys=ids.map(id=>[data.worldEpoch,id]); + const [control,world,indexRows,puzzleRows,stateRecords,tombstoneRows]=await Promise.all([ + requestValue(controlStore.get('active')),requestValue(worldsStore.get(data.worldEpoch)), + Promise.all(keys.map(key=>requestValue(indexStore.get(key)))),Promise.all(keys.map(key=>requestValue(puzzleStore.get(key)))), + Promise.all(keys.map(key=>requestValue(stateStore.get(key)))),Promise.all(keys.map(key=>requestValue(tombstoneStore.get(key)))) + ]);await done; + const metaRows=ids.map((id,index)=>metaFromV2Records(indexRows[index],puzzleRows[index])), + stateRows=ids.map((id,index)=>{const value=stateFromV2Record(indexRows[index],stateRecords[index]);return value?{id,value}:null}); + return{ids,metaRows,stateRows,tombstoneRows,metaById:new Map(ids.map((id,index)=>[id,metaRows[index]||null])),tombstoneById:new Map(ids.map((id,index)=>[id,tombstoneRows[index]||null])),global:world?.global,worldEpoch:control?.activeEpoch||world?.epoch||null}; +} +function mergeGlobalFields(external){ + if(!external)return;const current=globalForStorage(data,data.updatedAt||data.clockFloor||0),merged=mergeGlobalRecords(current,external);applyGlobalRecordToData(merged); +} +function preserveBoardEditingBeforeSync(id,{meta=false}={}){ + const board=rendered.get(id);if(!board?.drawing)return false; + const pointerId=board.drawing.pointerId;board.drawing=null;board.armedGate=null;cancelBoardDragFrame(board);clearDragRender(board); + if(pointerId!=null)safeRelease(board.svg,pointerId);else queueMicrotask(reconcileDrawingPresentation); + const state=data.states[id];if(state){state.rev=nextRevision();markStateDirty(id)} + if(meta&&data.metas[id]){data.metas[id].rev=nextRevision();markMetaDirty(id)} + renderBoard(board);return true; +} +async function applyWorldSignal(signal){ + if(!signal||signal.sessionId===sessionId)return; + if(signal.type==='world-replaced'||validWorldEpoch(signal.worldEpoch)&&validWorldEpoch(data.worldEpoch)&&signal.worldEpoch!==data.worldEpoch){lifecyclePersistenceSuppressed=true;rememberWorldEpoch(signal.worldEpoch);setTimeout(()=>location.reload(),50);return} + await waitForInteractionSettle(); + const metaOrDelete=new Set([...(signal.metaIds||[]),...(signal.deleted||[])]),affected=new Set([...metaOrDelete,...(signal.stateIds||[])]); + for(const id of affected)preserveBoardEditingBeforeSync(id,{meta:metaOrDelete.has(id)}); + const delta=await loadDbChanges(signal);if(!delta)return;if(validWorldEpoch(delta.worldEpoch)&&delta.worldEpoch!==data.worldEpoch){lifecyclePersistenceSuppressed=true;rememberWorldEpoch(delta.worldEpoch);setTimeout(()=>location.reload(),50);return} + for(const id of affected)preserveBoardEditingBeforeSync(id,{meta:metaOrDelete.has(id)});mergeGlobalFields(delta.global); + const added=[];for(const row of delta.metaRows.filter(Boolean)){const incoming=normalizeMeta(row.id,row);if(!incoming)continue;const current=data.metas[row.id];if(!current||compareRevisionVersions(incoming,current)>0){destroyBoard(rendered.get(row.id));destroyStaticBoard(staticRendered.get(row.id));data.metas[row.id]=incoming;added.push(row.id)}} + for(const row of delta.stateRows.filter(Boolean)){if(!data.metas[row.id])continue;const incoming=normalizeState(row.value),current=data.states[row.id];if(!current||compareRevisionVersions(incoming,current)>0){data.states[row.id]=incoming;normalizedStateObjects.add(incoming);const board=rendered.get(row.id);if(board)board.solvedPathsRendered=false;rememberStateSignatures(row.id,incoming)}} + for(const id of signal.deleted||[]){ + const tombstone=delta.tombstoneById.get(id);if(delta.metaById.get(id)||!tombstone||compareRevisionVersions(tombstone,data.metas[id])<0||compareRevisionVersions(tombstone,data.states[id])<0||dirtyMetaIds.has(id)||dirtyStateIds.has(id))continue; + delete data.metas[id];delete data.states[id];stateStatSignatures.delete(id);stateEconomySignatures.delete(id);destroyBoard(rendered.get(id));destroyStaticBoard(staticRendered.get(id));if(activeBoard===id)activeBoard=null;if(hudBoardId===id)hudBoardId=null; + } + refreshWorldView({rebuild:true,syncConnections:true,markStats:true,resumeTimer:true,persist:true}); + for(const id of added)if(visibleMetaIds().has(id))try{await hydrateMeta(data.metas[id])}catch(error){data.quarantine[id]={failedAt:trustedNow(),message:String(error?.message||error),retries:(data.quarantine[id]?.retries||0)+1}} +} +function queueWorldSignal(signal){ + if(!signal||signal.sessionId===sessionId)return; + if(!worldInitReady){deferredWorldSignals.push(signal);if(deferredWorldSignals.length>256)deferredWorldSignals.shift();return} + const id=worldCommitId(signal);if(id&&(seenWorldCommitIds.has(id)||pendingWorldCommitIds.has(id)))return;if(id)pendingWorldCommitIds.add(id); + const run=async()=>{try{await applyWorldSignal(signal);rememberWorldCommit(signal)}catch(error){if((signal._retryCount||0)<3)setTimeout(()=>queueWorldSignal({...signal,_retryCount:(signal._retryCount||0)+1}),250*2**(signal._retryCount||0));else console.warn('BEND FIELD: cross-tab sync failed',error)}finally{if(id)pendingWorldCommitIds.delete(id)}}; + syncQueue=syncQueue.then(run,run); +} +function drainWorldSignals(){worldInitReady=true;const pending=deferredWorldSignals;deferredWorldSignals=[];for(const signal of pending)queueWorldSignal(signal)} +worldChannel?.addEventListener('message',event=>queueWorldSignal(event.data)); +window.addEventListener('storage',event=>{ + if(event.key!==syncStorageKey||!event.newValue)return;try{queueWorldSignal(JSON.parse(event.newValue))}catch(error){console.warn('BEND FIELD: ignored malformed sync signal',error)} +}); +function checkpointForLifecycle(){if(lifecyclePersistenceSuppressed)return;writeDirtyRecoveryJournal();void flushSave({lifecycle:true})} +window.addEventListener('pagehide',checkpointForLifecycle); +document.addEventListener('visibilitychange',()=>{const hidden=document.visibilityState==='hidden';document.body.classList.toggle('effects-paused',hidden);if(hidden){cancelReactionGesture();hideRealtimeCursor();clearTimeout(realtimeHeartbeatTimer);realtimeHeartbeatTimer=0;pauseNoiseBackground();checkpointForLifecycle();void pushCloudPending()}else{scheduleNoiseBackground(true);resumeTimeAttackTimer();connectRealtime();scheduleRealtimeViewport(true);scheduleRealtimeHeartbeat();schedulePresenceRender(true);scheduleReactionRender();void pullCloudWorld();scheduleMirrorCheckpoint()}}); +const cloudBtn=document.querySelector('#cloudBtn'); +function emptyCloudPending(){return{metaIds:new Set(),stateIds:new Set(),deleted:new Set(),globalChanged:false}} +let cloudAvailable=false,cloudPushTimer=null,cloudPushPending=emptyCloudPending(),cloudSyncing=false,cloudCheckpointRetryTimer=0,worldPollTimer=0,lastCloudWorldGlobalSignature=''; +const remotePlayers=new Map(),boardClaims=new Map(),realtimeClaimRequests=new Map(),remoteCursorImageCache=new Map(),realtimeHeldPointers=new Set(),realtimeReactions=new Map(); +let realtimeSocket=null,realtimeReady=false,realtimePresenceId=null,realtimeClaimTtlMs=5*60*1000,realtimeReconnectTimer=0,realtimeViewportTimer=0,realtimeCursorTimer=0,realtimeHeartbeatTimer=0,realtimeLastCursorSentAt=0,realtimeLastCursorSampleAt=0,realtimePendingCursor=null,realtimePendingCursorClient=null,realtimeRequestSequence=0,realtimeOwnClaimBoardId=null,realtimeLastClaimTouchAt=0,presenceFrame=0,presenceDelayTimer=0,presenceLastDraw=0,presenceDirty=true,presenceLastTimestamp=0,presenceCameraSnapshot=null,reactionFrame=0,reactionDelayTimer=0,reactionLastDraw=0,reactionDirty=true,reactionCameraSnapshot=null,reactionGesture=null,reactionSequence=0; +function realtimeWebSocketUrl(){const protocol=location.protocol==='https:'?'wss:':'ws:';return`${protocol}//${location.host}/api/realtime`} +function realtimeSend(message){if(!realtimeReady||realtimeSocket?.readyState!==WebSocket.OPEN)return false;try{realtimeSocket.send(JSON.stringify(message));return true}catch(_){return false}} +function realtimeViewportBounds(){const rect=getViewportRect(),topLeft=worldUnitAtClient(rect.left,rect.top),bottomRight=worldUnitAtClient(rect.right,rect.bottom);return{minX:Math.min(topLeft[0],bottomRight[0]),minY:Math.min(topLeft[1],bottomRight[1]),maxX:Math.max(topLeft[0],bottomRight[0]),maxY:Math.max(topLeft[1],bottomRight[1])}} +function sendRealtimeViewport(){realtimeViewportTimer=0;if(!realtimeReady)return false;const bounds=realtimeViewportBounds();return realtimeSend({type:'viewport',...bounds})} +function scheduleRealtimeViewport(immediate=false){if(!realtimeReady)return false;if(immediate){clearTimeout(realtimeViewportTimer);realtimeViewportTimer=0;return sendRealtimeViewport()}if(realtimeViewportTimer)return true;realtimeViewportTimer=setTimeout(sendRealtimeViewport,REALTIME_VIEWPORT_INTERVAL);return true} +function scheduleRealtimeHeartbeat(){clearTimeout(realtimeHeartbeatTimer);realtimeHeartbeatTimer=0;if(!realtimeReady||document.visibilityState==='hidden')return false;realtimeHeartbeatTimer=setTimeout(()=>{realtimeHeartbeatTimer=0;if(realtimePendingCursor)realtimeSend({type:'cursor',...realtimePendingCursor,vx:0,vy:0,sentAt:Date.now()});scheduleRealtimeHeartbeat()},REALTIME_CURSOR_HEARTBEAT_INTERVAL);return true} +function queueRealtimeCursor(event){ + if(!realtimeReady||event.pointerType==='touch'||document.body.classList.contains('is-drawing')||!viewport.contains(event.target))return; + realtimePendingCursorClient={clientX:event.clientX,clientY:event.clientY,inputAt:Number(event.timeStamp)||perfNow()}; + const wait=REALTIME_CURSOR_INTERVAL-(perfNow()-realtimeLastCursorSentAt);if(realtimeCursorTimer)return; + realtimeCursorTimer=setTimeout(()=>{ + realtimeCursorTimer=0;const client=realtimePendingCursorClient;if(!client||!realtimeReady)return; + const point=worldUnitAtClient(client.clientX,client.clientY),previous=realtimePendingCursor,now=perfNow(),elapsed=Math.max(1,now-(realtimeLastCursorSampleAt||now)), + next={x:point[0],y:point[1],vx:previous?(point[0]-previous.x)*1000/elapsed:0,vy:previous?(point[1]-previous.y)*1000/elapsed:0,sentAt:Date.now(),cursorStyle:data.cursorStyle||'default'}; + realtimeLastCursorSampleAt=now; + realtimePendingCursor=next;realtimeLastCursorSentAt=perfNow(); + if(previous&&previous.cursorStyle===next.cursorStyle&&Math.abs(previous.x-next.x)<.002&&Math.abs(previous.y-next.y)<.002)return; + realtimeSend({type:'cursor',...next}); + },Math.max(0,wait)); +} +function hideRealtimeCursor(){clearTimeout(realtimeCursorTimer);realtimeCursorTimer=0;realtimeLastCursorSampleAt=0;realtimePendingCursorClient=null;realtimePendingCursor=null;if(realtimeReady)realtimeSend({type:'cursor-hide'})} +function remoteCursorImage(item){ + if(!item?.flagAsset)return null;let entry=remoteCursorImageCache.get(item.flagAsset);if(entry){remoteCursorImageCache.delete(item.flagAsset);remoteCursorImageCache.set(item.flagAsset,entry);return entry.image} + const image=new Image();entry={image,ready:false};remoteCursorImageCache.set(item.flagAsset,entry);while(remoteCursorImageCache.size>48)remoteCursorImageCache.delete(remoteCursorImageCache.keys().next().value);image.onload=()=>{entry.ready=true;schedulePresenceRender(true)};image.onerror=()=>{entry.failed=true};image.src=item.flagAsset;return image; +} +function roundedCanvasRect(context,x,y,width,height,radius){const r=Math.max(0,Math.min(radius,width/2,height/2));context.beginPath();context.moveTo(x+r,y);context.arcTo(x+width,y,x+width,y+height,r);context.arcTo(x+width,y+height,x,y+height,r);context.arcTo(x,y+height,x,y,r);context.arcTo(x,y,x+width,y,r);context.closePath()} +function drawDefaultRemoteCursor(context,x,y){context.save();context.translate(x,y);context.beginPath();context.moveTo(-6,-10);context.lineTo(9,1);context.lineTo(2,3);context.lineTo(6,11);context.lineTo(1,13);context.lineTo(-3,5);context.lineTo(-8,10);context.closePath();context.lineJoin='round';context.lineWidth=4;context.strokeStyle='rgba(8,12,15,.95)';context.stroke();context.lineWidth=2;context.strokeStyle='#fff';context.stroke();context.fillStyle='#d9f06f';context.fill();context.restore()} +function drawRemoteCursorGlyph(context,player,x,y){ + const item=CURSOR_ITEM_BY_STYLE.get(player.cursorStyle); + if(!item){drawDefaultRemoteCursor(context,x,y);return} + if(item.flagAsset){const entry=remoteCursorImageCache.get(item.flagAsset),image=remoteCursorImage(item);if(entry?.ready){context.save();context.translate(x,y);roundedCanvasRect(context,-15,-11,30,22,4);context.clip();context.drawImage(image,-17,-13,34,26);context.restore();context.strokeStyle='rgba(255,255,255,.9)';context.lineWidth=1.5;roundedCanvasRect(context,x-15,y-11,30,22,4);context.stroke()}else drawDefaultRemoteCursor(context,x,y);return} + context.save();context.font='25px "Segoe UI Emoji","Apple Color Emoji","Noto Color Emoji",sans-serif';context.textAlign='center';context.textBaseline='middle';context.shadowColor='rgba(0,0,0,.8)';context.shadowBlur=4;context.fillText(item.cursorEmoji||'•',x,y);context.restore(); +} +function drawRemotePlayerName(context,player,x,y){ + const name=String(player.name||'旅人').slice(0,24);context.save();context.font='700 10px DotGothic16Local,monospace';context.textBaseline='middle';const width=Math.ceil(context.measureText(name).width)+10,left=x+12,top=y+11;roundedCanvasRect(context,left,top,width,18,3);context.fillStyle='rgba(8,12,15,.88)';context.fill();context.strokeStyle='rgba(255,255,255,.3)';context.lineWidth=1;context.stroke();context.fillStyle='#fff';context.fillText(name,left+5,top+9);context.restore(); +} +function onlineLayerCameraSnapshot(){const scale=Math.max(MIN_CAMERA_SCALE,cam.scale);return{scale,offsetX:cam.x-renderOriginX*UNIT*scale,offsetY:cam.y-renderOriginY*UNIT*scale}} +function shiftCameraBoundCanvas(canvas,snapshot){ + if(!canvas||!snapshot)return false;const current=onlineLayerCameraSnapshot(),ratio=current.scale/snapshot.scale, + tx=current.offsetX-ratio*snapshot.offsetX,ty=current.offsetY-ratio*snapshot.offsetY; + canvas.style.transform=`matrix(${ratio.toFixed(6)},0,0,${ratio.toFixed(6)},${tx.toFixed(2)},${ty.toFixed(2)})`;return true; +} +function shiftOnlineLayersForCamera(){ + if(remotePlayers.size)shiftCameraBoundCanvas(presenceCanvas,presenceCameraSnapshot); + if(realtimeReactions.size)shiftCameraBoundCanvas(reactionCanvas,reactionCameraSnapshot); +} +function redrawOnlineLayersAfterCamera(){ + // Keep the camera compensation transform until the fresh frame is painted. + // Clearing it here exposes the stale bitmap for one frame and causes a jump. + if(remotePlayers.size||presenceDirty)schedulePresenceRender(true);if(realtimeReactions.size||reactionDirty)scheduleReactionRender(true); +} +function schedulePresenceRender(markDirty=false){ + if(markDirty)presenceDirty=true;if(!presenceCanvas||presenceFrame||(!presenceDirty&&!remotePlayers.size))return; + const step=timestamp=>{if(!presenceFrame)return;if(timestamp-presenceLastDrawREALTIME_PLAYER_STALE_MS){remotePlayers.delete(presenceId);playersRemoved=true;continue}if(!Number.isFinite(player.currentX)){player.currentX=player.targetX;player.currentY=player.targetY}const alpha=1-Math.exp(-dt/45);player.currentX+=(player.targetX-player.currentX)*alpha;player.currentY+=(player.targetY-player.currentY)*alpha;if(Math.abs(player.targetX-player.currentX)>.001||Math.abs(player.targetY-player.currentY)>.001)animate=true;const x=(player.currentX-renderOriginX)*UNIT*cam.scale+cam.x,y=(player.currentY-renderOriginY)*UNIT*cam.scale+cam.y;if(x<-80||y<-80||x>width+120||y>height+80)continue;drawRemoteCursorGlyph(context,player,x,y);drawRemotePlayerName(context,player,x,y)} + presenceDirty=false;if(animate)schedulePresenceRender();else if(remotePlayers.size&&!presenceDelayTimer)presenceDelayTimer=setTimeout(()=>{presenceDelayTimer=0;schedulePresenceRender(true)},1000);if(playersRemoved)scheduleMinimap(true); +} +function applyRemotePlayer(raw){ + if(!raw||raw.presenceId===realtimePresenceId||!Number.isFinite(raw.x)||!Number.isFinite(raw.y))return false; + const vx=Number.isFinite(raw.vx)?raw.vx:0,vy=Number.isFinite(raw.vy)?raw.vy:0,lead=.035,previous=remotePlayers.get(raw.presenceId)||{currentX:raw.x,currentY:raw.y}; + previous.presenceId=raw.presenceId;previous.playerId=raw.playerId;previous.name=String(raw.name||'旅人').slice(0,24);previous.cursorStyle=String(raw.cursorStyle||'default');previous.targetX=raw.x+vx*lead;previous.targetY=raw.y+vy*lead;previous.vx=vx;previous.vy=vy;previous.sentAt=Number(raw.sentAt)||0;previous.lastSeen=Date.now();remotePlayers.set(raw.presenceId,previous);schedulePresenceRender(true);scheduleMinimap(true);return true; +} +function normalizeRealtimeReaction(raw){const emoji=String(raw?.emoji||''),x=Number(raw?.x),y=Number(raw?.y),createdAt=Number(raw?.createdAt),expiresAt=Number(raw?.expiresAt),id=typeof raw?.id==='string'?raw.id.slice(0,80):'';if(!id||!REACTION_EMOJIS.includes(emoji)||!Number.isFinite(x)||!Number.isFinite(y)||!Number.isFinite(createdAt)||!Number.isFinite(expiresAt)||expiresAt<=trustedNow())return null;return{id,emoji,x,y,createdAt,expiresAt,playerId:raw.playerId||null,playerName:String(raw.playerName||'').slice(0,24)}} +function applyRealtimeReaction(raw){const reaction=normalizeRealtimeReaction(raw);if(!reaction)return false;realtimeReactions.set(reaction.id,reaction);reactionDirty=true;scheduleReactionRender();return true} +function scheduleReactionRender(markDirty=false){if(markDirty)reactionDirty=true;if(!reactionCanvas||reactionFrame||(!reactionDirty&&!realtimeReactions.size))return;const step=timestamp=>{if(!reactionFrame)return;if(timestamp-reactionLastDraw.84?(1-life)/.16:1,scale=.45+.55*(1-Math.pow(1-appear,3)),x=(reaction.x-renderOriginX)*UNIT*cam.scale+cam.x,y=(reaction.y-renderOriginY)*UNIT*cam.scale+cam.y-18*life;if(x<-60||y<-80||x>width+60||y>height+60)continue;context.save();context.globalAlpha=Math.max(0,fade);context.translate(x,y);context.scale(scale,scale);context.font='34px "Segoe UI Emoji","Apple Color Emoji","Noto Color Emoji",sans-serif';context.textAlign='center';context.textBaseline='middle';context.shadowColor='rgba(0,0,0,.58)';context.shadowBlur=5;context.fillText(reaction.emoji,0,0);context.restore()}if(active)scheduleReactionRender()} +function publishReactionAt(emoji,x,y){if(!REACTION_EMOJIS.includes(emoji)||!Number.isFinite(x)||!Number.isFinite(y))return false;data.lastReaction=emoji;markGlobalDirty(false);const timestamp=Date.now(),reaction={id:`local-${sessionId}-${++reactionSequence}`,emoji,x,y,createdAt:timestamp,expiresAt:timestamp+REALTIME_REACTION_DURATION,playerId:currentPlayerId(),playerName:currentPlayerName()};applyRealtimeReaction(reaction);if(realtimeReady)realtimeSend({type:'reaction',id:reaction.id,emoji,x,y});return true} +function reactionAllowedAt(clientX,clientY,target){if(target?.closest?.('button,a,input,select,textarea,[role="button"]'))return false;const[x,y]=worldUnitAtClient(clientX,clientY),boardId=occupancy.get(key2(Math.floor(x),Math.floor(y)));return!boardId||metaState(boardId).solved} +function hideReactionRadial(){if(reactionRadial)reactionRadial.hidden=true;document.body.classList.remove('reaction-selecting')} +function radialReactionIndex(clientX,clientY){if(!reactionGesture)return 0;const dx=clientX-reactionGesture.clientX,dy=clientY-reactionGesture.clientY,distance=Math.hypot(dx,dy);if(distance<18)return reactionGesture.selected??0;let best=0,bestDistance=Infinity;for(let index=0;index{const button=document.createElement('button'),angle=-Math.PI/2+index*Math.PI*2/REACTION_EMOJIS.length;button.type='button';button.textContent=emoji;button.setAttribute('role','menuitem');button.style.setProperty('--rx',`${Math.cos(angle)*48}px`);button.style.setProperty('--ry',`${Math.sin(angle)*48}px`);button.classList.toggle('selected',index===gesture.selected);reactionRadial.append(button)});reactionRadial.style.left=`${gesture.clientX}px`;reactionRadial.style.top=`${gesture.clientY}px`;reactionRadial.hidden=false;document.body.classList.add('reaction-selecting')} +function beginReactionGesture(event){if(event.button!==0||event.pointerType==='touch'&&!event.isPrimary||!reactionAllowedAt(event.clientX,event.clientY,event.target))return;const[x,y]=worldUnitAtClient(event.clientX,event.clientY);reactionGesture={pointerId:event.pointerId,clientX:event.clientX,clientY:event.clientY,x,y,moved:false,menuOpen:false,selected:REACTION_EMOJIS.indexOf(data.lastReaction),timer:setTimeout(openReactionRadial,REACTION_LONG_PRESS_MS)};try{viewport.setPointerCapture(event.pointerId)}catch(_){}} +function moveReactionGesture(event){const gesture=reactionGesture;if(!gesture||gesture.pointerId!==event.pointerId)return;if(gesture.menuOpen){event.preventDefault();event.stopImmediatePropagation();updateReactionRadialSelection(event.clientX,event.clientY);return}if(Math.hypot(event.clientX-gesture.clientX,event.clientY-gesture.clientY)>REACTION_MOVE_CANCEL_PX){gesture.moved=true;clearTimeout(gesture.timer)}} +function endReactionGesture(event){const gesture=reactionGesture;if(!gesture||gesture.pointerId!==event.pointerId)return;clearTimeout(gesture.timer);const index=gesture.menuOpen?radialReactionIndex(event.clientX,event.clientY):-1;reactionGesture=null;try{if(viewport.hasPointerCapture?.(event.pointerId))viewport.releasePointerCapture(event.pointerId)}catch(_){}if(gesture.menuOpen){event.preventDefault();event.stopImmediatePropagation();touchPoints.delete(event.pointerId);viewport.classList.remove('panning');refreshInteractionState();const emoji=REACTION_EMOJIS[index]||data.lastReaction;hideReactionRadial();publishReactionAt(emoji,gesture.x,gesture.y);return}hideReactionRadial();if(!gesture.moved)publishReactionAt(data.lastReaction||'👍',gesture.x,gesture.y)} +function cancelReactionGesture(event=null){const pointerId=reactionGesture?.pointerId;if(reactionGesture?.timer)clearTimeout(reactionGesture.timer);reactionGesture=null;if(pointerId!=null)try{if(viewport.hasPointerCapture?.(pointerId))viewport.releasePointerCapture(pointerId)}catch(_){}hideReactionRadial()} +function currentBoardClaim(boardId){const claim=boardClaims.get(boardId);if(claim&&claim.expiresAt<=trustedNow()){boardClaims.delete(boardId);refreshClaimPresentation(boardId);return null}return claim||null} +function boardClaimOwnedByMe(boardId){const claim=currentBoardClaim(boardId);return Boolean(claim&&claim.playerId===currentPlayerId())} +function boardClaimedByOther(boardId){const claim=currentBoardClaim(boardId);return Boolean(claim&&claim.playerId!==currentPlayerId())} +function applyClaimPresentationToBoard(board){if(!board)return;const claim=currentBoardClaim(board.id),solved=metaState(board.id).solved,own=Boolean(claim&&claim.playerId===currentPlayerId()),hudVisible=boardPlayHudVisible(board,solved);board.card.classList.toggle('claimed-other',Boolean(claim&&!own&&!solved));board.card.classList.toggle('claimed-own',Boolean(claim&&own&&!solved));board.card.classList.toggle('hud-current',hudVisible);if(hudVisible)positionBoardLabel(board);if(board.claimBadge){board.claimBadge.hidden=!claim||solved;board.claimBadge.textContent=claim?own?'占有中':`${claim.playerName||'他のプレイヤー'}がプレイ中`:''}board.svg?.setAttribute?.('aria-disabled',String(Boolean(claim&&!own&&!solved)))} +function refreshClaimPresentation(boardId=null){ + const ids=boardId?[boardId]:[...new Set([...rendered.keys(),...staticRendered.keys()])];for(const id of ids){const board=rendered.get(id);if(board)applyClaimPresentationToBoard(board);const staticBoard=staticRendered.get(id),claim=currentBoardClaim(id),own=claim?.playerId===currentPlayerId(),solved=metaState(id).solved;if(staticBoard){staticBoard.card.classList.toggle('claimed-other',Boolean(claim&&!own&&!solved));staticBoard.card.classList.toggle('claimed-own',Boolean(claim&&own&&!solved));staticBoard.card.dataset.claimLabel=claim&&!solved?(own?'占有中':`${claim.playerName||'他のプレイヤー'}がプレイ中`):'';staticBoard.card.title=claim&&!own?`${claim.playerName||'他のプレイヤー'}がプレイ中`:''}} +} +function applyClaim(raw){if(!raw?.boardId)return;boardClaims.set(raw.boardId,{...raw,expiresAt:Number(raw.expiresAt)||trustedNow()});if(raw.playerId===currentPlayerId())realtimeOwnClaimBoardId=raw.boardId;refreshClaimPresentation(raw.boardId)} +function removeClaim(boardId,reason='released'){const claim=boardClaims.get(boardId);boardClaims.delete(boardId);if(realtimeOwnClaimBoardId===boardId)realtimeOwnClaimBoardId=null;refreshClaimPresentation(boardId);const board=rendered.get(boardId);if(claim?.playerId===currentPlayerId()&&board?.drawing?.pointerId!=null&&reason!=='cleared'){cancelPointerGestures();toast('盤面の占有期限が切れました。')}} +function applyRealtimeSnapshot(message){ + const seenPlayers=new Set();for(const player of message.players||[]){seenPlayers.add(player.presenceId);applyRemotePlayer(player)}for(const id of[...remotePlayers.keys()])if(!seenPlayers.has(id))remotePlayers.delete(id); + const seenClaims=new Set();for(const claim of message.claims||[]){seenClaims.add(claim.boardId);applyClaim(claim)}for(const id of[...boardClaims.keys()])if(!seenClaims.has(id))removeClaim(id,'viewport');for(const reaction of message.reactions||[])applyRealtimeReaction(reaction);schedulePresenceRender(true);refreshClaimPresentation(); +} +function settleRealtimeClaimResult(message){const pending=realtimeClaimRequests.get(message.requestId);if(!pending)return;if(pending.timer)clearTimeout(pending.timer);realtimeClaimRequests.delete(message.requestId);if(message.claim)applyClaim(message.claim);pending.resolve(message.ok===true)} +function handleRealtimeMessage(message){ + if(!message||typeof message!=='object')return; + if(message.type==='ready'){realtimeReady=true;realtimePresenceId=message.presenceId;realtimeClaimTtlMs=Math.max(1000,Number(message.claimTtlMs)||realtimeClaimTtlMs);serverClockOffset=Number.isFinite(message.serverTime)?message.serverTime-Date.now():serverClockOffset;scheduleRealtimeViewport(true);if(realtimePendingCursor)realtimeSend({type:'cursor',...realtimePendingCursor});scheduleRealtimeHeartbeat();return} + if(message.type==='snapshot')return applyRealtimeSnapshot(message); + if(message.type==='cursor')return applyRemotePlayer(message); + if(message.type==='player-left'){remotePlayers.delete(message.presenceId);schedulePresenceRender(true);scheduleMinimap(true);return} + if(message.type==='claim')return applyClaim(message.claim); + if(message.type==='claim-release')return removeClaim(message.boardId,message.reason); + if(message.type==='claim-result')return settleRealtimeClaimResult(message); + if(message.type==='reaction')return applyRealtimeReaction(message.reaction); + if(message.type==='player-profile'){for(const player of remotePlayers.values())if(player.playerId===message.playerId)player.name=String(message.name||player.name).slice(0,24);for(const claim of boardClaims.values())if(claim.playerId===message.playerId)claim.playerName=String(message.name||claim.playerName).slice(0,24);refreshClaimPresentation();schedulePresenceRender(true);return} + if(message.type==='board-cleared'&&message.event){const revision=Number(message.event.revision)||0;if(revision>(data.worldFeedRevision||0)){data.worldFeedRevision=revision;markGlobalDirty(false);addClearFeedEvent(message.event)}removeClaim(message.event.id,'cleared');void pullCloudWorld();} +} +function rejectRealtimeClaims(){for(const pending of realtimeClaimRequests.values()){clearTimeout(pending.timer);pending.resolve(false)}realtimeClaimRequests.clear()} +function scheduleRealtimeReconnect(delay=1500){clearTimeout(realtimeReconnectTimer);if(!cloudAvailable||!data.cloudProfile||document.visibilityState==='hidden')return;realtimeReconnectTimer=setTimeout(connectRealtime,delay)} +function connectRealtime(){ + clearTimeout(realtimeReconnectTimer);realtimeReconnectTimer=0;if(!cloudApiEnabled||!cloudAvailable||!data.cloudProfile||typeof WebSocket==='undefined')return false;if(realtimeSocket&&[WebSocket.OPEN,WebSocket.CONNECTING].includes(realtimeSocket.readyState))return true; + const socket=new WebSocket(realtimeWebSocketUrl());realtimeSocket=socket;realtimeReady=false; + socket.onopen=()=>{if(socket!==realtimeSocket)return;socket.send(JSON.stringify({type:'hello',playerId:data.cloudProfile.playerId,token:data.cloudProfile.token}))}; + socket.onmessage=event=>{if(socket!==realtimeSocket)return;try{handleRealtimeMessage(JSON.parse(event.data))}catch(error){console.warn('BEND FIELD: realtime message failed',error)}}; + socket.onerror=()=>{};socket.onclose=()=>{if(socket!==realtimeSocket)return;clearTimeout(realtimeHeartbeatTimer);realtimeHeartbeatTimer=0;realtimeReady=false;realtimePresenceId=null;realtimeSocket=null;remotePlayers.clear();realtimeReactions.clear();reactionDirty=true;rejectRealtimeClaims();schedulePresenceRender(true);scheduleReactionRender(true);scheduleMinimap(true);scheduleRealtimeReconnect()};return true; +} +function requestBoardClaim(boardId){ + if(metaState(boardId).solved)return Promise.resolve(false);const existing=currentBoardClaim(boardId);if(existing?.playerId===currentPlayerId()){touchBoardClaim(boardId,true);return Promise.resolve(true)}if(existing){toast(`${existing.playerName||'他のプレイヤー'}がプレイ中です。`);return Promise.resolve(false)} + if(!cloudApiEnabled||!cloudAvailable)return Promise.resolve(true);if(!realtimeReady){connectRealtime();toast('共有接続を確認しています。');return Promise.resolve(false)} + const requestId=`${sessionId}:${++realtimeRequestSequence}`,promise=new Promise(resolve=>{const timer=setTimeout(()=>{realtimeClaimRequests.delete(requestId);resolve(false)},REALTIME_CLAIM_REQUEST_TIMEOUT);realtimeClaimRequests.set(requestId,{resolve,timer,boardId})}); + if(!realtimeSend({type:'claim',requestId,boardId})){const pending=realtimeClaimRequests.get(requestId);clearTimeout(pending?.timer);realtimeClaimRequests.delete(requestId);return Promise.resolve(false)}return promise.then(ok=>{if(!ok){const claim=currentBoardClaim(boardId);toast(claim?`${claim.playerName||'他のプレイヤー'}がプレイ中です。`:'盤面を占有できませんでした。')}return ok}); +} +async function ensureBoardClaimForInput(board){if(!board||metaState(board.id).solved)return false;return requestBoardClaim(board.id)} +function touchBoardClaim(boardId,force=false){if(!boardClaimOwnedByMe(boardId)||!realtimeReady)return false;const now=Date.now();if(!force&&now-realtimeLastClaimTouchAtrealtimeHeldPointers.add(event.pointerId),{passive:true,capture:true}); +for(const type of['pointerup','pointercancel'])window.addEventListener(type,event=>realtimeHeldPointers.delete(event.pointerId),{passive:true,capture:true}); +window.addEventListener('pointermove',queueRealtimeCursor,{passive:true,capture:true}); +viewport.addEventListener('pointerleave',hideRealtimeCursor,{passive:true}); + +function sharedWorldGlobalForCloud(){return{schema:SAVE_SCHEMA,gameplayVersion:GAMEPLAY_DATA_VERSION,worldGeneration:WORLD_GENERATION,appVersion:APP_VERSION,generatorVersion:GENERATOR_VERSION,nextId:data.nextId,solved:data.solved||0,lastSolveAt:data.lastSolveAt||0,specialMechanicsSeen:[...new Set(data.specialMechanicsSeen||[])].filter(type=>['warp','lock','crossing'].includes(type)).sort(),quarantine:data.quarantine||{}}} +function sharedWorldGlobalSignature(){return JSON.stringify(sharedWorldGlobalForCloud())} +function mergeCloudPending(target,signal={}){ + for(const id of signal.metaIds||[]){target.deleted.delete(id);target.metaIds.add(id)} + for(const id of signal.stateIds||[]){if(data?.states?.[id]?.solved!==true)continue;target.deleted.delete(id);target.stateIds.add(id)} + for(const id of signal.deleted||[]){target.metaIds.delete(id);target.stateIds.delete(id);target.deleted.add(id)} + target.globalChanged=target.globalChanged||signal.globalChanged===true;return target; +} +function takeCloudPendingBatch(source,limit=512){ + const batch=emptyCloudPending(),remainder=emptyCloudPending(),maximum=Math.max(1,Math.floor(limit)||1);let used=0; + batch.globalChanged=source.globalChanged===true; + for(const key of['metaIds','stateIds','deleted'])for(const id of source[key]||[])(used0||pending.stateIds.size>0||pending.deleted.size>0} +function cloudProfileIdentity(profile=data.cloudProfile){return profile?`${profile.playerId}.${profile.token}`:''} +function scheduleCloudCheckpointRetry(){if(cloudCheckpointRetryTimer)return;cloudCheckpointRetryTimer=setTimeout(()=>{cloudCheckpointRetryTimer=0;if(hasPendingPersistence())void persistNow({skipCloud:true}).then(ok=>{if(!ok)scheduleCloudCheckpointRetry()})},1000)} +function restoreCloudPushPending(){cloudPushPending=emptyCloudPending();mergeCloudPending(cloudPushPending,currentCloudPending())} +function acknowledgeCloudPending(pending,metaRevs,stateRevs,changeSeq){ + for(const id of pending.metaIds)if(!dirtyMetaIds.has(id)&&data.metas[id]?.rev===metaRevs.get(id)){cloudJournalMetaIds.delete(id);cloudOutboxDeleteKeys.add(`meta:${id}`)} + for(const id of pending.stateIds)if(!dirtyStateIds.has(id)&&data.states[id]?.rev===stateRevs.get(id)){cloudJournalStateIds.delete(id);cloudOutboxDeleteKeys.add(`state:${id}`)} + for(const id of pending.deleted)if(!deletedBoardIds.has(id)&&!data.metas[id]){cloudJournalDeletedIds.delete(id);cloudOutboxDeleteKeys.add(`deleted:${id}`)} + if(cloudJournalChangeSeq===changeSeq){cloudJournalGlobalChanged=false;cloudOutboxDeleteKeys.add('global')} + data.cloudPending=currentCloudPending();restoreCloudPushPending(); +} +function armCloudPush(delay=5000){clearTimeout(cloudPushTimer);cloudPushTimer=null;if(!cloudAvailable||!data.cloudProfile||cloudSyncing||!cloudPendingHasWork())return false;cloudPushTimer=setTimeout(()=>{cloudPushTimer=null;void pushCloudPending()},Math.max(0,delay));return true} +async function fetchJson(url,options={},timeout=5000){const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),timeout);try{const response=await fetch(url,{...options,signal:controller.signal,headers:{'content-type':'application/json',...(options.headers||{})}}),body=await response.json().catch(()=>({}));if(!response.ok){const error=new Error(body.error||`HTTP ${response.status}`);error.status=response.status;error.body=body;throw error}return body}finally{clearTimeout(timer)}} +function cloudAuthHeaders(){return data.cloudProfile?{authorization:`Bearer ${data.cloudProfile.playerId}.${data.cloudProfile.token}`}:{}} +function applyPlayerEconomyEnvelope(raw){const player=raw?.player||raw;if(!player||!Array.isArray(player.purchases))return false;data.playerPurchases=normalizePlayerPurchases(player.purchases);data.playerEarnedScore=Number.isSafeInteger(player.earnedScore)&&player.earnedScore>=0?player.earnedScore:0;playerEconomyLoaded=true;invalidateEconomyCaches();markGlobalDirty(false);updateHud();if(openStoreBoardId)renderStorePanel();if(inventoryModal?.classList.contains('show'))renderInventoryPanel();return true} +async function pullPlayerEconomy(){if(!cloudAvailable||!data.cloudProfile)return false;try{const result=await fetchJson('/api/player/state',{headers:cloudAuthHeaders()});serverClockOffset=Number.isFinite(result.serverTime)?result.serverTime-Date.now():serverClockOffset;applyPlayerEconomyEnvelope(result);await persistNow({skipCloud:true});return true}catch(error){playerEconomyLoaded=false;console.warn('BEND FIELD: player economy pull failed',error);return false}} +async function buyPersonalStoreItem(boardId,itemId){const result=await fetchJson('/api/player/purchase',{method:'POST',headers:cloudAuthHeaders(),body:JSON.stringify({boardId,itemId})},10000);applyPlayerEconomyEnvelope(result);await persistNow({skipCloud:true});return result.purchase||null} +function setCloudStatus(label,state=''){if(!cloudBtn)return;const name=currentPlayerName();cloudBtn.textContent=state==='saved'&&cloudAvailable?`共有 · ${name}`:label;cloudBtn.dataset.state=state;cloudBtn.title=cloudAvailable?`共有ワールド接続中:${name}。クリックで名前または同期コードを変更`:'共有ワールドを利用できません'} +function addClearFeedEvent(event){ + if(!clearFeed||!event||typeof event.playerName!=='string')return;const item=document.createElement('div');item.className='clear-feed-item';const name=document.createElement('b'),details=document.createElement('span');name.textContent=`🎉 ${event.playerName}`;details.textContent=` (${Math.round(event.x||0)}, ${Math.round(event.y||0)}) · Lv.${Math.max(1,Math.round(event.level||1))}`;item.append(name,details);clearFeed.prepend(item);while(clearFeed.children.length>3)clearFeed.lastElementChild?.remove();setTimeout(()=>{item.classList.add('is-leaving');setTimeout(()=>item.remove(),380)},10000) +} +function applyCloudEnvelope(result,{initial=false}={}){ + let changed=false;if(result?.player?.name&&data.playerName!==result.player.name){data.playerName=String(result.player.name).slice(0,24);changed=true} + const latest=Math.max(0,Number(result?.latestEventRevision)||0),events=Array.isArray(result?.clearEvents)?result.clearEvents:[]; + if(initial&&!(data.worldFeedRevision>0)){data.worldFeedRevision=latest;changed=true}else{for(const event of events)if((event.revision||0)>(data.worldFeedRevision||0))addClearFeedEvent(event);if(latest>(data.worldFeedRevision||0)){data.worldFeedRevision=latest;changed=true}} + if(changed)markGlobalDirty(false);return changed; +} +async function createCloudProfile(){const result=await fetchJson('/api/cloud/session',{method:'POST',body:JSON.stringify({name:data.playerName||''})});serverClockOffset=result.serverTime-Date.now();data.cloudProfile={playerId:result.playerId,token:result.token};data.playerName=result.name||data.playerName||null;data.cloudRevision=0;markGlobalDirty(false);if(!await persistNow({skipCloud:true})){scheduleCloudCheckpointRetry();throw new Error('Shared-world profile could not be committed locally')}return data.cloudProfile} +async function pullCloudWorld(force=false,sinceOverride=null,{initial=false,authoritative=false}={}){ + if(typeof fieldArchiveBusy!=='undefined'&&fieldArchiveBusy)return false;if(!cloudAvailable||!data.cloudProfile||cloudSyncing&&!force)return false; + if(document.body?.classList?.contains('is-interacting')||[...rendered.values()].some(board=>board.drawing?.keyboardActive))return false;if(hasPendingPersistence()&&!await flushSave())return false; + const profileIdentity=cloudProfileIdentity();cloudSyncing=true;setCloudStatus('共有取得中','syncing');let pulled=false; + try{ + const previousRevision=sinceOverride??data.cloudRevision??0,remoteIds=new Set();let cursor=0,targetRevision=null,changed=false,fullSnapshot=false,firstPage=true,envelopeChanged=false,authoritativeWorld=authoritative===true; + do{ + const query=`/api/cloud/pull?since=${previousRevision}&eventsSince=${data.worldFeedRevision||0}${cursor?`&cursor=${cursor}&at=${targetRevision}`:''}`,result=await fetchJson(query,{headers:cloudAuthHeaders()},30000); + await waitForInteractionSettle(); + if(cloudProfileIdentity()!==profileIdentity)throw new Error('Shared-world profile changed while pulling');serverClockOffset=result.serverTime-Date.now();targetRevision??=result.revision||0;if(initial&&previousRevision===0&&targetRevision>0)authoritativeWorld=true;if(result.revision!==targetRevision)throw new Error('Shared world changed during paged pull'); + if(firstPage){envelopeChanged=applyCloudEnvelope(result,{initial})||envelopeChanged;firstPage=false}changed=result.changed===true;fullSnapshot=result.fullSnapshot===true; + if(changed&&result.page){ + const external={...(result.page.global||{}),worldEpoch:data.worldEpoch,metas:{},states:{}}; + for(const[id,raw]of Object.entries(result.page.metas||{})){const meta=normalizeMeta(id,raw);if(!meta)throw new Error(`Shared metadata is invalid: ${id}`);external.metas[id]=meta;remoteIds.add(id)} + for(const[id,raw]of Object.entries(result.page.states||{}))external.states[id]=normalizeState(raw); + cloudApplyingRemote=true;try{mergeSnapshotIntoData(external,{finalize:false,authoritativeWorld});for(const id of result.page.deleted||[])if(data.metas[id]&&!cloudJournalMetaIds.has(id)&&!cloudJournalStateIds.has(id)){delete data.metas[id];delete data.states[id];markBoardDeleted(id);destroyBoard(rendered.get(id))}for(const id of Object.keys(external.metas))dirtyMetaIds.add(id);for(const id of Object.keys(external.states))dirtyStateIds.add(id)}finally{cloudApplyingRemote=false} + } + cursor=Number.isSafeInteger(result.nextCursor)?result.nextCursor:0; + }while(cursor); + data.cloudRevision=targetRevision||0;lastCloudWorldGlobalSignature=sharedWorldGlobalSignature(); + if(changed){cloudApplyingRemote=true;try{if(fullSnapshot)for(const id of Object.keys(data.metas))if(!remoteIds.has(id)&&(authoritativeWorld||!cloudJournalMetaIds.has(id)&&!cloudJournalStateIds.has(id))){clearSharedWorldJournalRow('meta',id);clearSharedWorldJournalRow('state',id);delete data.metas[id];delete data.states[id];markBoardDeleted(id);destroyBoard(rendered.get(id));destroyStaticBoard(staticRendered.get(id))}resolveMergedOverlaps();statsDirty=true;markGlobalDirty(false)}finally{cloudApplyingRemote=false}refreshWorldView({rebuild:true,syncConnections:true,persist:false});if(!await persistNow({skipCloud:true})){scheduleCloudCheckpointRetry();throw new Error('Shared-world pull could not be committed locally')}}else if(data.cloudRevision!==previousRevision||envelopeChanged){markGlobalDirty(false);if(!await persistNow({skipCloud:true})){scheduleCloudCheckpointRetry();throw new Error('Shared-world revision could not be committed locally')}} + setCloudStatus('共有中','saved');pulled=true;return true; + }catch(error){setCloudStatus('共有失敗','error');console.warn('BEND FIELD: shared-world pull failed',error);return false} + finally{cloudSyncing=false;restoreCloudPushPending();if(cloudPendingHasWork())armCloudPush(pulled?250:5000)} +} +function scheduleCloudPush(signal={},delay=5000){mergeCloudPending(cloudPushPending,signal);armCloudPush(delay)} +async function pushCloudPending(){ + clearTimeout(cloudPushTimer);cloudPushTimer=null;if(typeof fieldArchiveBusy!=='undefined'&&fieldArchiveBusy){armCloudPush(1000);return false}if(!cloudAvailable||!data.cloudProfile||cloudSyncing||!cloudPendingHasWork())return false; + const{batch:pending,remainder}=takeCloudPendingBatch(cloudPushPending),profileIdentity=cloudProfileIdentity();cloudPushPending=remainder; + const globalSignature=sharedWorldGlobalSignature();if(!pending.metaIds.size&&!pending.stateIds.size&&!pending.deleted.size&&pending.globalChanged&&globalSignature===lastCloudWorldGlobalSignature){cloudJournalGlobalChanged=false;cloudOutboxDeleteKeys.add('global');data.cloudPending=currentCloudPending();restoreCloudPushPending();if(!await persistNow({skipCloud:true})){scheduleCloudCheckpointRetry();return false}return true} + cloudSyncing=true;setCloudStatus('共有送信中','syncing');let payload=null,sentMetaRevs=new Map(),sentStateRevs=new Map(),sentCloudChangeSeq=cloudJournalChangeSeq,retryDelay=250; + try{ + const rows=await cloudRowsForStorage(pending.metaIds,pending.stateIds);payload={baseRevision:data.cloudRevision||0,global:sharedWorldGlobalForCloud(),metas:rows.metas,states:rows.states,deleted:[]};sentMetaRevs=new Map(payload.metas.map(meta=>[meta.id,meta.rev||0]));sentStateRevs=new Map(payload.states.map(row=>[row.id,row.value?.rev||0])); + const result=await fetchJson('/api/cloud/push',{method:'POST',headers:cloudAuthHeaders(),body:JSON.stringify(payload)},30000);if(cloudProfileIdentity()!==profileIdentity){mergeCloudPending(cloudPushPending,pending);retryDelay=5000;setCloudStatus('共有保留','error');return false} + serverClockOffset=result.serverTime-Date.now();data.cloudRevision=result.revision;applyCloudEnvelope(result);lastCloudWorldGlobalSignature=sharedWorldGlobalSignature();acknowledgeCloudPending(pending,sentMetaRevs,sentStateRevs,sentCloudChangeSeq);if(!await persistNow({skipCloud:true})){scheduleCloudCheckpointRetry();setCloudStatus('共有保留','error');return false}setCloudStatus('共有中','saved');return true; + }catch(error){mergeCloudPending(cloudPushPending,pending);if(error.status===423&&payload){cloudSyncing=false;const pulled=await pullCloudWorld(true,0,{authoritative:true});retryDelay=pulled?500:5000;setCloudStatus(pulled?'占有期限切れを反映':'共有失敗',pulled?'saved':'error');toast('盤面の占有期限が切れたため、共有状態へ戻しました。')}else if(error.status===409&&payload){cloudSyncing=false;const pulled=await pullCloudWorld(true,payload.baseRevision,{authoritative:payload.baseRevision===0});retryDelay=pulled?250:5000}else if(error.status===400&&payload?.metas?.length){cloudSyncing=false;const pulled=await pullCloudWorld(true,0,{authoritative:true});retryDelay=pulled?500:5000;setCloudStatus(pulled?'共有競合を解消':'共有失敗',pulled?'saved':'error')}else{retryDelay=5000;setCloudStatus('共有失敗','error');console.warn('BEND FIELD: shared-world push failed',error)}return false} + finally{cloudSyncing=false;if(cloudPendingHasWork())armCloudPush(retryDelay)} +} +function scheduleWorldPoll(delay=document.visibilityState==='visible'?6000:20000){clearTimeout(worldPollTimer);if(!cloudAvailable||!data.cloudProfile)return;worldPollTimer=setTimeout(async()=>{worldPollTimer=0;if(document.visibilityState==='visible')await pullCloudWorld();scheduleWorldPoll()},Math.max(1000,delay))} +async function initCloudSync(){ + if(data.cloudSyncPaused){cloudAvailable=false;setCloudStatus('端末のみ','local');return false}if(!fieldIndexComplete)await completeV2IndexScan();if(cloudApiEnabled&&!cloudOutboxReady){cloudAvailable=false;setCloudStatus('共有保留','error');console.warn('BEND FIELD: shared-world sync deferred because the durable outbox could not be verified');return false}if(!cloudApiEnabled){cloudAvailable=false;setCloudStatus('端末のみ','local');return false} + try{const status=await fetchJson('/api/cloud/status',{},2500);cloudAvailable=status.available===true;serverClockOffset=status.serverTime-Date.now()}catch(_){cloudAvailable=false;setCloudStatus('端末のみ','local');return false} + if(!data.cloudProfile)await createCloudProfile();setCloudStatus('共有中','saved');await pullCloudWorld(false,null,{initial:true});await pullPlayerEconomy(); + if((data.cloudRevision||0)===0&&Object.keys(data.metas).length){for(const id of Object.keys(data.metas)){noteCloudRow('meta',id);if(data.states[id]?.solved)noteCloudRow('state',id)}cloudJournalGlobalChanged=true;data.cloudPending=currentCloudPending();markGlobalDirty(false);restoreCloudPushPending();await persistNow({skipCloud:true})} + if(cloudPendingHasWork())await pushCloudPending();connectRealtime();scheduleRealtimeViewport(true);scheduleWorldPoll();return true; +} + +async function changePlayerNameFromTopUi(){ + const entered=prompt('プレイヤー名を入力してください(24文字まで)。',currentPlayerName());if(entered==null)return; + const name=entered.replace(/[\u0000-\u001f\u007f]/g,'').replace(/\s+/g,' ').trim().slice(0,24);if(!name){toast('プレイヤー名を入力してください。');return} + if(cloudAvailable&&data.cloudProfile){ + try{const result=await fetchJson('/api/cloud/profile',{method:'POST',headers:cloudAuthHeaders(),body:JSON.stringify({name})});data.playerName=result.name||name;setCloudStatus('共有中','saved')} + catch(error){toast(`名前を変更できませんでした。 ${error.message}`);return} + }else data.playerName=name; + markGlobalDirty(false);await persistNow({skipCloud:true});updatePlayerNameUi();renderAll();schedulePresenceRender(true);toast(`プレイヤー名を「${data.playerName}」に変更しました。`) +} + +if(cloudBtn)cloudBtn.onclick=async()=>{ + if(data.cloudSyncPaused){if(!confirm('この復元フィールドを共有ワールドへ再接続しますか?'))return;data.cloudSyncPaused=false;markGlobalDirty(false);if(!await persistNow({skipCloud:true})){data.cloudSyncPaused=true;toast('共有ワールドを再開できませんでした。');return}await initCloudSync();return} + if(cloudSyncing){toast('共有処理が終わってから変更してください。');return}if(!cloudAvailable){toast('共有ワールドを利用できません。');return}if(!data.cloudProfile)await createCloudProfile(); + const code=`${data.cloudProfile.playerId}.${data.cloudProfile.token}`,entered=prompt(`プレイヤー名を入力してください(24文字まで)。\n別端末の同期コードを使う場合は、そのコードを貼り付けてください。\n\n現在の同期コード:${code}`,currentPlayerName());if(!entered)return; + const match=/^([a-f0-9]{16,64})\.([a-f0-9]{32,128})$/i.exec(entered.trim());if(match){data.cloudProfile={playerId:match[1],token:match[2]};data.cloudRevision=0;data.worldFeedRevision=0;data.playerPurchases=[];data.playerEarnedScore=0;playerEconomyLoaded=false;markGlobalDirty(false);if(!await persistNow({skipCloud:true})){scheduleCloudCheckpointRetry();toast('同期コードを保存できませんでした。');return}location.reload();return} + const name=entered.replace(/[\u0000-\u001f\u007f]/g,'').replace(/\s+/g,' ').trim().slice(0,24);if(!name){toast('プレイヤー名を入力してください。');return} + try{const result=await fetchJson('/api/cloud/profile',{method:'POST',headers:cloudAuthHeaders(),body:JSON.stringify({name})});data.playerName=result.name;markGlobalDirty(false);await persistNow({skipCloud:true});setCloudStatus('共有中','saved');toast(`プレイヤー名を「${result.name}」に変更しました。`)}catch(error){toast(`名前を変更できませんでした。 ${error.message}`)} +}; +async function init(){ + deleteRetiredWorldData(); + data=attachWorldEpoch(await readInitialDataAsync(),startupWorldEpoch);const generatedInitialPlayerName=!data.playerName||!String(data.playerName).trim();if(generatedInitialPlayerName)data.playerName=createAutomaticPlayerName();seedRevisionClock(data);if(generatedInitialPlayerName)markGlobalDirty(false); + for(const[id,tombstone]of recoveredDeletionTombstones)if(!data.metas[id]){deletedBoardIds.add(id);deletedBoardRevisions.set(id,tombstone.rev||0);deletedBoardAuthors.set(id,tombstone.revAuthor||'')} + for(const id of startupRecoveredMetaIds)if(data.metas[id])dirtyMetaIds.add(id); + for(const id of startupRecoveredStateIds)if(data.states[id])dirtyStateIds.add(id); + for(const id of startupRecoveredDeletedIds)if(!data.metas[id]){deletedBoardIds.add(id);deletedBoardRevisions.set(id,nextRevision());deletedBoardAuthors.set(id,sessionId)} + if(startupRecoveredMetaIds.size||startupRecoveredStateIds.size||startupRecoveredDeletedIds.size)markGlobalDirty(false); + let storedCloudPending=startupCloudOutboxLoaded?startupCloudPending:null; + if(!storedCloudPending&&cloudApiEnabled&&idbAvailable&&idbHealth==='ready')try{storedCloudPending=await withStartupTimeout(loadCloudOutboxFromDb(),IDB_STARTUP_TIMEOUT,'Cloud outbox startup timed out');cloudOutboxReady=true}catch(error){cloudOutboxReady=false;loadNotices.push('\u672a\u9001\u4fe1\u306e\u5909\u66f4\u3092\u78ba\u8a8d\u3067\u304d\u306a\u3044\u305f\u3081\u3001\u30af\u30e9\u30a6\u30c9\u540c\u671f\u3092\u4fdd\u7559\u3057\u307e\u3057\u305f\u3002');console.warn('BEND FIELD: cloud outbox startup fallback',error)} + else if(startupCloudOutboxLoaded||!cloudApiEnabled)cloudOutboxReady=true;else cloudOutboxReady=false; + storedCloudPending=storedCloudPending||normalizeCloudPending(null); + const initialCloudPending=emptyCloudPending();mergeCloudPending(initialCloudPending,data.cloudPending);mergeCloudPending(initialCloudPending,storedCloudPending); + restoreCloudPending({metaIds:[...initialCloudPending.metaIds],stateIds:[...initialCloudPending.stateIds],deleted:[...initialCloudPending.deleted],globalChanged:initialCloudPending.globalChanged});restoreCloudPushPending(); + applyUiSettings();syncCursorAppearance(data.cursorStyle||'default');document.body.dataset.debugItems=data.debugAllItems?'on':'off'; + stateStatSignatures.clear();stateEconomySignatures.clear();for(const[id,state]of Object.entries(data.states)){normalizedStateObjects.add(state);if(!state._summaryOnly){stateStatSignatures.set(id,stateStatSignature(state));stateEconomySignatures.set(id,stateEconomySignature(state))}} + orphanPruneDirty=true;statsDirty=true;invalidateEconomyCaches(); + if(!fieldIndexComplete){cachedStats={solved:data.solved||0,score:data.score||0,earned:null};statsDirty=false} + await ensureStart(); + const initialMeta=data.metas[data.selectedBoardId]||data.metas.B0||Object.values(data.metas)[0]; + try{ + if(initialMeta&&!initialMeta.puzzle)await hydrateMeta(initialMeta); + if(!data.metas.B0?.puzzle&&data.metas.B0)await hydrateMeta(data.metas.B0); + if(!data.metas.B0?.puzzle)throw new Error('\u539f\u70b9\u306e\u76e4\u9762\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3002'); + if(startupWorldControl?.activationVerified===false)await validateActiveWorldReadiness(data.worldEpoch); + }catch(error){ + if(startupWorldControl?.activationVerified===false&&idbAvailable){const restored=await rollbackUnverifiedWorld(await openWorldDb(),startupWorldControl).catch(()=>null);if(restored){lifecyclePersistenceSuppressed=true;rememberWorldEpoch(restored.activeEpoch);announceWorldReplacement(restored.activeEpoch);location.reload();return}} + throw error; + } + refreshWorldView({rebuild:true,syncConnections:false,resumeTimer:true}); + requestAnimationFrame(()=>{if(!restoreSavedCamera())centerMeta(initialMeta||data.metas.B0,{select:false});scheduleMinimap(true);scheduleNoiseBackground(true)}); + document.body.classList.remove('loading');document.body.dataset.ready='true';document.body.dataset.generator='gate-procedural-v47'; + if(!fieldIndexComplete)setTimeout(()=>{void completeV2IndexScan().catch(error=>{console.warn('BEND FIELD: field index scan failed',error);showStatus(`\u30d5\u30a3\u30fc\u30eb\u30c9\u7d22\u5f15\u3092\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093\u3067\u3057\u305f\u3002 ${error?.message||error}`,{retry:true,fresh:false})})},0); + drainWorldSignals(); + openHelp(true); + if(loadNotices.length)showStatus(loadNotices.join(' '),{retry:false,fresh:true}); + if(hasPendingPersistence())void save(true); + // Warm the generator process only; no surrounding puzzle is generated before a solved gate opens it. + setTimeout(()=>{createPuzzleWorker();if(navigator.storage?.persist)void navigator.storage.persist().catch(()=>{})},0); + setTimeout(()=>{void collectV2Garbage().catch(error=>console.warn('BEND FIELD: epoch cleanup deferred',error))},1500); + setTimeout(()=>{ + reopenMissingGateExpansions(); + if(pendingExpansionCount())void repairExpansions().catch(handleExpansionError); + },100); + if(cloudApiEnabled)setTimeout(()=>{void initCloudSync().catch(error=>console.warn('BEND FIELD: deferred cloud sync failed',error))},1000);else setCloudStatus('端末のみ','local'); +} +init().catch(error=>{ + console.error(error);document.body.classList.remove('loading');document.body.dataset.ready='error';document.body.dataset.error=error?.message||String(error); + preserveRecovery(readCompactMirrorRaw()||safeLocalGet(storageKey),'Startup failed'); + showStatus(`\u30b2\u30fc\u30e0\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\uff1a${error?.message||error}`,{retry:true,fresh:true});toast('\u30b2\u30fc\u30e0\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3002',4000); +}); + +document.addEventListener('click',event=>{const button=event.target.closest?.('button');if(button&&!button.disabled&&!button.closest('.board-actions')&&!button.classList.contains('store-buy')&&!button.classList.contains('line-store'))playSound('click')},{capture:true}); diff --git a/assets/flags/1f1e6-1f1e8.svg b/assets/flags/1f1e6-1f1e8.svg new file mode 100644 index 0000000..53f90dc --- /dev/null +++ b/assets/flags/1f1e6-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1e9.svg b/assets/flags/1f1e6-1f1e9.svg new file mode 100644 index 0000000..be10594 --- /dev/null +++ b/assets/flags/1f1e6-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1ea.svg b/assets/flags/1f1e6-1f1ea.svg new file mode 100644 index 0000000..be8e114 --- /dev/null +++ b/assets/flags/1f1e6-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1eb.svg b/assets/flags/1f1e6-1f1eb.svg new file mode 100644 index 0000000..769efca --- /dev/null +++ b/assets/flags/1f1e6-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1ec.svg b/assets/flags/1f1e6-1f1ec.svg new file mode 100644 index 0000000..2716617 --- /dev/null +++ b/assets/flags/1f1e6-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1ee.svg b/assets/flags/1f1e6-1f1ee.svg new file mode 100644 index 0000000..6a91dd9 --- /dev/null +++ b/assets/flags/1f1e6-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1f1.svg b/assets/flags/1f1e6-1f1f1.svg new file mode 100644 index 0000000..2c8655d --- /dev/null +++ b/assets/flags/1f1e6-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1f2.svg b/assets/flags/1f1e6-1f1f2.svg new file mode 100644 index 0000000..0a966ab --- /dev/null +++ b/assets/flags/1f1e6-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1f4.svg b/assets/flags/1f1e6-1f1f4.svg new file mode 100644 index 0000000..65803b6 --- /dev/null +++ b/assets/flags/1f1e6-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1f6.svg b/assets/flags/1f1e6-1f1f6.svg new file mode 100644 index 0000000..fd29680 --- /dev/null +++ b/assets/flags/1f1e6-1f1f6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1f7.svg b/assets/flags/1f1e6-1f1f7.svg new file mode 100644 index 0000000..e8e60ef --- /dev/null +++ b/assets/flags/1f1e6-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1f8.svg b/assets/flags/1f1e6-1f1f8.svg new file mode 100644 index 0000000..8b27532 --- /dev/null +++ b/assets/flags/1f1e6-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1f9.svg b/assets/flags/1f1e6-1f1f9.svg new file mode 100644 index 0000000..bfe1ec7 --- /dev/null +++ b/assets/flags/1f1e6-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1fa.svg b/assets/flags/1f1e6-1f1fa.svg new file mode 100644 index 0000000..989da76 --- /dev/null +++ b/assets/flags/1f1e6-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1fc.svg b/assets/flags/1f1e6-1f1fc.svg new file mode 100644 index 0000000..f383951 --- /dev/null +++ b/assets/flags/1f1e6-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1fd.svg b/assets/flags/1f1e6-1f1fd.svg new file mode 100644 index 0000000..03bc680 --- /dev/null +++ b/assets/flags/1f1e6-1f1fd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e6-1f1ff.svg b/assets/flags/1f1e6-1f1ff.svg new file mode 100644 index 0000000..b584854 --- /dev/null +++ b/assets/flags/1f1e6-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1e6.svg b/assets/flags/1f1e7-1f1e6.svg new file mode 100644 index 0000000..bbcd3b5 --- /dev/null +++ b/assets/flags/1f1e7-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1e7.svg b/assets/flags/1f1e7-1f1e7.svg new file mode 100644 index 0000000..7f9e8c9 --- /dev/null +++ b/assets/flags/1f1e7-1f1e7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1e9.svg b/assets/flags/1f1e7-1f1e9.svg new file mode 100644 index 0000000..6edc844 --- /dev/null +++ b/assets/flags/1f1e7-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1ea.svg b/assets/flags/1f1e7-1f1ea.svg new file mode 100644 index 0000000..8670426 --- /dev/null +++ b/assets/flags/1f1e7-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1eb.svg b/assets/flags/1f1e7-1f1eb.svg new file mode 100644 index 0000000..8bceec7 --- /dev/null +++ b/assets/flags/1f1e7-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1ec.svg b/assets/flags/1f1e7-1f1ec.svg new file mode 100644 index 0000000..6e81fba --- /dev/null +++ b/assets/flags/1f1e7-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1ed.svg b/assets/flags/1f1e7-1f1ed.svg new file mode 100644 index 0000000..73de582 --- /dev/null +++ b/assets/flags/1f1e7-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1ee.svg b/assets/flags/1f1e7-1f1ee.svg new file mode 100644 index 0000000..e53644c --- /dev/null +++ b/assets/flags/1f1e7-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1ef.svg b/assets/flags/1f1e7-1f1ef.svg new file mode 100644 index 0000000..133d711 --- /dev/null +++ b/assets/flags/1f1e7-1f1ef.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f1.svg b/assets/flags/1f1e7-1f1f1.svg new file mode 100644 index 0000000..9d4904d --- /dev/null +++ b/assets/flags/1f1e7-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f2.svg b/assets/flags/1f1e7-1f1f2.svg new file mode 100644 index 0000000..5e7b7f6 --- /dev/null +++ b/assets/flags/1f1e7-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f3.svg b/assets/flags/1f1e7-1f1f3.svg new file mode 100644 index 0000000..3c20edb --- /dev/null +++ b/assets/flags/1f1e7-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f4.svg b/assets/flags/1f1e7-1f1f4.svg new file mode 100644 index 0000000..ad0a8c9 --- /dev/null +++ b/assets/flags/1f1e7-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f6.svg b/assets/flags/1f1e7-1f1f6.svg new file mode 100644 index 0000000..bde4921 --- /dev/null +++ b/assets/flags/1f1e7-1f1f6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f7.svg b/assets/flags/1f1e7-1f1f7.svg new file mode 100644 index 0000000..956e39d --- /dev/null +++ b/assets/flags/1f1e7-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f8.svg b/assets/flags/1f1e7-1f1f8.svg new file mode 100644 index 0000000..a75f68b --- /dev/null +++ b/assets/flags/1f1e7-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1f9.svg b/assets/flags/1f1e7-1f1f9.svg new file mode 100644 index 0000000..e822f94 --- /dev/null +++ b/assets/flags/1f1e7-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1fb.svg b/assets/flags/1f1e7-1f1fb.svg new file mode 100644 index 0000000..3d104a6 --- /dev/null +++ b/assets/flags/1f1e7-1f1fb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1fc.svg b/assets/flags/1f1e7-1f1fc.svg new file mode 100644 index 0000000..5edeb5d --- /dev/null +++ b/assets/flags/1f1e7-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1fe.svg b/assets/flags/1f1e7-1f1fe.svg new file mode 100644 index 0000000..3fef573 --- /dev/null +++ b/assets/flags/1f1e7-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e7-1f1ff.svg b/assets/flags/1f1e7-1f1ff.svg new file mode 100644 index 0000000..6f43e4a --- /dev/null +++ b/assets/flags/1f1e7-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1e6.svg b/assets/flags/1f1e8-1f1e6.svg new file mode 100644 index 0000000..d9c386d --- /dev/null +++ b/assets/flags/1f1e8-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1e8.svg b/assets/flags/1f1e8-1f1e8.svg new file mode 100644 index 0000000..ce130d7 --- /dev/null +++ b/assets/flags/1f1e8-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1e9.svg b/assets/flags/1f1e8-1f1e9.svg new file mode 100644 index 0000000..d1b15c9 --- /dev/null +++ b/assets/flags/1f1e8-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1eb.svg b/assets/flags/1f1e8-1f1eb.svg new file mode 100644 index 0000000..72166cb --- /dev/null +++ b/assets/flags/1f1e8-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1ec.svg b/assets/flags/1f1e8-1f1ec.svg new file mode 100644 index 0000000..3d466e3 --- /dev/null +++ b/assets/flags/1f1e8-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1ed.svg b/assets/flags/1f1e8-1f1ed.svg new file mode 100644 index 0000000..741b521 --- /dev/null +++ b/assets/flags/1f1e8-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1ee.svg b/assets/flags/1f1e8-1f1ee.svg new file mode 100644 index 0000000..bd2c3e0 --- /dev/null +++ b/assets/flags/1f1e8-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f0.svg b/assets/flags/1f1e8-1f1f0.svg new file mode 100644 index 0000000..04e0344 --- /dev/null +++ b/assets/flags/1f1e8-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f1.svg b/assets/flags/1f1e8-1f1f1.svg new file mode 100644 index 0000000..52b3a00 --- /dev/null +++ b/assets/flags/1f1e8-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f2.svg b/assets/flags/1f1e8-1f1f2.svg new file mode 100644 index 0000000..7da7b66 --- /dev/null +++ b/assets/flags/1f1e8-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f3.svg b/assets/flags/1f1e8-1f1f3.svg new file mode 100644 index 0000000..c10116d --- /dev/null +++ b/assets/flags/1f1e8-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f4.svg b/assets/flags/1f1e8-1f1f4.svg new file mode 100644 index 0000000..dc825d2 --- /dev/null +++ b/assets/flags/1f1e8-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f5.svg b/assets/flags/1f1e8-1f1f5.svg new file mode 100644 index 0000000..4eafe7a --- /dev/null +++ b/assets/flags/1f1e8-1f1f5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f6.svg b/assets/flags/1f1e8-1f1f6.svg new file mode 100644 index 0000000..06294f8 --- /dev/null +++ b/assets/flags/1f1e8-1f1f6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1f7.svg b/assets/flags/1f1e8-1f1f7.svg new file mode 100644 index 0000000..acecc89 --- /dev/null +++ b/assets/flags/1f1e8-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1fa.svg b/assets/flags/1f1e8-1f1fa.svg new file mode 100644 index 0000000..13b6370 --- /dev/null +++ b/assets/flags/1f1e8-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1fb.svg b/assets/flags/1f1e8-1f1fb.svg new file mode 100644 index 0000000..9b2cc18 --- /dev/null +++ b/assets/flags/1f1e8-1f1fb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1fc.svg b/assets/flags/1f1e8-1f1fc.svg new file mode 100644 index 0000000..c53d09f --- /dev/null +++ b/assets/flags/1f1e8-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1fd.svg b/assets/flags/1f1e8-1f1fd.svg new file mode 100644 index 0000000..6a322f5 --- /dev/null +++ b/assets/flags/1f1e8-1f1fd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1fe.svg b/assets/flags/1f1e8-1f1fe.svg new file mode 100644 index 0000000..19bead4 --- /dev/null +++ b/assets/flags/1f1e8-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e8-1f1ff.svg b/assets/flags/1f1e8-1f1ff.svg new file mode 100644 index 0000000..fd3b470 --- /dev/null +++ b/assets/flags/1f1e8-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e9-1f1ea.svg b/assets/flags/1f1e9-1f1ea.svg new file mode 100644 index 0000000..10a5399 --- /dev/null +++ b/assets/flags/1f1e9-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e9-1f1ec.svg b/assets/flags/1f1e9-1f1ec.svg new file mode 100644 index 0000000..565a7aa --- /dev/null +++ b/assets/flags/1f1e9-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e9-1f1ef.svg b/assets/flags/1f1e9-1f1ef.svg new file mode 100644 index 0000000..42cbb24 --- /dev/null +++ b/assets/flags/1f1e9-1f1ef.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e9-1f1f0.svg b/assets/flags/1f1e9-1f1f0.svg new file mode 100644 index 0000000..5ab629b --- /dev/null +++ b/assets/flags/1f1e9-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e9-1f1f2.svg b/assets/flags/1f1e9-1f1f2.svg new file mode 100644 index 0000000..750424f --- /dev/null +++ b/assets/flags/1f1e9-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e9-1f1f4.svg b/assets/flags/1f1e9-1f1f4.svg new file mode 100644 index 0000000..c627c34 --- /dev/null +++ b/assets/flags/1f1e9-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1e9-1f1ff.svg b/assets/flags/1f1e9-1f1ff.svg new file mode 100644 index 0000000..c29a7e2 --- /dev/null +++ b/assets/flags/1f1e9-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1e6.svg b/assets/flags/1f1ea-1f1e6.svg new file mode 100644 index 0000000..d1fd565 --- /dev/null +++ b/assets/flags/1f1ea-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1e8.svg b/assets/flags/1f1ea-1f1e8.svg new file mode 100644 index 0000000..c035be7 --- /dev/null +++ b/assets/flags/1f1ea-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1ea.svg b/assets/flags/1f1ea-1f1ea.svg new file mode 100644 index 0000000..47a5589 --- /dev/null +++ b/assets/flags/1f1ea-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1ec.svg b/assets/flags/1f1ea-1f1ec.svg new file mode 100644 index 0000000..2034a3e --- /dev/null +++ b/assets/flags/1f1ea-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1ed.svg b/assets/flags/1f1ea-1f1ed.svg new file mode 100644 index 0000000..9b8dc5a --- /dev/null +++ b/assets/flags/1f1ea-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1f7.svg b/assets/flags/1f1ea-1f1f7.svg new file mode 100644 index 0000000..8e1e510 --- /dev/null +++ b/assets/flags/1f1ea-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1f8.svg b/assets/flags/1f1ea-1f1f8.svg new file mode 100644 index 0000000..d1fd565 --- /dev/null +++ b/assets/flags/1f1ea-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1f9.svg b/assets/flags/1f1ea-1f1f9.svg new file mode 100644 index 0000000..762cc1f --- /dev/null +++ b/assets/flags/1f1ea-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ea-1f1fa.svg b/assets/flags/1f1ea-1f1fa.svg new file mode 100644 index 0000000..045024a --- /dev/null +++ b/assets/flags/1f1ea-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1eb-1f1ee.svg b/assets/flags/1f1eb-1f1ee.svg new file mode 100644 index 0000000..e07328e --- /dev/null +++ b/assets/flags/1f1eb-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1eb-1f1ef.svg b/assets/flags/1f1eb-1f1ef.svg new file mode 100644 index 0000000..190134b --- /dev/null +++ b/assets/flags/1f1eb-1f1ef.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1eb-1f1f0.svg b/assets/flags/1f1eb-1f1f0.svg new file mode 100644 index 0000000..0091bc7 --- /dev/null +++ b/assets/flags/1f1eb-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1eb-1f1f2.svg b/assets/flags/1f1eb-1f1f2.svg new file mode 100644 index 0000000..b49556b --- /dev/null +++ b/assets/flags/1f1eb-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1eb-1f1f4.svg b/assets/flags/1f1eb-1f1f4.svg new file mode 100644 index 0000000..93a1272 --- /dev/null +++ b/assets/flags/1f1eb-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1eb-1f1f7.svg b/assets/flags/1f1eb-1f1f7.svg new file mode 100644 index 0000000..4eafe7a --- /dev/null +++ b/assets/flags/1f1eb-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1e6.svg b/assets/flags/1f1ec-1f1e6.svg new file mode 100644 index 0000000..a8c6fa4 --- /dev/null +++ b/assets/flags/1f1ec-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1e7.svg b/assets/flags/1f1ec-1f1e7.svg new file mode 100644 index 0000000..21b97e9 --- /dev/null +++ b/assets/flags/1f1ec-1f1e7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1e9.svg b/assets/flags/1f1ec-1f1e9.svg new file mode 100644 index 0000000..e4f37f9 --- /dev/null +++ b/assets/flags/1f1ec-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1ea.svg b/assets/flags/1f1ec-1f1ea.svg new file mode 100644 index 0000000..8c2bd5a --- /dev/null +++ b/assets/flags/1f1ec-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1eb.svg b/assets/flags/1f1ec-1f1eb.svg new file mode 100644 index 0000000..2f10cee --- /dev/null +++ b/assets/flags/1f1ec-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1ec.svg b/assets/flags/1f1ec-1f1ec.svg new file mode 100644 index 0000000..84f6043 --- /dev/null +++ b/assets/flags/1f1ec-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1ed.svg b/assets/flags/1f1ec-1f1ed.svg new file mode 100644 index 0000000..3330218 --- /dev/null +++ b/assets/flags/1f1ec-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1ee.svg b/assets/flags/1f1ec-1f1ee.svg new file mode 100644 index 0000000..432a727 --- /dev/null +++ b/assets/flags/1f1ec-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f1.svg b/assets/flags/1f1ec-1f1f1.svg new file mode 100644 index 0000000..8a2ba3e --- /dev/null +++ b/assets/flags/1f1ec-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f2.svg b/assets/flags/1f1ec-1f1f2.svg new file mode 100644 index 0000000..383cf9d --- /dev/null +++ b/assets/flags/1f1ec-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f3.svg b/assets/flags/1f1ec-1f1f3.svg new file mode 100644 index 0000000..16f4a90 --- /dev/null +++ b/assets/flags/1f1ec-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f5.svg b/assets/flags/1f1ec-1f1f5.svg new file mode 100644 index 0000000..ca9e4c6 --- /dev/null +++ b/assets/flags/1f1ec-1f1f5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f6.svg b/assets/flags/1f1ec-1f1f6.svg new file mode 100644 index 0000000..d4e7119 --- /dev/null +++ b/assets/flags/1f1ec-1f1f6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f7.svg b/assets/flags/1f1ec-1f1f7.svg new file mode 100644 index 0000000..74d842d --- /dev/null +++ b/assets/flags/1f1ec-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f8.svg b/assets/flags/1f1ec-1f1f8.svg new file mode 100644 index 0000000..d8b1e5f --- /dev/null +++ b/assets/flags/1f1ec-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1f9.svg b/assets/flags/1f1ec-1f1f9.svg new file mode 100644 index 0000000..fea623c --- /dev/null +++ b/assets/flags/1f1ec-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1fa.svg b/assets/flags/1f1ec-1f1fa.svg new file mode 100644 index 0000000..2098ecc --- /dev/null +++ b/assets/flags/1f1ec-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1fc.svg b/assets/flags/1f1ec-1f1fc.svg new file mode 100644 index 0000000..6e01b9e --- /dev/null +++ b/assets/flags/1f1ec-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ec-1f1fe.svg b/assets/flags/1f1ec-1f1fe.svg new file mode 100644 index 0000000..1edc6ef --- /dev/null +++ b/assets/flags/1f1ec-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ed-1f1f0.svg b/assets/flags/1f1ed-1f1f0.svg new file mode 100644 index 0000000..ef5ca3b --- /dev/null +++ b/assets/flags/1f1ed-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ed-1f1f2.svg b/assets/flags/1f1ed-1f1f2.svg new file mode 100644 index 0000000..989da76 --- /dev/null +++ b/assets/flags/1f1ed-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ed-1f1f3.svg b/assets/flags/1f1ed-1f1f3.svg new file mode 100644 index 0000000..298ec95 --- /dev/null +++ b/assets/flags/1f1ed-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ed-1f1f7.svg b/assets/flags/1f1ed-1f1f7.svg new file mode 100644 index 0000000..7b8740c --- /dev/null +++ b/assets/flags/1f1ed-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ed-1f1f9.svg b/assets/flags/1f1ed-1f1f9.svg new file mode 100644 index 0000000..8ccca42 --- /dev/null +++ b/assets/flags/1f1ed-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ed-1f1fa.svg b/assets/flags/1f1ed-1f1fa.svg new file mode 100644 index 0000000..206baa1 --- /dev/null +++ b/assets/flags/1f1ed-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1e8.svg b/assets/flags/1f1ee-1f1e8.svg new file mode 100644 index 0000000..46b0949 --- /dev/null +++ b/assets/flags/1f1ee-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1e9.svg b/assets/flags/1f1ee-1f1e9.svg new file mode 100644 index 0000000..de31273 --- /dev/null +++ b/assets/flags/1f1ee-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1ea.svg b/assets/flags/1f1ee-1f1ea.svg new file mode 100644 index 0000000..3c50257 --- /dev/null +++ b/assets/flags/1f1ee-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f1.svg b/assets/flags/1f1ee-1f1f1.svg new file mode 100644 index 0000000..5cf3241 --- /dev/null +++ b/assets/flags/1f1ee-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f2.svg b/assets/flags/1f1ee-1f1f2.svg new file mode 100644 index 0000000..7fc9d46 --- /dev/null +++ b/assets/flags/1f1ee-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f3.svg b/assets/flags/1f1ee-1f1f3.svg new file mode 100644 index 0000000..55f97e6 --- /dev/null +++ b/assets/flags/1f1ee-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f4.svg b/assets/flags/1f1ee-1f1f4.svg new file mode 100644 index 0000000..565a7aa --- /dev/null +++ b/assets/flags/1f1ee-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f6.svg b/assets/flags/1f1ee-1f1f6.svg new file mode 100644 index 0000000..06cfe31 --- /dev/null +++ b/assets/flags/1f1ee-1f1f6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f7.svg b/assets/flags/1f1ee-1f1f7.svg new file mode 100644 index 0000000..e8ae7b1 --- /dev/null +++ b/assets/flags/1f1ee-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f8.svg b/assets/flags/1f1ee-1f1f8.svg new file mode 100644 index 0000000..c8e918c --- /dev/null +++ b/assets/flags/1f1ee-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ee-1f1f9.svg b/assets/flags/1f1ee-1f1f9.svg new file mode 100644 index 0000000..6c38017 --- /dev/null +++ b/assets/flags/1f1ee-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ef-1f1ea.svg b/assets/flags/1f1ef-1f1ea.svg new file mode 100644 index 0000000..a17c379 --- /dev/null +++ b/assets/flags/1f1ef-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ef-1f1f2.svg b/assets/flags/1f1ef-1f1f2.svg new file mode 100644 index 0000000..dd82d4f --- /dev/null +++ b/assets/flags/1f1ef-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ef-1f1f4.svg b/assets/flags/1f1ef-1f1f4.svg new file mode 100644 index 0000000..40710a5 --- /dev/null +++ b/assets/flags/1f1ef-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ef-1f1f5.svg b/assets/flags/1f1ef-1f1f5.svg new file mode 100644 index 0000000..3a724e9 --- /dev/null +++ b/assets/flags/1f1ef-1f1f5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1ea.svg b/assets/flags/1f1f0-1f1ea.svg new file mode 100644 index 0000000..5bee37f --- /dev/null +++ b/assets/flags/1f1f0-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1ec.svg b/assets/flags/1f1f0-1f1ec.svg new file mode 100644 index 0000000..2616d9e --- /dev/null +++ b/assets/flags/1f1f0-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1ed.svg b/assets/flags/1f1f0-1f1ed.svg new file mode 100644 index 0000000..54f6e90 --- /dev/null +++ b/assets/flags/1f1f0-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1ee.svg b/assets/flags/1f1f0-1f1ee.svg new file mode 100644 index 0000000..233cce8 --- /dev/null +++ b/assets/flags/1f1f0-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1f2.svg b/assets/flags/1f1f0-1f1f2.svg new file mode 100644 index 0000000..91c12b8 --- /dev/null +++ b/assets/flags/1f1f0-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1f3.svg b/assets/flags/1f1f0-1f1f3.svg new file mode 100644 index 0000000..461e0f2 --- /dev/null +++ b/assets/flags/1f1f0-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1f5.svg b/assets/flags/1f1f0-1f1f5.svg new file mode 100644 index 0000000..d530523 --- /dev/null +++ b/assets/flags/1f1f0-1f1f5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1f7.svg b/assets/flags/1f1f0-1f1f7.svg new file mode 100644 index 0000000..7b5ee23 --- /dev/null +++ b/assets/flags/1f1f0-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1fc.svg b/assets/flags/1f1f0-1f1fc.svg new file mode 100644 index 0000000..db949b2 --- /dev/null +++ b/assets/flags/1f1f0-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1fe.svg b/assets/flags/1f1f0-1f1fe.svg new file mode 100644 index 0000000..57323f8 --- /dev/null +++ b/assets/flags/1f1f0-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f0-1f1ff.svg b/assets/flags/1f1f0-1f1ff.svg new file mode 100644 index 0000000..d2101ab --- /dev/null +++ b/assets/flags/1f1f0-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1e6.svg b/assets/flags/1f1f1-1f1e6.svg new file mode 100644 index 0000000..0ea005d --- /dev/null +++ b/assets/flags/1f1f1-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1e7.svg b/assets/flags/1f1f1-1f1e7.svg new file mode 100644 index 0000000..4271b73 --- /dev/null +++ b/assets/flags/1f1f1-1f1e7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1e8.svg b/assets/flags/1f1f1-1f1e8.svg new file mode 100644 index 0000000..12b2237 --- /dev/null +++ b/assets/flags/1f1f1-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1ee.svg b/assets/flags/1f1f1-1f1ee.svg new file mode 100644 index 0000000..9e474bc --- /dev/null +++ b/assets/flags/1f1f1-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1f0.svg b/assets/flags/1f1f1-1f1f0.svg new file mode 100644 index 0000000..a2fe814 --- /dev/null +++ b/assets/flags/1f1f1-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1f7.svg b/assets/flags/1f1f1-1f1f7.svg new file mode 100644 index 0000000..dd4a1e4 --- /dev/null +++ b/assets/flags/1f1f1-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1f8.svg b/assets/flags/1f1f1-1f1f8.svg new file mode 100644 index 0000000..ec06e4f --- /dev/null +++ b/assets/flags/1f1f1-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1f9.svg b/assets/flags/1f1f1-1f1f9.svg new file mode 100644 index 0000000..5fcfd8b --- /dev/null +++ b/assets/flags/1f1f1-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1fa.svg b/assets/flags/1f1f1-1f1fa.svg new file mode 100644 index 0000000..e66c904 --- /dev/null +++ b/assets/flags/1f1f1-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1fb.svg b/assets/flags/1f1f1-1f1fb.svg new file mode 100644 index 0000000..f5f3922 --- /dev/null +++ b/assets/flags/1f1f1-1f1fb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f1-1f1fe.svg b/assets/flags/1f1f1-1f1fe.svg new file mode 100644 index 0000000..c6c12ed --- /dev/null +++ b/assets/flags/1f1f1-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1e6.svg b/assets/flags/1f1f2-1f1e6.svg new file mode 100644 index 0000000..d6d689a --- /dev/null +++ b/assets/flags/1f1f2-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1e8.svg b/assets/flags/1f1f2-1f1e8.svg new file mode 100644 index 0000000..8604a1c --- /dev/null +++ b/assets/flags/1f1f2-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1e9.svg b/assets/flags/1f1f2-1f1e9.svg new file mode 100644 index 0000000..eb2d4a2 --- /dev/null +++ b/assets/flags/1f1f2-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1ea.svg b/assets/flags/1f1f2-1f1ea.svg new file mode 100644 index 0000000..47c5b2e --- /dev/null +++ b/assets/flags/1f1f2-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1eb.svg b/assets/flags/1f1f2-1f1eb.svg new file mode 100644 index 0000000..4eafe7a --- /dev/null +++ b/assets/flags/1f1f2-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1ec.svg b/assets/flags/1f1f2-1f1ec.svg new file mode 100644 index 0000000..becf2f4 --- /dev/null +++ b/assets/flags/1f1f2-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1ed.svg b/assets/flags/1f1f2-1f1ed.svg new file mode 100644 index 0000000..6774f9b --- /dev/null +++ b/assets/flags/1f1f2-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f0.svg b/assets/flags/1f1f2-1f1f0.svg new file mode 100644 index 0000000..371b235 --- /dev/null +++ b/assets/flags/1f1f2-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f1.svg b/assets/flags/1f1f2-1f1f1.svg new file mode 100644 index 0000000..3a522a0 --- /dev/null +++ b/assets/flags/1f1f2-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f2.svg b/assets/flags/1f1f2-1f1f2.svg new file mode 100644 index 0000000..69db533 --- /dev/null +++ b/assets/flags/1f1f2-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f3.svg b/assets/flags/1f1f2-1f1f3.svg new file mode 100644 index 0000000..b9635cf --- /dev/null +++ b/assets/flags/1f1f2-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f4.svg b/assets/flags/1f1f2-1f1f4.svg new file mode 100644 index 0000000..790900e --- /dev/null +++ b/assets/flags/1f1f2-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f5.svg b/assets/flags/1f1f2-1f1f5.svg new file mode 100644 index 0000000..f0a5fb4 --- /dev/null +++ b/assets/flags/1f1f2-1f1f5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f6.svg b/assets/flags/1f1f2-1f1f6.svg new file mode 100644 index 0000000..f705309 --- /dev/null +++ b/assets/flags/1f1f2-1f1f6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f7.svg b/assets/flags/1f1f2-1f1f7.svg new file mode 100644 index 0000000..8335c8b --- /dev/null +++ b/assets/flags/1f1f2-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f8.svg b/assets/flags/1f1f2-1f1f8.svg new file mode 100644 index 0000000..04a1cc1 --- /dev/null +++ b/assets/flags/1f1f2-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1f9.svg b/assets/flags/1f1f2-1f1f9.svg new file mode 100644 index 0000000..5538102 --- /dev/null +++ b/assets/flags/1f1f2-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1fa.svg b/assets/flags/1f1f2-1f1fa.svg new file mode 100644 index 0000000..6c24981 --- /dev/null +++ b/assets/flags/1f1f2-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1fb.svg b/assets/flags/1f1f2-1f1fb.svg new file mode 100644 index 0000000..b57be9c --- /dev/null +++ b/assets/flags/1f1f2-1f1fb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1fc.svg b/assets/flags/1f1f2-1f1fc.svg new file mode 100644 index 0000000..9b8ddf5 --- /dev/null +++ b/assets/flags/1f1f2-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1fd.svg b/assets/flags/1f1f2-1f1fd.svg new file mode 100644 index 0000000..93d54c4 --- /dev/null +++ b/assets/flags/1f1f2-1f1fd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1fe.svg b/assets/flags/1f1f2-1f1fe.svg new file mode 100644 index 0000000..29a3e3f --- /dev/null +++ b/assets/flags/1f1f2-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f2-1f1ff.svg b/assets/flags/1f1f2-1f1ff.svg new file mode 100644 index 0000000..cfa9577 --- /dev/null +++ b/assets/flags/1f1f2-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1e6.svg b/assets/flags/1f1f3-1f1e6.svg new file mode 100644 index 0000000..d2a79fd --- /dev/null +++ b/assets/flags/1f1f3-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1e8.svg b/assets/flags/1f1f3-1f1e8.svg new file mode 100644 index 0000000..e5dff93 --- /dev/null +++ b/assets/flags/1f1f3-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1ea.svg b/assets/flags/1f1f3-1f1ea.svg new file mode 100644 index 0000000..53f25f5 --- /dev/null +++ b/assets/flags/1f1f3-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1eb.svg b/assets/flags/1f1f3-1f1eb.svg new file mode 100644 index 0000000..990687f --- /dev/null +++ b/assets/flags/1f1f3-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1ec.svg b/assets/flags/1f1f3-1f1ec.svg new file mode 100644 index 0000000..6c6e31c --- /dev/null +++ b/assets/flags/1f1f3-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1ee.svg b/assets/flags/1f1f3-1f1ee.svg new file mode 100644 index 0000000..990868a --- /dev/null +++ b/assets/flags/1f1f3-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1f1.svg b/assets/flags/1f1f3-1f1f1.svg new file mode 100644 index 0000000..65e8be9 --- /dev/null +++ b/assets/flags/1f1f3-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1f4.svg b/assets/flags/1f1f3-1f1f4.svg new file mode 100644 index 0000000..4f5260a --- /dev/null +++ b/assets/flags/1f1f3-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1f5.svg b/assets/flags/1f1f3-1f1f5.svg new file mode 100644 index 0000000..5e5faaf --- /dev/null +++ b/assets/flags/1f1f3-1f1f5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1f7.svg b/assets/flags/1f1f3-1f1f7.svg new file mode 100644 index 0000000..72485e7 --- /dev/null +++ b/assets/flags/1f1f3-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1fa.svg b/assets/flags/1f1f3-1f1fa.svg new file mode 100644 index 0000000..dd50901 --- /dev/null +++ b/assets/flags/1f1f3-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f3-1f1ff.svg b/assets/flags/1f1f3-1f1ff.svg new file mode 100644 index 0000000..956a9d2 --- /dev/null +++ b/assets/flags/1f1f3-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f4-1f1f2.svg b/assets/flags/1f1f4-1f1f2.svg new file mode 100644 index 0000000..29af825 --- /dev/null +++ b/assets/flags/1f1f4-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1e6.svg b/assets/flags/1f1f5-1f1e6.svg new file mode 100644 index 0000000..4fc55f5 --- /dev/null +++ b/assets/flags/1f1f5-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1ea.svg b/assets/flags/1f1f5-1f1ea.svg new file mode 100644 index 0000000..fc93b29 --- /dev/null +++ b/assets/flags/1f1f5-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1eb.svg b/assets/flags/1f1f5-1f1eb.svg new file mode 100644 index 0000000..333c6d0 --- /dev/null +++ b/assets/flags/1f1f5-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1ec.svg b/assets/flags/1f1f5-1f1ec.svg new file mode 100644 index 0000000..2d20ed8 --- /dev/null +++ b/assets/flags/1f1f5-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1ed.svg b/assets/flags/1f1f5-1f1ed.svg new file mode 100644 index 0000000..e9f011d --- /dev/null +++ b/assets/flags/1f1f5-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1f0.svg b/assets/flags/1f1f5-1f1f0.svg new file mode 100644 index 0000000..a718df6 --- /dev/null +++ b/assets/flags/1f1f5-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1f1.svg b/assets/flags/1f1f5-1f1f1.svg new file mode 100644 index 0000000..8169875 --- /dev/null +++ b/assets/flags/1f1f5-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1f2.svg b/assets/flags/1f1f5-1f1f2.svg new file mode 100644 index 0000000..dc55c02 --- /dev/null +++ b/assets/flags/1f1f5-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1f3.svg b/assets/flags/1f1f5-1f1f3.svg new file mode 100644 index 0000000..234f53f --- /dev/null +++ b/assets/flags/1f1f5-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1f7.svg b/assets/flags/1f1f5-1f1f7.svg new file mode 100644 index 0000000..f4c2ace --- /dev/null +++ b/assets/flags/1f1f5-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1f8.svg b/assets/flags/1f1f5-1f1f8.svg new file mode 100644 index 0000000..6ce8ec7 --- /dev/null +++ b/assets/flags/1f1f5-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1f9.svg b/assets/flags/1f1f5-1f1f9.svg new file mode 100644 index 0000000..c1d4a84 --- /dev/null +++ b/assets/flags/1f1f5-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1fc.svg b/assets/flags/1f1f5-1f1fc.svg new file mode 100644 index 0000000..043f7a5 --- /dev/null +++ b/assets/flags/1f1f5-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f5-1f1fe.svg b/assets/flags/1f1f5-1f1fe.svg new file mode 100644 index 0000000..c8e83dc --- /dev/null +++ b/assets/flags/1f1f5-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f6-1f1e6.svg b/assets/flags/1f1f6-1f1e6.svg new file mode 100644 index 0000000..f3e91d0 --- /dev/null +++ b/assets/flags/1f1f6-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f7-1f1ea.svg b/assets/flags/1f1f7-1f1ea.svg new file mode 100644 index 0000000..ab1399f --- /dev/null +++ b/assets/flags/1f1f7-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f7-1f1f4.svg b/assets/flags/1f1f7-1f1f4.svg new file mode 100644 index 0000000..33ac6ed --- /dev/null +++ b/assets/flags/1f1f7-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f7-1f1f8.svg b/assets/flags/1f1f7-1f1f8.svg new file mode 100644 index 0000000..5c6c69e --- /dev/null +++ b/assets/flags/1f1f7-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f7-1f1fa.svg b/assets/flags/1f1f7-1f1fa.svg new file mode 100644 index 0000000..46f74d5 --- /dev/null +++ b/assets/flags/1f1f7-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f7-1f1fc.svg b/assets/flags/1f1f7-1f1fc.svg new file mode 100644 index 0000000..6175c02 --- /dev/null +++ b/assets/flags/1f1f7-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1e6.svg b/assets/flags/1f1f8-1f1e6.svg new file mode 100644 index 0000000..d0d9580 --- /dev/null +++ b/assets/flags/1f1f8-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1e7.svg b/assets/flags/1f1f8-1f1e7.svg new file mode 100644 index 0000000..a55ff60 --- /dev/null +++ b/assets/flags/1f1f8-1f1e7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1e8.svg b/assets/flags/1f1f8-1f1e8.svg new file mode 100644 index 0000000..40e42ea --- /dev/null +++ b/assets/flags/1f1f8-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1e9.svg b/assets/flags/1f1f8-1f1e9.svg new file mode 100644 index 0000000..ddb60ba --- /dev/null +++ b/assets/flags/1f1f8-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1ea.svg b/assets/flags/1f1f8-1f1ea.svg new file mode 100644 index 0000000..a039dc2 --- /dev/null +++ b/assets/flags/1f1f8-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1ec.svg b/assets/flags/1f1f8-1f1ec.svg new file mode 100644 index 0000000..199e54e --- /dev/null +++ b/assets/flags/1f1f8-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1ed.svg b/assets/flags/1f1f8-1f1ed.svg new file mode 100644 index 0000000..57d004d --- /dev/null +++ b/assets/flags/1f1f8-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1ee.svg b/assets/flags/1f1f8-1f1ee.svg new file mode 100644 index 0000000..e25c04c --- /dev/null +++ b/assets/flags/1f1f8-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1ef.svg b/assets/flags/1f1f8-1f1ef.svg new file mode 100644 index 0000000..4f5260a --- /dev/null +++ b/assets/flags/1f1f8-1f1ef.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f0.svg b/assets/flags/1f1f8-1f1f0.svg new file mode 100644 index 0000000..c4f7caf --- /dev/null +++ b/assets/flags/1f1f8-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f1.svg b/assets/flags/1f1f8-1f1f1.svg new file mode 100644 index 0000000..b08dd1d --- /dev/null +++ b/assets/flags/1f1f8-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f2.svg b/assets/flags/1f1f8-1f1f2.svg new file mode 100644 index 0000000..b53d00d --- /dev/null +++ b/assets/flags/1f1f8-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f3.svg b/assets/flags/1f1f8-1f1f3.svg new file mode 100644 index 0000000..c233472 --- /dev/null +++ b/assets/flags/1f1f8-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f4.svg b/assets/flags/1f1f8-1f1f4.svg new file mode 100644 index 0000000..293dd34 --- /dev/null +++ b/assets/flags/1f1f8-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f7.svg b/assets/flags/1f1f8-1f1f7.svg new file mode 100644 index 0000000..c483fb9 --- /dev/null +++ b/assets/flags/1f1f8-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f8.svg b/assets/flags/1f1f8-1f1f8.svg new file mode 100644 index 0000000..0aa63d7 --- /dev/null +++ b/assets/flags/1f1f8-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1f9.svg b/assets/flags/1f1f8-1f1f9.svg new file mode 100644 index 0000000..f2bb52a --- /dev/null +++ b/assets/flags/1f1f8-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1fb.svg b/assets/flags/1f1f8-1f1fb.svg new file mode 100644 index 0000000..873310c --- /dev/null +++ b/assets/flags/1f1f8-1f1fb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1fd.svg b/assets/flags/1f1f8-1f1fd.svg new file mode 100644 index 0000000..2047243 --- /dev/null +++ b/assets/flags/1f1f8-1f1fd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1fe.svg b/assets/flags/1f1f8-1f1fe.svg new file mode 100644 index 0000000..2b4f515 --- /dev/null +++ b/assets/flags/1f1f8-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f8-1f1ff.svg b/assets/flags/1f1f8-1f1ff.svg new file mode 100644 index 0000000..cb7f84a --- /dev/null +++ b/assets/flags/1f1f8-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1e6.svg b/assets/flags/1f1f9-1f1e6.svg new file mode 100644 index 0000000..547fa05 --- /dev/null +++ b/assets/flags/1f1f9-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1e8.svg b/assets/flags/1f1f9-1f1e8.svg new file mode 100644 index 0000000..3c61bc7 --- /dev/null +++ b/assets/flags/1f1f9-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1e9.svg b/assets/flags/1f1f9-1f1e9.svg new file mode 100644 index 0000000..d106ba8 --- /dev/null +++ b/assets/flags/1f1f9-1f1e9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1eb.svg b/assets/flags/1f1f9-1f1eb.svg new file mode 100644 index 0000000..cf4bfac --- /dev/null +++ b/assets/flags/1f1f9-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1ec.svg b/assets/flags/1f1f9-1f1ec.svg new file mode 100644 index 0000000..4a05a30 --- /dev/null +++ b/assets/flags/1f1f9-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1ed.svg b/assets/flags/1f1f9-1f1ed.svg new file mode 100644 index 0000000..0bd4165 --- /dev/null +++ b/assets/flags/1f1f9-1f1ed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1ef.svg b/assets/flags/1f1f9-1f1ef.svg new file mode 100644 index 0000000..6045f46 --- /dev/null +++ b/assets/flags/1f1f9-1f1ef.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1f0.svg b/assets/flags/1f1f9-1f1f0.svg new file mode 100644 index 0000000..bfa9362 --- /dev/null +++ b/assets/flags/1f1f9-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1f1.svg b/assets/flags/1f1f9-1f1f1.svg new file mode 100644 index 0000000..6030072 --- /dev/null +++ b/assets/flags/1f1f9-1f1f1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1f2.svg b/assets/flags/1f1f9-1f1f2.svg new file mode 100644 index 0000000..a57c35c --- /dev/null +++ b/assets/flags/1f1f9-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1f3.svg b/assets/flags/1f1f9-1f1f3.svg new file mode 100644 index 0000000..c13e730 --- /dev/null +++ b/assets/flags/1f1f9-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1f4.svg b/assets/flags/1f1f9-1f1f4.svg new file mode 100644 index 0000000..20a9555 --- /dev/null +++ b/assets/flags/1f1f9-1f1f4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1f7.svg b/assets/flags/1f1f9-1f1f7.svg new file mode 100644 index 0000000..861da57 --- /dev/null +++ b/assets/flags/1f1f9-1f1f7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1f9.svg b/assets/flags/1f1f9-1f1f9.svg new file mode 100644 index 0000000..578c8eb --- /dev/null +++ b/assets/flags/1f1f9-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1fb.svg b/assets/flags/1f1f9-1f1fb.svg new file mode 100644 index 0000000..6558df6 --- /dev/null +++ b/assets/flags/1f1f9-1f1fb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1fc.svg b/assets/flags/1f1f9-1f1fc.svg new file mode 100644 index 0000000..4cd304e --- /dev/null +++ b/assets/flags/1f1f9-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1f9-1f1ff.svg b/assets/flags/1f1f9-1f1ff.svg new file mode 100644 index 0000000..a9ddb8e --- /dev/null +++ b/assets/flags/1f1f9-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fa-1f1e6.svg b/assets/flags/1f1fa-1f1e6.svg new file mode 100644 index 0000000..989b5c2 --- /dev/null +++ b/assets/flags/1f1fa-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fa-1f1ec.svg b/assets/flags/1f1fa-1f1ec.svg new file mode 100644 index 0000000..6602ca9 --- /dev/null +++ b/assets/flags/1f1fa-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fa-1f1f2.svg b/assets/flags/1f1fa-1f1f2.svg new file mode 100644 index 0000000..d51f600 --- /dev/null +++ b/assets/flags/1f1fa-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fa-1f1f3.svg b/assets/flags/1f1fa-1f1f3.svg new file mode 100644 index 0000000..a035a76 --- /dev/null +++ b/assets/flags/1f1fa-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fa-1f1f8.svg b/assets/flags/1f1fa-1f1f8.svg new file mode 100644 index 0000000..d51f600 --- /dev/null +++ b/assets/flags/1f1fa-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fa-1f1fe.svg b/assets/flags/1f1fa-1f1fe.svg new file mode 100644 index 0000000..796244c --- /dev/null +++ b/assets/flags/1f1fa-1f1fe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fa-1f1ff.svg b/assets/flags/1f1fa-1f1ff.svg new file mode 100644 index 0000000..b913772 --- /dev/null +++ b/assets/flags/1f1fa-1f1ff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fb-1f1e6.svg b/assets/flags/1f1fb-1f1e6.svg new file mode 100644 index 0000000..7b2bffa --- /dev/null +++ b/assets/flags/1f1fb-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fb-1f1e8.svg b/assets/flags/1f1fb-1f1e8.svg new file mode 100644 index 0000000..fb97611 --- /dev/null +++ b/assets/flags/1f1fb-1f1e8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fb-1f1ea.svg b/assets/flags/1f1fb-1f1ea.svg new file mode 100644 index 0000000..294b5c6 --- /dev/null +++ b/assets/flags/1f1fb-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fb-1f1ec.svg b/assets/flags/1f1fb-1f1ec.svg new file mode 100644 index 0000000..d8194cd --- /dev/null +++ b/assets/flags/1f1fb-1f1ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fb-1f1ee.svg b/assets/flags/1f1fb-1f1ee.svg new file mode 100644 index 0000000..d0602d2 --- /dev/null +++ b/assets/flags/1f1fb-1f1ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fb-1f1f3.svg b/assets/flags/1f1fb-1f1f3.svg new file mode 100644 index 0000000..4e0e1b5 --- /dev/null +++ b/assets/flags/1f1fb-1f1f3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fb-1f1fa.svg b/assets/flags/1f1fb-1f1fa.svg new file mode 100644 index 0000000..151e7aa --- /dev/null +++ b/assets/flags/1f1fb-1f1fa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fc-1f1eb.svg b/assets/flags/1f1fc-1f1eb.svg new file mode 100644 index 0000000..9895422 --- /dev/null +++ b/assets/flags/1f1fc-1f1eb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fc-1f1f8.svg b/assets/flags/1f1fc-1f1f8.svg new file mode 100644 index 0000000..6b075cb --- /dev/null +++ b/assets/flags/1f1fc-1f1f8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fd-1f1f0.svg b/assets/flags/1f1fd-1f1f0.svg new file mode 100644 index 0000000..39890a9 --- /dev/null +++ b/assets/flags/1f1fd-1f1f0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fe-1f1ea.svg b/assets/flags/1f1fe-1f1ea.svg new file mode 100644 index 0000000..a82532c --- /dev/null +++ b/assets/flags/1f1fe-1f1ea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1fe-1f1f9.svg b/assets/flags/1f1fe-1f1f9.svg new file mode 100644 index 0000000..76765b9 --- /dev/null +++ b/assets/flags/1f1fe-1f1f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ff-1f1e6.svg b/assets/flags/1f1ff-1f1e6.svg new file mode 100644 index 0000000..275c136 --- /dev/null +++ b/assets/flags/1f1ff-1f1e6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ff-1f1f2.svg b/assets/flags/1f1ff-1f1f2.svg new file mode 100644 index 0000000..d276896 --- /dev/null +++ b/assets/flags/1f1ff-1f1f2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f1ff-1f1fc.svg b/assets/flags/1f1ff-1f1fc.svg new file mode 100644 index 0000000..15a8464 --- /dev/null +++ b/assets/flags/1f1ff-1f1fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.svg b/assets/flags/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.svg new file mode 100644 index 0000000..fa20956 --- /dev/null +++ b/assets/flags/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.svg b/assets/flags/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.svg new file mode 100644 index 0000000..bfb8b48 --- /dev/null +++ b/assets/flags/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.svg b/assets/flags/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.svg new file mode 100644 index 0000000..a832676 --- /dev/null +++ b/assets/flags/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/flags/LICENSE-TWEMOJI.txt b/assets/flags/LICENSE-TWEMOJI.txt new file mode 100644 index 0000000..dc8853a --- /dev/null +++ b/assets/flags/LICENSE-TWEMOJI.txt @@ -0,0 +1,393 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/assets/fonts/DotGothic16-Regular.ttf b/assets/fonts/DotGothic16-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..844fc6373c3d5716eb9bde1d0663d8fffa4f8f06 GIT binary patch literal 2069236 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
bN2RH59RHRt7(@_~Yhr64bZ1k?WT)laAEf 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 nNM$GymA1RT?xH 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

&q3cQWsW*@9lr|&PW}iVp*O+d&yD=WTQI!RQ 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½=^A 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 z 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%0Da!Yw3D@FzFv8}s`%*X(34@?K^ym5pc4*zl{R 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�%eyza}Aq33*P;C3f9r@e7`xCwZ6# zthAOAGLRv(-0An}S*Vu(NQ7)YJDu4$P8^V0_9edJ1l~jT;Ls)n3LSoc2~e~u zcOa1hv`SuZj_}IAOKesXDR@n7k)M>jIx*VA+&>MU`5(YvU3UVo5Ri!tV9opLW&~$S z93#6k zp-ptWa6q64!t1^J;~{D&5{g=fqKzw}P{40AyF>8grc(hEF%!C1KI@Bc%J$^aJvcD4 zUGe`=#@oBJeWXbyjf4s^I#IxNF-8J0lR4ByRBPD zq#&hx4RdG9PcICy>`F}%w4rr#Lnp!>W4(Rm{5g4<;ETaA@@)u|;a+&c{i7hq{P+e! z$c_Sd(l2vxFF!MQ7bg`p;y})S6v8aXC!^cY8%ijl^F-GSVSOz*5g??-lE+Hnl2f~JfF{L-G+(hX zgp)g1P?Zrd!Q?&&cAJctvaN9 z(&e;Nm^W6%Wk7#3bUB~3G4RCy@^M1(((H*k`YmvVgEg%{^!V$^&y zy?jl5Eg9=C8PN;NTU-&TNjS=WMKQ@Txru?D^Z- z0vM|0q$|(C!)w4)s`!Ha#YgP#4HBMrPw7ecSeC!p{75q_=F4EIQJBR2WA-MU>uMlR zElK_7`%g`J=}4Jm?kHG7}+vk!#zQ=#om7oue!M5i>O9; z-DV4vgrX%aLPWA##+&E~xO6`eQlW@JrVwlr5!B8d_5!|LRM5<$v}}upT|GD(LL}n4 zv#}tiWfwjRS?P74Z`d3P4F!Vt4Ez>4@R?UdT8b6Fky}dc3+*9vDYw_ORT_*$AtFt_ zc^uYY0+QKccbbCFf6JHjId(?fekt>N?iMX4CnB zgdL4;+(03#Ck8=E{*<0RZ-GBIo?Qs0SKa{nn%4Y%dzf&g#!B(Q#_^=&6}MnEzza|X zP3c&sziprVPUK$K-W(*+Ttfc+$i$(;DIt315!es^D$$VsT!Jin2w!UaZ^dFOx5|N4 z{Kui``*{#VKOsKi=C#9v=oe}IE9G3Y%gOv_{{QrddzFg+S0A36zCjcRN9S_6d1+66 zJw^ag^fvf2Dj4Xf(WTs#rjdGgR}qoykw|bA+>|%DJ(36NW{EY1NV+>a$K+aDWG%li zr_Bm8HW9IrU^ZL+Y=I@Z@1hs;qs2bqn1_kJAc5?Ux-}eK&Pj+Fmw=EXYjx2taeT9N z`fjeA0lW@J9p<)PVCh55zOS`rL?; zP9sP3WhSCXUV|pkeA0X`PQS3{@b7~4KFSs;6-0hh`MvAL@GBtoOgQD&;Xu&dL)qXMpttCZxNy{C9v3r*jIOhKM*6 z+rjlf`qZ5s?ZX1M=>ikaKZ*)2?~hb;qq{im)28l5

jV;Fl*18rVAd*E>hUhmV+wvsiuug_1=1 zW|=`7R{)sKsH4SFoKS59CV^q_ z5FwfQ?DA!V0G_7+rc(MJv4K_Ye~r^$ea3LIVSg3|IQSkWchpn*gxPpZ+hIWpy;(dw2r5u}ml;o#GgTjcJ3XKdV`j?3ASU zBL#0`dAT8P!ZvtLsRQmxccjXSJq$-@)&tVVY_!ri6KjxKm)Zy+^GC0~)Qu zn)!eNjx_IhW9;C2^aus}Xaa5r|1Jn%CJHJ-VUo(R*;7|Ja?`Sr8ig@pU->$2;F((> zG{#!@Pnp)E6U~G80ml<$RsX01h(Jsx6#RBsle9anNXDs}Ivdor$ zIhSb?`{DtT>wv0*P-_oPF|6BtjTjr`Kyd9lLOf8}xgX9IVPxF9D(boQp*sIdy-A7y z*3$}wTq_^SqLi2f z{FE!BKg265IQu!mDOM#}5tm^s#k$VbHNmtku7-<4YQ;Z>uJ%T;cEDmtbnDn}KKK&Z z9Ec_cQ`V&*TB&dCiKY%!y6q%XA=1xBRJ$M+W=2yG>t-amCJ@y84z=tw0gnW;GY}N$ zk$gFoyHge3>0StF-6s_qkf`B*9THosS%UfOx>^LG&+p>$+rQSM)zQ(z2y7bLEx&P{ zW>IVR{q#RG&2aFE{r$Ow8W&o1hOQ}YwF;Jo^y=%YoKxRSZIME@hM(&(MSy?9Dcda}~=sQfL&Cn=R% z!y}!86Jvi6@RM-Zr}xK$FX>{@wO*osH*NrYG=W*(t+UC&=S2J!s^MgbhZ+IwqTi0X zEuB3(EI_tu5w=#4SXd!w6iBARD2&2>udrzsjjrsKriG;2Q9)vnKdL(=`&(?=!iV(4 zeBEdiW>WLx1KbW**e~xwki0*2Go; z$NwM91)g|>Z(PqUPJ~F`bX^T!BHlnDT(d#re~zX|cY04%> zehr;nub#2d{xuR+Hc@ArUnlJ-M%~frM}_bU%hwHlf4CRkLQT@U)U!z8*|%B^2itRa zx!Lh#HXRoS;dp6sQ zCl8ZOr8JA678C6gYxZ1Dku-_4|hnkBI#H6=h4w!Drv|iR0S1Blm&#C zWj5jLdaO^zYDX4;%h%BqDwWzoZ+F{oeQ$bsj&~F1N!M=OHtP-&i`92_Nv5b~1J&*f zEbDchLg6#_AW*!E>%0$!+sJo877Ijt>sHcyPZ$t?NGBW%G|V4Q?PPPbg&z#X-*d2P zVGrV3Nq@0N|L6Jdh)Jt-3$Rg`;yWAR^$Vg@(G|f#LPMrp5bk5&zl2+km0lS4$aOJH3eBZq#F@&Pc zF)en+VulxUN*aedeyE}MPrVSKhlwZ)kQ z%U&=}mnP?%+M2LlesGQ|3q)5!z*lbS7WJ(u54$~<9q6* ev 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 z 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 Z_c3o?{k8r3g??>+5B=tq{&~_b{|90?mfHXT literal 0 HcmV?d00001 diff --git a/assets/fonts/OFL-DotGothic16.txt b/assets/fonts/OFL-DotGothic16.txt new file mode 100644 index 0000000..118ae3a --- /dev/null +++ b/assets/fonts/OFL-DotGothic16.txt @@ -0,0 +1,93 @@ +Copyright 2020 The DotGothic16 Project Authors (https://github.com/fontworks-fonts/DotGothic16) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/cloud-data/2877fb2945abbc1bde895507.boards/B0.1.json b/cloud-data/2877fb2945abbc1bde895507.boards/B0.1.json new file mode 100644 index 0000000..4891914 --- /dev/null +++ b/cloud-data/2877fb2945abbc1bde895507.boards/B0.1.json @@ -0,0 +1 @@ +{"meta":{"id":"B0","x":0,"y":0,"chunks":[[0,0]],"level":1,"targetLevel":1,"seed":1369960461,"axis":"MIX","entrySide":null,"sealedSides":[],"puzzle":{"g":[[0,0,"W"],[0,1,"N"],[1,4,"E"],[0,4,"E"],[4,0,"W"],[4,3,"S"],[2,4,"E"],[4,4,"E"],[4,1,"S"],[4,2,"S"]],"n":[[1,0,3],[1,2,2],[2,0,3],[4,4,2],[3,1,2]],"valid":[[0,0],[0,1],[0,2],[0,3],[0,4],[1,0],[1,1],[1,2],[1,3],[1,4],[2,0],[2,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[3,3],[3,4],[4,0],[4,1],[4,2],[4,3],[4,4]],"obstacles":[],"specialCells":{"crossings":[],"warps":[],"locks":[]},"bounds":{"w":5,"h":5},"axis":"MIX","solution":[{"startGate":0,"endGate":1,"openGate":null,"detachedStart":false,"cells":[[0,0],[1,0],[1,1],[0,1]],"colorIndex":0,"startColorIndex":0,"endColorIndex":null},{"startGate":2,"endGate":3,"openGate":null,"detachedStart":false,"cells":[[1,4],[1,3],[1,2],[0,2],[0,3],[0,4]],"colorIndex":0,"startColorIndex":0,"endColorIndex":null},{"startGate":4,"endGate":5,"openGate":null,"detachedStart":false,"cells":[[4,0],[3,0],[2,0],[2,1],[2,2],[2,3],[3,3],[4,3]],"colorIndex":0,"startColorIndex":0,"endColorIndex":null},{"startGate":6,"endGate":7,"openGate":null,"detachedStart":false,"cells":[[2,4],[3,4],[4,4]],"colorIndex":0,"startColorIndex":0,"endColorIndex":null},{"startGate":8,"endGate":9,"openGate":null,"detachedStart":false,"cells":[[4,1],[3,1],[3,2],[4,2]],"colorIndex":0,"startColorIndex":0,"endColorIndex":null}],"level":1,"difficulty":1,"maxTurns":3,"totalTurns":12,"style":"variable-world-gates","complexity":{"rating":1,"score":1,"areaUnits":1,"lineCount":5,"totalTurns":12,"avgTurns":2.4,"maxTurns":3,"avgLength":5,"maxLength":8},"regionalTarget":1,"regionalFallback":false,"regionalOutlier":false,"uniqueness":null,"solutionQuality":null,"interactionBurden":null},"generatorVersion":5,"rev":1785229005024000,"revAuthor":"80369096-caa3-48ed-a80f-cab225a4f113"},"state":{"paths":[],"specialProgress":{"crossings":[]},"solved":false,"expanded":false,"expansionRetryRound":0,"solvedBy":null,"scoreAwarded":0,"scoreVersion":6,"rewardIdentity":null,"rewardCoefficient":null,"store":null,"rev":0,"revAuthor":"80369096-caa3-48ed-a80f-cab225a4f113"}} \ No newline at end of file diff --git a/cloud-data/2877fb2945abbc1bde895507.json b/cloud-data/2877fb2945abbc1bde895507.json new file mode 100644 index 0000000..0cc8508 --- /dev/null +++ b/cloud-data/2877fb2945abbc1bde895507.json @@ -0,0 +1 @@ +{"playerId":"2877fb2945abbc1bde895507","tokenHash":"7385cbb15992c7639a31fd94a95b803c87b115bbb52efeee30d23198897d320b","revision":1,"global":{"schema":31,"appVersion":"47.35","generatorVersion":5,"worldGeneration":"v47-field-reset-20260728-interaction-fix","updatedAt":1785229006137,"cloudRevision":1,"gameplayVersion":3,"globalRev":1785229006131000,"quarantine":{},"bonusEvents":{},"clockFloor":1785229006124,"cloudSyncPaused":false,"nextId":1,"solved":0,"score":0,"bonusScore":0,"bonusScoreVersion":6,"lastSolveAt":0,"timeAttack":null,"timeAttackRev":0,"timeAttackCooldowns":{"3":0,"5":0,"10":0},"lastTimeAttack":null,"timeAttackSuggestionsDisabled":false,"cursorStyle":"default","debugAllItems":false,"fieldEffects":[],"specialMechanicsSeen":[],"cameraAnchor":{"centerX":2.3255813953488373,"centerY":1.9767441860465116,"scale":1},"selectedBoardId":null},"boardVersions":{"B0":1},"changes":[{"revision":1,"metaIds":["B0"],"stateIds":["B0"],"deleted":[]}],"createdAt":1785229006091,"updatedAt":1785229006137} \ No newline at end of file diff --git a/docs/bend-field-performance-solutions.docx b/docs/bend-field-performance-solutions.docx new file mode 100644 index 0000000000000000000000000000000000000000..c7cef55bd2d7d594d1401e82da715d72ea7842e2 GIT binary patch literal 15666 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 +``` + +It reads the index, puzzle, and state in one readonly transaction. Missing rows +or revision mismatches are rejected rather than combined. + +Details are hydrated for: + +- Boards entering the interactive viewport and its prefetch margin. +- The selected or keyboard-focused board. +- Boards on both sides of an active gate connection. +- Boards involved in pending expansion or repair work. +- A shop before it opens or an inventory item before it is consumed. + +The detail cache is an LRU limited to 128 boards or 32 MiB, whichever is reached +first. Active pointer boards, dirty boards, time-attack boards, and boards still +referenced by a renderer or editor are pinned. A dirty board must be committed +before it can be evicted. + +Full-field score, inventory, minimap, shop-marker, and distant-overview loops +must use index summaries. They must not hydrate every board as a side effect. + +### Commit and concurrency rules + +All canonical field writes use one repository entry point: + +```ts +async function commitFieldDelta(delta: FieldDelta): Promise +``` + +The transaction: + +1. Reads `WorldControl`. +2. Verifies both the expected active epoch and active format. +3. Applies puzzle, state, summary, global, recovery, tombstone, and outbox + changes. +4. Commits the updated revisions together. + +An epoch or format mismatch aborts with `STALE_WORLD_EPOCH`. + +The existing Web Lock and storage-lease fallback remain the cross-tab mutation +guard. BroadcastChannel and storage events are only wake-up signals; receivers +must re-read authoritative control and revision records from IndexedDB. + +Compression, file writes, fetches, timers, worker replies, and rendering yields +must not be awaited inside an IndexedDB transaction. Browsers may auto-commit a +transaction when it has no pending IndexedDB request. Data should be parsed, +normalized, summarized, and encoded before its bounded transaction is opened. + +### Recovery + +IndexedDB remains authoritative. LocalStorage retains only: + +- The active-epoch hint. +- A bounded dirty-row recovery journal. +- Cross-tab wake-up data. + +It no longer attempts to mirror the entire version-2 field. Recovery records +remain epoch-scoped and cannot be applied after an import, reset, or rollback +switches to another epoch. + +## Portable archive + +### Format + +The default filename is: + +```text +bend-field-save-YYYY-MM-DD.bfsave +``` + +The payload is UTF-8 NDJSON. It is wrapped in one gzip stream when +`CompressionStream("gzip")` is available and remains uncompressed otherwise. +The importer detects the gzip magic bytes and verifies that the decoded +manifest declares the same compression mode. + +Decoded records have this exact order: + +```json +{"type":"manifest","format":"bend-field-save","archiveVersion":2,"saveSchema":31,"gameplayVersion":3,"worldGeneration":"...","appVersion":"...","generatorVersion":1,"exportedAt":"...","boardCount":42,"estimatedRawBytes":123456,"encoding":"ndjson","compression":"gzip"} +{"type":"global","value":{}} +{"type":"board","id":"B0","meta":{},"state":{}} +{"type":"board","id":"B1","meta":{},"state":{}} +{"type":"end","boardCount":42,"rawBytes":123456,"crc32":"12ab34cd"} +``` + +Board records are sorted by numeric board ID. Exactly one global record and one +footer are required. No record is pretty-printed. + +The footer CRC32 covers every decoded UTF-8 byte before the footer, including +record newlines. Gzip validation, CRC32, deterministic ordering, byte counts, +and board counts detect accidental corruption and truncation. They do not +authenticate the archive's author. + +Version 2 intentionally has no random-access index, encryption, signature, or +per-block hash. Import consumes every record in order, so a custom container +would add complexity without improving the current use case. + +### Portable allowlist + +Portable board metadata includes geometry, levels, seed, puzzle definition, +generator version, and connection-related data. Portable board state includes +paths, special progress, solved/expanded status, rewards, and shop purchases. + +Portable global state includes gameplay version, bonus events, +encountered mechanics, last-solve information, time-attack gameplay state, and +the trusted clock floor. + +The importer recomputes `nextId`, solved count, total score, bonus score, field +bounds, board summaries, and storage-size estimates. + +The following data is always excluded: + +- `cloudProfile`, player ID, token, cloud revision, cloud pending data, and + outbox rows. +- Recovery envelopes, journals, coverage markers, and tombstones. +- `worldEpoch`, `sessionId`, revisions' author identities, and transport + revision state. +- Origin URL. +- Quarantine diagnostics, debug flags, performance data, and transient caches. +- Cosmetic preferences that are not part of the field. + +Portable records omit local revisions and authors. Import assigns a fresh epoch +and fresh local revisions. + +### Export pipeline + +The export button obtains its destination synchronously while it still has user +activation. Destination priority is: + +1. `showSaveFilePicker()` and a direct `FileSystemWritableFileStream`. +2. An OPFS temporary file whose resulting `File` is downloaded. +3. An in-memory Blob only when the projected archive is at most 64 MiB. + +All capabilities are feature-detected. Lack of gzip support selects identity +encoding; it does not change the archive record format. + +After a destination is available, export: + +1. Flushes pending field writes and aborts if the flush fails. +2. Acquires the exclusive world lock. +3. Captures the active epoch, global record, board count, and size estimate. +4. Reads board IDs in deterministic pages using short readonly transactions. +5. Encodes records through a worker and writes them with stream backpressure. +6. Writes the footer, closes the destination, and releases the lock. + +Canonical database writes are queued while the lock is held. The archive +therefore represents the committed field at export start. The UI may allow +navigation but must pause puzzle edits, purchases, field placement, reset, and +time-attack mutations until export finishes or is canceled. + +Worker input uses approximately 1 MiB chunks with at most two chunks in flight. +The worker performs JSON serialization, parsing, and CRC work. Native streams +perform compression and decompression. + +## Import and activation + +### Inspection + +`inspectFieldArchive(file, { signal })` reads enough of the file to display: + +- Archive and save-schema versions. +- Export date. +- World generation. +- Board count. +- Estimated raw size. +- Compression mode. + +Inspection does not trust the declared values and does not activate anything. +The complete stream is validated during staging. + +### Limits + +Version-2 import enforces: + +- Compressed file size at most 1 GiB. +- Decoded content at most 2 GiB. +- A single decoded record at most 8 MiB. +- Expansion ratio at most 100:1. +- At most `MAX_BOARDS`, currently 200,000. +- Existing board-coordinate, chunk-shape, puzzle-cell, and path-count limits. +- Write batches of at most 256 boards or 4 MiB. + +`navigator.storage.estimate()` provides an advisory preflight. Import requests +approximately twice the estimated staged size plus 32 MiB of headroom because +the old and new fields coexist during validation. The actual IndexedDB result +remains authoritative: `QuotaExceededError` aborts staging without deleting the +active field. + +Large or version-2 restores require IndexedDB. There is no destructive +localStorage-only fallback. + +### Strict validation + +Each record is treated as untrusted data. Version-2 restore fails closed for: + +- Invalid archive identity, ordering, or unsupported version. +- A schema/world-generation pair without an explicit migration. +- Duplicate or noncanonical board IDs. +- Missing or invalid B0. +- Missing metadata or state. +- Invalid puzzle bounds, gates, cells, paths, stores, or references. +- Overlapping world chunks. +- Board, byte, line, cell, path, or expansion-ratio limits. +- A missing footer or incorrect board count, byte count, or CRC. + +Unlike current tolerant snapshot normalization, a damaged version-2 archive +never silently drops a board. + +Validation maintains occupied chunks and derived totals while records are +staged. After the final record, a database pass verifies staged counts, +revisions, summary/detail agreement, B0 hydration, and global aggregates. + +`importFieldArchive()` stops after producing a completely validated `ready` +epoch. It never changes the active pointer; activation is a separate operation +after the replacement confirmation. + +### Staging and atomic activation + +Import creates a new local epoch with `status: "staging"`. The epoch from the +archive is never reused. Each validated batch is committed independently and +updates resumable progress on its `WorldRecord`. + +After complete validation, the epoch becomes `ready`. Activation then acquires +the world lock and uses one short transaction to: + +1. Re-read the expected active epoch and global revision. +2. Move the current epoch to `rollback`. +3. Mark the ready epoch `active`. +4. Set `previousEpoch`. +5. Flip `WorldControl.activeEpoch`. +6. Mark the activation as unverified. + +Only after this transaction commits may localStorage hints and cross-tab +replacement messages be updated. + +The application reloads into the new epoch. It verifies the header, first index +page, B0 puzzle/state, and occupancy before enabling mutations. Failure at this +point atomically restores `previousEpoch`. + +After the new epoch completes its first durable gameplay checkpoint, +`activationVerified` becomes true and the old epoch becomes garbage. At most +one rollback epoch is retained, and it is not presented as save history. + +Canceled, interrupted, corrupt, quota-failing, or worker-failing imports leave +the active pointer unchanged. Abandoned staging epochs are marked for cleanup +and removed after 24 hours. + +Restored archives start local-only with no cloud profile or outbox. Reconnecting +cloud sync requires an explicit player action and a separately designed paged +full-sync protocol. The current 10,000-change push path must not be used to +silently upload a very large restored world. + +### Legacy JSON + +Existing JSON envelopes and raw snapshots remain importable and retain the +current 100 MiB file limit. They are sanitized before conversion and pass +through the same epoch-staging and activation pipeline. + +Compatibility is handled through explicit +`migrateArchiveRecord(fromSchema, record)` registrations. An unknown schema or +world generation is rejected instead of silently creating a fresh field. + +Legacy normalization may repair or discard malformed data. When that occurs, +the importer displays a lossy-import report with totals and the first affected +board IDs. Activation requires separate confirmation of that report. New +version-2 archives never use lossy repair. + +## Migration from the current database + +The version-change transaction creates stores and indexes only. It must not copy +an entire field inside `onupgradeneeded`. + +While `activeFormat` is 1: + +1. Startup continues using the existing stores. +2. One tab becomes migration leader through the existing world lock. +3. A new version-2 staging epoch is created. +4. Legacy rows are copied in resumable batches. +5. Normal commits dual-write legacy and version-2 records in the same + transaction. +6. Backfill uses revision-conditional writes so it cannot overwrite newer + dual-written data. +7. The last legacy cursor key and copied counts are persisted after every + batch. + +After board, global, tombstone, recovery, and outbox counts and revisions +reconcile, the persistence queue is flushed and `activeFormat` switches to 2 in +one transaction. Dual-writing continues until the first version-2 activation +verification checkpoint. + +Quota failure, a blocked upgrade, or interruption leaves format 1 authoritative +and retryable. The old stores remain for one application release and are +removed only by a later IndexedDB version upgrade. + +## Garbage collection + +Garbage collection: + +- Never deletes the active, previous, or recovery-pinned epoch. +- Deletes no more than 500 rows per idle batch. +- Persists the current store and key after every batch. +- Resumes after reload or interruption. +- Removes abandoned staging epochs older than 24 hours. +- Removes a verified rollback epoch after the new field's first checkpoint. +- Cleans failed OPFS export files. + +## Interfaces + +The repository and archive modules expose these conceptual operations: + +```ts +loadActiveWorldHeader(): Promise<{ + control: WorldControl; + world: WorldRecord; +}>; + +scanBoardIndex( + epoch: string, + afterKey?: IDBValidKey, + limit?: number +): Promise; + +hydrateBoardDetails( + epoch: string, + id: string, + expectedRevisions?: { metaRev: number; stateRev: number } +): Promise; + +commitFieldDelta(delta: FieldDelta): Promise; + +inspectFieldArchive( + file: File, + options?: { signal?: AbortSignal } +): Promise; + +exportFieldArchive(options: { + writable: WritableStream; + signal?: AbortSignal; + onProgress?: (progress: FieldArchiveProgress) => void; +}): Promise; + +importFieldArchive(options: { + file: File; + signal?: AbortSignal; + onProgress?: (progress: FieldArchiveProgress) => void; +}): Promise; + +activateStagedWorld( + stagedEpoch: string, + expectedActive: { epoch: string; globalRev: number } +): Promise; + +rollbackUnverifiedActivation(expectedEpoch: string): Promise; +migrateLegacyWorld(): Promise; +collectWorldGarbage(): Promise; +``` + +Progress has the following stable shape: + +```ts +interface FieldArchiveProgress { + phase: + | "prepare" + | "encode" + | "write" + | "validate" + | "stage" + | "activate" + | "cleanup"; + boardsDone: number; + boardsTotal: number; + bytesRead: number; + bytesWritten: number; +} +``` + +Progress is reported at least every 250 ms while work is advancing. +Cancellation is acknowledged within one worker chunk or one database batch. + +## Performance budgets + +- Initial gameplay readiness requires only the field header, first index page, + and initial viewport details. +- Startup does not load every puzzle or state. +- Archive import/export adds at most 32 MiB of JavaScript heap beyond the + resident field summaries and browser stream buffers. +- At most two approximately 1 MiB worker chunks are in flight. +- No archive or migration task occupies the main thread for 50 ms or longer. +- IndexedDB writes contain at most 256 boards or 4 MiB. +- Index scans use at most 512 records per transaction. +- Import and export UI remains cancelable throughout encode, write, validation, + and staging. + +## Verification + +### Semantic and privacy tests + +- Gzip and identity-encoded round trips preserve the complete portable gameplay + field. +- Derived totals after import equal totals recomputed from the original field. +- Archive text and decoded records contain none of the excluded credential, + recovery, epoch, session, debug, or transport fields. +- Numeric board ordering and CRC output are deterministic for identical + portable input. + +### Invalid archive tests + +- Corrupt gzip data. +- Truncated records or missing footer. +- Incorrect CRC, byte count, or board count. +- Reordered, duplicated, or unknown records. +- Duplicate IDs, missing B0, and overlapping geometry. +- Malformed puzzles, paths, stores, and references. +- Oversized file, decoded stream, line, board count, and expansion ratio. +- Unsupported archive, save schema, or world-generation combination. + +### Failure and concurrency tests + +- Cancellation and injected failure before and after every staging batch. +- Quota failure during preflight and during a write transaction. +- Worker termination and destination-write failure. +- Crash before activation, during pointer activation, and before verification. +- Automatic rollback after failed B0 or first-index validation. +- Stale-tab writes after import, reset, rollback, or migration cutover. +- Cross-tab field replacement and blocked database upgrade. +- Migration resume, dual-write conflict, and conditional backfill. +- Garbage-collection resume and protection of active/rollback epochs. +- Cloud remains disconnected after portable restore. + +### Lazy-load tests + +- B0 becomes interactive before the complete index scan finishes. +- Details hydrate for viewport, gate adjacency, repair, inventory, and shop + access. +- Missing or mismatched revisions fail hydration. +- Dirty, active, and referenced boards cannot be evicted. +- Full-field summaries do not cause detail hydration. + +### Browser and scale tests + +- Direct file picker, OPFS, and bounded Blob output paths. +- Gzip and identity compression paths. +- Web Lock and storage-lease concurrency paths. +- 10,000-board round trip and startup coverage in continuous integration. +- 100,000-board and 200,000-board browser benchmarks as scheduled tests. +- Long-task, heap, progress-frequency, cancellation-latency, transaction-size, + and startup-readiness budgets. + +Existing local `BEND_PERF` instrumentation should record only aggregate +duration, byte, board, cancellation, quota, rollback, and cleanup metrics. No +field contents, board IDs, player identity, or remote telemetry are added. + +## Current implementation requirements + +1. Add the version-6 stores, repository interfaces, epoch checks, and resumable + migration without changing the active read path. +2. Enable summary-first startup, real detail hydration, summary-based + full-field operations, and the bounded LRU. +3. Add the streamed `.bfsave` exporter and all output sinks. +4. Add strict staged import, atomic activation, rollback, and legacy conversion. +5. Route reset through the same epoch activation path. +6. Garbage collection removes inactive epochs and obsolete stores only after verified activation. +7. Cloud synchronization uses paged pulls, bounded push batches, recent-revision deltas, and per-board versioned server storage. diff --git a/docs/internal-system.md b/docs/internal-system.md new file mode 100644 index 0000000..8edd2ea --- /dev/null +++ b/docs/internal-system.md @@ -0,0 +1,117 @@ +# Bend Field: Internal System Reference + +## Runtime layout + +The game is a browser application with no framework dependency. + +- `index.html` defines the application shell, HUD, dialogs, and script loading order. +- `style.css` owns the visual system, responsive layout, zoom presentation, and interaction styling. +- `app.js` owns runtime state, rendering, input, generation orchestration, persistence, economy, and UI behavior. +- `app-logic.js` contains deterministic shared logic used by both runtime code and tests. +- `puzzle-core.js` contains deterministic puzzle generation, solving, and difficulty analysis. +- `puzzle-worker.js` runs expensive generation and verification away from the main thread. +- `server.js` serves static files and owns the shared-world HTTP API, solution validation, revisions, and clear events. `realtime-server.js` owns transient WebSocket presence, five-minute board-claim leases, and short-lived reactions. Player purchases are stored separately from the shared world and validated by `server.js`. + +## World and puzzle data + +The global data object stores: + +- Board metadata in `data.metas`. +- Mutable board state in `data.states`. +- Local score, time-attack state, purchases, cursor selection, and unfinished routes. +- Shared solved count, board definitions, cleared state, first-solver identity, expansion state,. +- Persistence revisions, authors, tombstones, and shared-world synchronization state. + +A board metadata record identifies its world position, occupied five-by-five chunks, level, seed, puzzle definition, sealed sides, and revision. + +A board state record contains drawn paths, special-cell progress, solved/expanded state, reward data, shop state, and revision. + +A path contains its start gate, optional end gate, ordered cells, color information, and detached status. A detached path has two interactive pickups and does not participate in cross-board thickness through its former gate. + +## Input and connection pipeline + +Pointer samples are coalesced and processed once per animation frame. + +1. Screen coordinates are converted to board SVG coordinates without a live layout read. +2. Traversed cells are resolved in pointer order. +3. Occupancy, warp, crossing, key/door, and self-rewind rules validate each step. +4. Pickup snapping searches other open endpoints. +5. Pickup and gate targets may bridge at most one empty cell in a straight line. +6. Every bridge cell is checked against puzzle bounds and live occupancy. +7. A successful join is committed as one board command and pointer capture is released. + +Gate hitboxes cover the inward gate area plus a small outward strip equal to 14% of one cell. The geometric snap selector remains shared by direct gate input, gate-cell input, and drag completion. + +## Puzzle generation + +World expansion is gate-driven. Solving a board opens unresolved gate frontiers, then generation: + +1. Chooses region-appropriate connected chunk shapes. +2. Collects required facing-gate connections. +3. Generates and validates a complete puzzle candidate. +4. Applies obstacles and scheduled special cells. +5. Verifies connection requirements, interaction burden, difficulty, and uniqueness where required. +6. Commits metadata and state only after all validation succeeds. + +Enclosed one-chunk world holes are treated as terminal fills. Existing saves are scanned for both gate-backed and ungated enclosed holes. Ungated terminal puzzles are sealed on all four sides; gate-backed fills keep only required connection sides open. + +## Obstacle policy + +Obstacle placement operates by removing safe detours from generated solution paths. + +- The baseline selection rate is 2%. +- Puzzle area gradually adds up to four percentage points for large puzzles. +- Adjacent candidates receive a clustering preference. +- The final obstacle count is capped at `floor(totalCells × 0.20)`. +- Detours larger than the remaining allowance are rejected. +- Crossing conversion and fallback crossing templates use the same 20% cap. + +After obstacle mutation, number clues and turn totals are rebuilt from the modified solution. + +## Difficulty and rewards + +The base score uses board level and rendered connected-line thickness. Two effort multipliers are then applied: + +- A hard-puzzle multiplier begins above level 4 and increases through level 10. +- A large-puzzle multiplier uses the number of occupied chunks and grows faster at high levels. + +The result is passed through the deterministic board coefficient, active Score Lens multiplier, and time-attack modifier. `SCORE_VERSION` identifies the active reward formula for stored results. + +## Rendering and performance + +The field uses several levels of detail: + +- Full interactive SVG boards near the active viewport. +- Static board summaries outside the immediate interaction area. +- A single canvas-based distant overview that reuses the minimap cell and long-line renderer. + +Camera updates, board drag frames, the minimap, distant overview, presence cursors, reactions, and static noise are scheduled and instrumented independently. Difficulty-field overlays do not exist in the current runtime. + +## Persistence + +IndexedDB is the durable local source of truth. A compact local mirror supports recovery when IndexedDB startup or writes fail. + +Persistence uses: + +- Dirty metadata and state ID sets. +- Chunked transactions. +- Monotonic revisions and revision authors. +- Deleted-board tombstones. +- A shared-world outbox for retryable, server-validated mutations. + +Unfinished routes are deliberately excluded from the shared outbox. Shared pulls replace board definitions and accepted clear state while preserving personal economy, cursor state, and compatible local unfinished routes. + +The server stores one shared world and immutable versioned board shards. All mutations are serialized through one world queue. Clients poll revision deltas; successful clears are validated by the server before neighboring board batches are accepted. + +The world-generation identifier is separate from the application version. Ordinary behavior and balance updates do not reset an existing field. + +## Verification + +`test/run-all.js` runs source, gameplay, economy, performance, storage, concurrency, generation, interaction, reset, special-cell, and server regressions. + +`test/browser-performance-benchmark.js` verifies real Edge startup and measures pointer work, camera work, minimap conversion, level-of-detail processing, persistence, overview rendering, DOM size, and main-thread responsiveness. + + +## Documentation policy + +Documentation describes the current product and system only. Do not add changelog, release-note, update-history, migration-diary, or version-specific change-summary documents. When behavior changes, update the relevant current specification in place and remove obsolete historical notes. diff --git a/docs/shared-world-complete.md b/docs/shared-world-complete.md new file mode 100644 index 0000000..23ba134 --- /dev/null +++ b/docs/shared-world-complete.md @@ -0,0 +1,34 @@ +# Shared world runtime + +The runtime shares board definitions, solved states, first-solver identity, world expansion, and clear notifications. + +Transient WebSocket state includes nearby cursors, viewport subscriptions, board claims, reactions, and clear notifications. Transient state is not written to the shared-world file. + +Player-specific persistent state includes the player name, earned score ledger, purchases, and equipped cursor state on the client. Purchases are validated against the server-authoritative store inventory and price. + +Difficulty-adjustment field items and shared field-placement APIs are not part of the current runtime. + +## HTTP endpoints + +- `POST /api/cloud/session` +- `POST /api/cloud/profile` +- `GET /api/cloud/status` +- `GET /api/cloud/pull` +- `POST /api/cloud/push` +- `GET /api/player/state` +- `POST /api/player/purchase` +- `POST /api/player/generation-bonus` + +## Realtime messages + +- `viewport` +- `cursor` +- `cursor-hide` +- `claim` +- `release` +- `reaction` +- `board-cleared` + +## Authority model + +The server validates solved routes, reconstructs rewards and stores, records the first solver, grants bounded adjacent expansion, tracks each player's earned and spent score, and rejects arbitrary board insertion. diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bf31055f5f27f024cac7ffb2a236d7ec2428ed6c GIT binary patch literal 32038 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 + + + + diff --git a/field-persistence-worker.js b/field-persistence-worker.js new file mode 100644 index 0000000..83187db --- /dev/null +++ b/field-persistence-worker.js @@ -0,0 +1,32 @@ +'use strict'; +const encoder=new TextEncoder(),crcTable=new Uint32Array(256),MAX_CHUNK_BYTES=1024*1024; +for(let index=0;index<256;index++){ + let value=index; + for(let bit=0;bit<8;bit++)value=value&1?0xedb88320^(value>>>1):value>>>1; + crcTable[index]=value>>>0; +} +let crc=0xffffffff,rawBytes=0; +function crc32Update(value,bytes){let next=value>>>0;for(const byte of bytes)next=crcTable[(next^byte)&255]^(next>>>8);return next>>>0} +function crc32Hex(){return((crc^0xffffffff)>>>0).toString(16).padStart(8,'0')} +function splitBytes(parts){ + const chunks=[];let current=new Uint8Array(MAX_CHUNK_BYTES),offset=0; + for(const bytes of parts){let sourceOffset=0;while(sourceOffset{ + const message=event.data||{},id=message.id; + try{ + if(message.op==='encode'){ + const parts=[]; + for(const record of message.records||[]){const bytes=encoder.encode(`${JSON.stringify(record)}\n`);if(message.includeInCrc!==false){crc=crc32Update(crc,bytes);rawBytes+=bytes.byteLength}parts.push(bytes)} + const chunks=splitBytes(parts);self.postMessage({id,chunks,rawBytes,crc32:crc32Hex()},chunks);return; + } + if(message.op==='parse'){ + const items=[]; + for(const sourceLine of message.lines||[]){const line=sourceLine.replace(/\r$/,'');let record;try{record=JSON.parse(line)}catch(_){throw new Error('An archive record is not valid JSON.')}const bytes=encoder.encode(`${line}\n`);if(record?.type!=='end'){crc=crc32Update(crc,bytes);rawBytes+=bytes.byteLength}items.push({record,byteLength:bytes.byteLength,rawBytes,crc32:crc32Hex()})} + self.postMessage({id,items,rawBytes,crc32:crc32Hex()});return; + } + if(message.op==='snapshot'){self.postMessage({id,rawBytes,crc32:crc32Hex()});return} + throw new Error('Unknown archive worker operation.'); + }catch(error){self.postMessage({id,error:error?.message||String(error)})} +}; diff --git a/field-persistence.js b/field-persistence.js new file mode 100644 index 0000000..5f706c9 --- /dev/null +++ b/field-persistence.js @@ -0,0 +1,257 @@ +(function(global){ + 'use strict'; + + const ARCHIVE_FORMAT='bend-field-save'; + const ARCHIVE_VERSION=2; + const MAX_ARCHIVE_FILE_BYTES=1024*1024*1024; + const MAX_ARCHIVE_RAW_BYTES=2*1024*1024*1024; + const MAX_ARCHIVE_LINE_BYTES=8*1024*1024; + const MAX_ARCHIVE_EXPANSION_RATIO=100; + const MAX_BLOB_BYTES=64*1024*1024; + const PROGRESS_INTERVAL=250; + const WORKER_TARGET_BYTES=1024*1024; + const MAX_WORKER_RECORDS=32; + const encoder=new TextEncoder(); + const crcTable=new Uint32Array(256); + + for(let index=0;index<256;index++){ + let value=index; + for(let bit=0;bit<8;bit++)value=value&1?0xedb88320^(value>>>1):value>>>1; + crcTable[index]=value>>>0; + } + + function crc32Update(crc,bytes){ + let value=crc>>>0; + for(const byte of bytes)value=crcTable[(value^byte)&255]^(value>>>8); + return value>>>0; + } + + function crc32Hex(crc){return((crc^0xffffffff)>>>0).toString(16).padStart(8,'0')} + function abortError(){return new DOMException('The operation was canceled.','AbortError')} + function throwIfAborted(signal){if(signal?.aborted)throw signal.reason||abortError()} + function archiveError(message,code='INVALID_ARCHIVE'){const error=new Error(message);error.code=code;return error} + function validateManifestRecord(record,compression){ + if(record?.type!=='manifest'||record.format!==ARCHIVE_FORMAT||record.archiveVersion!==ARCHIVE_VERSION)throw archiveError('This is not a supported Bend Field archive.','UNSUPPORTED_ARCHIVE'); + if(record.compression!==compression)throw archiveError('The archive compression declaration does not match its contents.'); + if(record.encoding!=='ndjson')throw archiveError('The archive encoding is not supported.','UNSUPPORTED_ARCHIVE'); + if(!Number.isSafeInteger(record.boardCount)||record.boardCount<1)throw archiveError('The archive board count is invalid.'); + if(!Number.isSafeInteger(record.estimatedRawBytes)||record.estimatedRawBytes<0||record.estimatedRawBytes>MAX_ARCHIVE_RAW_BYTES)throw archiveError('The archive size declaration is invalid.','ARCHIVE_TOO_LARGE'); + return record; + } + function encodeRecord(record){return encoder.encode(`${JSON.stringify(record)}\n`)} + function boardNumber(id){return/^B(?:0|[1-9]\d*)$/.test(id)?Number(id.slice(1)):-1} + function triggerDownload(file,name){ + const url=URL.createObjectURL(file),link=document.createElement('a'); + link.href=url;link.download=name;link.style.display='none';document.body.append(link);link.click();link.remove(); + setTimeout(()=>URL.revokeObjectURL(url),1000); + } + + function progressReporter(callback,base={}){ + let last=0; + return(patch={},force=false)=>{ + if(typeof callback!=='function')return; + const now=performance.now(); + if(!force&&now-last{const message=event.data||{},entry=pending.get(message.id);if(!entry)return;pending.delete(message.id);if(message.error)entry.reject(archiveError(message.error,'ARCHIVE_WORKER'));else entry.resolve(message)}; + worker.onerror=event=>{const error=archiveError(event?.message||'The archive worker failed.','ARCHIVE_WORKER');for(const entry of pending.values())entry.reject(error);pending.clear()}; + const request=(op,payload={})=>new Promise((resolve,reject)=>{const id=++nextId;pending.set(id,{resolve,reject});try{worker.postMessage({id,op,...payload})}catch(error){pending.delete(id);reject(error)}}); + return{encode:(records,includeInCrc=true)=>request('encode',{records,includeInCrc}),parse:lines=>request('parse',{lines}),snapshot:()=>request('snapshot'),terminate(){for(const entry of pending.values())entry.reject(archiveError('The archive worker was terminated.','ARCHIVE_WORKER'));pending.clear();worker.terminate()}}; + } + + async function createArchiveDestination(name,estimatedBytes=0){ + if(typeof global.showSaveFilePicker==='function'){ + const handle=await global.showSaveFilePicker({ + suggestedName:name, + types:[{description:'Bend Field save',accept:{'application/x-bend-field-save':['.bfsave']}}] + }); + const writable=await handle.createWritable(); + return{ + kind:'file', + writable, + async finish(){}, + async abort(reason){try{await writable.abort(reason)}catch(_){}} + }; + } + if(global.navigator?.storage?.getDirectory){ + const root=await global.navigator.storage.getDirectory(),temporaryName=`.${name}.${Date.now()}.partial`, + handle=await root.getFileHandle(temporaryName,{create:true}),writable=await handle.createWritable(); + return{ + kind:'opfs', + writable, + async finish(){ + const file=await handle.getFile();triggerDownload(file,name); + try{await root.removeEntry(temporaryName)}catch(_){} + }, + async abort(reason){ + try{await writable.abort(reason)}catch(_){} + try{await root.removeEntry(temporaryName)}catch(_){} + } + }; + } + if(estimatedBytes>MAX_BLOB_BYTES)throw archiveError('This browser cannot stream a save file of this size.','UNSUPPORTED_OUTPUT'); + const chunks=[];let size=0,closed=false; + const writable=new WritableStream({ + write(chunk){ + const bytes=chunk instanceof Uint8Array?chunk:new Uint8Array(chunk); + size+=bytes.byteLength;if(size>MAX_BLOB_BYTES)throw archiveError('The save exceeded the safe in-memory download limit.','OUTPUT_TOO_LARGE'); + chunks.push(bytes.slice()); + }, + close(){closed=true}, + abort(){chunks.length=0;size=0} + }); + return{ + kind:'blob', + writable, + async finish(){ + if(!closed)throw archiveError('The save stream did not close.','OUTPUT_INCOMPLETE'); + triggerDownload(new Blob(chunks,{type:'application/x-bend-field-save'}),name); + }, + async abort(){chunks.length=0;size=0} + }; + } + + function outputPipeline(writable,useGzip,onBytes){ + const counter=new TransformStream({transform(chunk,controller){onBytes(chunk.byteLength);controller.enqueue(chunk)}}); + if(useGzip){ + const gzip=new CompressionStream('gzip'),pipe=gzip.readable.pipeThrough(counter).pipeTo(writable); + return{writer:gzip.writable.getWriter(),pipe}; + } + const pass=new TransformStream(),pipe=pass.readable.pipeThrough(counter).pipeTo(writable); + return{writer:pass.writable.getWriter(),pipe}; + } + + async function writeArchive({writable,manifest,globalState,boards,signal,onProgress}){ + throwIfAborted(signal); + const useGzip=manifest.compression==='gzip'; + if(useGzip&&typeof CompressionStream!=='function')throw archiveError('Gzip compression is not available.','UNSUPPORTED_COMPRESSION'); + let bytesWritten=0,rawBytes=0,crc=0xffffffff,crcHex='00000000',boardsDone=0,writer=null,pipe=null; + const report=progressReporter(onProgress,{boardsTotal:manifest.boardCount||0}),codec=createArchiveCodec(); + try{ + ({writer,pipe}=outputPipeline(writable,useGzip,amount=>{bytesWritten+=amount})); + const writeRecords=async(records,includeInCrc=true)=>{ + throwIfAborted(signal);if(!records.length)return; + if(codec){const result=await codec.encode(records,includeInCrc);rawBytes=result.rawBytes;crcHex=result.crc32;for(const buffer of result.chunks){throwIfAborted(signal);await writer.write(new Uint8Array(buffer))}return} + for(const record of records){const bytes=encodeRecord(record);if(includeInCrc){crc=crc32Update(crc,bytes);rawBytes+=bytes.byteLength;crcHex=crc32Hex(crc)}await writer.write(bytes)} + }; + report({phase:'prepare',boardsDone,bytesRead:rawBytes,bytesWritten},true); + await writeRecords([{type:'manifest',format:ARCHIVE_FORMAT,archiveVersion:ARCHIVE_VERSION,...manifest},{type:'global',value:globalState}]); + const estimatedBoardBytes=Math.max(1024,Math.ceil((Number(manifest.estimatedRawBytes)||0)/Math.max(1,manifest.boardCount||1))),batchLimit=Math.max(1,Math.min(MAX_WORKER_RECORDS,Math.floor(WORKER_TARGET_BYTES/estimatedBoardBytes)||1));let batch=[]; + for await(const board of boards){throwIfAborted(signal);batch.push({type:'board',...board});boardsDone++;if(batch.length>=batchLimit){await writeRecords(batch);batch=[];report({phase:'write',boardsDone,bytesRead:rawBytes,bytesWritten})}} + await writeRecords(batch); + if(boardsDone!==manifest.boardCount)throw archiveError('The exported board count changed during export.','FIELD_CHANGED'); + if(codec){const state=await codec.snapshot();rawBytes=state.rawBytes;crcHex=state.crc32}else crcHex=crc32Hex(crc); + await writeRecords([{type:'end',boardCount:boardsDone,rawBytes,crc32:crcHex}],false); + await writer.close();await pipe;report({phase:'write',boardsDone,bytesRead:rawBytes,bytesWritten},true); + return{boardCount:boardsDone,rawBytes,bytesWritten,crc32:crcHex,compression:manifest.compression,worker:codec!=null}; + }catch(error){try{await writer?.abort(error)}catch(_){}try{await pipe}catch(_){}throw error} + finally{codec?.terminate()} + } + + function decodedStream(file){ + const source=file.stream(); + return file.slice(0,2).arrayBuffer().then(buffer=>{ + const bytes=new Uint8Array(buffer),gzip=bytes[0]===0x1f&&bytes[1]===0x8b; + if(gzip){ + if(typeof DecompressionStream!=='function')throw archiveError('This browser cannot read gzip save files.','UNSUPPORTED_COMPRESSION'); + return{stream:source.pipeThrough(new DecompressionStream('gzip')),compression:'gzip'}; + } + return{stream:source,compression:'identity'}; + }); + } + + async function firstArchiveRecord(file,signal){ + if(!(file instanceof Blob))throw archiveError('No save file was selected.'); + if(file.size<=0||file.size>MAX_ARCHIVE_FILE_BYTES)throw archiveError('The save file size is outside the supported range.','ARCHIVE_TOO_LARGE'); + const decoded=await decodedStream(file),reader=decoded.stream.getReader(),decoder=new TextDecoder(); + let text='',decodedBytes=0; + try{ + while(true){ + throwIfAborted(signal); + const{value,done}=await reader.read();if(done)break; + decodedBytes+=value.byteLength;if(decodedBytes>MAX_ARCHIVE_LINE_BYTES)throw archiveError('The archive manifest is too large.','LINE_TOO_LARGE'); + text+=decoder.decode(value,{stream:true});const newline=text.indexOf('\n'); + if(newline>=0){ + const line=text.slice(0,newline).replace(/\r$/,'');let record; + try{record=JSON.parse(line)}catch(_){throw archiveError('The archive manifest is not valid JSON.')} + validateManifestRecord(record,decoded.compression);return{record,compression:decoded.compression}; + } + } + throw archiveError('The archive manifest is incomplete.'); + }finally{try{await reader.cancel()}catch(_){}} + } + + async function inspectArchive(file,{signal}={}){ + const{record}=await firstArchiveRecord(file,signal); + return{ + archiveVersion:record.archiveVersion, + saveSchema:record.saveSchema, + gameplayVersion:record.gameplayVersion, + worldGeneration:record.worldGeneration, + appVersion:record.appVersion, + generatorVersion:record.generatorVersion, + exportedAt:record.exportedAt, + boardCount:record.boardCount, + estimatedRawBytes:record.estimatedRawBytes, + compression:record.compression + }; + } + + async function readArchive(file,{signal,onProgress,onManifest,onGlobal,onBoard}={}){ + if(!(file instanceof Blob))throw archiveError('No save file was selected.'); + if(file.size<=0||file.size>MAX_ARCHIVE_FILE_BYTES)throw archiveError('The save file size is outside the supported range.','ARCHIVE_TOO_LARGE'); + const decoded=await decodedStream(file),reader=decoded.stream.getReader(),decoder=new TextDecoder(),report=progressReporter(onProgress),codec=createArchiveCodec(); + let buffer='',decodedBytes=0,rawBytes=0,crc=0xffffffff,crcHex='00000000',phase='manifest',manifest=null,globalState=null,boardsDone=0,previousBoardNumber=-1,footer=null,pendingLines=[],pendingLineBytes=0; + const processRecord=async(record,state)=>{ + throwIfAborted(signal);rawBytes=state.rawBytes;crcHex=state.crc32; + if(phase==='manifest'){ + validateManifestRecord(record,decoded.compression);manifest=record;phase='global';await onManifest?.(record);return; + } + if(phase==='global'){ + if(record?.type!=='global'||!record.value||typeof record.value!=='object')throw archiveError('The archive global record is missing or invalid.'); + globalState=record.value;phase='boards';await onGlobal?.(record.value);return; + } + if(phase!=='boards')throw archiveError('The archive contains records after its footer.'); + if(record?.type==='board'){ + const number=boardNumber(record.id);if(number<0||number<=previousBoardNumber)throw archiveError('Archive boards are duplicated or not in numeric order.'); + if(record.id==='B0'&&boardsDone!==0)throw archiveError('B0 must be the first board.');if(boardsDone===0&&record.id!=='B0')throw archiveError('The archive does not begin with B0.');if(boardsDone>=manifest.boardCount)throw archiveError('The archive contains more boards than declared.'); + previousBoardNumber=number;await onBoard?.(record,boardsDone);boardsDone++;report({phase:'validate',boardsDone,boardsTotal:manifest.boardCount,bytesRead:decodedBytes,bytesWritten:0});return; + } + if(record?.type==='end'){ + footer=record;phase='done';if(boardsDone!==manifest.boardCount||record.boardCount!==boardsDone)throw archiveError('The archive board count does not match its footer.');if(record.rawBytes!==state.rawBytes||String(record.crc32).toLowerCase()!==state.crc32)throw archiveError('The archive integrity check failed.','CHECKSUM_MISMATCH');return; + } + throw archiveError('The archive contains an unknown or misplaced record.'); + }; + const flushLines=async()=>{ + if(!pendingLines.length)return;const lines=pendingLines;pendingLines=[];pendingLineBytes=0; + if(codec){const result=await codec.parse(lines);for(const item of result.items){if(item.byteLength>MAX_ARCHIVE_LINE_BYTES)throw archiveError('An archive record exceeded the size limit.','LINE_TOO_LARGE');await processRecord(item.record,item)}return} + for(const sourceLine of lines){const line=sourceLine.replace(/\r$/,'');if(!line)throw archiveError('The archive contains an empty record.');const lineBytes=encoder.encode(`${line}\n`);if(lineBytes.byteLength>MAX_ARCHIVE_LINE_BYTES)throw archiveError('An archive record exceeded the size limit.','LINE_TOO_LARGE');let record;try{record=JSON.parse(line)}catch(_){throw archiveError('An archive record is not valid JSON.')}if(record?.type!=='end'){crc=crc32Update(crc,lineBytes);rawBytes+=lineBytes.byteLength;crcHex=crc32Hex(crc)}await processRecord(record,{rawBytes,crc32:crcHex})} + }; + try{ + while(true){throwIfAborted(signal);const{value,done}=await reader.read();if(done)break;decodedBytes+=value.byteLength;if(decodedBytes>MAX_ARCHIVE_RAW_BYTES)throw archiveError('The expanded archive exceeded the size limit.','ARCHIVE_TOO_LARGE');if(decoded.compression==='gzip'&&decodedBytes/Math.max(1,file.size)>MAX_ARCHIVE_EXPANSION_RATIO)throw archiveError('The archive expansion ratio is unsafe.','EXPANSION_LIMIT');buffer+=decoder.decode(value,{stream:true});if(encoder.encode(buffer).byteLength>MAX_ARCHIVE_LINE_BYTES&&!buffer.includes('\n'))throw archiveError('An archive record exceeded the size limit.','LINE_TOO_LARGE');let newline;while((newline=buffer.indexOf('\n'))>=0){const line=buffer.slice(0,newline);buffer=buffer.slice(newline+1);if(!line)throw archiveError('The archive contains an empty record.');const estimatedBytes=line.length*3+1;if(pendingLines.length&&(pendingLines.length>=MAX_WORKER_RECORDS||pendingLineBytes+estimatedBytes>WORKER_TARGET_BYTES))await flushLines();pendingLines.push(line);pendingLineBytes+=estimatedBytes;if(pendingLines.length>=MAX_WORKER_RECORDS||pendingLineBytes>=WORKER_TARGET_BYTES)await flushLines()}} + buffer+=decoder.decode();if(buffer){const estimatedBytes=buffer.length*3+1;if(pendingLines.length&&pendingLineBytes+estimatedBytes>WORKER_TARGET_BYTES)await flushLines();pendingLines.push(buffer);pendingLineBytes+=estimatedBytes}await flushLines();if(phase!=='done'||!footer)throw archiveError('The archive footer is missing or incomplete.');report({phase:'validate',boardsDone,boardsTotal:manifest.boardCount,bytesRead:decodedBytes,bytesWritten:0},true);return{manifest,globalState,footer,boardCount:boardsDone,decodedBytes,compression:decoded.compression,worker:codec!=null}; + }finally{try{reader.releaseLock()}catch(_){}codec?.terminate()} + } + + async function cleanupTemporaryExports({olderThanMs=24*60*60*1000}={}){ + if(!global.navigator?.storage?.getDirectory)return 0;let removed=0;const root=await global.navigator.storage.getDirectory(),cutoff=Date.now()-olderThanMs; + for await(const[name]of root.entries()){ + const match=/^\.bend-field-save-.*\.(\d+)\.partial$/.exec(name);if(!match||Number(match[1])>cutoff)continue; + try{await root.removeEntry(name);removed++}catch(_){} + } + return removed; + } + + global.BendFieldPersistence=Object.freeze({ + ARCHIVE_FORMAT,ARCHIVE_VERSION,MAX_ARCHIVE_FILE_BYTES,MAX_ARCHIVE_RAW_BYTES,MAX_ARCHIVE_LINE_BYTES, + MAX_ARCHIVE_EXPANSION_RATIO,MAX_BLOB_BYTES,crc32Update,crc32Hex,createArchiveDestination, + inspectArchive,writeArchive,readArchive,cleanupTemporaryExports + }); +})(globalThis); diff --git a/index.html b/index.html new file mode 100644 index 0000000..95e1981 --- /dev/null +++ b/index.html @@ -0,0 +1,159 @@ + + + + + + +曲線フィールド v47.77 + + + + + +

+
+
曲線フィールド v47.77
+
クリア 0
+
0
+
盤面 1
+
FPS 待機
+
レベル1
+
+
+ + + + + + +
+ +
+
+ + +
+ + + + + + + + + + +
+ + + + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..596fa04 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "bend-field-v47-shared-world", + "private": true, + "version": "47.77.0", + "scripts": { + "start": "node server.js", + "test": "node test/run-all.js", + "benchmark:browser": "node test/browser-performance-benchmark.js", + "benchmark:field-storage": "node test/browser-field-storage-benchmark.js" + } +} diff --git a/puzzle-core.js b/puzzle-core.js new file mode 100644 index 0000000..55052cf --- /dev/null +++ b/puzzle-core.js @@ -0,0 +1,557 @@ +'use strict'; +(function(root){ +const GENERATOR_VERSION=5; +const SIDE_D={N:[-1,0],S:[1,0],W:[0,-1],E:[0,1]}; +function key2(x,y){return x+','+y} +function ckey(r,c){return r+','+c} +function sameCell(a,b){return a&&b&&a[0]===b[0]&&a[1]===b[1]} +function hash32(x){x|=0;x=Math.imul(x^(x>>>16),0x45d9f3b);x=Math.imul(x^(x>>>16),0x45d9f3b);return (x^(x>>>16))>>>0}function hash2(x,y){return hash32(Math.imul(x|0,73856093)^Math.imul(y|0,19349663))}function rngFrom(seed){let s=seed>>>0;return()=>((s=hash32(s+0x9e3779b9))>>>0)/4294967296}function shuffle(a,rng){for(let i=a.length-1;i>0;i--){const j=Math.floor(rng()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a} +function smooth(t){return t*t*(3-2*t)}function lattice(x,y){return hash2(x,y)/4294967295}function noise2(x,y,scale=5){const fx=x/scale,fy=y/scale,x0=Math.floor(fx),y0=Math.floor(fy),tx=smooth(fx-x0),ty=smooth(fy-y0);const a=lattice(x0,y0),b=lattice(x0+1,y0),c=lattice(x0,y0+1),d=lattice(x0+1,y0+1);const u=a+(b-a)*tx,v=c+(d-c)*tx;return u+(v-u)*ty} +const STARTER_DIFFICULTIES=Object.freeze({ + '-1,-1':2,'0,-1':3,'1,-1':3, + '-1,0':3, '0,0':1, '1,0':2, + '-1,1':2, '0,1':1, '1,1':3 +}); +function macroDifficulty(x,y){ + const starter=STARTER_DIFFICULTIES[key2(x,y)]; + if(starter!=null)return starter; + // Broad low-frequency fields first, distance only as a weak long-range tendency. + const n=.68*noise2(x,y,8)+.32*noise2(x+17,y-23,15); + const dist=Math.min(1,Math.hypot(x,y)/45); + return Math.max(1,Math.min(10,1+Math.floor(10*Math.max(0,Math.min(.999999,n*.90+dist*.10))))) +} +function regionDifficultyBand(target){ + // The macro region is authoritative. Most boards must stay within ±1; + // edge regions are clamped naturally to 1–2 or 9–10. + return{ + min:Math.max(1,target-1), + max:Math.min(10,target+1) + } +} +function difficultyFitsRegion(rating,target){ + const b=regionDifficultyBand(target); + return rating>=b.min&&rating<=b.max; +} +function regionMismatch(rating,target){ + const b=regionDifficultyBand(target); + if(ratingb.max)return rating-b.max; + return 0; +} +const DIFFICULTY_DEFINITIONS=Object.freeze([ + Object.freeze({level:1,name:'SIMPLE',description:'Short routes, few bends, and a compact search space.'}), + Object.freeze({level:2,name:'EASY',description:'A compact field with slightly longer or more winding routes.'}), + Object.freeze({level:3,name:'STEADY',description:'More route choices, bends, and gate-pair possibilities.'}), + Object.freeze({level:4,name:'MODERATE',description:'A larger field whose routes require forward planning.'}), + Object.freeze({level:5,name:'STANDARD',description:'Balanced board size and route complexity.'}), + Object.freeze({level:6,name:'TACTICAL',description:'Longer routes and more interacting decisions.'}), + Object.freeze({level:7,name:'HARD',description:'Large fields with complex route interactions.'}), + Object.freeze({level:8,name:'EXPERT',description:'Dense expert fields with little room for recovery.'}), + Object.freeze({level:9,name:'MASTER',description:'Very large search spaces and demanding clue interactions.'}), + Object.freeze({level:10,name:'ABYSS',description:'The largest, longest, and most winding solutions.'}) +]); +function difficultyDefinition(level=1){ + const safe=Math.max(1,Math.min(10,Number.isFinite(level)?Math.round(level):1)); + return DIFFICULTY_DEFINITIONS[safe-1]; +} +function normalizeShape(chunks){let minx=Math.min(...chunks.map(q=>q[0])),miny=Math.min(...chunks.map(q=>q[1]));return chunks.map(([x,y])=>[x-minx,y-miny]).sort((a,b)=>a[1]-b[1]||a[0]-b[0])} +function transformShape(shape,rot=0,flip=false){let p=shape.map(([x,y])=>[flip?-x:x,y]);for(let k=0;k[-y,x]);return normalizeShape(p)} +const BASE_SHAPES=[ + [[0,0]],[[0,0],[1,0]],[[0,0],[0,1]],[[0,0],[1,0],[2,0]],[[0,0],[0,1],[0,2]], + [[0,0],[1,0],[0,1]],[[0,0],[1,0],[1,1]],[[0,0],[1,0],[0,1],[1,1]], + [[0,0],[1,0],[2,0],[0,1]],[[0,0],[1,0],[2,0],[1,1]],[[0,0],[1,0],[1,1],[2,1]], + [[0,0],[1,0],[0,1],[1,1],[0,2]],[[0,0],[1,0],[2,0],[0,1],[0,2]], + [[0,0],[1,0],[2,0],[0,1],[1,1],[0,2]],[[0,0],[1,0],[2,0],[1,1],[2,1],[2,2]] +]; +const SHAPES=[];{const seen=new Set();for(const s of BASE_SHAPES)for(let f=0;f<2;f++)for(let r=0;r<4;r++){const q=transformShape(s,r,!!f),k=q.map(v=>v.join(':')).join('|');if(!seen.has(k)){seen.add(k);SHAPES.push(q)}}} +function shapeCells(chunks){const out=[];for(const[x,y]of chunks)for(let r=0;r<5;r++)for(let c=0;c<5;c++)out.push([y*5+r,x*5+c]);return out} +function shapeBounds(chunks){return{w:(Math.max(...chunks.map(q=>q[0]))+1)*5,h:(Math.max(...chunks.map(q=>q[1]))+1)*5}} + + +function outsideCell(g){const[dR,dC]=SIDE_D[g[2]];return[g[0]+dR,g[1]+dC]} +function turnAnalysisRaw(path,gates){const gs=gates[path.startGate],ge=gates[path.endGate],pts=[outsideCell(gs),...path.cells,outsideCell(ge)],dirs=[];for(let i=0;i=0&&value>>0))%H_PORT_PROFILES.length; + // Preserve the established topology for stored boards and the starter board. + if(unitX===0&&boundaryY===0)return 0; + if(unitX===0&&boundaryY===1)return 2; + const h=boundaryHash(unitX,boundaryY,0x41c64e6d)%100; + if(h<39)return 0; + if(h<78)return 1; + return 2; +} +function verticalPortProfileIndex(boundaryX,unitY,options=null){ + const fixed=fixedProfileIndex(options,verticalBoundaryKey(boundaryX,unitY),V_PORT_PROFILES.length); + if(fixed!=null)return fixed; + if(Number.isInteger(options?.portSeed))return boundaryHash(boundaryX,unitY,0x9e3779b9^(options.portSeed>>>0))%V_PORT_PROFILES.length; + if(boundaryX===0&&unitY===0)return 1; + if(boundaryX===1&&unitY===0)return 3; + const h=boundaryHash(boundaryX,unitY,0x9e3779b9)%100; + if(h<28)return 0; + if(h<56)return 1; + if(h<82)return 2; + if(h<91)return 3; + return 4; +} +function unitPortSpec(unitX,unitY,options=null){ + return{ + ni:horizontalPortProfileIndex(unitX,unitY,options), + si:horizontalPortProfileIndex(unitX,unitY+1,options), + wi:verticalPortProfileIndex(unitX,unitY,options), + ei:verticalPortProfileIndex(unitX+1,unitY,options) + }; +} +function portSignature(spec){ + return `${spec.ni},${spec.si},${spec.wi},${spec.ei}`; +} +function registerExternalPorts(map,dx,dy,spec,chunkSet){ + if(!chunkSet.has(key2(dx,dy-1))) + for(const c of H_PORT_PROFILES[spec.ni])map.set(ckey(dy*5,dx*5+c),'N'); + if(!chunkSet.has(key2(dx,dy+1))) + for(const c of H_PORT_PROFILES[spec.si])map.set(ckey(dy*5+4,dx*5+c),'S'); + if(!chunkSet.has(key2(dx-1,dy))) + for(const r of V_PORT_PROFILES[spec.wi])map.set(ckey(dy*5+r,dx*5),'W'); + if(!chunkSet.has(key2(dx+1,dy))) + for(const r of V_PORT_PROFILES[spec.ei])map.set(ckey(dy*5+r,dx*5+4),'E'); +} + +function puzzleCandidateFromPatterns(chunks,seed,level,attempt,originX=0,originY=0,generationOptions=null){ + const valid=shapeCells(chunks), + bounds=shapeBounds(chunks), + chunkSet=new Set(chunks.map(q=>key2(q[0],q[1]))), + externalSides=new Map(), + adj=new Map(valid.map(c=>[ckey(c[0],c[1]),[]])); + + const addEdge=(a,b)=>{ + const ka=ckey(a[0],a[1]),kb=ckey(b[0],b[1]); + if(!adj.has(ka)||!adj.has(kb))return; + adj.get(ka).push([b[0],b[1]]); + adj.get(kb).push([a[0],a[1]]); + }; + + // Each 5x5 area's connector signature is derived from its WORLD boundary coordinates. + // Two independently generated neighboring boards therefore always agree on the exact cells. + for(const[dx,dy]of chunks){ + const unitX=originX+dx,unitY=originY+dy, + spec=unitPortSpec(unitX,unitY,generationOptions), + variants=MICRO_PATTERN_SETS[portSignature(spec)]; + + if(!variants?.length)return null; + + const localSeed=hash32( + seed^ + Math.imul(attempt+1,0x45d9f3b)^ + Math.imul(unitX+37,73856093)^ + Math.imul(unitY+53,19349663) + ); + const rng=rngFrom(localSeed), + pat=variants[Math.floor(rng()*variants.length)%variants.length]; + + for(const frag of pat){ + const pts=frag.map(([r,c])=>[dy*5+r,dx*5+c]); + for(let i=0;i(adj.get(ckey(c[0],c[1]))||[]).length===1); + if(ends.length!==2)return null; + + const sSide=externalSides.get(ckey(ends[0][0],ends[0][1])), + eSide=externalSides.get(ckey(ends[1][0],ends[1][1])); + if(!sSide||!eSide)return null; + + const ordered=[[ends[0][0],ends[0][1]]]; + let prev=null,cur=ends[0],guard=valid.length+5; + + while(!sameCell(cur,ends[1])&&guard-->0){ + const nexts=(adj.get(ckey(cur[0],cur[1]))||[]) + .filter(v=>!prev||!sameCell(v,prev)); + if(!nexts.length)return null; + const next=nexts[0]; + prev=cur; + cur=next; + ordered.push([cur[0],cur[1]]); + } + + if(!sameCell(cur,ends[1]))return null; + components.push({cells:ordered,sSide,eSide}); + } + + const gates=[],nums=[],solution=[]; + let maxTurns=0,totalTurns=0; + const clueRng=rngFrom(hash32(seed^Math.imul(attempt+11,0x27d4eb2d))); + + for(const comp of components){ + const first=comp.cells[0],last=comp.cells[comp.cells.length-1]; + const s=gates.length; + gates.push([first[0],first[1],comp.sSide]); + const e=gates.length; + gates.push([last[0],last[1],comp.eSide]); + + const path={startGate:s,endGate:e,cells:comp.cells}, + ta=turnAnalysisRaw(path,gates); + + if(!ta.cells.length||ta.count<=0)return null; + + maxTurns=Math.max(maxTurns,ta.count); + totalTurns+=ta.count; + + let bend; + if(level<=3){ + const edgePool=[ + ...ta.cells.slice(0,Math.min(2,ta.cells.length)), + ...ta.cells.slice(Math.max(0,ta.cells.length-2)) + ]; + bend=edgePool[Math.floor(clueRng()*edgePool.length)]; + }else{ + const lo=Math.floor(ta.cells.length*.25), + hi=Math.max(lo+1,Math.ceil(ta.cells.length*.75)); + bend=ta.cells[lo+Math.floor(clueRng()*(hi-lo))] + ||ta.cells[Math.floor(clueRng()*ta.cells.length)]; + } + + nums.push([bend[0],bend[1],ta.count]); + solution.push(path); + } + + return{ + g:gates,n:nums,valid,bounds,axis:'MIX',solution,level, + maxTurns,totalTurns,style:'variable-world-gates' + }; +} + +function solutionComplexity(p){ + const areaUnits=Math.max(1,p.valid.length/25), + paths=Array.isArray(p.solution)?p.solution:[], + lineCount=Math.max(1,paths.length||p.n.length), + turns=paths.length + ?paths.map(path=>turnAnalysisRaw(path,p.g).count) + :p.n.map(n=>n[2]), + lengths=paths.length?paths.map(path=>path.cells.length):[p.valid.length/lineCount], + totalTurns=turns.reduce((sum,value)=>sum+value,0), + avgTurns=totalTurns/lineCount, + maxTurns=Math.max(0,...turns), + avgLength=lengths.reduce((sum,value)=>sum+value,0)/lineCount, + maxLength=Math.max(0,...lengths); + + // This is intentionally independent of the requested world region. It measures + // the solution we actually generated: + // board area + longest/average bend load + longest/average route + pair count. + const score= + 1+ + 1.35*Math.log2(areaUnits)+ + .32*Math.max(0,maxTurns-3)+ + .22*Math.max(0,avgTurns-2.5)+ + .09*Math.max(0,maxLength-8)+ + .12*Math.max(0,avgLength-5)+ + .08*Math.max(0,lineCount-5); + const rating=Math.max(1,Math.min(10,Math.floor(score+.15))); + return{rating,score,areaUnits,lineCount,totalTurns,avgTurns,maxTurns,avgLength,maxLength}; +} +function clueConstrainedDifficulty(p,rating){ + if(rating<=5)return rating; + const clues=(p.n||[]).map(clue=>Number(clue[2])||0),demanding=clues.filter(value=>value>=4).length; + // A single short branch should not collapse an otherwise large, demanding + // board to level 5. Cap only when the board has no meaningful bend load. + return !clues.length||Math.max(...clues)<4||demanding=7)return raw; + return target; +} +function generatePuzzle(chunks,seed,level,originX=0,originY=0,generationOptions=null){ + // Macro geography is primary: first search only within the region's allowed band. + // Among valid candidates, prefer the exact regional target. + let bestInBand=null,bestInBandScore=Infinity; + let closest=null,closestScore=Infinity; + const tries=level<=2?96:level<=5?64:48; + + for(let attempt=0;attempt>>0:null; + p.complexity=complexity; + p.difficulty=rating; + const linePenalty=p.solution.length<3?6:0; + const targetDistance=Math.abs(rating-level); + const intrinsicTie=Math.abs(complexity.score-(level+.25)); + + const score=targetDistance*80+linePenalty+intrinsicTie; + + if(difficultyFitsRegion(rating,level)&&score>>0:null; + p.complexity=complexity; + p.difficulty=rating; + + if(difficultyFitsRegion(rating,level)){ + p.level=rating; + p.regionalTarget=level; + return p; + } + + const mismatch=regionMismatch(rating,level); + const score=mismatch*1000+Math.abs(rating-level)*80; + if(scorewarpPairKey(pair.a,pair.b))),result=new Set(); + for(const path of puzzle?.solution||[])for(let index=1;indexsameCell(candidate,cell))??-1;if(index<0)continue; + const previous=path.cells[index-1],next=path.cells[index+1];if(!previous||!next)continue; + const horizontal=previous[0]===cell[0]&&next[0]===cell[0],vertical=previous[1]===cell[1]&&next[1]===cell[1];if(horizontal||vertical)axes.push(horizontal?'H':'V'); + } + if(axes.includes('H')&&axes.includes('V'))usedCrossings++; + } + for(const pair of warpPairs)if(paths.some(path=>path.cells?.some((cell,index)=>index>0&&(sameCell(path.cells[index-1],pair.a)&&sameCell(cell,pair.b)||sameCell(path.cells[index-1],pair.b)&&sameCell(cell,pair.a)))))usedWarps++; + for(const lock of locks)if(paths.some(path=>{const keyIndex=path.cells?.findIndex(cell=>sameCell(cell,lock.key))??-1,doorIndex=path.cells?.findIndex(cell=>sameCell(cell,lock.door))??-1;return keyIndex>=0&&doorIndex>keyIndex}))usedLocks++; + return{total:crossings.length+warpPairs.length+locks.length,used:usedCrossings+usedWarps+usedLocks,usedCrossings,usedWarps,usedLocks}; +} +function interactionQuality(puzzle,stats,analysis={}){ + const paths=puzzle.solution||[],totalLength=paths.reduce((sum,path)=>sum+(path.cells?.length||0),0),maxLength=Math.max(0,...paths.map(path=>path.cells?.length||0)), + usage=solutionSpecialUsage(puzzle),specials=usage.total, + forcedRatio=stats.nodeCount?Math.min(1,stats.rootForced/stats.nodeCount):0,branchPenalty=Math.min(30,Math.log2(Math.max(1,stats.searchNodes))*3), + monotonyPenalty=Math.max(0,maxLength-Math.max(18,totalLength*.45))*.55, + redundantClues=Math.max(0,analysis.redundantClues||0),redundantSpecials=Math.max(0,analysis.redundantSpecials??Math.max(0,specials-usage.used)), + contributingSpecials=Math.max(0,analysis.contributingSpecials??usage.used),unknownQualityChecks=Math.max(0,analysis.unknownQualityChecks||0), + score=Math.max(0,Math.min(100,48+forcedRatio*24+Math.min(12,contributingSpecials*3)-branchPenalty-monotonyPenalty-redundantClues*5-redundantSpecials*8-unknownQualityChecks)); + return{score,totalLength,maxLength,rootForced:stats.rootForced,searchNodes:stats.searchNodes,maxCandidates:stats.maxCandidates,firstBranchDepth:stats.firstBranchDepth, + specialCount:specials,specialsUsed:usage.used,contributingSpecials,redundantClues,redundantSpecials,unknownQualityChecks,accepted:score>=20&&redundantSpecials===0}; +} +function verifyPuzzleUniqueness(puzzle,{maxMs=450,nodeCap=250000,analyzeQuality=true,allowMissingClues=false}={}){ + const started=Date.now(),signature=puzzleSignature(puzzle),finish=(status,extra={})=>({status,signature,ruleVersion:UNIQUENESS_RULE_VERSION,elapsedMs:Date.now()-started,...extra}); + try{ + if(!puzzle||!Array.isArray(puzzle.valid)||!puzzle.valid.length||!Array.isArray(puzzle.g)||!puzzle.g.length||puzzle.g.length%2||!Array.isArray(puzzle.n))return finish('invalid'); + const validSet=new Set(),gateByCell=new Map(),crossings=new Set((puzzle.specialCells?.crossings||[]).map(cell=>ckey(...cell))); + for(const cell of puzzle.valid){if(!Array.isArray(cell)||cell.length!==2||!Number.isInteger(cell[0])||!Number.isInteger(cell[1])||validSet.has(ckey(...cell)))return finish('invalid');validSet.add(ckey(...cell))} + for(let index=0;index{ + const index=nodes.length,key=ckey(...cell);nodes.push({cell:[cell[0],cell[1]],axis,target:axis?2:gateByCell.has(key)?1:2,edges:[],fixed:[]});return index; + }; + for(const cell of puzzle.valid){const key=ckey(...cell);if(crossings.has(key))cellNodes.set(key,{H:addNode(cell,'H'),V:addNode(cell,'V')});else cellNodes.set(key,{N:addNode(cell,null)})} + const nodeFor=(cell,axis)=>{const entry=cellNodes.get(ckey(...cell));return entry&&(axis==='H'?entry.H??entry.N:axis==='V'?entry.V??entry.N:entry.N)}; + const edges=[],edgeSeen=new Set(),preferred=solutionGridEdges(puzzle);if(!preferred)return finish('invalid'); + const addEdge=(a,b,axis)=>{ + const physical=physicalEdgeKey(a,b);if(edgeSeen.has(physical))return;edgeSeen.add(physical); + const left=nodeFor(a,axis),right=nodeFor(b,axis);if(!Number.isInteger(left)||!Number.isInteger(right))return; + const index=edges.length;edges.push({a:left,b:right,physical,preferred:preferred.has(physical)});nodes[left].edges.push(index);nodes[right].edges.push(index); + }; + for(const cell of puzzle.valid){const right=[cell[0],cell[1]+1],down=[cell[0]+1,cell[1]];if(validSet.has(ckey(...right)))addEdge(cell,right,'H');if(validSet.has(ckey(...down)))addEdge(cell,down,'V')} + const warpKeys=new Set(); + for(const pair of puzzle.specialCells?.warps||[]){ + const left=nodeFor(pair?.a),right=nodeFor(pair?.b);if(!Number.isInteger(left)||!Number.isInteger(right)||left===right)return finish('invalid'); + const key=warpPairKey(pair.a,pair.b);if(warpKeys.has(key))return finish('invalid');warpKeys.add(key);nodes[left].fixed.push(right);nodes[right].fixed.push(left); + } + for(const lock of puzzle.specialCells?.locks||[])if(!validSet.has(ckey(...(lock?.key||[])))||!validSet.has(ckey(...(lock?.door||[]))))return finish('invalid'); + const state=new Int8Array(edges.length);state.fill(-1);const degreeOn=new Int16Array(nodes.length),unknown=new Int16Array(nodes.length); + for(let index=0;indexnodes[index].target||degreeOn[index]+unknown[index]{ + const current=state[edgeIndex];if(current!==-1)return current===value; + state[edgeIndex]=value;trail.push(edgeIndex);const edge=edges[edgeIndex]; + unknown[edge.a]--;unknown[edge.b]--;if(value){degreeOn[edge.a]++;degreeOn[edge.b]++}return true; + },rollback=mark=>{while(trail.length>mark){const edgeIndex=trail.pop(),edge=edges[edgeIndex],value=state[edgeIndex];if(value){degreeOn[edge.a]--;degreeOn[edge.b]--}unknown[edge.a]++;unknown[edge.b]++;state[edgeIndex]=-1}}; + const propagate=()=>{ + let changed=true; + while(changed){changed=false;for(let nodeIndex=0;nodeIndexunknown[nodeIndex])return false; + if(!unknown[nodeIndex]||need!==0&&need!==unknown[nodeIndex])continue;const value=need===unknown[nodeIndex]?1:0; + for(const edgeIndex of node.edges)if(state[edgeIndex]===-1){if(!assign(edgeIndex,value))return false;changed=true} + }} + return true; + }; + const gateByNode=new Map();for(const[key,gateIndex]of gateByCell){const node=nodeFor(key.split(',').map(Number));if(!Number.isInteger(node))return finish('invalid');gateByNode.set(node,gateIndex)} + const validateSolution=()=>{ + const adjacency=nodes.map(node=>[...node.fixed]);for(let index=0;index0){if(visited.has(current)&¤t!==startNode)return null;visited.add(current);routeByNode.set(current,routes.length);routeNodes.push(current);cells.push([...nodes[current].cell]); + if(current!==startNode&&gateByNode.has(current)){const endGate=gateByNode.get(current),forward=startGate<=endGate,routedCells=forward?cells:[...cells].reverse();routes.push({startGate:Math.min(startGate,endGate),endGate:Math.max(startGate,endGate),cells:routedCells,nodes:forward?routeNodes:[...routeNodes].reverse()});break} + const next=adjacency[current].filter(node=>node!==previous);if(next.length!==1)return null;previous=current;current=next[0]; + } + if(!routes.length||routes[routes.length-1].nodes.indexOf(startNode)<0)return null; + } + if(visited.size!==nodes.length||routes.length*2!==puzzle.g.length)return null; + for(const cellKey of crossings){const pair=cellNodes.get(cellKey);if(routeByNode.get(pair.H)===routeByNode.get(pair.V))return null} + const cluesByRoute=routes.map(()=>[]); + for(const clue of puzzle.n){const entry=cellNodes.get(ckey(clue[0],clue[1])),routeIds=new Set(Object.values(entry||{}).map(node=>routeByNode.get(node)).filter(Number.isInteger));if(routeIds.size!==1)return null;cluesByRoute[[...routeIds][0]].push(clue)} + for(let index=0;index1||!allowMissingClues&&cluesByRoute[index].length!==1)return null;const clue=cluesByRoute[index][0];if(!clue)continue;const route=routes[index],turns=routeTurnCount(route,puzzle.g,warpKeys);if(turns!==clue[2])return null} + for(const lock of puzzle.specialCells?.locks||[]){const keyEntry=cellNodes.get(ckey(...lock.key)),doorEntry=cellNodes.get(ckey(...lock.door)),keyRoutes=new Set(Object.values(keyEntry||{}).map(node=>routeByNode.get(node))),doorRoutes=new Set(Object.values(doorEntry||{}).map(node=>routeByNode.get(node)));if(![...keyRoutes].some(id=>doorRoutes.has(id)))return null} + return routes.sort((left,right)=>left.startGate-right.startGate||left.endGate-right.endGate).map(route=>`${route.startGate}-${route.endGate}:${route.cells.map(cell=>ckey(...cell)).join(';')}`).join('|'); + }; + let searchNodes=0,timedOut=false,maxCandidates=0,firstBranchDepth=null,rootForced=0;const canonical=new Set(); + if(!propagate())return finish('unsolved',{solutions:0,nodes:0});rootForced=trail.length; + const search=depth=>{ + if(canonical.size>=2)return;if(++searchNodes>nodeCap||((searchNodes&1023)===0&&Date.now()-started>maxMs)){timedOut=true;return} + if(!propagate())return; + let chosen=-1,bestUnknown=Infinity; + for(let nodeIndex=0;nodeIndexstate[edgeIndex]===-1&&edges[edgeIndex].preferred);if(chosen==null||chosen<0)chosen=nodes[nodeIndex].edges.find(edgeIndex=>state[edgeIndex]===-1);if(bestUnknown===1)break}} + if(chosen<0){const key=validateSolution();if(key)canonical.add(key);return} + if(firstBranchDepth==null)firstBranchDepth=depth;maxCandidates=Math.max(maxCandidates,bestUnknown);const mark=trail.length,order=edges[chosen].preferred?[1,0]:[0,1]; + for(const value of order){if(assign(chosen,value))search(depth+1);rollback(mark);if(canonical.size>=2||timedOut)break} + }; + search(0); + if(canonical.size>=2)return finish('multiple',{solutions:2,nodes:searchNodes}); + if(timedOut)return finish('timeout',{solutions:canonical.size,nodes:searchNodes}); + if(!canonical.size)return finish('unsolved',{solutions:0,nodes:searchNodes}); + const stats={nodeCount:nodes.length,rootForced,searchNodes,maxCandidates,firstBranchDepth:firstBranchDepth??0}; + let qualityAnalysis={}; + if(analyzeQuality){ + const usage=solutionSpecialUsage(puzzle),remaining=()=>Math.max(0,maxMs-(Date.now()-started)),checks=[]; + for(let index=0;index { + const {id, action='generate', chunks, seed, level, originX, originY, generationOptions, generatorVersion} = data; + try { + if (generatorVersion !== BendPuzzle.GENERATOR_VERSION) throw new Error('Generator version mismatch'); + if (action === 'verify') { + const signature = BendPuzzle.puzzleSignature(data.puzzle); + const cacheKey = JSON.stringify([signature, data.limits || null]); + let result = uniquenessCache.get(cacheKey); + if (!result) { + result = BendPuzzle.verifyPuzzleUniqueness(data.puzzle, data.limits || null); + uniquenessCache.set(cacheKey, result); + if (uniquenessCache.size > 256) uniquenessCache.delete(uniquenessCache.keys().next().value); + } + self.postMessage({id, result, generatorVersion: BendPuzzle.GENERATOR_VERSION}); + return; + } + const puzzle = BendPuzzle.generatePuzzle(chunks, seed, level, originX, originY, generationOptions || null); + self.postMessage({id, puzzle, generatorVersion: BendPuzzle.GENERATOR_VERSION}); + } catch (error) { + self.postMessage({id, error: error?.message || String(error), generatorVersion: BendPuzzle.GENERATOR_VERSION}); + } +}; diff --git a/realtime-server.js b/realtime-server.js new file mode 100644 index 0000000..8015881 --- /dev/null +++ b/realtime-server.js @@ -0,0 +1,286 @@ +'use strict'; + +const crypto = require('crypto'); +const { URL } = require('url'); + +const WS_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; +const MAX_MESSAGE_BYTES = 64 * 1024; +const MAX_VIEW_SPAN = 512; +const MAX_WORLD_COORD = 1_000_000_000; +const REACTION_EMOJIS = new Set(['👍','👉🏻','🙏','🧠','🎉']); +const REACTION_TTL_MS = 4500; +const MAX_REACTIONS = 1024; +const REACTION_MIN_INTERVAL_MS = 450; +const SUBSCRIPTION_BUCKET_SIZE = 64; + +function finite(value, fallback = 0) { + const number = Number(value); + return Number.isFinite(number) ? number : fallback; +} +function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } +function cleanCursorStyle(value) { + const style = String(value || 'default').replace(/[^a-z0-9_-]/gi, '').slice(0, 80); + return style || 'default'; +} +function cleanBoardId(value) { + const id = String(value || ''); + return /^B(?:0|[1-9][0-9]*)$/.test(id) ? id : null; +} +function normalizeViewport(message) { + let minX = clamp(finite(message.minX), -MAX_WORLD_COORD, MAX_WORLD_COORD); + let minY = clamp(finite(message.minY), -MAX_WORLD_COORD, MAX_WORLD_COORD); + let maxX = clamp(finite(message.maxX), -MAX_WORLD_COORD, MAX_WORLD_COORD); + let maxY = clamp(finite(message.maxY), -MAX_WORLD_COORD, MAX_WORLD_COORD); + if (maxX < minX) [minX, maxX] = [maxX, minX]; + if (maxY < minY) [minY, maxY] = [maxY, minY]; + const centerX = (minX + maxX) / 2, centerY = (minY + maxY) / 2; + const spanX = Math.min(MAX_VIEW_SPAN, Math.max(1, maxX - minX)); + const spanY = Math.min(MAX_VIEW_SPAN, Math.max(1, maxY - minY)); + return {minX: centerX - spanX / 2, minY: centerY - spanY / 2, maxX: centerX + spanX / 2, maxY: centerY + spanY / 2}; +} +function pointInViewport(viewport, x, y, margin = 4) { + return Boolean(viewport) && x >= viewport.minX - margin && x <= viewport.maxX + margin && y >= viewport.minY - margin && y <= viewport.maxY + margin; +} +function boundsInViewport(viewport, bounds, margin = 3) { + return Boolean(viewport && bounds) && bounds.maxX >= viewport.minX - margin && bounds.minX <= viewport.maxX + margin && bounds.maxY >= viewport.minY - margin && bounds.minY <= viewport.maxY + margin; +} +function encodeFrame(opcode, payload = Buffer.alloc(0)) { + if (!Buffer.isBuffer(payload)) payload = Buffer.from(payload); + const length = payload.length; + let header; + if (length < 126) { + header = Buffer.allocUnsafe(2); header[0] = 0x80 | opcode; header[1] = length; + } else if (length <= 0xffff) { + header = Buffer.allocUnsafe(4); header[0] = 0x80 | opcode; header[1] = 126; header.writeUInt16BE(length, 2); + } else { + header = Buffer.allocUnsafe(10); header[0] = 0x80 | opcode; header[1] = 127; header.writeBigUInt64BE(BigInt(length), 2); + } + return Buffer.concat([header, payload]); +} +function sendJson(client, value) { + if (!client || client.closed || !client.socket.writable) return false; + try { client.socket.write(encodeFrame(1, Buffer.from(JSON.stringify(value)))); return true; } + catch (_) { return false; } +} +function sendClose(client, code = 1000, reason = '') { + if (!client || client.closed) return; + client.closed = true; + const text = Buffer.from(String(reason).slice(0, 100)); + const payload = Buffer.allocUnsafe(2 + text.length); payload.writeUInt16BE(code, 0); text.copy(payload, 2); + try { client.socket.end(encodeFrame(8, payload)); } catch (_) { client.socket.destroy(); } +} + +function createRealtimeHub({server, authenticate, getBoardInfo, path = '/api/realtime', claimTtlMs = 5 * 60 * 1000, now = Date.now}) { + if (!server || typeof authenticate !== 'function' || typeof getBoardInfo !== 'function') throw new Error('Invalid realtime hub options'); + const clients = new Map(); + const claims = new Map(); + const reactions = new Map(); + const viewportBuckets = new Map(); + let serial = Promise.resolve(); + let nextConnection = 1; + let nextReaction = 1; + + function withSerial(task) { + const run = serial.then(task, task); serial = run.then(() => undefined, () => undefined); return run; + } + function viewportBucketKeys(viewport){ + if(!viewport)return[];const keys=[];const minX=Math.floor((viewport.minX-4)/SUBSCRIPTION_BUCKET_SIZE),maxX=Math.floor((viewport.maxX+4)/SUBSCRIPTION_BUCKET_SIZE),minY=Math.floor((viewport.minY-4)/SUBSCRIPTION_BUCKET_SIZE),maxY=Math.floor((viewport.maxY+4)/SUBSCRIPTION_BUCKET_SIZE); + for(let y=minY;y<=maxY;y++)for(let x=minX;x<=maxX;x++)keys.push(`${x},${y}`);return keys; + } + function unregisterViewport(client){for(const key of client.viewportBucketKeys||[]){const set=viewportBuckets.get(key);if(!set)continue;set.delete(client);if(!set.size)viewportBuckets.delete(key)}client.viewportBucketKeys=[]} + function registerViewport(client,viewport){unregisterViewport(client);client.viewport=viewport;client.viewportBucketKeys=viewportBucketKeys(viewport);for(const key of client.viewportBucketKeys){let set=viewportBuckets.get(key);if(!set)viewportBuckets.set(key,set=new Set());set.add(client)}} + function subscribersAt(x,y){const key=`${Math.floor(x/SUBSCRIPTION_BUCKET_SIZE)},${Math.floor(y/SUBSCRIPTION_BUCKET_SIZE)}`;return viewportBuckets.get(key)||new Set()} + function publicPlayer(client) { + return {presenceId: client.id, playerId: client.playerId, name: client.name, x: client.x, y: client.y, cursorStyle: client.cursorStyle || 'default', at: client.cursorAt || now()}; + } + function publicClaim(claim) { + return {boardId: claim.boardId, playerId: claim.ownerPlayerId, playerName: claim.ownerName, presenceId: claim.ownerPresenceId, expiresAt: claim.expiresAt, ...claim.bounds}; + } + function claimActive(claim, timestamp = now()) { return Boolean(claim && claim.expiresAt > timestamp); } + function publicReaction(reaction) { return {id:reaction.id, playerId:reaction.playerId, playerName:reaction.playerName, emoji:reaction.emoji, x:reaction.x, y:reaction.y, createdAt:reaction.createdAt, expiresAt:reaction.expiresAt}; } + function pruneReactions(timestamp = now()) { + for (const [id, reaction] of reactions) if (reaction.expiresAt <= timestamp) reactions.delete(id); + while (reactions.size > MAX_REACTIONS) reactions.delete(reactions.keys().next().value); + } + function pruneClaims(timestamp = now()) { + const removed = []; + for (const [boardId, claim] of claims) if (!claimActive(claim, timestamp)) { claims.delete(boardId); removed.push(boardId); } + for (const boardId of removed) broadcastAll({type:'claim-release', boardId, reason:'expired', serverTime:timestamp}); + return removed.length; + } + function snapshotFor(client) { + pruneClaims(); + const players = []; + for (const other of clients.values()) { + if (!other.authenticated || other.id === client.id || !Number.isFinite(other.x) || !Number.isFinite(other.y)) continue; + if (pointInViewport(client.viewport, other.x, other.y)) players.push(publicPlayer(other)); + } + const visibleClaims = []; + for (const claim of claims.values()) if (claim.ownerPlayerId === client.playerId || boundsInViewport(client.viewport, claim.bounds)) visibleClaims.push(publicClaim(claim)); + pruneReactions(); const visibleReactions=[]; + for (const reaction of reactions.values()) if (pointInViewport(client.viewport,reaction.x,reaction.y)) visibleReactions.push(publicReaction(reaction)); + return {type:'snapshot', players, claims:visibleClaims, reactions:visibleReactions, serverTime:now()}; + } + function sendSnapshot(client) { if (client.authenticated) sendJson(client, snapshotFor(client)); } + function broadcastAll(message, predicate = null) { + for (const client of clients.values()) if (client.authenticated && (!predicate || predicate(client))) sendJson(client, message); + } + function broadcastCursor(sender) { + const message = {type:'cursor', ...publicPlayer(sender), serverTime:now()}; + for(const client of subscribersAt(sender.x,sender.y))if(client.authenticated&&client.id!==sender.id&&pointInViewport(client.viewport,sender.x,sender.y))sendJson(client,message); + } + function broadcastClaim(claim) { + const message = {type:'claim', claim:publicClaim(claim), serverTime:now()}; + broadcastAll(message, client => client.playerId === claim.ownerPlayerId || boundsInViewport(client.viewport, claim.bounds)); + } + function releaseBoardClaim(boardId, reason = 'released') { + const claim = claims.get(boardId); if (!claim) return false; + claims.delete(boardId); + broadcastAll({type:'claim-release', boardId, reason, serverTime:now()}, client => client.playerId === claim.ownerPlayerId || boundsInViewport(client.viewport, claim.bounds)); + return true; + } + function releaseOtherClaimsForPlayer(playerId, keepBoardId) { + for (const [boardId, claim] of claims) if (boardId !== keepBoardId && claim.ownerPlayerId === playerId) releaseBoardClaim(boardId, 'moved'); + } + async function requestClaim(client, message) { + const requestId = String(message.requestId || '').slice(0, 80), boardId = cleanBoardId(message.boardId); + if (!boardId) return sendJson(client, {type:'claim-result', requestId, ok:false, reason:'invalid-board', serverTime:now()}); + await withSerial(async () => { + pruneClaims(); + const board = await getBoardInfo(boardId); + if (!board || board.solved) return sendJson(client, {type:'claim-result', requestId, ok:false, reason:board?.solved?'solved':'missing', serverTime:now()}); + const existing = claims.get(boardId); + if (claimActive(existing) && existing.ownerPlayerId !== client.playerId) return sendJson(client, {type:'claim-result', requestId, ok:false, reason:'occupied', claim:publicClaim(existing), serverTime:now()}); + releaseOtherClaimsForPlayer(client.playerId, boardId); + const timestamp = now(), claim = { + boardId, ownerPlayerId:client.playerId, ownerName:client.name, ownerPresenceId:client.id, + expiresAt:timestamp + claimTtlMs, bounds:board.bounds + }; + claims.set(boardId, claim); client.currentClaimBoardId = boardId; + sendJson(client, {type:'claim-result', requestId, ok:true, claim:publicClaim(claim), serverTime:timestamp}); + broadcastClaim(claim); + }); + } + function touchClaim(client, message) { + const boardId = cleanBoardId(message.boardId), claim = boardId && claims.get(boardId); + if (!claimActive(claim) || claim.ownerPlayerId !== client.playerId) return false; + claim.expiresAt = now() + claimTtlMs; claim.ownerName = client.name; claim.ownerPresenceId = client.id; + broadcastClaim(claim); + return true; + } + function releaseOwnClaim(client, message) { + const boardId = cleanBoardId(message.boardId) || client.currentClaimBoardId, claim = boardId && claims.get(boardId); + if (!claim || claim.ownerPlayerId !== client.playerId) return false; + return releaseBoardClaim(boardId, 'released'); + } + function publishReaction(client, message) { + const emoji=String(message.emoji||''),x=finite(message.x,NaN),y=finite(message.y,NaN),timestamp=now(); + if(!REACTION_EMOJIS.has(emoji)||!Number.isFinite(x)||!Number.isFinite(y)||Math.abs(x)>MAX_WORLD_COORD||Math.abs(y)>MAX_WORLD_COORD)return false; + if(timestamp-client.lastReactionAt MAX_WORLD_COORD || Math.abs(y) > MAX_WORLD_COORD) return; + const timestamp = now(); if (timestamp - client.lastCursorMessageAt < 45) return; + client.lastCursorMessageAt = timestamp; client.x = x; client.y = y; client.cursorStyle = cleanCursorStyle(message.cursorStyle); client.cursorAt = timestamp; broadcastCursor(client); return; + } + if (message.type === 'cursor-hide') { + const x = client.x, y = client.y; client.x = NaN; client.y = NaN; client.cursorAt = now(); + if (Number.isFinite(x) && Number.isFinite(y)) broadcastAll({type:'player-left', presenceId:client.id, playerId:client.playerId, serverTime:now()}, other => pointInViewport(other.viewport, x, y)); + return; + } + if (message.type === 'claim') return requestClaim(client, message); + if (message.type === 'claim-touch') return touchClaim(client, message); + if (message.type === 'release') return releaseOwnClaim(client, message); + if (message.type === 'reaction') return publishReaction(client,message); + if (message.type === 'snapshot-request') return sendSnapshot(client); + } + function consumeFrames(client, chunk) { + if (client.closed) return; + client.buffer = client.buffer.length ? Buffer.concat([client.buffer, chunk]) : chunk; + while (client.buffer.length >= 2) { + const first = client.buffer[0], second = client.buffer[1], fin = Boolean(first & 0x80), opcode = first & 0x0f, masked = Boolean(second & 0x80); + let length = second & 0x7f, offset = 2; + if (!masked) return sendClose(client, 1002, 'Client frames must be masked'); + if (length === 126) { if (client.buffer.length < 4) return; length = client.buffer.readUInt16BE(2); offset = 4; } + else if (length === 127) { + if (client.buffer.length < 10) return; const big = client.buffer.readBigUInt64BE(2); if (big > BigInt(MAX_MESSAGE_BYTES)) return sendClose(client, 1009, 'Message too large'); length = Number(big); offset = 10; + } + if (length > MAX_MESSAGE_BYTES || client.buffer.length < offset + 4 + length) { if (length > MAX_MESSAGE_BYTES) sendClose(client, 1009, 'Message too large'); return; } + const mask = client.buffer.subarray(offset, offset + 4); offset += 4; + const payload = Buffer.from(client.buffer.subarray(offset, offset + length)); client.buffer = client.buffer.subarray(offset + length); + for (let index = 0; index < payload.length; index++) payload[index] ^= mask[index & 3]; + if (opcode === 8) return sendClose(client, 1000, ''); + if (opcode === 9) { try { client.socket.write(encodeFrame(10, payload)); } catch (_) {} continue; } + if (opcode === 10) { client.lastPongAt = now(); continue; } + if (opcode === 1) { + if (fin) client.queue = client.queue.then(() => handleMessage(client, payload.toString('utf8'))).catch(() => sendClose(client, 1011, 'Message handling failed')); + else { client.fragmentOpcode = 1; client.fragments = [payload]; client.fragmentBytes = payload.length; } + continue; + } + if (opcode === 0 && client.fragmentOpcode === 1) { + client.fragmentBytes += payload.length; if (client.fragmentBytes > MAX_MESSAGE_BYTES) return sendClose(client, 1009, 'Message too large'); client.fragments.push(payload); + if (fin) { const text = Buffer.concat(client.fragments).toString('utf8'); client.fragmentOpcode = 0; client.fragments = []; client.fragmentBytes = 0; client.queue = client.queue.then(() => handleMessage(client, text)).catch(() => sendClose(client, 1011, 'Message handling failed')); } + continue; + } + if (opcode !== 2) return sendClose(client, 1003, 'Unsupported frame'); + } + } + function removeClient(client) { + if (!client || client.removed) return; client.removed = true; clearTimeout(client.authTimer); unregisterViewport(client); clients.delete(client.id); + if (client.authenticated && Number.isFinite(client.x) && Number.isFinite(client.y)) broadcastAll({type:'player-left', presenceId:client.id, playerId:client.playerId, serverTime:now()}, other => pointInViewport(other.viewport, client.x, client.y)); + } + function handleUpgrade(req, socket, head) { + let url; try { url = new URL(req.url, `http://${req.headers.host || 'localhost'}`); } catch (_) { socket.destroy(); return; } + if (url.pathname !== path) { socket.destroy(); return; } + const key = req.headers['sec-websocket-key'], version = req.headers['sec-websocket-version']; + if (req.method !== 'GET' || typeof key !== 'string' || version !== '13') { socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n'); socket.destroy(); return; } + const accept = crypto.createHash('sha1').update(key + WS_GUID).digest('base64'); + socket.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${accept}\r\n\r\n`); + socket.setNoDelay(true); + const client = {id:`p${nextConnection++}-${crypto.randomBytes(4).toString('hex')}`, socket, buffer:Buffer.alloc(0), queue:Promise.resolve(), authenticated:false, closed:false, removed:false, viewport:null, x:NaN, y:NaN, cursorStyle:'default', cursorAt:0, lastCursorMessageAt:0, lastPongAt:now(), fragments:[], fragmentBytes:0, fragmentOpcode:0, currentClaimBoardId:null, lastReactionAt:0, authTimer:0, viewportBucketKeys:[]}; + clients.set(client.id, client); + client.authTimer = setTimeout(() => sendClose(client, 1008, 'Authentication timeout'), 5000); + socket.on('data', chunk => consumeFrames(client, chunk)); socket.on('error', () => removeClient(client)); socket.on('close', () => removeClient(client)); socket.on('end', () => removeClient(client)); + if (head?.length) consumeFrames(client, head); + } + server.on('upgrade', handleUpgrade); + const cleanupTimer = setInterval(() => {pruneClaims();pruneReactions();}, 15_000); cleanupTimer.unref?.(); + const heartbeatTimer = setInterval(() => { + const timestamp = now(); + for (const client of clients.values()) { + if (timestamp - client.lastPongAt > 90_000) { client.socket.destroy(); continue; } + try { client.socket.write(encodeFrame(9, Buffer.from(String(timestamp)))); } catch (_) { client.socket.destroy(); } + } + }, 30_000); heartbeatTimer.unref?.(); + + return { + claimTtlMs, + hasClaim(playerId, boardId) { pruneClaims(); const claim = claims.get(boardId); return claimActive(claim) && claim.ownerPlayerId === playerId; }, + releaseBoardClaim, + broadcastClearEvents(events) { for (const event of events || []) broadcastAll({type:'board-cleared', event, serverTime:now()}); }, + notifyProfileChange(playerId, name) { for (const client of clients.values()) if (client.playerId === playerId) client.name = name; broadcastAll({type:'player-profile', playerId, name, serverTime:now()}); }, + close() { clearInterval(cleanupTimer); clearInterval(heartbeatTimer); server.off('upgrade', handleUpgrade); for (const client of clients.values()) sendClose(client, 1001, 'Server shutdown'); clients.clear(); claims.clear(); reactions.clear(); }, + _debug: {clients, claims, reactions, snapshotFor} + }; +} + +module.exports = {createRealtimeHub}; diff --git a/scripts/fetch-flag-assets.mjs b/scripts/fetch-flag-assets.mjs new file mode 100644 index 0000000..d510985 --- /dev/null +++ b/scripts/fetch-flag-assets.mjs @@ -0,0 +1,32 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..'); +const output=path.join(root,'assets','flags'); +const regions=`AC AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CP CQ CR CU CV CW CX CY CZ DE DG DJ DK DM DO DZ EA EC EE EG EH ER ES ET EU FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU IC ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TA TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM UN US UY UZ VA VC VE VG VI VN VU WF WS XK YE YT ZA ZM ZW`.split(' '); +const subdivisionKeys=[ + '1f3f4-e0067-e0062-e0065-e006e-e0067-e007f', + '1f3f4-e0067-e0062-e0073-e0063-e0074-e007f', + '1f3f4-e0067-e0062-e0077-e006c-e0073-e007f' +]; +const regionKey=code=>[...code].map(letter=>(0x1f1e6+letter.charCodeAt(0)-65).toString(16)).join('-'); +const keys=[...regions.map(regionKey),...subdivisionKeys]; +const base='https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest'; + +await fs.mkdir(output,{recursive:true}); +const failures=[]; +for(let offset=0;offset{ + try{ + const response=await fetch(`${base}/assets/svg/${key}.svg`); + if(!response.ok)throw new Error(`${response.status} ${response.statusText}`); + await fs.writeFile(path.join(output,`${key}.svg`),Buffer.from(await response.arrayBuffer())); + }catch(error){failures.push(`${key}: ${error.message}`)} + })); +} +const licenseResponse=await fetch(`${base}/LICENSE-GRAPHICS`); +if(!licenseResponse.ok)throw new Error(`Twemoji license download failed: ${licenseResponse.status}`); +await fs.writeFile(path.join(output,'LICENSE-TWEMOJI.txt'),await licenseResponse.text(),'utf8'); +if(failures.length)throw new Error(`Flag downloads failed:\n${failures.join('\n')}`); +console.log(`Downloaded ${keys.length} Twemoji flag SVG assets.`); diff --git a/server.js b/server.js new file mode 100644 index 0000000..405c04f --- /dev/null +++ b/server.js @@ -0,0 +1,262 @@ +'use strict'; + +const http = require('http'); +const fs = require('fs'); +const fsp = fs.promises; +const path = require('path'); +const crypto = require('crypto'); +const { URL } = require('url'); +const { createRealtimeHub } = require('./realtime-server'); +const AppLogic = require('./app-logic'); +const PuzzleCore = require('./puzzle-core'); +const STORE_CATALOG = new Map(require('./store-catalog.json').map(item => [item.id, Object.freeze(item)])); + +const ROOT = __dirname; +const DATA_DIR = path.resolve(process.env.BEND_FIELD_DATA_DIR || path.join(ROOT, 'cloud-data')); +const WORLD_FILE = path.join(DATA_DIR, 'shared-world.json'); +const WORLD_BOARDS_DIR = path.join(DATA_DIR, 'shared-world.boards'); +const HOST = process.env.HOST || '0.0.0.0'; +const PORT = Number(process.env.PORT || 8080); +const MAX_BODY_BYTES = 64 * 1024 * 1024; +const MAX_BOARDS_PER_PUSH = 10000; +const MAX_PATHS_PER_BOARD = 512; +const MAX_CELLS_PER_PATH = 5000; +const CLOUD_PAGE_LIMIT = 512; +const CHANGE_HISTORY_LIMIT = 256; +const CLEAR_EVENT_LIMIT = 64; +const MAX_PLAYER_PURCHASES = 10000; +const GENERATION_FAILURE_BONUS = 2500; +const GENERATION_FAILURE_MIN_DELAY_MS = 8000; +const PLAYER_RE = /^[a-f0-9]{16,64}$/i; +const TOKEN_RE = /^[a-f0-9]{32,128}$/i; +const BOARD_RE = /^B(?:0|[1-9][0-9]*)$/; +const MIME = { + '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', + '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.ttf': 'font/ttf', '.json': 'application/json; charset=utf-8', + '.txt': 'text/plain; charset=utf-8', '.md': 'text/markdown; charset=utf-8', +}; +let worldQueue = Promise.resolve(); +const playerQueues = new Map(); +let realtimeHub = null; + +function json(res, status, value) { + const body = JSON.stringify(value); + res.writeHead(status, {'content-type':'application/json; charset=utf-8','content-length':Buffer.byteLength(body),'cache-control':'no-store','x-content-type-options':'nosniff'}); + res.end(body); +} +function serverTime(){return Date.now()} +function tokenHash(token){return crypto.createHash('sha256').update(token).digest('hex')} +function safeEqualHex(a,b){if(typeof a!=='string'||typeof b!=='string'||a.length!==b.length)return false;try{return crypto.timingSafeEqual(Buffer.from(a,'hex'),Buffer.from(b,'hex'))}catch{return false}} +function badRequest(message){throw Object.assign(new Error(message),{status:400})} +function finiteNumber(value,fallback=0){return Number.isFinite(Number(value))?Number(value):fallback} +function cleanPlayerName(value,fallback=''){ + const name=String(value||'').replace(/[\u0000-\u001f\u007f]/g,'').replace(/\s+/g,' ').trim().slice(0,24); + return name||fallback; +} +function defaultPlayerName(playerId){return `旅人-${String(playerId).slice(0,4).toUpperCase()}`} +function cleanId(value,maxLength=64){const text=String(value||'').trim();if(!text||text.length>maxLength||!/^[A-Za-z0-9:_-]+$/.test(text))return'';return text} +function storeItem(itemId){return STORE_CATALOG.get(String(itemId||''))||null} +function normalizePlayerPurchases(raw){ + const purchases=[],seenIds=new Set(),seenStoreItems=new Set(); + for(const source of Array.isArray(raw)?raw:[]){const purchaseId=cleanId(source?.purchaseId,64),boardId=cleanId(source?.boardId,32),item=storeItem(source?.itemId),storeKey=`${boardId}:${item?.id||''}`;if(!purchaseId||!boardId||!item||seenIds.has(purchaseId)||seenStoreItems.has(storeKey))continue;const boughtAt=finiteNumber(source.boughtAt,0);seenIds.add(purchaseId);seenStoreItems.add(storeKey);purchases.push({purchaseId,boardId,itemId:item.id,buyer:cleanPlayerName(source.buyer),boughtAt:boughtAt>0?boughtAt:0,paidCost:Number.isSafeInteger(source.paidCost)&&source.paidCost>0?source.paidCost:item.cost})} + return purchases; +} +function normalizeGenerationBonuses(raw){return[...new Set((Array.isArray(raw)?raw:[]).map(value=>String(value||'')).filter(value=>BOARD_RE.test(value)))].slice(-10000)} +function playerSpentScore(record){return normalizePlayerPurchases(record?.purchases).reduce((sum,purchase)=>sum+Math.max(0,Number(purchase.paidCost)||0),0)} +function playerEarnedScore(record){return Number.isSafeInteger(record?.earnedScore)&&record.earnedScore>=0?record.earnedScore:0} +function publicPlayerState(record){const earnedScore=playerEarnedScore(record),spentScore=playerSpentScore(record);return{revision:Number.isSafeInteger(record?.economyRevision)?record.economyRevision:0,purchases:normalizePlayerPurchases(record?.purchases),earnedScore,spentScore,availableScore:Math.max(0,earnedScore-spentScore),updatedAt:finiteNumber(record?.updatedAt,0)}} +function withPlayerQueue(playerId,task){const previous=playerQueues.get(playerId)||Promise.resolve(),run=previous.then(task,task),tail=run.then(()=>undefined,()=>undefined);playerQueues.set(playerId,tail);return run.finally(()=>{if(playerQueues.get(playerId)===tail)playerQueues.delete(playerId)})} +function playerPath(playerId){if(!PLAYER_RE.test(playerId))badRequest('Invalid player id');return path.join(DATA_DIR,`${playerId.toLowerCase()}.json`)} +function worldBoardVersionPath(boardId,revision){if(!BOARD_RE.test(boardId)||!Number.isSafeInteger(revision)||revision<0)badRequest('Invalid board version');return path.join(WORLD_BOARDS_DIR,`${boardId}.${revision}.json`)} +async function atomicWriteJson(file,value){const tmp=`${file}.${process.pid}.${crypto.randomBytes(6).toString('hex')}.tmp`;await fsp.writeFile(tmp,JSON.stringify(value),{encoding:'utf8',mode:0o600});await fsp.rename(tmp,file)} +async function readPlayer(playerId){ + try{const value=JSON.parse(await fsp.readFile(playerPath(playerId),'utf8'));if(!value||typeof value!=='object')throw new Error('Invalid player record');value.name=cleanPlayerName(value.name,defaultPlayerName(playerId));value.purchases=normalizePlayerPurchases(value.purchases);value.generationBonuses=normalizeGenerationBonuses(value.generationBonuses);value.economyRevision=Number.isSafeInteger(value.economyRevision)?value.economyRevision:0;value.earnedScore=playerEarnedScore(value);return value} + catch(error){if(error.code==='ENOENT')throw Object.assign(new Error('Cloud profile not found'),{status:404});throw error} +} +function emptyWorld(){const now=serverTime();return{revision:0,rowRevision:now*1000,expansionGrants:{},global:{schema:31,appVersion:'47.77',generatorVersion:5,worldGeneration:'v47-field-reset-20260728-interaction-fix',nextId:1,solved:0,earnedScore:0,specialMechanicsSeen:[],updatedAt:now,cloudRevision:0},boardVersions:{},changes:[],clearEvents:[],createdAt:now,updatedAt:now}} +async function readWorld(){ + try{const value=JSON.parse(await fsp.readFile(WORLD_FILE,'utf8'));if(!value||typeof value!=='object')throw new Error('Invalid shared world');value.revision=Number.isSafeInteger(value.revision)?value.revision:0;value.rowRevision=Number.isSafeInteger(value.rowRevision)?value.rowRevision:Math.max(0,serverTime()*1000);value.boardVersions=value.boardVersions&&typeof value.boardVersions==='object'?value.boardVersions:{};value.changes=Array.isArray(value.changes)?value.changes:[];value.clearEvents=Array.isArray(value.clearEvents)?value.clearEvents:[];value.expansionGrants=value.expansionGrants&&typeof value.expansionGrants==='object'?value.expansionGrants:{};value.global=value.global&&typeof value.global==='object'?value.global:{};return value} + catch(error){if(error.code==='ENOENT')return emptyWorld();throw error} +} +async function readWorldBoard(record,id){const revision=record.boardVersions[id];if(!Number.isSafeInteger(revision))return null;try{const value=JSON.parse(await fsp.readFile(worldBoardVersionPath(id,revision),'utf8'));return value&&typeof value==='object'?value:null}catch(error){if(error.code==='ENOENT')throw Object.assign(new Error(`Shared board is missing: ${id}`),{status:500});throw error}} +function authenticate(req){const raw=String(req.headers.authorization||''),match=/^Bearer\s+([^.]*)\.([^.]*)$/i.exec(raw);if(!match||!PLAYER_RE.test(match[1])||!TOKEN_RE.test(match[2]))throw Object.assign(new Error('Unauthorized'),{status:401});return{playerId:match[1].toLowerCase(),token:match[2].toLowerCase()}} +async function authenticatedPlayer(req){const auth=authenticate(req),record=await readPlayer(auth.playerId);if(!safeEqualHex(record.tokenHash,tokenHash(auth.token)))throw Object.assign(new Error('Invalid cloud sync code'),{status:403});return{...auth,record}} +async function readJsonBody(req){const chunks=[];let bytes=0;for await(const chunk of req){bytes+=chunk.length;if(bytes>MAX_BODY_BYTES)throw Object.assign(new Error('Request body is too large'),{status:413});chunks.push(chunk)}if(!chunks.length)return{};try{const parsed=JSON.parse(Buffer.concat(chunks).toString('utf8'));if(!parsed||typeof parsed!=='object'||Array.isArray(parsed))throw new Error();return parsed}catch{throw Object.assign(new Error('Invalid JSON body'),{status:400})}} +function cellKey(cell){return `${cell[0]},${cell[1]}`} +function normalizedRouteKey(cells){const forward=cells.map(cellKey).join('|'),reverse=[...cells].reverse().map(cellKey).join('|');return forwardmeta.chunks.length*25)badRequest(`Invalid puzzle area for ${meta.id}`); + const obstacles=new Set();for(const cell of Array.isArray(puzzle.obstacles)?puzzle.obstacles:[]){if(!Array.isArray(cell)||cell.length!==2||!Number.isSafeInteger(cell[0])||!Number.isSafeInteger(cell[1])||valid.has(cellKey(cell))||obstacles.has(cellKey(cell)))badRequest(`Invalid obstacle for ${meta.id}`);obstacles.add(cellKey(cell))} + if(valid.size+obstacles.size!==meta.chunks.length*25)badRequest(`Incomplete puzzle area for ${meta.id}`); + const gates=puzzle.g;if(!gates.length||gates.length%2!==0)badRequest(`Invalid gates for ${meta.id}`); + for(const gate of gates)if(!Array.isArray(gate)||gate.length!==3||!Number.isSafeInteger(gate[0])||!Number.isSafeInteger(gate[1])||!valid.has(`${gate[0]},${gate[1]}`)||!['N','S','W','E'].includes(gate[2]))badRequest(`Invalid gate for ${meta.id}`); + for(const clue of puzzle.n)if(!Array.isArray(clue)||clue.length!==3||!Number.isSafeInteger(clue[0])||!Number.isSafeInteger(clue[1])||!Number.isSafeInteger(clue[2])||clue[2]<0||clue[2]>512||!valid.has(`${clue[0]},${clue[1]}`))badRequest(`Invalid clue for ${meta.id}`); + const warpMap=new Map();for(const pair of puzzle.specialCells?.warps||[]){if(!Array.isArray(pair?.a)||!Array.isArray(pair?.b)||!valid.has(cellKey(pair.a))||!valid.has(cellKey(pair.b)))badRequest(`Invalid warp for ${meta.id}`);warpMap.set(cellKey(pair.a),cellKey(pair.b));warpMap.set(cellKey(pair.b),cellKey(pair.a))} + const crossingSet=new Set((puzzle.specialCells?.crossings||[]).map(cellKey)),coverage=new Map(); + const validateRoute=(route,label)=>{if(!route||typeof route!=='object'||!Array.isArray(route.cells)||!route.cells.length||!Number.isInteger(route.startGate)||!Number.isInteger(route.endGate)||route.startGate<0||route.endGate<0||route.startGate>=gates.length||route.endGate>=gates.length)badRequest(`Invalid ${label} for ${meta.id}`);if(cellKey(route.cells[0])!==`${gates[route.startGate][0]},${gates[route.startGate][1]}`||cellKey(route.cells[route.cells.length-1])!==`${gates[route.endGate][0]},${gates[route.endGate][1]}`)badRequest(`Gate mismatch in ${label} for ${meta.id}`);const local=new Set();for(let index=0;index(crossingSet.has(key)?2:1))badRequest(`Overlapping solution for ${meta.id}`);coverage.set(key,count)} + if(coverage.size!==valid.size)badRequest(`Incomplete solution for ${meta.id}`); + return{valid,gates,warpMap,crossingSet,solutionKeys:puzzle.solution.map(route=>normalizedRouteKey(route.cells)).sort()}; +} +function validateMeta(meta){ + if(!meta||typeof meta!=='object'||!BOARD_RE.test(meta.id))badRequest('Invalid board metadata');const chunks=Array.isArray(meta.chunks)?meta.chunks:[]; + if(!chunks.length||chunks.length>50)badRequest(`Invalid chunks for ${meta.id}`);const seen=new Set();for(const cell of chunks){if(!Array.isArray(cell)||cell.length!==2||!Number.isSafeInteger(cell[0])||!Number.isSafeInteger(cell[1])||Math.abs(cell[0])>1000000||Math.abs(cell[1])>1000000||seen.has(cellKey(cell)))badRequest(`Invalid chunk coordinate for ${meta.id}`);seen.add(cellKey(cell))} + if(!Number.isSafeInteger(meta.x)||!Number.isSafeInteger(meta.y)||Math.abs(meta.x)>100000000||Math.abs(meta.y)>100000000)badRequest(`Invalid board coordinate for ${meta.id}`); + if(!Number.isSafeInteger(meta.seed)||meta.seed<0||meta.seed>0xffffffff)badRequest(`Invalid seed for ${meta.id}`); + if(!Number.isInteger(meta.level)||meta.level<1||meta.level>10||!Number.isInteger(meta.targetLevel)||meta.targetLevel<1||meta.targetLevel>10)badRequest(`Invalid level for ${meta.id}`); + if(meta.entrySide!=null&&!['N','S','W','E'].includes(meta.entrySide))badRequest(`Invalid entry side for ${meta.id}`); + if(meta.sealedSides!=null&&(!Array.isArray(meta.sealedSides)||meta.sealedSides.some(side=>!['N','S','W','E'].includes(side))))badRequest(`Invalid sealed sides for ${meta.id}`); + const clean=JSON.parse(JSON.stringify(meta));validatePuzzle(clean);return clean; +} +function validateStateRow(row,meta){ + if(!row||typeof row!=='object'||!BOARD_RE.test(row.id)||!row.value||typeof row.value!=='object')badRequest('Invalid board state');if(!meta)badRequest(`State without metadata: ${row.id}`); + const state=JSON.parse(JSON.stringify(row.value)),puzzle=validatePuzzle(meta),paths=Array.isArray(state.paths)?state.paths:[];if(paths.length>MAX_PATHS_PER_BOARD)badRequest(`Too many paths for ${row.id}`); + for(const item of paths){if(!item||typeof item!=='object'||!Array.isArray(item.cells)||item.cells.length>MAX_CELLS_PER_PATH)badRequest(`Invalid path for ${row.id}`);if(!Number.isInteger(item.startGate)||item.startGate<0||item.startGate>=puzzle.gates.length||item.endGate!=null&&(!Number.isInteger(item.endGate)||item.endGate<0||item.endGate>=puzzle.gates.length))badRequest(`Invalid path gates for ${row.id}`);const seen=new Set();for(let index=0;indexroute.endGate==null?'':normalizedRouteKey(route.cells)).sort();if(solvedKeys.length!==puzzle.solutionKeys.length||solvedKeys.some((key,index)=>!key||key!==puzzle.solutionKeys[index]))badRequest(`Solved state does not match puzzle for ${row.id}`)} + return state; +} +function worldMetaFingerprint(meta){const clean=JSON.parse(JSON.stringify(meta));delete clean.rev;delete clean.revAuthor;delete clean.sealedSides;return JSON.stringify(clean)} +function sanitizeWorldGlobal(value,record){ + const source=value&&typeof value==='object'&&!Array.isArray(value)?value:{},prior=record?.global||{},clean={}; + for(const key of['schema','gameplayVersion','worldGeneration','worldEpoch','appVersion','generatorVersion','nextId','solved','lastSolveAt','specialMechanicsSeen','quarantine'])if(Object.prototype.hasOwnProperty.call(source,key))clean[key]=JSON.parse(JSON.stringify(source[key])); + clean.specialMechanicsSeen=Array.isArray(clean.specialMechanicsSeen)?[...new Set(clean.specialMechanicsSeen.filter(type=>['warp','lock','crossing'].includes(type)))]:Array.isArray(prior.specialMechanicsSeen)?prior.specialMechanicsSeen:[]; + clean.updatedAt=serverTime();return clean; +} + +const STORE_CHANCE=1/30,STORE_PRICE_VERSION=1,EXPANSION_GRANT_TTL_MS=10*60*1000,MAX_NEW_BOARDS_PER_GRANT=8; +function authoritativeStoreItemIds(seed){ + const all=[...STORE_CATALOG.values()],cursor=all.filter(item=>item.cursorStyle),other=all.filter(item=>!item.cursorStyle), + cursorPool=PuzzleCore.shuffle([...cursor],PuzzleCore.rngFrom(PuzzleCore.hash32((seed>>>0)^0x5f356495))), + otherPool=PuzzleCore.shuffle([...other],PuzzleCore.rngFrom(PuzzleCore.hash32((seed>>>0)^0x2c9277b5))), + selected=[...cursorPool.slice(0,12),...otherPool.slice(0,1)]; + return PuzzleCore.shuffle(selected,PuzzleCore.rngFrom(PuzzleCore.hash32((seed>>>0)^0x6d2b79f5))).map(item=>item.id); +} +function authoritativeReward(meta,state,worldSeed=0){ + const level=Math.max(1,Math.min(10,Number(meta.level)||1)),sections=Math.max(1,meta.chunks?.length||1), + totalCells=(state.paths||[]).reduce((sum,path)=>sum+(path.cells?.length||0),0), + base=Math.max(100,Math.round((level*level*100+totalCells*level*20)/80)*10), + reward=AppLogic.deterministicBoardReward(base,{worldSeed,meta,state,timeAttackModifier:1,scoreLensCount:0}); + return{award:Math.max(13,Math.min(Number.MAX_SAFE_INTEGER,reward.award)),identity:reward.identity,coefficient:reward.coefficient}; +} +function authoritativeStore(meta,state,playerName,worldSeed=0){ + const roll=(PuzzleCore.hash32((meta.seed>>>0)^0x7f4a7c15)>>>0)/4294967296;if(roll>=STORE_CHANCE||!state.paths?.length)return null; + const pathIndex=state.paths.reduce((best,path,index)=>(path.cells?.length||0)>(state.paths[best]?.cells?.length||0)?index:best,0),path=state.paths[pathIndex],cellIndex=Math.max(0,Math.min(path.cells.length-1,Math.floor(path.cells.length*.55))), + store={owner:playerName,pathIndex,cellIndex,openedAt:serverTime(),priceVersion:STORE_PRICE_VERSION,priceCoefficient:null,bonus:0,bonusVersion:6,itemIds:authoritativeStoreItemIds(meta.seed),purchases:[]}, + [x,y]=storeItemPriceLocation(meta,state,store);store.priceCoefficient=AppLogic.deterministicStorePrice(1,worldSeed,x,y,STORE_PRICE_VERSION).coefficient;return store; +} +function boardTouchesWorld(meta,rows){const occupied=new Set();for(const row of rows)for(const[dx,dy]of row?.meta?.chunks||[])occupied.add(`${row.meta.x+dx},${row.meta.y+dy}`);for(const[dx,dy]of meta.chunks||[]){const x=meta.x+dx,y=meta.y+dy;for(const[ox,oy]of[[1,0],[-1,0],[0,1],[0,-1]])if(occupied.has(`${x+ox},${y+oy}`))return true}return false} + +function publicStateForWorld(incoming,current,player,worldRevision,rowRevision,meta,worldSeed=0){ + const now=serverTime(),wasSolved=current?.solved===true,wantsSolved=incoming.solved===true,base={paths:[],specialProgress:{crossings:[]},solved:false,expanded:false,expansionRetryRound:0,solvedBy:null,solvedById:null,solvedAt:null,scoreAwarded:0,scoreVersion:incoming.scoreVersion||0,rewardIdentity:null,rewardCoefficient:null,store:null,rev:rowRevision,revAuthor:'shared-world'}; + if(wasSolved){ + const stable=JSON.parse(JSON.stringify(current));stable.expanded=stable.expanded===true||incoming.expanded===true;stable.expansionRetryRound=stable.expanded?0:Math.max(stable.expansionRetryRound||0,incoming.expansionRetryRound||0);stable.rev=rowRevision;stable.revAuthor='shared-world';if(stable.store)stable.store.purchases=[];return{state:stable,clearEvent:null}; + } + if(!wantsSolved)return{state:base,clearEvent:null}; + const state=JSON.parse(JSON.stringify(incoming));state.solved=true;state.solvedBy=player.name;state.solvedById=player.playerId;state.solvedAt=now;state.rev=rowRevision;state.revAuthor='shared-world';state.expanded=false;state.expansionRetryRound=0;const reward=authoritativeReward(meta,state,worldSeed);state.scoreAwarded=reward.award;state.scoreVersion=6;state.rewardIdentity=reward.identity;state.rewardCoefficient=reward.coefficient;state.store=authoritativeStore(meta,state,player.name,worldSeed); + return{state,clearEvent:{id:meta.id,playerId:player.playerId,playerName:player.name,x:meta.x,y:meta.y,level:meta.level,scoreAwarded:state.scoreAwarded,solvedAt:now,revision:worldRevision}}; +} +function cloudDeltaSince(record,since){if(!(since>0)||since>=record.revision)return null;const changes=record.changes.filter(change=>Number.isSafeInteger(change?.revision)&&change.revision>since).sort((a,b)=>a.revision-b.revision);if(!changes.length||changes[0].revision!==since+1||changes[changes.length-1].revision!==record.revision)return null;for(let index=1;indexreadWorldBoard(record,id))),metas={},states={};for(let index=0;indexundefined,()=>undefined);return run} +async function authenticateRealtime({playerId,token}){ + if(!PLAYER_RE.test(String(playerId||''))||!TOKEN_RE.test(String(token||'')))throw new Error('Unauthorized'); + const normalizedId=String(playerId).toLowerCase(),record=await readPlayer(normalizedId); + if(!safeEqualHex(record.tokenHash,tokenHash(String(token).toLowerCase())))throw new Error('Unauthorized'); + return{playerId:normalizedId,name:record.name}; +} +async function realtimeBoardInfo(boardId){ + if(!BOARD_RE.test(String(boardId||'')))return null;const record=await readWorld(),row=await readWorldBoard(record,boardId);if(!row?.meta)return null; + let minX=Infinity,minY=Infinity,maxX=-Infinity,maxY=-Infinity;for(const[dx,dy]of row.meta.chunks||[]){const x=row.meta.x+dx,y=row.meta.y+dy;minX=Math.min(minX,x);minY=Math.min(minY,y);maxX=Math.max(maxX,x+1);maxY=Math.max(maxY,y+1)} + if(!Number.isFinite(minX))return null;return{solved:row.state?.solved===true,bounds:{minX,minY,maxX,maxY,x:(minX+maxX)/2,y:(minY+maxY)/2}}; +} +function assertNoOverlaps(rows){const occupied=new Map();for(const row of rows){const meta=row?.meta;if(!meta)continue;for(const[dx,dy]of meta.chunks){const key=`${meta.x+dx},${meta.y+dy}`,prior=occupied.get(key);if(prior&&prior!==meta.id)badRequest(`Board overlap: ${prior} / ${meta.id}`);occupied.set(key,meta.id)}}} + +function storeItemPriceLocation(meta,state,store){ + const path=store&&state?.paths?.[store.pathIndex],cell=path?.cells?.[store.cellIndex]; + if(Array.isArray(cell))return[meta.x+(cell[1]+.5)/5,meta.y+(cell[0]+.5)/5]; + const count=Math.max(1,meta.chunks?.length||1),x=meta.x+.5+(meta.chunks||[]).reduce((sum,chunk)=>sum+chunk[0],0)/count,y=meta.y+.5+(meta.chunks||[]).reduce((sum,chunk)=>sum+chunk[1],0)/count;return[x,y]; +} +async function storePurchaseContext(world,boardId,itemId){ + if(!BOARD_RE.test(String(boardId||'')))badRequest('Invalid store board');const item=STORE_CATALOG.get(itemId);if(!item)badRequest('Invalid store item'); + const row=await readWorldBoard(world,boardId);if(!row?.meta||row.state?.solved!==true||!row.state?.store)badRequest('Store is not available'); + const store=row.state.store,itemIds=Array.isArray(store.itemIds)?store.itemIds:[];if(!itemIds.includes(item.id))badRequest('Item is not sold by this store'); + const starter=await readWorldBoard(world,'B0'),worldSeed=starter?.meta?.seed||0,[x,y]=storeItemPriceLocation(row.meta,row.state,store),computed=AppLogic.deterministicStorePrice(1,worldSeed,x,y,store.priceVersion||1),coefficient=computed.coefficient,adjusted=Math.round(Math.max(0,Number(item.cost)||0)*coefficient),price=item.id.startsWith('cursor-face-')?Math.max(500,Math.min(50000,adjusted)):Math.max(item.cursorStyle?500:3000,adjusted); + return{row,item,price}; +} +async function sharedWorldEarnedScore(world){ + const cached=Number(world?.global?.earnedScore);if(Number.isSafeInteger(cached)&&cached>=0)return cached; + let total=0;for(const id of Object.keys(world?.boardVersions||{})){const row=await readWorldBoard(world,id),award=Number(row?.state?.scoreAwarded)||0;if(row?.state?.solved===true&&Number.isSafeInteger(award)&&award>0)total+=award} + return Math.max(0,total); +} +async function assertPlayerCanAfford(world,record,price){ + const earned=playerEarnedScore(record),spent=playerSpentScore(record),available=Math.max(0,earned-spent);if(availablepurchase.boardId===boardId&&purchase.itemId===itemId)||null} +function createPlayerPurchase(record,boardId,item,price,timestamp=serverTime()){ + const purchase={purchaseId:crypto.randomBytes(16).toString('hex'),boardId,itemId:item.id,buyer:record.name,boughtAt:timestamp,paidCost:price}; + record.purchases=normalizePlayerPurchases([...(record.purchases||[]),purchase]); + record.economyRevision=(record.economyRevision||0)+1; + record.updatedAt=timestamp; + return record.purchases.find(row=>row.purchaseId===purchase.purchaseId)||purchase; +} +async function writePlayerRecord(record){record.purchases=normalizePlayerPurchases(record.purchases);record.generationBonuses=normalizeGenerationBonuses(record.generationBonuses);await atomicWriteJson(playerPath(record.playerId),record)} + +async function handleApi(req,res,url){ + if(req.method==='GET'&&url.pathname==='/api/cloud/status')return json(res,200,{available:true,sharedWorld:true,realtime:true,reactions:true,playerEconomy:true,sharedItems:false,claimTtlMs:realtimeHub?.claimTtlMs||300000,serverTime:serverTime()}); + if(req.method==='POST'&&url.pathname==='/api/cloud/session'){ + const body=await readJsonBody(req),playerId=crypto.randomBytes(12).toString('hex'),token=crypto.randomBytes(32).toString('hex'),now=serverTime(),name=cleanPlayerName(body.name,defaultPlayerName(playerId)); + const record={playerId,name,tokenHash:tokenHash(token),purchases:[],generationBonuses:[],earnedScore:0,economyRevision:0,createdAt:now,updatedAt:now};await atomicWriteJson(playerPath(playerId),record);return json(res,201,{playerId,token,name,revision:0,serverTime:now}); + } + if(req.method==='POST'&&url.pathname==='/api/cloud/profile'){ + const auth=await authenticatedPlayer(req),body=await readJsonBody(req),name=cleanPlayerName(body.name);if(!name)badRequest('Player name is required');auth.record.name=name;auth.record.updatedAt=serverTime();await atomicWriteJson(playerPath(auth.playerId),auth.record);realtimeHub?.notifyProfileChange(auth.playerId,name);return json(res,200,{playerId:auth.playerId,name,serverTime:auth.record.updatedAt}); + } + if(req.method==='GET'&&url.pathname==='/api/player/state'){ + const player=await authenticatedPlayer(req);return json(res,200,{player:publicPlayerState(player.record),serverTime:serverTime()}); + } + if(req.method==='POST'&&url.pathname==='/api/player/generation-bonus'){ + const auth=await authenticatedPlayer(req),body=await readJsonBody(req),boardId=String(body.boardId||'');if(!BOARD_RE.test(boardId))badRequest('Invalid board id'); + return withPlayerQueue(auth.playerId,()=>withWorldQueue(async()=>{const record=await readPlayer(auth.playerId),world=await readWorld(),row=await readWorldBoard(world,boardId),now=serverTime();if(!row?.state?.solved||row.state.solvedById!==auth.playerId)throw Object.assign(new Error('Only the solver can receive this bonus'),{status:403});if(row.state.expanded===true)throw Object.assign(new Error('Expansion already succeeded'),{status:409});if(now-(Number(row.state.solvedAt)||0)0))throw Object.assign(new Error('No failed expansion is eligible'),{status:409});record.generationBonuses=normalizeGenerationBonuses(record.generationBonuses);if(!record.generationBonuses.includes(boardId)){record.generationBonuses.push(boardId);record.earnedScore=Math.min(Number.MAX_SAFE_INTEGER,playerEarnedScore(record)+GENERATION_FAILURE_BONUS);record.economyRevision=(record.economyRevision||0)+1;record.updatedAt=now;await writePlayerRecord(record)}return json(res,200,{bonus:GENERATION_FAILURE_BONUS,player:publicPlayerState(record),serverTime:now})})); + } + if(req.method==='POST'&&url.pathname==='/api/player/purchase'){ + const auth=await authenticatedPlayer(req),body=await readJsonBody(req),boardId=String(body.boardId||''),itemId=String(body.itemId||''); + return withPlayerQueue(auth.playerId,async()=>{const record=await readPlayer(auth.playerId),world=await readWorld(),context=await storePurchaseContext(world,boardId,itemId),existing=purchaseForStoreItem(record,boardId,itemId);if(existing)return json(res,200,{purchase:existing,player:publicPlayerState(record),serverTime:serverTime()});await assertPlayerCanAfford(world,record,context.price);const purchase=createPlayerPurchase(record,boardId,context.item,context.price);await writePlayerRecord(record);return json(res,201,{purchase,player:publicPlayerState(record),serverTime:record.updatedAt})}); + } + if(req.method==='GET'&&url.pathname==='/api/cloud/pull'){ + const player=await authenticatedPlayer(req),record=await readWorld(),since=Math.max(0,Math.floor(finiteNumber(url.searchParams.get('since'),0))),eventsSince=Math.max(0,Math.floor(finiteNumber(url.searchParams.get('eventsSince'),0))),changed=since!==record.revision, + clearEvents=record.clearEvents.filter(event=>(event.revision||0)>eventsSince); + if(!changed)return json(res,200,{changed:false,revision:record.revision,latestEventRevision:record.clearEvents.at(-1)?.revision||0,clearEvents,player:{id:player.playerId,name:player.record.name},serverTime:serverTime()}); + const cursor=Math.max(0,Math.floor(finiteNumber(url.searchParams.get('cursor'),0))),at=Math.max(0,Math.floor(finiteNumber(url.searchParams.get('at'),record.revision)));if(cursor&&at!==record.revision)return json(res,409,{error:'World changed during paged pull',revision:record.revision,serverTime:serverTime()}); + const delta=cloudDeltaSince(record,since),fullSnapshot=!delta,metaIds=delta?.metaIds||new Set(Object.keys(record.boardVersions)),stateIds=delta?.stateIds||new Set(Object.keys(record.boardVersions)),ids=[...new Set([...metaIds,...stateIds])].filter(id=>Number.isSafeInteger(record.boardVersions[id])).sort((a,b)=>Number(a.slice(1))-Number(b.slice(1))),pageIds=ids.slice(cursor,cursor+CLOUD_PAGE_LIMIT),nextCursor=cursor+pageIds.lengthwithWorldQueue(async()=>{ + const playerRecord=await readPlayer(player.playerId),record=await readWorld(),baseRevision=Math.max(0,Math.floor(finiteNumber(body.baseRevision,0)));if(baseRevision!==record.revision)return json(res,409,{error:'Revision conflict',revision:record.revision,serverTime:serverTime()}); + const metas=Array.isArray(body.metas)?body.metas:[],states=Array.isArray(body.states)?body.states:[],deleted=[];if(metas.length+states.length>MAX_BOARDS_PER_PUSH*2)throw Object.assign(new Error('Too many board changes'),{status:413}); + const nextRevision=record.revision+1,rowRevision=Math.max((Number(record.rowRevision)||0)+1,serverTime()*1000),changedBoards=new Map(),previousVersions=new Map(),loadChangedBoard=async id=>{if(changedBoards.has(id))return changedBoards.get(id);const current=await readWorldBoard(record,id)||{meta:null,state:null};changedBoards.set(id,current);previousVersions.set(id,record.boardVersions[id]);return current}; + for(const rawMeta of metas){const clean=validateMeta(rawMeta),row=await loadChangedBoard(clean.id);if(row.meta&&worldMetaFingerprint(row.meta)!==worldMetaFingerprint(clean))badRequest(`Existing board is immutable: ${clean.id}`);clean.rev=rowRevision;clean.revAuthor='shared-world';row.meta=clean} + const existingIds=new Set(Object.keys(record.boardVersions)),newMetaIds=[...changedBoards.keys()].filter(id=>!existingIds.has(id)); + if(record.revision>0&&newMetaIds.length){const grant=record.expansionGrants?.[player.playerId];if(!grant||grant.expiresAtMath.min(MAX_NEW_BOARDS_PER_GRANT,grant.maxBoards||0))badRequest('Too many generated boards');const expectedStart=Math.max(1,Number(record.global?.nextId)||1),numbers=newMetaIds.map(id=>Number(id.slice(1))).sort((a,b)=>a-b);for(let i=0;iNumber(a.slice(1))-Number(b.slice(1)))){const row=changedBoards.get(id);if(!boardTouchesWorld(row.meta,[...existingRows,...accepted]))badRequest(`Generated board is not adjacent: ${id}`);accepted.push(row)}grant.maxBoards-=newMetaIds.length;if(grant.maxBoards<=0)delete record.expansionGrants[player.playerId]} + const clearEvents=[]; + for(const rawRow of states){if(!BOARD_RE.test(rawRow?.id))badRequest('Invalid board state');const row=await loadChangedBoard(rawRow.id);if(!row.meta)badRequest(`Board metadata is missing: ${rawRow.id}`);const incoming=validateStateRow(rawRow,row.meta),firstSolve=row.state?.solved!==true&&incoming.solved===true;if(firstSolve&&realtimeHub&&!realtimeHub.hasClaim(player.playerId,rawRow.id))throw Object.assign(new Error('Board claim is required'),{status:423});const starter=await readWorldBoard(record,'B0'),worldSeed=starter?.meta?.seed||0;const published=publicStateForWorld(incoming,row.state,playerRecord,nextRevision,rowRevision,row.meta,worldSeed);row.state=published.state;if(published.clearEvent){clearEvents.push(published.clearEvent);playerRecord.earnedScore=Math.min(Number.MAX_SAFE_INTEGER,playerEarnedScore(playerRecord)+published.clearEvent.scoreAwarded);playerRecord.economyRevision=(playerRecord.economyRevision||0)+1;playerRecord.updatedAt=published.clearEvent.solvedAt;record.expansionGrants[player.playerId]={boardId:rawRow.id,expiresAt:published.clearEvent.solvedAt+EXPANSION_GRANT_TTL_MS,maxBoards:MAX_NEW_BOARDS_PER_GRANT}}} + for(const [id,row] of changedBoards){if(!row.meta)badRequest(`Board metadata is missing: ${id}`);if(!row.state)row.state=publicStateForWorld({},null,playerRecord,nextRevision,rowRevision,row.meta).state} + const allIds=new Set([...Object.keys(record.boardVersions),...changedBoards.keys()]),allRows=[];for(const id of allIds){const row=changedBoards.get(id)||await readWorldBoard(record,id);if(row)allRows.push(row)}assertNoOverlaps(allRows); + await fsp.mkdir(WORLD_BOARDS_DIR,{recursive:true,mode:0o700});for(const[id,row]of changedBoards){await atomicWriteJson(worldBoardVersionPath(id,nextRevision),row);record.boardVersions[id]=nextRevision} + record.global={...(record.global||{}),...sanitizeWorldGlobal(body.global,record)};const boardNumbers=Object.keys(record.boardVersions).map(id=>Number(id.slice(1))).filter(Number.isSafeInteger);record.global.nextId=Math.max(Number(record.global.nextId)||1,(boardNumbers.length?Math.max(...boardNumbers)+1:1));let solved=0,earnedScore=0;for(const row of allRows)if(row.state?.solved){solved++;const award=Number(row.state.scoreAwarded)||0;if(Number.isSafeInteger(award)&&award>0)earnedScore+=award}record.global.solved=solved;record.global.earnedScore=Math.max(0,earnedScore);record.revision=nextRevision;record.rowRevision=rowRevision;record.global.cloudRevision=nextRevision;record.updatedAt=serverTime();record.global.updatedAt=record.updatedAt; + record.changes.push({revision:nextRevision,metaIds:metas.map(meta=>meta.id),stateIds:states.map(row=>row.id),deleted});if(record.changes.length>CHANGE_HISTORY_LIMIT)record.changes.splice(0,record.changes.length-CHANGE_HISTORY_LIMIT); + record.clearEvents.push(...clearEvents);if(record.clearEvents.length>CLEAR_EVENT_LIMIT)record.clearEvents.splice(0,record.clearEvents.length-CLEAR_EVENT_LIMIT); + if(clearEvents.length)await writePlayerRecord(playerRecord);await atomicWriteJson(WORLD_FILE,record);await Promise.all([...previousVersions].map(([id,revision])=>Number.isSafeInteger(revision)&&revision!==nextRevision?fsp.unlink(worldBoardVersionPath(id,revision)).catch(error=>{if(error.code!=='ENOENT')console.warn(error)}):null)); + for(const event of clearEvents)realtimeHub?.releaseBoardClaim(event.id,'cleared');realtimeHub?.broadcastClearEvents(clearEvents); + return json(res,200,{revision:nextRevision,clearEvents,latestEventRevision:record.clearEvents.at(-1)?.revision||0,player:{id:player.playerId,name:player.record.name},serverTime:record.updatedAt}); + })); + } + return json(res,404,{error:'API endpoint not found',serverTime:serverTime()}); +} +async function serveStatic(req,res,url){ + if(!['GET','HEAD'].includes(req.method))return json(res,405,{error:'Method not allowed'});let pathname;try{pathname=decodeURIComponent(url.pathname)}catch{return json(res,400,{error:'Invalid path'})}if(pathname==='/')pathname='/index.html';const file=path.resolve(ROOT,`.${pathname}`);if(!file.startsWith(`${ROOT}${path.sep}`)||file.startsWith(`${DATA_DIR}${path.sep}`)||path.basename(file).startsWith('.'))return json(res,403,{error:'Forbidden'});let stat;try{stat=await fsp.stat(file)}catch(error){if(error.code==='ENOENT')return json(res,404,{error:'Not found'});throw error}if(stat.isDirectory())return json(res,403,{error:'Directory listing is disabled'});const ext=path.extname(file).toLowerCase();let body=null;if(pathname==='/index.html')body=Buffer.from((await fsp.readFile(file,'utf8')).replace('name="bend-field-cloud-api" content="off"','name="bend-field-cloud-api" content="on"'));res.writeHead(200,{'content-type':MIME[ext]||'application/octet-stream','content-length':body?body.length:stat.size,'cache-control':ext==='.html'||ext==='.js'||ext==='.css'?'no-store':'public, max-age=86400','x-content-type-options':'nosniff','cross-origin-resource-policy':'same-origin','content-security-policy':"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; worker-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'"});if(req.method==='HEAD')return res.end();if(body)return res.end(body);const stream=fs.createReadStream(file);stream.on('error',error=>{console.error(error);if(!res.headersSent)json(res,error.code==='ENOENT'?404:500,{error:error.code==='ENOENT'?'Not found':'Internal server error'});else res.destroy(error)});stream.pipe(res); +} +async function main(){await fsp.mkdir(DATA_DIR,{recursive:true,mode:0o700});const server=http.createServer(async(req,res)=>{try{const url=new URL(req.url,`http://${req.headers.host||'localhost'}`);if(url.pathname.startsWith('/api/'))await handleApi(req,res,url);else await serveStatic(req,res,url)}catch(error){console.error(error);if(!res.headersSent)json(res,error.status||500,{error:error.status?error.message:'Internal server error',serverTime:serverTime()});else res.destroy()}});realtimeHub=createRealtimeHub({server,authenticate:authenticateRealtime,getBoardInfo:realtimeBoardInfo});server.listen(PORT,HOST,()=>{console.log(`BEND FIELD v47.77 shared world: http://${HOST}:${PORT}`);console.log(`Shared world data: ${DATA_DIR}`)})} +main().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/store-catalog.json b/store-catalog.json new file mode 100644 index 0000000..867e46c --- /dev/null +++ b/store-catalog.json @@ -0,0 +1,2186 @@ +[ + { + "id": "score-lens", + "cost": 200000, + "cursorStyle": null, + "scoreLens": true + }, + { + "id": "cursor-face-1f600", + "cost": 19000, + "cursorStyle": "emoji-1f600", + "scoreLens": false + }, + { + "id": "cursor-face-1f603", + "cost": 49500, + "cursorStyle": "emoji-1f603", + "scoreLens": false + }, + { + "id": "cursor-face-1f604", + "cost": 30000, + "cursorStyle": "emoji-1f604", + "scoreLens": false + }, + { + "id": "cursor-face-1f601", + "cost": 10500, + "cursorStyle": "emoji-1f601", + "scoreLens": false + }, + { + "id": "cursor-face-1f606", + "cost": 41000, + "cursorStyle": "emoji-1f606", + "scoreLens": false + }, + { + "id": "cursor-face-1f605", + "cost": 21500, + "cursorStyle": "emoji-1f605", + "scoreLens": false + }, + { + "id": "cursor-face-1f923", + "cost": 2000, + "cursorStyle": "emoji-1f923", + "scoreLens": false + }, + { + "id": "cursor-face-1f602", + "cost": 32500, + "cursorStyle": "emoji-1f602", + "scoreLens": false + }, + { + "id": "cursor-face-1f642", + "cost": 13000, + "cursorStyle": "emoji-1f642", + "scoreLens": false + }, + { + "id": "cursor-face-1f643", + "cost": 43500, + "cursorStyle": "emoji-1f643", + "scoreLens": false + }, + { + "id": "cursor-face-1fae0", + "cost": 24000, + "cursorStyle": "emoji-1fae0", + "scoreLens": false + }, + { + "id": "cursor-face-1f609", + "cost": 4500, + "cursorStyle": "emoji-1f609", + "scoreLens": false + }, + { + "id": "cursor-face-1f60a", + "cost": 35000, + "cursorStyle": "emoji-1f60a", + "scoreLens": false + }, + { + "id": "cursor-face-1f607", + "cost": 15500, + "cursorStyle": "emoji-1f607", + "scoreLens": false + }, + { + "id": "cursor-face-1f970", + "cost": 46000, + "cursorStyle": "emoji-1f970", + "scoreLens": false + }, + { + "id": "cursor-face-1f60d", + "cost": 26500, + "cursorStyle": "emoji-1f60d", + "scoreLens": false + }, + { + "id": "cursor-face-1f929", + "cost": 7000, + "cursorStyle": "emoji-1f929", + "scoreLens": false + }, + { + "id": "cursor-face-1f618", + "cost": 37500, + "cursorStyle": "emoji-1f618", + "scoreLens": false + }, + { + "id": "cursor-face-1f617", + "cost": 18000, + "cursorStyle": "emoji-1f617", + "scoreLens": false + }, + { + "id": "cursor-face-263a-fe0f", + "cost": 48500, + "cursorStyle": "emoji-263a-fe0f", + "scoreLens": false + }, + { + "id": "cursor-face-1f61a", + "cost": 29000, + "cursorStyle": "emoji-1f61a", + "scoreLens": false + }, + { + "id": "cursor-face-1f619", + "cost": 9500, + "cursorStyle": "emoji-1f619", + "scoreLens": false + }, + { + "id": "cursor-face-1f972", + "cost": 40000, + "cursorStyle": "emoji-1f972", + "scoreLens": false + }, + { + "id": "cursor-face-1f60b", + "cost": 20500, + "cursorStyle": "emoji-1f60b", + "scoreLens": false + }, + { + "id": "cursor-face-1f61b", + "cost": 1000, + "cursorStyle": "emoji-1f61b", + "scoreLens": false + }, + { + "id": "cursor-face-1f61c", + "cost": 31500, + "cursorStyle": "emoji-1f61c", + "scoreLens": false + }, + { + "id": "cursor-face-1f92a", + "cost": 12000, + "cursorStyle": "emoji-1f92a", + "scoreLens": false + }, + { + "id": "cursor-face-1f61d", + "cost": 42500, + "cursorStyle": "emoji-1f61d", + "scoreLens": false + }, + { + "id": "cursor-face-1f911", + "cost": 23000, + "cursorStyle": "emoji-1f911", + "scoreLens": false + }, + { + "id": "cursor-face-1f917", + "cost": 3500, + "cursorStyle": "emoji-1f917", + "scoreLens": false + }, + { + "id": "cursor-face-1f92d", + "cost": 34000, + "cursorStyle": "emoji-1f92d", + "scoreLens": false + }, + { + "id": "cursor-face-1fae2", + "cost": 14500, + "cursorStyle": "emoji-1fae2", + "scoreLens": false + }, + { + "id": "cursor-face-1fae3", + "cost": 45000, + "cursorStyle": "emoji-1fae3", + "scoreLens": false + }, + { + "id": "cursor-face-1f92b", + "cost": 25500, + "cursorStyle": "emoji-1f92b", + "scoreLens": false + }, + { + "id": "cursor-face-1f914", + "cost": 6000, + "cursorStyle": "emoji-1f914", + "scoreLens": false + }, + { + "id": "cursor-face-1fae1", + "cost": 36500, + "cursorStyle": "emoji-1fae1", + "scoreLens": false + }, + { + "id": "cursor-face-1f910", + "cost": 17000, + "cursorStyle": "emoji-1f910", + "scoreLens": false + }, + { + "id": "cursor-face-1f928", + "cost": 47500, + "cursorStyle": "emoji-1f928", + "scoreLens": false + }, + { + "id": "cursor-face-1f610", + "cost": 28000, + "cursorStyle": "emoji-1f610", + "scoreLens": false + }, + { + "id": "cursor-face-1f611", + "cost": 8500, + "cursorStyle": "emoji-1f611", + "scoreLens": false + }, + { + "id": "cursor-face-1f636", + "cost": 39000, + "cursorStyle": "emoji-1f636", + "scoreLens": false + }, + { + "id": "cursor-face-1fae5", + "cost": 19500, + "cursorStyle": "emoji-1fae5", + "scoreLens": false + }, + { + "id": "cursor-face-1f636-200d-1f32b-fe0f", + "cost": 50000, + "cursorStyle": "emoji-1f636-200d-1f32b-fe0f", + "scoreLens": false + }, + { + "id": "cursor-face-1f60f", + "cost": 30500, + "cursorStyle": "emoji-1f60f", + "scoreLens": false + }, + { + "id": "cursor-face-1f612", + "cost": 11000, + "cursorStyle": "emoji-1f612", + "scoreLens": false + }, + { + "id": "cursor-face-1f644", + "cost": 41500, + "cursorStyle": "emoji-1f644", + "scoreLens": false + }, + { + "id": "cursor-face-1f62c", + "cost": 22000, + "cursorStyle": "emoji-1f62c", + "scoreLens": false + }, + { + "id": "cursor-face-1f62e-200d-1f4a8", + "cost": 2500, + "cursorStyle": "emoji-1f62e-200d-1f4a8", + "scoreLens": false + }, + { + "id": "cursor-face-1f925", + "cost": 33000, + "cursorStyle": "emoji-1f925", + "scoreLens": false + }, + { + "id": "cursor-face-1fae8", + "cost": 13500, + "cursorStyle": "emoji-1fae8", + "scoreLens": false + }, + { + "id": "cursor-face-1f642-200d-2194-fe0f", + "cost": 44000, + "cursorStyle": "emoji-1f642-200d-2194-fe0f", + "scoreLens": false + }, + { + "id": "cursor-face-1f642-200d-2195-fe0f", + "cost": 24500, + "cursorStyle": "emoji-1f642-200d-2195-fe0f", + "scoreLens": false + }, + { + "id": "cursor-face-1f60c", + "cost": 5000, + "cursorStyle": "emoji-1f60c", + "scoreLens": false + }, + { + "id": "cursor-face-1f614", + "cost": 35500, + "cursorStyle": "emoji-1f614", + "scoreLens": false + }, + { + "id": "cursor-face-1f62a", + "cost": 16000, + "cursorStyle": "emoji-1f62a", + "scoreLens": false + }, + { + "id": "cursor-face-1f924", + "cost": 46500, + "cursorStyle": "emoji-1f924", + "scoreLens": false + }, + { + "id": "cursor-face-1f634", + "cost": 27000, + "cursorStyle": "emoji-1f634", + "scoreLens": false + }, + { + "id": "cursor-face-1fae9", + "cost": 7500, + "cursorStyle": "emoji-1fae9", + "scoreLens": false + }, + { + "id": "cursor-face-1f637", + "cost": 38000, + "cursorStyle": "emoji-1f637", + "scoreLens": false + }, + { + "id": "cursor-face-1f912", + "cost": 18500, + "cursorStyle": "emoji-1f912", + "scoreLens": false + }, + { + "id": "cursor-face-1f915", + "cost": 49000, + "cursorStyle": "emoji-1f915", + "scoreLens": false + }, + { + "id": "cursor-face-1f927", + "cost": 29500, + "cursorStyle": "emoji-1f927", + "scoreLens": false + }, + { + "id": "cursor-face-1f974", + "cost": 10000, + "cursorStyle": "emoji-1f974", + "scoreLens": false + }, + { + "id": "cursor-face-1f635", + "cost": 40500, + "cursorStyle": "emoji-1f635", + "scoreLens": false + }, + { + "id": "cursor-face-1f635-200d-1f4ab", + "cost": 21000, + "cursorStyle": "emoji-1f635-200d-1f4ab", + "scoreLens": false + }, + { + "id": "cursor-face-1f92f", + "cost": 1500, + "cursorStyle": "emoji-1f92f", + "scoreLens": false + }, + { + "id": "cursor-face-1f920", + "cost": 32000, + "cursorStyle": "emoji-1f920", + "scoreLens": false + }, + { + "id": "cursor-face-1f973", + "cost": 12500, + "cursorStyle": "emoji-1f973", + "scoreLens": false + }, + { + "id": "cursor-face-1f978", + "cost": 43000, + "cursorStyle": "emoji-1f978", + "scoreLens": false + }, + { + "id": "cursor-face-1f60e", + "cost": 23500, + "cursorStyle": "emoji-1f60e", + "scoreLens": false + }, + { + "id": "cursor-face-1f913", + "cost": 4000, + "cursorStyle": "emoji-1f913", + "scoreLens": false + }, + { + "id": "cursor-face-1f9d0", + "cost": 34500, + "cursorStyle": "emoji-1f9d0", + "scoreLens": false + }, + { + "id": "cursor-face-1f615", + "cost": 15000, + "cursorStyle": "emoji-1f615", + "scoreLens": false + }, + { + "id": "cursor-face-1fae4", + "cost": 45500, + "cursorStyle": "emoji-1fae4", + "scoreLens": false + }, + { + "id": "cursor-face-1f61f", + "cost": 26000, + "cursorStyle": "emoji-1f61f", + "scoreLens": false + }, + { + "id": "cursor-face-1f641", + "cost": 6500, + "cursorStyle": "emoji-1f641", + "scoreLens": false + }, + { + "id": "cursor-face-2639-fe0f", + "cost": 37000, + "cursorStyle": "emoji-2639-fe0f", + "scoreLens": false + }, + { + "id": "cursor-face-1f62e", + "cost": 17500, + "cursorStyle": "emoji-1f62e", + "scoreLens": false + }, + { + "id": "cursor-face-1f62f", + "cost": 48000, + "cursorStyle": "emoji-1f62f", + "scoreLens": false + }, + { + "id": "cursor-face-1f632", + "cost": 28500, + "cursorStyle": "emoji-1f632", + "scoreLens": false + }, + { + "id": "cursor-face-1f633", + "cost": 9000, + "cursorStyle": "emoji-1f633", + "scoreLens": false + }, + { + "id": "cursor-face-1faea", + "cost": 39500, + "cursorStyle": "emoji-1faea", + "scoreLens": false + }, + { + "id": "cursor-face-1f97a", + "cost": 20000, + "cursorStyle": "emoji-1f97a", + "scoreLens": false + }, + { + "id": "cursor-face-1f979", + "cost": 500, + "cursorStyle": "emoji-1f979", + "scoreLens": false + }, + { + "id": "cursor-face-1f626", + "cost": 31000, + "cursorStyle": "emoji-1f626", + "scoreLens": false + }, + { + "id": "cursor-face-1f627", + "cost": 11500, + "cursorStyle": "emoji-1f627", + "scoreLens": false + }, + { + "id": "cursor-face-1f628", + "cost": 42000, + "cursorStyle": "emoji-1f628", + "scoreLens": false + }, + { + "id": "cursor-face-1f630", + "cost": 22500, + "cursorStyle": "emoji-1f630", + "scoreLens": false + }, + { + "id": "cursor-face-1f625", + "cost": 3000, + "cursorStyle": "emoji-1f625", + "scoreLens": false + }, + { + "id": "cursor-face-1f622", + "cost": 33500, + "cursorStyle": "emoji-1f622", + "scoreLens": false + }, + { + "id": "cursor-face-1f62d", + "cost": 14000, + "cursorStyle": "emoji-1f62d", + "scoreLens": false + }, + { + "id": "cursor-face-1f631", + "cost": 44500, + "cursorStyle": "emoji-1f631", + "scoreLens": false + }, + { + "id": "cursor-face-1f616", + "cost": 25000, + "cursorStyle": "emoji-1f616", + "scoreLens": false + }, + { + "id": "cursor-face-1f623", + "cost": 5500, + "cursorStyle": "emoji-1f623", + "scoreLens": false + }, + { + "id": "cursor-face-1f61e", + "cost": 36000, + "cursorStyle": "emoji-1f61e", + "scoreLens": false + }, + { + "id": "cursor-face-1f613", + "cost": 16500, + "cursorStyle": "emoji-1f613", + "scoreLens": false + }, + { + "id": "cursor-face-1f629", + "cost": 47000, + "cursorStyle": "emoji-1f629", + "scoreLens": false + }, + { + "id": "cursor-face-1f62b", + "cost": 27500, + "cursorStyle": "emoji-1f62b", + "scoreLens": false + }, + { + "id": "cursor-face-1f971", + "cost": 8000, + "cursorStyle": "emoji-1f971", + "scoreLens": false + }, + { + "id": "cursor-face-1f624", + "cost": 38500, + "cursorStyle": "emoji-1f624", + "scoreLens": false + }, + { + "id": "cursor-face-1f620", + "cost": 19000, + "cursorStyle": "emoji-1f620", + "scoreLens": false + }, + { + "id": "cursor-flag-ac", + "cost": 10000, + "cursorStyle": "flag-ac", + "scoreLens": false + }, + { + "id": "cursor-flag-ad", + "cost": 10000, + "cursorStyle": "flag-ad", + "scoreLens": false + }, + { + "id": "cursor-flag-ae", + "cost": 10000, + "cursorStyle": "flag-ae", + "scoreLens": false + }, + { + "id": "cursor-flag-af", + "cost": 10000, + "cursorStyle": "flag-af", + "scoreLens": false + }, + { + "id": "cursor-flag-ag", + "cost": 10000, + "cursorStyle": "flag-ag", + "scoreLens": false + }, + { + "id": "cursor-flag-ai", + "cost": 10000, + "cursorStyle": "flag-ai", + "scoreLens": false + }, + { + "id": "cursor-flag-al", + "cost": 10000, + "cursorStyle": "flag-al", + "scoreLens": false + }, + { + "id": "cursor-flag-am", + "cost": 10000, + "cursorStyle": "flag-am", + "scoreLens": false + }, + { + "id": "cursor-flag-ao", + "cost": 10000, + "cursorStyle": "flag-ao", + "scoreLens": false + }, + { + "id": "cursor-flag-aq", + "cost": 10000, + "cursorStyle": "flag-aq", + "scoreLens": false + }, + { + "id": "cursor-flag-ar", + "cost": 10000, + "cursorStyle": "flag-ar", + "scoreLens": false + }, + { + "id": "cursor-flag-as", + "cost": 10000, + "cursorStyle": "flag-as", + "scoreLens": false + }, + { + "id": "cursor-flag-at", + "cost": 20000, + "cursorStyle": "flag-at", + "scoreLens": false + }, + { + "id": "cursor-flag-au", + "cost": 20000, + "cursorStyle": "flag-au", + "scoreLens": false + }, + { + "id": "cursor-flag-aw", + "cost": 10000, + "cursorStyle": "flag-aw", + "scoreLens": false + }, + { + "id": "cursor-flag-ax", + "cost": 10000, + "cursorStyle": "flag-ax", + "scoreLens": false + }, + { + "id": "cursor-flag-az", + "cost": 10000, + "cursorStyle": "flag-az", + "scoreLens": false + }, + { + "id": "cursor-flag-ba", + "cost": 10000, + "cursorStyle": "flag-ba", + "scoreLens": false + }, + { + "id": "cursor-flag-bb", + "cost": 10000, + "cursorStyle": "flag-bb", + "scoreLens": false + }, + { + "id": "cursor-flag-bd", + "cost": 10000, + "cursorStyle": "flag-bd", + "scoreLens": false + }, + { + "id": "cursor-flag-be", + "cost": 20000, + "cursorStyle": "flag-be", + "scoreLens": false + }, + { + "id": "cursor-flag-bf", + "cost": 10000, + "cursorStyle": "flag-bf", + "scoreLens": false + }, + { + "id": "cursor-flag-bg", + "cost": 10000, + "cursorStyle": "flag-bg", + "scoreLens": false + }, + { + "id": "cursor-flag-bh", + "cost": 10000, + "cursorStyle": "flag-bh", + "scoreLens": false + }, + { + "id": "cursor-flag-bi", + "cost": 10000, + "cursorStyle": "flag-bi", + "scoreLens": false + }, + { + "id": "cursor-flag-bj", + "cost": 10000, + "cursorStyle": "flag-bj", + "scoreLens": false + }, + { + "id": "cursor-flag-bl", + "cost": 10000, + "cursorStyle": "flag-bl", + "scoreLens": false + }, + { + "id": "cursor-flag-bm", + "cost": 10000, + "cursorStyle": "flag-bm", + "scoreLens": false + }, + { + "id": "cursor-flag-bn", + "cost": 10000, + "cursorStyle": "flag-bn", + "scoreLens": false + }, + { + "id": "cursor-flag-bo", + "cost": 10000, + "cursorStyle": "flag-bo", + "scoreLens": false + }, + { + "id": "cursor-flag-bq", + "cost": 10000, + "cursorStyle": "flag-bq", + "scoreLens": false + }, + { + "id": "cursor-flag-br", + "cost": 10000, + "cursorStyle": "flag-br", + "scoreLens": false + }, + { + "id": "cursor-flag-bs", + "cost": 10000, + "cursorStyle": "flag-bs", + "scoreLens": false + }, + { + "id": "cursor-flag-bt", + "cost": 10000, + "cursorStyle": "flag-bt", + "scoreLens": false + }, + { + "id": "cursor-flag-bv", + "cost": 10000, + "cursorStyle": "flag-bv", + "scoreLens": false + }, + { + "id": "cursor-flag-bw", + "cost": 10000, + "cursorStyle": "flag-bw", + "scoreLens": false + }, + { + "id": "cursor-flag-by", + "cost": 10000, + "cursorStyle": "flag-by", + "scoreLens": false + }, + { + "id": "cursor-flag-bz", + "cost": 10000, + "cursorStyle": "flag-bz", + "scoreLens": false + }, + { + "id": "cursor-flag-ca", + "cost": 20000, + "cursorStyle": "flag-ca", + "scoreLens": false + }, + { + "id": "cursor-flag-cc", + "cost": 10000, + "cursorStyle": "flag-cc", + "scoreLens": false + }, + { + "id": "cursor-flag-cd", + "cost": 10000, + "cursorStyle": "flag-cd", + "scoreLens": false + }, + { + "id": "cursor-flag-cf", + "cost": 10000, + "cursorStyle": "flag-cf", + "scoreLens": false + }, + { + "id": "cursor-flag-cg", + "cost": 10000, + "cursorStyle": "flag-cg", + "scoreLens": false + }, + { + "id": "cursor-flag-ch", + "cost": 20000, + "cursorStyle": "flag-ch", + "scoreLens": false + }, + { + "id": "cursor-flag-ci", + "cost": 10000, + "cursorStyle": "flag-ci", + "scoreLens": false + }, + { + "id": "cursor-flag-ck", + "cost": 10000, + "cursorStyle": "flag-ck", + "scoreLens": false + }, + { + "id": "cursor-flag-cl", + "cost": 20000, + "cursorStyle": "flag-cl", + "scoreLens": false + }, + { + "id": "cursor-flag-cm", + "cost": 10000, + "cursorStyle": "flag-cm", + "scoreLens": false + }, + { + "id": "cursor-flag-cn", + "cost": 10000, + "cursorStyle": "flag-cn", + "scoreLens": false + }, + { + "id": "cursor-flag-co", + "cost": 20000, + "cursorStyle": "flag-co", + "scoreLens": false + }, + { + "id": "cursor-flag-cp", + "cost": 10000, + "cursorStyle": "flag-cp", + "scoreLens": false + }, + { + "id": "cursor-flag-cq", + "cost": 10000, + "cursorStyle": "flag-cq", + "scoreLens": false + }, + { + "id": "cursor-flag-cr", + "cost": 20000, + "cursorStyle": "flag-cr", + "scoreLens": false + }, + { + "id": "cursor-flag-cu", + "cost": 10000, + "cursorStyle": "flag-cu", + "scoreLens": false + }, + { + "id": "cursor-flag-cv", + "cost": 10000, + "cursorStyle": "flag-cv", + "scoreLens": false + }, + { + "id": "cursor-flag-cw", + "cost": 10000, + "cursorStyle": "flag-cw", + "scoreLens": false + }, + { + "id": "cursor-flag-cx", + "cost": 10000, + "cursorStyle": "flag-cx", + "scoreLens": false + }, + { + "id": "cursor-flag-cy", + "cost": 10000, + "cursorStyle": "flag-cy", + "scoreLens": false + }, + { + "id": "cursor-flag-cz", + "cost": 20000, + "cursorStyle": "flag-cz", + "scoreLens": false + }, + { + "id": "cursor-flag-de", + "cost": 20000, + "cursorStyle": "flag-de", + "scoreLens": false + }, + { + "id": "cursor-flag-dg", + "cost": 10000, + "cursorStyle": "flag-dg", + "scoreLens": false + }, + { + "id": "cursor-flag-dj", + "cost": 10000, + "cursorStyle": "flag-dj", + "scoreLens": false + }, + { + "id": "cursor-flag-dk", + "cost": 20000, + "cursorStyle": "flag-dk", + "scoreLens": false + }, + { + "id": "cursor-flag-dm", + "cost": 10000, + "cursorStyle": "flag-dm", + "scoreLens": false + }, + { + "id": "cursor-flag-do", + "cost": 10000, + "cursorStyle": "flag-do", + "scoreLens": false + }, + { + "id": "cursor-flag-dz", + "cost": 10000, + "cursorStyle": "flag-dz", + "scoreLens": false + }, + { + "id": "cursor-flag-ea", + "cost": 10000, + "cursorStyle": "flag-ea", + "scoreLens": false + }, + { + "id": "cursor-flag-ec", + "cost": 10000, + "cursorStyle": "flag-ec", + "scoreLens": false + }, + { + "id": "cursor-flag-ee", + "cost": 20000, + "cursorStyle": "flag-ee", + "scoreLens": false + }, + { + "id": "cursor-flag-eg", + "cost": 10000, + "cursorStyle": "flag-eg", + "scoreLens": false + }, + { + "id": "cursor-flag-eh", + "cost": 10000, + "cursorStyle": "flag-eh", + "scoreLens": false + }, + { + "id": "cursor-flag-er", + "cost": 10000, + "cursorStyle": "flag-er", + "scoreLens": false + }, + { + "id": "cursor-flag-es", + "cost": 20000, + "cursorStyle": "flag-es", + "scoreLens": false + }, + { + "id": "cursor-flag-et", + "cost": 10000, + "cursorStyle": "flag-et", + "scoreLens": false + }, + { + "id": "cursor-flag-eu", + "cost": 10000, + "cursorStyle": "flag-eu", + "scoreLens": false + }, + { + "id": "cursor-flag-fi", + "cost": 20000, + "cursorStyle": "flag-fi", + "scoreLens": false + }, + { + "id": "cursor-flag-fj", + "cost": 10000, + "cursorStyle": "flag-fj", + "scoreLens": false + }, + { + "id": "cursor-flag-fk", + "cost": 10000, + "cursorStyle": "flag-fk", + "scoreLens": false + }, + { + "id": "cursor-flag-fm", + "cost": 10000, + "cursorStyle": "flag-fm", + "scoreLens": false + }, + { + "id": "cursor-flag-fo", + "cost": 10000, + "cursorStyle": "flag-fo", + "scoreLens": false + }, + { + "id": "cursor-flag-fr", + "cost": 20000, + "cursorStyle": "flag-fr", + "scoreLens": false + }, + { + "id": "cursor-flag-ga", + "cost": 10000, + "cursorStyle": "flag-ga", + "scoreLens": false + }, + { + "id": "cursor-flag-gb", + "cost": 20000, + "cursorStyle": "flag-gb", + "scoreLens": false + }, + { + "id": "cursor-flag-gd", + "cost": 10000, + "cursorStyle": "flag-gd", + "scoreLens": false + }, + { + "id": "cursor-flag-ge", + "cost": 10000, + "cursorStyle": "flag-ge", + "scoreLens": false + }, + { + "id": "cursor-flag-gf", + "cost": 10000, + "cursorStyle": "flag-gf", + "scoreLens": false + }, + { + "id": "cursor-flag-gg", + "cost": 10000, + "cursorStyle": "flag-gg", + "scoreLens": false + }, + { + "id": "cursor-flag-gh", + "cost": 10000, + "cursorStyle": "flag-gh", + "scoreLens": false + }, + { + "id": "cursor-flag-gi", + "cost": 10000, + "cursorStyle": "flag-gi", + "scoreLens": false + }, + { + "id": "cursor-flag-gl", + "cost": 10000, + "cursorStyle": "flag-gl", + "scoreLens": false + }, + { + "id": "cursor-flag-gm", + "cost": 10000, + "cursorStyle": "flag-gm", + "scoreLens": false + }, + { + "id": "cursor-flag-gn", + "cost": 10000, + "cursorStyle": "flag-gn", + "scoreLens": false + }, + { + "id": "cursor-flag-gp", + "cost": 10000, + "cursorStyle": "flag-gp", + "scoreLens": false + }, + { + "id": "cursor-flag-gq", + "cost": 10000, + "cursorStyle": "flag-gq", + "scoreLens": false + }, + { + "id": "cursor-flag-gr", + "cost": 20000, + "cursorStyle": "flag-gr", + "scoreLens": false + }, + { + "id": "cursor-flag-gs", + "cost": 10000, + "cursorStyle": "flag-gs", + "scoreLens": false + }, + { + "id": "cursor-flag-gt", + "cost": 10000, + "cursorStyle": "flag-gt", + "scoreLens": false + }, + { + "id": "cursor-flag-gu", + "cost": 10000, + "cursorStyle": "flag-gu", + "scoreLens": false + }, + { + "id": "cursor-flag-gw", + "cost": 10000, + "cursorStyle": "flag-gw", + "scoreLens": false + }, + { + "id": "cursor-flag-gy", + "cost": 10000, + "cursorStyle": "flag-gy", + "scoreLens": false + }, + { + "id": "cursor-flag-hk", + "cost": 10000, + "cursorStyle": "flag-hk", + "scoreLens": false + }, + { + "id": "cursor-flag-hm", + "cost": 10000, + "cursorStyle": "flag-hm", + "scoreLens": false + }, + { + "id": "cursor-flag-hn", + "cost": 10000, + "cursorStyle": "flag-hn", + "scoreLens": false + }, + { + "id": "cursor-flag-hr", + "cost": 10000, + "cursorStyle": "flag-hr", + "scoreLens": false + }, + { + "id": "cursor-flag-ht", + "cost": 10000, + "cursorStyle": "flag-ht", + "scoreLens": false + }, + { + "id": "cursor-flag-hu", + "cost": 20000, + "cursorStyle": "flag-hu", + "scoreLens": false + }, + { + "id": "cursor-flag-ic", + "cost": 10000, + "cursorStyle": "flag-ic", + "scoreLens": false + }, + { + "id": "cursor-flag-id", + "cost": 10000, + "cursorStyle": "flag-id", + "scoreLens": false + }, + { + "id": "cursor-flag-ie", + "cost": 20000, + "cursorStyle": "flag-ie", + "scoreLens": false + }, + { + "id": "cursor-flag-il", + "cost": 20000, + "cursorStyle": "flag-il", + "scoreLens": false + }, + { + "id": "cursor-flag-im", + "cost": 10000, + "cursorStyle": "flag-im", + "scoreLens": false + }, + { + "id": "cursor-flag-in", + "cost": 10000, + "cursorStyle": "flag-in", + "scoreLens": false + }, + { + "id": "cursor-flag-io", + "cost": 10000, + "cursorStyle": "flag-io", + "scoreLens": false + }, + { + "id": "cursor-flag-iq", + "cost": 10000, + "cursorStyle": "flag-iq", + "scoreLens": false + }, + { + "id": "cursor-flag-ir", + "cost": 10000, + "cursorStyle": "flag-ir", + "scoreLens": false + }, + { + "id": "cursor-flag-is", + "cost": 20000, + "cursorStyle": "flag-is", + "scoreLens": false + }, + { + "id": "cursor-flag-it", + "cost": 20000, + "cursorStyle": "flag-it", + "scoreLens": false + }, + { + "id": "cursor-flag-je", + "cost": 10000, + "cursorStyle": "flag-je", + "scoreLens": false + }, + { + "id": "cursor-flag-jm", + "cost": 10000, + "cursorStyle": "flag-jm", + "scoreLens": false + }, + { + "id": "cursor-flag-jo", + "cost": 10000, + "cursorStyle": "flag-jo", + "scoreLens": false + }, + { + "id": "cursor-flag-jp", + "cost": 20000, + "cursorStyle": "flag-jp", + "scoreLens": false + }, + { + "id": "cursor-flag-ke", + "cost": 10000, + "cursorStyle": "flag-ke", + "scoreLens": false + }, + { + "id": "cursor-flag-kg", + "cost": 10000, + "cursorStyle": "flag-kg", + "scoreLens": false + }, + { + "id": "cursor-flag-kh", + "cost": 10000, + "cursorStyle": "flag-kh", + "scoreLens": false + }, + { + "id": "cursor-flag-ki", + "cost": 10000, + "cursorStyle": "flag-ki", + "scoreLens": false + }, + { + "id": "cursor-flag-km", + "cost": 10000, + "cursorStyle": "flag-km", + "scoreLens": false + }, + { + "id": "cursor-flag-kn", + "cost": 10000, + "cursorStyle": "flag-kn", + "scoreLens": false + }, + { + "id": "cursor-flag-kp", + "cost": 10000, + "cursorStyle": "flag-kp", + "scoreLens": false + }, + { + "id": "cursor-flag-kr", + "cost": 20000, + "cursorStyle": "flag-kr", + "scoreLens": false + }, + { + "id": "cursor-flag-kw", + "cost": 10000, + "cursorStyle": "flag-kw", + "scoreLens": false + }, + { + "id": "cursor-flag-ky", + "cost": 10000, + "cursorStyle": "flag-ky", + "scoreLens": false + }, + { + "id": "cursor-flag-kz", + "cost": 10000, + "cursorStyle": "flag-kz", + "scoreLens": false + }, + { + "id": "cursor-flag-la", + "cost": 10000, + "cursorStyle": "flag-la", + "scoreLens": false + }, + { + "id": "cursor-flag-lb", + "cost": 10000, + "cursorStyle": "flag-lb", + "scoreLens": false + }, + { + "id": "cursor-flag-lc", + "cost": 10000, + "cursorStyle": "flag-lc", + "scoreLens": false + }, + { + "id": "cursor-flag-li", + "cost": 10000, + "cursorStyle": "flag-li", + "scoreLens": false + }, + { + "id": "cursor-flag-lk", + "cost": 10000, + "cursorStyle": "flag-lk", + "scoreLens": false + }, + { + "id": "cursor-flag-lr", + "cost": 10000, + "cursorStyle": "flag-lr", + "scoreLens": false + }, + { + "id": "cursor-flag-ls", + "cost": 10000, + "cursorStyle": "flag-ls", + "scoreLens": false + }, + { + "id": "cursor-flag-lt", + "cost": 20000, + "cursorStyle": "flag-lt", + "scoreLens": false + }, + { + "id": "cursor-flag-lu", + "cost": 20000, + "cursorStyle": "flag-lu", + "scoreLens": false + }, + { + "id": "cursor-flag-lv", + "cost": 20000, + "cursorStyle": "flag-lv", + "scoreLens": false + }, + { + "id": "cursor-flag-ly", + "cost": 10000, + "cursorStyle": "flag-ly", + "scoreLens": false + }, + { + "id": "cursor-flag-ma", + "cost": 10000, + "cursorStyle": "flag-ma", + "scoreLens": false + }, + { + "id": "cursor-flag-mc", + "cost": 10000, + "cursorStyle": "flag-mc", + "scoreLens": false + }, + { + "id": "cursor-flag-md", + "cost": 10000, + "cursorStyle": "flag-md", + "scoreLens": false + }, + { + "id": "cursor-flag-me", + "cost": 10000, + "cursorStyle": "flag-me", + "scoreLens": false + }, + { + "id": "cursor-flag-mf", + "cost": 10000, + "cursorStyle": "flag-mf", + "scoreLens": false + }, + { + "id": "cursor-flag-mg", + "cost": 10000, + "cursorStyle": "flag-mg", + "scoreLens": false + }, + { + "id": "cursor-flag-mh", + "cost": 10000, + "cursorStyle": "flag-mh", + "scoreLens": false + }, + { + "id": "cursor-flag-mk", + "cost": 10000, + "cursorStyle": "flag-mk", + "scoreLens": false + }, + { + "id": "cursor-flag-ml", + "cost": 10000, + "cursorStyle": "flag-ml", + "scoreLens": false + }, + { + "id": "cursor-flag-mm", + "cost": 10000, + "cursorStyle": "flag-mm", + "scoreLens": false + }, + { + "id": "cursor-flag-mn", + "cost": 10000, + "cursorStyle": "flag-mn", + "scoreLens": false + }, + { + "id": "cursor-flag-mo", + "cost": 10000, + "cursorStyle": "flag-mo", + "scoreLens": false + }, + { + "id": "cursor-flag-mp", + "cost": 10000, + "cursorStyle": "flag-mp", + "scoreLens": false + }, + { + "id": "cursor-flag-mq", + "cost": 10000, + "cursorStyle": "flag-mq", + "scoreLens": false + }, + { + "id": "cursor-flag-mr", + "cost": 10000, + "cursorStyle": "flag-mr", + "scoreLens": false + }, + { + "id": "cursor-flag-ms", + "cost": 10000, + "cursorStyle": "flag-ms", + "scoreLens": false + }, + { + "id": "cursor-flag-mt", + "cost": 10000, + "cursorStyle": "flag-mt", + "scoreLens": false + }, + { + "id": "cursor-flag-mu", + "cost": 10000, + "cursorStyle": "flag-mu", + "scoreLens": false + }, + { + "id": "cursor-flag-mv", + "cost": 10000, + "cursorStyle": "flag-mv", + "scoreLens": false + }, + { + "id": "cursor-flag-mw", + "cost": 10000, + "cursorStyle": "flag-mw", + "scoreLens": false + }, + { + "id": "cursor-flag-mx", + "cost": 20000, + "cursorStyle": "flag-mx", + "scoreLens": false + }, + { + "id": "cursor-flag-my", + "cost": 10000, + "cursorStyle": "flag-my", + "scoreLens": false + }, + { + "id": "cursor-flag-mz", + "cost": 10000, + "cursorStyle": "flag-mz", + "scoreLens": false + }, + { + "id": "cursor-flag-na", + "cost": 10000, + "cursorStyle": "flag-na", + "scoreLens": false + }, + { + "id": "cursor-flag-nc", + "cost": 10000, + "cursorStyle": "flag-nc", + "scoreLens": false + }, + { + "id": "cursor-flag-ne", + "cost": 10000, + "cursorStyle": "flag-ne", + "scoreLens": false + }, + { + "id": "cursor-flag-nf", + "cost": 10000, + "cursorStyle": "flag-nf", + "scoreLens": false + }, + { + "id": "cursor-flag-ng", + "cost": 10000, + "cursorStyle": "flag-ng", + "scoreLens": false + }, + { + "id": "cursor-flag-ni", + "cost": 10000, + "cursorStyle": "flag-ni", + "scoreLens": false + }, + { + "id": "cursor-flag-nl", + "cost": 20000, + "cursorStyle": "flag-nl", + "scoreLens": false + }, + { + "id": "cursor-flag-no", + "cost": 20000, + "cursorStyle": "flag-no", + "scoreLens": false + }, + { + "id": "cursor-flag-np", + "cost": 10000, + "cursorStyle": "flag-np", + "scoreLens": false + }, + { + "id": "cursor-flag-nr", + "cost": 10000, + "cursorStyle": "flag-nr", + "scoreLens": false + }, + { + "id": "cursor-flag-nu", + "cost": 10000, + "cursorStyle": "flag-nu", + "scoreLens": false + }, + { + "id": "cursor-flag-nz", + "cost": 20000, + "cursorStyle": "flag-nz", + "scoreLens": false + }, + { + "id": "cursor-flag-om", + "cost": 10000, + "cursorStyle": "flag-om", + "scoreLens": false + }, + { + "id": "cursor-flag-pa", + "cost": 10000, + "cursorStyle": "flag-pa", + "scoreLens": false + }, + { + "id": "cursor-flag-pe", + "cost": 10000, + "cursorStyle": "flag-pe", + "scoreLens": false + }, + { + "id": "cursor-flag-pf", + "cost": 10000, + "cursorStyle": "flag-pf", + "scoreLens": false + }, + { + "id": "cursor-flag-pg", + "cost": 10000, + "cursorStyle": "flag-pg", + "scoreLens": false + }, + { + "id": "cursor-flag-ph", + "cost": 10000, + "cursorStyle": "flag-ph", + "scoreLens": false + }, + { + "id": "cursor-flag-pk", + "cost": 10000, + "cursorStyle": "flag-pk", + "scoreLens": false + }, + { + "id": "cursor-flag-pl", + "cost": 20000, + "cursorStyle": "flag-pl", + "scoreLens": false + }, + { + "id": "cursor-flag-pm", + "cost": 10000, + "cursorStyle": "flag-pm", + "scoreLens": false + }, + { + "id": "cursor-flag-pn", + "cost": 10000, + "cursorStyle": "flag-pn", + "scoreLens": false + }, + { + "id": "cursor-flag-pr", + "cost": 10000, + "cursorStyle": "flag-pr", + "scoreLens": false + }, + { + "id": "cursor-flag-ps", + "cost": 10000, + "cursorStyle": "flag-ps", + "scoreLens": false + }, + { + "id": "cursor-flag-pt", + "cost": 20000, + "cursorStyle": "flag-pt", + "scoreLens": false + }, + { + "id": "cursor-flag-pw", + "cost": 10000, + "cursorStyle": "flag-pw", + "scoreLens": false + }, + { + "id": "cursor-flag-py", + "cost": 10000, + "cursorStyle": "flag-py", + "scoreLens": false + }, + { + "id": "cursor-flag-qa", + "cost": 10000, + "cursorStyle": "flag-qa", + "scoreLens": false + }, + { + "id": "cursor-flag-re", + "cost": 10000, + "cursorStyle": "flag-re", + "scoreLens": false + }, + { + "id": "cursor-flag-ro", + "cost": 10000, + "cursorStyle": "flag-ro", + "scoreLens": false + }, + { + "id": "cursor-flag-rs", + "cost": 10000, + "cursorStyle": "flag-rs", + "scoreLens": false + }, + { + "id": "cursor-flag-ru", + "cost": 10000, + "cursorStyle": "flag-ru", + "scoreLens": false + }, + { + "id": "cursor-flag-rw", + "cost": 10000, + "cursorStyle": "flag-rw", + "scoreLens": false + }, + { + "id": "cursor-flag-sa", + "cost": 10000, + "cursorStyle": "flag-sa", + "scoreLens": false + }, + { + "id": "cursor-flag-sb", + "cost": 10000, + "cursorStyle": "flag-sb", + "scoreLens": false + }, + { + "id": "cursor-flag-sc", + "cost": 10000, + "cursorStyle": "flag-sc", + "scoreLens": false + }, + { + "id": "cursor-flag-sd", + "cost": 10000, + "cursorStyle": "flag-sd", + "scoreLens": false + }, + { + "id": "cursor-flag-se", + "cost": 20000, + "cursorStyle": "flag-se", + "scoreLens": false + }, + { + "id": "cursor-flag-sg", + "cost": 10000, + "cursorStyle": "flag-sg", + "scoreLens": false + }, + { + "id": "cursor-flag-sh", + "cost": 10000, + "cursorStyle": "flag-sh", + "scoreLens": false + }, + { + "id": "cursor-flag-si", + "cost": 20000, + "cursorStyle": "flag-si", + "scoreLens": false + }, + { + "id": "cursor-flag-sj", + "cost": 10000, + "cursorStyle": "flag-sj", + "scoreLens": false + }, + { + "id": "cursor-flag-sk", + "cost": 20000, + "cursorStyle": "flag-sk", + "scoreLens": false + }, + { + "id": "cursor-flag-sl", + "cost": 10000, + "cursorStyle": "flag-sl", + "scoreLens": false + }, + { + "id": "cursor-flag-sm", + "cost": 10000, + "cursorStyle": "flag-sm", + "scoreLens": false + }, + { + "id": "cursor-flag-sn", + "cost": 10000, + "cursorStyle": "flag-sn", + "scoreLens": false + }, + { + "id": "cursor-flag-so", + "cost": 10000, + "cursorStyle": "flag-so", + "scoreLens": false + }, + { + "id": "cursor-flag-sr", + "cost": 10000, + "cursorStyle": "flag-sr", + "scoreLens": false + }, + { + "id": "cursor-flag-ss", + "cost": 10000, + "cursorStyle": "flag-ss", + "scoreLens": false + }, + { + "id": "cursor-flag-st", + "cost": 10000, + "cursorStyle": "flag-st", + "scoreLens": false + }, + { + "id": "cursor-flag-sv", + "cost": 10000, + "cursorStyle": "flag-sv", + "scoreLens": false + }, + { + "id": "cursor-flag-sx", + "cost": 10000, + "cursorStyle": "flag-sx", + "scoreLens": false + }, + { + "id": "cursor-flag-sy", + "cost": 10000, + "cursorStyle": "flag-sy", + "scoreLens": false + }, + { + "id": "cursor-flag-sz", + "cost": 10000, + "cursorStyle": "flag-sz", + "scoreLens": false + }, + { + "id": "cursor-flag-ta", + "cost": 10000, + "cursorStyle": "flag-ta", + "scoreLens": false + }, + { + "id": "cursor-flag-tc", + "cost": 10000, + "cursorStyle": "flag-tc", + "scoreLens": false + }, + { + "id": "cursor-flag-td", + "cost": 10000, + "cursorStyle": "flag-td", + "scoreLens": false + }, + { + "id": "cursor-flag-tf", + "cost": 10000, + "cursorStyle": "flag-tf", + "scoreLens": false + }, + { + "id": "cursor-flag-tg", + "cost": 10000, + "cursorStyle": "flag-tg", + "scoreLens": false + }, + { + "id": "cursor-flag-th", + "cost": 10000, + "cursorStyle": "flag-th", + "scoreLens": false + }, + { + "id": "cursor-flag-tj", + "cost": 10000, + "cursorStyle": "flag-tj", + "scoreLens": false + }, + { + "id": "cursor-flag-tk", + "cost": 10000, + "cursorStyle": "flag-tk", + "scoreLens": false + }, + { + "id": "cursor-flag-tl", + "cost": 10000, + "cursorStyle": "flag-tl", + "scoreLens": false + }, + { + "id": "cursor-flag-tm", + "cost": 10000, + "cursorStyle": "flag-tm", + "scoreLens": false + }, + { + "id": "cursor-flag-tn", + "cost": 10000, + "cursorStyle": "flag-tn", + "scoreLens": false + }, + { + "id": "cursor-flag-to", + "cost": 10000, + "cursorStyle": "flag-to", + "scoreLens": false + }, + { + "id": "cursor-flag-tr", + "cost": 20000, + "cursorStyle": "flag-tr", + "scoreLens": false + }, + { + "id": "cursor-flag-tt", + "cost": 10000, + "cursorStyle": "flag-tt", + "scoreLens": false + }, + { + "id": "cursor-flag-tv", + "cost": 10000, + "cursorStyle": "flag-tv", + "scoreLens": false + }, + { + "id": "cursor-flag-tw", + "cost": 10000, + "cursorStyle": "flag-tw", + "scoreLens": false + }, + { + "id": "cursor-flag-tz", + "cost": 10000, + "cursorStyle": "flag-tz", + "scoreLens": false + }, + { + "id": "cursor-flag-ua", + "cost": 10000, + "cursorStyle": "flag-ua", + "scoreLens": false + }, + { + "id": "cursor-flag-ug", + "cost": 10000, + "cursorStyle": "flag-ug", + "scoreLens": false + }, + { + "id": "cursor-flag-um", + "cost": 10000, + "cursorStyle": "flag-um", + "scoreLens": false + }, + { + "id": "cursor-flag-un", + "cost": 10000, + "cursorStyle": "flag-un", + "scoreLens": false + }, + { + "id": "cursor-flag-us", + "cost": 20000, + "cursorStyle": "flag-us", + "scoreLens": false + }, + { + "id": "cursor-flag-uy", + "cost": 10000, + "cursorStyle": "flag-uy", + "scoreLens": false + }, + { + "id": "cursor-flag-uz", + "cost": 10000, + "cursorStyle": "flag-uz", + "scoreLens": false + }, + { + "id": "cursor-flag-va", + "cost": 10000, + "cursorStyle": "flag-va", + "scoreLens": false + }, + { + "id": "cursor-flag-vc", + "cost": 10000, + "cursorStyle": "flag-vc", + "scoreLens": false + }, + { + "id": "cursor-flag-ve", + "cost": 10000, + "cursorStyle": "flag-ve", + "scoreLens": false + }, + { + "id": "cursor-flag-vg", + "cost": 10000, + "cursorStyle": "flag-vg", + "scoreLens": false + }, + { + "id": "cursor-flag-vi", + "cost": 10000, + "cursorStyle": "flag-vi", + "scoreLens": false + }, + { + "id": "cursor-flag-vn", + "cost": 10000, + "cursorStyle": "flag-vn", + "scoreLens": false + }, + { + "id": "cursor-flag-vu", + "cost": 10000, + "cursorStyle": "flag-vu", + "scoreLens": false + }, + { + "id": "cursor-flag-wf", + "cost": 10000, + "cursorStyle": "flag-wf", + "scoreLens": false + }, + { + "id": "cursor-flag-ws", + "cost": 10000, + "cursorStyle": "flag-ws", + "scoreLens": false + }, + { + "id": "cursor-flag-xk", + "cost": 10000, + "cursorStyle": "flag-xk", + "scoreLens": false + }, + { + "id": "cursor-flag-ye", + "cost": 10000, + "cursorStyle": "flag-ye", + "scoreLens": false + }, + { + "id": "cursor-flag-yt", + "cost": 10000, + "cursorStyle": "flag-yt", + "scoreLens": false + }, + { + "id": "cursor-flag-za", + "cost": 10000, + "cursorStyle": "flag-za", + "scoreLens": false + }, + { + "id": "cursor-flag-zm", + "cost": 10000, + "cursorStyle": "flag-zm", + "scoreLens": false + }, + { + "id": "cursor-flag-zw", + "cost": 10000, + "cursorStyle": "flag-zw", + "scoreLens": false + }, + { + "id": "cursor-flag-gbeng", + "cost": 10000, + "cursorStyle": "flag-gbeng", + "scoreLens": false + }, + { + "id": "cursor-flag-gbsct", + "cost": 10000, + "cursorStyle": "flag-gbsct", + "scoreLens": false + }, + { + "id": "cursor-flag-gbwls", + "cost": 10000, + "cursorStyle": "flag-gbwls", + "scoreLens": false + } +] diff --git a/style.css b/style.css new file mode 100644 index 0000000..79982a0 --- /dev/null +++ b/style.css @@ -0,0 +1,685 @@ +@font-face{font-family:"DotGothic16Local";src:url("assets/fonts/DotGothic16-Regular.ttf") format("truetype");font-style:normal;font-weight:400;font-display:swap} +:root{ + --bg:#121416; + --panel:#1c1f22; + --ink:#f3f4f5; + --muted:#8b949b; + --grid:rgba(198,210,218,.28); + --accent:#d9f06f; + --bad:#ff6e7a; + --dot-font:"DotGothic16Local","DotGothic16","MS Gothic","MS ゴシック",ui-monospace,monospace; + --emoji-font:"Segoe UI Emoji","Apple Color Emoji","Noto Color Emoji",sans-serif; + --bar-height:54px; + --safe-top:env(safe-area-inset-top,0px); + --safe-right:env(safe-area-inset-right,0px); + --safe-bottom:env(safe-area-inset-bottom,0px); + --safe-left:env(safe-area-inset-left,0px) +} +*{box-sizing:border-box;user-select:none;-webkit-user-select:none} +html,body{margin:0;width:100%;height:100%;overflow:hidden;background:var(--bg);color:var(--ink);font-family:var(--dot-font);font-variant-ligatures:none;-webkit-font-smoothing:none;text-rendering:geometricPrecision} +body,button,input,select,textarea{font-family:var(--dot-font)} +button,input,select,textarea{font-size:inherit} +button{font:inherit;color:inherit;letter-spacing:.04em} +button:focus-visible,[tabindex]:focus-visible{outline:3px solid #fff;outline-offset:3px} +#app{position:fixed;inset:0;overflow:hidden;background:radial-gradient(circle at 50% 45%,rgba(255,255,255,.025),transparent 34%),var(--bg)} +#topbar{ + position:absolute;z-index:50;left:0;right:0;top:0; + height:calc(var(--bar-height) + var(--safe-top)); + display:flex;align-items:center;gap:10px; + padding:var(--safe-top) calc(5px + var(--safe-right)) 0 calc(5px + var(--safe-left)); + background:linear-gradient(90deg,rgba(24,42,52,.985),rgba(31,35,58,.985)); + border-bottom:1px solid rgba(126,213,224,.22) +} +.brand{font-weight:900;letter-spacing:.08em;white-space:nowrap} +.brand small{font-weight:650;color:var(--muted);margin-left:8px} +.spacer{flex:1} +.toolbar{display:flex;align-items:center;gap:7px} +.pill{ + min-width:40px;min-height:40px;padding:0 5px;border-radius:2px; + border:1px solid rgba(255,255,255,.1);background:#25292d;cursor:pointer; + display:inline-flex;align-items:center;justify-content:center;gap:6px +} +.pill:hover{background:#2d3237} +.pill:disabled{opacity:.42;cursor:not-allowed} +.pill.danger{border-color:rgba(255,110,122,.42);color:#ffd5d9} +.pill.inventory-button b{color:var(--accent);font-variant-numeric:tabular-nums} +.pill.time-attack.active{border-color:rgba(217,240,111,.58);background:#2c3428;color:#eff9bd;box-shadow:inset 0 -2px #d9f06f} +.pill.time-attack.active>span:first-child{animation:timeAttackTick 1s steps(2,end) infinite} +.pill.time-attack.active.starting{position:relative;z-index:4;transform-origin:center;animation:timeAttackStartEmphasis 1s ease-in-out 5;box-shadow:inset 0 -2px #d9f06f,0 0 0 3px rgba(217,240,111,.2),0 8px 28px rgba(0,0,0,.36)} +.pill.time-attack.active.starting>span:first-child{font-size:25px} +.pill.time-attack.active.starting #timeAttackButtonLabel{font-size:17px;font-weight:900} +.stat{color:var(--muted);font-size:16px;white-space:nowrap} +.stat b{color:var(--ink)} +.stat.score{color:#c99cff}.stat.score b{color:#e1b4ff;font-variant-numeric:tabular-nums;text-shadow:0 0 9px rgba(177,94,255,.45)} +.stat.score.gem-receiving{animation:gemWalletPulse .52s steps(2,end) 2} +#saveStatus{ + display:none;position:absolute;z-index:2;right:calc(14px + var(--safe-right)); + top:calc(var(--safe-top) + var(--bar-height) + 8px); + min-height:36px;padding:0 12px;border-radius:2px;border:1px solid rgba(255,255,255,.14); + color:var(--ink);background:rgba(26,30,33,.96);cursor:pointer +} +#saveStatus[data-state="saving"],#saveStatus[data-state="error"]{display:block} +#saveStatus[data-state="error"]{border-color:var(--bad);color:#ffd8dc} +#viewport{ + position:absolute;left:0;right:0;top:calc(var(--bar-height) + var(--safe-top));bottom:0; + overflow:hidden;touch-action:none;cursor:default +} +#viewport::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:20;background:radial-gradient(circle at 50% 48%,transparent 42%,rgba(5,7,9,.18) 72%,rgba(5,7,9,.66) 115%);box-shadow:inset 0 0 90px rgba(0,0,0,.3)} +#viewport.panning{cursor:grabbing} +#world{position:absolute;left:0;top:0;transform-origin:0 0;will-change:transform} + +#overviewCanvas{position:absolute;left:0;top:0;display:block;pointer-events:none;z-index:3;transform-origin:0 0;will-change:transform;contain:strict} +#overviewCanvas[hidden]{display:none} +#viewport.canvas-overview #world{display:none} +#minimap{ + position:fixed;z-index:45;right:calc(12px + var(--safe-right));bottom:calc(12px + var(--safe-bottom)); + width:226px;padding:8px;border:1px solid rgba(255,255,255,.22);border-radius:2px; + background:rgba(14,17,19,.97);box-shadow:0 10px 30px rgba(0,0,0,.42) +} +.minimap-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:5px;color:#cbd2d6;font-size:9px;letter-spacing:.12em} +.minimap-head b{color:#fff}.minimap-head span{color:var(--accent)} +#minimapCanvas{display:block;width:100%;height:132px;border:0;background:#101315;cursor:crosshair;touch-action:none;image-rendering:pixelated} +#minimapCanvas:focus-visible{outline:2px solid #d9f06f;outline-offset:2px} +.minimap-controls{display:grid;grid-template-columns:repeat(2,1fr);gap:3px;margin-bottom:5px} +.minimap-controls button{min-width:0;min-height:24px;padding:3px 4px;border:1px solid rgba(255,255,255,.18);background:#172026;color:#dbe5e8;font:7px/1.1 var(--dot-font);font-weight:900;cursor:pointer} +.minimap-controls button:hover,.minimap-controls button:focus-visible{border-color:#d9f06f;background:#253139;outline:none} +.minimap-legend{display:flex;align-items:center;gap:10px;margin-top:6px;color:#899399;font-size:7px;font-weight:900;letter-spacing:.07em;white-space:nowrap} +.minimap-legend span::before{content:"";display:inline-block;width:6px;height:6px;margin-right:4px;vertical-align:-1px;background:#505a60} +.minimap-legend .solved::before{background:#79d4a0}.minimap-legend .unsolved::before{background:#41494e} +.minimap-legend .long-line::before{width:10px;height:2px;vertical-align:2px;background:#ffd45f} +.board-card{position:absolute;overflow:visible;pointer-events:none;z-index:4;contain:layout style paint} +.board-card.solved{z-index:0;pointer-events:auto} +.board-card.solved:hover,.board-card.solved:focus,.board-card.solved:focus-within{z-index:2} +.board-svg{position:absolute;inset:0;display:block;overflow:visible;touch-action:none;pointer-events:none;cursor:crosshair} + .board-label{display:none;position:absolute;z-index:28;padding:3px 5px;border-radius:2px;background:rgba(12,14,16,.94);border:1px solid rgba(245,249,252,.62);border-left:3px solid var(--region);font-size:13px;letter-spacing:.05em;color:#eef2f4;pointer-events:auto;white-space:nowrap;box-shadow:0 3px 12px rgba(0,0,0,.28),0 0 0 1px rgba(255,255,255,.12),0 0 9px rgba(232,244,255,.16);text-shadow:0 1px 0 #000} +.board-card.hud-current{z-index:4} +.board-card.hud-current:not(.solved) .board-label{display:flex} +.board-label b{color:#f2f4f5;letter-spacing:.1em;margin:0} +.board-card.solved .static-layer,.board-card.solved .special-cell-layer,.board-card.solved .number-layer,.board-card.solved .gate-layer,.board-card.solved .board-label{display:none} +.board-card.solved .board-svg{pointer-events:none;cursor:default} +.cell-shape{stroke:var(--grid);stroke-width:.8;vector-effect:non-scaling-stroke} +.outer-edge{stroke:rgba(220,229,234,.72);stroke-width:1.8;vector-effect:non-scaling-stroke} +.gate-hit{fill:transparent;cursor:crosshair;pointer-events:all}.board-input-surface{fill:transparent;cursor:crosshair;pointer-events:fill}.board-svg{cursor:crosshair;pointer-events:none} +.gate-hit:focus-visible,.endpoint-hit:focus-visible{fill:rgba(255,255,255,.16);stroke:#fff;stroke-width:3;vector-effect:non-scaling-stroke} +.num{font-size:30px;font-weight:900;fill:#fff;text-anchor:middle;dominant-baseline:central;paint-order:stroke;stroke:#171a1d;stroke-width:5px;stroke-linejoin:round;pointer-events:none} +.num.bad{fill:#d8a6ab} +.num.turn-warning{fill:#d9c08e} +.num.multiple-warning{fill:#d8a6ab} +.num.on-line{fill:var(--number-line-color,#fff)} +.number-turn-warning{opacity:0;pointer-events:none} +.number-turn-warning.show{opacity:1} +.number-warning-frame{fill:none;stroke:#ffe28a;stroke-width:2.5;vector-effect:non-scaling-stroke} +.number-warning-caption{fill:#fff1a8;stroke:#15100a;stroke-width:3px;paint-order:stroke fill;font-size:8.4px;font-weight:950;letter-spacing:.03em;text-anchor:middle;dominant-baseline:central} +.number-turn-warning.multiple .number-warning-frame{stroke:#ff8fa0} +.number-turn-warning.multiple .number-warning-caption{fill:#ffd5dc;font-size:7.1px} +.board-svg text,.tutorial-board text,svg text{font-family:var(--dot-font)} +.path{fill:none;stroke-width:var(--line-width,8);stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke;pointer-events:none} +.path.invalid{stroke:#cfa2a7!important;stroke-dasharray:3 8;opacity:.58} +.board-card.solved .path{opacity:.9} +.endpoint-knob{stroke:#101214;stroke-width:2.8;vector-effect:non-scaling-stroke;pointer-events:none} +.endpoint-halo{fill:none;stroke:rgba(255,255,255,.9);stroke-width:1.5;vector-effect:non-scaling-stroke;pointer-events:none} +.endpoint-knob.number-match,.endpoint-halo.number-match{transform-box:fill-box;transform-origin:center;animation:numberMatchKnob .36s ease-out both} +.number-match-pulse{fill:none;stroke:#f5ee9d;stroke-width:2;vector-effect:non-scaling-stroke;pointer-events:none;transform-box:fill-box;transform-origin:center;animation:numberMatchPulse .42s ease-out both} +.number-match-orbit{opacity:0;pointer-events:none;transition:opacity .14s ease} +.number-match-orbit.show{opacity:1} +.number-match-orbit-ring,.number-match-orbit-holes{fill:none;vector-effect:non-scaling-stroke} +.number-match-orbit-ring{stroke:#fff7a8;stroke-width:3.4} +.number-match-orbit-holes{stroke:#111416;stroke-width:3.4;stroke-linecap:round;stroke-dasharray:1.2 5.35;transform-box:fill-box;transform-origin:center;animation:numberMatchOrbit 1.15s linear infinite} +@keyframes numberMatchKnob{0%{transform:scale(1)}35%{transform:scale(1.65)}100%{transform:scale(1.3)}} +@keyframes numberMatchPulse{0%{opacity:.85;transform:scale(.6)}100%{opacity:0;transform:scale(2.15)}} +@keyframes numberMatchOrbit{to{transform:rotate(360deg)}} +.endpoint-hit{fill:transparent;cursor:grab;pointer-events:all} +.endpoint-cursor-image,.endpoint-cursor-emoji{pointer-events:none} +.endpoint-cursor-emoji{font-family:var(--emoji-font)!important;font-size:8.6px;text-anchor:middle;dominant-baseline:central} +.turn-badge{fill:#111416;stroke:rgba(255,255,255,.5);stroke-width:1;vector-effect:non-scaling-stroke;pointer-events:none} +.turn-count{fill:#fff;font-family:var(--dot-font);font-size:16px;font-weight:900;font-variant-ligatures:none;letter-spacing:0;text-anchor:middle;dominant-baseline:central;pointer-events:none} +.gate-marker{fill:none;stroke:#f4f5f5;stroke-width:2.6;stroke-linecap:square;stroke-linejoin:miter;vector-effect:non-scaling-stroke;pointer-events:none} +.gate-marker.sealed{stroke:#7d878e;stroke-width:2.2} +.gate-dot{opacity:.9;pointer-events:none} +.gate-knob{opacity:0;transform-box:fill-box;transform-origin:center;stroke:#101214;stroke-width:2.8;vector-effect:non-scaling-stroke;pointer-events:none;transition:opacity .08s ease,transform .08s ease;transform:scale(.45)} +.gate-knob.show{opacity:1;transform:scale(1)} +.gate-dot.frontier{stroke:var(--region);stroke-width:7;opacity:.92} +.gate-dot.sealed{stroke:#4c555a;stroke-width:3;opacity:.76} +.gate-dot.frontier{animation:frontierPulse 1.8s ease-in-out infinite} +.shared-gate-hidden{opacity:0!important;pointer-events:none!important} +.solver-badge{position:absolute;z-index:18;left:50%;top:50%;display:grid;gap:2px;min-width:118px;padding:9px 13px;transform:translate(-50%,-42%) scale(.92);opacity:0;border:1px solid color-mix(in srgb,var(--region) 68%,white);border-radius:2px;background:rgba(10,13,15,.94);box-shadow:0 10px 28px rgba(0,0,0,.34);text-align:center;pointer-events:none;transition:opacity .18s ease,transform .18s ease} +.solver-badge span{color:#aeb8be;font-size:8px;font-weight:800;letter-spacing:.18em} +.solver-badge strong{color:#fff;font-size:13px;letter-spacing:.12em} +.board-card.solved:hover .solver-badge,.board-card.solved:focus .solver-badge,.board-card.solved:focus-within .solver-badge{opacity:1;transform:translate(-50%,-50%) scale(1)} +.board-card.solved:focus-visible{outline:none} +.line-store{ + position:absolute;z-index:22;display:grid;grid-template-rows:1fr auto;place-items:center;width:52px;height:48px; + padding:5px 5px 4px;border:1px solid color-mix(in srgb,var(--region) 58%,#d9f06f);border-radius:9px; + background:linear-gradient(145deg,rgba(31,45,52,.98),rgba(13,19,23,.98)); + box-shadow:0 8px 20px rgba(0,0,0,.42),inset 0 1px rgba(255,255,255,.08),0 0 0 2px rgba(95,216,255,.06); + transform:translate(-50%,-50%);cursor:pointer;pointer-events:auto;transition:transform .14s ease,border-color .14s ease,box-shadow .14s ease +} +.line-store[hidden]{display:none} +.line-store .shop-shell,.static-shop-icon .shop-shell{position:relative;display:block;width:30px;height:24px;border:1.5px solid #d9f06f;border-radius:5px;background:linear-gradient(180deg,#263940,#152026);box-shadow:inset 0 1px rgba(255,255,255,.08)} +.line-store .shop-shell::before,.static-shop-icon .shop-shell::before{content:"";position:absolute;left:4px;right:4px;top:4px;height:5px;border-radius:2px;background:linear-gradient(90deg,#5fd8ff,#a98cff)} +.line-store .shop-shell::after,.static-shop-icon .shop-shell::after{content:"";position:absolute;left:3px;right:3px;top:11px;border-top:1px solid rgba(217,240,111,.65)} +.line-store .shop-shell i,.static-shop-icon .shop-shell i{position:absolute;left:6px;bottom:3px;width:6px;height:6px;border:1px solid #5fd8ff;border-radius:1px;background:rgba(95,216,255,.14)} +.line-store .shop-shell b,.static-shop-icon .shop-shell b{position:absolute;right:6px;bottom:0;width:7px;height:9px;border:1px solid #d9f06f;border-bottom:0;border-radius:2px 2px 0 0;background:#1c292e} +.line-store small{font-size:6px;font-weight:950;letter-spacing:.18em;color:#dfeaec} +.line-store:hover,.line-store:focus-visible{transform:translate(-50%,-50%) scale(1.06);border-color:#fff;box-shadow:0 10px 24px rgba(0,0,0,.5),0 0 0 3px rgba(95,216,255,.15)} +#toast{position:absolute;z-index:60;left:50%;bottom:calc(18px + var(--safe-bottom));max-width:min(92vw,720px);transform:translate(-50%,18px);opacity:0;padding:8px 10px;border-radius:2px;background:rgba(10,12,14,.92);border:1px solid rgba(255,255,255,.12);font-size:15px;pointer-events:none;transition:.2s ease;text-align:center} +#toast.show{opacity:1;transform:translate(-50%,0)} +#statusPanel{ + position:absolute;z-index:70;left:calc(14px + var(--safe-left));right:calc(14px + var(--safe-right)); + top:calc(var(--bar-height) + var(--safe-top) + 12px);max-width:760px;margin:auto; + padding:14px;border:1px solid rgba(255,110,122,.5);border-radius:2px; + background:rgba(28,31,34,.97);box-shadow:0 16px 50px rgba(0,0,0,.5) +} +#statusPanel[hidden]{display:none} +#statusPanel p{margin:0 0 10px;line-height:1.45;user-select:text;-webkit-user-select:text} +.status-actions{display:flex;gap:8px;flex-wrap:wrap} +.completion-burst{position:absolute;z-index:30;left:50%;top:50%;transform:translate(-50%,-50%);padding:10px 16px;border:1px solid #c26cff;border-radius:2px;background:rgba(10,12,14,.9);color:#edc8ff;font-weight:950;font-size:14px;letter-spacing:.08em;white-space:nowrap;pointer-events:none;animation:burst 1.8s ease-out forwards;box-shadow:0 0 34px rgba(174,79,255,.8)} +.gem-particle{position:fixed;z-index:290;display:block;width:28px;height:28px;margin:-14px 0 0 -14px;pointer-events:none;will-change:transform,opacity} +.gem-particle::before{content:"";position:absolute;inset:1px;transform:rotate(45deg);border:1px solid #f3c9ff;border-radius:2px 5px 2px 5px;background:linear-gradient(135deg,#f0b9ff 0 22%,#a747ed 24% 63%,#5d168f 65% 100%);box-shadow:0 0 10px #e3a0ff,0 0 24px rgba(142,52,214,.82)} +@keyframes gemWalletPulse{50%{transform:scale(1.12);opacity:1}} +.board-card.completing .path{animation:pathPulse 1.35s ease-out 1} +.board-card.revealing{animation:revealBoard .85s cubic-bezier(.2,.8,.2,1) both} +body.loading #viewport{opacity:.48} +body.loading #app::after{content:attr(data-loading-text);position:absolute;z-index:100;left:50%;top:50%;transform:translate(-50%,-50%);padding:12px 17px;border-radius:2px;background:#121416;border:1px solid rgba(255,255,255,.18);font-size:12px;font-weight:900;letter-spacing:.16em} +body.archive-busy #viewport,body.archive-busy #minimap,body.archive-busy #topbar .toolbar{pointer-events:none} +body.archive-busy #viewport,body.archive-busy #minimap{opacity:.58} +@keyframes revealBoard{from{opacity:0;transform:scale(.84)}to{opacity:1;transform:scale(1)}} +@keyframes pathPulse{50%{opacity:1}} +@keyframes burst{0%{opacity:0;transform:translate(-50%,-40%) scale(.7)}20%{opacity:1;transform:translate(-50%,-50%) scale(1.08)}75%{opacity:1}100%{opacity:0;transform:translate(-50%,-85%) scale(.96)}} +@keyframes frontierPulse{50%{stroke-width:13;opacity:.45}} +#modal,#storeModal,#inventoryModal,#timeAttackModal,#settingsModal{position:absolute;z-index:80;inset:0;display:none;place-items:center;background:rgba(0,0,0,.62);padding:calc(20px + var(--safe-top)) calc(20px + var(--safe-right)) calc(20px + var(--safe-bottom)) calc(20px + var(--safe-left))} +#modal.show,#storeModal.show,#inventoryModal.show,#timeAttackModal.show{display:grid} +.panel{width:min(720px,100%);max-height:calc(100dvh - 28px - var(--safe-top) - var(--safe-bottom));overflow-y:auto;overscroll-behavior:contain;background:#1d2124;border:1px solid rgba(255,255,255,.1);border-radius:2px;padding:16px;box-shadow:0 24px 80px rgba(0,0,0,.45)} +.panel h2{margin:0 0 8px;font-size:24px;line-height:1.4} +.panel p{color:#c4c9cd;line-height:1.6;font-size:16px} +.panel code{color:#e9f5a8} +.tutorial-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px} +.tutorial-card{min-width:0;padding:9px;border:1px solid rgba(255,255,255,.09);border-radius:2px;background:#181b1e} +.tutorial-card h3{margin:6px 0 1px;font-size:16px;letter-spacing:.06em} +.tutorial-card p{margin:0;color:#aeb6bb;font-size:14px;line-height:1.5} +.tutorial-demo{position:relative;height:86px;overflow:hidden;border-radius:0;background:linear-gradient(145deg,#22282b,#15191b);border:1px solid rgba(255,255,255,.06)} +.tutorial-demo svg{position:absolute;inset:7px;width:calc(100% - 14px);height:calc(100% - 14px);overflow:visible} +.tutorial-board .tutorial-cell{stroke:rgba(198,210,218,.42)} +.tutorial-board .outer-edge{stroke:#dfe6e9;stroke-width:2.2} +.tutorial-gate-marker{stroke-width:2} +.tutorial-path{stroke-dasharray:1;stroke-dashoffset:1} +.tutorial-draw-path{animation:tutorialGridDraw 3.2s ease-in-out infinite} +.tutorial-bend-path{animation:tutorialGridDraw 3.4s .2s ease-in-out infinite} +.tutorial-number-pulse{transform-box:fill-box;transform-origin:center;animation:tutorialGridNumber 3.4s ease-in-out infinite} +.tutorial-fill-path{animation:tutorialFill 4s ease-in-out infinite} +.tutorial-fill-1{animation-delay:0s} +.tutorial-fill-2{animation-delay:.32s} +.tutorial-fill-3{animation-delay:.64s} +.tutorial-shop-path{stroke-dashoffset:0;opacity:.88} +.tutorial-shop-marker{transform-box:fill-box;transform-origin:center;animation:tutorialGridShop 3s ease-in-out infinite} +.tutorial-shop-building{fill:#18201b;stroke:#d9f06f;stroke-width:2} +.tutorial-shop-gem{fill:#eafa9d;font-size:15px;text-anchor:middle;dominant-baseline:central} +.tutorial-shop-label{fill:#fff;font-size:5px;font-weight:950;letter-spacing:.08em;text-anchor:middle} +.tutorial-shop-score{fill:#d9f06f;font-size:8px;font-weight:950;opacity:0;animation:tutorialGridScore 3s ease-out infinite} +.control-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px} +.control-chips span{padding:9px 12px;border-radius:2px;background:#292e31;color:#d5dadd;font-size:13px;line-height:1.35;font-weight:850;letter-spacing:.035em} +.store-panel{width:min(900px,100%)} +.store-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;border-bottom:1px solid rgba(126,213,224,.22);padding-bottom:14px} +.store-heading small{color:#d9f06f;font-size:9px;font-weight:950;letter-spacing:.2em} +.store-heading h2{margin:4px 0 0} +.store-wallet{flex:0 0 auto;padding:9px 12px;border:1px solid rgba(194,108,255,.4);border-radius:2px;background:#2b2131;color:#d5b4e5;font-size:10px;font-weight:850;letter-spacing:.08em} +.store-wallet b{display:block;margin-top:2px;color:#edc8ff;font-size:18px;font-variant-numeric:tabular-nums} +#storeMeta{margin:12px 0;color:#9ea8ad;font-size:11px;line-height:1.5} +.store-inventory{display:flex;flex-direction:column;gap:16px} +.store-section{display:flex;flex-direction:column;gap:8px} +.store-section-title{margin:0;padding:0 0 7px;border-bottom:1px solid rgba(255,255,255,.09);color:#d9f06f;font-size:11px;font-weight:950;letter-spacing:.16em} +.store-section-list{display:grid;gap:6px}.store-item-list{grid-template-columns:minmax(0,1fr)} +.store-cursor-list{grid-template-columns:repeat(6,minmax(86px,1fr))} +.store-item{display:grid;grid-template-columns:46px minmax(0,1fr) auto;align-items:center;gap:12px;padding:12px;border:1px solid rgba(255,255,255,.09);border-radius:2px;background:#181c1e} +.store-item.store-cursor{display:grid;grid-template-columns:1fr;grid-template-rows:auto auto;justify-items:stretch;gap:5px;padding:6px} +.store-item.store-cursor .store-item-icon{justify-self:center;width:52px;height:48px;font-size:31px} +.store-item.store-cursor .store-buy{min-width:0;width:100%;min-height:32px;padding:3px 2px;font-size:11px;line-height:1.15} +.store-item.purchased{border-color:rgba(217,240,111,.28);background:#1c211c} +.store-item-icon{display:grid;place-items:center;width:44px;height:44px;border-radius:2px;background:#272d29;color:#d9f06f;font-size:25px} +.store-item-copy h4{margin:0 0 2px;font-size:13px;letter-spacing:.07em} +.store-item-copy strong{color:#d9f06f;font-size:10px} +.store-item-copy p{margin:3px 0 0;color:#929ca2;font-size:10px;line-height:1.4} +.store-buy{min-width:142px;min-height:42px;padding:7px 11px;border:1px solid rgba(217,240,111,.34);border-radius:2px;background:#30392d;color:#eef8c8;font-size:10px;font-weight:900;cursor:pointer} +.store-buy:hover:not(:disabled){background:#3a4734} +.store-buy:disabled{border-color:rgba(255,255,255,.09);background:#24282a;color:#747d82;cursor:not-allowed} +.inventory-panel{width:min(650px,100%)} +.inventory-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding-bottom:14px;border-bottom:1px solid rgba(126,213,224,.22)} +.inventory-heading small{color:#d9f06f;font-size:9px;font-weight:950;letter-spacing:.2em}.inventory-heading h2{margin:4px 0 0} +.inventory-heading>strong{padding:8px 10px;border:1px solid rgba(217,240,111,.25);background:#252b23;color:#d9f06f;font-size:9px;letter-spacing:.08em} +#inventoryTarget{margin:12px 0;color:#aeb6bb;font-size:10px;font-weight:900;letter-spacing:.08em} +.inventory-list{display:grid;gap:12px} +.inventory-section{display:grid;gap:6px}.inventory-section-title{margin:0;padding-bottom:5px;border-bottom:1px solid rgba(255,255,255,.09);color:#d9f06f;font-size:15px;letter-spacing:.12em} +.inventory-item-list{display:grid;gap:6px} +.inventory-cursor-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(54px,1fr));gap:6px} +.inventory-cursor-option{display:grid;place-items:center;aspect-ratio:1;min-width:0;padding:3px;border:1px solid rgba(255,255,255,.1);border-radius:2px;background:#272d29;font-size:29px;cursor:pointer} +.inventory-cursor-option:hover{background:#32393c}.inventory-cursor-option.selected{border-color:#d9f06f;background:#35402f;box-shadow:inset 0 0 0 2px rgba(217,240,111,.18)} +.inventory-item{display:grid;grid-template-columns:42px minmax(0,1fr) auto;align-items:center;gap:11px;padding:11px;border:1px solid rgba(255,255,255,.09);background:#181c1e} +.inventory-item.empty{display:block;color:#7f898f;text-align:center;font-size:10px;letter-spacing:.09em} +.inventory-item-icon{display:grid;place-items:center;width:40px;height:40px;background:#272d29;color:#d9f06f;font-size:22px} +.inventory-item h3{margin:0;font-size:12px;letter-spacing:.06em}.inventory-item p{margin:3px 0 0;color:#899399;font-size:9px;line-height:1.4} +.inventory-use{min-width:116px;min-height:39px;border:1px solid rgba(217,240,111,.34);background:#30392d;color:#eef8c8;font-size:9px;font-weight:950;cursor:pointer} +.inventory-use:hover:not(:disabled){background:#3a4734}.inventory-use:disabled{border-color:rgba(255,255,255,.08);background:#24282a;color:#737c81} +.time-attack-panel{width:min(760px,100%);padding:18px!important} +.time-attack-heading{display:flex;align-items:center;justify-content:space-between;gap:14px;padding-bottom:11px;border-bottom:1px solid rgba(126,213,224,.28)} +.time-attack-heading small{color:#d9f06f;font-size:12px;font-weight:950;letter-spacing:.16em} +.time-attack-heading h2{margin:2px 0 0;font-size:30px;line-height:1.12} +.time-attack-heading>strong{padding:8px 11px;border:1px solid rgba(217,240,111,.32);background:#252b23;color:#eafa9d;font-size:13px;letter-spacing:.06em;white-space:nowrap} +.time-attack-intro{margin:11px 0 9px!important;color:#d6dde0!important;font-size:15px!important;line-height:1.45!important} +.time-attack-kicker{margin:11px 0 7px!important;color:#b7c1c6!important;font-size:13px!important;font-weight:950;letter-spacing:.1em} +.time-attack-durations{display:grid;grid-template-columns:repeat(3,1fr);gap:8px} +.time-attack-durations button{display:grid;grid-template-columns:auto 1fr;grid-template-areas:'value course' 'value status';align-items:center;column-gap:12px;min-height:82px;padding:10px 13px;border:1px solid rgba(255,255,255,.16);border-radius:2px;background:#252a2d;color:#fff;cursor:pointer;text-align:left} +.time-attack-durations button:hover,.time-attack-durations button:focus-visible{border-color:#d9f06f;background:#30382d} +.time-attack-durations button:disabled{opacity:.55;cursor:not-allowed} +.duration-value{grid-area:value;display:flex;align-items:flex-end;gap:3px;color:#eafa9d}.duration-value b{font-size:39px;line-height:.9}.duration-value small{font-size:15px;font-weight:950;line-height:1} +.time-attack-durations button>strong{grid-area:course;font-size:16px;letter-spacing:.08em;color:#f4f7f8} +.duration-status{grid-area:status;color:#b8c2c7;font-size:12px;font-style:normal;font-weight:850;letter-spacing:.04em} +.time-attack-rules{display:grid;grid-template-columns:minmax(150px,.8fr) minmax(0,2.2fr);align-items:center;gap:10px;margin-top:10px;padding:9px 11px;border:1px solid rgba(255,255,255,.1);background:#181c1e} +.time-attack-rules>div b{display:block;color:#eafa9d;font-size:15px}.time-attack-rules>div span{display:block;margin-top:3px;color:#aab4b9;font-size:12px;line-height:1.35} +.time-attack-ladder{display:grid;grid-template-columns:repeat(5,1fr);gap:4px} +.time-attack-ladder span{display:grid;gap:1px;padding:6px 4px;border:1px solid rgba(255,255,255,.08);background:#202527;text-align:center} +.time-attack-ladder small{color:#9ca7ad;font-size:9px}.time-attack-ladder b{color:#fff;font-size:13px}.time-attack-ladder strong{color:#d9f06f;font-size:13px} +.time-attack-clock{margin:13px 0 9px;padding:13px;border:1px solid rgba(217,240,111,.4);background:repeating-linear-gradient(90deg,rgba(217,240,111,.025) 0 9px,transparent 9px 18px),#171b18;color:#f3ffbc;text-align:center;font-size:58px;font-weight:950;line-height:1;font-variant-numeric:tabular-nums;letter-spacing:.08em} +.time-attack-clock.urgent{border-color:rgba(255,110,122,.65);color:#ff9ca5;animation:timeAttackUrgent .5s steps(2,end) infinite} +.time-attack-stats,.time-attack-result-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:7px} +.time-attack-stats span,.time-attack-result-grid span{padding:10px 11px;border:1px solid rgba(255,255,255,.1);background:#191d1f;color:#aeb8bd;font-size:12px;font-weight:900;letter-spacing:.04em} +.time-attack-stats b,.time-attack-result-grid b{display:block;margin-top:3px;color:#f4f6f7;font-size:21px;font-variant-numeric:tabular-nums} +.time-attack-stats span:nth-child(2) b,.time-attack-result-grid span:nth-child(4) b,.time-attack-result-grid span:nth-child(5) b,.time-attack-result-grid span:nth-child(6) b{color:#d9f06f} +#timeAttackShareText{min-height:96px;margin:8px 0 0;padding:11px 13px;border:1px dashed rgba(217,240,111,.28);background:#141719;color:#d2dadd;white-space:pre-wrap;user-select:text;-webkit-user-select:text;font:13px/1.6 var(--dot-font)} +.modal-actions{position:sticky;bottom:-22px;margin:10px -22px -22px;padding:12px 22px 18px;background:linear-gradient(transparent,#1d2124 24%)} +.entry-mode-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px} +.entry-mode-actions .pill{display:grid;gap:3px;min-height:54px;padding:8px 10px;text-align:left} +.entry-mode-actions .pill b{font-size:16px;letter-spacing:.06em} +.entry-mode-actions .pill span{color:#aeb6bb;font-size:11px;font-weight:700} +.entry-mode-actions .primary-mode{border-color:rgba(114,227,143,.42);background:#26352d} +.entry-mode-actions .time-mode{border-color:rgba(95,216,255,.42);background:#24323a} +#modal.entry-choice .panel{border-color:rgba(255,255,255,.22)} +.panel .close{width:100%} +.time-attack-actions{display:flex;gap:8px}.time-attack-actions .pill{flex:1;width:auto} +@keyframes timeAttackTick{50%{opacity:.45}} +@keyframes timeAttackUrgent{to{background-color:#28191c}} +@keyframes timeAttackStartEmphasis{0%,100%{transform:scale(1.18)}50%{transform:scale(1.3)}} +@keyframes numberTurnPulse{to{fill:#ff865f}} +@keyframes numberWarningFrame{to{opacity:.48}} +@keyframes tutorialGridDraw{0%,12%{stroke-dashoffset:1;opacity:.5}58%,82%{stroke-dashoffset:0;opacity:1}100%{stroke-dashoffset:-1;opacity:.25}} +@keyframes tutorialGridNumber{0%,30%,100%{transform:scale(.85);fill:#fff}48%,76%{transform:scale(1.16);fill:#72e38f}} +@keyframes tutorialFill{0%,10%{stroke-dashoffset:1;opacity:.2}48%,82%{stroke-dashoffset:0;opacity:1}100%{stroke-dashoffset:-1;opacity:.18}} +@keyframes tutorialGridShop{0%,18%,100%{transform:scale(.72);opacity:.45}42%,78%{transform:scale(1.08);opacity:1}} +@keyframes tutorialGridScore{0%,35%,100%{opacity:0;transform:translateY(7px)}48%,75%{opacity:1;transform:translateY(0)}} +@media(max-width:700px){ + :root{--bar-height:108px} + #topbar{align-content:center;align-items:center;flex-wrap:wrap;gap:4px 9px;padding-bottom:6px} + .brand{font-size:13px} + .brand small,.stat.area{display:none} + .spacer{display:none} + .toolbar{order:10;width:100%;justify-content:space-between;gap:6px} + .toolbar .pill{flex:1;min-width:0;height:44px;padding:0 4px;font-size:10px} + .toolbar .help{flex:0 0 44px} + #saveStatus{top:calc(var(--safe-top) + var(--bar-height) + 6px)} + #statusPanel{top:calc(var(--bar-height) + var(--safe-top) + 8px)} + #minimap{width:164px;padding:6px;right:calc(7px + var(--safe-right));bottom:calc(7px + var(--safe-bottom))} + #minimapCanvas{height:96px} + .minimap-head{font-size:8px}.minimap-legend{gap:5px;font-size:6px}.minimap-legend span::before{margin-right:2px} + .panel{padding:18px} + .store-item{grid-template-columns:40px minmax(0,1fr)} + .store-item-icon{width:38px;height:38px} + .store-buy{grid-column:1/-1;width:100%} + .inventory-item{grid-template-columns:38px minmax(0,1fr)} + .inventory-item-icon{width:36px;height:36px}.inventory-use{grid-column:1/-1;width:100%} + .time-attack-panel{padding:12px!important}.time-attack-heading h2{font-size:25px}.time-attack-heading>strong{font-size:11px} + .time-attack-intro{font-size:13px!important}.time-attack-durations{grid-template-columns:1fr}.time-attack-durations button{min-height:68px}.duration-value b{font-size:33px} + .time-attack-rules{grid-template-columns:1fr}.time-attack-ladder{grid-template-columns:repeat(5,1fr)}.time-attack-ladder span{padding:5px 2px} + .time-attack-clock{font-size:44px}.time-attack-stats,.time-attack-result-grid{grid-template-columns:repeat(2,1fr)} + .modal-actions{bottom:-18px;margin:10px -18px -18px;padding:12px 18px 16px} +} +@media(max-width:360px){ + .toolbar .pill{min-width:0;font-size:9px} +} + +@media(prefers-reduced-motion:reduce){ + *,*::before,*::after{scroll-behavior:auto!important;transition-duration:.001ms!important} + .board-card.revealing,.board-card.completing .path,.completion-burst,.gate-dot.frontier,.tutorial-demo *,.tutorial-demo::after,.num.turn-warning,.number-turn-warning,.pill.time-attack.active>span:first-child,.time-attack-clock.urgent,.completion-flash,.gate-connect-pulse,.number-match-orbit-holes,#viewport::before{animation:none!important} + .completion-burst{opacity:1} + .tutorial-path{stroke-dashoffset:0} + .tutorial-shop-marker,.tutorial-number-pulse{transform:none} + .tutorial-shop-score{opacity:1} +} +.cloud-status{min-width:72px;font-size:10px;font-weight:900;letter-spacing:.1em} +.cloud-status[data-state="saved"]{border-color:rgba(217,240,111,.42);color:var(--accent)} +.cloud-status[data-state="syncing"]{border-color:rgba(255,255,255,.36);animation:cloudPulse .8s steps(2,end) infinite} +.cloud-status[data-state="error"]{border-color:var(--bad);color:#ffd8dc} +.cloud-status[data-state="local"]{color:var(--muted)} +@keyframes cloudPulse{50%{opacity:.52}} +@media(max-width:700px){.toolbar .cloud-status{flex:0 1 62px;min-width:50px;padding:0 5px;font-size:8px}} + +@media (max-width:560px){.entry-mode-actions{grid-template-columns:1fr}} + +/* v41 gate UI */ +:root{--bar-height:50px} +#topbar{gap:6px;padding:var(--safe-top) calc(4px + var(--safe-right)) 0 calc(4px + var(--safe-left))} +.brand{font-size:17px}.brand small{font-size:11px;margin-left:4px} +.stat{font-size:16px}.toolbar{gap:4px} +.pill{min-width:34px;min-height:34px;padding:0 5px;font-size:12px;gap:4px} +#saveStatus{min-height:32px;padding:0 6px;font-size:11px} +.board-label{padding:1px 3px;font-size:13px;letter-spacing:.035em} +.minimap-head{font-size:11px;margin-bottom:3px}.minimap-legend{font-size:8px;margin-top:3px;gap:6px} +#minimap{padding:5px} +.panel{padding:10px}.panel h2{margin:0 0 6px;font-size:24px}.panel p{font-size:15px;line-height:1.35} +.tutorial-grid{gap:6px}.tutorial-card{padding:5px}.tutorial-card h3{margin:4px 0 1px;font-size:17px}.tutorial-card p{font-size:14px;line-height:1.3} +.control-chips{gap:6px;margin-top:8px}.control-chips span{padding:8px 10px;font-size:13px} +.modal-actions{margin:6px -10px -10px;padding:8px 10px 10px}.entry-mode-actions .pill{min-height:42px;padding:5px 8px}.entry-mode-actions .pill b{font-size:16px} +.store-item,.inventory-item{padding:7px}.store-heading,.inventory-heading,.time-attack-heading{padding-bottom:7px} +#storeMeta,#inventoryTarget{margin:7px 0}.store-buy,.inventory-use{min-height:34px;padding:4px 7px} +#toast{padding:5px 7px;font-size:16px} +#statusPanel{padding:8px}#statusPanel p{margin:0 0 6px;font-size:15px}.status-actions{gap:4px} +@media(max-width:700px){ + :root{--bar-height:48px} + #topbar{gap:3px 5px;padding-bottom:3px} + .brand{font-size:15px}.brand small{font-size:9px}.stat{font-size:14px} + .toolbar .pill{height:38px;min-height:38px;padding:0 3px;font-size:11px} + .panel{padding:8px}.panel h2{font-size:21px}.tutorial-card h3{font-size:15px}.tutorial-card p{font-size:13px} + .modal-actions{margin:6px -8px -8px;padding:7px 8px 9px} +} +/* Completed gate endpoints remain visible on every board so they can be reopened directly. */ +.gate-knob.connected{stroke-width:2.4} + + +/* Low-resolution background noise stays cheaper than board rendering. */ +#noiseCanvas{position:absolute;inset:0;z-index:0;width:100%;height:100%;opacity:.06;pointer-events:none;image-rendering:pixelated;transition:opacity .18s ease} +#world{z-index:2} +.board-actions{display:inline-flex;gap:4px;margin-left:7px;vertical-align:middle;pointer-events:auto} +.board-actions[hidden],.board-card.solved .board-actions{display:none} +.board-action{min-height:25px;padding:2px 7px;border:1px solid rgba(255,255,255,.22);background:rgba(18,29,34,.94);color:#f1f6f7;font:11px/1 var(--dot-font);cursor:pointer;box-shadow:0 3px 10px rgba(0,0,0,.28)} +.board-action:hover{background:#27404a}.board-action:disabled{opacity:.36;cursor:default} +.gate-connect-pulse{fill:none;stroke-width:3.2;vector-effect:non-scaling-stroke;pointer-events:none;animation:gateConnectPulse .68s cubic-bezier(.12,.75,.2,1) forwards} +@keyframes gateConnectPulse{0%{opacity:1;r:7}100%{opacity:0;r:28}} +.board-card.gate-connected .outer-edge{stroke:var(--region);stroke-width:2} +.completion-flash{position:absolute;z-index:26;inset:20px;pointer-events:none;background:radial-gradient(circle,rgba(255,255,255,.34),color-mix(in srgb,var(--region) 28%,transparent) 38%,transparent 72%);animation:completionFlash 1.55s ease-out forwards} +@keyframes completionFlash{0%{opacity:0;transform:scale(.72)}25%{opacity:1}100%{opacity:0;transform:scale(1.12)}} +.completion-burst{border:0!important;background:rgba(10,12,14,.78)!important;box-shadow:0 0 34px var(--region)!important} +.board-svg:focus-visible{outline:none} +.board-actions{position:static} +.obstacle-cell{ + fill:#090b0d; + stroke:#5d6670; + stroke-width:1.4; + vector-effect:non-scaling-stroke; + background:repeating-linear-gradient(135deg,#0a0d0f 0 8px,#1a1f23 8px 12px) +} +.obstacle-cell+*{pointer-events:none} +.obstacle-cross{stroke:#838d96;stroke-width:2.2;vector-effect:non-scaling-stroke;opacity:.8;pointer-events:none} + +/* v47 */ +.endpoint-hit{cursor:grab} +.endpoint-hit:active{cursor:grabbing} +.board-card.has-unfilled-warning .board-label{border-color:#bda98b;color:#e4d8c2} +@keyframes unfilledCellPulse{to{fill:#7b2835!important}} + +/* Special cells */ +.special-cell-layer{pointer-events:none} +.special-cell{opacity:.92;transition:opacity .16s ease} +.special-cell-frame{fill:rgba(11,14,17,.42);stroke:rgba(255,255,255,.72);stroke-width:1.5;vector-effect:non-scaling-stroke} +.special-symbol-line{stroke:#fff;stroke-width:3.2;stroke-linecap:round;vector-effect:non-scaling-stroke} +.special-symbol-core{fill:#171a1d;stroke:#fff;stroke-width:1.5;vector-effect:non-scaling-stroke} +.special-cross .special-cell-frame{fill:rgba(255,174,74,.16);stroke:#ffb55a} +.special-cross.activated{opacity:1} +.special-cross.activated .special-cell-frame{fill:rgba(91,226,142,.22);stroke:#79efa5} +.special-cross.activated .special-symbol-line{stroke:#9affba} +.special-warp .special-cell-frame{fill:rgba(132,102,255,.18);stroke:#a997ff} +.warp-ring{fill:none;stroke:#b8aaff;stroke-width:2.8;stroke-dasharray:4 3;vector-effect:non-scaling-stroke} +.warp-core{fill:#7d67e8;stroke:#e7e1ff;stroke-width:1.4;vector-effect:non-scaling-stroke} +.special-warp.activated{opacity:1} +.special-key .special-cell-frame{fill:rgba(255,210,75,.18);stroke:#ffd45f} +.key-ring{fill:none;stroke:#ffe28a;stroke-width:3.2;vector-effect:non-scaling-stroke}.key-shaft,.key-tooth{stroke:#ffe28a;stroke-width:3.4;stroke-linecap:round;vector-effect:non-scaling-stroke} +.special-key.activated{opacity:1} +.special-key.activated .special-cell-frame{fill:rgba(255,215,75,.3);stroke:#fff0a8} +.special-door .special-cell-frame{fill:rgba(92,158,220,.16);stroke:#78b9ef} +.door-panel{fill:#36556d;stroke:#b8ddff;stroke-width:1.5;vector-effect:non-scaling-stroke} +.door-knob{fill:#fff2a2} +.special-door.unlocked{opacity:1} +.special-door.unlocked .special-cell-frame{fill:rgba(86,211,137,.2);stroke:#78eca5} +.special-door.unlocked .door-panel{fill:#397151;stroke:#b4ffd0} + + +/* v47 interaction and tooltip fixes */ +#specialTooltip{position:fixed;z-index:120;max-width:min(320px,calc(100vw - 20px));padding:7px 9px;border:1px solid rgba(255,255,255,.34);border-left:3px solid var(--accent);background:rgba(12,15,18,.97);color:#f5f7f8;box-shadow:0 8px 24px rgba(0,0,0,.48);font:12px/1.45 var(--dot-font);letter-spacing:.035em;pointer-events:none;white-space:normal} +#specialTooltip[hidden]{display:none} +.board-svg:focus,.board-card:focus{outline:none} + +/* v47.5 interaction polish */ +.board-label>.board-actions{display:inline-flex} +.board-label .board-action{min-height:21px;padding:2px 6px;font-size:9px} +.gate-knob.show{min-width:0} + + +/* v47.10 zoom, score lens, and crossing-state presentation */ +.score-lens-badge{position:absolute;z-index:31;left:50%;top:50%;min-width:86px;padding:5px 8px;transform:translate(-50%,-50%) scale(var(--inverse-camera-scale,1));transform-origin:center;border:1px solid rgba(236,250,168,.9);border-left:3px solid #d9f06f;background:rgba(12,16,14,.94);color:#f5ffbd;box-shadow:0 5px 18px rgba(0,0,0,.42),0 0 12px rgba(217,240,111,.28);font:11px/1.1 var(--dot-font);font-weight:950;letter-spacing:.06em;text-align:center;pointer-events:none;white-space:nowrap} +.score-lens-badge[hidden]{display:none} +.overview-layer{display:none;pointer-events:none} +.overview-fill{fill:#41494e;stroke:rgba(216,228,235,.34);stroke-width:1.4;vector-effect:non-scaling-stroke} +.board-card.solved .overview-fill{fill:#79d4a0;stroke:#365c46} +.world-overview .board-card{pointer-events:none} +.world-overview .board-card .board-label,.world-overview .board-card .solver-badge,.world-overview .board-card .line-store,.world-overview .board-card .static-layer,.world-overview .board-card .special-cell-layer,.world-overview .board-card .connector-layer,.world-overview .board-card .path-layer,.world-overview .board-card .number-layer,.world-overview .board-card .gate-layer{display:none!important} +.world-overview .board-card .overview-layer,.world-overview .board-card.solved .overview-layer{display:block!important} +.world-overview .score-lens-badge{font-size:10px} + +/* v47.13 interaction/reward pass */ +.sr-data{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip:rect(0 0 0 0)!important;white-space:nowrap!important} +.compact-selected{max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +#timeAttackSuggestion{position:fixed;z-index:91;right:calc(18px + var(--safe-right));top:calc(66px + var(--safe-top));display:flex;align-items:center;gap:10px;max-width:min(520px,calc(100vw - 28px));padding:10px 12px;border:1px solid rgba(217,240,111,.62);border-radius:8px;background:rgba(14,20,22,.96);box-shadow:0 10px 34px rgba(0,0,0,.42);color:#eef4f5} +#timeAttackSuggestion[hidden]{display:none}#timeAttackSuggestion>div{display:grid;gap:2px}#timeAttackSuggestion b{color:#d9f06f;font-size:10px;letter-spacing:.12em}#timeAttackSuggestion span{font-size:11px;font-weight:800}#timeAttackSuggestion .quiet{min-width:30px;padding-inline:8px;opacity:.72} +.cell-confirm-flash{fill:rgba(255,255,255,.56);pointer-events:none} +@keyframes cellConfirmFlash{0%{fill:rgba(255,255,255,.45)}100%{fill:transparent}} +body[data-cursor-style="drop-cyan"] #viewport,body[data-cursor-style="drop-cyan"] .board-svg,body[data-cursor-style="drop-cyan"] body[data-cursor-style="drop-cyan"] .gate-hit,body[data-cursor-style="drop-cyan"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='28'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='4'%20y1='4'%20x2='24'%20y2='24'%20gradientUnits='userSpaceOnUse'%3E%3Cstop%20stop-color='%2379f2df'%2F%3E%3Cstop%20offset='1'%20stop-color='%234aa7ff'%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Ccircle%20cx='14'%20cy='14'%20r='8.5'%20fill='url(%23g)'%20stroke='white'%20stroke-width='1.5'%2F%3E%3Ccircle%20cx='14'%20cy='14'%20r='2'%20fill='%23101417'%2F%3E%3C%2Fsvg%3E") 14 14,crosshair} +body[data-cursor-style="drop-rose"] #viewport,body[data-cursor-style="drop-rose"] .board-svg,body[data-cursor-style="drop-rose"] body[data-cursor-style="drop-rose"] .gate-hit,body[data-cursor-style="drop-rose"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='28'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='4'%20y1='4'%20x2='24'%20y2='24'%20gradientUnits='userSpaceOnUse'%3E%3Cstop%20stop-color='%23ffb0c9'%2F%3E%3Cstop%20offset='1'%20stop-color='%23ff5f7f'%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Ccircle%20cx='14'%20cy='14'%20r='8.5'%20fill='url(%23g)'%20stroke='white'%20stroke-width='1.5'%2F%3E%3Ccircle%20cx='14'%20cy='14'%20r='2'%20fill='%23101417'%2F%3E%3C%2Fsvg%3E") 14 14,crosshair} +body[data-cursor-style="ring"] #viewport,body[data-cursor-style="ring"] .board-svg,body[data-cursor-style="ring"] body[data-cursor-style="ring"] .gate-hit,body[data-cursor-style="ring"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='28'%3E%3Ccircle%20cx='14'%20cy='14'%20r='8.5'%20fill='none'%20stroke='white'%20stroke-width='2'%2F%3E%3Ccircle%20cx='14'%20cy='14'%20r='2.2'%20fill='%23d9f06f'%2F%3E%3C%2Fsvg%3E") 14 14,crosshair} +body[data-cursor-style="comet"] #viewport,body[data-cursor-style="comet"] .board-svg,body[data-cursor-style="comet"] body[data-cursor-style="comet"] .gate-hit,body[data-cursor-style="comet"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='28'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='4'%20y1='24'%20x2='22'%20y2='6'%20gradientUnits='userSpaceOnUse'%3E%3Cstop%20stop-color='%236b7cff'%20stop-opacity='.15'%2F%3E%3Cstop%20offset='1'%20stop-color='%23a98cff'%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Cpath%20d='M5%2023L21%207'%20stroke='url(%23g)'%20stroke-width='4'%20stroke-linecap='round'%2F%3E%3Ccircle%20cx='21'%20cy='7'%20r='3.2'%20fill='white'%20stroke='%237c6cff'%20stroke-width='1.4'%2F%3E%3C%2Fsvg%3E") 21 7,crosshair} + + +/* v47.14 radius items, expanded cursor catalog, and item debug mode */ +.debug-items-toggle{display:flex;align-items:center;gap:10px;margin:10px 0 4px;padding:9px 11px;border:1px solid rgba(95,216,255,.22);background:#151b1e;cursor:pointer} +.debug-items-toggle input{width:18px;height:18px;accent-color:#d9f06f}.debug-items-toggle span{display:flex;align-items:baseline;gap:9px}.debug-items-toggle b{color:#d9f06f;font-size:10px;letter-spacing:.14em}.debug-items-toggle small{color:#96a2a8;font-size:9px;font-weight:800} +body[data-debug-items="on"] .debug-items-toggle{border-color:rgba(217,240,111,.55);box-shadow:0 0 18px rgba(217,240,111,.08)} +.inventory-item.debug-available{border-color:rgba(95,216,255,.2)} +body[data-cursor-style="cross-lime"] #viewport,body[data-cursor-style="cross-lime"] .board-svg,body[data-cursor-style="cross-lime"] body[data-cursor-style="cross-lime"] .gate-hit,body[data-cursor-style="cross-lime"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='29'%20height='29'%3E%3Cpath%20d='M14.5%204v6M14.5%2019v6M4%2014.5h6M19%2014.5h6'%20stroke='%23d9f06f'%20stroke-width='1.8'%20stroke-linecap='round'%2F%3E%3Ccircle%20cx='14.5'%20cy='14.5'%20r='3.1'%20fill='%2311161a'%20stroke='white'%20stroke-width='1.4'%2F%3E%3C%2Fsvg%3E") 14 14,crosshair} +body[data-cursor-style="prism"] #viewport,body[data-cursor-style="prism"] .board-svg,body[data-cursor-style="prism"] body[data-cursor-style="prism"] .gate-hit,body[data-cursor-style="prism"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='30'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='6'%20y1='25'%20x2='19'%20y2='4'%20gradientUnits='userSpaceOnUse'%3E%3Cstop%20stop-color='%235fd8ff'%20stop-opacity='.35'%2F%3E%3Cstop%20offset='1'%20stop-color='%23ffffff'%20stop-opacity='.95'%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Cpath%20d='M7%2025L14%204l8%2017-8-3z'%20fill='url(%23g)'%20stroke='white'%20stroke-width='1.4'%20stroke-linejoin='round'%2F%3E%3C%2Fsvg%3E") 14 4,crosshair} +body[data-cursor-style="star-gold"] #viewport,body[data-cursor-style="star-gold"] .board-svg,body[data-cursor-style="star-gold"] body[data-cursor-style="star-gold"] .gate-hit,body[data-cursor-style="star-gold"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='28'%3E%3Cpath%20d='M14%204v5M14%2019v5M4%2014h5M19%2014h5'%20stroke='%23ffd45f'%20stroke-width='2'%20stroke-linecap='round'%2F%3E%3Ccircle%20cx='14'%20cy='14'%20r='4.2'%20fill='%23ffd45f'%20stroke='white'%20stroke-width='1.3'%2F%3E%3Ccircle%20cx='14'%20cy='14'%20r='1.5'%20fill='%2317191b'%2F%3E%3C%2Fsvg%3E") 14 14,crosshair} +body[data-cursor-style="diamond-violet"] #viewport,body[data-cursor-style="diamond-violet"] .board-svg,body[data-cursor-style="diamond-violet"] body[data-cursor-style="diamond-violet"] .gate-hit,body[data-cursor-style="diamond-violet"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='28'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='5'%20y1='5'%20x2='23'%20y2='23'%20gradientUnits='userSpaceOnUse'%3E%3Cstop%20stop-color='%23d7c9ff'%2F%3E%3Cstop%20offset='1'%20stop-color='%237f64e8'%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Cpath%20d='M14%204l10%2010-10%2010L4%2014z'%20fill='url(%23g)'%20stroke='white'%20stroke-width='1.4'%2F%3E%3Ccircle%20cx='14'%20cy='14'%20r='1.7'%20fill='%2317191b'%2F%3E%3C%2Fsvg%3E") 14 14,crosshair} +body[data-cursor-style="orbit-blue"] #viewport,body[data-cursor-style="orbit-blue"] .board-svg,body[data-cursor-style="orbit-blue"] body[data-cursor-style="orbit-blue"] .gate-hit,body[data-cursor-style="orbit-blue"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='30'%20height='30'%3E%3Cellipse%20cx='15'%20cy='15'%20rx='11'%20ry='6.5'%20fill='none'%20stroke='%2372c8ff'%20stroke-width='1.8'%20transform='rotate(-28%2015%2015)'%2F%3E%3Ccircle%20cx='15'%20cy='15'%20r='3.2'%20fill='white'%20stroke='%234d82ff'%20stroke-width='1.4'%2F%3E%3C%2Fsvg%3E") 15 15,crosshair} +body[data-cursor-style="needle"] #viewport,body[data-cursor-style="needle"] .board-svg,body[data-cursor-style="needle"] body[data-cursor-style="needle"] .gate-hit,body[data-cursor-style="needle"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='30'%3E%3Cpath%20d='M5%204l15%2012-7%201-4%208z'%20fill='white'%20stroke='%2315191c'%20stroke-width='1.5'%20stroke-linejoin='round'%2F%3E%3Ccircle%20cx='5'%20cy='4'%20r='1.7'%20fill='%23d9f06f'%2F%3E%3C%2Fsvg%3E") 5 4,crosshair} +body[data-cursor-style="pixel-mint"] #viewport,body[data-cursor-style="pixel-mint"] .board-svg,body[data-cursor-style="pixel-mint"] body[data-cursor-style="pixel-mint"] .gate-hit,body[data-cursor-style="pixel-mint"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='26'%20height='26'%3E%3Crect%20x='6'%20y='6'%20width='14'%20height='14'%20rx='4'%20fill='%2372e3b4'%20stroke='white'%20stroke-width='1.5'%2F%3E%3Ccircle%20cx='13'%20cy='13'%20r='2'%20fill='%23112018'%2F%3E%3C%2Fsvg%3E") 13 13,crosshair} +body[data-cursor-style="flame-orange"] #viewport,body[data-cursor-style="flame-orange"] .board-svg,body[data-cursor-style="flame-orange"] body[data-cursor-style="flame-orange"] .gate-hit,body[data-cursor-style="flame-orange"] .endpoint-hit{cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='28'%20height='30'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='8'%20y1='24'%20x2='18'%20y2='5'%20gradientUnits='userSpaceOnUse'%3E%3Cstop%20stop-color='%23ff9d55'%2F%3E%3Cstop%20offset='1'%20stop-color='%23ffe36c'%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Cpath%20d='M7%2025L14%205l7%2016-7-3z'%20fill='url(%23g)'%20stroke='white'%20stroke-width='1.4'%20stroke-linejoin='round'%2F%3E%3C%2Fsvg%3E") 14 5,crosshair} + +/* Emoji cursors replace the earlier geometric catalog while retaining saved style IDs. */ +body[data-cursor-style="drop-cyan"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F642%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="drop-rose"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F604%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="ring"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F609%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="comet"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F60E%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="cross-lime"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F929%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="prism"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F60B%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="star-gold"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F7E1%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="diamond-violet"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x2B50%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="orbit-blue"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F315%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="needle"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x2600%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="pixel-mint"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F7E8%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-style="flame-orange"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:url("data:image/svg+xml,%3Csvg%20xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width='32'%20height='32'%3E%3Ctext%20x='2'%20y='27'%20font-size='26'%3E%26%23x1F536%3B%3C/text%3E%3C/svg%3E") 16 16,auto} +body[data-cursor-mode="dom"],body[data-cursor-mode="dom"] *{cursor:none!important} +body[data-cursor-mode="native"] :is(#viewport,.board-svg,.gate-hit,.endpoint-hit){cursor:var(--active-native-cursor,auto)!important} +.emoji-glyph,#customEmojiCursor{font-family:var(--emoji-font)!important;font-variant-emoji:emoji} +.item-flag-image{display:block;width:34px;height:26px;object-fit:contain;pointer-events:none} +#customEmojiCursor{position:fixed;z-index:300;left:0;top:0;display:none;width:32px;height:32px;place-items:center;overflow:visible;border:0;border-radius:0;background:transparent;font-size:26px;line-height:1;opacity:1;pointer-events:none;will-change:transform} +#customEmojiCursor.visible{display:grid} +#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden;border:2px solid #101214;border-radius:50%;background:#f4d45f;font-size:26px;opacity:1;box-shadow:0 2px 7px rgba(0,0,0,.5)} +#customEmojiCursor.flag-cursor img{position:absolute;left:50%;top:50%;display:block;width:140%;height:110%;margin:0;transform:translate(-50%,-50%);border-radius:0;object-fit:cover;object-position:center;clip-path:none} +body[data-cursor-mode="native"] #customEmojiCursor{display:none!important} +#pickupHandleOverlay{position:fixed;z-index:299;left:0;top:0;display:none;width:15px;height:15px;border:2px solid #111820;border-radius:50%;background:var(--pickup-color,#f4d45f);box-shadow:0 0 0 2px rgba(255,255,255,.82),0 2px 7px rgba(0,0,0,.48);pointer-events:none;will-change:transform;contain:strict;transition:transform 16.67ms linear} +#pickupHandleOverlay.visible{display:block} + + + +/* v47.36 uses a single route-free viewport canvas for distant world rendering. */ + +/* Nearby LOD keeps only board silhouettes and solved/partial route summaries. */ +.board-card.board-static{z-index:1;overflow:hidden;pointer-events:none;opacity:.9} +.board-static-svg{position:absolute;inset:0;display:block;overflow:hidden;pointer-events:none} +.static-summary-fill{fill:transparent!important;stroke:rgba(225,237,243,.38);stroke-width:1.2;vector-effect:non-scaling-stroke;pointer-events:stroke} +.board-static.solved .static-summary-fill{fill:transparent!important;stroke:rgba(121,212,160,.42)} +.static-summary-path{fill:none;stroke-width:var(--static-line-width,3);stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke;opacity:.9;pointer-events:none} +.static-shop-icon{position:absolute;z-index:3;display:grid;place-items:center;width:38px;height:38px;padding:0;border:0;border-radius:10px;background:rgba(12,18,22,.82);transform:translate(-50%,-50%) scale(var(--inverse-camera-scale));transform-origin:center;box-shadow:0 4px 7px rgba(0,0,0,.55);cursor:pointer;pointer-events:auto} + + +.drag-live-tail{pointer-events:none} +.drag-tip-group{pointer-events:none;will-change:transform;transform-origin:0 0} +body.lightweight-rendering .path{filter:none!important;mix-blend-mode:normal!important} + +/* Performance modes keep input and camera movement ahead of decoration. */ +.drag-layer{pointer-events:none} +.claim-preview-layer{pointer-events:none} +.claim-pending-preview{opacity:.96} +.claim-pending-halo{fill:rgba(255,255,255,.08);stroke:rgba(255,255,255,.92);stroke-width:2;vector-effect:non-scaling-stroke} +.claim-pending-knob{fill:#f4d45f;stroke:#15191d;stroke-width:1.5;vector-effect:non-scaling-stroke} +.gate-knob.drag-target{opacity:1;transform:scale(1)} +#noiseCanvas.interaction-muted{opacity:.035} +body.reduced-effects #noiseCanvas,body.effects-paused #noiseCanvas{opacity:.045;transition:none} +#topbar.drawing-active .brand, +#topbar.drawing-active .stat:not(.score), +#topbar.drawing-active .toolbar>*:not(:focus-within), +#topbar.drawing-active #saveStatus:not(:focus){opacity:.28} +#topbar.drawing-active :focus, +#topbar.drawing-active :focus-within{opacity:1} +#topbar .brand,#topbar .stat,#topbar .toolbar>*,#saveStatus{transition:opacity .14s ease} +.board-card.input-active .path-layer, +.board-card.input-active .gate-layer, +.board-card.input-active .drag-layer{opacity:1;filter:none} +:is(#world.camera-interacting,.board-card.input-active) .gate-dot.frontier, +:is(#world.camera-interacting,.board-card.input-active) .num.turn-warning, +:is(#world.camera-interacting,.board-card.input-active) .number-turn-warning.show, +:is(#world.camera-interacting,.board-card.input-active) .unfilled-warning-cells, +body.reduced-effects .gate-dot.frontier, +body.reduced-effects .num.turn-warning, +body.reduced-effects .number-turn-warning.show, +body.reduced-effects .unfilled-warning-cells{animation-play-state:paused!important;filter:none!important} +@media(prefers-reduced-motion:reduce){ + #topbar .brand,#topbar .stat,#topbar .toolbar>*,#saveStatus{transition:none} +} +:is(#world.camera-interacting,.board-card.input-active) .special-cell, +#world.camera-interacting .overview-fill, +body.reduced-effects .special-cell, +body.reduced-effects .overview-fill{filter:none!important} +body.effects-paused *{animation-play-state:paused!important} + +/* Compact spacing with larger, locally bundled Japanese dot text. */ +#topbar{gap:4px;padding:var(--safe-top) calc(3px + var(--safe-right)) 0 calc(3px + var(--safe-left))} +.brand{font-size:18px}.brand small{font-size:12px}.stat{font-size:17px}.toolbar{gap:3px}.pill{padding:0 3px;font-size:14px;gap:3px} +.board-label{padding:1px 2px;font-size:15px}.num{font-size:30px}.number-warning-caption{font-size:8.4px}.number-turn-warning.multiple .number-warning-caption{font-size:7.1px} +.turn-count{font-size:18px}.line-store small{font-size:8px}.solver-badge{gap:1px;padding:6px 8px}.solver-badge span{font-size:10px}.solver-badge strong{font-size:15px} +#modal,#storeModal,#inventoryModal,#timeAttackModal,#settingsModal{padding:calc(8px + var(--safe-top)) calc(8px + var(--safe-right)) calc(8px + var(--safe-bottom)) calc(8px + var(--safe-left))} +.panel{padding:8px}.panel h2{font-size:25px}.panel p{font-size:16px} +.store-heading,.inventory-heading,.time-attack-heading{padding-bottom:6px}.store-heading small,.inventory-heading small,.time-attack-heading small{font-size:11px} +.store-wallet{padding:5px 7px;font-size:12px}.store-wallet b{font-size:20px}#storeMeta,#inventoryTarget{margin:5px 0;font-size:13px} +.store-inventory{gap:10px}.store-section{gap:5px}.store-section-title{padding-bottom:4px;font-size:15px}.store-item{gap:7px;padding:6px} +.store-item-copy h4{font-size:16px}.store-item-copy strong{font-size:12px}.store-item-copy p{font-size:12px}.store-buy{min-height:34px;padding:4px 6px;font-size:13px} +.store-item.store-cursor .store-buy{font-size:12px}.inventory-list{gap:9px}.inventory-item{gap:7px;padding:6px}.inventory-item h3{font-size:15px}.inventory-item p{font-size:12px}.inventory-use{min-height:34px;font-size:12px} +.inventory-heading>strong{padding:5px 7px;font-size:11px}.inventory-cursor-option{font-size:30px} +.minimap-head{font-size:12px}.minimap-legend{font-size:9px}.minimap-controls button{font-size:9px}.modal-actions{margin:5px -8px -8px;padding:7px 8px 9px} + +@media(max-width:700px){ + .store-cursor-list{grid-template-columns:repeat(4,minmax(66px,1fr))} + .store-item.store-cursor .store-buy{grid-column:auto} +} +@media(max-width:420px){ + .store-cursor-list{grid-template-columns:repeat(3,minmax(62px,1fr))} +} + +/* Shared-world phase 1: compact clear notifications above the minimap. */ +.clear-feed{display:grid;gap:3px;margin:-1px 0 5px;pointer-events:none} +.clear-feed:empty{display:none} +.clear-feed-item{padding:4px 6px;border:1px solid rgba(217,240,111,.28);background:rgba(10,14,16,.92);box-shadow:0 2px 10px rgba(0,0,0,.28);color:#dce5e8;font:8px/1.35 var(--dot-font);font-weight:800;letter-spacing:.02em;animation:clear-feed-in .18s ease-out both} +.clear-feed-item b{color:#fff}.clear-feed-item span{color:#d9f06f} +.clear-feed-item.is-leaving{animation:clear-feed-out .35s ease-in both} +@keyframes clear-feed-in{from{opacity:0;transform:translateY(5px) scale(.98)}to{opacity:1;transform:none}} +@keyframes clear-feed-out{to{opacity:0;transform:translateY(-4px)}} +@media (max-width:640px){.clear-feed-item{font-size:7px;padding:3px 5px}} + + +/* Shared-world phase 2: transient presence and board claims. */ +#presenceCanvas{position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;z-index:25} +.board-claim-badge{position:absolute;z-index:31;left:50%;top:8px;transform:translateX(-50%);max-width:calc(100% - 24px);padding:4px 7px;border:1px solid rgba(255,205,105,.65);background:rgba(19,16,12,.94);color:#ffe2a0;font:10px/1.2 var(--dot-font);font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none;box-shadow:0 3px 12px rgba(0,0,0,.36)} +.board-claim-badge[hidden]{display:none} +.board-card.claimed-other:not(.solved) .board-card.claimed-other:not(.solved) .gate-hit,.board-card.claimed-other:not(.solved) .endpoint-hit{cursor:not-allowed} +.board-card.claimed-other:not(.solved) .static-layer,.board-card.claimed-other:not(.solved) .special-cell-layer,.board-card.claimed-other:not(.solved) .number-layer,.board-card.claimed-other:not(.solved) .gate-layer{opacity:.68} +.board-card.claimed-other:not(.solved)::after{content:"";position:absolute;inset:var(--claim-inset,26px);z-index:29;border:2px dashed rgba(255,205,105,.5);background:rgba(28,20,8,.08);pointer-events:none} +.board-card.claimed-own:not(.solved) .board-label{border-color:rgba(217,240,111,.7)} +.board-static.claimed-other:not(.solved)::before,.board-static.claimed-own:not(.solved)::before{content:attr(data-claim-label);position:absolute;z-index:31;left:50%;top:8px;transform:translateX(-50%) scale(var(--inverse-camera-scale));transform-origin:top center;max-width:calc(100% - 24px);padding:4px 7px;border:1px solid rgba(255,205,105,.65);background:rgba(19,16,12,.94);color:#ffe2a0;font:10px/1.2 var(--dot-font);font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none;box-shadow:0 3px 12px rgba(0,0,0,.36)} +@media(max-width:640px){.board-claim-badge{font-size:8px;padding:3px 5px}} + + +/* Shared-world complete: ephemeral reactions behind boards. */ +#reactionCanvas{position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;z-index:2} +#reactionRadial{position:fixed;z-index:230;width:132px;height:132px;transform:translate(-50%,-50%);pointer-events:none} +#reactionRadial[hidden]{display:none} +#reactionRadial button{position:absolute;left:50%;top:50%;display:grid;place-items:center;width:42px;height:42px;margin:-21px;border:1px solid rgba(255,255,255,.55);border-radius:50%;background:rgba(16,20,22,.94);font-size:24px;line-height:1;pointer-events:auto;transform:translate(var(--rx),var(--ry));transition:transform .1s ease,background .1s ease,border-color .1s ease} +#reactionRadial button:hover,#reactionRadial button.selected{background:rgba(217,240,111,.24);border-color:#d9f06f;transform:translate(var(--rx),var(--ry)) scale(1.14)} +body.reaction-selecting #viewport{cursor:none!important} + +/* v47.71 HUD, input, cursor, and time-attack refinements */ +.fps-stat{position:fixed;z-index:46;left:calc(6px + var(--safe-left));bottom:calc(5px + var(--safe-bottom));min-width:0;padding:0;border:0;background:none;box-shadow:none;color:rgba(200,210,214,.48);font-size:8px;line-height:1;font-variant-numeric:tabular-nums;pointer-events:none;text-shadow:0 1px 2px rgba(0,0,0,.72)} +.fps-stat b{color:inherit;letter-spacing:.02em;font-weight:700} +.unfilled-warning-cells{fill:#3b3430;stroke:#bda98b;stroke-width:1.4;vector-effect:non-scaling-stroke;pointer-events:none} +.board-input-surface{outline:none} +:is(#world.camera-interacting,.board-card.input-active) .unfilled-warning-cells,body.reduced-effects .unfilled-warning-cells{filter:none!important} + +#fpsCounter[data-fps=idle]{color:#aab4b9}#fpsCounter[data-fps=good]{color:#d9f06f}#fpsCounter[data-fps=ok]{color:#ffd46f}#fpsCounter[data-fps=low]{color:#ff7c88} + +/* v47.72 ownership, player identity, and subtle diagnostics */ +.pill.player-name{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-inline:8px;font-size:11px;color:#edf2f3} +.pill.player-name::before{content:'●';color:#d9f06f;font-size:8px} +.endpoint-hit{clip-rule:nonzero} + +/* Settings, board HUD, and shared map presentation. */ +#settingsModal{position:fixed;z-index:200;inset:0;display:grid;place-items:center;padding:calc(14px + var(--safe-top)) calc(14px + var(--safe-right)) calc(14px + var(--safe-bottom)) calc(14px + var(--safe-left));background:rgba(5,7,9,.72)} +#settingsModal[aria-hidden="true"]{display:none} +.settings-panel{width:min(480px,100%)} +.settings-heading{display:flex;align-items:flex-end;justify-content:space-between;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,.13)} +.settings-heading small{color:#8fa0a8;font-size:10px;letter-spacing:.18em}.settings-heading h2{margin:1px 0 0} +.settings-field{display:grid;gap:6px;margin:14px 0}.settings-field>span{color:#dce5e8;font-size:13px;font-weight:900}.settings-field input{width:100%;min-height:42px;padding:7px 10px;border:1px solid rgba(255,255,255,.24);border-radius:2px;background:#101518;color:#fff;font-size:17px;user-select:text;-webkit-user-select:text} +.settings-toggle{display:flex;align-items:center;gap:10px;margin:8px 0;padding:10px;border:1px solid rgba(255,255,255,.14);background:#161b1e;cursor:pointer}.settings-toggle input{width:20px;height:20px;accent-color:#d9f06f}.settings-toggle span{display:grid;gap:2px}.settings-toggle b{font-size:15px}.settings-toggle small{color:#9da8ae;font-size:11px;line-height:1.45} +.pill.settings{font-size:16px} + +.solver-badge{width:calc(100% - 20px);min-width:0;max-width:520px;padding-inline:14px} +.solver-badge strong{display:block;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;word-break:keep-all;overflow-wrap:normal;letter-spacing:.04em} +.board-label[data-side="N"],.board-label[data-side="S"]{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0} +.board-label[data-side="N"]>b,.board-label[data-side="S"]>b{white-space:nowrap} + +body.lightweight-rendering #noiseCanvas{display:none} +body.lightweight-rendering #viewport::after{opacity:.28} +body.lightweight-rendering .completion-flash,body.lightweight-rendering .completion-burst,body.lightweight-rendering .gem-particle{filter:none!important;box-shadow:none!important} +body.lightweight-rendering .number-match-orbit,body.lightweight-rendering .gate-connect-pulse{display:none!important} +body.lightweight-rendering .board-label,body.lightweight-rendering .solver-badge,body.lightweight-rendering #minimap{box-shadow:none!important} +body.lightweight-rendering *{transition-duration:0s!important} + +/* Minimap and distant overview intentionally use solid, borderless cells. */ +#minimapCanvas{image-rendering:auto} + +/* Interaction performance: keep moving layers compositor-only. */ +#world{backface-visibility:hidden;transform-style:flat} +.board-card{isolation:auto} +.board-svg{contain:layout style paint;backface-visibility:hidden} +#presenceCanvas,#reactionCanvas{transform-origin:0 0;will-change:transform;contain:strict} +#viewport.panning::after{display:none} +#viewport.panning .board-label, +#viewport.panning .solver-badge, +#viewport.panning .claim-badge, +#viewport.panning .score-lens-badge, +#viewport.panning .line-store{visibility:hidden!important} +#viewport.panning .board-svg{shape-rendering:optimizeSpeed} +:is(#world.camera-interacting,.board-card.input-active) .number-match-orbit-holes, +:is(#world.camera-interacting,.board-card.input-active) .number-match-pulse, +:is(#world.camera-interacting,.board-card.input-active) .cell-confirm-flash{animation:none!important} +:is(#world.camera-interacting,.board-card.input-active) .gate-knob, +:is(#world.camera-interacting,.board-card.input-active) .solver-badge, +:is(#world.camera-interacting,.board-card.input-active) .line-store{transition:none!important} +body.lightweight-rendering .board-label, +body.lightweight-rendering .solver-badge, +body.lightweight-rendering .claim-badge, +body.lightweight-rendering .line-store, +body.lightweight-rendering .score-lens-badge{box-shadow:none!important;text-shadow:none!important} diff --git a/test/anomaly-smoke-test.js b/test/anomaly-smoke-test.js new file mode 100644 index 0000000..be957a4 --- /dev/null +++ b/test/anomaly-smoke-test.js @@ -0,0 +1,11 @@ +'use strict'; +const {vm,assert,functionSource,loadBendPuzzle,loadAppLogic}=require('./helpers/app-source'); +const BendPuzzle=loadBendPuzzle(),AppLogic=loadAppLogic(); +const context={AppLogic,hash32:BendPuzzle.hash32,rngFrom:BendPuzzle.rngFrom,shuffle:BendPuzzle.shuffle,deepClone:value=>JSON.parse(JSON.stringify(value)),ckey:(r,c)=>`${r},${c}`,sameCell:(a,b)=>a[0]===b[0]&&a[1]===b[1],manhattan:(a,b)=>Math.abs(a[0]-b[0])+Math.abs(a[1]-b[1]),SIDE_D:{N:[-1,0],S:[1,0],W:[0,-1],E:[0,1]}}; +vm.createContext(context); +vm.runInContext(['specialCellSet','warpMap','warpPairForCell','isWarpTransition','gateObj','outsidePoint','analyzeTurns','turnAnalysis','obstacleDetours','obstacleCellLimit','addObstaclePattern'].map(name=>functionSource(name)).join('\n')+'\nthis.logic={obstacleCellLimit,addObstaclePattern};',context); +let obstacleCells=0,allCells=0,boards=0;for(let seed=1;seed<=240;seed++){let puzzle;try{puzzle=BendPuzzle.generatePuzzle([[0,0]],0x330000+seed,3,seed%7,-seed%5)}catch(_){continue}const changed=context.logic.addObstaclePattern(puzzle,0x550000+seed);obstacleCells+=(changed.obstacles||[]).length;allCells+=puzzle.valid.length;boards++} +const obstacleRate=obstacleCells/allCells;assert(boards>200,'Too few obstacle samples');assert(obstacleRate>=.008&&obstacleRate<=.05,`Obstacle rate ${(obstacleRate*100).toFixed(2)}% is implausible`); +let largeObstacleCells=0,largeAllCells=0,largeBoards=0;for(let seed=1;seed<=60;seed++){let puzzle;try{puzzle=BendPuzzle.generatePuzzle([[0,0],[1,0],[0,1],[1,1]],0x660000+seed,5,20,-20)}catch(_){continue}const changed=context.logic.addObstaclePattern(puzzle,0x770000+seed),totalCells=changed.valid.length+(changed.obstacles||[]).length;assert((changed.obstacles||[]).length<=Math.floor(totalCells*.2),'Obstacle generation exceeded 20% of the puzzle');largeObstacleCells+=(changed.obstacles||[]).length;largeAllCells+=totalCells;largeBoards++} +const largeObstacleRate=largeObstacleCells/largeAllCells;assert(largeBoards>=45,'Too few large obstacle samples');assert(largeObstacleRate>obstacleRate+.005,`Large-puzzle obstacle rate ${(largeObstacleRate*100).toFixed(2)}% is not higher than small puzzles`); +console.log(`Obstacle test passed: ${(obstacleRate*100).toFixed(2)}% small and ${(largeObstacleRate*100).toFixed(2)}% large obstacle cells`); diff --git a/test/browser-field-storage-benchmark.js b/test/browser-field-storage-benchmark.js new file mode 100644 index 0000000..61ba973 --- /dev/null +++ b/test/browser-field-storage-benchmark.js @@ -0,0 +1,110 @@ +'use strict'; + +const fs=require('fs'); +const os=require('os'); +const path=require('path'); +const http=require('http'); +const crypto=require('crypto'); +const {spawn,spawnSync}=require('child_process'); +const {assert,root}=require('./helpers/app-source'); + +const browserPath=process.env.BEND_FIELD_BROWSER_PATH||process.env.BEND_FIELD_EDGE_PATH||( + process.platform==='win32'?'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe':'/usr/bin/chromium' +); +const requested=(process.env.BEND_FIELD_SCALE_SIZES||'10000,100000,200000').split(',').map(Number).filter(value=>Number.isSafeInteger(value)&&value>0&&value<=200000); +if(!requested.length)throw new Error('BEND_FIELD_SCALE_SIZES did not contain a supported board count.'); +const worldDbName='bend-field:v30:v47-field-reset-20260728-interaction-fix:world',explicitBenchmarkUrl=process.env.BEND_FIELD_BENCHMARK_URL||'',benchmarkHost=process.env.BEND_FIELD_BENCHMARK_HOST||'localhost',useExtension=process.env.BEND_FIELD_BENCHMARK_EXTENSION==='1'; +const debuggingPort=22000+Math.floor(Math.random()*1000),temporaryRoot=fs.mkdtempSync(path.join(os.tmpdir(),'bend-field-v2-scale-')),profilePath=path.join(temporaryRoot,'browser-profile'); +const sleep=milliseconds=>new Promise(resolve=>setTimeout(resolve,milliseconds)); +function stopBrowserTree(child){ + if(!child?.pid)return; + if(process.platform==='win32'){ + const taskkill=path.join(process.env.SystemRoot||'C:\\Windows','System32','taskkill.exe'), + result=spawnSync(taskkill,['/pid',String(child.pid),'/T','/F'],{stdio:'ignore',windowsHide:true}); + if(result.status!==0&&!child.killed)try{child.kill()}catch(_){} + }else if(!child.killed)child.kill('SIGTERM'); +} + + +function extensionIdFromPublicKey(der){ + const digest=crypto.createHash('sha256').update(der).digest().subarray(0,16); + return[...digest].map(byte=>String.fromCharCode(97+(byte>>4),97+(byte&15))).join(''); +} +function prepareBenchmarkExtension(){ + const directory=path.join(temporaryRoot,'extension');fs.mkdirSync(directory,{recursive:true}); + for(const name of['index.html','style.css','favicon.svg','favicon.ico','puzzle-core.js','app-logic.js','field-persistence.js','field-persistence-worker.js','app.js','puzzle-worker.js'])fs.copyFileSync(path.join(root,name),path.join(directory,name)); + const{publicKey}=crypto.generateKeyPairSync('rsa',{modulusLength:2048}),der=publicKey.export({type:'spki',format:'der'}),key=der.toString('base64'),id=extensionIdFromPublicKey(der); + fs.writeFileSync(path.join(directory,'manifest.json'),JSON.stringify({manifest_version:3,name:'Bend Field V2 Storage Benchmark',version:'47.36.0',key,permissions:['unlimitedStorage']},null,2)); + return{directory,id,url:`chrome-extension://${id}/index.html`}; +} + +class CdpClient{ + constructor(url){this.url=url;this.sequence=0;this.pending=new Map();this.socket=null} + async connect(){ + this.socket=new WebSocket(this.url); + await new Promise((resolve,reject)=>{const timer=setTimeout(()=>reject(new Error('CDP connection timed out')),15000);this.socket.addEventListener('open',()=>{clearTimeout(timer);resolve()},{once:true});this.socket.addEventListener('error',event=>{clearTimeout(timer);reject(event.error||new Error('CDP connection failed'))},{once:true})}); + this.socket.addEventListener('message',event=>{const message=JSON.parse(event.data);if(!message.id)return;const pending=this.pending.get(message.id);if(!pending)return;this.pending.delete(message.id);if(message.error)pending.reject(new Error(`${pending.method}: ${message.error.message}`));else pending.resolve(message.result)}); + } + send(method,params={}){const id=++this.sequence;return new Promise((resolve,reject)=>{this.pending.set(id,{resolve,reject,method});this.socket.send(JSON.stringify({id,method,params}))})} + async evaluate(expression){const response=await this.send('Runtime.evaluate',{expression,awaitPromise:true,returnByValue:true,userGesture:true});if(response.exceptionDetails)throw new Error(response.exceptionDetails.exception?.description||response.exceptionDetails.text||'Browser evaluation failed');return response.result.value} + close(){try{this.socket?.close()}catch(_){}} +} +async function waitFor(check,{timeout=30000,interval=50,label='condition'}={}){const deadline=Date.now()+timeout;let lastError=null;while(Date.now(){const response=await fetch(`http://127.0.0.1:${debuggingPort}/json/list`,{signal:AbortSignal.timeout(1500)});if(!response.ok)return null;return(await response.json()).find(target=>target.type==='page'&&target.webSocketDebuggerUrl)},{timeout:20000,label:'browser DevTools endpoint'})} +async function startServer(){ + const types={'.html':'text/html; charset=utf-8','.js':'text/javascript; charset=utf-8','.css':'text/css; charset=utf-8','.svg':'image/svg+xml','.ttf':'font/ttf','.ico':'image/x-icon'}; + const server=http.createServer((request,response)=>{const pathname=new URL(request.url,'http://localhost').pathname,relative=pathname==='/'?'index.html':decodeURIComponent(pathname.slice(1)),file=path.resolve(root,relative);if(file!==root&&!file.startsWith(root+path.sep)){response.writeHead(403);response.end();return}fs.readFile(file,(error,body)=>{if(error){response.writeHead(error.code==='ENOENT'?404:500);response.end();return}response.writeHead(200,{'content-type':types[path.extname(file)]||'application/octet-stream','cache-control':'no-store'});response.end(body)})}); + await new Promise((resolve,reject)=>{server.once('error',reject);server.listen(0,'127.0.0.1',()=>{server.off('error',reject);resolve()})});return server; +} +const transactionDoneSource=`tx=>new Promise((resolve,reject)=>{tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);tx.onabort=()=>reject(tx.error)})`; +async function waitUntilReady(client,timeout=60000){return waitFor(()=>client.evaluate("document.body?.dataset?.ready==='true'&&globalThis.__bendStartupSnapshot"),{timeout,label:'game startup'})} +async function waitForV2(client){ + return waitFor(()=>client.evaluate(`(async()=>{const db=await new Promise((resolve,reject)=>{const request=indexedDB.open(${JSON.stringify(worldDbName)});request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)});const tx=db.transaction('control','readonly'),request=tx.objectStore('control').get('active'),control=await new Promise((resolve,reject)=>{request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)});db.close();return control?.activeFormat===2&&control.activeEpoch})()`),{timeout:60000,label:'V2 storage migration'}); +} +async function readStarter(client){ + return client.evaluate(`(async()=>{const done=${transactionDoneSource},read=request=>new Promise((resolve,reject)=>{request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}),db=await new Promise((resolve,reject)=>{const request=indexedDB.open(${JSON.stringify(worldDbName)});request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)});let tx=db.transaction('control','readonly'),control=await read(tx.objectStore('control').get('active'));await done(tx);const epoch=control.activeEpoch;tx=db.transaction(['worlds','boardIndex','boardPuzzles','boardStates'],'readonly');const[world,index,puzzle,state]=await Promise.all([read(tx.objectStore('worlds').get(epoch)),read(tx.objectStore('boardIndex').get([epoch,'B0'])),read(tx.objectStore('boardPuzzles').get([epoch,'B0'])),read(tx.objectStore('boardStates').get([epoch,'B0']))]);await done(tx);db.close();return{control,world,index,puzzle,state}})()`); +} +async function seedV2(client,starter,count){ + const expression=`(async()=>{ + const starter=${JSON.stringify(starter)},count=${count},dbName=${JSON.stringify(worldDbName)},batchSize=4000, + done=${transactionDoneSource},open=()=>new Promise((resolve,reject)=>{const request=indexedDB.open(dbName);request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}),db=await open(),epoch=starter.control.activeEpoch; + let tx=db.transaction(['boardIndex','boardPuzzles','boardStates','worlds'],'readwrite');tx.objectStore('boardIndex').clear();tx.objectStore('boardPuzzles').clear();tx.objectStore('boardStates').clear();tx.objectStore('worlds').clear();await done(tx); + const puzzleValue=starter.puzzle.puzzle,stateValue=starter.state.value,started=performance.now(); + for(let offset=0;offsetsetTimeout(resolve,0)); + } + const global={...starter.world.global,worldEpoch:epoch,nextId:count,solved:0,score:0,globalRev:(starter.world.global.globalRev||0)+count+1,globalRevAuthor:'scale-benchmark',selectedBoardId:'B0',cameraAnchor:null,updatedAt:Date.now()},world={...starter.world,epoch,status:'active',global,boardCount:count,solvedCount:0,score:0,bounds:{minX:0,minY:0,maxX:Math.min(1000,count),maxY:Math.ceil(count/1000)},approximateBytes:count*2048,source:{kind:'benchmark'},progress:null}; + tx=db.transaction(['control','worlds'],'readwrite');tx.objectStore('worlds').put(world);tx.objectStore('control').put({...starter.control,key:'active',activeFormat:2,activeEpoch:epoch,previousEpoch:undefined,activationVerified:true});await done(tx);db.close();localStorage.clear();sessionStorage.clear();return{seedMs:performance.now()-started,count}; + })()`; + const result=await client.evaluate(expression);assert(result?.count===count,`Only ${result?.count||0} of ${count} V2 boards were seeded`);return result; +} +async function measure(client,count){ + const navigationStarted=Date.now();await client.send('Page.reload',{ignoreCache:true});const startup=await waitUntilReady(client,180000),readyMs=Date.now()-navigationStarted; + const snapshot=await client.evaluate(`(()=>({capture:globalThis.__bendStartupSnapshot,ready:document.body.dataset.ready,loaded:fieldIndexLoadedCount,expected:fieldIndexExpectedCount,complete:fieldIndexComplete,b0:Boolean(data?.metas?.B0?.puzzle),navigation:performance.getEntriesByType('navigation')[0]?.duration||0,memory:performance.memory?.usedJSHeapSize||null}))()`); + assert(snapshot.ready==='true'&&snapshot.b0,`${count} board V2 startup did not hydrate B0`);assert(snapshot.capture.expected===count,`${count} board V2 startup declared ${snapshot.capture.expected} boards`);assert(snapshot.capture.loaded<=512,`${count} board V2 startup eagerly loaded ${snapshot.capture.loaded} board indexes`);assert(snapshot.capture.complete===false,`${count} board V2 startup completed the full index scan before first paint`); + const scanStarted=Date.now();await waitFor(()=>client.evaluate('fieldIndexComplete===true&&fieldIndexLoadedCount===fieldIndexExpectedCount'),{timeout:600000,interval:100,label:`${count} board index scan`});const scanMs=Date.now()-scanStarted; + const final=await client.evaluate(`({loaded:fieldIndexLoadedCount,expected:fieldIndexExpectedCount,metas:Object.keys(data.metas).length,memory:performance.memory?.usedJSHeapSize||null})`);assert(final.loaded===count&&final.metas===count,`${count} board V2 background index scan was incomplete`); + return{boards:count,readyMs,navigationMs:snapshot.navigation,capturedReadyMs:snapshot.capture.at,initialIndexes:snapshot.capture.loaded,scanMs,heapAtReady:snapshot.memory,heapAfterScan:final.memory}; +} +async function main(){ + if(!fs.existsSync(browserPath))throw new Error(`Browser was not found at ${browserPath}`); + const extension=useExtension?prepareBenchmarkExtension():null;console.log(`Launching ${browserPath}`);const server=await startServer(),port=server.address().port,args=['--headless','--no-first-run','--no-sandbox','--enable-unsafe-swiftshader','--allow-file-access-from-files','--disable-dev-shm-usage','--disable-background-networking',`--remote-debugging-port=${debuggingPort}`,'--remote-allow-origins=*',`--user-data-dir=${profilePath}`,'--window-size=1280,900','about:blank'];if(extension){args.splice(-1,0,`--disable-extensions-except=${extension.directory}`,`--load-extension=${extension.directory}`)}const browser=spawn(browserPath,args,{stdio:'ignore'});let client=null; + try{ + console.log('Waiting for DevTools');const target=await endpoint();console.log('Connecting to page');client=new CdpClient(target.webSocketDebuggerUrl);await client.connect();await client.send('Page.enable');await client.send('Runtime.enable'); + await client.send('Page.addScriptToEvaluateOnNewDocument',{source:`(()=>{const capture=()=>{if(document.body?.dataset?.ready==='true'&&!globalThis.__bendStartupSnapshot)globalThis.__bendStartupSnapshot={at:performance.now(),loaded:typeof fieldIndexLoadedCount==='number'?fieldIndexLoadedCount:null,expected:typeof fieldIndexExpectedCount==='number'?fieldIndexExpectedCount:null,complete:typeof fieldIndexComplete==='boolean'?fieldIndexComplete:null};};const observe=()=>{if(document.documentElement)new MutationObserver(capture).observe(document.documentElement,{subtree:true,attributes:true,attributeFilter:['data-ready']});capture();};if(document.documentElement)observe();else addEventListener('DOMContentLoaded',observe,{once:true})})();`}); + const benchmarkUrl=explicitBenchmarkUrl||(extension?extension.url:`http://${benchmarkHost}:${port}/`);console.log(`Navigating to ${benchmarkUrl}`);await client.send('Page.navigate',{url:benchmarkUrl});try{await waitUntilReady(client,30000)}catch(error){const state=await client.evaluate("({href:location.href,ready:document.body?.dataset?.ready,error:document.body?.dataset?.error,status:document.querySelector('#statusMessage')?.textContent||'',text:document.body?.innerText?.slice(0,800)||'',scripts:[...document.scripts].map(s=>s.src)})").catch(()=>null);throw new Error(`${error.message}: ${JSON.stringify(state)}`)}console.log('Game ready; waiting for V2');await waitForV2(client);console.log('V2 active; reading starter');const starter=await readStarter(client);assert(starter?.index?.id==='B0'&&starter?.puzzle?.puzzle&&starter?.state?.value,'Could not read the V2 starter board'); + const results=[];for(const count of requested){console.log(`Seeding ${count.toLocaleString()} boards`);const seeded=await seedV2(client,starter,count),measured=await measure(client,count),result={...measured,seedMs:seeded.seedMs};results.push(result);console.log(`${count.toLocaleString()} boards | seed ${(seeded.seedMs/1000).toFixed(1)} s | ready ${measured.readyMs} ms (${measured.initialIndexes} indexes) | full scan ${(measured.scanMs/1000).toFixed(1)} s`)} + console.log(`FIELD_STORAGE_BENCHMARK_JSON=${JSON.stringify(results)}`);console.log('V2 field storage scale benchmark passed'); + }finally{ + client?.close();stopBrowserTree(browser); + server.closeAllConnections?.();await new Promise(resolve=>server.close(()=>resolve()));for(let attempt=0;attempt<8;attempt++){try{fs.rmSync(temporaryRoot,{recursive:true,force:true});break}catch(error){if(attempt===7)console.warn(`Benchmark cleanup deferred: ${error.message}`);await sleep(200)}} + } +} +main().then(()=>process.exit(0)).catch(error=>{console.error(error);process.exit(1)}); diff --git a/test/browser-performance-benchmark.js b/test/browser-performance-benchmark.js new file mode 100644 index 0000000..022077c --- /dev/null +++ b/test/browser-performance-benchmark.js @@ -0,0 +1,635 @@ +'use strict'; + +const fs=require('fs'); +const os=require('os'); +const path=require('path'); +const http=require('http'); +const {spawn,spawnSync}=require('child_process'); +const {assert,root}=require('./helpers/app-source'); + +const edgePath=process.env.BEND_FIELD_EDGE_PATH||'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe'; +const sleep=milliseconds=>new Promise(resolve=>setTimeout(resolve,milliseconds)); +let serverPort=0; +const debuggingPort=20000+Math.floor(Math.random()*1000),benchmarkHost=process.env.BEND_FIELD_BENCHMARK_HOST||'localhost'; +const startupOnly=process.env.BEND_FIELD_STARTUP_ONLY==='1'; +const temporaryRoot=fs.mkdtempSync(path.join(os.tmpdir(),'bend-field-browser-benchmark-')); +const profilePath=path.join(temporaryRoot,'edge-profile'); +const worldDbName='bend-field:v30:v47-field-reset-20260728-interaction-fix:world'; +const allProfiles=[ + {name:'small',boards:16,mode:'grid'}, + {name:'medium',boards:128,mode:'grid'}, + {name:'large',boards:512,mode:'grid'}, + {name:'complex',boards:16,mode:'complex',chunks:10}, + {name:'long-line',boards:256,mode:'line'} +]; +const requestedProfile=process.env.BEND_FIELD_BENCHMARK_PROFILE||'',profiles=requestedProfile?allProfiles.filter(profile=>profile.name===requestedProfile):allProfiles; +if(requestedProfile&&!profiles.length)throw new Error(`Unknown benchmark profile: ${requestedProfile}`); +function stopBrowserTree(child){ + if(!child?.pid)return; + if(process.platform==='win32'){ + const taskkill=path.join(process.env.SystemRoot||'C:\\Windows','System32','taskkill.exe'), + result=spawnSync(taskkill,['/pid',String(child.pid),'/T','/F'],{stdio:'ignore',windowsHide:true}); + if(result.status!==0&&!child.killed)try{child.kill()}catch(_){} + }else if(!child.killed)child.kill('SIGTERM'); +} + +class CdpClient{ + constructor(url){this.url=url;this.sequence=0;this.pending=new Map();this.events=[];this.socket=null} + async connect(){ + this.socket=new WebSocket(this.url); + await new Promise((resolve,reject)=>{ + const timer=setTimeout(()=>reject(new Error('CDP WebSocket connection timed out')),10000); + this.socket.addEventListener('open',()=>{clearTimeout(timer);resolve()},{once:true}); + this.socket.addEventListener('error',event=>{clearTimeout(timer);reject(event.error||new Error('CDP WebSocket failed'))},{once:true}); + }); + this.socket.addEventListener('message',event=>{ + const message=JSON.parse(event.data); + if(!message.id){this.events.push(message);if(this.events.length>200)this.events.shift();return} + const pending=this.pending.get(message.id);if(!pending)return;this.pending.delete(message.id); + if(message.error)pending.reject(new Error(`${pending.method}: ${message.error.message}`)); + else pending.resolve(message.result); + }); + } + send(method,params={}){ + const id=++this.sequence; + return new Promise((resolve,reject)=>{ + this.pending.set(id,{resolve,reject,method}); + this.socket.send(JSON.stringify({id,method,params})); + }); + } + async evaluate(expression){ + const response=await this.send('Runtime.evaluate',{expression,awaitPromise:true,returnByValue:true,userGesture:true}); + if(response.exceptionDetails)throw new Error(response.exceptionDetails.exception?.description||response.exceptionDetails.text||'Browser evaluation failed'); + return response.result.value; + } + close(){try{this.socket?.close()}catch(_){}} +} + +async function waitFor(check,{timeout=20000,interval=100,label='condition'}={}){ + const deadline=Date.now()+timeout;let lastError=null; + while(Date.now(){ + const response=await fetch(`http://127.0.0.1:${debuggingPort}/json/list`,{signal:AbortSignal.timeout(1500)}); + if(!response.ok)return null; + const targets=await response.json(); + return targets.find(target=>target.type==='page'&&target.webSocketDebuggerUrl); + },{label:'Edge DevTools endpoint'}); +} + +async function startStaticServer(){ + const types={'.html':'text/html; charset=utf-8','.js':'text/javascript; charset=utf-8','.css':'text/css; charset=utf-8','.svg':'image/svg+xml','.ttf':'font/ttf','.ico':'image/x-icon'}; + const server=http.createServer((request,response)=>{ + const pathname=new URL(request.url,'http://localhost').pathname; + const relative=pathname==='/'?'index.html':decodeURIComponent(pathname.slice(1)); + const file=path.resolve(root,relative); + if(file!==root&&!file.startsWith(root+path.sep)){response.writeHead(403);response.end();return} + fs.readFile(file,(error,body)=>{ + if(error){response.writeHead(error.code==='ENOENT'?404:500);response.end();return} + response.writeHead(200,{'content-type':types[path.extname(file)]||'application/octet-stream','cache-control':'no-store'}); + response.end(body); + }); + }); + await new Promise((resolve,reject)=>{ + server.once('error',reject);server.listen(0,'127.0.0.1',()=>{server.off('error',reject);resolve()}); + }); + serverPort=server.address().port; + return server; +} + +async function ready(client){ + try{ + await waitFor(()=>client.evaluate("document.body?.dataset?.ready==='true'&&Boolean(globalThis.BEND_PERF)"), + {timeout:30000,label:'game startup'}); + }catch(error){ + const state=await client.evaluate("({href:location.href,ready:document.body?.dataset?.ready,hasPerf:Boolean(globalThis.BEND_PERF),status:document.querySelector('#statusMessage')?.textContent||'',text:document.body?.innerText?.slice(0,500)||''})").catch(()=>null); + const exception=client.events.findLast?.(event=>event.method==='Runtime.exceptionThrown')?.params?.exceptionDetails; + const consoleError=client.events.findLast?.(event=>event.method==='Runtime.consoleAPICalled'&&event.params?.type==='error')?.params; + const consoleText=consoleError?.args?.map(argument=>argument.description||argument.value||'').join(' ')||''; + throw new Error(`${error.message}: ${JSON.stringify(state)} ${exception?.exception?.description||exception?.text||consoleText}`); + } + await client.evaluate("document.querySelector('#normalPlayBtn')?.click();true"); + await sleep(350); +} + +async function readStarterRows(client){ + const expression=`(async()=>{ + const db=await new Promise((resolve,reject)=>{const request=indexedDB.open(${JSON.stringify(worldDbName)});request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}); + const read=request=>new Promise((resolve,reject)=>{request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}); + let tx=db.transaction('control','readonly'),control=await read(tx.objectStore('control').get('active')); + await new Promise((resolve,reject)=>{tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);tx.onabort=()=>reject(tx.error)}); + const epoch=control?.activeEpoch;tx=db.transaction(['worlds','boardIndex','boardPuzzles','boardStates'],'readonly'); + const [world,index,puzzle,state]=await Promise.all([ + read(tx.objectStore('worlds').get(epoch)),read(tx.objectStore('boardIndex').get([epoch,'B0'])), + read(tx.objectStore('boardPuzzles').get([epoch,'B0'])),read(tx.objectStore('boardStates').get([epoch,'B0'])) + ]); + await new Promise((resolve,reject)=>{tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);tx.onabort=()=>reject(tx.error)}); + db.close();return{control,world,index,puzzle,state}; + })()`; + return waitFor(async()=>{ + const rows=await client.evaluate(expression); + return rows?.control?.activeFormat===2&&rows?.world?.global&&rows?.index&&rows?.puzzle?.puzzle&&rows?.state?.value?rows:null; + },{timeout:20000,label:'starter IndexedDB rows'}); +} + +async function populateWorld(client,starter,profile){ + const expression=`(async()=>{ + const starter=${JSON.stringify(starter)},profile=${JSON.stringify(profile)},count=profile.boards; + const makeComplexPuzzle=(source,chunkCount)=>{ + const puzzle=structuredClone(source),columns=5,g=[],n=[],valid=[],solution=[]; + for(let chunk=0;chunk[cell[0]+rowOffset,cell[1]+columnOffset])}); + } + puzzle.g=g;puzzle.n=n;puzzle.valid=valid;puzzle.solution=solution;puzzle.bounds={w:Math.min(columns,chunkCount)*5,h:Math.ceil(chunkCount/columns)*5};puzzle.level=10;puzzle.difficulty=10;puzzle.maxTurns=Math.max(...solution.map(path=>path.cells.length));puzzle.totalTurns=solution.reduce((sum,path)=>sum+path.cells.length,0);return puzzle; + }; + const starterPuzzle=starter.puzzle.puzzle,starterState=starter.state.value, + complexPuzzle=profile.mode==='complex'?makeComplexPuzzle(starterPuzzle,profile.chunks||10):null, + complexChunks=profile.mode==='complex'?Array.from({length:profile.chunks||10},(_,index)=>[index%5,Math.floor(index/5)]):null; + const db=await new Promise((resolve,reject)=>{const request=indexedDB.open(${JSON.stringify(worldDbName)});request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}); + const tx=db.transaction(['control','worlds','boardIndex','boardPuzzles','boardStates','outboxV2','recoveryV2','tombstonesV2'],'readwrite'), + indexStore=tx.objectStore('boardIndex'),puzzleStore=tx.objectStore('boardPuzzles'),stateStore=tx.objectStore('boardStates'); + for(const name of ['worlds','boardIndex','boardPuzzles','boardStates','outboxV2','recoveryV2','tombstonesV2'])tx.objectStore(name).clear(); + const columns=Math.ceil(Math.sqrt(count)),spacingX=profile.mode==='complex'?7:2,spacingY=profile.mode==='complex'?4:2; + for(let index=0;index0?[{startGate:0,endGate:3,openGate:null,cells:[[0,0],[0,1],[0,2],[0,3],[0,4]],colorIndex:0,startColorIndex:0,endColorIndex:0}]:[]; + state.solved=false;state.specialProgress={crossings:[]};state.rev=stateRev;state.revAuthor=author; + indexStore.put({epoch:starter.control.activeEpoch,id,number:index,x,y,chunks,level:profile.mode==='complex'?10:starter.index.level,targetLevel:profile.mode==='complex'?10:starter.index.targetLevel,seed:index+1,axis:starter.index.axis,entrySide:null,sealedSides:[],metaRev,stateRev,metaRevAuthor:author,stateRevAuthor:author,revAuthor:author,solved:false,expanded:false,scoreAwarded:0,hasProgress:state.paths.length>0,specialFlags:{crossing:false,warp:false,lock:false},shop:null}); + puzzleStore.put({epoch:starter.control.activeEpoch,id,metaRev,revAuthor:author,generatorVersion:starter.puzzle.generatorVersion,puzzle}); + stateStore.put({epoch:starter.control.activeEpoch,id,stateRev,revAuthor:author,value:state}); + } + const current=structuredClone(starter.world.global),epoch=starter.control.activeEpoch; + current.worldEpoch=epoch;current.nextId=count;current.solved=0;current.score=0;current.bonusScore=0;current.bonusEvents={}; + current.globalRev=300000+count;current.globalRevAuthor='benchmark';current.updatedAt=Date.now();current.clockFloor=Date.now(); + const world={...starter.world,epoch,status:'active',global:current,boardCount:count,solvedCount:0,score:0,bounds:{minX:0,minY:0,maxX:profile.mode==='line'?count:columns*spacingX,maxY:profile.mode==='line'?1:Math.ceil(count/columns)*spacingY},source:{kind:'benchmark'},progress:null}; + tx.objectStore('worlds').put(world);tx.objectStore('control').put({key:'active',activeFormat:2,activeEpoch:epoch,activationVerified:true}); + await new Promise((resolve,reject)=>{tx.oncomplete=resolve;tx.onerror=()=>reject(tx.error);tx.onabort=()=>reject(tx.error)}); + db.close();localStorage.clear();sessionStorage.clear();return{count,solution:profile.mode==='complex'?complexPuzzle.solution:starterPuzzle.solution}; + })()`; + const result=await client.evaluate(expression);assert(result?.count===profile.boards,'Synthetic benchmark world was not written completely');return result.solution; +} + +async function reload(client){ + await client.evaluate("document.body.dataset.ready='reloading';true"); + await client.send('Page.reload',{ignoreCache:true}); + await ready(client); + await client.evaluate("centerMeta(data.metas.B0);ensureBoards();renderAll();true"); + await waitFor(()=>client.evaluate("Boolean(document.querySelector('.board-card[data-id=\"B0\"]:not(.board-static)'))"), + {timeout:15000,label:'interactive origin board'}); + await sleep(500); +} +async function waitForPersistenceIdle(client){ + await client.evaluate("(async()=>{if(hasPendingPersistence())await persistNow({skipCloud:true});return true})()"); + await waitFor(()=>client.evaluate("!hasPendingPersistence()&&!saveTimer"),{timeout:20000,label:'benchmark persistence idle'}); + await client.evaluate("persistQueue.then(()=>true)"); +} +async function prepareInteractionMeasurement(client){ + await waitForPersistenceIdle(client); + await client.evaluate("globalThis.gc?.();true"); + await sleep(120); + await client.evaluate('BEND_PERF.reset();true'); +} +async function restoreOriginState(client,state){ + const restored=await client.evaluate(`(async()=>{ + const board=rendered.get('B0'),snapshot=${JSON.stringify(state)},nextFrame=()=>new Promise(resolve=>requestAnimationFrame(()=>resolve())), + applySnapshot=()=>{ + if(board){cancelBoardDragFrame(board);clearDragRender(board);board.drawing=null;board.armedGate=null;if(board.pendingClaimPointer)clearPendingClaimPointer(board,board.pendingClaimPointer.pointerId)} + realtimeHeldPointers.clear();const current=metaState('B0');replaceObjectContents(current,deepClone(snapshot));normalizedStateObjects.add(current);return current; + }; + applySnapshot();refreshInteractionState();await nextFrame();await new Promise(resolve=>setTimeout(resolve,80)); + const settledPathCount=metaState('B0').paths.length;applySnapshot();changed('B0'); + if(board)renderBoardNow(board);refreshInteractionState();centerMeta(data.metas.B0,{select:false});await nextFrame();await persistNow({skipCloud:true}); + return{pathCount:metaState('B0').paths.length,settledPathCount,solved:metaState('B0').solved,drawing:Boolean(board?.drawing),pending:Boolean(board?.pendingClaimPointer)}; + })()`); + assert(restored.pathCount===(state.paths||[]).length&&restored.solved===Boolean(state.solved)&&!restored.drawing&&!restored.pending,`Canonical origin restore failed: ${JSON.stringify(restored)}`); +} + +async function pointFor(client,selector){ + const point=await client.evaluate(`(()=>{const element=document.querySelector(${JSON.stringify(selector)});if(!element)return null;const rect=element.getBoundingClientRect();return{x:rect.left+rect.width/2,y:rect.top+rect.height/2}})()`); + if(!point)throw new Error(`Missing benchmark target: ${selector}`); + return point; +} +async function pointForCell(client,boardId,row,column){ + const point=await client.evaluate(`(()=>{const board=rendered.get(${JSON.stringify(boardId)});if(!board)return null;const rect=board.svg.getBoundingClientRect(),vb=board.svg.viewBox.baseVal,x=PAD+(${column}+.5)*CELL,y=PAD+(${row}+.5)*CELL;return{x:rect.left+x*rect.width/vb.width,y:rect.top+y*rect.height/vb.height}})()`); + if(!point)throw new Error(`Missing benchmark board cell: ${boardId} ${row},${column}`); + return point; +} +async function pointsForCells(client,boardId,cells){ + const points=await client.evaluate(`(()=>{const board=rendered.get(${JSON.stringify(boardId)}),cells=${JSON.stringify(cells)};if(!board)return null;const rect=board.svg.getBoundingClientRect(),vb=board.svg.viewBox.baseVal;return cells.map(([row,column])=>{const x=PAD+(column+.5)*CELL,y=PAD+(row+.5)*CELL;return{x:rect.left+x*rect.width/vb.width,y:rect.top+y*rect.height/vb.height}})})()`); + if(!points)throw new Error(`Missing benchmark board cells: ${boardId}`); + return points; +} + +async function safePanPoint(client,selector){ + const point=await client.evaluate(`(()=>{const card=document.querySelector(${JSON.stringify(selector)}),view=document.querySelector('#viewport')?.getBoundingClientRect(),rect=card?.getBoundingClientRect();if(!card||!view||!rect)return null;for(const fy of[.18,.32,.5,.68,.82])for(const fx of[.18,.32,.5,.68,.82]){const x=rect.left+rect.width*fx,y=rect.top+rect.height*fy;if(xview.right||yview.bottom)continue;const target=document.elementFromPoint(x,y);if(target?.closest?.(${JSON.stringify(selector)})&&!target.closest?.('button,a,input,select,textarea,[role="button"]'))return{x,y}}return null})()`); + return point||pointFor(client,selector); +} + +async function solveOrigin(client,solution,cpuRate=1){ + for(const pathRow of solution){ + const start=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.startGate}"]`), + end=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.endGate}"]`); + const cellPoints=await pointsForCells(client,'B0',pathRow.cells); + await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button:'left',buttons:1,clickCount:1}); + await sleep(20); + for(const point of cellPoints){ + await client.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:point.x,y:point.y,button:'left',buttons:1}); + await sleep(cpuRate===1?8:16); + } + await client.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:end.x,y:end.y,button:'left',buttons:1}); + await sleep(25*Math.max(1,cpuRate)); + await client.send('Input.dispatchMouseEvent',{type:'mouseReleased',x:end.x,y:end.y,button:'left',buttons:0,clickCount:1}); + await waitFor(()=>client.evaluate("!rendered.get('B0')?.drawing"),{timeout:3000*Math.max(1,cpuRate),label:`origin path ${pathRow.startGate}-${pathRow.endGate} release`}); + await sleep(45*Math.max(1,cpuRate)); + } +} + +async function viewportCenter(client){ + return client.evaluate("(()=>{const r=document.querySelector('#viewport').getBoundingClientRect();return{x:r.left+r.width/2,y:r.top+r.height/2}})()"); +} + +async function pan(client,steps=24,{button='right',selector=null,startPoint=null}={}){ + const center=await viewportCenter(client),start=startPoint||(selector?await safePanPoint(client,selector):{x:center.x+180,y:center.y+80}),buttons=button==='left'?1:2; + await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button,buttons,clickCount:1}); + for(let index=1;index<=steps;index++){ + await client.send('Input.dispatchMouseEvent',{ + type:'mouseMoved',x:start.x-index*9,y:start.y-index*4,button:'none',buttons + }); + await sleep(12); + } + await client.send('Input.dispatchMouseEvent',{type:'mouseReleased',x:start.x-steps*9,y:start.y-steps*4,button,buttons:0,clickCount:1}); + await sleep(250); + return start; +} +async function panCadenceProbe(client,steps=24){ + return client.evaluate(`new Promise(resolve=>{ + const viewport=document.querySelector('#viewport'),rect=viewport.getBoundingClientRect(),pointerId=91,startX=rect.left+rect.width*.7,startY=rect.top+rect.height*.6; + const [worldX,worldY]=worldUnitAtClient(startX,startY),reactionNow=Date.now(),remoteId='benchmark-remote'; + applyRemotePlayer({presenceId:remoteId,playerId:'benchmark',name:'Remote',cursorStyle:'default',x:worldX,y:worldY,vx:.08,vy:.03,sentAt:reactionNow}); + applyRealtimeReaction({id:'benchmark-reaction',emoji:REACTION_EMOJIS[0],x:worldX,y:worldY,createdAt:reactionNow,expiresAt:reactionNow+Math.max(3000,${steps}*25)}); + const dispatch=(type,index,buttons)=>viewport.dispatchEvent(new PointerEvent(type,{bubbles:true,cancelable:true,pointerId,pointerType:'mouse',isPrimary:true,button:type==='pointerdown'||type==='pointerup'?2:-1,buttons,clientX:startX-index*7,clientY:startY-index*3})); + dispatch('pointerdown',0,2);let index=0; + const tick=()=>{index++;dispatch('pointermove',index,2);if(index===10)queueWorldSignal({sessionId:'benchmark-cross-tab',commitId:'benchmark:'+Date.now(),worldEpoch:data.worldEpoch,stateIds:['B1']});if(index<${steps})requestAnimationFrame(tick);else{dispatch('pointerup',index,0);setTimeout(()=>Promise.resolve(syncQueue).finally(()=>{remotePlayers.delete(remoteId);realtimeReactions.delete('benchmark-reaction');resolve(index)}),80)}}; + requestAnimationFrame(tick); + })`); +} +async function pinchZoomProbe(client){ + return client.evaluate(`new Promise(resolve=>{ + const viewport=document.querySelector('#viewport'),rect=viewport.getBoundingClientRect(),cx=rect.left+rect.width/2,cy=rect.top+rect.height/2,before=cam.scale; + const dispatch=(type,pointerId,x)=>viewport.dispatchEvent(new PointerEvent(type,{bubbles:true,cancelable:true,pointerId,pointerType:'touch',isPrimary:pointerId===111,button:type==='pointerdown'?0:-1,buttons:type==='pointerup'?0:1,clientX:x,clientY:cy})); + dispatch('pointerdown',111,cx-45);dispatch('pointerdown',112,cx+45);let frame=0; + const tick=()=>{frame++;dispatch('pointermove',111,cx-45-frame*5);dispatch('pointermove',112,cx+45+frame*5);if(frame<8)requestAnimationFrame(tick);else{dispatch('pointerup',111,cx-85);dispatch('pointerup',112,cx+85);setTimeout(()=>resolve({before,after:cam.scale,changed:Math.abs(cam.scale-before)>.01}),100)}}; + requestAnimationFrame(tick); + })`); +} +async function pickupEdgePanProbe(client,pathRow){ + const originalState=await client.evaluate("deepClone(metaState('B0'))"),start=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.startGate}"]`), + edge=await client.evaluate("(()=>{const r=document.querySelector('#viewport').getBoundingClientRect();return{x:r.right-2,y:r.top+r.height/2}})()"), + before=await client.evaluate("({x:cam.x,y:cam.y,frames:BEND_PERF.snapshot().counters.dragFrames||0})"); + await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button:'left',buttons:1,clickCount:1}); + try{ + await waitFor(()=>client.evaluate("Boolean(rendered.get('B0')?.drawing)"),{timeout:5000,label:'pickup edge-pan activation'}); + await client.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:edge.x,y:edge.y,button:'left',buttons:1});await sleep(350); + const after=await client.evaluate("({x:cam.x,y:cam.y,frames:BEND_PERF.snapshot().counters.dragFrames||0})"); + return{cameraMoved:Math.hypot(after.x-before.x,after.y-before.y)>5,dragFrames:after.frames-before.frames}; + }finally{ + await client.send('Input.dispatchMouseEvent',{type:'mouseReleased',x:edge.x,y:edge.y,button:'left',buttons:0,clickCount:1}).catch(()=>{}); + await client.evaluate(`(()=>{data.states.B0=${JSON.stringify(originalState)};normalizedStateObjects.add(data.states.B0);markStateDirty('B0');renderBoardNow(rendered.get('B0'));return true})()`); + await client.evaluate("(async()=>{await persistNow({skipCloud:true});return true})()"); + } +} +async function pickupCadenceProbe(client,pathRow,steps=120,sampleDelay=8){ + const start=await pointFor(client,`.board-card[data-id="B0"] .gate-hit[data-gate="${pathRow.startGate}"]`), + center=await pointForCell(client,'B0',pathRow.cells[0][0],pathRow.cells[0][1]), + originalState=await client.evaluate("deepClone(metaState('B0'))"); + await client.send('Input.dispatchMouseEvent',{type:'mousePressed',x:start.x,y:start.y,button:'left',buttons:1,clickCount:1}); + try{ + await waitFor(()=>client.evaluate("Boolean(rendered.get('B0')?.drawing)"),{timeout:5000,label:'continuous pickup activation'}); + const pendingMoves=[]; + for(let index=0;index{}); + await sleep(120); + await client.evaluate(`(()=>{data.states.B0=${JSON.stringify(originalState)};normalizedStateObjects.add(data.states.B0);markStateDirty('B0');renderBoardNow(rendered.get('B0'));return true})()`); + await client.evaluate("(async()=>{await persistNow({skipCloud:true});return true})()"); + } +} +async function claimLatencyProbe(client,pathRow,approve){ + return client.evaluate(`(async()=>{ + const board=rendered.get('B0'),state=metaState('B0'),originalState=deepClone(state),originalEnsure=ensureBoardClaimForInput,pointerId=${approve?93:94};let resolveClaim; + const + wait=milliseconds=>new Promise(resolve=>setTimeout(resolve,milliseconds)),nextFrame=()=>new Promise(resolve=>requestAnimationFrame(resolve)); + const cleanup=()=>{cancelBoardDragFrame(board);clearDragRender(board);board.drawing=null;board.armedGate=null;clearPendingClaimPointer(board,pointerId,{release:false});realtimeHeldPointers.delete(pointerId);try{if(board.svg.hasPointerCapture?.(pointerId))board.svg.releasePointerCapture(pointerId)}catch(_){}data.states.B0=originalState;normalizedStateObjects.add(originalState);renderBoardNow(board);refreshInteractionState()}; + try{ + const cells=${JSON.stringify(pathRow.cells)},startGate=${pathRow.startGate},startColor=canonicalGateColorIndex(board.meta,startGate,null); + state.paths=[{startGate,endGate:null,openGate:null,cells:cells.slice(0,2).map(cell=>[...cell]),colorIndex:startColor,startColorIndex:startColor,endColorIndex:null}];board.drawing=null;renderBoardNow(board); + const hit=board.pathLayer.querySelector('.endpoint-hit[data-path-index="0"][data-endpoint-side="end"]'),rect=board.svg.getBoundingClientRect(),vb=board.svg.viewBox.baseVal, + screenPoint=([row,column])=>({clientX:rect.left+(PAD+(column+.5)*CELL)*rect.width/vb.width,clientY:rect.top+(PAD+(row+.5)*CELL)*rect.height/vb.height}), + down=screenPoint(cells[1]),latest=screenPoint(cells[Math.min(2,cells.length-1)]),before=JSON.stringify(state.paths),fullBefore=perfCounters.fullBoardRenders||0; + ensureBoardClaimForInput=()=>new Promise(resolve=>{resolveClaim=resolve}); + hit.dispatchEvent(new PointerEvent('pointerdown',{bubbles:true,cancelable:true,pointerId,pointerType:'mouse',isPrimary:true,button:0,buttons:1,...down})); + await nextFrame();board.svg.dispatchEvent(new PointerEvent('pointermove',{bubbles:true,cancelable:true,pointerId,pointerType:'mouse',isPrimary:true,button:-1,buttons:1,...latest})); + await wait(70);await nextFrame(); + const pending=board.pendingClaimPointer,previewWithinFrame=Boolean(pending?.preview?.getAttribute?.('transform')),tracksLatest=Boolean(pending&&Math.abs(pending.clientX-latest.clientX)<.1&&Math.abs(pending.clientY-latest.clientY)<.1), + modelUntouched=JSON.stringify(state.paths)===before&&!board.drawing; + resolveClaim(${approve});await wait(20);await nextFrame();await nextFrame(); + const approved=Boolean(board.drawing?.pointerId===pointerId),processed=board.lastProcessedPointerMove, + noJump=${approve}?Boolean(processed&&Math.abs(processed.clientX-latest.clientX)<.1&&Math.abs(processed.clientY-latest.clientY)<.1):!board.drawing&&!board.pendingClaimPointer&&JSON.stringify(state.paths)===before, + fullBoardRenders=(perfCounters.fullBoardRenders||0)-fullBefore; + return{previewWithinFrame,tracksLatest,modelUntouched,approved,noJump,fullBoardRenders}; + }finally{ensureBoardClaimForInput=originalEnsure;cleanup()} + })()`); +} + +async function measureCursorModes(client){ + return client.evaluate(`(async()=>{ + const emoji=CURSOR_ITEMS.find(item=>item.cursorEmoji&&!item.flagAsset),flag=CURSOR_ITEMS.find(item=>item.flagAsset),nextFrame=()=>new Promise(resolve=>requestAnimationFrame(resolve)); + try{ + localStorage.removeItem('bend-field-cursor-renderer');syncCursorAppearance('default');const defaultMode=document.body.dataset.cursorMode; + syncCursorAppearance(emoji.cursorStyle);const emojiMode=document.body.dataset.cursorMode; + syncCursorAppearance(flag.cursorStyle);const flagMode=document.body.dataset.cursorMode; + localStorage.setItem('bend-field-cursor-renderer','dom');syncCursorAppearance(emoji.cursorStyle); + const viewport=document.querySelector('#viewport'),cursorEvent='onpointerrawupdate'in window?'pointerrawupdate':'pointermove',first=new PointerEvent(cursorEvent,{bubbles:true,pointerId:95,pointerType:'mouse',clientX:120,clientY:130}); + viewport.dispatchEvent(first);await nextFrame();const firstTransform=customEmojiCursor.style.transform,visibleBeforeDrag=customEmojiCursor.classList.contains('visible'); + document.body.classList.add('is-drawing');viewport.dispatchEvent(new PointerEvent(cursorEvent,{bubbles:true,pointerId:95,pointerType:'mouse',clientX:160,clientY:170}));await nextFrame(); + return{defaultMode,emojiMode,flagMode,domMode:document.body.dataset.cursorMode,visibleBeforeDrag,visibleDuringDrag:customEmojiCursor.classList.contains('visible'),movedDuringDrag:firstTransform!==customEmojiCursor.style.transform}; + }finally{document.body.classList.remove('is-drawing');localStorage.removeItem('bend-field-cursor-renderer');syncCursorAppearance('default')} + })()`); +} +async function measureDisplayCadence(client,frames=90){ + return client.evaluate(`new Promise(resolve=>{ + const gaps=[];let previous=0,count=0; + const tick=timestamp=>{if(previous)gaps.push(timestamp-previous);previous=timestamp;if(++count<${frames})requestAnimationFrame(tick);else{const sorted=gaps.sort((a,b)=>a-b),pick=q=>sorted[Math.floor((sorted.length-1)*q)]||0;resolve({count:gaps.length,p50:pick(.5),p95:pick(.95),max:sorted[sorted.length-1]||0})}}; + requestAnimationFrame(tick); + })`); +} +async function measureCursorCadence(client,steps=180){ + const setup=await client.evaluate(`(()=>{ + const emoji=CURSOR_ITEMS.find(item=>item.cursorEmoji&&!item.flagAsset),rect=document.querySelector('#viewport').getBoundingClientRect(); + localStorage.setItem('bend-field-cursor-renderer','dom');syncCursorAppearance(emoji.cursorStyle);BEND_PERF.reset(); + return{left:rect.left+40,top:rect.top+40,width:Math.max(120,rect.width-80),height:Math.max(120,rect.height-80)}; + })()`); + try{ + for(let index=0;index=30&&gap.p50<=20,`DOM cursor cadence missed acceptance: ${JSON.stringify(gap)}`); + assert(age.count>=25&&age.p95<25,`DOM cursor input age missed acceptance: ${JSON.stringify(age)}`); + return snapshot; + }finally{await client.evaluate("localStorage.removeItem('bend-field-cursor-renderer');syncCursorAppearance('default');true")} +} + +async function zoom(client,deltaY,repetitions){ + const center=await viewportCenter(client); + for(let index=0;index{ + const percentile=(rows,p)=>{const ordered=[...rows].sort((a,b)=>a-b);return ordered[Math.min(ordered.length-1,Math.floor(ordered.length*p))]||0}; + const sample=(operation,count=2000)=>{const rows=[];for(let index=0;indexdirectionFromDelta(CELL*(index%2?.46:.6),index%2?0:CELL*.55)); + const pointerSamples=sample(()=>pointerEventSamples({pointerId:1,pointerType:'pen',getCoalescedEvents:()=>[{clientX:10,clientY:20},{clientX:20,clientY:20},{clientX:20,clientY:10}]})); + const rect=getMinimapRect(),event={clientX:rect.left+rect.width*.5,clientY:rect.top+rect.height*.5}; + const minimap=sample(()=>minimapWorldPoint(event)); + const noise=sample(()=>paintNoiseBackground(false),80); + const controls={ + origin:Boolean(document.querySelector('#minimapOriginBtn')), + random:Boolean(document.querySelector('#minimapRandomBtn')), + unsolved:Boolean(document.querySelector('#minimapUnsolvedBtn')), + current:Boolean(document.querySelector('#minimapCurrentBtn')) + }; + const originalScale=cam.scale;cam.scale=OVERVIEW_ZOOM_THRESHOLD-.001;const overviewBelow=inWorldOverview(); + cam.scale=OVERVIEW_ZOOM_THRESHOLD+.001;const overviewAbove=inWorldOverview();cam.scale=originalScale;inWorldOverview(); + const summaryProbe=document.createElementNS('http://www.w3.org/2000/svg','path');summaryProbe.classList.add('static-summary-fill'); + document.body.append(summaryProbe);const summaryFill=getComputedStyle(summaryProbe).fill;summaryProbe.remove(); + const active=rendered.get(activeBoard);let northHudGap=null; + if(active){positionBoardLabel(active);if(active.label.dataset.side==='N'){const placement=hudPlacementCandidates(active.meta)[0],edgeY=PAD+placement.dy*UNIT;northHudGap=edgeY-parseFloat(active.label.style.top)}} + let heartbeats=0;const heartbeat=setInterval(()=>heartbeats++,0),workerStarted=performance.now(); + const workerResult=await verifyPuzzleUniquenessAsync(data.metas.B0.puzzle,2000); + const workerElapsed=performance.now()-workerStarted;clearInterval(heartbeat); + return{ + snap,pointerSamples,minimap,noise,workerElapsed,heartbeats,workerStatus:workerResult.status, + controls,overviewBelow,overviewAbove,summaryFill,northHudGap, + cellHitCount:document.querySelectorAll('.cell-hit').length, + cellShapeCount:document.querySelectorAll('.board-card .cell-shape').length, + renderedBoardCount:rendered.size, + visibleDetailedCount:[...visibleMetaIds()].filter(id=>data.metas[id]?.puzzle&&rendered.has(id)).length, + visiblePuzzleCount:[...visibleMetaIds()].filter(id=>data.metas[id]?.puzzle).length, + fpsVisible:Boolean(document.querySelector('#fpsCounter')), + difficultyItems:STORE_ITEMS.filter(item=>item.id==='level-min-10'||item.id==='level-max-10').length + }; + })()`); + assert(result.snap.p95<1,`Pointer snap p95 ${result.snap.p95.toFixed(3)} ms exceeded 1 ms`); + assert(result.pointerSamples.p95<1,`Pointer-sample p95 ${result.pointerSamples.p95.toFixed(3)} ms exceeded 1 ms`); + assert(result.minimap.p95<1,`Minimap conversion p95 ${result.minimap.p95.toFixed(3)} ms exceeded 1 ms`); + assert(result.noise.p95<10&&result.noise.max<20,`Noise update p95/max ${result.noise.p95.toFixed(3)}/${result.noise.max.toFixed(3)} ms exceeded the budget`); + assert(result.controls.origin&&result.controls.random&&!result.controls.unsolved&&!result.controls.current,'Minimap teleport controls do not match origin + random'); + assert(result.overviewBelow&&!result.overviewAbove,'World overview retained zoom hysteresis'); + assert(result.summaryFill==='rgba(0, 0, 0, 0)'||result.summaryFill==='transparent',`Nearby board summary still has a filled square (${result.summaryFill})`); + if(result.northHudGap!=null)assert(Math.abs(result.northHudGap-22)<.01,`Top puzzle HUD gap is ${result.northHudGap}, expected 22 world pixels`); + assert(result.cellHitCount===0&&result.cellShapeCount>0,'Detailed boards still allocate one hit node per cell or lack compound cell paths'); + assert(result.visibleDetailedCount===result.visiblePuzzleCount,'A visible valid board was hidden from detailed rendering'); + assert(result.fpsVisible&&result.difficultyItems===0,'FPS display is missing or retired difficulty items remain'); + assert(result.heartbeats>0&&['unique','multiple','unsolved','timeout','invalid'].includes(result.workerStatus),'Uniqueness verification did not yield to the main thread'); + return result; +} +function validateMeasurement(result){ + const {snapshot,cpuRate,profile}=result,cadenceSnapshot=result.pickupCadence||snapshot, + drag=timing(snapshot,'processBoardDragFrame'), + probeDrag=timing(cadenceSnapshot,'processBoardDragFrame'), + camera=timing(snapshot,'commitCameraInteraction'), + dragGap=timing(cadenceSnapshot,'pickupVisualFrameGap'),cameraGap=timing(snapshot,'cameraFrameGap'), + dragAge=timing(cadenceSnapshot,'pickupVisualInputAge'),cameraAge=timing(snapshot,'cameraInputAge'), + minimap=timing(snapshot,'drawMinimap'),ensure=timing(snapshot,'ensureBoards'),save=timing(snapshot,'persistDirtyToDb'), + overview=timing(snapshot,'drawWorldOverview'),mirrorChunk=timing(snapshot,'mirrorChunkWrite'), + dragLimit=cpuRate===1?8:16,minimapLimit=cpuRate===1?20:33,lodLimit=cpuRate===1?40:100; + assert(drag.count+probeDrag.count>=5,`${profile}/${cpuRate}x captured only ${drag.count} functional and ${probeDrag.count} continuous-input drag frames: ${JSON.stringify(result.pickupProbe)}`); + assert(camera.count>=5,`${profile}/${cpuRate}x did not capture frame-coalesced camera work`); + assert(minimap.count>=2,`${profile}/${cpuRate}x did not capture minimap work`); + assert(ensure.count>=2,`${profile}/${cpuRate}x did not capture LOD work`); + assert(save.count>=1,`${profile}/${cpuRate}x did not capture an autosave`); + assert(overview.count>=1,`${profile}/${cpuRate}x did not capture overview rendering`); + assert(cameraGap.count>=5&&(cpuRate!==1||dragGap.count>=4),`${profile}/${cpuRate}x did not capture enough real interaction cadence samples (pickup ${dragGap.count}, camera ${cameraGap.count})`); + if(cpuRate===1){ + const approved=result.claimApproved,denied=result.claimDenied; + assert(approved?.previewWithinFrame&&approved.tracksLatest&&approved.modelUntouched&&approved.approved&&approved.noJump&&approved.fullBoardRenders===0,`${profile} claim approval preview/commit failed: ${JSON.stringify(approved)}`); + assert(denied?.previewWithinFrame&&denied.tracksLatest&&denied.modelUntouched&&!denied.approved&&denied.noJump&&denied.fullBoardRenders===0,`${profile} claim denial rollback failed: ${JSON.stringify(denied)}`); + assert(result.edgePan?.cameraMoved&&result.edgePan.dragFrames>=2,`${profile} pickup edge-pan did not retain continuous drag frames: ${JSON.stringify(result.edgePan)}`); + } + assert(result.pinch?.changed,`${profile}/${cpuRate}x pinch zoom did not change camera scale: ${JSON.stringify(result.pinch)}`); + assert(result.overviewPathsObserved===0,`${profile}/${cpuRate}x far overview rendered route lines`); + assert((snapshot.counters.overviewCacheBuilds||0)>result.overviewBuildBaseline,`${profile}/${cpuRate}x long overview pan did not rebuild the exhausted cache after settlement`); + if(cpuRate===1){ + const cadenceHot=Object.entries(cadenceSnapshot.timings||{}).filter(([,value])=>value.max>1).sort((a,b)=>b[1].max-a[1].max).slice(0,12); + assert(dragGap.p50<=18&&dragGap.p95<=22,`${profile} pickup visual median/p95 gap ${dragGap.p50.toFixed(2)}/${dragGap.p95.toFixed(2)} ms exceeded the capped 60 Hz cadence budget; active timings ${JSON.stringify(cadenceHot)}`); + assert(cameraGap.p50<=20,`${profile} camera median gap ${cameraGap.p50.toFixed(2)} ms exceeded 20 ms`); + assert(dragAge.p95<30,`${profile} pickup input age ${dragAge.p95.toFixed(2)} ms exceeded 30 ms`); + assert(cameraAge.p95<25,`${profile} camera input age ${cameraAge.p95.toFixed(2)} ms exceeded 25 ms`); + } + assert(Math.max(drag.p95,probeDrag.p95)<=dragLimit,`${profile}/${cpuRate}x drag p95 ${Math.max(drag.p95,probeDrag.p95).toFixed(2)} ms exceeded acceptance`); + assert(camera.p95<=dragLimit,`${profile}/${cpuRate}x camera p95 ${camera.p95.toFixed(2)} ms exceeded acceptance`); + assert(minimap.p95<=minimapLimit,`${profile}/${cpuRate}x minimap p95 ${minimap.p95.toFixed(2)} ms exceeded acceptance`); + assert(ensure.p95<=lodLimit,`${profile}/${cpuRate}x LOD p95 ${ensure.p95.toFixed(2)} ms exceeded acceptance`); + if(mirrorChunk.count)assert(mirrorChunk.max<50,`${profile}/${cpuRate}x mirror chunk write ${mirrorChunk.max.toFixed(2)} ms became a long task`); + if(cpuRate===1){ + const modelWork=timing(snapshot,'pickupModelWork'),visualWork=timing(snapshot,'pickupVisualWork'),dragRender=timing(snapshot,'renderDragFrame'); + assert(result.pickupProbeLongTasks===0&&result.pickupLongTasks===0&&result.panLongTasks===0,`${profile} recorded a 50 ms long task during continuous pickup (${result.pickupProbeLongTasks}), real pickup (${result.pickupLongTasks}), or the ten-second pan (${result.panLongTasks}); last ${snapshot.gauges.lastInteractionLongTaskMs||0} ms; phases preview ${timing(snapshot,'pickupPointerDownPreview').max.toFixed(1)}, commit ${timing(snapshot,'pickupPointerDownCommit').max.toFixed(1)}, finish ${timing(snapshot,'pickupPointerFinish').max.toFixed(1)}, drag ${drag.max.toFixed(1)}, model ${modelWork.max.toFixed(1)}, visual ${visualWork.max.toFixed(1)}, render ${dragRender.max.toFixed(1)}; ${snapshot.gauges.lastInteractionLoafScripts||'no LoAF attribution'}`); + } + for(const name of ['minimapDrawsDuringInteraction','lodPassesDuringInteraction','overviewBuildsDuringInteraction','persistenceDuringInteraction','worldRefreshesDuringInteraction']) + assert((snapshot.counters[name]||0)===0&&(cadenceSnapshot.counters[name]||0)===0,`${profile}/${cpuRate}x ran ${name} during an active gesture`); + assert((snapshot.counters.worldRefreshesDeferredDuringInteraction||0)>=1,`${profile}/${cpuRate}x did not defer the injected cross-tab refresh until gesture settlement`); + assert(snapshot.gauges.renderedBoards>=snapshot.gauges.visibleUnsolvedBoards,`${profile}/${cpuRate}x omitted a visible unsolved board from detailed rendering`); + assert((cadenceSnapshot.rates.dragFramesPerSecond||0)<=65,`${profile}/${cpuRate}x pickup presentation exceeded the 60 FPS ceiling (${(cadenceSnapshot.rates.dragFramesPerSecond||0).toFixed(1)} FPS)`); + assert(snapshot.gauges.domNodes<18000,`${profile}/${cpuRate}x DOM size is not viewport-bounded`); +} + +async function measureScenario(client,starter,profile,cpuRate){ + await client.send('Emulation.setCPUThrottlingRate',{rate:1}); + await client.evaluate("if(typeof lifecyclePersistenceSuppressed!=='undefined')lifecyclePersistenceSuppressed=true;true"); + const solution=await populateWorld(client,starter,profile); + await reload(client); + const pristineOriginState=await client.evaluate("deepClone(metaState('B0'))"); + await client.send('Emulation.setCPUThrottlingRate',{rate:cpuRate}); + const claimApproved=cpuRate===1?await claimLatencyProbe(client,solution[0],true):null; + if(cpuRate===1)await restoreOriginState(client,pristineOriginState); + const claimDenied=cpuRate===1?await claimLatencyProbe(client,solution[0],false):null; + if(cpuRate===1)await restoreOriginState(client,pristineOriginState); + const edgePan=cpuRate===1?await pickupEdgePanProbe(client,solution[0]):null; + if(cpuRate===1)await restoreOriginState(client,pristineOriginState); + if(cpuRate===1)await sleep(300); + await prepareInteractionMeasurement(client); + const pickupProbe=await pickupCadenceProbe(client,solution[0],cpuRate===1?180:120,cpuRate===1?8:16); + const pickupCadence=pickupProbe.snapshot;delete pickupProbe.snapshot; + const pickupProbeLongTasks=pickupCadence?.counters.interactionLongTasks||0; + await restoreOriginState(client,pristineOriginState); + await sleep(300); + await prepareInteractionMeasurement(client); + const preSolveState=await client.evaluate("({pathCount:metaState('B0').paths.length,solved:metaState('B0').solved,drawing:Boolean(rendered.get('B0')?.drawing),pending:Boolean(rendered.get('B0')?.pendingClaimPointer)})"); + assert(preSolveState.pathCount===0&&!preSolveState.solved&&!preSolveState.drawing&&!preSolveState.pending,`${profile.name}/${cpuRate}x pickup probes did not restore a pristine origin board: ${JSON.stringify(preSolveState)}`); + await solveOrigin(client,solution,cpuRate); + try{await waitFor(()=>client.evaluate("metaState('B0').solved===true"),{timeout:5000*Math.max(1,cpuRate),label:`${profile.name}/${cpuRate}x solved origin`})} + catch(error){const diagnostic=await client.evaluate("(()=>{const state=metaState('B0'),board=rendered.get('B0');return{solved:state.solved,paths:state.paths.map(path=>({startGate:path.startGate,endGate:path.endGate,openGate:path.openGate,cells:path.cells})),drawing:board?.drawing?{pathIndex:board.drawing.pathIndex,catchupPending:board.drawing.catchupPending}:null,queued:board?.pointerMoveSamples?.length||0,logicalTrace:board?.logicalPointerCellTrace||[],flushTrace:board?.lastFlushPointerCells||[],staticBoard:staticRendered.has('B0')}})()");throw new Error(`${error.message}: ${JSON.stringify(diagnostic)}`)} + await sleep(850); + const pickupLongTasks=await client.evaluate('BEND_PERF.snapshot().counters.interactionLongTasks||0'); + await client.evaluate('finishCompletionVisual("B0",true);centerMeta(data.metas.B0);true');await sleep(350); + const leftStart=await safePanPoint(client,'.board-card[data-id="B0"]'), + leftProbe=await client.evaluate(`(()=>{const target=document.elementFromPoint(${leftStart.x},${leftStart.y});return{tag:target?.tagName||null,classes:target?.getAttribute?.('class')||null,board:target?.closest?.('.board-card')?.dataset?.id||null,allowed:leftFieldPanAllowed({button:0,target})}})()`); + const beforeLeftPan=await client.evaluate('({x:cam.x,y:cam.y,solved:metaState("B0").solved})'); + assert(beforeLeftPan.solved&&leftProbe.allowed,`${profile.name}/${cpuRate}x solved-board left drag is not eligible for panning: ${JSON.stringify({leftStart,leftProbe,beforeLeftPan})}`); + await zoom(client,180,15); + const overviewPathsObserved=await client.evaluate('drawWorldOverview();BEND_PERF.snapshot().gauges.overviewPaths||0'); + const overviewBuildBaseline=await client.evaluate('BEND_PERF.snapshot().counters.overviewCacheBuilds||0'); + const panLongTaskBaseline=await client.evaluate('BEND_PERF.snapshot().counters.interactionLongTasks||0'); + await pan(client); + await panCadenceProbe(client,cpuRate===1?600:60); + await waitFor(()=>client.evaluate(`(()=>{if(!inWorldOverview()||!overviewCache)return false;const[centerX,centerY]=cameraCenterInChunks();return!overviewDirty&&Math.abs(centerX-overviewCache.anchorX)*overviewCache.unit<=overviewCache.overscan*.82&&Math.abs(centerY-overviewCache.anchorY)*overviewCache.unit<=overviewCache.overscan*.82})()`),{timeout:10000,label:'settled overview cache rebuild'}); + const pinch=await pinchZoomProbe(client); + await sleep(80); + const panLongTasks=(await client.evaluate('BEND_PERF.snapshot().counters.interactionLongTasks||0'))-panLongTaskBaseline; + await zoom(client,-180,15); + await sleep(650); + try{await waitFor(()=>client.evaluate("!hasPendingPersistence()"),{timeout:20000,label:'durable persistence drain'})} + catch(error){ + const diagnostic=await client.evaluate(`({globalDirty,dirtyMetaIds:[...dirtyMetaIds],dirtyStateIds:[...dirtyStateIds],deletedBoardIds:[...deletedBoardIds],cloudOutboxDeleteKeys:[...cloudOutboxDeleteKeys],saveTimer:Boolean(saveTimer),status:saveStatusEl?.dataset?.state||null,message:statusMessage?.textContent||null,storageFormat:activeStorageFormat,epoch:data?.worldEpoch})`); + throw new Error(`${error.message}: ${JSON.stringify(diagnostic)}`); + } + await sleep(500); + const snapshot=await client.evaluate('BEND_PERF.snapshot()'); + const result={profile:profile.name,boards:profile.boards,cpuRate,snapshot,pickupCadence,pickupProbe,overviewPathsObserved,overviewBuildBaseline,pickupProbeLongTasks,pickupLongTasks,panLongTasks,claimApproved,claimDenied,edgePan,pinch}; + validateMeasurement(result); + return result; +} + +async function main(runProfiles=profiles,cpuRates=[1,4]){ + if(!fs.existsSync(edgePath))throw new Error(`Microsoft Edge was not found at ${edgePath}`); + const server=await startStaticServer(); + const edge=spawn(edgePath,[ + '--headless','--no-first-run','--no-sandbox','--force-device-scale-factor=1','--js-flags=--expose-gc','--disable-dev-shm-usage','--disable-extensions','--disable-background-networking','--disable-background-timer-throttling','--disable-renderer-backgrounding','--disable-backgrounding-occluded-windows','--disable-crash-reporter','--disable-breakpad','--disable-component-update','--disable-features=OptimizationGuideModelDownloading,OnDeviceModelService,PromptApiForGeminiNano', + `--remote-debugging-port=${debuggingPort}`,'--remote-allow-origins=*',`--user-data-dir=${profilePath}`, + `--host-resolver-rules=MAP ${benchmarkHost} 127.0.0.1`,'--window-size=1440,1000','about:blank' + ],{stdio:'ignore',windowsHide:true}); + let client=null; + try{ + const target=await endpoint();client=new CdpClient(target.webSocketDebuggerUrl);await client.connect(); + await client.send('Page.enable');await client.send('Runtime.enable'); + await client.send('Page.navigate',{url:`http://${benchmarkHost}:${serverPort}/`}); + if(startupOnly){ + await sleep(12000); + const state=await client.evaluate("({ready:document.body?.dataset?.ready||null,version:document.querySelector('.brand small')?.textContent||null,boards:typeof data!=='undefined'?Object.keys(data.metas).length:null,origin:typeof data!=='undefined'&&Boolean(data.metas?.B0?.puzzle),worldGeneration:typeof data!=='undefined'?data.worldGeneration:null,turnFont:getComputedStyle(document.querySelector('.board-svg text')||document.body).fontFamily,status:document.querySelector('#statusMessage')?.textContent||'',text:document.body?.innerText?.slice(0,300)||''})"); + assert(state.ready==='true'&&state.version==='v47.77'&&state.boards===1&&state.origin&&state.worldGeneration==='v47-field-reset-20260728-interaction-fix',`Real-browser startup state is incomplete: ${JSON.stringify(state)}`); + assert(/DotGothic16|Press Start 2P|MS Gothic|monospace/i.test(state.turnFont),'Dot-styled game font is not active in the browser'); + console.log(`Real-browser startup passed: ${JSON.stringify(state)}`);return; + } + await ready(client);await sleep(1000); + const displayCadence=await measureDisplayCadence(client); + assert(displayCadence.count>=60&&displayCadence.p50<=20,`Headless display baseline is not 60 Hz: ${JSON.stringify(displayCadence)}`); + const gameplayBudgets=await measureGameplaySimplificationBudgets(client); + const cursorModes=await measureCursorModes(client); + assert(cursorModes.defaultMode==='default'&&cursorModes.emojiMode==='native'&&cursorModes.flagMode==='native'&&cursorModes.domMode==='dom'&&cursorModes.visibleBeforeDrag&&cursorModes.visibleDuringDrag&&cursorModes.movedDuringDrag,`Cursor mode runtime probe failed: ${JSON.stringify(cursorModes)}`); + const cursorCadence=await measureCursorCadence(client); + console.log(`Display cadence | median ${displayCadence.p50.toFixed(2)} ms | p95 ${displayCadence.p95.toFixed(2)} ms`); + console.log(`Gameplay budgets | snap ${gameplayBudgets.snap.p95.toFixed(3)} ms | pointer samples ${gameplayBudgets.pointerSamples.p95.toFixed(3)} ms | minimap ${gameplayBudgets.minimap.p95.toFixed(3)} ms | noise max ${gameplayBudgets.noise.max.toFixed(3)} ms | worker ${gameplayBudgets.workerElapsed.toFixed(1)} ms`); + console.log(`Cursor cadence | median gap ${timing(cursorCadence,'cursorFrameGap').p50.toFixed(2)} ms | input p95 ${timing(cursorCadence,'cursorInputAge').p95.toFixed(2)} ms`); + const starter=await readStarterRows(client),results=[]; + for(const profile of runProfiles)for(const cpuRate of cpuRates){ + const result=await measureScenario(client,starter,profile,cpuRate);results.push(result); + const drag=timing(result.snapshot,'processBoardDragFrame'),camera=timing(result.snapshot,'commitCameraInteraction'),minimap=timing(result.snapshot,'drawMinimap'), + ensure=timing(result.snapshot,'ensureBoards'),save=timing(result.snapshot,'persistDirtyToDb'); + console.log(`${profile.name.padEnd(6)} ${cpuRate}x CPU | drag p95 ${drag.p95.toFixed(2)} ms | camera ${camera.p95.toFixed(2)} ms | minimap ${minimap.p95.toFixed(2)} ms | LOD ${ensure.p95.toFixed(2)} ms | save ${save.p95.toFixed(2)} ms | DOM ${result.snapshot.gauges.domNodes}`); + } + console.log(`BROWSER_BENCHMARK_JSON=${JSON.stringify(results.map(result=>({ + profile:result.profile,boards:result.boards,cpuRate:result.cpuRate, + dragP95:timing(result.snapshot,'processBoardDragFrame').p95, + cameraP95:timing(result.snapshot,'commitCameraInteraction').p95, + dragGapP95:timing(result.pickupCadence||result.snapshot,'pickupVisualFrameGap').p95, + dragInputAgeP95:timing(result.pickupCadence||result.snapshot,'pickupVisualInputAge').p95, + cameraGapP50:timing(result.snapshot,'cameraFrameGap').p50, + cameraInputAgeP95:timing(result.snapshot,'cameraInputAge').p95, + minimapP95:timing(result.snapshot,'drawMinimap').p95, + lodP95:timing(result.snapshot,'ensureBoards').p95, + saveP95:timing(result.snapshot,'persistDirtyToDb').p95, + overviewP95:timing(result.snapshot,'drawWorldOverview').p95, + renderedBoards:result.snapshot.gauges.renderedBoards, + staticBoards:result.snapshot.gauges.staticBoards, + domNodes:result.snapshot.gauges.domNodes, + longTasks:result.snapshot.counters.longTasks||0 + })))}`); + console.log('Real-browser performance benchmark passed'); + }finally{ + client?.close(); + stopBrowserTree(edge); + server.closeAllConnections?.();await new Promise(resolve=>server.close(()=>resolve())); + for(let attempt=0;attempt<8;attempt++){ + try{fs.rmSync(temporaryRoot,{recursive:true,force:true});break} + catch(error){if(attempt===7)console.warn(`Benchmark cleanup deferred: ${error.message}`);await sleep(150)} + } + } +} + +module.exports={main,allProfiles}; +if(require.main===module)main().then(()=>process.exit(0)).catch(error=>{console.error(error);process.exit(1)}); diff --git a/test/cleanup-performance-smoke-test.js b/test/cleanup-performance-smoke-test.js new file mode 100644 index 0000000..a22c83a --- /dev/null +++ b/test/cleanup-performance-smoke-test.js @@ -0,0 +1,54 @@ +'use strict'; +const assert=require('assert/strict'); +const fs=require('fs'); +const path=require('path'); +const {root,app,html,css,functionSource,read}=require('./helpers/app-source'); +const server=read('server.js'),realtime=read('realtime-server.js'),catalog=JSON.parse(read('store-catalog.json')); + +// Retired difficulty-adjustment system must be absent from every runtime layer. +assert.equal(catalog.some(item=>item.id==='level-min-10'||item.id==='level-max-10'||item.targetMode||item.fieldRadius),false); +for(const [name,source] of [['app',app],['server',server],['realtime',realtime],['html',html]]){ + assert(!source.includes('fieldOverlayCanvas'),`${name}: field overlay remains`); + assert(!source.includes('level-min-10')&&!source.includes('level-max-10'),`${name}: retired item id remains`); + assert(!source.includes('fieldAdjusted')&&!source.includes('generatedDifficulty'),`${name}: retired difficulty metadata remains`); +} +assert(!server.includes("url.pathname==='/api/player/place-field'")&&!realtime.includes('broadcastFieldEffect')); +assert(!app.includes('invalidateFieldOverlay')&&!app.includes('invalidateStoreEffectCache')); +assert.equal(catalog.length>0,true);assert.equal(catalog.filter(item=>item.id==='score-lens').length,1); + +// Clicking a board must select/input only; camera navigation is explicit elsewhere. +assert(functionSource('shouldTeleportToUnsolvedBoard').includes('return false')); +assert(!functionSource('bindBoard').includes('centerMeta(')); +assert(!app.includes('function promoteStaticBoard(')); +assert(functionSource('makeStaticBoard').includes('card.tabIndex=-1')&&!functionSource('makeStaticBoard').includes("setAttribute('role','button')")); + +// Every hydrated puzzle in the visible field remains fully detailed. +assert(functionSource('desiredInteractiveBoardIds').includes('for(const id of ids)')); +assert(!app.includes('INTERACTIVE_DETAIL_CELL_BUDGET')&&!functionSource('ensureBoards').includes('makeStaticBoard(meta)')); +const makeBoard=functionSource('makeBoard'); +assert(makeBoard.includes('boardCellsPath(p.valid)')&&makeBoard.includes("class:'cell-shape'")); +assert(!makeBoard.includes('cellHits.set')&&!makeBoard.includes("class:'cell-hit'")); +assert(functionSource('drawOuterEdges').includes("svgEl('path'")); +assert(functionSource('flashConfirmedCell').includes("svgEl('rect'")&&functionSource('flashConfirmedCell').includes('node.remove()')); + +// Work and memory are bounded without hiding visible boards. +assert(app.includes('LOD_CHANGES_PER_PASS=1')&&app.includes('BOARD_RENDERS_PER_FRAME=6')&&app.includes('HYDRATE_CONCURRENCY=4')); +assert(functionSource('evictHydratedBoardDetails').includes('hydratedBoardLru.size>32')); +assert(functionSource('evictHydratedBoardDetails').includes('hydratedBoardBytes>8*1024*1024')); +assert(functionSource('drawPresenceLayer').includes('dpr=1')&&functionSource('drawReactionLayer').includes('dpr=1')); +assert(functionSource('scheduleNoiseBackground').includes('noisePainted')&&!functionSource('scheduleNoiseBackground').includes('setTimeout')); +assert(!functionSource('applyCamera').includes('FRAME_INTERVAL')&&app.includes('AUXILIARY_FPS=30')&&app.includes('DRAG_TARGET_FPS=60')); + +// Lightweight diagnostics and larger/longer completion reward visuals. +assert(html.includes('id="fpsCounter"')&&functionSource('refreshFpsCounter').includes('FPS ${fpsLastValue}')); +assert(css.includes('.gem-particle{position:fixed')&&css.includes('width:28px;height:28px')); +assert(functionSource('completionEffect').includes('1800')); +assert(functionSource('playGemCollectionAnimation').includes('duration=reduced?520:1450')); +assert(!functionSource('bindBoard').includes('skipCompletionVisuals')); + +// Purchase normalization must remain functional after deleting field purchases. +assert(server.includes('function cleanId(')&&server.includes('function storeItem(')); +assert.equal(fs.existsSync(path.join(root,'docs','v47.71-input-ui-performance.md')),false); +const internalSpec=fs.readFileSync(path.join(root,'docs','internal-system.md'),'utf8'); +assert(internalSpec.includes('Do not add changelog')&&internalSpec.includes('update the relevant current specification in place')); +console.log('Difficulty removal, no-click-teleport, rendering, memory, FPS, and completion visual guards passed'); diff --git a/test/concurrency-smoke-test.js b/test/concurrency-smoke-test.js new file mode 100644 index 0000000..464c0b9 --- /dev/null +++ b/test/concurrency-smoke-test.js @@ -0,0 +1,235 @@ +'use strict'; +const {vm,app,assert,functionSource}=require('./helpers/app-source'); + +const persistSource=functionSource('persistDirtyToDb'); +const startupSource=functionSource('loadSnapshotFromDb'); +const startupV2Source=functionSource('loadV2SnapshotFromDb'); +const initialSource=functionSource('readInitialDataAsync'); +const replaceSource=functionSource('beginWorldReplacement'); +const clearSource=functionSource('clearDatabaseWorld'); + +assert(startupSource.includes('loadV2SnapshotFromDb')&&startupV2Source.includes("db.transaction(['worlds','boardIndex','outboxV2','recoveryV2','tombstonesV2'],'readonly')"), + 'Startup does not read the snapshot, recovery coverage, and tombstones atomically'); +assert(startupV2Source.includes("objectStore('recoveryV2').getAll")&&startupV2Source.includes("objectStore('tombstonesV2').getAll"), + 'Startup omits database recovery journals or tombstones'); +assert(!initialSource.includes('loadRecoveryCoverage('), + 'Startup still performs the recovery coverage read in a second transaction'); +assert(replaceSource.includes('await preserveRecoveryDurably('), + 'World replacement does not await a verified recovery backup'); +assert(functionSource('retryRecovery').includes('readRecoveryEnvelope')&&functionSource('retryRecovery').includes('stageRecoverySnapshotV2')&&functionSource('retryRecovery').includes("kind:'recovery'"), + 'Recovery backup is not restored through the validated staged-world path'); +assert(clearSource.includes("activateReadyWorldV2({epoch:newEpoch,expected,world},{kind:'reset'})"), + 'Fresh-world reset does not use atomic epoch activation'); +assert(!app.includes('worldMutationLockDepth')&&functionSource('withWorldMutationLock').includes("mode:'exclusive'"), + 'World mutation locking still bypasses unrelated asynchronous callers'); +assert(functionSource('persistNow').includes('if(options.lockHeld===true)return run()')&&functionSource('expandMetaNow').includes('lockHeld:true'), + 'Nested expansion persistence can deadlock behind a queued lock waiter'); +assert(app.includes('if(!worldInitReady){deferredWorldSignals.push(signal)'), + 'Cross-tab messages are not buffered until initialization is complete'); +assert(functionSource('initCloudSync').includes('if(cloudApiEnabled&&!cloudOutboxReady)'), + 'Cloud synchronization is not fail-closed when IndexedDB health is uncertain'); + +const request=result=>({result}); +function memoryStore(initial=[],keyOf=row=>row.id){ + const normalizeKey=key=>Array.isArray(key)?key.join(':'):key,rows=new Map(initial.map(row=>[normalizeKey(keyOf(row)),structuredClone(row)])); + return{ + rows, + get:key=>request(rows.has(normalizeKey(key))?structuredClone(rows.get(normalizeKey(key))):null), + put:row=>{rows.set(normalizeKey(keyOf(row)),structuredClone(row));return request(keyOf(row))}, + delete:key=>{rows.delete(normalizeKey(key));return request(undefined)}, + clear:()=>{rows.clear();return request(undefined)} + }; +} +function persistenceContext({localMeta=null,localState=null,existingMeta=null,existingState=null,deletion=null,identityEpoch='world:test'}={}){ + const existingId=existingMeta?.id||'B0',existingIndex=existingMeta?{epoch:'world:test',id:existingId,metaRev:existingMeta.rev||0,stateRev:existingState?.rev||0}:null; + const remoteGlobal={worldEpoch:'world:test',globalRev:20,globalRevAuthor:'remote',nextId:9,cursorStyle:'remote'}; + const stores={ + control:memoryStore([{key:'active',activeFormat:2,activeEpoch:identityEpoch}],row=>row.key), + worlds:memoryStore([{epoch:'world:test',status:'active',createdAt:1,source:{kind:'fresh'},global:remoteGlobal}],row=>row.epoch), + boardIndex:memoryStore(existingIndex?[existingIndex]:[],row=>`${row.epoch}:${row.id}`), + boardPuzzles:memoryStore(existingMeta?[{epoch:'world:test',id:existingId,metaRev:existingMeta.rev||0,meta:existingMeta}]:[],row=>`${row.epoch}:${row.id}`), + boardStates:memoryStore(existingState?[{epoch:'world:test',id:existingId,stateRev:existingState.rev||0,value:existingState}]:[],row=>`${row.epoch}:${row.id}`), + outboxV2:memoryStore([],row=>`${row.epoch}:${row.key}`),recoveryV2:memoryStore([],row=>`${row.epoch}:${row.key}`),tombstonesV2:memoryStore([],row=>`${row.epoch}:${row.id}`) + }; + const id=localMeta?.id||existingMeta?.id||'B0'; + const data={ + worldEpoch:'world:test',globalRev:10,globalRevAuthor:'local',nextId:2,cursorStyle:'local', + metas:localMeta?{[id]:structuredClone(localMeta)}:{}, + states:localState?{[id]:structuredClone(localState)}:{}, + updatedAt:0,clockFloor:0 + }; + const context={ + console,structuredClone,sessionId:'local',lastRevision:100,data, + dirtyMetaIds:new Set(localMeta?[id]:[]),dirtyStateIds:new Set(localState?[id]:[]), + deletedBoardIds:new Set(deletion?[id]:[]), + deletedBoardRevisions:new Map(deletion?[[id,deletion.rev]]:[]), + deletedBoardAuthors:new Map(deletion?[[id,deletion.revAuthor]]:[]), + recoveryJournalsToCover:[],recoveryJournalSeq:-1,recoveryWalPromise:Promise.resolve(), + cloudOutboxDeleteKeys:new Set(),cloudJournalMetaIds:new Set(),cloudJournalStateIds:new Set(), + cloudJournalDeletedIds:new Set(deletion?[id]:[]),cloudApiEnabled:false, + globalDirty:true,globalChangeSeq:1,cloudJournalGlobalChanged:true,worldSignalSeq:0, + idbAvailable:true,activeStorageFormat:2,FIELD_STORAGE_FORMAT:2,storageAccessError:null,normalizedStateObjects:new WeakSet(), + boardIndexSummaries:new Map(), + statsDirty:false,resetHistory:[], + pruneAndCount:()=>{}, + hasPendingPersistence:()=>context.globalDirty||context.dirtyMetaIds.size>0||context.dirtyStateIds.size>0||context.deletedBoardIds.size>0, + validWorldEpoch:value=>typeof value==='string'&&value.startsWith('world:'), + storedWorldEpoch:()=>null,createWorldEpoch:()=> 'world:test',rememberWorldEpoch:()=>true, + revisionVersion:value=>({rev:value?.rev||0,revAuthor:value?.revAuthor||value?.author||''}), + compareRevisionVersions:(a,b)=>(a?.rev||0)-(b?.rev||0)||String(a?.revAuthor||a?.author||'').localeCompare(String(b?.revAuthor||b?.author||'')), + newerRevisionValue:(a,b)=>context.compareRevisionVersions(a,b)>=0?a:b, + trustedNow:()=>1234, + globalForStorage:(source,updatedAt)=>({...source,metas:undefined,states:undefined,updatedAt,clockFloor:updatedAt}), + mergeGlobalRecords:(stored,incoming)=>context.compareRevisionVersions( + {rev:incoming.globalRev,revAuthor:incoming.globalRevAuthor}, + {rev:stored?.globalRev,revAuthor:stored?.globalRevAuthor} + )>=0?incoming:stored, + applyGlobalRecordToData:record=>Object.assign(data,structuredClone(record)), + staleWorldEpochError:()=>Object.assign(new Error('stale world'),{code:'STALE_WORLD_EPOCH'}), + metaRowsForStorage:ids=>ids.map(rowId=>data.metas[rowId]&&structuredClone(data.metas[rowId])).filter(Boolean), + stateRowsForStorage:ids=>ids.map(rowId=>data.states[rowId]&&({id:rowId,value:structuredClone(data.states[rowId])})).filter(Boolean), + snapshotForStorage:()=>({}),writeCompactMirror:()=>true, + openWorldDb:async()=>({transaction:()=>({objectStore:name=>stores[name],abort(){this.aborted=true}})}), + transactionDone:async()=>{},requestValue:async req=>req.result, + updateStorageRevision:()=>{},scheduleMirrorCheckpoint:()=>{}, + normalizeMeta:(_id,value)=>structuredClone(value),normalizeState:value=>structuredClone(value||{paths:[],rev:0,revAuthor:''}), + metaFromV2Records:(_index,puzzle)=>puzzle?.meta?structuredClone(puzzle.meta):null, + stateFromV2Record:(_index,state)=>state?.value?structuredClone(state.value):null, + puzzleRecordV2:(meta,epoch)=>({epoch,id:meta.id,metaRev:meta.rev||0,meta:structuredClone(meta)}), + stateRecordV2:(rowId,state,epoch)=>({epoch,id:rowId,stateRev:state?.rev||0,value:structuredClone(state)}), + summarizeBoardV2:(meta,state,epoch)=>({epoch,id:meta.id,metaRev:meta.rev||0,stateRev:state?.rev||0}), + fieldBoundsFromMetas:()=>({minX:0,minY:0,maxX:1,maxY:1}),SAVE_SCHEMA:31,WORLD_GENERATION:'current-only-test', + clearRecoveryJournalIfCovered:()=>{},broadcastWorldSignal:()=>{},scheduleCloudPush:()=>{}, + refreshWorldView:()=>{context.refreshes++},refreshes:0, + perfStart:()=>0,perfEnd:()=>{},perfGauge:()=>{} + }; + vm.createContext(context); + vm.runInContext(`${persistSource}\nthis.persistDirtyToDb=persistDirtyToDb;`,context); + return{context,stores,id}; +} + +async function verifyPersistenceConflicts(){ + { + const localMeta={id:'B0',x:1,rev:5,revAuthor:'local'},localState={paths:[],rev:5,revAuthor:'local'}; + const existingMeta={id:'B0',x:7,rev:10,revAuthor:'remote'},existingState={paths:[[1]],rev:11,revAuthor:'remote'}; + const {context,stores}=persistenceContext({localMeta,localState,existingMeta,existingState}); + await context.persistDirtyToDb(); + assert(stores.boardPuzzles.rows.get('world:test:B0').meta.x===7&&stores.boardStates.rows.get('world:test:B0').value.rev===11, + 'A stale tab overwrote a newer database row'); + assert(context.data.metas.B0.x===7&&context.data.states.B0.rev===11&&context.refreshes===1, + 'A stale in-memory board was not reconciled to the committed winner'); + assert(context.data.cursorStyle==='remote'&&context.data.nextId===9, + 'A stale global record overwrote the newer global winner'); + } + { + const existingMeta={id:'B0',x:4,rev:20,revAuthor:'remote'},existingState={paths:[],rev:21,revAuthor:'remote'}; + const {context,stores}=persistenceContext({ + existingMeta,existingState,deletion:{rev:15,revAuthor:'local'} + }); + await context.persistDirtyToDb(); + assert(stores.boardPuzzles.rows.has('world:test:B0')&&stores.boardStates.rows.has('world:test:B0')&&!stores.tombstonesV2.rows.has('world:test:B0'), + 'A stale deletion removed a newer database board'); + assert(context.data.metas.B0?.rev===20&&context.data.states.B0?.rev===21&&context.refreshes===1, + 'A rejected stale deletion was not restored in the losing tab'); + } + { + const existingMeta={id:'B0',rev:20,revAuthor:'remote'},existingState={paths:[],rev:20,revAuthor:'remote'}; + const {context,stores}=persistenceContext({ + existingMeta,existingState,deletion:{rev:25,revAuthor:'local'} + }); + await context.persistDirtyToDb(); + assert(!stores.boardPuzzles.rows.has('world:test:B0')&&!stores.boardStates.rows.has('world:test:B0'), + 'A newer deletion did not remove the older board rows'); + assert(stores.tombstonesV2.rows.get('world:test:B0')?.rev===25, + 'A committed deletion did not leave a durable tombstone'); + } + { + const localMeta={id:'B0',rev:5,revAuthor:'local'}; + const {context}=persistenceContext({localMeta,identityEpoch:'world:replacement'}); + let error=null;try{await context.persistDirtyToDb()}catch(caught){error=caught} + assert(error?.code==='STALE_WORLD_EPOCH'&&context.dirtyMetaIds.has('B0'), + 'A stale tab was allowed to write into a replaced world epoch'); + } +} + +function verifyGlobalMerge(){ + const context={ + deepClone:value=>structuredClone(value),structuredClone, + validWorldEpoch:value=>typeof value==='string'&&value.startsWith('world:'), + compareRevisionVersions:(a,b)=>(a?.rev||0)-(b?.rev||0)||String(a?.revAuthor||'').localeCompare(String(b?.revAuthor||'')), + bonusEventTotal:events=>Object.values(events||{}).reduce((sum,value)=>sum+(value||0),0) + }; + vm.createContext(context); + vm.runInContext(`${functionSource('mergeGlobalRecords')}\nthis.mergeGlobalRecords=mergeGlobalRecords;`,context); + const merged=context.mergeGlobalRecords( + {worldEpoch:'world:test',globalRev:30,globalRevAuthor:'remote',cursorStyle:'remote',nextId:8,clockFloor:11,cloudRevision:3,bonusEvents:{A:4},quarantine:{B:{failedAt:5}},lastSolveAt:10,specialMechanicsSeen:['warp'],combo:2}, + {worldEpoch:'world:test',globalRev:20,globalRevAuthor:'local',cursorStyle:'local',nextId:12,clockFloor:15,cloudRevision:9,bonusEvents:{A:2,C:7},quarantine:{B:{failedAt:8}},lastSolveAt:14,specialMechanicsSeen:['lock'],combo:4} + ); + assert(merged.cursorStyle==='remote'&&merged.nextId===12&&merged.clockFloor===15&&merged.cloudRevision===9, + 'Global LWW and monotonic fields were not merged independently'); + assert(merged.bonusEvents.A===4&&merged.bonusEvents.C===7&&merged.bonusScore===11, + 'Global additive bonus records were lost during a concurrent merge'); + assert(merged.quarantine.B.failedAt===8&&merged.lastSolveAt===14&&!Object.prototype.hasOwnProperty.call(merged,'combo')&&merged.specialMechanicsSeen.join(',')==='lock,warp', + 'Global timestamped records, encounter memory, or retired combo data were merged incorrectly'); +} + +async function verifySignalRetry(){ + const timers=[];let attempts=0,remembered=0; + const context={ + console:{warn:()=>{}},sessionId:'self',worldInitReady:false,deferredWorldSignals:[], + seenWorldCommitIds:new Set(),pendingWorldCommitIds:new Set(),syncQueue:Promise.resolve(), + worldCommitId:signal=>signal.commitId||'',applyWorldSignal:async()=>{attempts++;if(attempts===1)throw new Error('transient')}, + rememberWorldCommit:()=>remembered++, + setTimeout:callback=>{timers.push(callback);return timers.length} + }; + vm.createContext(context); + vm.runInContext(`${functionSource('queueWorldSignal')}\n${functionSource('drainWorldSignals')}\nthis.queueWorldSignal=queueWorldSignal;this.drainWorldSignals=drainWorldSignals;`,context); + const signal={commitId:'remote:1',sessionId:'remote'}; + context.queueWorldSignal(signal); + assert(context.deferredWorldSignals.length===1&&attempts===0,'A startup signal ran before initialization'); + context.drainWorldSignals();context.queueWorldSignal(signal); + await context.syncQueue; + assert(attempts===1&&remembered===0&&timers.length===1, + 'A failed signal was deduplicated as if it had succeeded'); + timers.shift()();await Promise.resolve();await context.syncQueue; + assert(attempts===2&&remembered===1,'A transient signal failure was not retried and committed once'); +} + +async function verifyDurableBackup(){ + const source=functionSource('preserveRecoveryDurably'); + const failing={ + Date,safeLocalSet:()=>false,sessionStorage:{setItem(){throw new Error('blocked')}}, + persistRecoveryEnvelope:async()=>false,volatileRecovery:null,recoveryStorageKey:'recovery' + }; + vm.createContext(failing);vm.runInContext(`${source}\nthis.preserveRecoveryDurably=preserveRecoveryDurably;`,failing); + let rejected=false;try{await failing.preserveRecoveryDurably('save','reset')}catch(_){rejected=true} + assert(rejected,'Destructive replacement proceeded without any durable backup'); + const database={ + Date,safeLocalSet:()=>false,sessionStorage:{setItem(){throw new Error('blocked')}}, + persistRecoveryEnvelope:async()=>true,volatileRecovery:null,recoveryStorageKey:'recovery' + }; + vm.createContext(database);vm.runInContext(`${source}\nthis.preserveRecoveryDurably=preserveRecoveryDurably;`,database); + const result=await database.preserveRecoveryDurably('save','reset'); + assert(!result.browserCopy&&result.databaseCopy,'A verified database-only backup was not accepted'); +} + +function verifyRevisionSeed(){ + const context={ + lastRevision:3,data:{},recoveredDeletionTombstones:new Map([['B9',{rev:81}]]) + }; + vm.createContext(context); + vm.runInContext(`${functionSource('seedRevisionClock')}\nthis.seedRevisionClock=seedRevisionClock;`,context); + const maximum=context.seedRevisionClock({ + globalRev:50,clockFloor:0,metas:{B0:{rev:60}},states:{B0:{rev:70}} + }); + assert(maximum===81&&context.lastRevision===81,'The revision clock was not seeded from all persisted row types'); +} + +(async()=>{ + await verifyPersistenceConflicts(); + verifyGlobalMerge(); + await verifySignalRetry(); + await verifyDurableBackup(); + verifyRevisionSeed(); + console.log('Concurrency and replacement safety test passed'); +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/economy-simulation-test.js b/test/economy-simulation-test.js new file mode 100644 index 0000000..ed730fe --- /dev/null +++ b/test/economy-simulation-test.js @@ -0,0 +1,22 @@ +'use strict'; +const {assert,loadAppLogic}=require('./helpers/app-source'); +const AppLogic=loadAppLogic(),samples=[],storeCoefficients=[]; +for(let seed=1;seed<=5000;seed++){ + const level=1+(seed%10),baseScore=20+level*level*4,meta={id:`B${seed}`,seed,generatorVersion:5}, + state={paths:[{startGate:0,endGate:1,cells:[[0,0],[0,1],[1,1],[1,2]]}]}, + reward=AppLogic.deterministicBoardReward(baseScore,{worldSeed:0x51a7f00d,meta,state}); + assert(reward.coefficient>=.8&&reward.coefficient<=1.2,'Board coefficient escaped its bound'); + samples.push(reward.award); + const prices=[700,1400,1800,2400].map(base=>AppLogic.deterministicStorePrice(base,0x51a7f00d,seed%97,Math.floor(seed/97),1)); + assert(prices.every(entry=>entry.coefficient===prices[0].coefficient),'Products in one store received different coefficients'); + storeCoefficients.push(prices[0].coefficient); +} +const mean=values=>values.reduce((sum,value)=>sum+value,0)/values.length, + rewardMean=mean(samples),storeMean=mean(storeCoefficients); +assert(storeMean>.99&&storeMean<1.01,'Store coefficient distribution is materially biased'); +assert(rewardMean>100&&rewardMean<230,'Reward rebalance left the measured progression band'); +assert(700<1400&&1400<1800&&1800<2400,'Cosmetics/lens/field price ladder is invalid'); +assert(1400/rewardMean>5&&1400/rewardMean<15,'Score lens is either trivial or effectively mandatory'); +assert(2400/rewardMean<24,'MAX field is outside a meaningful attainable score-sink range'); +assert(AppLogic.deterministicBoardReward(100,{meta:{id:'B1',seed:1,generatorVersion:5},state:{paths:[]},scoreLensCount:100}).lensMultiplier===1.5,'Repeated lenses create unbounded score inflation'); +console.log(`Economy simulation passed: mean reward ${rewardMean.toFixed(2)}, mean coefficient ${storeMean.toFixed(4)}`); diff --git a/test/expansion-repair-smoke-test.js b/test/expansion-repair-smoke-test.js new file mode 100644 index 0000000..0e362c3 --- /dev/null +++ b/test/expansion-repair-smoke-test.js @@ -0,0 +1,38 @@ +'use strict'; +const {vm,assert,functionSource}=require('./helpers/app-source'); + +const state={solved:true,expanded:false,expansionRetryRound:0,rev:0}; +const meta={id:'B0',puzzle:null,x:0,y:0}; +const chosen=[],attemptBases=[]; +let revision=0,missing=true,hydrations=0; +const context={ + metaState:()=>state, + hydrateMeta:async target=>{hydrations++;target.puzzle={}}, + hydrateAdjacentMetas:async()=>{}, + rebuildOccupancy:()=>{}, + repairFacingGateConnections:()=>{}, + unresolvedExpansionCandidates:()=>[{name:'north'},{name:'east'}], + placeChildAtFrontierAttempt:async(_meta,frontier,attemptBase)=>{ + chosen.push(frontier.name);attemptBases.push(attemptBase);return null; + }, + missingGateConnections:()=>missing?[1]:[], + nextRevision:()=>++revision, + markStateDirty:()=>{} +}; +vm.createContext(context); +vm.runInContext(`${functionSource('repairMetaFrontierNow')}\nthis.repairMetaFrontierNow=repairMetaFrontierNow;`,context); + +(async()=>{ + await context.repairMetaFrontierNow(meta); + await context.repairMetaFrontierNow(meta); + await context.repairMetaFrontierNow(meta); + assert(chosen.join(',')==='north,east,north','Expansion repair stayed on one linear frontier instead of rotating fairly'); + assert(hydrations===1,'Expansion repair skipped or repeatedly hydrated an off-screen compact board'); + assert(new Set(attemptBases).size===3,'Expansion repair repeated a deterministic generation seed'); + assert(state.expansionRetryRound===3&&!state.expanded,'Incomplete expansion repair did not persist its retry round'); + + missing=false; + await context.repairMetaFrontierNow(meta); + assert(state.expanded&&state.expansionRetryRound===0,'Completed expansion repair did not clear its retry state'); + console.log('Fair expansion-frontier retry test passed'); +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/expansion-smoke-test.js b/test/expansion-smoke-test.js new file mode 100644 index 0000000..62c93b8 --- /dev/null +++ b/test/expansion-smoke-test.js @@ -0,0 +1,91 @@ +'use strict'; +const {vm,assert,functionSource,loadBendPuzzle,loadAppLogic,starterPuzzle}=require('./helpers/app-source'); +const BendPuzzle=loadBendPuzzle(),AppLogic=loadAppLogic(); +const calls={refresh:0,render:0,hud:0,paint:0,save:0,sync:0}; +const testWatchdog=setTimeout(()=>{console.error('Expansion integration exceeded 45 seconds');process.exit(1)},45000);testWatchdog.unref?.(); +const context={console,BendPuzzle,AppLogic,GENERATOR_VERSION:5,UNIQUE_SOLUTION_MIN_LEVEL:6,deepClone:value=>structuredClone(value),structuredClone,hash32:BendPuzzle.hash32,rngFrom:BendPuzzle.rngFrom,shuffle:BendPuzzle.shuffle,generatedShapeKey:AppLogic.generatedShapeKey,generatedShapeFamilyKey:AppLogic.generatedShapeFamilyKey,balancedShapeCandidates:AppLogic.balancedShapeCandidates,growConnectedShape:AppLogic.growConnectedShape,macroDifficulty:BendPuzzle.macroDifficulty,difficultyFitsRegion:BendPuzzle.difficultyFitsRegion,solverDifficulty:BendPuzzle.solverDifficulty,SHAPES:BendPuzzle.SHAPES,H_PORT_PROFILES:BendPuzzle.H_PORT_PROFILES,V_PORT_PROFILES:BendPuzzle.V_PORT_PROFILES,horizontalBoundaryKey:BendPuzzle.horizontalBoundaryKey,verticalBoundaryKey:BendPuzzle.verticalBoundaryKey,key2:(x,y)=>`${x},${y}`,ckey:(r,c)=>`${r},${c}`,SIDE_D:{N:[-1,0],S:[1,0],W:[0,-1],E:[0,1]},OPP:{N:'S',S:'N',W:'E',E:'W'},data:{metas:{},states:{},nextId:1,specialMechanicsSeen:[]},occupancy:new Map(),closedVoidKeys:new Set(),adjacencyCache:new Map(),areaStoreEffects:()=>({forcedLevel:null,scoreLens:false}),targetLevelForNewBoard:(x,y)=>context.macroDifficulty(x,y),collectFieldEffectSources:()=>[],sleep:async()=>{},addObstaclePattern:p=>p,addSpecialCellPattern:p=>p,generatedPuzzleIssue:()=>null,generatePuzzleAsync:async(chunks,seed,level,x,y,timeout,options)=>BendPuzzle.generatePuzzle(chunks,seed,level,x,y,options),verifyPuzzleUniquenessAsync:async()=>({status:'unique',signature:'test',ruleVersion:2,nodes:1,quality:{score:80,accepted:true}}),mechanicTypesForPuzzle:()=>[],nextRevision:(()=>{let n=1;return()=>++n})(),markMetaDirty:()=>{},markStateDirty:()=>{},ensureBoards:()=>{},renderAll:()=>{calls.render++},updateHud:()=>{calls.hud++},nextPaint:async()=>{calls.paint++},save:async()=>{calls.save++;return true},hideStatus:()=>{},showStatus:()=>{},hydrateAdjacentMetas:async()=>{},repairFacingGateConnections:()=>0,syncBoundaryConnections:()=>{calls.sync++;return 0},puzzleOf:meta=>meta.puzzle,matchingNeighborGate:()=>null,metaAtGlobalCell:(r,c)=>{const id=context.occupancy.get(context.key2(Math.floor(c/5),Math.floor(r/5)));return id?context.data.metas[id]||null:null},globalCell:(meta,cell)=>[meta.y*5+cell[0],meta.x*5+cell[1]],sectionCountRange:AppLogic.sectionCountRange}; +context.addMetaToOccupancy=meta=>{for(const[dx,dy]of meta.chunks)context.occupancy.set(context.key2(meta.x+dx,meta.y+dy),meta.id);context.adjacencyCache.clear();return true}; +context.refreshWorldView=options=>{calls.refresh++;if(options?.syncConnections!==false)context.syncBoundaryConnections();context.renderAll();context.updateHud();if(options?.hide)context.hideStatus();return options?.persist?context.save(options.immediate):true}; +context.rebuildOccupancy=()=>{context.occupancy=new Map();context.closedVoidKeys=new Set();for(const meta of Object.values(context.data.metas))for(const[dx,dy]of meta.chunks)context.occupancy.set(context.key2(meta.x+dx,meta.y+dy),meta.id);const candidates=new Set();for(const key of context.occupancy.keys()){const[x,y]=key.split(',').map(Number);for(const[dr,dc]of Object.values(context.SIDE_D))candidates.add(context.key2(x+dc,y+dr))}for(const key of candidates){const[x,y]=key.split(',').map(Number);if(!context.occupancy.has(key)&&[...Object.values(context.SIDE_D)].every(([dr,dc])=>context.occupancy.has(context.key2(x+dc,y+dr))))context.closedVoidKeys.add(key)}}; +context.metaState=id=>context.data.states[id]||(context.data.states[id]={solved:false,expanded:false,paths:[],specialProgress:{crossings:[]},rev:0}); +context.unsolvedBoardCount=()=>Object.keys(context.data.metas).filter(id=>!context.metaState(id).solved).length; +vm.createContext(context); +vm.runInContext(` +const SHAPES_BY_SIZE=new Map();for(const shape of SHAPES){const size=shape.length;if(!SHAPES_BY_SIZE.has(size))SHAPES_BY_SIZE.set(size,[]);SHAPES_BY_SIZE.get(size).push(shape)} +${['cellSet','gateAtCell','gateConnectionAllowed','matchingNeighborGate','shapeCandidatesForLevel','nearbyShapeFamilyCounts','shapeCandidatesForArea','occupiedNeighborCount','isClosedVoidUnit','fitsMeta','placedShapeKeys','unitOccupiedWithExtra','isClosedVoidWithExtra','openUnitFrontiers','shapeFitsWithExtra','viableFuturePlacementAt','viableExpansionExists','prospectiveShapeHasFrontier','placementPreservesUnsolvedFrontiers','frontierCandidates','gateFrontierCandidates','unresolvedExpansionCandidates','closedVoidRepairCandidates','missingGateConnections','placementConnectionRequirements','puzzleSupportsConnectionRequirements','sameNumberList','fixedPortProfilesForRequirements','installPreparedChild','placeChildAtFrontierAttempt','frontierGeometryStillViable','placeChildAtFrontier','expandMetaNow'].map(name=>functionSource(name)).join('\n')} +const boundedPlaceChildAtFrontier=async(source,frontier,attemptBase=0)=>{for(let cycle=0;cycle<3;cycle++){const made=await placeChildAtFrontierAttempt(source,frontier,attemptBase+cycle*2000003);if(made)return made}return null}; +placeChildAtFrontier=boundedPlaceChildAtFrontier; +this.logic={gateFrontierCandidates,unresolvedExpansionCandidates,closedVoidRepairCandidates,missingGateConnections,placementConnectionRequirements,puzzleSupportsConnectionRequirements,openUnitFrontiers,viableExpansionExists,placementPreservesUnsolvedFrontiers,placeChildAtFrontierAttempt,placeChildAtFrontier,expandMetaNow};`,context); +const starter=starterPuzzle(); +const origin={id:'B0',x:0,y:0,chunks:[[0,0]],seed:0x51a7f00d,puzzle:starter,sealedSides:[],rev:1};context.data.metas.B0=origin;context.data.states.B0={solved:true,expanded:false,paths:starter.solution,specialProgress:{crossings:[]},rev:1};context.rebuildOccupancy(); +(async()=>{ + const targets=context.logic.gateFrontierCandidates(origin);assert(targets.length===4,'Origin does not expose four procedural directions'); + const started=Date.now(),made=await context.logic.expandMetaNow(origin),elapsed=Date.now()-started,remaining=context.logic.unresolvedExpansionCandidates(origin); + assert(made>=4,`Full expansion generated only ${made} fields`);assert(remaining.length===0,`Full expansion left ${remaining.length} frontiers`);assert(context.data.states.B0.expanded===true,'Origin was not marked fully expanded'); + const metaAtUnit=(unitX,unitY)=>Object.values(context.data.metas).find(meta=>meta.chunks.some(([dx,dy])=>meta.x+dx===unitX&&meta.y+dy===unitY)); + for(const gate of origin.puzzle.g){ + const[dr,dc]=context.SIDE_D[gate[2]],globalRow=origin.y*5+gate[0],globalCol=origin.x*5+gate[1],neighborRow=globalRow+dr,neighborCol=globalCol+dc, + neighbor=metaAtUnit(Math.floor(neighborCol/5),Math.floor(neighborRow/5)); + assert(neighbor&&neighbor.id!==origin.id,`Origin gate ${gate.join(',')} has no generated neighbor`); + const localRow=neighborRow-neighbor.y*5,localCol=neighborCol-neighbor.x*5, + matching=neighbor.puzzle.g.find(candidate=>candidate[0]===localRow&&candidate[1]===localCol&&candidate[2]===context.OPP[gate[2]]); + assert(matching,`Origin gate ${gate.join(',')} generated a board without the matching opposite gate`); + assert(!(neighbor.sealedSides||[]).includes(context.OPP[gate[2]]),`Matching gate for ${gate.join(',')} was sealed`); + } + assert(context.logic.missingGateConnections(origin).length===0,'Origin was marked expanded without an actual facing board for every gate'); + for(const meta of Object.values(context.data.metas).filter(meta=>meta.id!=='B0')){const range=AppLogic.sectionCountRange(meta.level);assert(meta.chunks.length>=range.min&&meta.chunks.length<=range.max,`${meta.id}: level ${meta.level} has ${meta.chunks.length} sections outside ${range.min}-${range.max}`);assert(context.logic.viableExpansionExists(meta),`${meta.id}: newly generated board has no viable future expansion placement`)} + assert(elapsed<8000,`Origin expansion took ${elapsed} ms`); + assert(calls.refresh===1&&calls.render===1&&calls.hud===1&&calls.save===1&&calls.paint===1,`Expansion was not committed once: ${JSON.stringify(calls)}`); + const branch=Object.values(context.data.metas).find(meta=>meta.id!=='B0');context.data.states[branch.id].solved=true;context.data.states[branch.id].expanded=false;const beforeBranchIds=new Set(Object.keys(context.data.metas)),branchStarted=Date.now(),branchMade=await context.logic.expandMetaNow(branch),branchElapsed=Date.now()-branchStarted; + assert(branchMade>0,`${branch.id}: solved child did not generate a branch`);const branchRemaining=context.logic.gateFrontierCandidates(branch);assert(context.data.states[branch.id].expanded===(branchRemaining.length===0),`${branch.id}: expanded flag does not match remaining gate targets`);assert(Object.keys(context.data.metas).some(id=>!beforeBranchIds.has(id)),`${branch.id}: no adjacent field was added`); + for(const meta of Object.values(context.data.metas))if(!context.metaState(meta.id).solved)assert(context.logic.viableExpansionExists(meta),`${meta.id}: branch expansion stranded an unsolved board`); + assert(branchElapsed<15000,`${branch.id}: branch expansion took ${branchElapsed} ms`); + let chained=0; + for(let step=0;step<8;step++){ + const candidates=Object.values(context.data.metas).filter(meta=>!context.metaState(meta.id).solved&&context.logic.viableExpansionExists(meta)); + assert(candidates.length,`Expansion chain stopped at step ${step}`);let generated=0,candidate=null; + for(const option of candidates){context.metaState(option.id).solved=true;context.metaState(option.id).expanded=false;generated=await context.logic.expandMetaNow(option);candidate=option;if(generated>0)break} + assert(candidate,`Expansion chain had no candidate at step ${step}`);chained+=generated;if(context.metaState(candidate.id).expanded)assert(context.logic.missingGateConnections(candidate).length===0,`${candidate.id}: expanded despite a missing facing gate at step ${step}`); + for(const meta of Object.values(context.data.metas))if(!context.metaState(meta.id).solved)assert(context.logic.viableExpansionExists(meta),`${meta.id}: expansion chain created a non-expandable board`); + } + const source100=BendPuzzle.generatePuzzle([[0,0]],0x7a1100,6,100,100);context.data={metas:{S:{id:'S',x:100,y:100,chunks:[[0,0]],seed:0x7a1100,puzzle:source100,sealedSides:[],rev:1}},states:{S:{solved:true,expanded:false,paths:source100.solution,specialProgress:{crossings:[]},rev:1}},nextId:100};context.rebuildOccupancy();context.macroDifficulty=()=>6; + const child=await context.logic.placeChildAtFrontier(context.data.metas.S,{unitX:101,unitY:100,side:'E',targetSide:'W',contacts:[]},0); + assert(child,'Level-6 normal frontier could not generate a board');const range=AppLogic.sectionCountRange(child.targetLevel);assert(child.chunks.length>=range.min&&child.chunks.length<=range.max,'Higher-level board violates its authoritative regional section range');assert(child.chunks.length>1,'Higher-level multi-section generation still depends on a retired special mode');assert(!('labyrinth' in child)&&child.anomaly!=='giant','Retired special-board metadata was generated');assert(context.logic.viableExpansionExists(child),'Higher-level child was generated as a dead end'); + const originalGenerate=context.generatePuzzleAsync; + vm.runInContext('this.originalShapeCandidatesForLevel=shapeCandidatesForLevel;this.originalShapes=SHAPES;shapeCandidatesForLevel=()=>[[[0,0],[1,0]]];SHAPES=[[[0,0],[1,0]]];',context); + const fallbackPuzzle=BendPuzzle.generatePuzzle([[0,0]],0x4187,3,300,300),fallbackSource={id:'F',x:300,y:300,chunks:[[0,0]],seed:0x4187,puzzle:fallbackPuzzle,sealedSides:[],rev:1};context.data={metas:{F:fallbackSource},states:{F:{solved:true,expanded:false,paths:fallbackPuzzle.solution,specialProgress:{crossings:[]},rev:1}},nextId:700};context.rebuildOccupancy();context.macroDifficulty=()=>6;context.generatePuzzleAsync=async(chunks,seed,level,x,y,timeout,options)=>{if(chunks.length>1)throw new Error('synthetic unsupported shape');return BendPuzzle.generatePuzzle(chunks,seed,level,x,y,options)}; + const fallbackEast=fallbackPuzzle.g.findIndex(g=>g[2]==='E'),fallbackGate=fallbackPuzzle.g[fallbackEast],[fdr,fdc]=context.SIDE_D.E,fgr=fallbackSource.y*5+fallbackGate[0],fgc=fallbackSource.x*5+fallbackGate[1],fallbackChild=await context.logic.placeChildAtFrontierAttempt(fallbackSource,{unitX:Math.floor((fgc+fdc)/5),unitY:Math.floor((fgr+fdr)/5),side:'E',targetSide:'W',contacts:[{gateIndex:fallbackEast}],gateDriven:true},0); + assert(fallbackChild===null,'Invalid single-section fallback was forced into a level requiring multiple sections');assert(Object.keys(context.data.metas).length===1&&context.data.metas.F===fallbackSource,'Rejected fallback mutated the world');assert(context.data.nextId===700,'Rejected fallback consumed a board id'); + context.generatePuzzleAsync=originalGenerate;vm.runInContext('shapeCandidatesForLevel=originalShapeCandidatesForLevel;SHAPES=originalShapes;',context); + const multiShape=[[0,0],[1,0]],multiPuzzle=BendPuzzle.generatePuzzle(multiShape,987654,3,12,-9),multiSource={id:'M',x:12,y:-9,chunks:multiShape,seed:987654,puzzle:multiPuzzle,sealedSides:['N','S','W'],rev:1};context.data={metas:{M:multiSource},states:{M:{solved:true,expanded:false,paths:multiPuzzle.solution,specialProgress:{crossings:[]},rev:1}},nextId:800};context.rebuildOccupancy();context.macroDifficulty=()=>3; + const multiMade=await context.logic.expandMetaNow(multiSource);assert(multiMade>0,'Clearing a multi-section board did not unlock any new board');assert(context.unsolvedBoardCount()>0,'Multi-section clear left no playable board');if(context.metaState('M').expanded)assert(context.logic.missingGateConnections(multiSource).length===0,'Multi-section board was marked expanded with missing gate neighbors'); + const requirementPuzzle=BendPuzzle.generatePuzzle([[0,0]],77,1,50,50),requirementSource={id:'R',x:50,y:50,chunks:[[0,0]],seed:77,puzzle:requirementPuzzle,sealedSides:[],rev:1};context.data={metas:{R:requirementSource},states:{R:{solved:true,expanded:false,paths:requirementPuzzle.solution,specialProgress:{crossings:[]},rev:1}},nextId:500};context.rebuildOccupancy(); + const eastGateIndex=requirementPuzzle.g.findIndex(g=>g[2]==='E'),eastGate=requirementPuzzle.g[eastGateIndex],[edr,edc]=context.SIDE_D.E,egr=requirementSource.y*5+eastGate[0],egc=requirementSource.x*5+eastGate[1],targetX=Math.floor((egc+edc)/5),targetY=Math.floor((egr+edr)/5),requirements=context.logic.placementConnectionRequirements(targetX,targetY,[[0,0]]),validChild=BendPuzzle.generatePuzzle([[0,0]],123,1,targetX,targetY),invalidChild=JSON.parse(JSON.stringify(validChild));invalidChild.g=invalidChild.g.filter(g=>!(g[0]===egr+edr-targetY*5&&g[1]===egc+edc-targetX*5&&g[2]==='W')); + assert(requirements.some(req=>req.metaId==='R'&&req.gateIndex===eastGateIndex),'Proposed placement did not capture the source gate requirement');assert(context.logic.puzzleSupportsConnectionRequirements(validChild,targetX,targetY,requirements),'A deterministic matching child was rejected');assert(!context.logic.puzzleSupportsConnectionRequirements(invalidChild,targetX,targetY,requirements),'A child without the facing gate was accepted'); + const regenPuzzle=BendPuzzle.generatePuzzle([[0,0]],0x471100,1,600,600),regenSource={id:'Q',x:600,y:600,chunks:[[0,0]],seed:0x471100,puzzle:regenPuzzle,sealedSides:[],rev:1};context.data={metas:{Q:regenSource},states:{Q:{solved:true,expanded:false,paths:regenPuzzle.solution,specialProgress:{crossings:[]},rev:1}},nextId:900};context.rebuildOccupancy();context.macroDifficulty=()=>1; + const regenEast=regenPuzzle.g.findIndex(g=>g[2]==='E'),regenGate=regenPuzzle.g[regenEast],[rdr,rdc]=context.SIDE_D.E,rgr=regenSource.y*5+regenGate[0],rgc=regenSource.x*5+regenGate[1],regenOptions=[],regenSpecialSeeds=[];let integrityChecks=0; + const regenOriginalGenerate=context.generatePuzzleAsync,regenOriginalSpecial=context.addSpecialCellPattern,regenOriginalIssue=context.generatedPuzzleIssue; + context.generatePuzzleAsync=async(chunks,seed,level,x,y,timeout,options)=>{regenOptions.push(options||null);return BendPuzzle.generatePuzzle(chunks,seed,level,x,y,options)};context.addSpecialCellPattern=(puzzle,seed)=>{regenSpecialSeeds.push(seed);return puzzle};context.generatedPuzzleIssue=()=>integrityChecks++===0?'synthetic impossible board':null; + vm.runInContext('this.regenOriginalShapeCandidates=shapeCandidatesForLevel;shapeCandidatesForLevel=()=>[[[0,0]]];',context); + const regenerated=await context.logic.placeChildAtFrontier(regenSource,{unitX:Math.floor((rgc+rdc)/5),unitY:Math.floor((rgr+rdr)/5),side:'E',targetSide:'W',contacts:[{gateIndex:regenEast}],gateDriven:true},0); + assert(regenerated,'Whole-board regeneration did not recover after a rejected completed candidate');assert(regenOptions.length>=2&®enSpecialSeeds.length>=2,'Rejected board did not rerun routing and special-cell generation');assert(regenOptions[0]?.portSeed==null&&Number.isInteger(regenOptions[1]?.portSeed),'Unconfirmed gate positions were not rerolled after rejection');assert(regenSpecialSeeds[0]!==regenSpecialSeeds[1],'Special-cell seed was not rerolled after rejection'); + context.generatePuzzleAsync=regenOriginalGenerate;context.addSpecialCellPattern=regenOriginalSpecial;context.generatedPuzzleIssue=regenOriginalIssue;vm.runInContext('shapeCandidatesForLevel=regenOriginalShapeCandidates;',context); + context.data.metas.U={id:'U',x:200,y:200,chunks:[[0,0]],seed:9,puzzle:starter,sealedSides:[],rev:1};context.data.states.U={solved:false,expanded:false,paths:[],specialProgress:{crossings:[]},rev:1};context.data.metas.N={id:'N',x:199,y:200,chunks:[[0,0]],seed:10,puzzle:starter,sealedSides:[],rev:1};context.data.states.N={solved:true,expanded:true,paths:[],specialProgress:{crossings:[]},rev:1};context.data.metas.S2={id:'S2',x:200,y:201,chunks:[[0,0]],seed:11,puzzle:starter,sealedSides:[],rev:1};context.data.states.S2={solved:true,expanded:true,paths:[],specialProgress:{crossings:[]},rev:1};context.data.metas.W2={id:'W2',x:200,y:199,chunks:[[0,0]],seed:12,puzzle:starter,sealedSides:[],rev:1};context.data.states.W2={solved:true,expanded:true,paths:[],specialProgress:{crossings:[]},rev:1};context.rebuildOccupancy();assert(context.logic.openUnitFrontiers(context.data.metas.U).length===1,'Synthetic board does not have exactly one remaining frontier');assert(!context.logic.placementPreservesUnsolvedFrontiers(201,200,[[0,0]]),"Placement was allowed to consume an unsolved board's final frontier"); + const closedPuzzle=BendPuzzle.generatePuzzle([[0,0]],0x47c105ed,3,900,900),closedSource={id:'C',x:900,y:900,chunks:[[0,0]],seed:0x47c105ed,puzzle:closedPuzzle,sealedSides:[],rev:1},closedGateIndex=0,closedGate=closedPuzzle.g[closedGateIndex],[cdr,cdc]=context.SIDE_D[closedGate[2]],closedGlobalRow=closedSource.y*5+closedGate[0],closedGlobalCol=closedSource.x*5+closedGate[1],closedX=Math.floor((closedGlobalCol+cdc)/5),closedY=Math.floor((closedGlobalRow+cdr)/5); + context.data={metas:{C:closedSource},states:{C:{solved:true,expanded:false,paths:closedPuzzle.solution,specialProgress:{crossings:[]},rev:1}},nextId:1000,specialMechanicsSeen:[]}; + let blockerIndex=0;for(const[dx,dy]of[[0,-1],[0,1],[-1,0],[1,0]]){const x=closedX+dx,y=closedY+dy;if(x===closedSource.x&&y===closedSource.y)continue;const id=`V${blockerIndex++}`;context.data.metas[id]={id,x,y,chunks:[[0,0]],seed:blockerIndex,puzzle:null,sealedSides:[],rev:1};context.data.states[id]={solved:true,expanded:true,paths:[],specialProgress:{crossings:[]},rev:1}} + context.rebuildOccupancy();context.macroDifficulty=()=>10; + const closedFrontier=context.logic.gateFrontierCandidates(closedSource).find(candidate=>candidate.unitX===closedX&&candidate.unitY===closedY); + assert(closedFrontier?.terminalFill,'A missing gate inside a one-section enclosed gap was still discarded'); + const closedChild=await context.logic.placeChildAtFrontier(closedSource,closedFrontier,0); + assert(closedChild&&closedChild.chunks.length===1&&context.occupancy.get(context.key2(closedX,closedY))===closedChild.id,'The enclosed field gap was not repaired with a generated puzzle'); + const ungatedX=1201,ungatedY=1200,ungatedSource={id:'G',x:1200,y:1200,chunks:[[0,0]],seed:0x47c105ee,puzzle:BendPuzzle.generatePuzzle([[0,0]],0x47c105ee,3,1200,1200),sealedSides:['E'],rev:1}; + context.data={metas:{G:ungatedSource},states:{G:{solved:true,expanded:true,paths:ungatedSource.puzzle.solution,specialProgress:{crossings:[]},rev:1}},nextId:1100,specialMechanicsSeen:[]}; + blockerIndex=0;for(const[dx,dy]of[[0,-1],[0,1],[-1,0],[1,0]]){const x=ungatedX+dx,y=ungatedY+dy;if(x===ungatedSource.x&&y===ungatedSource.y)continue;const id=`W${blockerIndex++}`;context.data.metas[id]={id,x,y,chunks:[[0,0]],seed:blockerIndex,puzzle:null,sealedSides:[],rev:1};context.data.states[id]={solved:true,expanded:true,paths:[],specialProgress:{crossings:[]},rev:1}} + context.rebuildOccupancy();context.macroDifficulty=()=>8; + const ungatedRepair=context.logic.closedVoidRepairCandidates().find(candidate=>candidate.frontier.unitX===ungatedX&&candidate.frontier.unitY===ungatedY); + assert(ungatedRepair?.frontier.closedVoidRepair&&!ungatedRepair.frontier.gateDriven,'A closed occupancy hole without an open gate was not detected'); + const ungatedChild=await context.logic.placeChildAtFrontier(ungatedRepair.source,ungatedRepair.frontier,0); + assert(ungatedChild&&ungatedChild.chunks.length===1&&ungatedChild.sealedSides.includes('N')&&ungatedChild.sealedSides.includes('S')&&ungatedChild.sealedSides.includes('W')&&ungatedChild.sealedSides.includes('E'),`Ungated enclosed gap was not generated as a sealed puzzle: ${JSON.stringify(ungatedChild&&{chunks:ungatedChild.chunks,sealedSides:ungatedChild.sealedSides})}`); + console.log(`Expansion integration passed: ${made} origin fields in ${elapsed} ms; ${branchMade} branch fields; ${chained} chained fields; level ${child.level} normal board has ${child.chunks.length} sections`); +})().catch(error=>{console.error(error);process.exitCode=1}).finally(()=>clearTimeout(testWatchdog)); diff --git a/test/field-persistence-browser-test.html b/test/field-persistence-browser-test.html new file mode 100644 index 0000000..90b12f6 --- /dev/null +++ b/test/field-persistence-browser-test.html @@ -0,0 +1,32 @@ + + +Field persistence browser test + + + diff --git a/test/field-persistence-smoke-test.js b/test/field-persistence-smoke-test.js new file mode 100644 index 0000000..0e2067e --- /dev/null +++ b/test/field-persistence-smoke-test.js @@ -0,0 +1,74 @@ +'use strict'; +const path=require('path'); +const vm=require('vm'); +const {assert,root,read}=require('./helpers/app-source'); + +delete global.BendFieldPersistence; +require(path.join(root,'field-persistence.js')); +const persistence=global.BendFieldPersistence; +assert(persistence&&persistence.ARCHIVE_VERSION===2,'Archive persistence module did not initialize'); + +const manifest={ + saveSchema:31,gameplayVersion:3,worldGeneration:'test-world',appVersion:'47.36',generatorVersion:1, + exportedAt:'2026-07-28T00:00:00.000Z',boardCount:2,estimatedRawBytes:4096,encoding:'ndjson',compression:'identity' +}; +const globalState={gameplayVersion:3,bonusEvents:{},fieldEffects:[],specialMechanicsSeen:[],clockFloor:0,lastSolveAt:0,timeAttack:null,timeAttackCooldowns:{3:0,5:0,10:0},lastTimeAttack:null}; +const boardRows=[ + {id:'B0',meta:{id:'B0',x:0,y:0,chunks:[[0,0]],puzzle:{g:[[0,0,0],[0,1,2]],n:[],valid:[[0,0],[0,1]],solution:[]}},state:{paths:[],specialProgress:{crossings:[]},solved:false,expanded:false,scoreAwarded:0}}, + {id:'B1',meta:{id:'B1',x:1,y:0,chunks:[[0,0]],puzzle:{g:[[0,0,0],[0,1,2]],n:[],valid:[[0,0],[0,1]],solution:[]}},state:{paths:[],specialProgress:{crossings:[]},solved:false,expanded:false,scoreAwarded:0}} +]; +async function* boards(){for(const row of boardRows)yield structuredClone(row)} +async function encode(compression,module=persistence){ + const chunks=[]; + const writable=new WritableStream({write(chunk){chunks.push((chunk instanceof Uint8Array?chunk:new Uint8Array(chunk)).slice())}}); + const result=await module.writeArchive({writable,manifest:{...manifest,compression},globalState:structuredClone(globalState),boards:boards()}); + return{result,blob:new Blob(chunks,{type:'application/x-bend-field-save'}),bytes:Buffer.concat(chunks.map(chunk=>Buffer.from(chunk)))}; +} +async function decode(blob,module=persistence){ + const seen=[]; + const result=await module.readArchive(blob,{onBoard:record=>seen.push(record)}); + return{result,seen}; +} +(async()=>{ + for(const compression of ['identity','gzip']){ + if(compression==='gzip'&&typeof CompressionStream!=='function')continue; + const first=await encode(compression),second=await encode(compression),decoded=await decode(first.blob); + assert(first.result.boardCount===2&&decoded.result.boardCount===2&&decoded.seen.map(row=>row.id).join(',')==='B0,B1',`${compression} archive did not round-trip`); + assert(first.result.crc32===decoded.result.footer.crc32,`${compression} archive checksum changed during round-trip`); + assert(first.bytes.equals(second.bytes),`${compression} archive output is not deterministic for identical records`); + } + const identity=await encode('identity'),text=identity.bytes.toString('utf8'),modified=text.replace('"x":1','"x":2'); + assert(modified!==text,'Archive corruption fixture did not modify a board record'); + let checksumRejected=false; + try{await decode(new Blob([modified]))}catch(error){checksumRejected=error?.code==='CHECKSUM_MISMATCH'} + assert(checksumRejected,'Archive checksum corruption was not rejected'); + let truncationRejected=false; + try{await decode(new Blob([text.split('\n').slice(0,-2).join('\n')+'\n']))}catch(error){truncationRejected=/footer|incomplete/i.test(error?.message||'')} + assert(truncationRejected,'Truncated archive was not rejected'); + let manifestRejected=false; + try{await persistence.inspectArchive(new Blob([text.replace('\"encoding\":\"ndjson\"','\"encoding\":\"json\"')]))}catch(error){manifestRejected=error?.code==='UNSUPPORTED_ARCHIVE'} + assert(manifestRejected,'Unsupported archive encoding was not rejected at inspection time'); + const lines=text.trimEnd().split('\n'); + const expectArchiveFailure=async(candidate,pattern,label)=>{let rejected=false;try{await decode(new Blob([candidate]))}catch(error){rejected=pattern.test(`${error?.code||''} ${error?.message||''}`)}assert(rejected,label)}; + const reordered=[...lines];[reordered[2],reordered[3]]=[reordered[3],reordered[2]];await expectArchiveFailure(`${reordered.join('\n')}\n`,/order|B0/i,'Reordered archive boards were accepted'); + const duplicate=[...lines];duplicate[3]=duplicate[2];await expectArchiveFailure(`${duplicate.join('\n')}\n`,/duplicated|order/i,'Duplicate archive board IDs were accepted'); + const unknown=[...lines],unknownRecord=JSON.parse(unknown[3]);unknownRecord.type='mystery';unknown[3]=JSON.stringify(unknownRecord);await expectArchiveFailure(`${unknown.join('\n')}\n`,/unknown|misplaced/i,'Unknown archive records were accepted'); + const wrongFooter=[...lines],footer=JSON.parse(wrongFooter[4]);footer.rawBytes++;wrongFooter[4]=JSON.stringify(footer);await expectArchiveFailure(`${wrongFooter.join('\n')}\n`,/CHECKSUM|integrity/i,'Incorrect footer byte counts were accepted'); + const wrongCount=[...lines],declared=JSON.parse(wrongCount[0]);declared.boardCount=1;wrongCount[0]=JSON.stringify(declared);await expectArchiveFailure(`${wrongCount.join('\n')}\n`,/more boards|count/i,'Incorrect manifest board counts were accepted'); + const aborted=new AbortController();aborted.abort();let canceled=false;try{await persistence.readArchive(identity.blob,{signal:aborted.signal})}catch(error){canceled=error?.name==='AbortError'}assert(canceled,'Archive cancellation was not acknowledged'); + let writeFailed=false;try{await persistence.writeArchive({writable:new WritableStream({write(){throw new Error('injected destination failure')}}),manifest:{...manifest},globalState:structuredClone(globalState),boards:boards()})}catch(_){writeFailed=true}assert(writeFailed,'Destination write failure did not abort export'); + if(typeof CompressionStream==='function'&&typeof DecompressionStream==='function'){const compressed=await encode('gzip'),damaged=Buffer.from(compressed.bytes);damaged[Math.max(10,Math.floor(damaged.length/2))]^=0xff;let gzipRejected=false;try{await decode(new Blob([damaged]))}catch(_){gzipRejected=true}assert(gzipRejected,'Corrupt gzip archive was accepted')} + const worker=read('field-persistence-worker.js'),moduleSource=read('field-persistence.js'); + class FakeWorker{ + constructor(){this.onmessage=null;this.onerror=null;const owner=this,context={TextEncoder,Uint8Array,Uint32Array,JSON,Error,self:{postMessage(data){queueMicrotask(()=>owner.onmessage?.({data}))}}};vm.createContext(context);vm.runInContext(worker,context);this.workerSelf=context.self} + postMessage(data){queueMicrotask(()=>{try{this.workerSelf.onmessage({data:structuredClone(data)})}catch(error){this.onerror?.({message:error.message})}})} + terminate(){} + } + global.Worker=FakeWorker;delete global.BendFieldPersistence;delete require.cache[require.resolve(path.join(root,'field-persistence.js'))];require(path.join(root,'field-persistence.js'));const workerPersistence=global.BendFieldPersistence; + const workerEncoded=await encode('identity',workerPersistence),workerDecoded=await decode(workerEncoded.blob,workerPersistence); + assert(workerEncoded.result.worker===true&&workerDecoded.result.worker===true&&workerDecoded.seen.length===2,'Archive worker execution path did not round-trip records'); + delete global.Worker;global.BendFieldPersistence=persistence; + assert(worker.includes('MAX_CHUNK_BYTES=1024*1024')&&worker.includes('postMessage({id,chunks,rawBytes,crc32:crc32Hex()},chunks)'),'Archive worker does not enforce transferable 1 MiB output chunks'); + assert(moduleSource.includes("new Worker('field-persistence-worker.js?v=47.77')")&&moduleSource.includes('item.byteLength>MAX_ARCHIVE_LINE_BYTES')&&moduleSource.includes('WORKER_TARGET_BYTES=1024*1024')&&moduleSource.includes('pendingLineBytes'),'Archive worker path, 1 MiB batching, or worker line limit is missing'); + console.log('Field archive round-trip and corruption checks passed'); +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/field-save-load-v2-smoke-test.js b/test/field-save-load-v2-smoke-test.js new file mode 100644 index 0000000..24104ae --- /dev/null +++ b/test/field-save-load-v2-smoke-test.js @@ -0,0 +1,38 @@ +'use strict'; +const {assert,functionSource,app,read}=require('./helpers/app-source'); +const openDb=functionSource('openWorldDb'); +const loadV2=functionSource('loadV2SnapshotFromDb'); +const persistence=functionSource('persistDirtyToDb'); +const stageArchive=functionSource('stageArchiveV2'); +const stageRecovery=functionSource('stageRecoverySnapshotV2'); +const activation=functionSource('activateReadyWorldV2'); +const reset=functionSource('clearDatabaseWorld'); +const verify=functionSource('verifyActiveWorldActivation'); +const readiness=functionSource('validateActiveWorldReadiness'); +const validation=functionSource('validateStagedWorldV2'); +const collect=functionSource('collectV2Garbage'); +const batchDelete=functionSource('deleteV2EpochBatch'); +const persistNow=functionSource('persistNow'); +const init=functionSource('init'); +const importSave=functionSource('importSaveFile'); +const activateStaged=functionSource('activateStagedWorldV2'); +const postflight=functionSource('verifyStagedStorageEstimate'); + +assert(openDb.includes('activeFormat:FIELD_STORAGE_FORMAT')&&openDb.includes("source:{kind:'fresh'}")&&!openDb.includes('activeFormat:1'),'Fresh storage does not start directly in the current V2 format'); +assert(openDb.includes("for(const retired of['metas','states','global','recovery','tombstones','outbox'])")&&openDb.includes('db.deleteObjectStore(retired)'),'Retired object stores are not deleted during the current-only upgrade'); +assert(!persistence.includes("objectStore('metas')")&&!persistence.includes("objectStore('states')")&&!persistence.includes("objectStore('outbox')")&&!persistence.includes("objectStore('recovery')")&&!persistence.includes("objectStore('tombstones')"),'Persistence still dual-writes the retired storage format'); +assert(!init.includes('migrateLoadedWorldToV2')&&!importSave.includes('migrateLoadedWorldToV2'),'Runtime still invokes the retired storage migration'); +assert(!app.includes('legacyCost')&&!app.includes('migrateScoreAmount')&&!app.includes('loadedFromLegacy')&&!app.includes("kind:'migration'")&&!app.includes('full-resync'),'Retired score, startup, or cloud migration compatibility remains'); +assert(functionSource('assertCurrentArchive').includes('manifest?.saveSchema!==SAVE_SCHEMA')&&functionSource('assertCurrentArchive').includes('manifest?.worldGeneration!==WORLD_GENERATION'),'Archive import does not require the exact current format'); +assert(!app.includes('archiveMigrationRegistry')&&!app.includes('PREVIOUS_WORLD_GENERATION')&&!importSave.includes('looksJson')&&!importSave.includes('.json'),'Old-generation or legacy JSON import compatibility remains'); +assert(stageArchive.includes('validateStagedWorldV2')&&stageArchive.includes('verifyStagedStorageEstimate')&&stageArchive.includes('archive global record is not canonical')&&stageArchive.includes("status:'ready'")&&stageRecovery.includes('validateStagedWorldV2')&&stageRecovery.includes('verifyStagedStorageEstimate')&&stageRecovery.includes("status:'ready'"),'Current archive/recovery staging skips canonical, database, or storage validation'); +assert(validation.includes('boardIndexMatchesDetails')&&validation.includes('count(IDBKeyRange.only(epoch))')&&validation.includes('global aggregates')&&validation.includes('originOwnedByB0'),'Staged validation does not verify detail revisions, row counts, summaries, aggregates, and B0 origin ownership'); +assert(activation.includes("olderPrevious.status='garbage'")&&activation.includes("currentWorld.status='rollback'")&&activation.includes('activationVerified:false')&&reset.includes("activateReadyWorldV2({epoch:newEpoch,expected,world},{kind:'reset'})")&&reset.includes('deleteV2Epoch(newEpoch)')&&activateStaged.includes('deleteV2Epoch(staged?.epoch)'),'Import and reset do not share the atomic ready-world activation primitive or clean failed staging epochs'); +assert(readiness.includes("indexes[0].id!=='B0'")&&readiness.includes('B0 revision check')&&readiness.includes('meta.x+dx===0')&&init.includes('rollbackUnverifiedWorld'),'Startup does not validate B0 origin ownership and roll back an unverified activation'); +assert(persistNow.includes('result.count>0')&&persistNow.includes('verifyActiveWorldActivation')&&verify.includes("previous.status='garbage'"),'Previous epoch is not retained until the first durable board checkpoint'); +assert(batchDelete.includes('getAllKeys(epochKeyRange(epoch),limit)')&&batchDelete.includes("phase:'cleanup'")&&app.includes('GC_BATCH_ROWS=500'),'Epoch garbage collection is not bounded and resumable'); +assert(collect.includes('control?.previousEpoch')&&collect.includes("startsWith('pin:')")&&collect.includes("world.status==='ready'")&&collect.includes('cleanupTemporaryExports'),'Garbage collection does not protect rollback/recovery epochs or clean stale ready/export artifacts'); +assert(postflight.includes('QUOTA_POSTFLIGHT'),'Import postflight does not recheck browser storage headroom'); +assert(app.includes('cameraAnchor')&&app.includes('selectedBoardId')&&init.includes('restoreSavedCamera')&&loadV2.includes('selectedIndex'),'Saved viewport and selected-board startup hints are not restored before the full index scan'); +assert(persistence.includes('v2Active')&&read('test/browser-field-storage-benchmark.js').includes("'10000,100000,200000'"),'Current V2 persistence or large-field benchmark profiles are missing'); +console.log('Current-only field save/load V2 integration guards passed'); diff --git a/test/gameplay-simplification-smoke-test.js b/test/gameplay-simplification-smoke-test.js new file mode 100644 index 0000000..e110ac4 --- /dev/null +++ b/test/gameplay-simplification-smoke-test.js @@ -0,0 +1,143 @@ +'use strict'; +const {vm,app,html,css,worker,assert,functionSource,loadBendPuzzle,loadAppLogic}=require('./helpers/app-source'); +const AppLogic=loadAppLogic(),BendPuzzle=loadBendPuzzle(); + +// 1. Combo/undo removal and legacy omission. +const retiredFieldCleanup=functionSource('mergeGlobalRecords'),activeGameplaySource=app.replace(retiredFieldCleanup,''); +assert(!/\bcombo\b/i.test(activeGameplaySource)&&!/\bundo\b/i.test(activeGameplaySource)&&!/\b(combo|undo)\b/i.test(html),'Combo or visible undo remains in active gameplay/UI source'); +assert(/\bcombo\b/.test(retiredFieldCleanup)&&/\bundo\b/.test(retiredFieldCleanup),'Legacy combo/undo fields are not explicitly removed during synchronization'); +assert(!functionSource('globalForStorage').includes('combo')&&!functionSource('globalForStorage').includes('undo'),'Retired fields are still written'); + +// 2. Seeded score/store economy invariants. +for(let seed=0;seed<500;seed++){ + const coefficient=AppLogic.deterministicStorePrice(1000,seed,4,-7,1).coefficient; + assert(coefficient>=.8&&coefficient<=1.2,'Seeded coefficient escaped 0.8-1.2'); + const first=AppLogic.deterministicStorePrice(1000,seed,4,-7,1),second=AppLogic.deterministicStorePrice(1000,seed,4,-7,1); + assert(JSON.stringify(first)===JSON.stringify(second),'Store price rerolled for one location'); +} +const lensReward=count=>AppLogic.deterministicBoardReward(100,{meta:{id:'B1',seed:1,generatorVersion:5},state:{paths:[]},scoreLensCount:count}).lensMultiplier; +assert(lensReward(0)===1&&lensReward(1)===1.25&&lensReward(2)===1.4&&lensReward(99)===1.5,'Score-lens diminishing cap is invalid'); +assert(functionSource('rewardDetailsForBoard').includes('timeAttackRewardModifier()')&&functionSource('recordTimeAttackScore').includes('baseCollected')&&functionSource('finishTimeAttack').includes('if(result.bonus>0)'),'Time-attack reward modifier is deferred or double-applied'); + +// 3. Difficulty-field feature removal and lightweight detailed-board rendering. +assert(typeof AppLogic.winningLevelField==='undefined'&&typeof AppLogic.visibleFieldUnits==='undefined','Retired difficulty-field helpers remain exported'); +assert(!app.includes('level-min-10')&&!app.includes('level-max-10')&&!app.includes('fieldOverlayCanvas')&&!html.includes('fieldPreviewCanvas'),'Difficulty adjustment items or overlays remain active'); +assert(functionSource('makeBoard').includes('boardCellsPath')&&!functionSource('makeBoard').includes('cellHits')&&functionSource('desiredInteractiveBoardIds').includes('for(const id of ids)'),'Visible detailed boards are missing or still allocate per-cell hit nodes'); + +// 4-6. Center snapping without sticky hysteresis/speculative turns, and silent invalid motion. +for(const marker of ['rawPointerPosition','currentConfirmedCell','candidateDirection','currentCandidateCell','renderedHandlePosition'])assert(functionSource('drawingForPath').includes(marker),`Pointer state is missing ${marker}`); +assert(app.includes('POINTER_SNAP_THRESHOLD=CELL*.45')&&!app.includes('POINTER_SNAP_RELEASE')&&!app.includes('hysteresisDragPoint'),'Single-threshold pointer snapping or hysteresis removal is incomplete'); +assert(app.includes('POINTER_DOMINANT_RATIO=1.25')&&!app.includes('POINTER_BUFFER_MS')&&!app.includes('consumeBufferedPointerTurn'),'Speculative one-step turn extension remains active'); +assert(functionSource('pointerEventSamples').includes("pointerType!=='mouse'")&&functionSource('processBoardDragFrame').includes('const move=b.pendingPointerMove'),'Ordered pointer samples are not preserved through a drag frame'); +assert(!functionSource('extendOne').includes('toast(')&&functionSource('extendOne').includes("if(lock&&!pathHasLockKey(path,lock))return false"),'Invalid direction still produces error feedback'); +assert(functionSource('openTipMergePlan').includes('tipDistance!==0')&&!app.includes('function pickupJoinStepsAtPoint'),'Adjacent path pickups can still auto-connect'); +assert(functionSource('openTipMergePlan').includes('seen.has(key)')&&functionSource('joinTips').includes('cancelBoardDragFrame(b)')&&functionSource('joinTips').includes('safeRelease(b.svg,pointerId)'),'Pickup merging can duplicate an overlapping route or retain pointer work after joining'); + +// 7. Level schedule and introduction memory. +for(let level=1;level<=4;level++)assert(AppLogic.specialSchedule(level,100,level).types.length===0,`Level ${level} scheduled a special`); +for(const level of[5,6]){const schedule=AppLogic.specialSchedule(level,100,level,['warp','lock','crossing'],[]);assert(schedule.types.length===1&&schedule.setCount===1,`Level ${level} did not schedule exactly one minimum type`)} +const introduction=AppLogic.specialSchedule(9,200,77,['warp'],[]); +assert(introduction.types.length===1&&introduction.setCount===1&&introduction.introduction,'First encounter is not isolated at minimum density'); +assert(functionSource('addSpecialCellPattern').includes('AppLogic.specialSchedule')&&!app.includes('SPECIAL_CELL_PRODUCTION_CHANCE'),'Legacy independent special chance remains active'); +assert(functionSource('generatedPuzzleIssue').includes("crossingCoverage.has(key)?2:1"),'Generated crossing boards are rejected by single-owner coverage validation'); + +const plain={ + valid:[[0,0],[0,1]],g:[[0,0,'W'],[0,1,'E']],n:[[0,0,0]],obstacles:[], + specialCells:{crossings:[],warps:[],locks:[]},solution:[{startGate:0,endGate:1,cells:[[0,0],[0,1]]}] +}; +const multiple={ + valid:[[0,0],[0,1],[1,0],[1,1]],g:[[0,0,'S'],[0,1,'S'],[1,0,'N'],[1,1,'N']],n:[[0,0,2],[1,1,2]],obstacles:[], + specialCells:{crossings:[],warps:[],locks:[]},solution:[{startGate:0,endGate:1,cells:[[0,0],[0,1]]},{startGate:2,endGate:3,cells:[[1,0],[1,1]]}] +}; +const crossing={ + valid:[[1,0],[1,1],[1,2],[0,1],[2,1]],g:[[1,0,'W'],[1,2,'E'],[0,1,'N'],[2,1,'S']],n:[[1,0,0],[0,1,0]],obstacles:[], + specialCells:{crossings:[[1,1]],warps:[],locks:[]},solution:[{startGate:0,endGate:1,cells:[[1,0],[1,1],[1,2]]},{startGate:2,endGate:3,cells:[[0,1],[1,1],[2,1]]}] +}; +const warp={ + valid:[[0,0],[0,2]],g:[[0,0,'W'],[0,2,'E']],n:[[0,0,0]],obstacles:[], + specialCells:{crossings:[],warps:[{a:[0,0],b:[0,2]}],locks:[]},solution:[{startGate:0,endGate:1,cells:[[0,0],[0,2]]}] +}; +const lock={ + ...multiple, + specialCells:{crossings:[],warps:[],locks:[{key:[0,0],door:[0,1]}]} +}; + +// 8. Worker-oriented uniqueness outcomes and deterministic fixtures. +for(const fixture of[plain,crossing,warp,lock]){ + const first=BendPuzzle.verifyPuzzleUniqueness(fixture,{maxMs:1000,nodeCap:10000}),second=BendPuzzle.verifyPuzzleUniqueness(fixture,{maxMs:1000,nodeCap:10000}); + assert(first.status==='unique',`Expected unique ${JSON.stringify(first)}`); + assert(first.status===second.status&&first.signature===second.signature,'Uniqueness result is not stable'); +} +assert(BendPuzzle.verifyPuzzleUniqueness(multiple,{maxMs:1000,nodeCap:10000}).status==='multiple','Known multi-solution fixture was accepted'); +assert(BendPuzzle.verifyPuzzleUniqueness({...plain,n:[[0,0,1]]},{maxMs:1000,nodeCap:10000}).status==='unsolved','Known unsolved fixture was not reported'); +assert(BendPuzzle.verifyPuzzleUniqueness(multiple,{maxMs:1000,nodeCap:1}).status==='timeout','Node-cap timeout was not reported'); +assert(worker.includes("action === 'verify'")&&functionSource('verifyPuzzleUniquenessAsync').includes("action:'verify'")&&!functionSource('verifyPuzzleUniquenessAsync').includes('verifyPuzzleUniqueness('),'Uniqueness is not worker-isolated'); + +// 9. Session suggestion. +assert(functionSource('scheduleTimeAttackSuggestionAfterCompletion').includes('sessionCompletionCount!==10')&&functionSource('tryShowTimeAttackSuggestion').includes('7000')&&html.includes('id="timeAttackSuggestion" role="status" aria-live="polite"'),'Completion-10 suggestion timing/live region is missing'); + +// 10. Minimap teleport and controls. +assert(!html.includes('id="teleportBtn"')&&html.includes('id="minimapOriginBtn"')&&html.includes('id="minimapRandomBtn"')&&!html.includes('id="minimapUnsolvedBtn"')&&!html.includes('id="minimapCurrentBtn"'),'Minimap teleport controls do not match origin/random'); +assert(functionSource('centerRandomBoard').includes('getRandomValues')&&!app.includes('centerRandomUnsolved'),'Random minimap teleport still cycles through unsolved boards'); +assert(functionSource('beginMinimapPointer').includes('centerWorldUnit')&&functionSource('moveMinimapPointer').includes('centerWorldUnit')&&html.includes('id="minimapCanvas" width="210" height="132" role="application" tabindex="0"'),'Direct/keyboard minimap interaction is missing'); +assert(functionSource('centerWorldUnit').includes('worldNavigationBounds'),'Minimap teleport is not clamped to world navigation bounds'); +assert(functionSource('inWorldOverview').includes('cam.scale<=OVERVIEW_ZOOM_THRESHOLD')&&!app.includes('OVERVIEW_HYSTERESIS')&&css.includes('.static-summary-fill{fill:transparent!important'),'Zoomed board summaries retain sticky green/gray squares'); +assert(!functionSource('drawWorldOverview').includes('drawWorldOverviewPaths')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines'),'Far zoom does not use the minimap long-line renderer'); +assert(functionSource('makeStaticBoard').includes('renderedConnectedLineWidth(meta,index)')&&!functionSource('makeStaticBoard').includes('state.solved')&&css.includes('.static-summary-path{')&&css.includes('vector-effect:non-scaling-stroke;opacity:.9'),'Line thickness disappears from an unsolved zoomed-in board'); +assert(functionSource('rebuildWorldOverviewCache').includes('drawMapBoardCells')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines')&&!functionSource('rebuildWorldOverviewCache').includes('storeCellForMeta'),'Zoomed-out view no longer matches the minimap renderer'); +assert(functionSource('leftFieldPanAllowed').includes("classList?.contains('solved')")&&functionSource('beginPan').includes('leftFieldPanAllowed(e)'),'Solved/undiscovered left-drag field panning is missing'); + +const priceContext={MIN_STORE_ITEM_PRICE:3000,storePriceDetails:()=>({coefficient:.8})}; +vm.createContext(priceContext); +vm.runInContext(`${functionSource('storeItemPrice')}\nthis.storeItemPrice=storeItemPrice;`,priceContext); +assert(priceContext.storeItemPrice({},null,{cost:2500})===3000&&priceContext.storeItemPrice({},null,{cost:10000})===8000,'Store prices do not enforce a 3,000 minimum while retaining higher price variation'); + +const cursorItems=Array.from({length:12},(_,index)=>({id:`C${index}`,cursorStyle:`face-${index}`})), + otherItems=Array.from({length:1},(_,index)=>({id:`O${index}`})),allStoreItems=[...cursorItems,...otherItems], + shopContext={ + CURSOR_ITEMS:cursorItems,STORE_ITEMS:allStoreItems, + hash32:BendPuzzle.hash32,rngFrom:BendPuzzle.rngFrom,shuffle:BendPuzzle.shuffle, + storeItem:id=>allStoreItems.find(item=>item.id===id)||null + }; +vm.createContext(shopContext); +vm.runInContext(`${functionSource('normalizeStoreItemIds')}\n${functionSource('seededStoreItemIds')}\n${functionSource('storeInventoryItems')}\nthis.shop={normalizeStoreItemIds,seededStoreItemIds,storeInventoryItems};`,shopContext); +const seedA=shopContext.shop.seededStoreItemIds(123456),seedARepeat=shopContext.shop.seededStoreItemIds(123456),seedB=shopContext.shop.seededStoreItemIds(654321), + selected=seedA.map(shopContext.storeItem); +assert(JSON.stringify(seedA)===JSON.stringify(seedARepeat)&&JSON.stringify(seedA)!==JSON.stringify(seedB),'Store inventory is not deterministic per field seed'); +assert(seedA.length===13&&new Set(seedA).size===13&&selected.filter(item=>item.cursorStyle).length===12&&selected.filter(item=>!item.cursorStyle).length===1,'Seeded store inventory is not exactly twelve cursors and one other item'); +assert(JSON.stringify(shopContext.shop.storeInventoryItems({seed:9},{itemIds:seedA}).map(item=>item.id))===JSON.stringify(seedA),'Persisted store inventory IDs are not honored'); + +// 11-12. Noise/reduced motion and bounded caches/worker. +assert(html.includes('id="noiseCanvas" width="80" height="64"')&&functionSource('scheduleNoiseBackground').includes('noisePainted')&&!functionSource('scheduleNoiseBackground').includes('setTimeout')&&functionSource('paintNoiseBackground').includes("perfCount('noiseFrames')"),'Static low-cost noise background is missing'); +assert(css.includes('#noiseCanvas{')&&css.includes('opacity:.06'),'Noise effective opacity exceeds the low-contrast budget'); +assert(functionSource('evictHydratedBoardDetails').includes('hydratedBoardLru.size>32')&&functionSource('evictHydratedBoardDetails').includes('hydratedBoardBytes>8*1024*1024')&&worker.includes('uniquenessCache.size > 256'),'Hydration or uniqueness cache is unbounded/missing'); + +// 13-14. Contextual HUD and active-board isolation. +assert(css.includes('#topbar.drawing-active')&&css.includes(':focus-within')&&css.includes('transition:opacity'),'Contextual HUD fading constraints are missing'); +assert(!css.includes('.board-card:not(.input-active) .static-layer')&&!css.includes('filter:saturate')&&!css.includes('backdrop-filter')&&!css.includes('drop-shadow'),'Non-selected boards are dimmed or global presentation still creates costly filter surfaces'); + +// 15. Skippable completion independent of persistence. +const solveSource=functionSource('checkSolvedAndExpand'); +assert(solveSource.indexOf('completionEffect(immediateBoard,award)')32')&&functionSource('evictHydratedBoardDetails').includes('hydratedBoardBytes>8*1024*1024')&&app.includes('BOARD_RENDERS_PER_FRAME=6'),'Hydration, cache, or render work is not bounded'); +assert(functionSource('drawPresenceLayer').includes('dpr=1')&&functionSource('drawReactionLayer').includes('dpr=1'),'Fullscreen online canvases still allocate high-DPR backing stores'); +assert(html.includes('id="fpsCounter"')&&functionSource('refreshFpsCounter').includes('FPS ${fpsLastValue}')&&app.includes('AUXILIARY_FPS=30')&&app.includes('DRAG_TARGET_FPS=60'),'FPS diagnostics or split interaction budgets are missing'); +assert(functionSource('scheduleNoiseBackground').includes('noisePainted')&&!functionSource('scheduleNoiseBackground').includes('setTimeout'),'Noise background still repaints continuously'); + +// 17. Bounded interaction burden in candidate selection. +assert(AppLogic.interactionBurden(plain).score<=40&&functionSource('placeChildAtFrontierAttempt').includes('AppLogic.interactionBurden')&&functionSource('placeChildAtFrontierAttempt').includes('boundedBurdenPenalty'),'Interaction burden is unbounded or not integrated'); +assert(BendPuzzle.UNIQUENESS_RULE_VERSION===2&&BendPuzzle.verifyPuzzleUniqueness(lock,{maxMs:1000,nodeCap:10000}).quality?.contributingSpecials>=1,'Solution quality does not measure special contribution'); + +// 18. Encounter migration and synchronization. +assert(functionSource('normalizeSnapshot').includes('mechanicTypesForPuzzle')&&functionSource('mergeGlobalRecords').includes('specialMechanicsSeen')&&functionSource('addSpecialCellPattern').includes('data.specialMechanicsSeen'),'Special encounter memory is not migrated, synchronized, and consumed'); + +console.log('Gameplay simplification regression groups 1-18 passed'); diff --git a/test/helpers/app-source.js b/test/helpers/app-source.js new file mode 100644 index 0000000..5a18934 --- /dev/null +++ b/test/helpers/app-source.js @@ -0,0 +1,46 @@ +'use strict'; +const fs=require('fs'); +const path=require('path'); +const vm=require('vm'); +const root=path.resolve(__dirname,'../..'); +const read=name=>fs.readFileSync(path.join(root,name),'utf8'); +const app=read('app.js'),html=read('index.html'),css=read('style.css'),worker=read('puzzle-worker.js'),appLogicSource=read('app-logic.js'); +function assert(value,message){if(!value)throw new Error(message)} +function functionSource(name,source=app){ + let start=source.indexOf(`async function ${name}(`);if(start<0)start=source.indexOf(`function* ${name}(`);if(start<0)start=source.indexOf(`function ${name}(`); + assert(start>=0,`Missing ${name}`); + let paren=source.indexOf('(',start),parenDepth=0,body=-1; + for(let i=paren;i=0,`Missing body for ${name}`);let depth=0; + for(let i=body;ifunctionSource(name)).join('\n'); + vm.runInContext(`${sources}\nthis.logic={${expose.join(',')}};`,context); + return context.logic; +} +function loadBendPuzzle(){ + delete global.BendPuzzle; + delete require.cache[require.resolve(path.join(root,'puzzle-core.js'))]; + require(path.join(root,'puzzle-core.js')); + return global.BendPuzzle; +} +function loadAppLogic(){ + delete require.cache[require.resolve(path.join(root,'app-logic.js'))]; + return require(path.join(root,'app-logic.js')); +} +function starterPuzzle(){ + const match=app.match(/const STARTER_PUZZLE=Object\.freeze\((\{.*\})\);\nconst SCORE_VERSION/s); + assert(match,'Bundled origin puzzle missing'); + return JSON.parse(match[1]); +} +module.exports={root,read,app,html,css,worker,appLogicSource,assert,functionSource,createContext,runFunctions,loadBendPuzzle,loadAppLogic,starterPuzzle,vm,path,fs}; diff --git a/test/helpers/realtime-client.js b/test/helpers/realtime-client.js new file mode 100644 index 0000000..e1daa2b --- /dev/null +++ b/test/helpers/realtime-client.js @@ -0,0 +1,52 @@ +'use strict'; + +const assert=require('assert/strict'); + +async function connectRealtime(base,session,{timeout=4000}={}){ + assert(session?.playerId&&session?.token,'Realtime session is required'); + const url=base.replace(/^http/,'ws')+'/api/realtime'; + const socket=new WebSocket(url); + const queue=[]; + const waiters=[]; + let closed=false; + function dispatch(message){ + for(let index=0;index{ + try{dispatch(JSON.parse(String(event.data)))}catch(error){dispatch({type:'test-parse-error',error})} + }); + socket.addEventListener('close',()=>{closed=true;for(const waiter of waiters.splice(0)){clearTimeout(waiter.timer);waiter.reject(new Error('Realtime socket closed'))}}); + await new Promise((resolve,reject)=>{ + const timer=setTimeout(()=>reject(new Error('Realtime socket open timed out')),timeout); + socket.addEventListener('open',()=>{clearTimeout(timer);resolve()},{once:true}); + socket.addEventListener('error',()=>{clearTimeout(timer);reject(new Error('Realtime socket failed'))},{once:true}); + }); + socket.send(JSON.stringify({type:'hello',playerId:session.playerId,token:session.token})); + async function waitFor(predicateOrType,waitTimeout=timeout){ + const predicate=typeof predicateOrType==='function'?predicateOrType:message=>message?.type===predicateOrType; + const found=queue.findIndex(predicate); + if(found>=0)return queue.splice(found,1)[0]; + if(closed)throw new Error('Realtime socket is closed'); + return new Promise((resolve,reject)=>{ + const waiter={predicate,resolve,reject,timer:null}; + waiter.timer=setTimeout(()=>{const index=waiters.indexOf(waiter);if(index>=0)waiters.splice(index,1);reject(new Error('Realtime message timed out'))},waitTimeout); + waiters.push(waiter); + }); + } + const ready=await waitFor('ready'); + return{ + socket,ready, + send(message){socket.send(JSON.stringify(message))}, + waitFor, + close(){try{socket.close()}catch(_){}}, + queued:queue + }; +} + +module.exports={connectRealtime}; diff --git a/test/interaction-smoke-test.js b/test/interaction-smoke-test.js new file mode 100644 index 0000000..aedf90f --- /dev/null +++ b/test/interaction-smoke-test.js @@ -0,0 +1,285 @@ +'use strict'; +const {vm,assert,functionSource,loadAppLogic,app,css}=require('./helpers/app-source'); +const AppLogic=loadAppLogic(); +const context={PAD:16,CELL:40,ckey:(r,c)=>`${r},${c}`}; +vm.createContext(context); +vm.runInContext(`${functionSource('cellsCrossedBySegment')}\n${functionSource('liveEndpointPoint')}\n${functionSource('hexRgb')}\n${functionSource('mixHex')}\n${functionSource('pathStrokePieces')}\nthis.logic={cellsCrossedBySegment,liveEndpointPoint,pathStrokePieces};`,context); +const {cellsCrossedBySegment,liveEndpointPoint,pathStrokePieces}=context.logic; +const horizontal=cellsCrossedBySegment([36,36],[76,76],{preferredAxis:'H',validCells:new Set(['0,0','0,1','1,1'])}); +assert(JSON.stringify(horizontal)==='[[0,1],[1,1]]','Exact-corner drag did not follow the horizontal valid route'); +const vertical=cellsCrossedBySegment([36,36],[76,76],{preferredAxis:'H',validCells:new Set(['0,0','1,0','1,1'])}); +assert(JSON.stringify(vertical)==='[[1,0],[1,1]]','Exact-corner drag ignored the only valid route'); +const board={drawing:{pointerId:7,pathIndex:2,lastPoint:[120,44],renderedHandlePosition:[80,44]}},tip=liveEndpointPoint(board,2,[80,44]); +assert(tip[0]===80&&tip[1]===44,'Live endpoint followed raw pointer noise instead of the confirmed cell center'); +board.drawing.renderedHandlePosition=[120,44]; +assert(liveEndpointPoint(board,2,[80,44])[0]===120,'Live endpoint did not show the buffered next-cell center'); +assert(liveEndpointPoint(board,1,[80,44])[0]===80,'Live endpoint affected a non-active line'); +const pieces=pathStrokePieces([[[0,0],[30,0],[30,40],[50,40]]],'#000000','#ffffff'); +assert(pieces.length===1&&pieces[0].points.length===4,'A continuous bent route was split into excess SVG gradient nodes'); +assert(Math.abs(pieces[0].startProgress)<1e-9&&Math.abs(pieces.at(-1).endProgress-1)<1e-9,'Route color blend does not span the full path'); +const splitPieces=pathStrokePieces([[[0,0],[30,0]],[[70,0],[70,40],[90,40]]],'#000000','#ffffff'); +assert(splitPieces.length===2,'A warp-separated route did not retain one gradient node per visible segment'); +assert(splitPieces[0].endColor===splitPieces[1].startColor,'Route color blend has a seam between visible segments'); +assert(splitPieces[0].endProgress`${r},${c}`,activePath:b=>b.path,boardCellCenter:([r,c])=>[16+(c+.5)*40,16+(r+.5)*40],pathTailAxis:()=> 'H', + getViewportRect:()=>({left:0,top:0,right:1000,bottom:800,width:1000,height:800}) +}; +vm.createContext(gestureContext); +vm.runInContext(`${functionSource('directionFromDelta')}\n${functionSource('gateHitBox')}\n${functionSource('edgePanVelocity')}\nthis.gesture={directionFromDelta,gateHitBox,edgePanVelocity};`,gestureContext); +assert(gestureContext.gesture.directionFromDelta(17,0)===null&&gestureContext.gesture.directionFromDelta(19,0)==='E','Single snap threshold is not applied directly'); +assert(gestureContext.gesture.directionFromDelta(19,18,'H')==='E'&&gestureContext.gesture.directionFromDelta(19,18,'V')==='S'&&gestureContext.gesture.directionFromDelta(0,-19)==='N','Diagonal drag does not select a stable orthogonal axis'); +const eastHit=gestureContext.gesture.gateHitBox([200,120],'E'),westHit=gestureContext.gesture.gateHitBox([200,120],'W'); +assert(eastHit.x<200&&eastHit.x+eastHit.width===200&&westHit.x===200&&westHit.x+westHit.width>200,'Gate hit boxes are not confined to the owning board'); +assert(eastHit.width<=gestureContext.CELL*.5&&westHit.width<=gestureContext.CELL*.5,'Gate hit boxes extend too far into the puzzle'); +const centerVelocity=gestureContext.gesture.edgePanVelocity(500,400),rightVelocity=gestureContext.gesture.edgePanVelocity(998,400); +assert(centerVelocity[0]===0&¢erVelocity[1]===0&&rightVelocity[0]>0,'Edge auto-pan velocity is not limited to the viewport edge'); +console.log('Direct snap threshold, outward gate reach, and edge auto-pan test passed'); + +const sampleContext={perfNow:()=>100,DRAG_MAX_POINTER_SAMPLES:12}; +vm.createContext(sampleContext); +vm.runInContext(`${functionSource('pointerEventSamples')}\n${functionSource('trimBoardPointerSamples')}\n${functionSource('setBoardPointerSample')}\n${functionSource('appendBoardPointerSamples')}\nthis.pointer={pointerEventSamples,appendBoardPointerSamples};`,sampleContext); +const cornerSamples=[ + {clientX:220,clientY:280},{clientX:140,clientY:280},{clientX:140,clientY:196}, + {clientX:290,clientY:196},{clientX:290,clientY:250} +],sampleBoard={pendingPointerMove:null}; +sampleContext.pointer.appendBoardPointerSamples(sampleBoard,{pointerId:12,pointerType:'pen',getCoalescedEvents:()=>cornerSamples}); +assert(JSON.stringify([sampleBoard.pendingPointerMove.clientX,sampleBoard.pendingPointerMove.clientY])===JSON.stringify([290,250])&&sampleBoard.pointerMoveSamples.length===1,'Pointer coalescing did not retain the newest sample in the bounded logic queue'); +const threeTurnPath={startGate:0,endGate:null,cells:[[4,6],[4,5],[4,4],[4,3],[4,2],[4,1],[3,1],[2,1],[2,2],[2,3],[3,3]]}; +assert(AppLogic.analyzePathTurns(threeTurnPath,[[4,6,'E']],[],false).count===3,'The pictured left-up-right-down pickup gesture does not display three turns'); +assert(!functionSource('extendPointerTo').includes('consumeBufferedPointerTurn'),'Pickup drag still extends a speculative second cell'); + +assert(!functionSource('renderDragFrame').includes('motion')&&!functionSource('extendPointerTo').includes('Motion'),'Line extension still performs animation bookkeeping'); +console.log('Bounded ordered-sample pickup gesture and immediate line rendering test passed'); + +const pickupState={paths:[{startGate:0,endGate:null,cells:[[0,0]]},{startGate:1,endGate:null,cells:[[0,1]]}]},pickupContext={ + manhattan:(a,b)=>Math.abs(a[0]-b[0])+Math.abs(a[1]-b[1]),ckey:(r,c)=>`${r},${c}`,cellSet:p=>p.validSet,metaState:()=>pickupState +}; +vm.createContext(pickupContext); +vm.runInContext(`${functionSource('openTipMergePlan')}\nthis.openTipMergePlan=openTipMergePlan;`,pickupContext); +const pickupBoard={id:'B0',p:{validSet:new Set(['0,0','0,1'])},drawing:{pathIndex:0}}; +assert(pickupContext.openTipMergePlan(pickupBoard,0,1)===null,'Adjacent pickups connected without occupying the same cell'); +assert(pickupContext.openTipMergePlan(pickupBoard,0,1,'end',true)?.merged.cells.length===2,'A knob explicitly moved into the other endpoint cell did not connect'); +pickupContext.metaState=()=>({paths:[{startGate:0,endGate:null,cells:[[0,0]]},{startGate:1,endGate:null,cells:[[0,0]]}]}); +const sameCellPickupBoard={id:'B0',p:{validSet:new Set(['0,0'])},drawing:{pathIndex:0}},sameCellMerge=pickupContext.openTipMergePlan(sameCellPickupBoard,0,1); +assert(sameCellMerge&&sameCellMerge.merged.cells.length===1,'Two pickups in the same cell did not merge without duplicating the cell'); + +const overlapState={paths:[ + {startGate:0,endGate:null,cells:[[0,0],[1,0],[1,1]]}, + {startGate:1,endGate:null,cells:[[0,2],[1,2],[1,1],[2,1]]} +]}; +pickupContext.metaState=()=>overlapState; +const overlapBoard={id:'B0',p:{validSet:new Set(['0,0','1,0','1,1','0,2','1,2','2,1'])},drawing:{pathIndex:0}}; +assert(pickupContext.openTipMergePlan(overlapBoard,0,1)===null,'Overlapping pickups were allowed to create a self-duplicated path'); + +const mergeState={paths:[ + {startGate:0,endGate:null,cells:[[0,0]]}, + {startGate:1,endGate:null,cells:[[0,0]]} +]}; +let cancelledMergeFrames=0,releasedMergePointers=0; +const mergeContext={ + metaState:()=>mergeState,manhattan:(a,b)=>Math.abs(a[0]-b[0])+Math.abs(a[1]-b[1]),ckey:(r,c)=>`${r},${c}`,cellSet:p=>p.validSet, + cancelBoardDragFrame:()=>cancelledMergeFrames++, + applyBoardCommand:(_board,mutate)=>mutate()===false?false:true, + safeRelease:()=>releasedMergePointers++,playSound:()=>{},queueMicrotask:callback=>callback(),reconcileDrawingPresentation:()=>{} +}; +vm.createContext(mergeContext); +vm.runInContext(`${functionSource('openTipMergePlan')}\n${functionSource('joinTips')}\nthis.joinTips=joinTips;`,mergeContext); +const mergeBoard={id:'B0',p:{validSet:new Set(['0,0'])},drawing:{pathIndex:0,pointerId:17},svg:{},joiningPaths:false}; +assert(mergeContext.joinTips(mergeBoard,0,1),'Same-cell pickup merge was rejected'); +assert(mergeState.paths.length===1&&mergeState.paths[0].cells.length===1,'Pickup merge duplicated the shared cell'); +assert(mergeBoard.drawing===null&&cancelledMergeFrames===1&&releasedMergePointers===1,'Pickup merge did not terminate its pending frame and pointer capture exactly once'); +assert(!app.includes('function pickupJoinStepsAtPoint')&&!app.includes('function gateConnectionSteps'),'Removed adjacent pickup/gate auto-bridging code is still present'); + +let cameraCallback=null,cameraTimerCallback=null,cameraApplies=0; +const cameraContext={ + CAMERA_DISPLAY_WATCHDOG_MS:18,DRAG_FRAME_INTERVAL:1000/60, + cam:{x:0,y:0,scale:1},cameraInteractionFrame:0,cameraInteractionDelayTimer:0,cameraInteractionLastDraw:0,pendingCameraInteraction:null, + requestAnimationFrame:callback=>{cameraCallback=callback;return 7},cancelAnimationFrame:()=>{},setTimeout:callback=>{cameraTimerCallback=callback;return 8},clearTimeout:()=>{},applyCamera:immediate=>{assert(immediate===true,'Queued camera update was not committed directly')}, + perfNow:()=>100,perfStart:()=>0,perfEnd:()=>0,perfCount:()=>{},recordInteractionCommit:()=>{},observeInteractionFrame:()=>{},data:{cameraAnchor:null},currentCameraAnchor:()=>({centerX:0,centerY:0,scale:1}),markGlobalDirty:()=>{}, +}; +vm.createContext(cameraContext); +vm.runInContext(`${functionSource('commitCameraInteraction')}\n${functionSource('queueCameraInteraction')}\nthis.queueCameraInteraction=queueCameraInteraction;`,cameraContext); +cameraContext.applyCamera=()=>cameraApplies++; +cameraContext.queueCameraInteraction({x:10,y:20,scale:1});cameraContext.queueCameraInteraction({x:30,y:40,scale:1}); +assert(cameraContext.cameraInteractionFrame===7&&cameraContext.cam.x===0,'Raw pan events were applied before the animation frame'); +cameraCallback(); +assert(cameraContext.cam.x===30&&cameraContext.cam.y===40&&cameraApplies===1,'Pan events were not coalesced to the latest frame'); +cameraTimerCallback(); +assert(cameraApplies===1,'Camera watchdog duplicated a completed animation-frame commit'); +console.log('Pickup capture and frame-coalesced panning test passed'); + +const blankTarget={closest:()=>null}; +const card=(solved=false)=>({dataset:{id:'B0'},classList:{contains:name=>name==='solved'&&solved}}); +const targetForCard=board=>({closest:selector=>selector==='.board-card'?board:null}); +const panContext={pendingFieldItemPlacement:null,data:{metas:{B0:{id:'B0'}}},metaState:()=>({solved:false})}; +vm.createContext(panContext); +vm.runInContext(`${functionSource('leftFieldPanAllowed')}\nthis.leftFieldPanAllowed=leftFieldPanAllowed;`,panContext); +assert(panContext.leftFieldPanAllowed({button:0,target:blankTarget}),'Blank undiscovered field did not allow left-drag panning'); +assert(!panContext.leftFieldPanAllowed({button:0,target:targetForCard(card(false))}),'Unsolved puzzle area stole left-drag for field panning'); +assert(panContext.leftFieldPanAllowed({button:0,target:targetForCard(card(true))}),'Solved puzzle area did not allow left-drag panning'); +assert(!panContext.leftFieldPanAllowed({button:2,target:blankTarget}),'Left-pan eligibility accepted a non-left button'); +console.log('Solved/undiscovered left-drag pan eligibility test passed'); + +const overviewContext={ + metaState:id=>({solved:id==='S'}),currentLineGraphCaches:()=>({components:new Map(),geometries:new Map()}), + collectConnectedLineComponent:()=>[],minimapGeometryForComponent:()=>({segments:[]}),MINIMAP_LONG_LINE:4 +}; +vm.createContext(overviewContext); +vm.runInContext(`${functionSource('drawMapBoardCells')}\nthis.drawMapBoardCells=drawMapBoardCells;`,overviewContext); +let fills=0,strokes=0;const mapContext={beginPath:()=>{},rect:()=>{},fill:()=>{fills++},stroke:()=>{strokes++},set fillStyle(_value){}}; +overviewContext.drawMapBoardCells(mapContext,[{id:'S',x:0,y:0,chunks:[[0,0]]},{id:'U',x:1,y:0,chunks:[[0,0]]}],x=>x,y=>y,1); +assert(fills===2&&strokes===0,'Minimap/overview board renderer still draws board outlines'); +assert(functionSource('rebuildWorldOverviewCache').includes('drawMapBoardCells')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines')&&!functionSource('rebuildWorldOverviewCache').includes('storeCellForMeta'),'Far overview does not exactly reuse minimap board/line rendering'); +console.log('Borderless shared minimap/overview renderer test passed'); + +let resetRenderCount=0,resetChangeCount=0,resetSyncCount=0; +const resetState={paths:[{cells:[[0,0],[0,1]]}],specialProgress:{crossings:[[0,0]]},solved:false}; +const resetBoard={id:'B0',drawing:{pointerId:9},armedGate:1,solvedPathsRendered:true,svg:{hasPointerCapture:()=>true,releasePointerCapture:()=>{}}}; +const resetContext={ + rendered:new Map(),activeBoard:'B0',metaState:()=>resetState,cancelBoardDragFrame:()=>{},clearTipMotion:()=>{}, + applyBoardCommand:(board,mutate,options)=>{mutate(resetState);if(options.persist)resetChangeCount++}, + renderBoardNow:()=>resetRenderCount++,updateSelectedProgress:()=>{},queueMicrotask:callback=>callback(),reconcileDrawingPresentation:()=>{}, + syncBoundaryConnections:()=>resetSyncCount++,toast:()=>{} +}; +vm.createContext(resetContext); +vm.runInContext(`${functionSource('resetSelectedBoard')}\nthis.resetSelectedBoard=resetSelectedBoard;`,resetContext); +resetContext.resetSelectedBoard(resetBoard); +assert(resetState.paths.length===0&&resetState.specialProgress.crossings.length===0,'First reset click did not clear the puzzle'); +assert(resetRenderCount===1&&resetChangeCount===1&&resetSyncCount===0,'Reset was not immediately rendered and persisted exactly once'); +console.log('First-click reset test passed'); + +const shopItems=[{id:'O1'},{id:'O2'},...Array.from({length:12},(_,index)=>({id:`C${index+1}`,cursorStyle:`face-${index+1}`}))]; +assert(functionSource('renderStorePanel').includes('storeInventoryItems(meta,store)')&&functionSource('renderStorePanel').includes("'store-cursor':'store-other'"),'Shop rendering bypasses the fixed 2+12 item inventory'); +const storeRateContext={ + STORE_CHANCE:.10,hash32:value=>value>>>0,LOCAL_SOLVER:'tester',STORE_PRICE_VERSION:1,SCORE_VERSION:3, + trustedNow:()=>1000,currentPlayerName:()=>"tester",seededStoreItemIds:()=>shopItems.map(item=>item.id),storePriceDetails:()=>({coefficient:1}) +}; +vm.createContext(storeRateContext); +vm.runInContext(`${functionSource('maybeOpenStore')}\nthis.maybeOpenStore=maybeOpenStore;`,storeRateContext); +const eligibleStoreState=()=>({solved:true,store:null,paths:[{cells:[[0,0],[0,1]]}]}); +const belowThreshold=eligibleStoreState(),atThreshold=eligibleStoreState(); +storeRateContext.maybeOpenStore({seed:1},belowThreshold,0,.099999); +storeRateContext.maybeOpenStore({seed:1},atThreshold,0,.10); +assert(belowThreshold.store&&atThreshold.store===null,'Shop appearance boundary is not exactly 1/10'); + +assert(!app.includes('function overviewShopAtClient(')&&!functionSource('beginPan').includes('overviewShopAtClient'),'Zoomed-out view still has a shop-only hit target absent from the minimap'); + +const orphanState={paths:[{startGate:0,endGate:null,cells:[[0,0]]}]},orphanContext={ + metaState:()=>orphanState,applyBoardCommand:(_board,mutate)=>{mutate();return true} +}; +vm.createContext(orphanContext); +vm.runInContext(`${functionSource('discardUnmovedCreatedPath')}\nthis.discardUnmovedCreatedPath=discardUnmovedCreatedPath;`,orphanContext); +const orphanBoard={id:'B0',drawing:{pathIndex:0,createdPath:true},armedGate:0}; +assert(orphanContext.discardUnmovedCreatedPath(orphanBoard)&&orphanState.paths.length===0&&orphanBoard.drawing===null,'Cancelled one-cell drag leaves an orphan pickup'); + +const numberNode={ + classList:{on:false,toggle(_name,value){this.on=value}}, + style:{value:null,setProperty(_name,value){this.value=value},removeProperty(){this.value=null}} +},numberContext={ + ckey:(r,c)=>`${r},${c}`,metaState:()=>({paths:[{cells:[[0,0]]}]}),occupiedMap:()=>new Map([['0,0',0]]),pathColorAtCell:()=> '#72e38f' +}; +vm.createContext(numberContext); +vm.runInContext(`${functionSource('refreshDragNumberColors')}\nthis.refreshDragNumberColors=refreshDragNumberColors;`,numberContext); +numberContext.refreshDragNumberColors({id:'B0',p:{},drawing:{pathIndex:0,segmentOccupancy:new Map([['0,0',0]])},numberNodes:new Map([['0,0',numberNode]])}); +assert(numberNode.classList.on&&numberNode.style.value==='#72e38f'&&functionSource('renderDragFrame').includes('refreshDragNumberColors(b)'),'Number color does not update during pickup dragging'); + +const detachedState={paths:[{startGate:0,endGate:null,openGate:null,cells:[[0,0],[0,1],[0,2]],colorIndex:2,startColorIndex:2,endColorIndex:null}]}; +const detachedContext={ + metaState:()=>detachedState,applyBoardCommand:(_board,mutate)=>{mutate();return true}, + drawingFromGate:(_board,pathIndex,gateIndex,pointerId)=>({pathIndex,gateIndex,pointerId}) +}; +vm.createContext(detachedContext); +vm.runInContext(`${functionSource('pathUsesGate')}\n${functionSource('detachPathFromStartGate')}\n${functionSource('reverseDetachedPath')}\nthis.detached={pathUsesGate,detachPathFromStartGate,reverseDetachedPath};`,detachedContext); +const detachedBoard={id:'B0',drawing:null,armedGate:null}; +assert(detachedContext.detached.detachPathFromStartGate(detachedBoard,0,0,19),'Dragging the gate of an open line did not detach its anchored end'); +const detachedPath=detachedState.paths[0]; +assert(detachedPath.detachedStart&&JSON.stringify(detachedPath.cells)==='[[0,2],[0,1],[0,0]]','Detached line does not retain two oriented edge pickups'); +assert(detachedContext.detached.pathUsesGate(detachedPath,0)&&detachedBoard.drawing.pointerId===19,'New gate-side pickup is not associated with the active drag'); +assert(!functionSource('renderBoardNow').includes("whitePickupEnd")&&functionSource('renderBoardNow').includes("'data-endpoint-side':'start'"),'Two-ended line does not render both colored pickup handles'); +const normalizeDetachedContext={isPlainObject:value=>value&&typeof value==='object'&&!Array.isArray(value),LINE_COLORS:Array(10).fill('#000')}; +vm.createContext(normalizeDetachedContext); +vm.runInContext(`${functionSource('normalizePath')}\nthis.normalizePath=normalizePath;`,normalizeDetachedContext); +const normalizedDetached=normalizeDetachedContext.normalizePath(detachedPath); +assert(normalizedDetached.detachedStart&&!('whitePickupEnd' in normalizedDetached),'Two-ended pickup state is lost or retains the obsolete white pickup marker during persistence normalization'); + +pickupContext.metaState=()=>({paths:[ + {startGate:0,endGate:null,detachedStart:true,cells:[[0,0],[0,1]],colorIndex:2,startColorIndex:2,endColorIndex:2}, + {startGate:1,endGate:null,cells:[[0,3],[0,2],[0,1]],colorIndex:4,startColorIndex:4,endColorIndex:null} +]}); +const detachedMergeBoard={id:'B0',p:{validSet:new Set(['0,0','0,1','0,2','0,3'])},drawing:{pathIndex:0}}, + detachedMerge=pickupContext.openTipMergePlan(detachedMergeBoard,0,1); +assert(detachedMerge&&!detachedMerge.merged.detachedStart&&detachedMerge.merged.startGate===1&&detachedMerge.merged.endGate==null&&JSON.stringify(detachedMerge.merged.cells)==='[[0,3],[0,2],[0,1],[0,0]]','An isolated line could not merge into an anchored pickup'); +pickupContext.metaState=()=>({paths:[ + {startGate:0,endGate:null,cells:[[0,0]],colorIndex:1,startColorIndex:1}, + {startGate:1,endGate:null,detachedStart:true,cells:[[0,0],[0,1],[0,2]],colorIndex:3,startColorIndex:3,endColorIndex:3} +]}); +const detachedStartMerge=pickupContext.openTipMergePlan(detachedMergeBoard,0,1,'start'); +assert(detachedStartMerge&&JSON.stringify(detachedStartMerge.merged.cells)==='[[0,0],[0,1],[0,2]]','The opposite endpoint of an isolated line could not be joined'); + +const graphData={metas:{A:{id:'A',puzzle:{}},B:{id:'B',puzzle:{}}},states:{ + A:{paths:[{startGate:0,endGate:null,detachedStart:true,cells:[[0,0],[0,1]]}]}, + B:{paths:[{startGate:0,endGate:null,cells:[[0,0],[1,0],[2,0]]}]} +}}; +const graphEnvironment={data:graphData,metaState:id=>graphData.states[id],matchingNeighborGate:meta=>meta.id==='A'?{meta:graphData.metas.B,gateIndex:0}:{meta:graphData.metas.A,gateIndex:0}}; +const detachedComponent=AppLogic.collectConnectedLineComponent(graphData.metas.A,0,new Map(),graphEnvironment); +assert(detachedComponent.members.length===1&&detachedComponent.length===2,'Detached pickup still inherits thickness through its former gate'); + +const reconnectState={paths:[{startGate:0,endGate:null,openGate:0,detachedStart:true,cells:[[0,1],[0,0]],colorIndex:2,startColorIndex:2,endColorIndex:2}]},gateEffects=[]; +const reconnectContext={ + metaState:()=>reconnectState,usedGateSet:()=>new Set(),LINE_COLORS:['#0af','#fa0','#0f0'],canonicalGateColorIndex:()=>1, + applyBoardCommand:(_board,mutate)=>{mutate();return true},neighborColor:()=>null,playSound:()=>{}, + usesLightweightDragOverlay:()=>false,requestAnimationFrame:callback=>callback(), + gateConnectEffect:(_board,_gate,color)=>gateEffects.push(color),queueMicrotask:callback=>callback(),reconcileDrawingPresentation:()=>{} +}; +vm.createContext(reconnectContext); +vm.runInContext(`${functionSource('finalizeAtGate')}\nthis.finalizeAtGate=finalizeAtGate;`,reconnectContext); +const reconnectBoard={id:'B0',p:{g:[[0,0,'W']]},drawing:{pathIndex:0},armedGate:0}; +assert(reconnectContext.finalizeAtGate(reconnectBoard,0),'Detached pickup could not reconnect to its original gate'); +assert(!reconnectState.paths[0].detachedStart&&reconnectState.paths[0].startGate===0&&gateEffects[0]==='#0f0','Reconnected two-ended line did not collapse to one anchored line with its line-color effect'); +console.log('Shop access/order, orphan cleanup, live number color, and dual-pickup tests passed'); + +const rewardContext={MAX_SCORE:1000000000,anomalyScoreMultiplier:()=>1}; +vm.createContext(rewardContext); +vm.runInContext(`${functionSource('scoreFromThickness')}\nthis.scoreFromThickness=scoreFromThickness;`,rewardContext); +const smallEasy=rewardContext.scoreFromThickness({level:2,chunks:[[0,0]]},20), + smallHard=rewardContext.scoreFromThickness({level:10,chunks:[[0,0]]},20), + largeHard=rewardContext.scoreFromThickness({level:10,chunks:Array.from({length:8},(_,index)=>[index,0])},20); +assert(smallHard>smallEasy*8&&largeHard>smallHard*2.5,'Hard and large puzzle rewards do not scale strongly enough with expected solve time'); + +const gateArmingState={paths:[{startGate:0,endGate:null,openGate:0,cells:[[0,0]]}]},gateArmingContext={ + metaState:()=>gateArmingState,sameCell:(a,b)=>a&&b&&a[0]===b[0]&&a[1]===b[1] +}; +vm.createContext(gateArmingContext); +vm.runInContext(`${functionSource('updateGateSnapArming')}\nthis.updateGateSnapArming=updateGateSnapArming;`,gateArmingContext); +const gateArmingBoard={id:'B0'},gateDrawing={pathIndex:0,originGate:0,originGateCell:[0,0],gateSnapArmed:false,leftOriginGateCell:false}; +gateArmingContext.updateGateSnapArming(gateArmingBoard,gateDrawing); +assert(!gateDrawing.gateSnapArmed&&!gateDrawing.leftOriginGateCell,'A connected gate rearmed before the line endpoint left its gate cell'); +gateArmingState.paths[0].cells.push([0,1]);gateArmingContext.updateGateSnapArming(gateArmingBoard,gateDrawing); +assert(gateDrawing.gateSnapArmed&&gateDrawing.leftOriginGateCell,'Dragging a connected gate endpoint out of its cell did not unlock movement'); +assert(functionSource('extendPointerTo').includes("snappedGate===drawing.originGate&&!drawing.leftOriginGateCell"),'The origin gate can recapture a connected endpoint before it moves away'); + +const disappearingState={paths:[{startGate:0,endGate:null,detachedStart:true,cells:[[0,0],[0,1]],colorIndex:1,startColorIndex:1,endColorIndex:1}]}; +let disappearingReleased=0,disappearingPersisted=0; +const disappearingContext={ + CELL:40,metaState:()=>disappearingState,boardCellCenter:([r,c])=>[20+c*40,20+r*40],pointerLocalRadius:(_board,pixels)=>pixels, + cancelBoardDragFrame:()=>{},clearTipMotion:()=>{},applyBoardCommand:(_board,mutate,options)=>{const result=mutate();if(result===false)return false;if(options?.persist)disappearingPersisted++;return true}, + safeRelease:()=>disappearingReleased++,playSound:()=>{},queueMicrotask:callback=>callback(),reconcileDrawingPresentation:()=>{} +}; +vm.createContext(disappearingContext); +vm.runInContext(`${functionSource('removeDetachedPathAtOwnEndpoint')}\nthis.removeDetachedPathAtOwnEndpoint=removeDetachedPathAtOwnEndpoint;`,disappearingContext); +const disappearingBoard={id:'B0',drawing:{pathIndex:0,pointerId:23},armedGate:null,svg:{}}; +assert(!disappearingContext.removeDetachedPathAtOwnEndpoint(disappearingBoard,[60,20]),'A two-ended line vanished before its handles overlapped'); +assert(disappearingContext.removeDetachedPathAtOwnEndpoint(disappearingBoard,[20,20]),'Overlapping both handles did not remove the two-ended line'); +assert(disappearingState.paths.length===0&&disappearingBoard.drawing===null&&disappearingReleased===1&&disappearingPersisted===1,'Two-ended line removal did not cleanly persist and release pointer capture'); +console.log('Connected-gate dragging and two-handle line disappearance tests passed'); + +assert(!app.includes('RETRACTION_HOLD_MS')&&!app.includes('pendingRetraction'),'Own-line retraction still has a delayed state'); +assert(functionSource('extendOne').includes('path.cells.splice(rewind+1)')&&functionSource('extendPointerTo').includes('rewindActivePathToCell(b,targetCell'),'Original immediate own-line shortening was not restored'); +assert(functionSource('gateFromPointOrCell').includes('maxPixels:0')&&!functionSource('extendPointerTo').includes('gateConnectionSteps'),'A knob can still snap to a gate from an adjacent cell'); +assert(functionSource('shouldTeleportToUnsolvedBoard').includes('return false')&&!functionSource('bindBoard').includes('centerMeta(')&&!app.includes('function promoteStaticBoard('),'Board input can still promote a summary or teleport the camera'); +assert(functionSource('matchingNumberKeys').includes('partialTurnCount')&&functionSource('updateNumberMatchFeedback').includes("classList.toggle('number-match',current.size>0)")&&functionSource('updateNumberMatchFeedback').includes("matchOrbit.classList.toggle('show'")&&css.includes('@keyframes numberMatchOrbit')&&css.includes('stroke-dasharray:1.2 5.35'),'Matching turn-number feedback does not keep an enlarged knob with a rotating perforated orbit'); +assert(functionSource('renderBoardNow').includes("'\\u66f2\\u304c\\u308b'")&&app.includes('numberLayer.append(node,warning)')&&css.includes('.number-turn-warning.show{opacity:1}'),'The 曲がる warning is not prominent or is behind the number'); +console.log('Immediate retraction, exact-cell gate, no-click-teleport, and number-match feedback guards passed'); diff --git a/test/mirror-chunk-smoke-test.js b/test/mirror-chunk-smoke-test.js new file mode 100644 index 0000000..0025b68 --- /dev/null +++ b/test/mirror-chunk-smoke-test.js @@ -0,0 +1,38 @@ +'use strict'; +const {vm,app,read,assert,functionSource}=require('./helpers/app-source'); + +const checkpointSource=functionSource('runMirrorCheckpoint'),benchmark=read('test/browser-performance-benchmark.js'); +assert(checkpointSource.includes("build.stage==='serialize'")&&checkpointSource.includes('writeMirrorBuildChunk(build)'),'Idle mirror checkpoint does not serialize and write bounded chunks'); +assert(!checkpointSource.includes('writeCompactMirror(')&&!checkpointSource.includes('JSON.stringify(snapshot)'),'Idle mirror checkpoint still performs a full synchronous mirror write'); +assert(app.includes("MIRROR_CHUNK_BYTES=64*1024")&&app.includes("MIRROR_CHUNK_FORMAT='bend-field-chunked-v1'"),'Chunked mirror format or size bound is missing'); +assert(!benchmark.includes('33*multiplier')&&!benchmark.includes('20*multiplier')&&!benchmark.includes('40*multiplier'),'CPU-throttled benchmark thresholds are still multiplied beyond the documented targets'); +assert(benchmark.includes("name:'complex'")&&benchmark.includes("chunks:10")&&benchmark.includes("name:'long-line'"),'Browser benchmark does not cover a ten-chunk board and a long connected line'); + +const serializerContext={JSON,Number,Object,Array}; +vm.createContext(serializerContext); +vm.runInContext(`${functionSource('jsonValueFragments')}\nthis.jsonValueFragments=jsonValueFragments;`,serializerContext); +const sample={schema:30,title:'曲線\\"test',enabled:true,missing:null,metas:{A:{id:'A',chunks:[[0,0],[1,0]],puzzle:{valid:[[0,0],[0,1]]}}},states:{A:{paths:[{cells:[[0,0],[0,1]]}]}}}; +const serialized=[...serializerContext.jsonValueFragments(sample)].join(''); +assert(serialized===JSON.stringify(sample)&&JSON.parse(serialized).metas.A.chunks.length===2,'Incremental JSON serializer changed the mirror payload'); + +const storage=new Map(),manifest={format:'bend-field-chunked-v1',generation:'g1',chunks:3,bytes:11,updatedAt:12}; +storage.set('save',JSON.stringify(manifest));storage.set('chunk:g1:0','{"a":');storage.set('chunk:g1:1','[1,2]');storage.set('chunk:g1:2','}'); +const readContext={ + storageKey:'save',mirrorChunkPrefix:'chunk:',MIRROR_CHUNK_FORMAT:'bend-field-chunked-v1',LOCAL_MIRROR_MAX_BYTES:1024,MIRROR_CHUNK_BYTES:4, + safeLocalGet:key=>storage.has(key)?storage.get(key):null,JSON,Number,Math +}; +vm.createContext(readContext); +vm.runInContext(`${functionSource('mirrorChunkKey')}\n${functionSource('parseMirrorManifest')}\n${functionSource('readCompactMirrorRaw')}\nthis.readCompactMirrorRaw=readCompactMirrorRaw;`,readContext); +assert(readContext.readCompactMirrorRaw()==='{"a":[1,2]}','Chunked mirror manifest was not reconstructed correctly'); +storage.delete('chunk:g1:1');assert(readContext.readCompactMirrorRaw()===null,'Incomplete chunked mirror was accepted'); + +const writes=[],chunkContext={ + mirrorChunkKey:(generation,index)=>`chunk:${generation}:${index}`,safeLocalSet:(key,value)=>{writes.push([key,value]);return true}, + perfStart:()=>0,perfNow:()=>3,perfGauge:()=>{},perfEnd:()=>{},perfCount:()=>{} +}; +vm.createContext(chunkContext); +vm.runInContext(`${functionSource('writeMirrorBuildChunk')}\nthis.writeMirrorBuildChunk=writeMirrorBuildChunk;`,chunkContext); +const build={generation:'g2',chunkIndex:0,pendingChunk:'x'.repeat(65536),bytes:0}; +assert(chunkContext.writeMirrorBuildChunk(build)===true&&writes.length===1&&writes[0][1].length===65536&&build.pendingChunk===''&&build.bytes===65536,'Mirror chunk writer did not perform exactly one bounded write'); + +console.log('Chunked recovery mirror and strict performance acceptance regression passed'); diff --git a/test/performance-smoke-test.js b/test/performance-smoke-test.js new file mode 100644 index 0000000..4eb75e1 --- /dev/null +++ b/test/performance-smoke-test.js @@ -0,0 +1,174 @@ +'use strict'; +const {vm,app,css,assert,functionSource}=require('./helpers/app-source'); + +const flashSource=functionSource('flashConfirmedCell'),eventSource=functionSource('eventToSvg'), + dragSource=functionSource('renderDragFrame'),extendSource=functionSource('extendPointerTo'), + extendOneSource=functionSource('extendOne'),bindBoardSource=functionSource('bindBoard'), + dragCacheSource=functionSource('buildDragCache'), + minimapSource=functionSource('drawMinimap'),minimapBuildSource=functionSource('rebuildMinimapWorld'), + widthRefreshSource=functionSource('refreshRenderedLineWidths'),ensureBoardsSource=functionSource('ensureBoards'); + +assert(!flashSource.includes('getBoundingClientRect')&&!flashSource.includes('querySelector'),'Cell confirmation flash performs a layout read or DOM query'); +assert(flashSource.includes("svgEl('rect'")&&flashSource.includes('b.dragLayer.append(node)')&&flashSource.includes("typeof node.animate==='function'"),'Cell confirmation flash does not use one temporary SVG node and WAAPI'); +assert(!functionSource('makeBoard').includes('cellHits.set')&&functionSource('makeBoard').includes('cellFlashAnimations:new Map()'),'Board construction still retains one hit node per cell or lacks animation tracking'); +assert(!eventSource.includes('getBoundingClientRect')&&eventSource.includes('boardScreenRect(b)'),'Pointer conversion reads live SVG layout'); +assert(!functionSource('pointerLocalRadius').includes('getBoundingClientRect')&&functionSource('edgePanVelocity').includes('getViewportRect()'),'High-frequency input still reads live element geometry'); + +assert(extendSource.includes('renderDragFrame(b)'),'Pointer drawing does not enter the drag-only renderer'); +assert(!extendSource.includes('invalidateLineGraphCaches')&&!extendSource.includes('queueLineWidthRefresh'),'Pointer frames still invalidate or traverse connected-line components'); +assert(dragSource.includes('drawingLineWidth(b,index)')&&functionSource('drawingLineWidth').includes('drawing.lineWidth=width'),'Drag width is not fixed for the active gesture'); +assert(dragSource.includes('pathVisualNodes')&&dragSource.includes('dragCache')&&dragSource.includes('refreshDragSpecialState')&&dragCacheSource.includes('b.dragLayer')&&dragCacheSource.includes('strokes'),'Drag-only renderer does not retain and selectively replace active visuals'); +for(const forbidden of ['pathValid(','straightNumberWarningKeys(','multipleNumberWarningKeys(','queueLineWidthRefresh(','renderBoard(b)'])assert(!dragSource.includes(forbidden),`Drag-only renderer performs full-render work: ${forbidden}`); +assert(functionSource('processBoardDragFrame').includes('extendPointerTo(b,point,true)')&&!functionSource('processBoardDragFrame').includes('renderBoard('),'Pointer RAF does not defer raw-sample rendering to one drag-frame repaint'); +assert(extendOneSource.includes('isCrossing=b.crossingKeySet.has(key)')&&extendOneSource.includes('currentOwner=')&&extendOneSource.includes('occ.get(exitKey)'),'Ordinary drag occupancy bypasses its retained map'); +assert(extendOneSource.includes('if(!suppressMotion){flashConfirmedCell')&&(extendSource.match(/emitBatchedFeedback\(\)/g)||[]).length===2,'Batched pointer traversal emits per-cell confirmation effects'); +assert((bindBoardSource.match(/b\.drawing\.keyboardActive=true/g)||[]).length>=2,'Keyboard continuation does not keep its board at interactive LOD'); + +assert(functionSource('renderedConnectedLineWidth').includes('currentLineGraphCaches()')&&functionSource('markStateDirty').includes('invalidateLineGraphCaches(id,pathIndex)'),'Connected-line caches are not persistent or invalidated by the touched state/path'); +assert(widthRefreshSource.includes('board.pathStrokeNodes')&&widthRefreshSource.includes('board.connectorNodes'),'Line-width refresh does not prefer retained SVG node references'); +assert(!widthRefreshSource.includes('querySelectorAll'),'Line-width refresh retains a DOM-query fallback'); +assert(minimapBuildSource.includes('minimapCache={revision:minimapWorldRevision')&&minimapSource.includes('if(stale)rebuildMinimapWorld')&&minimapSource.includes('drawImage(minimapBase'),'Minimap does not retain and reuse an overscanned world layer'); +assert(functionSource('drawMapLongLines').includes('minimapGeometryForComponent(component,caches.geometries)'),'Long-line minimap geometry is recalculated during every world-layer rebuild'); +assert(functionSource('markStateDirty').includes('invalidateWorldPresentation()'),'State changes do not invalidate minimap/overview content'); +assert(app.includes('globalThis.BEND_PERF=')&&functionSource('perfObserve').includes('samples.length>240'),'Performance measurements are missing or unbounded'); +assert(css.includes('#noiseCanvas.interaction-muted')&&css.includes('body.reduced-effects #noiseCanvas')&&functionSource('scheduleNoiseBackground').includes('noisePainted')&&!functionSource('scheduleNoiseBackground').includes('setTimeout'),'Decorative noise is not static or interaction/reduced-motion suppression is missing'); +assert((ensureBoardsSource.match(/changes=3&&ensureBoardsSource.includes('if(pending)scheduleLodPass()'),'LOD creation or eviction bypasses the per-pass budget'); +assert(functionSource('desiredInteractiveBoardIds').includes('for(const id of ids)')&&!app.includes('INTERACTIVE_DETAIL_CELL_BUDGET'),'Visible-puzzle detail selection is incomplete or still budget-culls boards'); +assert(ensureBoardsSource.includes("perfGauge('visibleUnsolvedBoards'"),'LOD performance telemetry cannot prove visible unsolved-board coverage'); + +const visibleIdsForDetail=new Set(Array.from({length:30},(_,index)=>`B${index}`)), + visibleContext={data:{metas:Object.fromEntries([...visibleIdsForDetail].map(id=>[id,{id,x:Number(id.slice(1)),y:0,puzzle:{bounds:{w:5,h:5},valid:Array.from({length:25})}}]))}}; +vm.createContext(visibleContext); +vm.runInContext(`${functionSource('desiredInteractiveBoardIds')}\nthis.desiredInteractiveBoardIds=desiredInteractiveBoardIds;`,visibleContext); +const desiredVisible=visibleContext.desiredInteractiveBoardIds(visibleIdsForDetail); +assert(desiredVisible.has('B0')&&desiredVisible.size===visibleIdsForDetail.size,'Not every visible hydrated puzzle remained detailed'); + +let animationStarts=0,animationCancels=0,flashRemovals=0; +function makeFlashNode(){return{animate(){animationStarts++;const animation={cancel(){animationCancels++},finished:Promise.resolve()};return animation},remove(){flashRemovals++}}} +const flashContext={ + PAD:10,CELL:40,ckey:(r,c)=>`${r},${c}`,svgEl:()=>makeFlashNode(),setTimeout:callback=>{callback();return 1},clearTimeout(){} +}; +vm.createContext(flashContext); +vm.runInContext(`${flashSource}\nthis.flashConfirmedCell=flashConfirmedCell;`,flashContext); +const flashBoard={svg:{isConnected:true},dragLayer:{append(){}},cellFlashAnimations:new Map()}; +flashContext.flashConfirmedCell(flashBoard,[1,2]);flashContext.flashConfirmedCell(flashBoard,[1,2]); +assert(animationStarts===2&&animationCancels>=1&&flashBoard.cellFlashAnimations.size<=1,'Repeated cell confirmation did not replace one temporary animation'); + +let rectCalls=0; +const eventContext={boardScreenRect:()=>{rectCalls++;return{left:10,top:20,width:200,height:100}}}; +vm.createContext(eventContext); +vm.runInContext(`${eventSource}\nthis.eventToSvg=eventToSvg;`,eventContext); +const eventBoard={svg:{viewBox:{baseVal:{width:400,height:200}},getBoundingClientRect(){throw new Error('event conversion forced layout')}}}, + mapped=eventContext.eventToSvg(eventBoard,{clientX:110,clientY:70}); +assert(rectCalls===1&&mapped[0]===200&&mapped[1]===100,'Cached board geometry produced incorrect pointer coordinates'); + +const cacheContext={lineGraphRevision:0,lineGraphCacheRevision:-1,lineComponentCache:new Map(),lineWidthCache:new Map(),lineMinimapGeometryCache:new WeakMap(),data:{metas:{},states:{}},linePathKey:(id,index)=>`${id}:${index}`,matchingNeighborGate:()=>null}; +vm.createContext(cacheContext); +vm.runInContext(`${functionSource('invalidateLineGraphCaches')}\n${functionSource('currentLineGraphCaches')}\nthis.logic={invalidateLineGraphCaches,currentLineGraphCaches};`,cacheContext); +const firstCaches=cacheContext.logic.currentLineGraphCaches(),secondCaches=cacheContext.logic.currentLineGraphCaches(); +assert(firstCaches.components===secondCaches.components&&firstCaches.widths===secondCaches.widths,'Connected-line caches do not persist across reads'); +cacheContext.logic.invalidateLineGraphCaches();const thirdCaches=cacheContext.logic.currentLineGraphCaches(); +assert(thirdCaches.components!==firstCaches.components&&thirdCaches.widths!==firstCaches.widths,'Connected-line invalidation reused stale cache maps'); +const shared={members:[[{id:'A'},0],[{id:'B'},0]],length:10},unrelated={members:[[{id:'C'},0]],length:3}; +thirdCaches.components.set('A:0',shared);thirdCaches.components.set('B:0',shared);thirdCaches.components.set('C:0',unrelated); +thirdCaches.widths.set('A:0',5);thirdCaches.widths.set('B:0',5);thirdCaches.widths.set('C:0',4); +cacheContext.logic.invalidateLineGraphCaches('A',0); +assert(!thirdCaches.components.has('A:0')&&!thirdCaches.components.has('B:0')&&thirdCaches.components.get('C:0')===unrelated&&thirdCaches.widths.has('C:0'),'Path-granular invalidation cleared an unrelated connected-line component'); + +const widthWrites=[],meta={id:'A',puzzle:{}}, + pathNode={dataset:{pathIndex:'0'},style:{setProperty:(name,value)=>widthWrites.push(['path',name,value])}}, + connectorNode={dataset:{ownerBoard:'A',pathIndex:'0'},style:{setProperty:(name,value)=>widthWrites.push(['connector',name,value])}}, + widthBoard={id:'A',meta,pathStrokeNodes:[pathNode],connectorNodes:[connectorNode],pathLayer:{querySelectorAll(){throw new Error('path DOM scan')}},connectorLayer:{querySelectorAll(){throw new Error('connector DOM scan')}}}; +const widthContext={ + rendered:new Map([['A',widthBoard]]),data:{metas:{A:meta}},metaState:()=>({paths:[{}]}), + linePathKey:(id,index)=>`${id}:${index}`,splitLinePathKey:key=>{const split=key.lastIndexOf(':');return[key.slice(0,split),Number(key.slice(split+1))]}, + currentLineGraphCaches:()=>({components:new Map(),widths:new Map()}), + collectConnectedLineComponent:()=>({members:[[meta,0]]}),renderedConnectedLineWidth:()=>8, + applyLineWidth:(node,width)=>node.style.setProperty('--line-width',width.toFixed(2)), + perfStart:()=>0,perfEnd:()=>0,perfGauge:()=>{} +}; +vm.createContext(widthContext); +vm.runInContext(`${widthRefreshSource}\nthis.refreshRenderedLineWidths=refreshRenderedLineWidths;`,widthContext); +widthContext.refreshRenderedLineWidths(new Set(['A:0'])); +assert(widthWrites.length===2&&widthWrites.every(([,name,value])=>name==='--line-width'&&value==='8.00'),'Retained line nodes were not updated without DOM scans'); + + +let geometryStateReads=0,geometryBuilds=0; +const geometryMeta={id:'G',x:2,y:3,puzzle:{}},geometryComponent={length:1500,members:[[geometryMeta,0]]},geometryContext={ + CHUNK:5,metaState:()=>{geometryStateReads++;return{paths:[{startGate:0,endGate:1,cells:[[0,0],[0,1],[1,1]]}]}}, + puzzleOf:()=>({}),gateObj:(_p,index)=>index?{cell:[1,1],side:'S'}:{cell:[0,0],side:'W'},pathColor:()=> '#fff',perfCount:()=>geometryBuilds++,currentLineGraphCaches:()=>({geometries:new WeakMap()}) +}; +vm.createContext(geometryContext);vm.runInContext(`${functionSource('minimapGeometryForComponent')}\nthis.minimapGeometryForComponent=minimapGeometryForComponent;`,geometryContext); +const geometryCache=new WeakMap(),firstGeometry=geometryContext.minimapGeometryForComponent(geometryComponent,geometryCache),secondGeometry=geometryContext.minimapGeometryForComponent(geometryComponent,geometryCache); +assert(firstGeometry===secondGeometry&&geometryStateReads===1&&geometryBuilds===1&&firstGeometry.segments[0].points.length>=2,'Simplified long-line geometry was not retained per connected component'); + +let minimapBuilds=0,center=[1,0],drawCopies=0; +const mapContext2d={setTransform(){},clearRect(){},drawImage(){drawCopies++},beginPath(){},moveTo(){},lineTo(){},stroke(){}}, + attributes=new Map(),minimapCanvas={ + width:210,height:132,getBoundingClientRect:()=>({width:210,height:132}),getContext:()=>mapContext2d, + getAttribute:name=>attributes.get(name)||null,setAttribute:(name,value)=>attributes.set(name,value) + }, + minimapContext={ + minimapCanvas,minimapStatus:{textContent:''},minimapBase:{},minimapDirty:true,minimapLongSegments:0,minimapWorldRevision:1, + minimapCache:{revision:1,width:210,height:132,dpr:1,anchorX:0,anchorY:0,scale:5,overscanPixels:50,baseWidth:310,baseHeight:232,longSegments:0}, + window:{devicePixelRatio:1},MINIMAP_VIEW_CHUNKS_X:42,data:{metas:{}},getMinimapRect:()=>({width:210,height:132}), + cameraCenterInChunks:()=>center,visibleMetaIdsForBounds:()=>new Set(),metaState:()=>({solved:false}), + perfStart:()=>0,perfEnd:()=>0,perfCount:()=>{} + }; +minimapContext.rebuildMinimapWorld=(width,height,dpr,x,y)=>{ + minimapBuilds++;minimapContext.minimapCache={...minimapContext.minimapCache,revision:minimapContext.minimapWorldRevision,width,height,dpr,anchorX:x,anchorY:y}; +}; +vm.createContext(minimapContext); +vm.runInContext(`${minimapSource}\nthis.drawMinimap=drawMinimap;`,minimapContext); +minimapContext.drawMinimap();assert(minimapBuilds===0&&drawCopies===1,'Small camera movement rebuilt the cached minimap world layer'); +center=[20,0];minimapContext.drawMinimap();assert(minimapBuilds===1&&drawCopies===2,'Camera movement beyond minimap overscan did not rebuild exactly once'); + +let overviewLodSchedules=0; +const overviewRendered=new Map(Array.from({length:6},(_,index)=>[`D${index}`,{id:`D${index}`,drawing:null}])), + overviewStatic=new Map(Array.from({length:6},(_,index)=>[`S${index}`,{id:`S${index}`}])); +const overviewLodContext={ + LOD_CHANGES_PER_PASS:4,rendered:overviewRendered,staticRendered:overviewStatic,inWorldOverview:()=>true, + destroyBoard:board=>overviewRendered.delete(board.id),destroyStaticBoard:board=>overviewStatic.delete(board.id), + scheduleLodPass:()=>overviewLodSchedules++,scheduleWorldOverview:()=>{},perfStart:()=>0,perfCount:()=>{},perfEnd:()=>{} +}; +vm.createContext(overviewLodContext);vm.runInContext(`${ensureBoardsSource}\nthis.ensureBoards=ensureBoards;`,overviewLodContext);overviewLodContext.ensureBoards(); +assert(overviewRendered.size+overviewStatic.size===8&&overviewLodSchedules===1,'Overview eviction exceeded its four-change budget or failed to schedule continuation'); + +let creationLodSchedules=0; +const visibleIds=new Set(Array.from({length:10},(_,index)=>`B${index}`)),creationStatic=new Map(), + creationData={metas:Object.fromEntries([...visibleIds].map(id=>[id,{id,puzzle:{}}]))}; +const creationLodContext={ + LOD_CHANGES_PER_PASS:4,rendered:new Map(),staticRendered:creationStatic,data:creationData,inWorldOverview:()=>false, + visibleMetaIds:()=>visibleIds,desiredInteractiveBoardIds:ids=>new Set(ids),metaState:()=>({solved:false}), + makeStaticBoard:meta=>creationStatic.set(meta.id,{id:meta.id,signature:'current'}),destroyStaticBoard:board=>board&&creationStatic.delete(board.id), + destroyBoard:()=>{},makeBoard:meta=>creationLodContext.rendered.set(meta.id,{id:meta.id,drawing:null}),staticBoardSignature:()=> 'current', + scheduleLodPass:()=>creationLodSchedules++,perfStart:()=>0,perfCount:()=>{},perfGauge:()=>{},perfEnd:()=>{} +}; +vm.createContext(creationLodContext);vm.runInContext(`${ensureBoardsSource}\nthis.ensureBoards=ensureBoards;`,creationLodContext);creationLodContext.ensureBoards(); +assert(creationLodContext.rendered.size===4&&creationStatic.size===0&&creationLodSchedules===1,'Detailed board creation exceeded its four-change budget or hid visible boards behind static summaries'); + +(async()=>{ + let workerConstructions=0,workerNow=1000,timerId=0; + const restartCallbacks=[],restartDelays=[],workerRetryContext={ + APP_VERSION:'47.36',GENERATOR_VERSION:5,puzzleWorker:null,workerRestartTimer:null,workerFailureCount:0,workerDisabledUntil:0,workerRetryAt:0, + Date:{now:()=>workerNow},Worker:function Worker(){workerConstructions++;throw new Error('synthetic worker failure')}, + setTimeout:(callback,delay)=>{restartCallbacks.push(callback);restartDelays.push(delay);return++timerId},clearTimeout:()=>{},console:{warn:()=>{}} + }; + vm.createContext(workerRetryContext); + vm.runInContext(`${functionSource('scheduleWorkerRestart')}\n${functionSource('createPuzzleWorker')}\nthis.createPuzzleWorker=createPuzzleWorker;`,workerRetryContext); + workerRetryContext.createPuzzleWorker();workerRetryContext.createPuzzleWorker(); + assert(workerConstructions===1&&restartDelays[0]===1000,'Worker creation retried immediately while a backoff timer was armed'); + workerNow+=1000;restartCallbacks.shift()(); + assert(workerConstructions===2&&restartDelays[1]===2000,'Worker restart did not advance through the gated exponential backoff'); + + let mainThreadCalls=0; + const workerContext={ + puzzleWorker:null,workerSeq:0,workerJobs:new Map(),location:{protocol:'https:'},Worker:function Worker(){}, + createPuzzleWorker:()=>null,perfCount:()=>{},generatePuzzleOnMainThread:()=>{mainThreadCalls++;return Promise.resolve({})} + }; + vm.createContext(workerContext); + vm.runInContext(`${functionSource('generatePuzzleAsync')}\nthis.generatePuzzleAsync=generatePuzzleAsync;`,workerContext); + let rejected=false;try{await workerContext.generatePuzzleAsync([[0,0]],1,1)}catch(_){rejected=true} + assert(rejected&&mainThreadCalls===0,'Served-mode worker failure fell back to synchronous main-thread generation'); + console.log('Performance regression smoke test passed'); +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/phase2-source-smoke-test.js b/test/phase2-source-smoke-test.js new file mode 100644 index 0000000..150bdf9 --- /dev/null +++ b/test/phase2-source-smoke-test.js @@ -0,0 +1,24 @@ +'use strict'; +const fs=require('fs'); +const path=require('path'); +const assert=require('assert/strict'); +const {root,functionSource}=require('./helpers/app-source'); +const app=fs.readFileSync(path.join(root,'app.js'),'utf8'); +const server=fs.readFileSync(path.join(root,'server.js'),'utf8'); +const realtime=fs.readFileSync(path.join(root,'realtime-server.js'),'utf8'); +const html=fs.readFileSync(path.join(root,'index.html'),'utf8'); +const css=fs.readFileSync(path.join(root,'style.css'),'utf8'); +const pkg=JSON.parse(fs.readFileSync(path.join(root,'package.json'),'utf8')); + +assert(html.includes('{ + const server=http.createServer((_req,res)=>{res.writeHead(404);res.end()}); + const identities=new Map([ + ['alice',{playerId:'alice',token:'a',name:'Alice'}], + ['bob',{playerId:'bob',token:'b',name:'Bob'}] + ]); + const hub=createRealtimeHub({ + server,claimTtlMs:120, + authenticate:async({playerId,token})=>{const identity=identities.get(playerId);if(!identity||identity.token!==token)throw new Error('bad auth');return identity}, + getBoardInfo:async boardId=>boardId==='B0'?{solved:false,bounds:{minX:0,minY:0,maxX:1,maxY:1}}:null + }); + await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve)); + const base=`http://127.0.0.1:${server.address().port}`; + let alice=null,bob=null; + try{ + alice=await connectRealtime(base,{playerId:'alice',token:'a'});bob=await connectRealtime(base,{playerId:'bob',token:'b'}); + alice.send({type:'viewport',minX:-2,minY:-2,maxX:2,maxY:2});bob.send({type:'viewport',minX:-2,minY:-2,maxX:2,maxY:2});await alice.waitFor('snapshot');await bob.waitFor('snapshot'); + alice.send({type:'claim',requestId:'lease-a',boardId:'B0'});assert.equal((await alice.waitFor(message=>message.type==='claim-result'&&message.requestId==='lease-a')).ok,true);await bob.waitFor(message=>message.type==='claim'&&message.claim?.boardId==='B0'); + alice.close();alice=null; + bob.send({type:'claim',requestId:'lease-b-early',boardId:'B0'});const early=await bob.waitFor(message=>message.type==='claim-result'&&message.requestId==='lease-b-early');assert.equal(early.ok,false);assert.equal(early.reason,'occupied'); + await new Promise(resolve=>setTimeout(resolve,170));bob.send({type:'snapshot-request'});await bob.waitFor(message=>message.type==='claim-release'&&message.boardId==='B0'&&message.reason==='expired'); + bob.send({type:'claim',requestId:'lease-b-late',boardId:'B0'});const late=await bob.waitFor(message=>message.type==='claim-result'&&message.requestId==='lease-b-late');assert.equal(late.ok,true); + console.log('Realtime lease expiry and disconnect semantics passed'); + }finally{ + alice?.close();bob?.close();hub.close();await new Promise(resolve=>server.close(resolve)); + } +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/realtime-phase2-smoke-test.js b/test/realtime-phase2-smoke-test.js new file mode 100644 index 0000000..fae2229 --- /dev/null +++ b/test/realtime-phase2-smoke-test.js @@ -0,0 +1,70 @@ +'use strict'; +const {spawn}=require('child_process'); +const fs=require('fs'); +const os=require('os'); +const path=require('path'); +const assert=require('assert/strict'); +const {root,starterPuzzle}=require('./helpers/app-source'); +const {connectRealtime}=require('./helpers/realtime-client'); + +const port=20000+Math.floor(Math.random()*10000); +const dataDir=fs.mkdtempSync(path.join(os.tmpdir(),'bend-field-realtime-phase2-')); +const child=spawn(process.execPath,[path.join(root,'server.js')],{ + env:{...process.env,PORT:String(port),HOST:'127.0.0.1',BEND_FIELD_DATA_DIR:dataDir},stdio:['ignore','pipe','pipe'] +}); +let stderr='',aliceRealtime=null,bobRealtime=null;child.stderr.on('data',chunk=>stderr+=chunk); +const base=`http://127.0.0.1:${port}`; +const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms)); +async function request(url,options={}){const response=await fetch(base+url,options),body=await response.json();return{response,body}} +function auth(session){return{authorization:`Bearer ${session.playerId}.${session.token}`,'content-type':'application/json'}} +function boardMeta(id,x,seed,puzzle){return{id,x,y:0,chunks:[[0,0]],level:1,targetLevel:1,seed,axis:'MIX',sealedSides:[],puzzle,rev:1,revAuthor:'client'}} +function solvedState(puzzle){return{paths:puzzle.solution.map(route=>({startGate:route.startGate,endGate:route.endGate,cells:route.cells.map(cell=>[...cell])})),solved:true,expanded:false,rev:2,revAuthor:'client'}} + +(async()=>{ + for(let index=0;index<80;index++){try{const status=await request('/api/cloud/status');if(status.response.ok)break}catch(_){}await sleep(50)} + const status=await request('/api/cloud/status');assert.equal(status.body.realtime,true);assert.equal(status.body.claimTtlMs,300000); + const alice=(await request('/api/cloud/session',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({name:'Alice'})})).body; + const bob=(await request('/api/cloud/session',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({name:'Bob'})})).body; + const puzzle=starterPuzzle(),b0=boardMeta('B0',0,111,puzzle),b1=boardMeta('B1',2,222,puzzle); + const bootstrap=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:0,global:{schema:31,appVersion:'47.70',generatorVersion:5,worldGeneration:'v47-field-reset-20260728-interaction-fix',nextId:2},metas:[b0,b1],states:[{id:'B0',value:{paths:[],solved:false,rev:1,revAuthor:'alice'}},{id:'B1',value:{paths:[],solved:false,rev:1,revAuthor:'alice'}}],deleted:[]})}); + assert.equal(bootstrap.response.status,200);assert.equal(bootstrap.body.revision,1); + + aliceRealtime=await connectRealtime(base,alice);bobRealtime=await connectRealtime(base,bob); + aliceRealtime.send({type:'viewport',minX:-8,minY:-8,maxX:8,maxY:8});bobRealtime.send({type:'viewport',minX:-8,minY:-8,maxX:8,maxY:8}); + await aliceRealtime.waitFor('snapshot');await bobRealtime.waitFor('snapshot'); + + aliceRealtime.send({type:'cursor',x:.25,y:.5,cursorStyle:'default'}); + const cursor=await bobRealtime.waitFor(message=>message.type==='cursor'&&message.playerId===alice.playerId); + assert.equal(cursor.name,'Alice');assert.equal(cursor.x,.25);assert.equal(cursor.y,.5);assert.equal(cursor.cursorStyle,'default'); + aliceRealtime.send({type:'cursor-hide'});await bobRealtime.waitFor(message=>message.type==='player-left'&&message.presenceId===aliceRealtime.ready.presenceId); + await sleep(55);aliceRealtime.send({type:'cursor',x:.25,y:.5,cursorStyle:'default'});await bobRealtime.waitFor(message=>message.type==='cursor'&&message.playerId===alice.playerId); + + aliceRealtime.send({type:'claim',requestId:'alice-b0',boardId:'B0'}); + const aliceClaim=await aliceRealtime.waitFor(message=>message.type==='claim-result'&&message.requestId==='alice-b0');assert.equal(aliceClaim.ok,true);assert.equal(aliceClaim.claim.boardId,'B0'); + const observedClaim=await bobRealtime.waitFor(message=>message.type==='claim'&&message.claim?.boardId==='B0');assert.equal(observedClaim.claim.playerName,'Alice'); + + bobRealtime.send({type:'claim',requestId:'bob-b0',boardId:'B0'}); + const denied=await bobRealtime.waitFor(message=>message.type==='claim-result'&&message.requestId==='bob-b0');assert.equal(denied.ok,false);assert.equal(denied.reason,'occupied'); + + const rejectedClear=await request('/api/cloud/push',{method:'POST',headers:auth(bob),body:JSON.stringify({baseRevision:1,global:{nextId:2},metas:[],states:[{id:'B0',value:solvedState(puzzle)}],deleted:[]})}); + assert.equal(rejectedClear.response.status,423);assert.match(rejectedClear.body.error,/claim/i); + + const clearPromise=bobRealtime.waitFor(message=>message.type==='board-cleared'&&message.event?.id==='B0'); + const releasePromise=bobRealtime.waitFor(message=>message.type==='claim-release'&&message.boardId==='B0'&&message.reason==='cleared'); + const cleared=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:1,global:{nextId:2,lastSolveAt:Date.now()},metas:[],states:[{id:'B0',value:solvedState(puzzle)}],deleted:[]})}); + assert.equal(cleared.response.status,200);assert.equal(cleared.body.clearEvents[0].playerName,'Alice'); + const clearEvent=await clearPromise;assert.equal(clearEvent.event.level,1);await releasePromise; + + const profilePromise=bobRealtime.waitFor(message=>message.type==='player-profile'&&message.playerId===alice.playerId); + const renamed=await request('/api/cloud/profile',{method:'POST',headers:auth(alice),body:JSON.stringify({name:'Alice 2'})});assert.equal(renamed.response.status,200);assert.equal((await profilePromise).name,'Alice 2'); + + aliceRealtime.send({type:'claim',requestId:'alice-b1',boardId:'B1'}); + const b1Claim=await aliceRealtime.waitFor(message=>message.type==='claim-result'&&message.requestId==='alice-b1');assert.equal(b1Claim.ok,true); + await bobRealtime.waitFor(message=>message.type==='claim'&&message.claim?.boardId==='B1'); + aliceRealtime.close();aliceRealtime=null; + await bobRealtime.waitFor(message=>message.type==='player-left'&&message.playerId===alice.playerId); + bobRealtime.send({type:'claim',requestId:'bob-b1-after-disconnect',boardId:'B1'}); + const retained=await bobRealtime.waitFor(message=>message.type==='claim-result'&&message.requestId==='bob-b1-after-disconnect');assert.equal(retained.ok,false);assert.equal(retained.reason,'occupied'); + + console.log('BEND FIELD realtime phase 2 smoke test passed'); +})().catch(error=>{console.error(error);if(stderr)console.error(stderr);process.exitCode=1}).finally(()=>{aliceRealtime?.close();bobRealtime?.close();child.kill('SIGTERM');fs.rmSync(dataDir,{recursive:true,force:true})}); diff --git a/test/reset-smoke-test.js b/test/reset-smoke-test.js new file mode 100644 index 0000000..cc6a57a --- /dev/null +++ b/test/reset-smoke-test.js @@ -0,0 +1,24 @@ +'use strict'; +const {vm,app,assert,functionSource}=require('./helpers/app-source'); +const removed=[],deleted=[]; +const staleSessionJournal='bend-field:v30:v47-field-reset-20260727:journal:stale-session',retired=[ + {schema:31,generation:'v47-field-reset-20260728-bugfix'}, + {schema:30,generation:'v47-field-reset-20260727'} +]; +const context={ + RETIRED_WORLD_STORES:retired, + safeLocalRemove:key=>{removed.push(key);return true}, + safeLocalKeys:prefix=>staleSessionJournal.startsWith(prefix)?[staleSessionJournal]:[], + indexedDB:{deleteDatabase:name=>deleted.push(name)} +}; +vm.createContext(context); +vm.runInContext(`${functionSource('deleteRetiredWorldData')}\nthis.run=deleteRetiredWorldData;`,context); +context.run(); +for(const item of retired){ + const prefix=`bend-field:v${item.schema}:${item.generation}`; + for(const suffix of[':compact',':recovery',':journal',':revision',':signal',':world:lease'])assert(removed.includes(prefix+suffix),`Old save ${suffix} was not removed`); + assert(deleted.includes(`${prefix}:world`),'Old IndexedDB was not deleted'); +} +assert(removed.includes(staleSessionJournal),'Old per-session recovery journal was not removed'); +assert(app.includes("SAVE_SCHEMA=31,STORAGE_SCHEMA=30,IDB_LAYOUT_VERSION=8,FIELD_STORAGE_FORMAT=2,GAMEPLAY_DATA_VERSION=3,WORLD_GENERATION='v47-field-reset-20260728-interaction-fix'"),'v47.36 interaction-fix field reset generation is not active'); +console.log('Full field reset test passed'); diff --git a/test/run-all.js b/test/run-all.js new file mode 100644 index 0000000..4b96529 --- /dev/null +++ b/test/run-all.js @@ -0,0 +1,16 @@ +'use strict'; +const path=require('path'); +const fs=require('fs'); +const {execFileSync}=require('child_process'); +const tests=[ + 'source-smoke-test.js','v4771-ui-input-smoke-test.js','v4772-ownership-reaction-name-smoke-test.js', + 'v4774-drag-overview-smoke-test.js','v4775-pan-cursor-performance-smoke-test.js','v4776-frame-pipeline-smoke-test.js','v4777-hud-input-performance-smoke-test.js','v4778-interaction-scheduler-smoke-test.js','cleanup-performance-smoke-test.js','field-persistence-smoke-test.js','field-save-load-v2-smoke-test.js','gameplay-simplification-smoke-test.js','economy-simulation-test.js','performance-smoke-test.js','mirror-chunk-smoke-test.js','storage-smoke-test.js','save-pipeline-smoke-test.js','concurrency-smoke-test.js','stage34-smoke-test.js', + 'expansion-repair-smoke-test.js','expansion-smoke-test.js','interaction-smoke-test.js','anomaly-smoke-test.js','special-cell-smoke-test.js','reset-smoke-test.js','shared-world-client-smoke-test.js','phase2-source-smoke-test.js','realtime-lease-unit-test.js','realtime-phase2-smoke-test.js','server-smoke-test.js','shared-world-complete-smoke-test.js','security-authority-smoke-test.js' +]; +for(const file of tests)execFileSync(process.execPath,[path.join(__dirname,file)],{stdio:'inherit'}); +const browserPath=process.env.BEND_FIELD_BROWSER_PATH||process.env.BEND_FIELD_EDGE_PATH||(process.platform==='win32'?'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe':'/usr/bin/chromium'); +if(process.env.BEND_FIELD_RUN_BROWSER_TESTS==='1'){ + if(!fs.existsSync(browserPath))throw new Error(`No supported browser was found at ${browserPath}`); + execFileSync(process.execPath,[path.join(__dirname,'browser-performance-benchmark.js')],{stdio:'inherit',env:{...process.env,BEND_FIELD_BROWSER_PATH:browserPath,BEND_FIELD_EDGE_PATH:browserPath,BEND_FIELD_BENCHMARK_PROFILE:'small'}}); + execFileSync(process.execPath,[path.join(__dirname,'browser-field-storage-benchmark.js')],{stdio:'inherit',env:{...process.env,BEND_FIELD_BROWSER_PATH:browserPath,BEND_FIELD_SCALE_SIZES:'1000'}}); +}else console.warn('Real-browser tests are opt-in. Set BEND_FIELD_RUN_BROWSER_TESTS=1 or run the benchmark scripts directly.'); diff --git a/test/save-pipeline-smoke-test.js b/test/save-pipeline-smoke-test.js new file mode 100644 index 0000000..b433aee --- /dev/null +++ b/test/save-pipeline-smoke-test.js @@ -0,0 +1,143 @@ +'use strict'; +const {vm,app,assert,functionSource}=require('./helpers/app-source'); +for(const marker of ['GATE_ARROW','legacyStorageKeys','saveFailed','minimapTransform','generatorMode','hydrateRemaining','boardAtGlobalCell','areaDifficultyShift','plannedExpansionFrontiers','metaIsVisible','mergeLatestFromStorage','shopBtn','shopCountEl']){ + assert(!app.includes(marker),`Dead marker remains: ${marker}`); +} +const saveSource=functionSource('save'),persistSource=functionSource('persistDirtyToDb'), + pullCloudSource=functionSource('pullCloudWorld'),pushCloudSource=functionSource('pushCloudPending'); +assert(!saveSource.includes('writeCompactMirror'),'save() still writes the full mirror before persistence'); +assert((persistSource.match(/snapshotForStorage\(/g)||[]).length===1&&(persistSource.match(/writeCompactMirror\(/g)||[]).length===1,'IndexedDB persistence still builds or writes a full mirror'); +assert(persistSource.includes('scheduleMirrorCheckpoint()'),'Successful IndexedDB persistence does not schedule an idle recovery checkpoint'); +assert(!app.includes("pagehide',()=>{try{writeCompactMirror")&&!app.includes("visibilitychange',()=>{if(document.visibilityState==='hidden')try{writeCompactMirror"),'Lifecycle handlers still rebuild the mirror directly'); +assert(pullCloudSource.indexOf('cloudProfileIdentity()!==profileIdentity')`B${index}`)),stateIds:new Set(['B0']),deleted:new Set(['B999']),globalChanged:true}, + {batch,remainder}=batchContext.takeCloudPendingBatch(source,512); + assert(batch.metaIds.size===512&&remainder.metaIds.size===88&&remainder.stateIds.has('B0')&&remainder.deleted.has('B999')&&batch.globalChanged,'Cloud change batching lost or exceeded work'); +} + +const writes={meta:0,state:0,global:0,outbox:0,recovery:0,mirror:0,snapshot:0,checkpoint:0,revision:0,journalClear:0,signal:0,cloud:0}; +const recoveryWrites=[],journalClears=[]; +const request=result=>({result}); +const stores={ + control:{get:()=>request({key:'active',activeFormat:2,activeEpoch:'world:test-epoch'})}, + worlds:{get:()=>request({epoch:'world:test-epoch',status:'active',createdAt:1,source:{kind:'fresh'},global:null}),put:()=>writes.global++}, + boardIndex:{put:()=>{},delete:()=>{},get:()=>request(null)},boardPuzzles:{put:()=>writes.meta++,delete:()=>{},get:()=>request(null)},boardStates:{put:()=>writes.state++,delete:()=>{},get:()=>request(null)}, + outboxV2:{put:()=>writes.outbox++,delete:()=>{}},recoveryV2:{put:row=>{writes.recovery++;recoveryWrites.push(row)},delete:()=>{}},tombstonesV2:{put:()=>{},delete:()=>{},get:()=>request(null)} +}; +const context={ + console,sessionId:'test-session',lastRevision:100, + data:{worldEpoch:'world:test-epoch',globalRev:10,globalRevAuthor:'test-session',metas:{B0:{id:'B0',rev:7,revAuthor:'test-session'}},states:{B0:{rev:9,revAuthor:'test-session'}},updatedAt:0,clockFloor:0}, + dirtyMetaIds:new Set(['B0']),dirtyStateIds:new Set(['B0']),deletedBoardIds:new Set(),deletedBoardRevisions:new Map(), + deletedBoardAuthors:new Map(), + recoveryJournalsToCover:[{sessionId:'prior-session',seq:6,_storageKey:'journal:prior'}],recoveryJournalSeq:3, + cloudOutboxDeleteKeys:new Set(),cloudJournalMetaIds:new Set(),cloudJournalStateIds:new Set(),cloudJournalDeletedIds:new Set(),cloudApiEnabled:false, + globalDirty:true,globalChangeSeq:3,cloudJournalGlobalChanged:true,worldSignalSeq:0,idbAvailable:true,activeStorageFormat:2,FIELD_STORAGE_FORMAT:2,storageAccessError:null,storageKey:'save', + pruneAndCount:()=>{},hasPendingPersistence:()=>context.globalDirty||context.dirtyMetaIds.size>0||context.dirtyStateIds.size>0||context.deletedBoardIds.size>0, + validWorldEpoch:value=>typeof value==='string'&&value.startsWith('world:'),storedWorldEpoch:()=>null,createWorldEpoch:()=> 'world:test-epoch',rememberWorldEpoch:()=>true, + revisionVersion:value=>({rev:value?.rev||0,revAuthor:value?.revAuthor||value?.author||''}),compareRevisionVersions:(a,b)=>(a?.rev||0)-(b?.rev||0)||String(a?.revAuthor||a?.author||'').localeCompare(String(b?.revAuthor||b?.author||'')),newerRevisionValue:(a,b)=>((a?.rev||0)>=(b?.rev||0)?a:b), + trustedNow:()=>123456, + globalForStorage:(source,updatedAt)=>({worldEpoch:source.worldEpoch,globalRev:source.globalRev,globalRevAuthor:source.globalRevAuthor,updatedAt,clockFloor:updatedAt}), + mergeGlobalRecords:(_stored,incoming)=>incoming,applyGlobalRecordToData:record=>Object.assign(context.data,record),staleWorldEpochError:()=>Object.assign(new Error('stale'),{code:'STALE_WORLD_EPOCH'}), + metaForStorage:value=>({...value}),stateForStorage:value=>({...value}), + metaRowsForStorage:ids=>ids.map(id=>context.data.metas[id]&&({...context.data.metas[id]})).filter(Boolean), + stateRowsForStorage:ids=>ids.map(id=>context.data.states[id]&&({id,value:{...context.data.states[id]}})).filter(Boolean), + snapshotForStorage:updatedAt=>{writes.snapshot++;return{updatedAt}}, + openWorldDb:async()=>({transaction:()=>({objectStore:name=>stores[name],abort(){}})}),transactionDone:async()=>{},requestValue:async req=>req.result, + recoveryWalPromise:Promise.resolve(),normalizeMeta:(_id,value)=>value,normalizeState:value=>value,normalizedStateObjects:new WeakSet(), + boardIndexSummaries:new Map(), + metaFromV2Records:()=>null,stateFromV2Record:()=>null,puzzleRecordV2:value=>value,stateRecordV2:(_id,value)=>value,summarizeBoardV2:value=>value, + fieldBoundsFromMetas:()=>({minX:0,minY:0,maxX:1,maxY:1}),SAVE_SCHEMA:31,WORLD_GENERATION:'current-only-test', + writeCompactMirror:snapshot=>{assert(snapshot.updatedAt===123456,'Mirror did not use the captured persistence snapshot');writes.mirror++;return true},safeLocalSet:()=>{writes.mirror++;return true}, + scheduleMirrorCheckpoint:()=>writes.checkpoint++, + updateStorageRevision:()=>writes.revision++,clearRecoveryJournalIfCovered:(seq,covered)=>{writes.journalClear++;journalClears.push({seq,covered})}, + broadcastWorldSignal:()=>writes.signal++,scheduleCloudPush:()=>writes.cloud++, + perfStart:()=>0,perfEnd:()=>0,perfGauge:()=>{},deepClone:value=>JSON.parse(JSON.stringify(value)),resetHistory:[],invalidateStoreEffectCache:()=>{},statsDirty:false +}; +vm.createContext(context); +vm.runInContext(`${persistSource}\nthis.persistDirtyToDb=persistDirtyToDb;`,context); +(async()=>{ + const first=await context.persistDirtyToDb(); + assert(!first.skipped,'Dirty save was skipped'); + assert(writes.meta===1&&writes.state===1&&writes.global===1,'Incremental database stores were not written once'); + assert(writes.snapshot===0&&writes.mirror===0,'Ordinary IndexedDB persistence built or synchronously wrote a full mirror'); + assert(writes.checkpoint===1,'Ordinary IndexedDB persistence did not schedule one idle mirror checkpoint'); + assert(recoveryWrites.some(row=>row.key==='covered:test-session'&&row.value.seq===3)&&recoveryWrites.some(row=>row.key==='covered:prior-session'&&row.value.seq===6),'Persistence did not atomically write current and recovered-session coverage markers'); + assert(journalClears.length===1&&journalClears[0].seq===3&&journalClears[0].covered[0]._storageKey==='journal:prior','Committed recovery journals were not passed to cleanup after persistence'); + assert(context.dirtyMetaIds.size===0&&context.dirtyStateIds.size===0&&!context.globalDirty,'Dirty state was not cleared after persistence'); + const second=await context.persistDirtyToDb(); + assert(second.skipped,'Unchanged persistence did not exit early'); + assert(writes.snapshot===0&&writes.mirror===0&&writes.checkpoint===1&&writes.global===1,'Unchanged persistence performed additional work'); + + context.deletedBoardIds.add('B9');context.deletedBoardRevisions.set('B9',10); + context.transactionDone=async()=>{context.deletedBoardRevisions.set('B9',11)}; + const deletionCommit=await context.persistDirtyToDb(); + assert(!deletionCommit.skipped&&context.deletedBoardIds.has('B9')&&context.deletedBoardRevisions.get('B9')===11,'A newer deletion tombstone was cleared by an older in-flight commit'); + + context.dirtyMetaIds.add('B0');context.dirtyStateIds.add('B0');context.globalDirty=true;context.globalChangeSeq++; + const beforeFailure={mirror:writes.mirror,snapshot:writes.snapshot,checkpoint:writes.checkpoint,signal:writes.signal,cloud:writes.cloud}; + context.transactionDone=async()=>{throw new Error('transaction failed')}; + let failed=false;try{await context.persistDirtyToDb()}catch(_){failed=true} + assert(failed,'Database failure was swallowed'); + assert(context.dirtyMetaIds.has('B0')&&context.dirtyStateIds.has('B0')&&context.globalDirty,'Database failure cleared dirty state'); + assert(writes.mirror===beforeFailure.mirror&&writes.snapshot===beforeFailure.snapshot&&writes.checkpoint===beforeFailure.checkpoint&&writes.signal===beforeFailure.signal&&writes.cloud===beforeFailure.cloud,'Database failure published or checkpointed an uncommitted save'); + + context.idbAvailable=false;context.transactionDone=async()=>{};context.writeCompactMirror=()=>false;context.storageAccessError=new Error('mirror failed'); + failed=false;try{await context.persistDirtyToDb()}catch(_){failed=true} + assert(failed,'Mirror-only storage failure was swallowed'); + assert(writes.snapshot===1,'Mirror-only storage did not build its required fallback snapshot'); + assert(context.dirtyMetaIds.has('B0')&&context.dirtyStateIds.has('B0')&&context.globalDirty,'Mirror failure cleared dirty state'); + + let scheduled=0,immediate=0; + const saveContext={ + Promise,SAVE_DELAY:180,saveTimer:null,lifecyclePersistenceSuppressed:false, + hasPendingPersistence:()=>false,persistNow:()=>{immediate++;return Promise.resolve(true)}, + setSaveStatus:()=>{},clearTimeout:()=>{},setTimeout:()=>{scheduled++;return 1} + }; + vm.createContext(saveContext);vm.runInContext(`${saveSource}\nthis.save=save;`,saveContext); + assert(saveContext.save()===true&&scheduled===0,'No-op save scheduled work'); + await saveContext.save(true);assert(immediate===0,'No-op immediate save invoked persistence'); + saveContext.hasPendingPersistence=()=>true; + saveContext.save();assert(scheduled===1,'Dirty deferred save was not scheduled'); + await saveContext.save(true);assert(immediate===1,'Dirty immediate save did not persist'); + + let checkpointPersists=0; + const checkpointTimers=[],checkpointContext={ + cloudCheckpointRetryTimer:0,hasPendingPersistence:()=>true,persistNow:async()=>{checkpointPersists++;return false}, + setTimeout:(callback,delay)=>{checkpointTimers.push({callback,delay});return checkpointTimers.length} + }; + vm.createContext(checkpointContext);vm.runInContext(`${functionSource('scheduleCloudCheckpointRetry')}\nthis.scheduleCloudCheckpointRetry=scheduleCloudCheckpointRetry;`,checkpointContext); + checkpointContext.scheduleCloudCheckpointRetry();checkpointContext.scheduleCloudCheckpointRetry(); + assert(checkpointTimers.length===1&&checkpointTimers[0].delay===1000,'Cloud checkpoint retries were not coalesced behind one timer'); + checkpointTimers[0].callback();await Promise.resolve();await Promise.resolve(); + assert(checkpointPersists===1&&checkpointTimers.length===2,'Failed cloud checkpoint was not retried after the guarded persistence attempt'); + + let profileRetrySchedules=0; + const profileContext={ + data:{cloudProfile:null,cloudRevision:9},serverClockOffset:null,Date, + fetchJson:async()=>({playerId:'a'.repeat(16),token:'b'.repeat(32),serverTime:Date.now()}), + markGlobalDirty:()=>{},persistNow:async()=>false,scheduleCloudCheckpointRetry:()=>profileRetrySchedules++ + }; + vm.createContext(profileContext);vm.runInContext(`${functionSource('createCloudProfile')}\nthis.createCloudProfile=createCloudProfile;`,profileContext); + let profileFailed=false;try{await profileContext.createCloudProfile()}catch(_){profileFailed=true} + assert(profileFailed&&profileRetrySchedules===1&&profileContext.data.cloudProfile?.playerId==='a'.repeat(16),'Uncommitted cloud profile was treated as a completed checkpoint'); + + let acknowledged=0,armedDelay=null; + const pending={metaIds:new Set(['B0']),stateIds:new Set(),deleted:new Set(),globalChanged:false}; + const cloudContext={ + data:{cloudProfile:{playerId:'a'.repeat(16),token:'b'.repeat(32)},cloudRevision:5,metas:{B0:{id:'B0',rev:7}},states:{}}, + cloudAvailable:true,cloudSyncing:false,cloudPushTimer:null,cloudPushPending:pending,cloudJournalChangeSeq:1,serverClockOffset:null, + clearTimeout:()=>{},setCloudStatus:()=>{},sharedWorldGlobalSignature:()=>"world",sharedWorldGlobalForCloud:()=>({}),lastCloudWorldGlobalSignature:"old",applyCloudEnvelope:()=>{},cloudRowsForStorage:async ids=>({metas:[...ids].map(id=>({id,rev:7})),states:[]}), + cloudAuthHeaders:()=>({}),fetchJson:async()=>{cloudContext.data.cloudProfile={playerId:'c'.repeat(16),token:'d'.repeat(32)};return{revision:6,serverTime:Date.now()}}, + acknowledgeCloudPending:()=>acknowledged++,markGlobalDirty:()=>{},persistNow:async()=>true,scheduleCloudCheckpointRetry:()=>{}, + pullCloudWorld:async()=>true,armCloudPush:delay=>{armedDelay=delay;return true},console:{warn:()=>{}},Date + }; + vm.createContext(cloudContext); + vm.runInContext(`${functionSource('emptyCloudPending')}\n${functionSource('mergeCloudPending')}\n${functionSource('takeCloudPendingBatch')}\n${functionSource('cloudPendingHasWork')}\n${functionSource('cloudProfileIdentity')}\n${pushCloudSource}\nthis.pushCloudPending=pushCloudPending;`,cloudContext); + const profileChangedResult=await cloudContext.pushCloudPending(); + assert(profileChangedResult===false&&acknowledged===0&&cloudContext.data.cloudRevision===5,'Cloud response from an obsolete profile was acknowledged'); + assert(cloudContext.cloudPushPending.metaIds.has('B0')&&armedDelay===5000,'Profile-switch guard did not restore and defer pending cloud work'); + console.log('Save pipeline cleanup test passed'); +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/security-authority-smoke-test.js b/test/security-authority-smoke-test.js new file mode 100644 index 0000000..7f66e47 --- /dev/null +++ b/test/security-authority-smoke-test.js @@ -0,0 +1,14 @@ +'use strict'; +const {spawn}=require('child_process');const fs=require('fs');const os=require('os');const path=require('path');const assert=require('assert/strict'); +const {root,starterPuzzle}=require('./helpers/app-source');const {connectRealtime}=require('./helpers/realtime-client'); +const port=32000+Math.floor(Math.random()*2000),dataDir=fs.mkdtempSync(path.join(os.tmpdir(),'bend-auth-')),child=spawn(process.execPath,[path.join(root,'server.js')],{env:{...process.env,PORT:String(port),HOST:'127.0.0.1',BEND_FIELD_DATA_DIR:dataDir},stdio:['ignore','pipe','pipe']});let stderr='',ws;child.stderr.on('data',c=>stderr+=c);const base=`http://127.0.0.1:${port}`,sleep=ms=>new Promise(r=>setTimeout(r,ms)); +async function req(url,opt={}){const response=await fetch(base+url,opt);return{response,body:await response.json()}}function auth(s){return{authorization:`Bearer ${s.playerId}.${s.token}`,'content-type':'application/json'}} +function meta(id,x,seed,p){return{id,x,y:0,chunks:[[0,0]],level:1,targetLevel:1,seed,axis:'MIX',sealedSides:[],puzzle:p,rev:1,revAuthor:'x'}} +(async()=>{for(let i=0;i<100;i++){try{if((await req('/api/cloud/status')).response.ok)break}catch{}await sleep(30)}const alice=(await req('/api/cloud/session',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({name:'Alice'})})).body,p=starterPuzzle(),b0=meta('B0',0,15,p); +let r=await req('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:0,global:{nextId:1},metas:[b0],states:[{id:'B0',value:{paths:[],solved:false}}]})});assert.equal(r.response.status,200);ws=await connectRealtime(base,alice);ws.send({type:'viewport',minX:-5,minY:-5,maxX:5,maxY:5});await ws.waitFor('snapshot');ws.send({type:'claim',requestId:'c',boardId:'B0'});assert.equal((await ws.waitFor(m=>m.type==='claim-result'&&m.requestId==='c')).ok,true); +const solved={paths:p.solution.map(q=>({startGate:q.startGate,endGate:q.endGate,cells:q.cells})),solved:true,scoreAwarded:Number.MAX_SAFE_INTEGER,store:{pathIndex:0,cellIndex:0,itemIds:['level-min-10'],priceCoefficient:0,purchases:[]}}; +r=await req('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:1,global:{nextId:1},metas:[],states:[{id:'B0',value:solved}]})});assert.equal(r.response.status,200);const pulled=await req('/api/cloud/pull?since=0',{headers:auth(alice)}),state=pulled.body.page.states.B0;assert(state.scoreAwarded>0&&state.scoreAwarded<100000,'server must replace forged reward');assert(state.store&&state.store.priceCoefficient>=.8&&state.store.priceCoefficient<=1.2,'server must replace forged store pricing');assert.equal(state.store.itemIds.length,13); +const econ=(await req('/api/player/state',{headers:auth(alice)})).body.player;assert.equal(econ.earnedScore,state.scoreAwarded);assert.equal(econ.availableScore,state.scoreAwarded); +const forged=meta('B1',999999,123,p);r=await req('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:2,global:{nextId:2},metas:[forged],states:[{id:'B1',value:{paths:[],solved:false}}]})});assert.notEqual(r.response.status,200,'non-adjacent forged board must be rejected'); +console.log('Server authority security smoke test passed'); +})().catch(e=>{console.error(e);if(stderr)console.error(stderr);process.exitCode=1}).finally(()=>{ws?.close();child.kill('SIGTERM');fs.rmSync(dataDir,{recursive:true,force:true})}); diff --git a/test/server-smoke-test.js b/test/server-smoke-test.js new file mode 100644 index 0000000..991f7e8 --- /dev/null +++ b/test/server-smoke-test.js @@ -0,0 +1,69 @@ +'use strict'; +const {spawn}=require('child_process'); +const fs=require('fs'); +const os=require('os'); +const path=require('path'); +const assert=require('assert/strict'); +const {root,starterPuzzle}=require('./helpers/app-source'); +const {connectRealtime}=require('./helpers/realtime-client'); + +const port=19000+Math.floor(Math.random()*10000); +const dataDir=fs.mkdtempSync(path.join(os.tmpdir(),'bend-field-shared-world-')); +const child=spawn(process.execPath,[path.join(root,'server.js')],{ + env:{...process.env,PORT:String(port),HOST:'127.0.0.1',BEND_FIELD_DATA_DIR:dataDir},stdio:['ignore','pipe','pipe'] +}); +let stderr='';child.stderr.on('data',chunk=>stderr+=chunk); +const base=`http://127.0.0.1:${port}`; +const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms)); +let aliceRealtime=null; +async function request(url,options={}){ + const response=await fetch(base+url,options),body=await response.json(); + return{response,body}; +} +async function requestText(url){const response=await fetch(base+url),body=await response.text();return{response,body}} +function auth(session){return{authorization:`Bearer ${session.playerId}.${session.token}`,'content-type':'application/json'}} +function boardMeta(id,x,seed,puzzle){return{id,x,y:0,chunks:[[0,0]],level:1,targetLevel:1,seed,axis:'MIX',sealedSides:[],puzzle,rev:1,revAuthor:'client'}} +function solvedState(puzzle){return{paths:puzzle.solution.map(route=>({startGate:route.startGate,endGate:route.endGate,cells:route.cells.map(cell=>[...cell])})),solved:true,expanded:false,rev:2,revAuthor:'client'}} +(async()=>{ + for(let i=0;i<60;i++){try{const {response}=await request('/api/cloud/status');if(response.ok)break}catch(_){}await sleep(50)} + const status=await request('/api/cloud/status');assert.equal(status.response.status,200);assert.equal(status.body.sharedWorld,true); + const page=await requestText('/');assert.equal(page.response.status,200);assert(page.body.indexOf('app-logic.js?v=47.77')>page.body.indexOf('puzzle-core.js?v=47.77-5')&&page.body.indexOf('app-logic.js?v=47.77')1_000_000_000_000);assert(bobInitial.body.page.states.B0.rev>1_000_000_000_000);assert.equal(bobInitial.body.page.global.score,undefined);assert.equal(bobInitial.body.page.global.cursorStyle,undefined);assert.equal(bobInitial.body.page.global.cloudProfile,null);assert.equal(bobInitial.body.player.name,'Bob'); + + aliceRealtime=await connectRealtime(base,alice);aliceRealtime.send({type:'viewport',minX:-10,minY:-10,maxX:10,maxY:10});await aliceRealtime.waitFor('snapshot');aliceRealtime.send({type:'claim',requestId:'server-smoke-claim',boardId:'B0'});const claimResult=await aliceRealtime.waitFor(message=>message.type==='claim-result'&&message.requestId==='server-smoke-claim');assert.equal(claimResult.ok,true); + const clearPayload={baseRevision:1,global:{nextId:1,lastSolveAt:Date.now()},metas:[],states:[{id:'B0',value:solvedState(starter)}],deleted:[]}; + const cleared=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify(clearPayload)});assert.equal(cleared.response.status,200);assert.equal(cleared.body.revision,2);assert.equal(cleared.body.clearEvents.length,1);assert.equal(cleared.body.clearEvents[0].playerName,'Alice');assert.equal(cleared.body.clearEvents[0].id,'B0');assert.equal(cleared.body.clearEvents[0].level,1); + + const bobAfterClear=await request('/api/cloud/pull?since=1&eventsSince=0',{headers:auth(bob)});assert.equal(bobAfterClear.response.status,200);assert.equal(bobAfterClear.body.fullSnapshot,false);assert.equal(bobAfterClear.body.page.states.B0.solved,true);assert.equal(bobAfterClear.body.page.states.B0.solvedBy,'Alice');assert.equal(bobAfterClear.body.page.states.B0.solvedById,alice.playerId);assert(Number.isFinite(bobAfterClear.body.page.states.B0.solvedAt));assert.equal(bobAfterClear.body.clearEvents.length,1);assert.equal(bobAfterClear.body.clearEvents[0].playerName,'Alice'); + + const stale=await request('/api/cloud/push',{method:'POST',headers:auth(bob),body:JSON.stringify({...clearPayload,baseRevision:1})});assert.equal(stale.response.status,409);assert.equal(stale.body.revision,2); + const repeat=await request('/api/cloud/push',{method:'POST',headers:auth(bob),body:JSON.stringify({...clearPayload,baseRevision:2})});assert.equal(repeat.response.status,200);assert.equal(repeat.body.revision,3);assert.deepEqual(repeat.body.clearEvents,[]); + const preserved=await request('/api/cloud/pull?since=0&eventsSince=0',{headers:auth(bob)});assert.equal(preserved.body.page.states.B0.solvedBy,'Alice'); + + const renamed=await request('/api/cloud/profile',{method:'POST',headers:auth(bob),body:JSON.stringify({name:'Bob 2'})});assert.equal(renamed.response.status,200);assert.equal(renamed.body.name,'Bob 2'); + const unchanged=await request('/api/cloud/pull?since=3&eventsSince=2',{headers:auth(bob)});assert.equal(unchanged.body.changed,false);assert.equal(unchanged.body.player.name,'Bob 2'); + + const b1=boardMeta('B1',1,987654,starter); + const generated=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:3,global:{nextId:2},metas:[b1],states:[{id:'B1',value:{paths:[],solved:false,rev:4,revAuthor:'alice'}}],deleted:[]})});assert.equal(generated.response.status,200);assert.equal(generated.body.revision,4); + const bobGenerated=await request('/api/cloud/pull?since=3&eventsSince=2',{headers:auth(bob)});assert.equal(bobGenerated.response.status,200);assert.equal(bobGenerated.body.page.metas.B1.seed,987654);assert.equal(bobGenerated.body.page.states.B1.solved,false); + + const invalid=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:4,global:{},metas:[{...b1,id:'B2',x:2,chunks:[]}],states:[],deleted:[]})});assert.equal(invalid.response.status,400); + const overlap=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:4,global:{},metas:[boardMeta('B2',1,222,starter)],states:[],deleted:[]})});assert.equal(overlap.response.status,400); + + + const aliceRecord=JSON.parse(fs.readFileSync(path.join(dataDir,`${alice.playerId}.json`),'utf8'));assert.equal(aliceRecord.name,'Alice');assert.notEqual(aliceRecord.tokenHash,alice.token);assert.equal(aliceRecord.boardVersions,undefined);assert.equal(aliceRecord.global,undefined); + const world=JSON.parse(fs.readFileSync(path.join(dataDir,'shared-world.json'),'utf8'));assert.equal(world.revision,4);assert.equal(world.global.score,undefined);assert.equal(world.global.cursorStyle,undefined);assert.equal(world.boardVersions.B0,3);assert.equal(world.boardVersions.B1,4);assert.equal(world.clearEvents[0].playerName,'Alice'); + const b0Shard=JSON.parse(fs.readFileSync(path.join(dataDir,'shared-world.boards','B0.3.json'),'utf8'));assert.equal(b0Shard.state.solvedBy,'Alice');assert.equal(b0Shard.state.paths.length,starter.solution.length); + console.log('BEND FIELD shared-world phase 2 server smoke test passed'); +})().catch(error=>{console.error(error);if(stderr)console.error(stderr);process.exitCode=1}).finally(()=>{aliceRealtime?.close();child.kill('SIGTERM');fs.rmSync(dataDir,{recursive:true,force:true})}); diff --git a/test/shared-world-client-smoke-test.js b/test/shared-world-client-smoke-test.js new file mode 100644 index 0000000..b05daad --- /dev/null +++ b/test/shared-world-client-smoke-test.js @@ -0,0 +1,72 @@ +'use strict'; +const assert=require('assert/strict'); +const vm=require('vm'); +const {functionSource,loadAppLogic}=require('./helpers/app-source'); + +const outboxContext={ + data:{states:{B0:{solved:false},B1:{solved:true}}},cloudApiEnabled:true, + cloudJournalMetaIds:new Set(),cloudJournalStateIds:new Set(),cloudJournalDeletedIds:new Set(),cloudOutboxDeleteKeys:new Set(),cloudJournalChangeSeq:0,cloudJournalGlobalChanged:false +}; +vm.createContext(outboxContext); +vm.runInContext(`${functionSource('currentCloudPending')}\n${functionSource('noteCloudRow')}\nthis.logic={currentCloudPending,noteCloudRow};`,outboxContext); +outboxContext.logic.noteCloudRow('state','B0'); +assert.equal(outboxContext.cloudJournalStateIds.has('B0'),false,'Unsolved personal path entered the shared journal'); +assert.equal(outboxContext.cloudOutboxDeleteKeys.has('state:B0'),true,'Stale unsolved shared outbox row was not scheduled for deletion'); +outboxContext.logic.noteCloudRow('state','B1'); +assert.equal(outboxContext.cloudJournalStateIds.has('B1'),true,'Solved state was not added to the shared journal'); +assert.deepEqual([...outboxContext.logic.currentCloudPending().stateIds],['B1']); + +const AppLogic=loadAppLogic(),now=1_800_000_000_000; +const leaseContext={ + cloudAvailable:true,data:{cloudProfile:{playerId:'bbbbbbbbbbbbbbbb'}},currentPlayerId:()=> 'bbbbbbbbbbbbbbbb',trustedNow:()=>now,AppLogic, + SHARED_EXPANSION_GRACE_MS:60_000,SHARED_EXPANSION_JITTER_MS:30_000 +}; +vm.createContext(leaseContext); +vm.runInContext(`${functionSource('sharedExpansionRepairDelay')}\nthis.sharedExpansionRepairDelay=sharedExpansionRepairDelay;`,leaseContext); +assert.equal(leaseContext.sharedExpansionRepairDelay({solved:true,solvedById:'bbbbbbbbbbbbbbbb',solvedAt:now}),0,'The solving client cannot expand its own clear'); +const wait=leaseContext.sharedExpansionRepairDelay({solved:true,solvedById:'aaaaaaaaaaaaaaaa',solvedAt:now}); +assert(wait>=60_000&&wait<90_000,'A non-solving client can race the solver before the recovery grace period'); +assert.equal(leaseContext.sharedExpansionRepairDelay({solved:true,solvedById:'aaaaaaaaaaaaaaaa',solvedAt:now-100_000}),0,'A disconnected solver can leave expansion permanently blocked'); +leaseContext.cloudAvailable=false; +assert.equal(leaseContext.sharedExpansionRepairDelay({solved:true,solvedById:'aaaaaaaaaaaaaaaa',solvedAt:now}),0,'Offline expansion was incorrectly lease-gated'); + +const localMeta={id:'B0',x:99,y:99,seed:1,chunks:[[0,0]],sealedSides:[],rev:9_999}; +const remoteMeta={id:'B0',x:0,y:0,seed:2,chunks:[[0,0]],sealedSides:[],rev:2_000}; +const authoritativeContext={ + data:{ + metas:{B0:localMeta},states:{B0:{solved:true,solvedBy:'Local',paths:[{cells:[[0,0]]}],rev:9_999}}, + nextId:99,solved:1,lastSolveAt:123,specialMechanicsSeen:['warp'],quarantine:{local:true}, + playerName:'Player',score:777,cursorStyle:'flag-jp' + }, + isPlainObject:value=>value&&typeof value==='object'&&!Array.isArray(value),lastRevision:0,normalizedStateObjects:new Set(), + cloudJournalMetaIds:new Set(['B0']),cloudJournalStateIds:new Set(['B0']),cloudJournalDeletedIds:new Set(),cloudOutboxDeleteKeys:new Set(), + sameMetaGeometry:(a,b)=>a.x===b.x&&a.y===b.y&&a.seed===b.seed&&JSON.stringify(a.chunks)===JSON.stringify(b.chunks)&&JSON.stringify(a.sealedSides||[])===JSON.stringify(b.sealedSides||[]), + destroyBoard:()=>{},destroyStaticBoard:()=>{},rendered:new Map(),staticRendered:new Map(),deepClone:value=>JSON.parse(JSON.stringify(value)), + compareRevisionVersions:(a,b)=>(a?.rev||0)-(b?.rev||0), + mergeBoardStates:(current,incoming)=>{ + if(current?.solved&&!incoming?.solved)return JSON.parse(JSON.stringify(incoming)); + if(current&&!current.solved&&!incoming.solved)return{...JSON.parse(JSON.stringify(incoming)),paths:JSON.parse(JSON.stringify(current.paths||[]))}; + return JSON.parse(JSON.stringify(incoming)); + }, + markStateDirty:id=>authoritativeContext.dirtyStateIds.add(id),dirtyStateIds:new Set(),sanitizeStateForPuzzle:()=>{}, + mergeGlobalFields:()=>{throw new Error('Authoritative shared global unexpectedly used generic merge')},resolveMergedOverlaps:()=>[],statsDirty:false +}; +vm.createContext(authoritativeContext); +vm.runInContext(`${functionSource('clearSharedWorldJournalRow')}\n${functionSource('applyAuthoritativeSharedGlobal')}\n${functionSource('mergeSnapshotIntoData')}\nthis.mergeSnapshotIntoData=mergeSnapshotIntoData;`,authoritativeContext); +authoritativeContext.mergeSnapshotIntoData({ + metas:{B0:remoteMeta},states:{B0:{solved:false,paths:[],rev:2_000}}, + nextId:2,solved:0,lastSolveAt:0,specialMechanicsSeen:['lock'],quarantine:{shared:true} +},{finalize:false,authoritativeWorld:true}); +assert.equal(authoritativeContext.data.metas.B0.seed,2,'An existing local world row overrode the authoritative shared board'); +assert.equal(authoritativeContext.data.states.B0.solved,false,'An unconfirmed local clear survived authoritative shared adoption'); +assert.equal(authoritativeContext.data.nextId,2,'A private local board counter leaked into the shared world'); +assert.equal(authoritativeContext.data.playerName,'Player');assert.equal(authoritativeContext.data.score,777);assert.equal(authoritativeContext.data.cursorStyle,'flag-jp'); +assert.equal(authoritativeContext.cloudJournalMetaIds.size,0);assert.equal(authoritativeContext.cloudJournalStateIds.size,0); +assert(authoritativeContext.cloudOutboxDeleteKeys.has('meta:B0')&&authoritativeContext.cloudOutboxDeleteKeys.has('state:B0'),'Adopted shared rows did not clear stale local outbox records'); + +// Matching unsolved boards keep the player's unfinished line locally while the board definition stays shared. +authoritativeContext.data.metas.B0=remoteMeta; +authoritativeContext.data.states.B0={solved:false,paths:[{startGate:0,cells:[[0,0],[0,1]]}],rev:3_000}; +authoritativeContext.mergeSnapshotIntoData({metas:{B0:{...remoteMeta,rev:4_000}},states:{B0:{solved:false,paths:[],rev:4_000}},nextId:2},{finalize:false,authoritativeWorld:true}); +assert.equal(authoritativeContext.data.states.B0.paths.length,1,'Authoritative shared refresh erased a matching board\'s personal unfinished path'); +console.log('Shared-world phase 1 client synchronization smoke test passed'); diff --git a/test/shared-world-complete-smoke-test.js b/test/shared-world-complete-smoke-test.js new file mode 100644 index 0000000..370f919 --- /dev/null +++ b/test/shared-world-complete-smoke-test.js @@ -0,0 +1,47 @@ +'use strict'; +const {spawn}=require('child_process'); +const fs=require('fs'); +const os=require('os'); +const path=require('path'); +const assert=require('assert/strict'); +const {root,starterPuzzle,read,functionSource}=require('./helpers/app-source'); +const {connectRealtime}=require('./helpers/realtime-client'); + +const app=read('app.js'),html=read('index.html'),css=read('style.css'),serverSource=read('server.js'),realtimeSource=read('realtime-server.js'); +assert(html.includes('id="reactionCanvas"')&&html.includes('id="reactionRadial"')&&css.includes('#reactionCanvas')&&css.includes('z-index:2'),'Reaction layer is not behind boards'); +assert(functionSource('beginReactionGesture').includes('REACTION_LONG_PRESS_MS')&&functionSource('reactionAllowedAt').includes('metaState(boardId).solved'),'Reaction click/long-press eligibility is missing'); +assert(functionSource('drawReactionLayer').includes('REALTIME_REACTION_DURATION')||app.includes('REALTIME_REACTION_DURATION=4500'),'Reaction animation is not bounded'); +assert(functionSource('inventoryEntries').includes('onlinePlayerEconomy()')&&functionSource('purchaseStoreItem').includes('buyPersonalStoreItem'),'Player inventory or personal purchasing is not server-backed'); +assert(serverSource.includes("url.pathname==='/api/player/purchase'")&&serverSource.includes('assertPlayerCanAfford')&&!serverSource.includes("url.pathname==='/api/player/place-field'"),'Player purchase validation is missing or retired field-placement API remains'); +assert(realtimeSource.includes("message.type === 'reaction'")&&realtimeSource.includes('REACTION_MIN_INTERVAL_MS')&&!realtimeSource.includes('broadcastFieldEffect'),'Realtime reaction throttling is missing or field broadcasts remain'); + +const port=24000+Math.floor(Math.random()*8000),dataDir=fs.mkdtempSync(path.join(os.tmpdir(),'bend-field-complete-')); +const child=spawn(process.execPath,[path.join(root,'server.js')],{env:{...process.env,PORT:String(port),HOST:'127.0.0.1',BEND_FIELD_DATA_DIR:dataDir},stdio:['ignore','pipe','pipe']}); +let stderr='',aliceWs=null,bobWs=null;child.stderr.on('data',chunk=>stderr+=chunk); +const base=`http://127.0.0.1:${port}`,sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms)); +async function request(url,options={}){const response=await fetch(base+url,options),body=await response.json();return{response,body}} +function auth(session){return{authorization:`Bearer ${session.playerId}.${session.token}`,'content-type':'application/json'}} +function boardMeta(id,x,seed,puzzle){return{id,x,y:0,chunks:[[0,0]],level:1,targetLevel:1,seed,axis:'MIX',sealedSides:[],puzzle,rev:1,revAuthor:'client'}} +function solvedState(puzzle){return{paths:puzzle.solution.map(route=>({startGate:route.startGate,endGate:route.endGate,cells:route.cells.map(cell=>[...cell])})),solved:true,expanded:false,scoreAwarded:100000,scoreVersion:6,store:{owner:'',pathIndex:0,cellIndex:0,itemIds:['score-lens'],purchases:[],priceVersion:1,priceCoefficient:1},rev:2,revAuthor:'client'}} + +(async()=>{ + for(let i=0;i<100;i++){try{if((await request('/api/cloud/status')).response.ok)break}catch(_){}await sleep(40)} + const status=(await request('/api/cloud/status')).body;assert.equal(status.reactions,true);assert.equal(status.playerEconomy,true);assert.equal(status.sharedItems,false); + const alice=(await request('/api/cloud/session',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({name:'Alice'})})).body; + const bob=(await request('/api/cloud/session',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({name:'Bob'})})).body; + const puzzle=starterPuzzle(),b0=boardMeta('B0',0,15,puzzle); + let pushed=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:0,global:{schema:31,appVersion:'47.70',generatorVersion:5,worldGeneration:'v47-field-reset-20260728-interaction-fix',nextId:1},metas:[b0],states:[{id:'B0',value:{paths:[],solved:false,rev:1,revAuthor:'alice'}}],deleted:[]})});assert.equal(pushed.response.status,200); + aliceWs=await connectRealtime(base,alice);bobWs=await connectRealtime(base,bob);aliceWs.send({type:'viewport',minX:-8,minY:-8,maxX:8,maxY:8});bobWs.send({type:'viewport',minX:-8,minY:-8,maxX:8,maxY:8});await aliceWs.waitFor('snapshot');await bobWs.waitFor('snapshot'); + aliceWs.send({type:'reaction',id:'reaction-one',emoji:'🎉',x:.4,y:.6});const reaction=await bobWs.waitFor(message=>message.type==='reaction'&&message.reaction?.id==='reaction-one');assert.equal(reaction.reaction.emoji,'🎉');assert.equal(reaction.reaction.playerName,'Alice');assert(reaction.reaction.expiresAt>reaction.reaction.createdAt); + aliceWs.send({type:'claim',requestId:'claim-b0',boardId:'B0'});assert.equal((await aliceWs.waitFor(message=>message.type==='claim-result'&&message.requestId==='claim-b0')).ok,true); + pushed=await request('/api/cloud/push',{method:'POST',headers:auth(alice),body:JSON.stringify({baseRevision:1,global:{nextId:1},metas:[],states:[{id:'B0',value:solvedState(puzzle)}],deleted:[]})});assert.equal(pushed.response.status,200);let revision=pushed.body.revision; + const alicePath=path.join(dataDir,`${alice.playerId}.json`),aliceRecord=JSON.parse(fs.readFileSync(alicePath,'utf8'));aliceRecord.earnedScore=250000;fs.writeFileSync(alicePath,JSON.stringify(aliceRecord)); + const purchase=await request('/api/player/purchase',{method:'POST',headers:auth(alice),body:JSON.stringify({boardId:'B0',itemId:'score-lens'})});assert.equal(purchase.response.status,201);assert.equal(purchase.body.player.purchases.length,1);assert.equal(purchase.body.purchase.itemId,'score-lens'); + const duplicate=await request('/api/player/purchase',{method:'POST',headers:auth(alice),body:JSON.stringify({boardId:'B0',itemId:'score-lens'})});assert.equal(duplicate.response.status,200);assert.equal(duplicate.body.purchase.purchaseId,purchase.body.purchase.purchaseId); + const bobState=await request('/api/player/state',{headers:auth(bob)});assert.deepEqual(bobState.body.player.purchases,[]); + const retiredPurchase=await request('/api/player/purchase',{method:'POST',headers:auth(alice),body:JSON.stringify({boardId:'B0',itemId:'level-min-10'})});assert.equal(retiredPurchase.response.status,400); + const retiredPlacement=await request('/api/player/place-field',{method:'POST',headers:auth(alice),body:JSON.stringify({boardId:'B0',itemId:'level-min-10',x:4.25,y:-2.5})});assert.equal(retiredPlacement.response.status,404); + const pull=await request('/api/cloud/pull?since=0&eventsSince=0',{headers:auth(bob)});assert.equal(pull.response.status,200);assert.equal(pull.body.page.global.fieldEffects,undefined); + assert(!app.includes('level-min-10')&&!app.includes('level-max-10')&&!app.includes('fieldOverlayCanvas'),'Retired difficulty items remain in the client bundle'); + console.log('Shared-world reactions and personal item economy smoke test passed'); +})().catch(error=>{console.error(error);if(stderr)console.error(stderr);process.exitCode=1}).finally(()=>{aliceWs?.close();bobWs?.close();child.kill('SIGTERM');fs.rmSync(dataDir,{recursive:true,force:true})}); diff --git a/test/source-smoke-test.js b/test/source-smoke-test.js new file mode 100644 index 0000000..6751640 --- /dev/null +++ b/test/source-smoke-test.js @@ -0,0 +1,163 @@ +'use strict'; +const cp=require('child_process'); +const fs=require('fs'); +const {root,path,vm,app,html,css,worker,appLogicSource,assert,functionSource,loadBendPuzzle,loadAppLogic,starterPuzzle,read}=require('./helpers/app-source'); +const serverSource=read('server.js'); +const packageVersion=JSON.parse(read('package.json')).version,appVersion=packageVersion.split('.').slice(0,2).join('.'); +for(const file of ['app.js','app-logic.js','puzzle-core.js','puzzle-worker.js','field-persistence.js','field-persistence-worker.js','server.js'])cp.execFileSync(process.execPath,['--check',path.join(root,file)],{stdio:'inherit'}); +for(const marker of [ + `APP_VERSION='${appVersion}',SAVE_SCHEMA=31,STORAGE_SCHEMA=30,IDB_LAYOUT_VERSION=8,FIELD_STORAGE_FORMAT=2,GAMEPLAY_DATA_VERSION=3,WORLD_GENERATION='v47-field-reset-20260728-interaction-fix'`, + 'SPECIAL_CELL_MIN_LEVEL=5,SPECIAL_CELL_DEBUG_ALL_LEVELS=false', + 'shapeCandidatesForLevel','addSpecialCellPattern','addWarpSpecial','addLockSpecial','addCrossingSpecial', + 'specialPathValid','crossingsSatisfied','activateCrossing','pathRenderSegments','pathStrokePieces','pathProgressAtCell','specialCellInfoMap','normalizeSpecialCells' +])assert(app.includes(marker),`Missing v47 marker: ${marker}`); +assert(app.includes('const AppLogic=globalThis.BendAppLogic')&&app.includes('AppLogic.shapeCandidatesForLevel')&&app.includes('AppLogic.stateForStorage')&&app.includes('AppLogic.collectConnectedLineComponent'),'Application does not consume the shared pure-logic module'); +assert(html.includes(`style.css?v=${appVersion}`)&&html.includes(`puzzle-core.js?v=${appVersion}-5`)&&html.includes(`app-logic.js?v=${appVersion}`)&&html.includes(`field-persistence.js?v=${appVersion}`)&&html.includes(`app.js?v=${appVersion}`),'Web assets do not match package/app/generator version'); +assert(worker.includes(`puzzle-core.js?v=${appVersion}-5`),'Worker imports an old puzzle-core asset'); +assert(functionSource('createPuzzleWorker').includes('message.error?job.reject')&&!functionSource('createPuzzleWorker').includes('message.error?generatePuzzleOnMainThread'),'Algorithmic worker failures are retried redundantly on the main thread'); +assert(html.includes(`v${appVersion}`)&&html.includes(`v${appVersion}`),'Visible version does not match package/app version'); +assert(html.includes('id="viewport" tabindex="-1"')&&['modal','storeModal','inventoryModal','timeAttackModal'].every(id=>html.includes(`id="${id}" aria-hidden="true" inert`)),'Hidden dialogs are not inert or the viewport is not programmatically focusable'); +assert(functionSource('closeDialogRoot').includes('focusOutsideDialog(root,preferredFocus)')&&functionSource('closeDialogRoot').indexOf('focusOutsideDialog')0')&&functionSource('clearSharedWorldJournalRow').includes('cloudOutboxDeleteKeys'),'Initial shared-world adoption does not replace stale local world rows or clean their outbox entries'); +assert(functionSource('noteCloudRow').includes("kind==='state'&&data?.states?.[id]?.solved!==true")&&functionSource('currentCloudPending').includes("solved===true"),'Unfinished personal paths can still enter the shared durable outbox'); +assert(functionSource('sharedExpansionRepairDelay').includes('SHARED_EXPANSION_GRACE_MS')&&functionSource('repairExpansions').includes('sharedExpansionRepairDelay(st)<=0'),'Non-solving clients can race the solver while publishing newly generated boards'); +assert(app.includes('solvedById')&&appLogicSource.includes('solvedById')&&serverSource.includes('state.solvedById=player.playerId'),'Shared solver identity is not persisted independently of the display name'); +assert(serverSource.includes('rowRevision=Math.max')&&serverSource.includes('serverTime()*1000'),'Server row revisions are not comparable with client revisions'); +assert(!app.includes('nearestUnselectedEndpointAtClient'),'Unselected endpoint clicks are still intercepted before dragging'); +assert(functionSource('extendPointerTo').includes('renderDragFrame(b)')&&functionSource('renderDragFrame').includes('liveEndpointPoint')&&!functionSource('renderDragFrame').includes('pathValid')&&!app.includes('path-live-tail'),'Pointer-following line does not use the lightweight live-endpoint renderer'); +assert(!app.includes('pathChangeMotion')&&!app.includes('renderDragTipMotion')&&!css.includes('.path-growth')&&!css.includes('.path-retraction'),'Line growth or retraction animation remains'); +assert(css.includes('.turn-count{fill:#fff;font-family:var(--dot-font)'),'Turn counts do not explicitly use the dot-styled game font'); +assert(functionSource('addWarpSpecial').includes('invalidateSpecialCellCaches'),'Warp insertion does not invalidate turn-analysis caches'); +assert(functionSource('specialCellInfoMap').includes('description')&&functionSource('specialInfoFromEvent').includes('b.specialInfo')&&html.includes('id="specialTooltip"'),'Special-cell hover descriptions are missing'); +for(const selector of ['.special-cross','.special-warp','.special-key','.special-door'])assert(css.includes(selector),`Missing special-cell style ${selector}`); + +assert(functionSource('makeSpecialMarker').includes("class:'key-ring'")&&!functionSource('makeSpecialMarker').includes(String.raw`textContent='\u9375'`),'Key special cell still uses a kanji glyph'); +assert(css.includes('.board-card.solved .special-cell-layer'),'Solved boards do not hide special cells'); +assert(functionSource('selectBoard').includes('setActiveBoard(b.id)')&&functionSource('setActiveBoard').includes('previous.drawing?.pointerId==null'),'Board selection does not preserve an active pointer draw while reconciling inactive keyboard state'); +assert(!css.includes('.board-card:not(.active)'),'Unselected-board styling remains active'); +assert(css.includes('.board-card.hud-current:not(.solved) .board-label')&&functionSource('boardPlayHudVisible').includes('hudBoardId===b?.id')&&functionSource('renderBoardNow').includes("card.classList.toggle('hud-current',hudVisible)"),'Board HUD is not limited to an actively played board'); +assert(functionSource('gateFromCell').includes('maxPixels')&&functionSource('extendPointerTo').includes('active.startGate,20'),'Opposite gate selection is not distance-limited'); +assert(functionSource('renderBoardNow').includes('pathStrokePieces(segments,startColor,endColor)')&&functionSource('pathColorAtCell').includes('pathProgressAtCell'),'Line colors are not blended along cumulative route length'); +assert(functionSource('updateSelectedProgress').includes('b.meta.level')&&!functionSource('updateSelectedProgress').includes('filled'),'Top HUD includes information other than level'); +assert(functionSource('renderBoardNow').includes('label.replaceChildren')&&functionSource('makeBoard').includes('label.append(boardActions)'),'Board HUD does not contain the level and attached actions'); +assert(html.includes('id="noiseCanvas" width="80" height="64"')&&functionSource('paintNoiseBackground').includes("perfCount('noiseFrames')")&&!css.includes('starTwinkle'),'Low-resolution noise background is missing or the retired starfield remains'); +assert(functionSource('unresolvedExpansionCandidates').includes('gateFrontierCandidates(meta)')&&!functionSource('unresolvedExpansionCandidates').includes('frontierCandidates(meta)'),'Normal expansion still creates non-gate frontier boards'); +assert(functionSource('syncBoundaryConnections').includes('boundaryColorSource(meta,gi,hit)'),'Connected gate colors are not canonicalized'); +assert(!app.includes('level-min-10')&&!app.includes('level-max-10')&&!app.includes('fieldEffects')&&!app.includes('fieldOverlayCanvas')&&!serverSource.includes('/api/player/place-field'),'Difficulty adjustment items or their field implementation remain active'); +assert(functionSource('makeBoard').includes('cellShape')&&functionSource('makeBoard').includes('boardCellsPath')&&!functionSource('makeBoard').includes('cellHits'),'Detailed boards do not use compound SVG paths or still allocate per-cell hit nodes'); +assert(functionSource('desiredInteractiveBoardIds').includes('for(const id of ids)')&&!app.includes('INTERACTIVE_DETAIL_CELL_BUDGET'),'Visible puzzles are not all selected for detailed rendering'); +assert(functionSource('shouldTeleportToUnsolvedBoard').includes('return false')&&!app.includes('function promoteStaticBoard(')&&!functionSource('bindBoard').includes('centerMeta('),'Board input can still promote a summary or teleport the camera'); +assert(html.includes('id="fpsCounter"')&&functionSource('refreshFpsCounter').includes('FPS ${fpsLastValue}')&&app.includes('AUXILIARY_FPS=30')&&app.includes('DRAG_TARGET_FPS=60'),'Lightweight FPS display or split interaction budgets are missing'); +assert(css.includes('.gem-particle{position:fixed')&&css.includes('width:28px;height:28px')&&functionSource('completionEffect').includes('1800'),'Completion gems are not enlarged or retained long enough'); +assert(css.includes('#customEmojiCursor{')&&css.includes('overflow:visible')&&css.includes('#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden'),'Non-flag custom cursors are still clipped or flag clipping is no longer isolated'); + +assert(functionSource('extendOne').includes('warpedDuringExtend=true')&&!functionSource('extendOne').includes('safeRelease(b.svg,pointerId)'),'Warp traversal still releases pointer capture'); +assert(functionSource('extendPointerTo').includes('pointerOffset'),'Warp continuation does not remap the pointer to the exit'); +assert(!app.includes('hysteresisDragPoint')&&!app.includes('DRAG_AXIS_LOCK_DISTANCE')&&!app.includes('POINTER_SNAP_RELEASE'),'Retired drawing hysteresis remains active'); +assert(functionSource('scheduleBoardDragFrame').includes('requestAnimationFrame')&&functionSource('queueBoardPointerMove').includes('scheduleBoardDragFrame')&&functionSource('bindBoard').includes('queueBoardPointerMove')&&functionSource('processBoardDragFrame').includes('const move=b.pendingPointerMove'),'Pointer movement is not frame-batched with ordered samples'); +assert(functionSource('queueCameraInteraction').includes('requestAnimationFrame')&&functionSource('movePan').includes('queueCameraInteraction'),'Camera panning is not frame-coalesced'); +assert(functionSource('leftFieldPanAllowed').includes("classList?.contains('solved')")&&functionSource('beginPan').includes('e.button!==2&&!leftFieldPanAllowed(e)'),'Left-drag field panning is not isolated to solved or undiscovered space'); +assert(functionSource('processBoardDragFrame').includes('edgePanVelocity')&&functionSource('processBoardDragFrame').includes('applyCamera(true)'),'Drag edge auto-pan is missing'); +assert(!functionSource('makeBoard').includes('darkness')&&!css.includes('.darkness'),'Retired darkness rendering remains'); +assert(functionSource('positionBoardLabel').includes('hudPlacementCandidates')&&functionSource('positionBoardLabel').includes("placement.side==='S'"),'HUD does not move to a free edge'); +assert(functionSource('positionBoardLabel').includes("b.label.style.width=innerWidth+'px'")&&functionSource('positionBoardLabel').includes("b.label.style.top=(PAD-22)+'px'"),'Top/bottom HUD does not span the board edge or clear upper gates'); +assert(functionSource('gateHitBox').includes("side==='N'")&&functionSource('makeBoard').includes('...gateHitBox(gp,g.side)'),'Gate hit areas are not constrained to the owning board'); +assert(!app.includes('sharedGateVisible'),'No-op shared-gate visibility wrapper remains'); +assert(functionSource('isSolved').includes('crossingsSatisfied(st,p)'),'Crossing is not a prerequisite for normal board completion'); +assert(functionSource('crossingsSatisfied').includes('crossingStateAtCell')&&functionSource('activateCrossing').includes('path.cells.push(cell)'),'Crossing is not derived from the live overlapping line state'); +assert(functionSource('extendOne').includes('lockForDoor')&&functionSource('extendOne').includes('pathHasLockKey'),'Door traversal does not require the same line to touch the key'); +assert(functionSource('extendOne').includes('warpPairForCell')&&functionSource('extendOne').includes('path.cells.push(cell,[...warpExit])'),'Warp traversal does not move to its paired cell'); +assert(functionSource('resetSelectedBoard').includes('specialProgress={crossings:[]}'),'Reset does not clear crossing progress'); +assert(functionSource('resetSelectedBoard').includes('renderBoardNow(b)')&&!functionSource('resetSelectedBoard').includes('syncBoundaryConnections'),'Reset is not immediate or still recreates inherited routes'); +assert(!app.toLowerCase().includes('undolastreset')&&!html.toLowerCase().includes('undo'),'Visible reset undo remains active'); + +assert(app.includes('STORE_CHANCE=1/30'),'Store appearance rate is not 1/30'); +assert(app.includes('MINIMAP_VIEW_CHUNKS_X=42'),'Minimap does not use the wider scale'); +assert(app.includes('SOUND_GAIN_MULTIPLIER=3.6')&&functionSource('soundTone').includes('Math.min(.28'),'Sound effects were not amplified'); +assert(app.includes('UNIQUE_SOLUTION_MIN_LEVEL=6')&&functionSource('placeChildAtFrontierAttempt').includes('level>=UNIQUE_SOLUTION_MIN_LEVEL'),'Unique-solution selection does not begin at level 6'); +assert(functionSource('shapeCandidatesForArea').includes('nearbyShapeFamilyCounts')&&appLogicSource.includes('generatedShapeFamilyKey')&&appLogicSource.includes('balancedShapeCandidates'),'Area-local board shape balancing is missing'); +assert(!html.includes('矢印キー:線を伸ばす')&&css.includes('.control-chips span{padding:9px 12px')&&css.includes('font-size:13px'),'Help controls are too small or still list arrow keys'); +assert(css.includes('.path.invalid{stroke:#cfa2a7')&&css.includes('.unfilled-warning-cells{fill:#3b3430')&&!css.includes('.num.turn-warning{fill:#ffbd69;animation'),'Rule warnings remain overly aggressive'); +assert(functionSource('addCrossingSpecial').includes('neighbors.some(candidate=>!valid.has')&&functionSource('addCrossingSpecial').includes('gateCells.has(key)'),'Crossing cells can still appear on a board edge or gate cell'); +assert(functionSource('updateZoomPresentation').includes('world-overview')&&functionSource('drawWorldOverview').includes('overviewCanvas')&&html.includes('id="overviewCanvas"')&&css.includes('#viewport.canvas-overview #world'),'Canvas overview mode is missing'); +assert(!functionSource('drawWorldOverview').includes('drawWorldOverviewPaths')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines'),'Far zoom does not use the minimap long-line renderer'); +assert(functionSource('rebuildWorldOverviewCache').includes('drawMapBoardCells')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines')&&!app.includes('function drawWorldOverviewShops('),'Zoomed-out rendering does not share the minimap renderer'); +assert(functionSource('makeStaticBoard').includes('renderedConnectedLineWidth(meta,index)')&&!functionSource('makeStaticBoard').includes('state.solved')&&css.includes('.static-summary-path{'),'Unsolved nearby boards do not preserve route thickness'); +assert(functionSource('updateTimeAttackUi').includes("classList.toggle('starting'")&&css.includes('@keyframes timeAttackStartEmphasis'),'Time-attack start clock emphasis is missing'); +const yellowFaces=app.match(/const YELLOW_FACE_CURSOR_SOURCE=`([\s\S]*?)`;/)?.[1]?.split('\n')||[]; +assert(yellowFaces.length===101&&yellowFaces.some(row=>row.startsWith('1FAE9|'))&&yellowFaces.some(row=>row.startsWith('1FAEA|')),'Complete Unicode Emoji 17.0 yellow-face cursor catalog is missing'); +assert(app.includes('MAX_FACE_CURSOR_PRICE=50000')&&app.includes('cost=MIN_CURSOR_PRICE+((index*61+37)%100)*MIN_CURSOR_PRICE')&&functionSource('storeItemPrice').includes('Math.min(MAX_FACE_CURSOR_PRICE,adjusted)'),'Yellow-face cursor prices are not deterministic random values spanning 500-50000 gems'); +const flagCodes=app.match(/const FLAG_REGION_CODES=`([^`]+)`\.split\(' '\)/)?.[1]?.split(' ')||[]; +assert(flagCodes.length===259&&new Set(flagCodes).size===259&&app.includes("['gbeng','England'],['gbsct','Scotland'],['gbwls','Wales']"),'Complete Unicode Emoji 17.0 flag cursor catalog is missing'); +const oecdCodes=app.match(/const OECD_FLAG_CODES=new Set\('([^']+)'\.split\(' '\)\)/)?.[1]?.split(' ')||[]; +assert(oecdCodes.length===38&&new Set(oecdCodes).size===38&&app.includes('OECD_FLAG_CURSOR_BASE_PRICE=20000')&&app.includes('FLAG_CURSOR_BASE_PRICE=10000'),'Flag cursor base prices or the 38-country OECD tier are missing'); +const flagAssetDir=path.join(root,'assets','flags'),flagAssets=fs.readdirSync(flagAssetDir).filter(name=>name.endsWith('.svg')); +assert(flagAssets.length===262&&fs.existsSync(path.join(flagAssetDir,'LICENSE-TWEMOJI.txt')),'Bundled cross-platform flag SVG catalog or attribution is incomplete'); +assert(functionSource('syncCursorAppearance').includes("image.src=selected.flagAsset")&&functionSource('setItemIcon').includes("image.src=item.flagAsset")&&functionSource('syncCursorAppearance').includes('nativeSupported')&&css.includes('cursor:none!important')&&css.includes('#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden')&&css.includes('border-radius:50%'),'Native/DOM SVG-backed circular flag cursor rendering is missing'); +assert(functionSource('buildDragCache').includes('endpoint-cursor-image')&&functionSource('buildDragCache').includes('DRAG_FLAG_CLIP_RADIUS')&&functionSource('buildDragCache').includes('x:-DRAG_FLAG_CURSOR_SIZE/2')&&functionSource('buildDragCache').includes("class:'drag-tip-group'")&&functionSource('updateDragCursorDesign').includes('activeCustomCursorItem')&&functionSource('renderDragFrame').includes('cache.tipGroup.style.transform')&&functionSource('bindBoard').includes('queueMicrotask(()=>updateCustomCursorFromPointer(e))')&&!css.includes('body.is-drawing #customEmojiCursor{display:none!important}'),'Grabbed cursor sizing, centering, clipping, or smooth cursor continuity is missing'); +assert(css.includes('@font-face{font-family:"DotGothic16Local"')&&css.includes('--emoji-font:')&&css.includes('body,button,input,select,textarea{font-family:var(--dot-font)}')&&!css.includes(':root{--dot-font:"DotGothic16"')&&html.includes('id="customEmojiCursor"'),'Bundled Japanese dot font is overridden or emoji-specific isolation is missing'); +assert(!html.includes('所持ジェム')&&!functionSource('completionEffect').includes('ジェム')&&!functionSource('updateScoreLensBadge').includes('予想ジェム'),'Standalone gem terminology remains in the reward UI'); +assert(functionSource('renderStorePanel').includes('storeInventoryItems(meta,store)')&&functionSource('purchaseStoreItem').includes('storeInventoryItems(meta,store).some'),'Store UI or purchase validation bypasses its seeded thirteen-item inventory'); +assert(functionSource('seededStoreItemIds').includes('.slice(0,12)')&&functionSource('seededStoreItemIds').includes('.slice(0,1)')&&functionSource('maybeOpenStore').includes('itemIds:seededStoreItemIds(meta.seed)'),'Stores do not persist twelve seeded cursors and one seeded non-cursor item'); +assert(functionSource('renderStorePanel').includes("{title:'アイテム'")&&functionSource('renderStorePanel').includes("{title:'カーソル'")&&functionSource('renderStorePanel').includes('if(category.cursor)card.append(icon,buy)')&&css.includes('.store-cursor-list{grid-template-columns:repeat(6'),'Shop is not split into item and horizontal twelve-cursor sections'); +assert(functionSource('renderInventoryPanel').includes("'inventory-cursor-grid'")&&functionSource('renderInventoryPanel').includes("option.classList.toggle('selected'")&&functionSource('useInventoryItemLoaded').includes("previousCursor===item.cursorStyle?'default':item.cursorStyle"),'Persistent click-to-toggle cursor inventory is missing'); +assert(functionSource('makeStaticBoard').includes("openStoreMeta(meta)")&&!functionSource('beginPan').includes('overviewShopAtClient'),'Distant overview still exposes a shop-only hit target instead of matching the minimap'); +assert(functionSource('discardUnmovedCreatedPath').includes('path.cells.length!==1')&&functionSource('bindBoard').includes('discardUnmovedCreatedPath(b)'),'Cancelled pickup creation can leave an orphan handle'); +assert(functionSource('renderDragFrame').includes('refreshDragNumberColors(b)'),'Number colors do not update in the live pickup renderer'); +assert(functionSource('detachPathFromStartGate').includes('path.detachedStart=true')&&functionSource('renderBoardNow').includes("'data-endpoint-side':'start'")&&!app.includes('whitePickupEnd')&&!functionSource('finalizeAtGate').includes("'#fff'"),'Two-ended colored pickup support is incomplete'); +assert(!functionSource('openTipMergePlan').includes('a.detachedStart||o.detachedStart')&&functionSource('openTipMergePlan').includes('tipDistance!==0')&&!app.includes('function gateConnectionSteps'),'Same-cell pickup joining or exact-cell gate snapping is incomplete'); +assert(functionSource('updateScoreLensBadge').includes('projectedScoreForBoard')&&functionSource('scoreLensVisibleForMeta').includes('SCORE_LENS_ZOOM_THRESHOLD')&&functionSource('useInventoryItemLoaded').includes('data.scoreLensEnabled=!previous')&&/id:'score-lens'[^\n]+scoreLens:true[^\n]+icon:/.test(app)&&!app.includes('SCORE_LENS_RADIUS')&&css.includes('.score-lens-badge'),'Persistent ON/OFF score lens display is missing or still asks for a position'); +assert(functionSource('renderStorePanel').includes('formatScore(price)')&&!functionSource('renderStorePanel').includes('price-data.score')&&!functionSource('purchaseStoreItem').includes('price-data.score'),'Store buttons do not always show the actual item price'); +assert(functionSource('zoomAt').includes('MIN_CAMERA_SCALE'),'Camera cannot zoom out to overview scale'); +assert(functionSource('addObstaclePattern').includes('puzzle.difficulty=sourcePuzzle.difficulty'),'Obstacle generation changes the displayed level and section constraint'); +assert(functionSource('addObstaclePattern').includes('largePuzzleBoost')&&functionSource('obstacleCellLimit').includes('Math.floor(total*.2)')&&functionSource('addCrossingSpecial').includes('obstacleCellLimit(p)'),'Large-puzzle obstacle scaling or the strict 20% cap is missing'); +assert(functionSource('scoreFromThickness').includes('hardMultiplier')&&functionSource('scoreFromThickness').includes('largeMultiplier'),'Hard and large puzzle score scaling is missing'); +const storeDescriptions=[...app.slice(app.indexOf('const STORE_ITEM_BASE='),app.indexOf('const YELLOW_FACE_CURSOR_SOURCE=')).matchAll(/description:'([^']*)'/g)].map(match=>match[1].replace(/\\u3002/g,'。')); +assert(storeDescriptions.length===1&&storeDescriptions.every(description=>(description.match(/。/g)||[]).length===1),'Current non-cursor store items are missing or their descriptions are not exactly one sentence'); +assert([...appLogicSource].every(ch=>ch.charCodeAt(0)<128),'app-logic.js contains non-ASCII source'); + +const BendPuzzle=loadBendPuzzle(),AppLogic=loadAppLogic(); +assert(BendPuzzle?.GENERATOR_VERSION===5,'Generator version mismatch'); +const starter=starterPuzzle();assert(BendPuzzle.solverDifficulty(starter)===1,'Bundled origin puzzle is not level 1'); +const generatedA=BendPuzzle.generatePuzzle([[0,0]],123456,2,0,0),generatedB=BendPuzzle.generatePuzzle([[0,0]],123456,2,0,0); +assert(JSON.stringify(generatedA)===JSON.stringify(generatedB),'Puzzle generation is not deterministic'); +const multi=BendPuzzle.generatePuzzle([[0,0],[1,0],[0,1]],987654,6,12,-9); +assert(multi.valid.length===75,'Multi-section normal puzzle generation failed'); +assert(multi.difficulty>5&&multi.n.filter(clue=>clue[2]>=4).length>=Math.ceil(multi.n.length*.25),'A demanding high-level puzzle was incorrectly collapsed to level 5'); +assert(multi.complexity.rawRating===BendPuzzle.solutionComplexity(multi).rating&&BendPuzzle.difficultyFitsRegion(multi.difficulty,6),'Raw complexity is not retained or regional difficulty classification escaped its band'); +const highCluePuzzle=BendPuzzle.generatePuzzle([[2,0],[0,1],[1,1],[2,1],[0,2],[1,2],[0,3],[1,3]],7496588,9,27,-1); +assert(highCluePuzzle.difficulty>5&&highCluePuzzle.n.filter(clue=>clue[2]>=4).length>=Math.ceil(highCluePuzzle.n.length*.25),'A valid level 6-10 puzzle lacks meaningful bend clues'); +assert(multi.solution.some(path=>path.cells.some((cell,index)=>index>0&&(Math.floor(cell[0]/5)!==Math.floor(path.cells[index-1][0]/5)||Math.floor(cell[1]/5)!==Math.floor(path.cells[index-1][1]/5)))),'Normal multi-section puzzle does not connect sections'); + +const shapesBySize=new Map();for(const shape of BendPuzzle.SHAPES){const size=shape.length;if(!shapesBySize.has(size))shapesBySize.set(size,[]);shapesBySize.get(size).push(shape)} +const shapeDeps={shapesBySize,hash32:BendPuzzle.hash32,rngFrom:BendPuzzle.rngFrom,shuffle:BendPuzzle.shuffle}; +const familyA=[[0,0],[1,0],[2,0]],familyARotated=[[0,0],[0,1],[0,2]],familyB=[[0,0],[1,0],[0,1]]; +assert(AppLogic.generatedShapeFamilyKey(familyA)===AppLogic.generatedShapeFamilyKey(familyARotated)&&AppLogic.generatedShapeFamilyKey(familyA)!==AppLogic.generatedShapeFamilyKey(familyB),'Shape-family normalization does not combine rotations/reflections correctly'); +const balancedFamilies=AppLogic.balancedShapeCandidates([familyA,familyARotated,familyB],12345,{hash32:BendPuzzle.hash32,rngFrom:BendPuzzle.rngFrom,shuffle:BendPuzzle.shuffle}).slice(0,2).map(AppLogic.generatedShapeFamilyKey); +assert(new Set(balancedFamilies).size===2,'Shape balancing still exhausts one orientation-rich family before another family'); +for(let level=1;level<=10;level++){ + const range=AppLogic.sectionCountRange(level);assert(range.max===level&&range.min===Math.max(1,level-3),`Level ${level} section range is invalid`); + const candidates=AppLogic.shapeCandidatesForLevel(0x470000+level,level,8,shapeDeps);assert(candidates.length>0,`Level ${level} has no section shapes`); + for(const shape of candidates){assert(shape.length>=range.min&&shape.length<=range.max,`Level ${level} generated ${shape.length} sections outside ${range.min}-${range.max}`);const set=new Set(shape.map(([x,y])=>`${x},${y}`));let reached=new Set([`${shape[0][0]},${shape[0][1]}`]),changed=true;while(changed){changed=false;for(const[x,y]of shape)if(!reached.has(`${x},${y}`)&&[[1,0],[-1,0],[0,1],[0,-1]].some(([dx,dy])=>reached.has(`${x+dx},${y+dy}`))){reached.add(`${x},${y}`);changed=true}}assert(reached.size===set.size,'Generated section shape is disconnected')} +} + +const normalizeContext={AppLogic,LINE_COLORS:Array(10).fill('#000'),isPlainObject:value=>!!value&&typeof value==='object'&&!Array.isArray(value),ckey:(r,c)=>`${r},${c}`,sameCell:(a,b)=>a[0]===b[0]&&a[1]===b[1],manhattan:(a,b)=>Math.abs(a[0]-b[0])+Math.abs(a[1]-b[1]),solverDifficulty:BendPuzzle.solverDifficulty,deepClone:value=>JSON.parse(JSON.stringify(value))}; +vm.createContext(normalizeContext); +vm.runInContext([functionSource('normalizePath'),functionSource('normalizeSpecialCells'),functionSource('repairWarpNumberClues'),functionSource('normalizeStoredPuzzle'),functionSource('puzzleForStorage'),'this.logic={normalizeStoredPuzzle,puzzleForStorage}'].join('\n'),normalizeContext); +const normalized=normalizeContext.logic.normalizeStoredPuzzle(starter,[[0,0]],1);assert(normalized,'Starter fails stored-puzzle validation'); +const stored=normalizeContext.logic.puzzleForStorage(normalized);assert(stored.solution.length===starter.solution.length,'Stored puzzle round-trip lost its solution'); + +assert(functionSource('gateCandidateAtPoint').includes('gateCandidatesInCell')&&functionSource('gateStartCandidate').includes('gateCandidateAtPoint')&&functionSource('bindBoard').includes('gateStartCandidate(b,point,hintCell,directGate)'),'Gate and gate-cell input do not share one selector'); +assert(functionSource('placeChildAtFrontierAttempt').includes('puzzleSupportsConnectionRequirements')&&functionSource('placeChildAtFrontierAttempt').includes('fallbackShape=[[0,0]]')&&functionSource('placeChildAtFrontier').includes('frontierGeometryStillViable')&&functionSource('expandMetaNow').includes('missingGateConnections(meta)'),'Expansion lacks validated safe fallback or actual connection verification'); +assert(functionSource('placeChildAtFrontierAttempt').includes('fixedPortProfilesForRequirements')&&functionSource('placeChildAtFrontierAttempt').includes('portSeed')&&functionSource('placeChildAtFrontierAttempt').includes('specialSeed')&&functionSource('placeChildAtFrontierAttempt').includes('generatedPuzzleIssue'),'Failed boards are not fully regenerated with provisional gates and special cells'); +assert(functionSource('closedVoidRepairCandidates').includes('closedVoidRepair:true')&&functionSource('repairExpansions').includes('closedVoidRepairCandidates().filter')&&functionSource('repairExpansions').includes('.slice(0,2)')&&functionSource('pendingExpansionCount').includes('closedVoidRepairCandidates().length'),'Saved fields do not detect and repair enclosed missing puzzle squares'); +assert(functionSource('generatePuzzleAsync').includes('generationOptions')&&worker.includes('generationOptions || null'),'Generation options are not passed through the worker'); +assert(functionSource('reopenMissingGateExpansions').includes('st.expanded=false')&&functionSource('reopenMissingGateExpansions').includes('missingGateConnections(meta)'),'Persisted false-positive expansion states are not reopened safely'); +console.log(`BEND FIELD v${appVersion} source and shared-logic smoke test passed`); diff --git a/test/special-cell-smoke-test.js b/test/special-cell-smoke-test.js new file mode 100644 index 0000000..930bc26 --- /dev/null +++ b/test/special-cell-smoke-test.js @@ -0,0 +1,88 @@ +'use strict'; +const {vm,assert,functionSource,loadBendPuzzle,loadAppLogic}=require('./helpers/app-source'); +const BendPuzzle=loadBendPuzzle(),AppLogic=loadAppLogic(); +const context={ + AppLogic, + SPECIAL_CELL_MIN_LEVEL:5,SPECIAL_CELL_DEBUG_ALL_LEVELS:false,data:{metas:{},specialMechanicsSeen:[]}, + hash32:BendPuzzle.hash32,rngFrom:BendPuzzle.rngFrom, + deepClone:value=>JSON.parse(JSON.stringify(value)),key2:(x,y)=>`${x},${y}`,ckey:(r,c)=>`${r},${c}`, + SIDE_D:{N:[-1,0],S:[1,0],W:[0,-1],E:[0,1]},sameCell:(a,b)=>a[0]===b[0]&&a[1]===b[1],manhattan:(a,b)=>Math.abs(a[0]-b[0])+Math.abs(a[1]-b[1]) +}; +vm.createContext(context); +vm.runInContext([ + 'specialCellSet','invalidateSpecialCellCaches','reservedSpecialKeys','cellSet','warpMap','warpPairForCell','isWarpTransition','pathCellsAdjacent','lockForDoor','pathHasLockKey','crossingKeys','pathIndexesAtCell','crossingStateAtCell','crossingsSatisfied','gateObj','outsidePoint','analyzeTurns','turnAnalysis','partialTurnCount','numbersForPath','specialPathValid','pathAxisAtCell','pathValid','usedGateSet','occupiedMap','isSolved','rebuildSolutionClues','addWarpSpecial','addLockSpecial','obstacleCellLimit','buildCrossingTemplate','addCrossingSpecial','recentSpecialMechanicTypes','addScheduledSpecial','specialCellUsage','addSpecialCellPattern' +].map(name=>functionSource(name)).join('\n')+'\nthis.logic={specialCellSet,turnAnalysis,pathValid,isSolved,addWarpSpecial,addLockSpecial,addCrossingSpecial,addSpecialCellPattern,isWarpTransition,crossingStateAtCell,crossingsSatisfied};',context); +const logic=context.logic; +for(let level=1;level<=4;level++)assert(AppLogic.specialSchedule(level,100,level).types.length===0,`Level ${level} scheduled a production special`); +for(const level of[5,6]){const schedule=AppLogic.specialSchedule(level,100,level,['warp','lock','crossing'],[]);assert(schedule.types.length===1&&schedule.setCount===1,`Level ${level} did not schedule exactly one minimum set`)} +const level7=AppLogic.specialSchedule(7,200,7,['warp','lock','crossing'],[]),level8=AppLogic.specialSchedule(8,200,8,['warp','lock','crossing'],[]); +assert(level7.setCount===2&&level8.setCount===3,'Level 7-8 special density does not increase deterministically'); +{ + const puzzle={valid:[[2,0],[2,1],[0,3],[0,4],[1,4],[2,4]],g:[[2,0,'W'],[2,4,'S']],n:[[0,4,1]],specialCells:{crossings:[],warps:[{a:[2,1],b:[0,3]}],locks:[]},solution:[]},path={startGate:0,endGate:1,openGate:null,cells:[[2,0],[2,1],[0,3],[0,4],[1,4],[2,4]]}; + assert(logic.turnAnalysis(path,puzzle).count===1,'Warp transition itself was counted as a turn'); +} + +let base=null; +const shapes=[[[0,0],[1,0],[0,1],[1,1]],[[0,0],[1,0],[2,0],[0,1],[1,1]],[[0,0],[1,0],[2,0],[0,1],[1,1],[2,1]]]; +for(const shape of shapes)for(let seed=1;seed<200&&!base;seed++){ + let puzzle;try{puzzle=BendPuzzle.generatePuzzle(shape,0x470000+seed,8,0,0)}catch(_){continue} + const reserved=new Set(),warp=context.deepClone(puzzle),lock=context.deepClone(puzzle); + if(logic.addWarpSpecial(warp,context.rngFrom(seed),reserved)&&logic.addLockSpecial(lock,context.rngFrom(seed+1),new Set()))base=puzzle; +} +assert(base,'Could not find a puzzle supporting warp and key/door special cells'); +{ + const single=BendPuzzle.generatePuzzle([[0,0]],0x47c205ed,5,0,0),cross=context.deepClone(single); + assert(logic.addCrossingSpecial(cross,context.rngFrom(0x47c205ed),new Set()),'A one-section crossing template could not be generated within the obstacle cap'); + assert((cross.obstacles||[]).length<=Math.floor((cross.valid.length+(cross.obstacles||[]).length)*.2),'Crossing template exceeded the 20% obstacle cap'); +} +{ + const cached=context.deepClone(base);cached.specialCells={crossings:[],warps:[],locks:[]};cached._warpMap=new Map(); + const added=logic.addWarpSpecial(cached,context.rngFrom(0x9e3779b9),new Set());assert(added,'Could not add a warp for stale-cache regression'); + assert(!Object.prototype.hasOwnProperty.call(cached,'_warpMap'),'Warp mutation did not invalidate the stale warp map');const addedPair=cached.specialCells.warps[0];assert(logic.isWarpTransition(cached,addedPair.a,addedPair.b),'Warp lookup did not rebuild after cache invalidation'); + const after=cached.solution.map(path=>logic.turnAnalysis(path,cached).count);assert(cached._warpMap&&typeof cached._warpMap.get==='function'&&cached._warpMap.size===2,'Warp map was not rebuilt after adding a warp'); + const warpedIndex=cached.solution.findIndex(path=>cached.specialCells.warps.some(pair=>path.cells.some(cell=>context.sameCell(cell,pair.a))));assert(warpedIndex>=0&&Number.isFinite(after[warpedIndex]),'Warp turn analysis did not run'); +} + +for(let level=1;level<=4;level++){const low=logic.addSpecialCellPattern(base,0x700000+level,level),special=logic.specialCellSet(low);assert(!special.warps.length&&!special.locks.length&&!special.crossings.length,`Level ${level} generated a production special`)} +const counts={warp:0,lock:0,cross:0,boards:0};let examples={}; +for(let seed=0;seed<900;seed++){ + context.data.specialMechanicsSeen=[]; + const puzzle=logic.addSpecialCellPattern(base,0x710000+seed,5);if(!puzzle)continue;const special=logic.specialCellSet(puzzle), + present=[special.warps.length&&'warp',special.locks.length&&'lock',special.crossings.length&&'cross'].filter(Boolean); + assert(present.length===1&&puzzle.specialSchedule?.introduction===true&&puzzle.specialSchedule?.setCount===1,'Level-5 first encounter was not one minimum special type'); + if(special.warps.length){counts.warp++;examples.warp||=puzzle} + if(special.locks.length){counts.lock++;examples.lock||=puzzle} + if(special.crossings.length){counts.cross++;examples.cross||=puzzle} + if(special.warps.length||special.locks.length||special.crossings.length)counts.boards++; +} +for(const [type,count] of Object.entries({warp:counts.warp,lock:counts.lock}))assert(count>0,`${type} was never selected by deterministic first-encounter scheduling`); +assert(counts.boards>0,'Level-5 scheduling generated no viable special boards'); + +function verifyPuzzle(puzzle){ + const special=logic.specialCellSet(puzzle),valid=new Set(puzzle.valid.map(cell=>context.ckey(...cell))),crossingKeys=new Set(special.crossings.map(cell=>context.ckey(...cell))),covered=new Map(); + assert((puzzle.obstacles||[]).length<=Math.floor((puzzle.valid.length+(puzzle.obstacles||[]).length)*.2),'Special-cell conversion exceeded the 20% obstacle cap'); + for(const path of puzzle.solution){ + for(let i=0;icontext.sameCell(cell,pair.a)),bi=path.cells.findIndex(cell=>context.sameCell(cell,pair.b));if(ai>=0||bi>=0){assert(Math.abs(ai-bi)===1,'Warp endpoints are not consecutive');found=true}}assert(found,'Warp pair is absent from the solution')} + for(const lock of special.locks){let found=false;for(const path of puzzle.solution){const ki=path.cells.findIndex(cell=>context.sameCell(cell,lock.key)),di=path.cells.findIndex(cell=>context.sameCell(cell,lock.door));if(ki>=0||di>=0){assert(ki>=0&&di>ki,'Key is not before its door on the solution line');found=true}}assert(found,'Key/door pair is absent from the solution')} + const state={paths:context.deepClone(puzzle.solution),specialProgress:{crossings:special.crossings.map(cell=>context.ckey(...cell))}};assert(logic.isSolved(state,puzzle),'Generated special-cell solution does not solve its board'); + for(const cell of special.crossings){const key=context.ckey(...cell),validSet=new Set(puzzle.valid.map(candidate=>context.ckey(...candidate))),gateSet=new Set(puzzle.g.map(g=>context.ckey(g[0],g[1])));assert(covered.get(key)===2,'Crossing solution does not use the crossing cell exactly twice');assert(!gateSet.has(key),'Crossing cell was placed on a gate cell');for(const neighbor of[[cell[0]-1,cell[1]],[cell[0]+1,cell[1]],[cell[0],cell[1]-1],[cell[0],cell[1]+1]])assert(validSet.has(context.ckey(...neighbor)),'Crossing cell was placed on the board edge')} +} + +for(const puzzle of Object.values(examples))verifyPuzzle(puzzle); +{ + const puzzle={valid:[[1,2],[2,1],[2,2],[2,3],[3,2]],g:[],n:[],specialCells:{crossings:[[2,2]],warps:[],locks:[]}},state={paths:[{cells:[[2,1],[2,2],[2,3]]},{cells:[[1,2],[2,2],[3,2]]}],specialProgress:{crossings:[]}}; + assert(logic.crossingStateAtCell(state,puzzle,[2,2]),'Perpendicular live crossing was not detected'); + state.paths[1].cells=[[1,2],[2,2],[2,3]];assert(!logic.crossingsSatisfied(state,puzzle),'Two lines with the same axis were accepted as a crossing'); + const solvedPuzzle={valid:[[2,0],[2,1],[2,2],[2,3],[1,3],[0,2],[1,2],[3,2],[3,1]],g:[[2,0,'W'],[1,3,'N'],[0,2,'N'],[3,1,'W']],n:[[2,3,1],[3,2,1]],specialCells:{crossings:[[2,2]],warps:[],locks:[]}},solvedState={paths:[{startGate:0,endGate:1,cells:[[2,0],[2,1],[2,2],[2,3],[1,3]]},{startGate:2,endGate:3,cells:[[0,2],[1,2],[2,2],[3,2],[3,1]]}],specialProgress:{crossings:[]}}; + assert(logic.isSolved(solvedState,solvedPuzzle),'A live perpendicular crossing did not permit normal board clear'); + solvedState.paths[1].cells=[[0,2],[1,2],[1,1],[2,1],[3,1]];solvedState.specialProgress.crossings=['2,2'];assert(!logic.isSolved(solvedState,solvedPuzzle),'Crossing history permitted clear after the crossing state was removed'); +} + +console.log(`Special-cell generation passed: ${counts.warp} warp, ${counts.lock} key/door, ${counts.cross} crossing first-encounter boards`); diff --git a/test/stage34-smoke-test.js b/test/stage34-smoke-test.js new file mode 100644 index 0000000..3b0ef01 --- /dev/null +++ b/test/stage34-smoke-test.js @@ -0,0 +1,39 @@ +'use strict'; +const {app,assert,functionSource,loadAppLogic}=require('./helpers/app-source'); +const AppLogic=loadAppLogic(); +const placeSource=functionSource('placeChildAtFrontier'),expandSource=functionSource('expandMetaNow'),metaStateSource=functionSource('metaState'),snapshotSource=functionSource('snapshotForStorage'); +for(const forbidden of ['ensureBoards','renderAll','updateHud','nextPaint','save(','rebuildOccupancy'])assert(!placeSource.includes(forbidden),`Child placement still performs ${forbidden}`); +assert((expandSource.match(/refreshWorldView\(/g)||[]).length===1,'Expansion does not use one presentation/persistence commit'); +for(const forbidden of ['ensureBoards','renderAll','updateHud'])assert(!expandSource.includes(forbidden),`Expansion bypasses the shared view pipeline with ${forbidden}`); +assert(metaStateSource.includes('normalizeState(state)'),'Live state normalization is not delegated to normalizeState'); +assert(snapshotSource.includes('metaRowsForStorage()')&&snapshotSource.includes('stateRowsForStorage()'),'Snapshot serialization bypasses shared row serializers'); +assert(functionSource('pushCloudPending').includes('cloudRowsForStorage')&&functionSource('cloudRowsForStorage').includes('metaForStorage')&&functionSource('cloudRowsForStorage').includes('stateForStorage'),'Cloud serialization bypasses full-detail row serializers'); +assert(functionSource('stateForStorage').includes('AppLogic.stateForStorage'),'State serialization is not delegated to the shared module'); +assert(functionSource('collectConnectedLineComponent').includes('AppLogic.collectConnectedLineComponent'),'Connected-line traversal is not delegated to the shared module'); + +const data={ + metas:{A:{id:'A',puzzle:{}},B:{id:'B',puzzle:{}}}, + states:{A:{paths:[{startGate:0,endGate:1,cells:[[0,0],[0,1]]}]},B:{paths:[{startGate:0,endGate:1,cells:[[0,0],[1,0],[2,0]]}]}} +}; +let matchingCalls=0; +const environment={ + data, + metaState:id=>data.states[id], + matchingNeighborGate(meta,gate){ + matchingCalls++; + if(meta.id==='A'&&gate===1)return{meta:data.metas.B,gateIndex:0}; + if(meta.id==='B'&&gate===0)return{meta:data.metas.A,gateIndex:1}; + return null; + } +}; +const componentCache=new Map(),lengthA=AppLogic.connectedLineLength(data.metas.A,0,componentCache,environment),callsAfterFirst=matchingCalls,lengthB=AppLogic.connectedLineLength(data.metas.B,0,componentCache,environment); +assert(lengthA===5&&lengthB===5,'Connected-line component length is incorrect'); +assert(matchingCalls===callsAfterFirst,'Connected-line component was traversed again for a cached member'); +const widthCache=new Map(),widthA=AppLogic.renderedConnectedLineWidth(data.metas.A,0,widthCache,environment,componentCache),callsAfterWidth=matchingCalls,widthB=AppLogic.renderedConnectedLineWidth(data.metas.B,0,widthCache,environment,componentCache); +assert(widthA===widthB&&matchingCalls===callsAfterWidth,'Connected-line width did not reuse the cached component'); + +const stateRow=AppLogic.stateForStorage({paths:[],specialProgress:{crossings:[]},solved:false,expanded:false,expansionRetryRound:7,solvedBy:null,scoreAwarded:0,scoreVersion:2,store:null,rev:5,transient:'remove'},{scoreVersion:2}); +assert(stateRow.rev===5&&stateRow.expansionRetryRound===7&&!('transient' in stateRow),'State serializer lost expansion repair progress or leaked transient fields'); +const emptyState=AppLogic.stateForStorage(null,{scoreVersion:3,normalizeState:()=>({paths:[],specialProgress:{crossings:[]},scoreVersion:3})}); +assert(emptyState.scoreVersion===3&&Array.isArray(emptyState.paths),'State serializer fallback is invalid'); +console.log('Stage 3/4 regression passed: batched expansion, shared traversal, normalization, serialization, and view pipeline'); diff --git a/test/storage-smoke-test.js b/test/storage-smoke-test.js new file mode 100644 index 0000000..2b6b6b0 --- /dev/null +++ b/test/storage-smoke-test.js @@ -0,0 +1,101 @@ +'use strict'; +const {vm,assert,functionSource}=require('./helpers/app-source'); +const initSource=functionSource('init'); +const initialSource=functionSource('readInitialDataAsync'); +assert(initialSource.includes("Object.prototype.hasOwnProperty.call(bundle,'snapshot')?bundle.snapshot:bundle"), + 'A fresh database bundle with snapshot:null can be mistaken for game data'); +assert(!initSource.includes('fullResync')&&!initSource.includes("cloudOutboxDeleteKeys.add('full-resync')"),'Retired full-resync migration remains in startup'); +assert(initSource.includes('recoveredDeletionTombstones')&&initSource.includes('deletedBoardRevisions.set(id,tombstone.rev')&&initSource.includes('deletedBoardAuthors.set(id,tombstone.revAuthor'),'Recovered deletion tombstones are not restored into the durable deletion queue'); +async function runUpgradeCase(oldVersion){ + const worldWrites=[],deletedStores=[];let openedVersion=0; + const genericStore=()=>({indexNames:{contains:()=>true},createIndex:()=>{},put:()=>{},clear:()=>{},get:()=>{const request={result:null};queueMicrotask(()=>request.onsuccess?.());return request}}), + worldsStore={...genericStore(),put:row=>worldWrites.push(row)}, + stores=new Proxy({worlds:worldsStore},{get:(target,name)=>target[name]||(target[name]=genericStore())}),db={objectStoreNames:{contains:()=>true},deleteObjectStore:name=>deletedStores.push(name),close(){}},request={result:db,transaction:{objectStore:name=>stores[name]}}; + const context={ + Promise,console:{warn:()=>{}},idbAvailable:true,idbHealth:'unverified',worldDbAbandoned:false,worldDbPromise:null,worldDbName:'test-world',IDB_LAYOUT_VERSION:8,FIELD_STORAGE_FORMAT:2,SAVE_SCHEMA:31,WORLD_GENERATION:'current-only-test',IDB_STARTUP_TIMEOUT:8000,cloudApiEnabled:false,cloudOutboxReady:true, + indexedDB:{open(_name,version){openedVersion=version;queueMicrotask(()=>{request.onupgradeneeded?.({oldVersion});queueMicrotask(()=>request.onsuccess?.())});return request}}, + validWorldEpoch:value=>typeof value==='string'&&value.startsWith('world:'),createWorldEpoch:()=> 'world:upgrade-test',rememberWorldEpoch:()=>true, + defaultData:()=>({metas:{},states:{},worldEpoch:null}),globalForStorage:value=>({...value,metas:undefined,states:undefined}), + requestValue:request=>new Promise((resolve,reject)=>{request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}), + queueMicrotask,setTimeout,clearTimeout + }; + vm.createContext(context);vm.runInContext(`${functionSource('openWorldDb')}\nthis.openWorldDb=openWorldDb;`,context);await context.openWorldDb(); + return{openedVersion,worldWrites,deletedStores}; +} +async function runCase({mirror,stored,loadError=null,journals=[],coverage=new Map()}){ + const removedKeys=[]; + const context={ + console:{warn:()=>{}},idbAvailable:true,idbHealth:'ready',indexedDbExpectedAtStartup:true,worldDbAbandoned:false,worldDbPromise:null,cloudApiEnabled:false,cloudOutboxReady:true,cloudAvailable:false,activeStorageFormat:2,FIELD_STORAGE_FORMAT:2,IDB_STARTUP_TIMEOUT:8000, + storageKey:'save',loadNotices:[],readInitialData:()=>mirror,safeLocalGet:()=>mirror?'{mirror}':null, + readRecoveryJournals:()=>journals,safeLocalRemove:key=>{removedKeys.push(key);return true}, + recoveryJournalsToCover:[],recoveredDeletionTombstones:new Map(), + startupRecoveredMetaIds:new Set(),startupRecoveredStateIds:new Set(),startupRecoveredDeletedIds:new Set(), + loadSnapshotFromDb:async()=>{if(loadError)throw loadError;return{snapshot:stored,coverage,databaseJournals:[],worldEpoch:'world:test-epoch'}}, + withStartupTimeout:promise=>promise, + validWorldEpoch:value=>typeof value==='string'&&value.startsWith('world:'),storedWorldEpoch:()=>null,createWorldEpoch:()=> 'world:test-epoch',rememberWorldEpoch:()=>true, + defaultData:()=>({metas:{},states:{}}), + attachWorldEpoch:(snapshot,epoch)=>Object.assign(snapshot||{metas:{},states:{}},{worldEpoch:epoch}),abandonWorldDatabase:()=>{context.worldDbAbandoned=true;context.idbAvailable=false;context.idbHealth='uncertain'}, + isPlainObject:value=>!!value&&typeof value==='object'&&!Array.isArray(value), + deepClone:value=>JSON.parse(JSON.stringify(value)), + mergeBoardStates:(current,incoming)=>!current?JSON.parse(JSON.stringify(incoming)):!incoming?current:((incoming.rev||0)>(current.rev||0)?JSON.parse(JSON.stringify(incoming)):current), + normalizeCloudPending:value=>({metaIds:[...new Set(value?.metaIds||[])],stateIds:[...new Set(value?.stateIds||[])],deleted:[...new Set(value?.deleted||[])],globalChanged:value?.globalChanged===true}), + normalizeMeta:(_id,value)=>value,normalizeState:value=>value, + normalizeSnapshot:value=>value + }; + vm.createContext(context); + vm.runInContext(`${functionSource('revisionVersion')}\n${functionSource('compareRevisionVersions')}\n${functionSource('setRecoveryJournalsToCover')}\n${functionSource('mergeRecoveryJournal')}\n${functionSource('mergeRecoveryJournals')}\n${functionSource('combineRecoveryJournals')}\n${functionSource('mergeV2RecoveryJournals')}\n${functionSource('readInitialDataAsync')}\nthis.logic={readInitialDataAsync,mergeRecoveryJournal};`,context); + return{result:await context.logic.readInitialDataAsync(),context,removedKeys}; +} +(async()=>{ + let test=await runCase({mirror:{metas:{},states:{}},stored:null}); + assert(test.result.metas&&test.result.states,'A fresh database bundle did not produce a valid default snapshot'); + const mirror={metas:{B0:{id:'B0'}},updatedAt:20},emptyDb={metas:{},updatedAt:30}; + test=await runCase({mirror,stored:emptyDb}); + assert(Object.keys(test.result.metas).length===0,'The retired compact mirror overrode the current database'); + const db={metas:{B0:{id:'B0'},B1:{id:'B1'}},updatedAt:40}; + test=await runCase({mirror,stored:db}); + assert(Object.keys(test.result.metas).length===2&&test.result.metas.B1,'Populated database was not loaded'); + const richerMirror={metas:{B0:{id:'B0'},B1:{id:'B1'},B2:{id:'B2'},B3:{id:'B3'}},updatedAt:35}; + const staleDb={metas:{B0:{id:'B0'}},updatedAt:50}; + test=await runCase({mirror:richerMirror,stored:staleDb}); + assert(Object.keys(test.result.metas).length===1&&test.result.metas.B0&&!test.result.metas.B3,'A stale richer mirror resurrected a board deleted from the newer database'); + const newerMirror={metas:{B0:{id:'B0'},B1:{id:'B1'}},updatedAt:60}; + const olderSameSizeDb={metas:{B0:{id:'B0'},B1:{id:'B1'}},updatedAt:55}; + test=await runCase({mirror:newerMirror,stored:olderSameSizeDb}); + assert(Object.keys(test.result.metas).length===2&&test.result.updatedAt===55,'The current database was not authoritative over a retired mirror'); + test=await runCase({mirror,stored:null,loadError:new Error('timeout')}); + assert(Object.keys(test.result.metas).length===1&&test.result.metas.B0,'Database failure did not fall back to the compact mirror'); + assert(test.context.idbAvailable===false&&test.context.idbHealth==='uncertain','Unverified database fallback remained writable'); + const journalBase={metas:{B0:{id:'B0',rev:100}},states:{B0:{rev:100}},updatedAt:100,nextId:2}; + const staleDelete={schema:30,worldGeneration:'test',updatedAt:101,sessionId:'delete-session',seq:1,deleted:[{id:'B0',rev:99}]}; + const freshDelete={...staleDelete,seq:2,deleted:[{id:'B0',rev:101}]}; + let merged=test.context.logic.mergeRecoveryJournal(journalBase,staleDelete); + assert(merged.metas.B0&&merged.states.B0,'Older deletion tombstone removed a newer board revision'); + merged=test.context.logic.mergeRecoveryJournal(journalBase,freshDelete); + assert(!merged.metas.B0&&!merged.states.B0,'Newer deletion tombstone did not remove the covered board and state'); + const currentDb={metas:{B0:{id:'B0',rev:10}},states:{B0:{rev:10}},updatedAt:100,nextId:2}; + const coveredJournal={schema:30,worldGeneration:'test',updatedAt:105,sessionId:'covered',seq:3,_storageKey:'journal:covered',metas:[{id:'B2',rev:1}],states:[],deleted:[]}; + const uncoveredJournal={schema:30,worldGeneration:'test',updatedAt:110,sessionId:'pending',seq:4,_storageKey:'journal:pending',metas:[{id:'B1',rev:11}],states:[],deleted:[]}; + test=await runCase({mirror:currentDb,stored:currentDb,journals:[coveredJournal,uncoveredJournal],coverage:new Map([['covered',3],['pending',2]])}); + assert(test.result.metas.B1&&!test.result.metas.B2,'Recovery coverage did not merge only the uncovered session journal'); + assert(test.removedKeys.includes('journal:covered')&&!test.removedKeys.includes('journal:pending'),'Covered journal cleanup removed the wrong per-session journal'); + assert(test.context.recoveryJournalsToCover.length===1&&test.context.recoveryJournalsToCover[0].sessionId==='pending','Uncovered journal was not retained for the next coverage commit'); + let upgrade=await runUpgradeCase(2);assert(upgrade.openedVersion===8&&['metas','states','global','recovery','tombstones','outbox'].every(name=>upgrade.deletedStores.includes(name))&&upgrade.worldWrites.some(row=>row.status==='active'),'Existing database did not delete retired stores and initialize a current-only world'); + upgrade=await runUpgradeCase(0);assert(upgrade.openedVersion===8&&upgrade.worldWrites.some(row=>row.status==='active'),'Fresh current-only world was not initialized'); + const outboxContext={normalizeCloudPending:value=>({metaIds:[...new Set(value?.metaIds||[])],stateIds:[...new Set(value?.stateIds||[])],deleted:[...new Set(value?.deleted||[])],globalChanged:value?.globalChanged===true})}; + vm.createContext(outboxContext);vm.runInContext(`${functionSource('cloudPendingFromOutboxRows')}\nthis.readOutbox=cloudPendingFromOutboxRows;`,outboxContext); + const pending=outboxContext.readOutbox([{type:'meta',id:'B1'},{type:'deleted',id:'B2'},{type:'global'}]); + assert(pending.metaIds[0]==='B1'&&pending.deleted[0]==='B2'&&pending.globalChanged,'Startup outbox rows were not integrated into pending cloud work'); + const journalPrefix='journal:',journalValues=new Map([ + ['journal',JSON.stringify({schema:30,worldGeneration:'world',updatedAt:3,sessionId:'retired-single-key',seq:1})], + [`${journalPrefix}one`,JSON.stringify({schema:30,worldGeneration:'world',updatedAt:1,sessionId:'one',seq:2})], + [`${journalPrefix}two`,JSON.stringify({schema:30,worldGeneration:'world',updatedAt:2,sessionId:'two',seq:3})] + ]),journalContext={ + recoveryJournalKey:'journal',recoveryJournalPrefix:journalPrefix,LOCAL_MIRROR_MAX_BYTES:10000,SAVE_SCHEMA:30,WORLD_GENERATION:'world', + safeLocalKeys:prefix=>[...journalValues.keys()].filter(key=>key.startsWith(prefix)),safeLocalGet:key=>journalValues.get(key)||null + }; + vm.createContext(journalContext);vm.runInContext(`${functionSource('readRecoveryJournals')}\nthis.readRecoveryJournals=readRecoveryJournals;`,journalContext); + const discovered=journalContext.readRecoveryJournals(); + assert(discovered.map(journal=>journal.sessionId).join(',')==='one,two'&&discovered.every(journal=>journal._storageKey),'Current per-session recovery journals were not discovered and ordered'); + console.log('Storage recovery integration passed'); +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/store-ui-browser-test.js b/test/store-ui-browser-test.js new file mode 100644 index 0000000..9b260df --- /dev/null +++ b/test/store-ui-browser-test.js @@ -0,0 +1,119 @@ +'use strict'; + +const http=require('http'); +const path=require('path'); +const {spawn}=require('child_process'); +const {chromium}=require('playwright'); +const {assert,root}=require('./helpers/app-source'); + +const port=61000+Math.floor(Math.random()*1000); +const url=`http://127.0.0.1:${port}/`; +const edgePath=process.env.BEND_FIELD_BROWSER_PATH||process.env.BEND_FIELD_EDGE_PATH||'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe'; +const sleep=milliseconds=>new Promise(resolve=>setTimeout(resolve,milliseconds)); +const server=spawn(process.execPath,['server.js'],{cwd:root,env:{...process.env,HOST:'127.0.0.1',PORT:String(port)},stdio:'ignore'}); +let browser=null; + +async function waitForServer(){ + for(let attempt=0;attempt<80;attempt++){ + const ready=await new Promise(resolve=>{ + const request=http.get(url,response=>{response.resume();resolve(response.statusCode===200)}); + request.on('error',()=>resolve(false));request.setTimeout(250,()=>{request.destroy();resolve(false)}); + }); + if(ready)return; + await sleep(100); + } + throw new Error('Local game server did not start'); +} + +(async()=>{ + try{ + await waitForServer(); + browser=await chromium.launch({executablePath:edgePath,headless:true}); + const page=await browser.newPage({viewport:{width:1000,height:900}}); + const browserErrors=[];page.on('pageerror',error=>browserErrors.push(String(error?.stack||error)));page.on('console',message=>{if(message.type()==='error')browserErrors.push(message.text())}); + await page.goto(url,{waitUntil:'domcontentloaded'}); + try{await page.waitForFunction(()=>document.body?.dataset?.ready==='true')} + catch(error){console.error('Store UI startup diagnostics:',JSON.stringify({errors:browserErrors,state:await page.evaluate(()=>({ready:document.body?.dataset?.ready||null,status:document.querySelector('#statusMessage')?.textContent||'',text:document.body?.innerText?.slice(0,500)||''}))}));throw error} + await page.evaluate(async()=>{await document.fonts.load('16px DotGothic16Local','日本語盤面');await document.fonts.ready}); + const result=await page.evaluate(async()=>{ + const meta=data.metas.B0,st=metaState('B0'),itemIds=seededStoreItemIds(meta.seed); + st.store={owner:'UI TEST',pathIndex:0,cellIndex:0,itemIds,purchases:[],priceVersion:STORE_PRICE_VERSION,priceCoefficient:1}; + data.score=1e9;openStoreMeta(meta); + const itemSection=document.querySelector('.store-items-section'),cursorSection=document.querySelector('.store-cursors-section'), + cursorCards=[...cursorSection.querySelectorAll('.store-item')],itemList=itemSection.querySelector('.store-section-list'), + cursorList=cursorSection.querySelector('.store-section-list'),flag=FLAG_CURSOR_ITEMS.find(item=>item.id==='cursor-flag-jp'); + const itemColumns=getComputedStyle(itemList).gridTemplateColumns.split(' ').length,cursorColumns=getComputedStyle(cursorList).gridTemplateColumns.split(' ').length, + cursorVertical=cursorCards.every(card=>card.querySelector('.store-item-icon').getBoundingClientRect().bottom<=card.querySelector('.store-buy').getBoundingClientRect().top+1); + const pricesBefore=[...document.querySelectorAll('.store-buy:not(:disabled)')].map(button=>button.textContent); + data.score=0;renderStorePanel(); + const pricesAfter=[...document.querySelectorAll('.store-buy')].map(button=>button.textContent); + const storeResult={ + headings:[...document.querySelectorAll('.store-section-title')].map(node=>node.textContent), + items:itemSection.querySelectorAll('.store-item').length,cursors:cursorCards.length, + cursorHasCopy:cursorCards.some(card=>card.querySelector('h4,p,strong,.store-item-copy')), + cursorIcons:cursorCards.map(card=>Boolean(card.querySelector('.store-item-icon')?.textContent||card.querySelector('.store-item-icon img')?.getAttribute('src'))), + itemColumns,cursorColumns,cursorVertical, + pricesBefore,pricesAfter,actualPrices:pricesAfter.every(text=>/[\d,]+$/.test(text)&&!text.includes('あと')&&!text.endsWith('個')) + }; + st.store.purchases.push({id:flag.id,buyer:'UI TEST',boughtAt:1,paidCost:3000,usedAt:0},{id:'score-lens',buyer:'UI TEST',boughtAt:2,paidCost:200000,usedAt:0});inventoryCache=null;applyCursorStyle('default'); + closeStore(false);renderInventoryPanel(); + const inventoryOptions=[...document.querySelectorAll('.inventory-cursor-option')],inventoryHasCopy=Boolean(document.querySelector('.inventory-cursors-section p,.inventory-cursors-section strong,.inventory-cursors-section .store-item-copy')); + const persistentSave=save;save=async()=>true; + await useInventoryItemLoaded(flag.id);const selectedOnce=data.cursorStyle; + await useInventoryItemLoaded(flag.id);const selectedTwice=data.cursorStyle; + const lensButton=[...document.querySelectorAll('.inventory-item h3')].find(node=>node.textContent.startsWith('ジェムレンズ'))?.closest('.inventory-item')?.querySelector('.inventory-use'), + lensInitial=lensButton?.textContent; + await useInventoryItemLoaded('score-lens');const lensOn=data.scoreLensEnabled,lensOnText=[...document.querySelectorAll('.inventory-item h3')].find(node=>node.textContent.startsWith('ジェムレンズ'))?.closest('.inventory-item')?.querySelector('.inventory-use')?.textContent; + await useInventoryItemLoaded('score-lens');const lensOff=data.scoreLensEnabled,lensOffText=[...document.querySelectorAll('.inventory-item h3')].find(node=>node.textContent.startsWith('ジェムレンズ'))?.closest('.inventory-item')?.querySelector('.inventory-use')?.textContent; + save=persistentSave; + applyCursorStyle(flag.cursorStyle); + viewport.dispatchEvent(new PointerEvent('pointermove',{bubbles:true,clientX:300,clientY:300,pointerType:'mouse'})); + await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))); + applyCursorStyle(flag.cursorStyle); + const board=rendered.get('B0'),dragCache=buildDragCache(board,0,false,1);updateDragCursorDesign(dragCache,[50,50]); + const dragDesign={image:dragCache.cursorImage.getAttribute('href'),display:getComputedStyle(dragCache.cursorImage).display,clip:dragCache.cursorImage.getAttribute('clip-path')}; + clearDragRender(board); + const gemAnimationStarted=playGemCollectionAnimation(board,1000),gemParticles=document.querySelectorAll('.gem-particle').length;document.querySelectorAll('.gem-particle').forEach(node=>node.remove()); + const normalDisplay=getComputedStyle(customEmojiCursor).display;document.body.classList.add('is-drawing');const drawingDisplay=getComputedStyle(customEmojiCursor).display;document.body.classList.remove('is-drawing'); + const firstNumber=board.numberNodes.values().next().value,firstWarning=board.numberWarningNodes.values().next().value; + return{ + flags:FLAG_CURSOR_ITEMS.length,fontReady:document.fonts.check('16px DotGothic16Local'),fontFaces:[...document.fonts].map(face=>({family:face.family,status:face.status,weight:face.weight})), + bodyFont:getComputedStyle(document.body).fontFamily,buttonFont:getComputedStyle(document.querySelector('button')).fontFamily,numberFont:getComputedStyle(firstNumber).fontFamily, + faceMin:Math.min(...YELLOW_FACE_CURSOR_ITEMS.map(item=>item.cost)),faceMax:Math.max(...YELLOW_FACE_CURSOR_ITEMS.map(item=>item.cost)), + flagBase:FLAG_CURSOR_ITEMS.find(item=>item.id==='cursor-flag-af').cost,oecdBase:flag.cost,shopChance:STORE_CHANCE, + meta:document.querySelector('#storeMeta').textContent, + ...storeResult,inventoryOptions:inventoryOptions.length,inventoryHasCopy,selectedOnce,selectedTwice,lensInitial,lensOn,lensOnText,lensOff,lensOffText, + customText:customEmojiCursor.textContent,customImage:customEmojiCursor.querySelector('img')?.getAttribute('src')||'',customVisible:customEmojiCursor.classList.contains('visible'), + customOpacity:getComputedStyle(customEmojiCursor).opacity,customFont:getComputedStyle(customEmojiCursor).fontFamily, + customWidth:getComputedStyle(customEmojiCursor).width,customRadius:getComputedStyle(customEmojiCursor).borderRadius,customFlagFit:getComputedStyle(customEmojiCursor.querySelector('img')).objectFit, + normalDisplay,drawingDisplay,dragDesign,gemAnimationStarted,gemParticles,warningInFront:Boolean(firstNumber.compareDocumentPosition(firstWarning)&Node.DOCUMENT_POSITION_FOLLOWING), + gemLabel:document.querySelector('.stat.score').textContent.trim() + }; + }); + if(!result.fontReady)console.log('Store UI diagnostics:',JSON.stringify(result)); + assert(result.flags===262,'Flag cursor catalog is incomplete'); + assert(result.fontReady&&/DotGothic16Local/.test(result.bodyFont)&&/DotGothic16Local/.test(result.buttonFont)&&/DotGothic16Local/.test(result.numberFont),'Bundled Japanese dot font did not load or was overridden'); + assert(result.faceMin===500&&result.faceMax===50000&&result.flagBase===10000&&result.oecdBase===20000&&Math.abs(result.shopChance-1/30)<1e-12,'Cursor prices or the 1/30 shop chance are incorrect'); + assert(result.meta==='店主:UI TEST'&&!result.meta.includes('価格は固定'),'Fixed-price shop copy remains'); + assert(result.headings.join('|')==='アイテム|カーソル','Shop sections are not separated'); + assert(result.items===2&&result.cursors===12,'Shop does not render its 2+12 inventory'); + assert(!result.cursorHasCopy&&result.cursorIcons.every(Boolean)&&result.actualPrices,'Cursor cards still expose names/descriptions, lack designs, or do not show actual prices'); + assert(result.itemColumns===1&&result.cursorColumns===6&&result.cursorVertical,'Shop cursor designs are not horizontally arranged above their purchase buttons'); + assert(result.inventoryOptions===1&&!result.inventoryHasCopy&&result.selectedOnce==='flag-jp'&&result.selectedTwice==='default',`Inventory cursor grid is not persistent or click-to-toggle: ${JSON.stringify({inventoryOptions:result.inventoryOptions,inventoryHasCopy:result.inventoryHasCopy,selectedOnce:result.selectedOnce,selectedTwice:result.selectedTwice})}`); + assert(result.lensInitial==='OFF'&&result.lensOn===true&&result.lensOnText==='ON'&&result.lensOff===false&&result.lensOffText==='OFF',`Score lens is not an ON/OFF inventory toggle: ${JSON.stringify({lensInitial:result.lensInitial,lensOn:result.lensOn,lensOnText:result.lensOnText,lensOff:result.lensOff,lensOffText:result.lensOffText})}`); + assert(!result.customText&&result.customImage.endsWith('assets/flags/1f1ef-1f1f5.svg')&&result.customVisible&&result.customOpacity==='0.76'&&result.customWidth==='14px'&&result.customRadius==='50%'&&result.customFlagFit==='cover','Japanese flag cursor is not a translucent circular knob-shaped SVG overlay'); + assert(result.normalDisplay==='grid'&&result.drawingDisplay==='none'&&result.dragDesign.display!=='none'&&result.dragDesign.image.endsWith('assets/flags/1f1ef-1f1f5.svg')&&/^url\(#/.test(result.dragDesign.clip),`The custom cursor does not transfer to the grabbed knob: ${JSON.stringify({normalDisplay:result.normalDisplay,drawingDisplay:result.drawingDisplay,dragDesign:result.dragDesign})}`); + assert(result.gemAnimationStarted&&result.gemParticles>=4,'Purple gem collection particles did not spawn from the board'); + assert(result.warningInFront&&!result.gemLabel.includes('ジェム')&&result.gemLabel.startsWith('◆'),'The 曲がる warning layer or standalone currency label is incorrect'); + if(process.env.BEND_FIELD_UI_SCREENSHOT){ + const base=path.resolve(process.env.BEND_FIELD_UI_SCREENSHOT); + await page.evaluate(()=>openStoreMeta(data.metas.B0));await page.screenshot({path:`${base}-store.png`}); + await page.evaluate(()=>{closeStore(false);closeHelp(false,true);const flag=FLAG_CURSOR_ITEMS.find(item=>item.id==='cursor-flag-jp');applyCursorStyle(flag.cursorStyle);viewport.dispatchEvent(new PointerEvent('pointermove',{bubbles:true,clientX:500,clientY:450,pointerType:'mouse'}))}); + await page.screenshot({path:`${base}-cursor.png`}); + } + console.log(`Store UI browser test passed: ${result.flags} flags, ${result.items}+${result.cursors} entries, ${result.cursorColumns} cursor columns`); + }finally{ + if(browser)await browser.close(); + server.kill(); + } +})().catch(error=>{console.error(error);process.exitCode=1}); diff --git a/test/v4771-ui-input-smoke-test.js b/test/v4771-ui-input-smoke-test.js new file mode 100644 index 0000000..fd10d61 --- /dev/null +++ b/test/v4771-ui-input-smoke-test.js @@ -0,0 +1,13 @@ +'use strict'; +const fs=require('fs'),path=require('path'); +const {assert,functionSource,app,css,html}=require('./helpers/app-source'); +const root=path.resolve(__dirname,'..'),catalog=JSON.parse(fs.readFileSync(path.join(root,'store-catalog.json'),'utf8')); +assert(functionSource('positionBoardLabel').includes('viewportRect')&&functionSource('applyCamera').includes('repositionActiveBoardHud'),'Board HUD is not repositioned against viewport bounds'); +assert(css.includes('.fps-stat{position:fixed')&&css.includes('left:calc(6px')&&css.includes('bottom:calc(5px')&&functionSource('refreshFpsCounter').includes('FPS 待機'),'FPS display is not bottom-left or idle-aware'); +assert(functionSource('pointerEventSamples').includes('coalesced[coalesced.length-1]')&&functionSource('appendBoardPointerSamples').includes('setBoardPointerSample')&&functionSource('setBoardPointerSample').includes('b.pendingPointerMove=sample')&&functionSource('renderDragFrame').includes('renderGeometryRevision'),'Knob dragging still processes stale samples or recomputes static geometry every frame'); +assert(functionSource('makeBoard').includes("class:'board-input-surface'")&&css.includes('.board-input-surface{fill:transparent;cursor:crosshair;pointer-events:fill}')&&css.includes('.board-svg{cursor:crosshair;pointer-events:none}'),'Board input remains active across overlapping transparent SVG padding'); +assert(css.includes('#customEmojiCursor.flag-cursor{width:32px;height:32px')&&css.includes('body[data-cursor-mode="dom"] *{cursor:none!important}')&&functionSource('updateCustomCursorFromPointer').includes("dataset.cursorMode==='dom'"),'Custom cursor size or UI-wide DOM fallback is missing'); +assert(catalog.find(item=>item.id==='score-lens')?.cost===200000&&app.includes("id:'score-lens',name:'ジェムレンズ',cost:200000"),'Gem lens base price is not 200000'); +assert(app.includes('TIME_ATTACK_MINUTES=Object.freeze([3,5,10])')&&html.includes('data-time-minutes="3"')&&html.includes('data-time-minutes="5"')&&html.includes('data-time-minutes="10"'),'Time attack durations are not 3, 5, and 10 minutes'); +assert(css.includes('.time-attack-panel{width:min(760px')&&html.includes('📋 結果をコピー')&&functionSource('timeAttackResultText').includes('⏱️'),'Time attack UI/result decoration is incomplete'); +console.log('v47.71 HUD, input, cursor, economy, and time-attack regression test passed'); diff --git a/test/v4772-ownership-reaction-name-smoke-test.js b/test/v4772-ownership-reaction-name-smoke-test.js new file mode 100644 index 0000000..d395747 --- /dev/null +++ b/test/v4772-ownership-reaction-name-smoke-test.js @@ -0,0 +1,29 @@ +'use strict'; +const {assert,functionSource,app,css,html,read,loadBendPuzzle,root,fs,path}=require('./helpers/app-source'); +assert(app.includes("const APP_VERSION='47.77'")&&html.includes('v47.77'),'Version was not advanced to v47.77'); +assert(functionSource('makeBoard').includes('board-input-clip-')&&functionSource('renderBoardNow').includes("'clip-path':`url(#${b.inputClipId})`") ,'Endpoint hit regions are not clipped to their owning board'); +assert(functionSource('sharedBoundaryColorIndex').includes('boundaryColorSource')&&functionSource('renderBoardNow').includes('sharedBoundaryColorIndex(meta,i)'),'Facing gate colors are not unified'); +assert(functionSource('renderBoardNow').includes('else renderDragFrame(b)'),'Stationary drag cursor is not restored after a full board redraw'); +assert(functionSource('bindBoard').includes('b.drawing=null;b.armedGate=null')&&!functionSource('bindBoard').includes('b.drawing.pointerId=e.pointerId'),'Released drawings can still be resumed from unrelated cells'); +assert(functionSource('radialReactionIndex').includes('bestDistance')&&functionSource('beginReactionGesture').includes('setPointerCapture')&&app.includes("window.addEventListener('pointermove',moveReactionGesture,true)"),'Radial hold-drag-release selection is incomplete'); +assert(html.includes('id="settingsBtn"')&&html.includes('id="settingsPlayerName"')&&html.includes('id="lightweightRenderingToggle"')&&html.includes('id="soundEnabledToggle"')&&functionSource('saveSettings').includes('/api/cloud/profile')&&functionSource('init').includes('createAutomaticPlayerName'),'Settings-based player naming, rendering, sound, or automatic initial name is missing'); +assert(functionSource('soundTone').includes('if(!uiSettings.soundEnabled)return')&&functionSource('applyUiSettings').includes("classList.toggle('lightweight-rendering'"),'Settings are not applied to sound and rendering'); +assert(functionSource('renderBoardNow').includes('st.solvedBy!==LEGACY_LOCAL_SOLVER'),'Legacy 「あなた」 solver labels are still displayed'); +assert(css.includes('.solver-badge strong{display:block;max-width:100%;white-space:nowrap')&&css.includes('text-overflow:ellipsis'),'Solver names can still wrap repeatedly'); +assert(functionSource('positionBoardLabel').includes("b.label.style.width=innerWidth+'px'"),'North/south board HUD does not extend to the board edge'); +assert(css.includes('#customEmojiCursor.flag-cursor img{position:absolute;left:50%;top:50%')&&css.includes('transform:translate(-50%,-50%)'),'Flag cursor is not centered'); +assert(css.includes('.fps-stat{position:fixed')&&css.includes('background:none')&&css.includes('font-size:8px'),'FPS display is not subtle and background-free'); +assert(html.includes('長くつながって太くなった線ほど高得点です。'),'Help does not explain long/thick-line scoring'); +assert(functionSource('rebuildMinimapWorld').includes('drawMapBoardCells')&&!functionSource('drawMapBoardCells').includes('stroke('),'Minimap board outlines are still drawn'); +assert(functionSource('rebuildWorldOverviewCache').includes('drawMapBoardCells')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines')&&!app.includes('function drawWorldOverviewShops('),'Zoomed-out rendering does not match the minimap'); +assert(functionSource('maybeGrantGenerationFailureBonus').includes('GENERATION_FAILURE_MIN_MS')&&functionSource('maybeGrantGenerationFailureBonus').includes('unresolvedExpansionCandidates(meta).length'),'Generation failure bonus is not limited to long unresolved expansion attempts'); +const server=read('server.js');assert(server.includes("row.state.expanded===true")&&server.includes("Expansion already succeeded")&&server.includes('GENERATION_FAILURE_BONUS = 2500'),'Generation bonus server guard or level-6-equivalent amount is missing'); +const internal=read('docs/internal-system.md');assert(internal.includes('Do not add changelog')&&internal.includes('update the relevant current specification in place'),'Documentation policy does not prohibit update-history documents'); +for(const name of fs.readdirSync(path.join(root,'docs')))assert(!/(changelog|release|history|update|v\d+|phase\d+)/i.test(name),`Historical update document remains: ${name}`); +const BendPuzzle=loadBendPuzzle();let hasLevel10Region=false,hasLevel6Region=false; +for(let y=-100;y<=100;y++)for(let x=-100;x<=100;x++){const level=BendPuzzle.macroDifficulty(x,y);if(level>=6)hasLevel6Region=true;if(level===10)hasLevel10Region=true} +assert(hasLevel6Region&&hasLevel10Region,'World difficulty map still lacks level 6+ or level 10 regions'); +const high=BendPuzzle.generatePuzzle([[0,0],[1,0],[0,1]],987654,6,12,-9); +assert(high.difficulty===6&&high.complexity.rawRating>=6,'Level-6 regional generation is still discarded or misclassified'); +assert(functionSource('normalizeStoredPuzzle').includes('solverDifficulty(puzzle,targetLevel)'),'Stored high-level boards lose their regional level after reload'); +console.log('v47.77 settings, map rendering, generation bonus, docs policy, and high-level generation regression test passed'); diff --git a/test/v4774-drag-overview-smoke-test.js b/test/v4774-drag-overview-smoke-test.js new file mode 100644 index 0000000..52f02f9 --- /dev/null +++ b/test/v4774-drag-overview-smoke-test.js @@ -0,0 +1,11 @@ +'use strict'; +const {assert,functionSource,app,css}=require('./helpers/app-source'); +assert(functionSource('boardPlayHudVisible').includes('hudBoardId===b?.id')&&functionSource('selectBoard').includes('setActiveBoard(b.id)'),'Board HUD does not persist on the selected board'); +assert(functionSource('rebuildWorldOverviewCache').includes('{showLevels:true}')&&functionSource('drawMapBoardCells').includes('showLevels&&!solved')&&functionSource('rebuildMinimapWorld').includes('drawMapBoardCells(base,visibleMetas,mapX,mapY,scale)'),'Overview levels are missing or leaked into the minimap'); +assert(functionSource('directionFromDelta').includes("preferredAxis!=='V'")&&!functionSource('directionFromDelta').includes('largestrealtime.indexOf('setTimeout'),'Raw pointer events must not perform realtime world conversion or send during drawing'); +const custom=functionSource('updateCustomCursorFromPointer'); +assert(custom.includes("dataset.cursorMode==='dom'")&&custom.includes('requestAnimationFrame')&&!custom.includes('FRAME_INTERVAL'),'DOM cursor fallback must remain visible during drag and commit at display cadence'); +assert(functionSource('syncCursorAppearance').includes('nativeSupported')&&css.includes('data-cursor-mode="native"'),'Static cursor skins must prefer the native cursor path'); +const drag=functionSource('extendPointerTo'),render=functionSource('renderDragFrame'); +assert(drag.includes('lastModelProbeKey')&&drag.includes('probeStep=6'),'Drag model work must be quantized instead of repeated for every raw move'); +assert(render.includes('blended=false')&&render.includes('lastRenderedTip')&&render.includes('setSvgAttr'),'Live drag rendering must avoid gradients and redundant SVG writes'); +assert(app.includes("const APP_VERSION='47.77'"),'Application version was not advanced'); +console.log('v47.77 HUD, warp cleanup, cursor, and drag performance regression test passed'); diff --git a/test/v4778-interaction-scheduler-smoke-test.js b/test/v4778-interaction-scheduler-smoke-test.js new file mode 100644 index 0000000..7f70a80 --- /dev/null +++ b/test/v4778-interaction-scheduler-smoke-test.js @@ -0,0 +1,144 @@ +'use strict'; +const {assert,functionSource,app,css,vm}=require('./helpers/app-source'); +const fs=require('fs'),path=require('path'),browserBenchmark=fs.readFileSync(path.join(__dirname,'browser-performance-benchmark.js'),'utf8'); + +assert(app.includes('AUXILIARY_FPS=30')&&app.includes('DRAG_TARGET_FPS=60')&&app.includes('DRAG_FRAME_INTERVAL=1000/DRAG_TARGET_FPS')&&app.includes('CAMERA_DISPLAY_WATCHDOG_MS=18'),'Interaction and auxiliary frame budgets are not separated'); +assert(app.includes('BEND_INTERACTION_SCHEDULER')&&app.includes('bend-field-interaction-scheduler-variant')&&app.includes('batteryDischargingTime'),'Scheduler rollout variant or battery telemetry guardrail is missing'); +assert(app.includes('DRAG_MAX_CATCHUP_CELLS=24')&&functionSource('extendPointerTo').includes('catchupPending')&&functionSource('processBoardDragFrame').includes('catchupPending'),'Large pointer jumps are not bounded and resumed across drag frames'); + +const cursor=functionSource('updateCustomCursorFromPointer'),cursorSync=functionSource('syncCursorAppearance'); +assert(cursor.includes('requestAnimationFrame')&&!cursor.includes('FRAME_INTERVAL'),'DOM cursor fallback is not synchronized to every display frame'); +assert(app.includes("addEventListener('pointermove',updateCustomCursorFromPointer")&&app.includes("addEventListener('pointerrawupdate',updateCustomCursorFromPointer"),'DOM cursor does not tolerate sparse raw-input delivery'); +assert(cursor.includes('customCursorActiveUntil=perfNow()+40')&&cursor.includes('customCursorFrame=requestAnimationFrame(step)')&&cursor.includes('Math.exp(-elapsed/8)'),'DOM cursor does not keep and smooth its display-rAF loop between active pointer events'); +assert(!cursor.includes("classList.contains('is-drawing')")&&!css.includes('body.is-drawing #customEmojiCursor'),'Custom cursor is hidden during pickup dragging'); +assert(cursorSync.includes('nativeSupported')&&cursorSync.includes('--active-native-cursor')&&css.includes('data-cursor-mode="native"'),'Native cursor assets or the DOM fallback switch are missing'); + +const cursorClasses=new Set(),cursorFrames=[],cursorRafQueue=[],cursorTransforms=[],cursorPerfSamples=new Map(); +let cursorClock=0,cursorRafId=0; +const cursorContext={ + document:{body:{dataset:{cursorMode:'dom'}}}, + customEmojiCursor:{classList:{contains:name=>cursorClasses.has(name),add:name=>cursorClasses.add(name),remove:name=>cursorClasses.delete(name)},style:{set transform(value){cursorTransforms.push(value)}}}, + perfNow:()=>cursorClock, + requestAnimationFrame:callback=>{cursorRafQueue.push(callback);return++cursorRafId}, + markVisualFrame:timestamp=>cursorFrames.push(timestamp), + perfObserve:(name,value)=>{const samples=cursorPerfSamples.get(name)||[];samples.push(value);cursorPerfSamples.set(name,samples);return value}, + perfCount:()=>{} +}; +vm.createContext(cursorContext); +vm.runInContext(`const interactionCommitAt=Object.create(null),interactionInputAt=Object.create(null);${functionSource('recordInteractionCommit')}let customCursorFrame=0,customCursorX=0,customCursorY=0,customCursorInputAt=0,customCursorActiveUntil=0,customCursorRenderedX=NaN,customCursorRenderedY=NaN,customCursorLastFrameAt=0,customCursorInputRevision=0,customCursorCommittedRevision=0;${cursor};this.updateCustomCursorFromPointer=updateCustomCursorFromPointer;`,cursorContext); +let cursorEventAt=0; +for(let frameAt=1000/60;frameAt<=1100;frameAt+=1000/60){ + while(cursorEventAt<=1000&&cursorEventAt<=frameAt+.001){ + cursorClock=cursorEventAt;cursorContext.updateCustomCursorFromPointer({clientX:cursorEventAt,clientY:cursorEventAt/2,timeStamp:cursorEventAt||.001,pointerType:'mouse',target:{isConnected:true}}); + cursorEventAt+=1000/30; + } + cursorClock=frameAt; + for(const callback of cursorRafQueue.splice(0))callback(frameAt); +} +const cursorFrameGaps=cursorFrames.slice(1).map((timestamp,index)=>timestamp-cursorFrames[index]).sort((a,b)=>a-b), + cursorMedianGap=cursorFrameGaps[Math.floor((cursorFrameGaps.length-1)*.5)], + sortedCursorAges=[...(cursorPerfSamples.get('cursorInputAge')||[])].sort((a,b)=>a-b),cursorP95Age=sortedCursorAges[Math.floor((sortedCursorAges.length-1)*.95)], + measuredCursorGaps=[...(cursorPerfSamples.get('cursorFrameGap')||[])].sort((a,b)=>a-b),measuredCursorMedianGap=measuredCursorGaps[Math.floor((measuredCursorGaps.length-1)*.5)], + changedCursorTransforms=cursorTransforms.filter((value,index)=>index===0||value!==cursorTransforms[index-1]).length; +assert(cursorFrames.length>=60&&cursorMedianGap<=20,`Warm DOM cursor loop produced ${cursorFrames.length} frames with a ${cursorMedianGap} ms median gap`); +assert(measuredCursorGaps.length>=30&&measuredCursorMedianGap<=20,`Production cursor instrumentation captured ${measuredCursorGaps.length} gaps with a ${measuredCursorMedianGap} ms median`); +assert(changedCursorTransforms>=55,`Warm DOM cursor loop changed its transform on only ${changedCursorTransforms} display frames`); +assert(sortedCursorAges.length>=25,`Production cursor instrumentation captured only ${sortedCursorAges.length} fresh-input latency samples`); +assert(cursorP95Age<25,`Warm DOM cursor loop produced ${cursorP95Age} ms p95 input age`); + +const cameraQueue=functionSource('queueCameraInteraction'),cameraApply=functionSource('applyCamera'); +assert(cameraQueue.includes('pendingCameraInteraction=next')&&cameraQueue.includes('requestAnimationFrame')&&cameraQueue.includes('CAMERA_DISPLAY_WATCHDOG_MS')&&cameraQueue.includes('cancelAnimationFrame')&&cameraQueue.includes('cameraInteractionLastDraw+DRAG_FRAME_INTERVAL'),'Camera does not use a latest-input 60 Hz queue with a missed-vsync watchdog'); +assert(cameraApply.includes('positionCachedWorldOverview')&&functionSource('positionCachedWorldOverview').includes('translate3d'),'Overview position is not updated in the camera fast path'); +assert(functionSource('scheduleWorldOverview').includes('requestIdleCallback')&&functionSource('scheduleWorldOverview').includes("classList.contains('is-interacting')"),'Overview rebuilds are not idle and interaction-safe'); + +const dragSchedule=functionSource('scheduleBoardDragFrame'),dragFrame=functionSource('processBoardDragFrame'); +assert(dragSchedule.includes('requestAnimationFrame')&&dragSchedule.includes('DRAG_DISPLAY_WATCHDOG_MS')&&dragSchedule.includes('cancelAnimationFrame')&&dragSchedule.includes('clearTimeout')&&dragSchedule.includes('b.dragVisualLastFrameAt+DRAG_FRAME_INTERVAL')&&dragSchedule.includes('INTERACTION_FRAME_TOLERANCE_MS')&&dragFrame.includes('DRAG_FRAME_INTERVAL'),'Pickup visuals or logic are not capped to their 60 Hz lane'); +assert(functionSource('queueBoardPointerMove').includes('appendBoardPointerSamples')&&functionSource('queueBoardPointerMove').includes('commitBoardDragFromInputDeadline')&&functionSource('queueBoardPointerMove').includes('scheduleBoardDragFrame')&&!functionSource('queueBoardPointerMove').includes('updatePickupHandleOverlay')&&!functionSource('queueBoardPointerMove').includes('markVisualFrame')&&!functionSource('queueBoardPointerMove').includes('extendPointerTo'),'Pointer events mutate presentation or model work outside the capped scheduler'); +assert(functionSource('commitBoardDragFromInputDeadline').includes('b.dragVisualLastFrameAt+DRAG_FRAME_INTERVAL')&&functionSource('commitBoardDragFromInputDeadline').includes('INTERACTION_FRAME_TOLERANCE_MS')&&functionSource('commitBoardDragFromInputDeadline').includes('processBoardDragFrame(b,now)'),'Pickup input deadline fallback is not governed by the 60 Hz presentation ceiling'); +assert(!app.includes('DRAG_INPUT_RESCUE_MS')&&!app.includes('function rescuePickupVisualFromInput('),'An input-rate pickup presentation path can still exceed 60 Hz'); +assert(functionSource('setBoardPointerSample').includes('dragVisualActiveUntil=perfNow()+40')&&dragFrame.includes('Math.exp(-visualElapsed/8)')&&dragFrame.includes('updateDrawingHandlePosition')&&dragFrame.includes('perfNow()=32'),'Complex pickup handles are not compositor-driven or the SVG live tail is repainted every display frame'); +assert(dragFrame.includes('updatePickupHandleOverlay')&&dragFrame.includes('usesLightweightDragOverlay')&&dragFrame.includes('dirtyBoards.delete(b)')&&app.includes('LIGHTWEIGHT_DRAG_BOARD_CELLS=120')&&functionSource('updatePickupHandleOverlay').includes('translate3d')&&css.includes('#pickupHandleOverlay{position:fixed'),'Large-board pickup still repaints the SVG during the gesture instead of using the lightweight display-rate handle'); +assert(functionSource('activateBoardPointerDrag').includes('setPickupScenePresentation(b,true)')&&functionSource('setLightweightDragPresentation').includes("style.opacity=next?'0':''")&&functionSource('setPickupScenePresentation').includes('setLightweightDragPresentation(board,next&&board===activeBoard)')&&functionSource('scheduleInteractionSettlePresentation').includes('setPickupScenePresentation(null,false)'),'Active large-board decorative layers are not compositor-suppressed during pickup and restored after settlement'); +assert(dragFrame.includes("recordInteractionCommit('pickupVisual'")&&dragFrame.includes('workDuration'),'Pickup cadence, input age, or callback work is not instrumented'); +assert(dragFrame.includes('timestamp<=b.dragVisualActiveUntil')&&dragFrame.includes("recordInteractionCommit('pickupVisual',timestamp,move.inputAt,false)")&&css.includes('transition:transform 16.67ms linear'),'Pickup sample gaps are not compositor-interpolated or warm visual ticks are not measured'); +assert(dragFrame.includes('freshVisualInput')&&dragFrame.includes('freshLogicalInput'),'Pickup latency instrumentation resamples stale input on warm display frames'); +assert(app.includes('DRAG_MAX_LOGICAL_SAMPLES_PER_FRAME=4')&&app.includes('DRAG_MODEL_BUDGET_MS=.5')&&dragFrame.includes('b.pointerMoveSamples.shift()')&&dragFrame.includes('perfNow()-modelStartedgateConnectEffect'),'Large-board gate decoration still mutates the SVG inside the finishing interaction task'); + +assert(functionSource('runDeferredSave').includes("classList?.contains('is-interacting')"),'Ordinary persistence is not deferred during interactions'); +assert(functionSource('applyWorldSignal').includes('await waitForInteractionSettle()')&&functionSource('pullCloudWorld').includes('await waitForInteractionSettle()'),'Cross-tab or cloud reconciliation can still run broad refresh work during a gesture'); +assert(functionSource('createPuzzleWorker').includes("perfCount('workerResultsDeferredDuringInteraction')")&&functionSource('createPuzzleWorker').includes('waitForInteractionSettle().then(deliver)'),'Puzzle-worker promise continuations can still run during an active gesture'); +assert(functionSource('desiredInteractiveBoardIds').includes('for(const id of ids)')&&!app.includes('INTERACTIVE_DETAIL_CELL_BUDGET')&&!functionSource('ensureBoards').includes('makeStaticBoard(meta)'),'Visible puzzles can still be hidden behind a clicked-only summary LOD'); +assert(functionSource('observeInteractionFrame').includes('workDuration>slowWorkThreshold')&&!functionSource('observeInteractionFrame').includes('elapsed>24'),'Quality fallback still mistakes frame spacing for callback overload'); +assert(functionSource('refreshInteractionState').includes('interactionQualityDowngradePending'),'Quality changes are not deferred until the gesture ends'); +assert(functionSource('refreshInteractionState').includes("topbar?.classList.toggle('drawing-active'")&&functionSource('refreshInteractionState').includes("world?.classList.toggle('camera-interacting'")&&functionSource('refreshInteractionState').includes('!usesLightweightDragOverlay(drawingBoard)')&&!css.includes('body.is-interacting .gate-dot')&&!css.includes('body.is-drawing #topbar'),'Pickup styling still invalidates the entire document or a lightweight large-board subtree'); +assert(functionSource('recordInteractionCommit').includes('InputAge')&&functionSource('observeInteractionFrame').includes('interactionDroppedFrameRatio')&&app.includes("perfCount('longTasks')"),'Interaction latency, dropped-frame, or long-task diagnostics are missing'); +assert(functionSource('ensureBoards').includes("classList?.contains?.('is-interacting')")&&functionSource('ensureBoards').includes('lodPassesDeferredDuringInteraction'),'Detailed and overview LOD work is not deferred during gestures'); + +assert(functionSource('drawingForPath').includes('pathCellIndex:new Map')&&functionSource('extendOne').includes('pathCellIndex?.set'),'Long pickup paths do not retain a cell index'); +assert(functionSource('rewindActivePathToCell').includes('index===path.cells.length-1)return false'),'A pickup hovering on its current tip is incorrectly classified as a geometry-changing rewind'); +assert(functionSource('makeBoard').includes('gateIndexesByCell')&&functionSource('gateCandidatesInCell').includes('gateIndexesByCell'),'Gate lookup is not indexed by cell'); +assert(functionSource('makeBoard').includes('endpointIndexesByCell')&&functionSource('nearestEndpointAtPoint').includes('endpointCandidatesNearPoint'),'Open endpoint lookup is not indexed by cell'); +assert(app.includes('REALTIME_CURSOR_INTERVAL=50')&&functionSource('queueRealtimeCursor').includes('vx:')&&functionSource('applyRemotePlayer').includes('lead=.035'),'Remote cursors are not sent at 20 Hz with velocity-based short extrapolation'); +for(const probe of['measureDisplayCadence','measureCursorCadence','claimLatencyProbe','pickupEdgePanProbe','pinchZoomProbe','panCadenceProbe']) + assert(browserBenchmark.includes(`function ${probe}`)||browserBenchmark.includes(`function ${probe}(`),`Browser performance matrix is missing ${probe}`); +assert(browserBenchmark.includes('--force-device-scale-factor=1'),'Browser performance matrix must normalize device scale for stable display-cadence measurements'); +const pickupCadenceProbeSource=browserBenchmark.slice(browserBenchmark.indexOf('async function pickupCadenceProbe'),browserBenchmark.indexOf('async function claimLatencyProbe')); +assert(pickupCadenceProbeSource.includes("deepClone(metaState('B0'))")&&pickupCadenceProbeSource.includes('data.states.B0=')&&pickupCadenceProbeSource.includes("persistNow({skipCloud:true})")&&pickupCadenceProbeSource.includes("snapshot:await client.evaluate('BEND_PERF.snapshot()')"),'Continuous pickup benchmark does not capture clean cadence data and restore its original board state'); +assert(browserBenchmark.includes('async function restoreOriginState')&&browserBenchmark.includes('replaceObjectContents(current,deepClone(snapshot))')&&browserBenchmark.includes('setTimeout(resolve,80)')&&browserBenchmark.includes("applySnapshot();changed('B0')")&&browserBenchmark.includes("centerMeta(data.metas.B0,{select:false})")&&browserBenchmark.match(/restoreOriginState\(client,pristineOriginState\)/g)?.length>=4,'Browser matrix does not settle, revision-stamp, and canonically reset origin state and camera between pickup probes'); +assert(browserBenchmark.includes('pickup probes did not restore a pristine origin board')&&browserBenchmark.includes("metaState('B0').solved===true"),'Browser matrix does not verify scenario isolation and solve completion before panning'); +assert(browserBenchmark.includes("spawnSync(taskkill,['/pid',String(child.pid),'/T','/F']")&&browserBenchmark.includes('stopBrowserTree(edge)')&&browserBenchmark.includes('fs.rmSync(temporaryRoot,{recursive:true,force:true})'),'Browser benchmark does not guarantee process-tree and temporary-profile cleanup'); +for(const profile of['small','medium','large','complex','long-line'])assert(browserBenchmark.includes(`name:'${profile}'`),`Browser benchmark profile ${profile} is missing`); +const longCells=Array.from({length:250},(_,index)=>[0,index]),longPath={startGate:0,endGate:null,openGate:null,cells:longCells.map(cell=>[...cell])}, + longState={paths:[longPath]},longOccupancy=new Map(longCells.map(cell=>[`${cell[0]},${cell[1]}`,0])),longDrawing={pathIndex:0,pointerId:1,pathCellIndex:new Map(longCells.map((cell,index)=>[`${cell[0]},${cell[1]}`,index]))}, + longBoard={id:'L',p:{g:Array.from({length:100},()=>[0,0,'N'])},drawing:longDrawing,crossingKeySet:new Set()},longContext={ + activePath:()=>longPath,ckey:(r,c)=>`${r},${c}`,warpPairForCell:()=>null,isWarpTransition:()=>false,pathIndexesAtCell:()=>[],metaState:()=>longState, + applyCalls:0,applyBoardCommand:(_board,mutate)=>{longContext.applyCalls++;mutate();return true} + }; +vm.createContext(longContext);vm.runInContext(`${functionSource('rewindActivePathToCell')}\nthis.rewindActivePathToCell=rewindActivePathToCell;`,longContext); +assert(longContext.rewindActivePathToCell(longBoard,[0,10],longOccupancy)&&longPath.cells.length===11&&longDrawing.pathCellIndex.size===11&&longOccupancy.size===11&&longContext.applyCalls===1,'A 250-cell pickup does not rewind with one suffix splice'); + +const traversalContext={PAD:0,CELL:1,ckey:(r,c)=>`${r},${c}`};vm.createContext(traversalContext); +vm.runInContext(`${functionSource('cellsCrossedBySegment')}\nthis.cellsCrossedBySegment=cellsCrossedBySegment;`,traversalContext); +const validLongCells=new Set(longCells.map(([r,c])=>`${r},${c}`)),forward=traversalContext.cellsCrossedBySegment([.5,.5],[249.5,.5],{preferredAxis:'H',validCells:validLongCells}); +assert(forward.length===249&&forward[248][1]===249,'A 250-cell pointer segment does not preserve every forward grid cell'); +let remainingCatchup=forward.length,batches=0,maxBatch=0;while(remainingCatchup){const batch=Math.min(24,remainingCatchup);maxBatch=Math.max(maxBatch,batch);remainingCatchup-=batch;batches++} +assert(maxBatch===24&&batches===11,'A 250-cell catch-up is not bounded to constant-size frame batches'); + +const endpointState={paths:Array.from({length:100},(_,index)=>({endGate:null,detachedStart:index%2===0,cells:[[0,index],[1,index]]}))}, + endpointContext={Map,ckey:(r,c)=>`${r},${c}`,metaState:()=>endpointState}; +vm.createContext(endpointContext);vm.runInContext(`${functionSource('rebuildEndpointIndexes')}\nthis.rebuildEndpointIndexes=rebuildEndpointIndexes;`,endpointContext); +const endpointBoard={id:'E',endpointIndexesByCell:new Map()},endpointIndex=endpointContext.rebuildEndpointIndexes(endpointBoard); +assert(endpointIndex.size===150&&endpointIndex.get('1,99')[0].index===99,'The 100-path endpoint index is incomplete'); + +const remoteContext={Number,Map,realtimePresenceId:null,remotePlayers:new Map(),schedulePresenceRender:()=>{},scheduleMinimap:()=>{}}; +vm.createContext(remoteContext);vm.runInContext(`${functionSource('applyRemotePlayer')}\nthis.applyRemotePlayer=applyRemotePlayer;`,remoteContext); +assert(remoteContext.applyRemotePlayer({presenceId:'R',x:10,y:20,vx:2,vy:-1,sentAt:123,name:'R'}),'Remote cursor sample was rejected'); +const remoteSample=remoteContext.remotePlayers.get('R'); +assert(Math.abs(remoteSample.targetX-10.07)<1e-9&&Math.abs(remoteSample.targetY-19.965)<1e-9&&remoteSample.sentAt===123,'Remote cursor velocity extrapolation is incorrect'); + +for(const refreshRate of[60,120,144]){ + const displayInterval=1000/refreshRate,visualCommits=[],logicalCommits=[];let nextVisual=0,nextLogical=0; + for(let timestamp=0;timestamp<1000;timestamp+=displayInterval){ + if(!nextVisual||timestamp+.01>=nextVisual){visualCommits.push(timestamp);do nextVisual=(nextVisual||timestamp)+1000/60;while(nextVisual<=timestamp+.01)} + if(!nextLogical||timestamp+2>=nextLogical){logicalCommits.push(timestamp);do nextLogical=(nextLogical||timestamp)+1000/60;while(nextLogical<=timestamp+2)} + } + assert(visualCommits.length>=59&&visualCommits.length<=61,`${refreshRate} Hz display produced ${visualCommits.length} pickup presentation frames instead of about 60`); + assert(logicalCommits.length>=59&&logicalCommits.length<=61,`${refreshRate} Hz display does not retain a 60 Hz pickup logic lane`); +} + +console.log('Display-rate cursor/pan and 60 Hz pickup scheduler regression test passed');