Spaces:
Running
Running
Orbit Chase Deploy commited on
Commit ·
1b2ff95
1
Parent(s): c06f99f
Deploy Orbit Chase 2026-08-18T01:46:19Z
Browse files- README.md +1 -2
- assets/index-DRZiR9In.js +0 -0
- assets/index-DVflwj4H.css +1 -0
- index.html +2 -1
- {public/models → models}/linear-sarsa-20260815-030554-ep8000.json +0 -0
- package-lock.json +0 -1197
- package.json +0 -22
- src/debugPanel.ts +0 -41
- src/game/PlayScene.ts +0 -174
- src/game/SarsaPlayerController.ts +0 -327
- src/game/arena.ts +0 -58
- src/game/audio.ts +0 -32
- src/game/simulation.ts +0 -252
- src/main.ts +0 -18
- src/styles.css +0 -18
- tsconfig.json +0 -14
README.md
CHANGED
|
@@ -4,8 +4,7 @@ emoji: 🎮
|
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: purple
|
| 6 |
sdk: static
|
| 7 |
-
|
| 8 |
-
app_file: dist/index.html
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
---
|
|
|
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: purple
|
| 6 |
sdk: static
|
| 7 |
+
app_file: index.html
|
|
|
|
| 8 |
pinned: false
|
| 9 |
license: mit
|
| 10 |
---
|
assets/index-DRZiR9In.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/index-DVflwj4H.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
:root{color:#e8f5ff;background:#071426;font-family:Inter,ui-sans-serif,system-ui,sans-serif}*{box-sizing:border-box}body{min-width:320px;min-height:100vh;margin:0;display:grid;place-items:center}main{width:min(100vw,1180px);padding:16px}.app-shell{display:flex;align-items:flex-start;justify-content:center;gap:14px}.game-column{width:min(100%,800px)}.game-toolbar{display:flex;justify-content:flex-end;gap:8px;margin:0 0 8px}.game-toolbar button{border:1px solid #447b9b;border-radius:7px;background:#173a55;color:#e8f5ff;cursor:pointer;font:inherit;font-size:14px;padding:7px 10px}.game-toolbar button:hover{background:#214d6d}.game-toolbar button:disabled{opacity:.55;cursor:default}#game{overflow:hidden;border:1px solid #2d607e;border-radius:14px;box-shadow:0 20px 70px #0008}canvas{display:block;width:100%!important;height:auto!important}#debug-panel{width:300px;max-height:640px;overflow:auto;border:1px solid #2d607e;border-radius:14px;background:#0a1c2d;box-shadow:0 20px 70px #0008;padding:16px}.debug-heading{display:flex;align-items:baseline;justify-content:space-between;gap:10px;border-bottom:1px solid #28546f;padding-bottom:10px}.debug-heading h2{color:#91efff;font-size:17px;margin:0}.debug-heading span{color:#8daaba;font-size:11px;white-space:nowrap}#debug-content{color:#d9efff;font:12px/1.6 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;margin:13px 0 0;white-space:pre-wrap}@media(max-width:1080px){.app-shell{align-items:stretch;flex-direction:column}.game-column{width:100%}#debug-panel{width:100%;max-height:340px}}
|
index.html
CHANGED
|
@@ -5,6 +5,8 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<meta name="theme-color" content="#071426" />
|
| 7 |
<title>Orbit Chase</title>
|
|
|
|
|
|
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
<main class="app-shell">
|
|
@@ -23,6 +25,5 @@
|
|
| 23 |
<pre id="debug-content">Waiting for game state…</pre>
|
| 24 |
</aside>
|
| 25 |
</main>
|
| 26 |
-
<script type="module" src="/src/main.ts"></script>
|
| 27 |
</body>
|
| 28 |
</html>
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<meta name="theme-color" content="#071426" />
|
| 7 |
<title>Orbit Chase</title>
|
| 8 |
+
<script type="module" crossorigin src="/assets/index-DRZiR9In.js"></script>
|
| 9 |
+
<link rel="stylesheet" crossorigin href="/assets/index-DVflwj4H.css">
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<main class="app-shell">
|
|
|
|
| 25 |
<pre id="debug-content">Waiting for game state…</pre>
|
| 26 |
</aside>
|
| 27 |
</main>
|
|
|
|
| 28 |
</body>
|
| 29 |
</html>
|
{public/models → models}/linear-sarsa-20260815-030554-ep8000.json
RENAMED
|
File without changes
|
package-lock.json
DELETED
|
@@ -1,1197 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "orbit-chase",
|
| 3 |
-
"version": "0.1.0",
|
| 4 |
-
"lockfileVersion": 3,
|
| 5 |
-
"requires": true,
|
| 6 |
-
"packages": {
|
| 7 |
-
"": {
|
| 8 |
-
"name": "orbit-chase",
|
| 9 |
-
"version": "0.1.0",
|
| 10 |
-
"dependencies": {
|
| 11 |
-
"phaser": "3.90.0"
|
| 12 |
-
},
|
| 13 |
-
"devDependencies": {
|
| 14 |
-
"typescript": "~5.7.2",
|
| 15 |
-
"vite": "~6.3.1"
|
| 16 |
-
}
|
| 17 |
-
},
|
| 18 |
-
"node_modules/@esbuild/aix-ppc64": {
|
| 19 |
-
"version": "0.25.12",
|
| 20 |
-
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
| 21 |
-
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
| 22 |
-
"cpu": [
|
| 23 |
-
"ppc64"
|
| 24 |
-
],
|
| 25 |
-
"dev": true,
|
| 26 |
-
"license": "MIT",
|
| 27 |
-
"optional": true,
|
| 28 |
-
"os": [
|
| 29 |
-
"aix"
|
| 30 |
-
],
|
| 31 |
-
"engines": {
|
| 32 |
-
"node": ">=18"
|
| 33 |
-
}
|
| 34 |
-
},
|
| 35 |
-
"node_modules/@esbuild/android-arm": {
|
| 36 |
-
"version": "0.25.12",
|
| 37 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
|
| 38 |
-
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
| 39 |
-
"cpu": [
|
| 40 |
-
"arm"
|
| 41 |
-
],
|
| 42 |
-
"dev": true,
|
| 43 |
-
"license": "MIT",
|
| 44 |
-
"optional": true,
|
| 45 |
-
"os": [
|
| 46 |
-
"android"
|
| 47 |
-
],
|
| 48 |
-
"engines": {
|
| 49 |
-
"node": ">=18"
|
| 50 |
-
}
|
| 51 |
-
},
|
| 52 |
-
"node_modules/@esbuild/android-arm64": {
|
| 53 |
-
"version": "0.25.12",
|
| 54 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
|
| 55 |
-
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
| 56 |
-
"cpu": [
|
| 57 |
-
"arm64"
|
| 58 |
-
],
|
| 59 |
-
"dev": true,
|
| 60 |
-
"license": "MIT",
|
| 61 |
-
"optional": true,
|
| 62 |
-
"os": [
|
| 63 |
-
"android"
|
| 64 |
-
],
|
| 65 |
-
"engines": {
|
| 66 |
-
"node": ">=18"
|
| 67 |
-
}
|
| 68 |
-
},
|
| 69 |
-
"node_modules/@esbuild/android-x64": {
|
| 70 |
-
"version": "0.25.12",
|
| 71 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
|
| 72 |
-
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
| 73 |
-
"cpu": [
|
| 74 |
-
"x64"
|
| 75 |
-
],
|
| 76 |
-
"dev": true,
|
| 77 |
-
"license": "MIT",
|
| 78 |
-
"optional": true,
|
| 79 |
-
"os": [
|
| 80 |
-
"android"
|
| 81 |
-
],
|
| 82 |
-
"engines": {
|
| 83 |
-
"node": ">=18"
|
| 84 |
-
}
|
| 85 |
-
},
|
| 86 |
-
"node_modules/@esbuild/darwin-arm64": {
|
| 87 |
-
"version": "0.25.12",
|
| 88 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
|
| 89 |
-
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
| 90 |
-
"cpu": [
|
| 91 |
-
"arm64"
|
| 92 |
-
],
|
| 93 |
-
"dev": true,
|
| 94 |
-
"license": "MIT",
|
| 95 |
-
"optional": true,
|
| 96 |
-
"os": [
|
| 97 |
-
"darwin"
|
| 98 |
-
],
|
| 99 |
-
"engines": {
|
| 100 |
-
"node": ">=18"
|
| 101 |
-
}
|
| 102 |
-
},
|
| 103 |
-
"node_modules/@esbuild/darwin-x64": {
|
| 104 |
-
"version": "0.25.12",
|
| 105 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
|
| 106 |
-
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
| 107 |
-
"cpu": [
|
| 108 |
-
"x64"
|
| 109 |
-
],
|
| 110 |
-
"dev": true,
|
| 111 |
-
"license": "MIT",
|
| 112 |
-
"optional": true,
|
| 113 |
-
"os": [
|
| 114 |
-
"darwin"
|
| 115 |
-
],
|
| 116 |
-
"engines": {
|
| 117 |
-
"node": ">=18"
|
| 118 |
-
}
|
| 119 |
-
},
|
| 120 |
-
"node_modules/@esbuild/freebsd-arm64": {
|
| 121 |
-
"version": "0.25.12",
|
| 122 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
|
| 123 |
-
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
| 124 |
-
"cpu": [
|
| 125 |
-
"arm64"
|
| 126 |
-
],
|
| 127 |
-
"dev": true,
|
| 128 |
-
"license": "MIT",
|
| 129 |
-
"optional": true,
|
| 130 |
-
"os": [
|
| 131 |
-
"freebsd"
|
| 132 |
-
],
|
| 133 |
-
"engines": {
|
| 134 |
-
"node": ">=18"
|
| 135 |
-
}
|
| 136 |
-
},
|
| 137 |
-
"node_modules/@esbuild/freebsd-x64": {
|
| 138 |
-
"version": "0.25.12",
|
| 139 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
|
| 140 |
-
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
| 141 |
-
"cpu": [
|
| 142 |
-
"x64"
|
| 143 |
-
],
|
| 144 |
-
"dev": true,
|
| 145 |
-
"license": "MIT",
|
| 146 |
-
"optional": true,
|
| 147 |
-
"os": [
|
| 148 |
-
"freebsd"
|
| 149 |
-
],
|
| 150 |
-
"engines": {
|
| 151 |
-
"node": ">=18"
|
| 152 |
-
}
|
| 153 |
-
},
|
| 154 |
-
"node_modules/@esbuild/linux-arm": {
|
| 155 |
-
"version": "0.25.12",
|
| 156 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
|
| 157 |
-
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
| 158 |
-
"cpu": [
|
| 159 |
-
"arm"
|
| 160 |
-
],
|
| 161 |
-
"dev": true,
|
| 162 |
-
"license": "MIT",
|
| 163 |
-
"optional": true,
|
| 164 |
-
"os": [
|
| 165 |
-
"linux"
|
| 166 |
-
],
|
| 167 |
-
"engines": {
|
| 168 |
-
"node": ">=18"
|
| 169 |
-
}
|
| 170 |
-
},
|
| 171 |
-
"node_modules/@esbuild/linux-arm64": {
|
| 172 |
-
"version": "0.25.12",
|
| 173 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
|
| 174 |
-
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
| 175 |
-
"cpu": [
|
| 176 |
-
"arm64"
|
| 177 |
-
],
|
| 178 |
-
"dev": true,
|
| 179 |
-
"license": "MIT",
|
| 180 |
-
"optional": true,
|
| 181 |
-
"os": [
|
| 182 |
-
"linux"
|
| 183 |
-
],
|
| 184 |
-
"engines": {
|
| 185 |
-
"node": ">=18"
|
| 186 |
-
}
|
| 187 |
-
},
|
| 188 |
-
"node_modules/@esbuild/linux-ia32": {
|
| 189 |
-
"version": "0.25.12",
|
| 190 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
|
| 191 |
-
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
| 192 |
-
"cpu": [
|
| 193 |
-
"ia32"
|
| 194 |
-
],
|
| 195 |
-
"dev": true,
|
| 196 |
-
"license": "MIT",
|
| 197 |
-
"optional": true,
|
| 198 |
-
"os": [
|
| 199 |
-
"linux"
|
| 200 |
-
],
|
| 201 |
-
"engines": {
|
| 202 |
-
"node": ">=18"
|
| 203 |
-
}
|
| 204 |
-
},
|
| 205 |
-
"node_modules/@esbuild/linux-loong64": {
|
| 206 |
-
"version": "0.25.12",
|
| 207 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
|
| 208 |
-
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
| 209 |
-
"cpu": [
|
| 210 |
-
"loong64"
|
| 211 |
-
],
|
| 212 |
-
"dev": true,
|
| 213 |
-
"license": "MIT",
|
| 214 |
-
"optional": true,
|
| 215 |
-
"os": [
|
| 216 |
-
"linux"
|
| 217 |
-
],
|
| 218 |
-
"engines": {
|
| 219 |
-
"node": ">=18"
|
| 220 |
-
}
|
| 221 |
-
},
|
| 222 |
-
"node_modules/@esbuild/linux-mips64el": {
|
| 223 |
-
"version": "0.25.12",
|
| 224 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
|
| 225 |
-
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
| 226 |
-
"cpu": [
|
| 227 |
-
"mips64el"
|
| 228 |
-
],
|
| 229 |
-
"dev": true,
|
| 230 |
-
"license": "MIT",
|
| 231 |
-
"optional": true,
|
| 232 |
-
"os": [
|
| 233 |
-
"linux"
|
| 234 |
-
],
|
| 235 |
-
"engines": {
|
| 236 |
-
"node": ">=18"
|
| 237 |
-
}
|
| 238 |
-
},
|
| 239 |
-
"node_modules/@esbuild/linux-ppc64": {
|
| 240 |
-
"version": "0.25.12",
|
| 241 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
|
| 242 |
-
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
| 243 |
-
"cpu": [
|
| 244 |
-
"ppc64"
|
| 245 |
-
],
|
| 246 |
-
"dev": true,
|
| 247 |
-
"license": "MIT",
|
| 248 |
-
"optional": true,
|
| 249 |
-
"os": [
|
| 250 |
-
"linux"
|
| 251 |
-
],
|
| 252 |
-
"engines": {
|
| 253 |
-
"node": ">=18"
|
| 254 |
-
}
|
| 255 |
-
},
|
| 256 |
-
"node_modules/@esbuild/linux-riscv64": {
|
| 257 |
-
"version": "0.25.12",
|
| 258 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
|
| 259 |
-
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
| 260 |
-
"cpu": [
|
| 261 |
-
"riscv64"
|
| 262 |
-
],
|
| 263 |
-
"dev": true,
|
| 264 |
-
"license": "MIT",
|
| 265 |
-
"optional": true,
|
| 266 |
-
"os": [
|
| 267 |
-
"linux"
|
| 268 |
-
],
|
| 269 |
-
"engines": {
|
| 270 |
-
"node": ">=18"
|
| 271 |
-
}
|
| 272 |
-
},
|
| 273 |
-
"node_modules/@esbuild/linux-s390x": {
|
| 274 |
-
"version": "0.25.12",
|
| 275 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
|
| 276 |
-
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
| 277 |
-
"cpu": [
|
| 278 |
-
"s390x"
|
| 279 |
-
],
|
| 280 |
-
"dev": true,
|
| 281 |
-
"license": "MIT",
|
| 282 |
-
"optional": true,
|
| 283 |
-
"os": [
|
| 284 |
-
"linux"
|
| 285 |
-
],
|
| 286 |
-
"engines": {
|
| 287 |
-
"node": ">=18"
|
| 288 |
-
}
|
| 289 |
-
},
|
| 290 |
-
"node_modules/@esbuild/linux-x64": {
|
| 291 |
-
"version": "0.25.12",
|
| 292 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
|
| 293 |
-
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
| 294 |
-
"cpu": [
|
| 295 |
-
"x64"
|
| 296 |
-
],
|
| 297 |
-
"dev": true,
|
| 298 |
-
"license": "MIT",
|
| 299 |
-
"optional": true,
|
| 300 |
-
"os": [
|
| 301 |
-
"linux"
|
| 302 |
-
],
|
| 303 |
-
"engines": {
|
| 304 |
-
"node": ">=18"
|
| 305 |
-
}
|
| 306 |
-
},
|
| 307 |
-
"node_modules/@esbuild/netbsd-arm64": {
|
| 308 |
-
"version": "0.25.12",
|
| 309 |
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
|
| 310 |
-
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
| 311 |
-
"cpu": [
|
| 312 |
-
"arm64"
|
| 313 |
-
],
|
| 314 |
-
"dev": true,
|
| 315 |
-
"license": "MIT",
|
| 316 |
-
"optional": true,
|
| 317 |
-
"os": [
|
| 318 |
-
"netbsd"
|
| 319 |
-
],
|
| 320 |
-
"engines": {
|
| 321 |
-
"node": ">=18"
|
| 322 |
-
}
|
| 323 |
-
},
|
| 324 |
-
"node_modules/@esbuild/netbsd-x64": {
|
| 325 |
-
"version": "0.25.12",
|
| 326 |
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
|
| 327 |
-
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
| 328 |
-
"cpu": [
|
| 329 |
-
"x64"
|
| 330 |
-
],
|
| 331 |
-
"dev": true,
|
| 332 |
-
"license": "MIT",
|
| 333 |
-
"optional": true,
|
| 334 |
-
"os": [
|
| 335 |
-
"netbsd"
|
| 336 |
-
],
|
| 337 |
-
"engines": {
|
| 338 |
-
"node": ">=18"
|
| 339 |
-
}
|
| 340 |
-
},
|
| 341 |
-
"node_modules/@esbuild/openbsd-arm64": {
|
| 342 |
-
"version": "0.25.12",
|
| 343 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
|
| 344 |
-
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
| 345 |
-
"cpu": [
|
| 346 |
-
"arm64"
|
| 347 |
-
],
|
| 348 |
-
"dev": true,
|
| 349 |
-
"license": "MIT",
|
| 350 |
-
"optional": true,
|
| 351 |
-
"os": [
|
| 352 |
-
"openbsd"
|
| 353 |
-
],
|
| 354 |
-
"engines": {
|
| 355 |
-
"node": ">=18"
|
| 356 |
-
}
|
| 357 |
-
},
|
| 358 |
-
"node_modules/@esbuild/openbsd-x64": {
|
| 359 |
-
"version": "0.25.12",
|
| 360 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
|
| 361 |
-
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
| 362 |
-
"cpu": [
|
| 363 |
-
"x64"
|
| 364 |
-
],
|
| 365 |
-
"dev": true,
|
| 366 |
-
"license": "MIT",
|
| 367 |
-
"optional": true,
|
| 368 |
-
"os": [
|
| 369 |
-
"openbsd"
|
| 370 |
-
],
|
| 371 |
-
"engines": {
|
| 372 |
-
"node": ">=18"
|
| 373 |
-
}
|
| 374 |
-
},
|
| 375 |
-
"node_modules/@esbuild/openharmony-arm64": {
|
| 376 |
-
"version": "0.25.12",
|
| 377 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
|
| 378 |
-
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
| 379 |
-
"cpu": [
|
| 380 |
-
"arm64"
|
| 381 |
-
],
|
| 382 |
-
"dev": true,
|
| 383 |
-
"license": "MIT",
|
| 384 |
-
"optional": true,
|
| 385 |
-
"os": [
|
| 386 |
-
"openharmony"
|
| 387 |
-
],
|
| 388 |
-
"engines": {
|
| 389 |
-
"node": ">=18"
|
| 390 |
-
}
|
| 391 |
-
},
|
| 392 |
-
"node_modules/@esbuild/sunos-x64": {
|
| 393 |
-
"version": "0.25.12",
|
| 394 |
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
|
| 395 |
-
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
| 396 |
-
"cpu": [
|
| 397 |
-
"x64"
|
| 398 |
-
],
|
| 399 |
-
"dev": true,
|
| 400 |
-
"license": "MIT",
|
| 401 |
-
"optional": true,
|
| 402 |
-
"os": [
|
| 403 |
-
"sunos"
|
| 404 |
-
],
|
| 405 |
-
"engines": {
|
| 406 |
-
"node": ">=18"
|
| 407 |
-
}
|
| 408 |
-
},
|
| 409 |
-
"node_modules/@esbuild/win32-arm64": {
|
| 410 |
-
"version": "0.25.12",
|
| 411 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
|
| 412 |
-
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
| 413 |
-
"cpu": [
|
| 414 |
-
"arm64"
|
| 415 |
-
],
|
| 416 |
-
"dev": true,
|
| 417 |
-
"license": "MIT",
|
| 418 |
-
"optional": true,
|
| 419 |
-
"os": [
|
| 420 |
-
"win32"
|
| 421 |
-
],
|
| 422 |
-
"engines": {
|
| 423 |
-
"node": ">=18"
|
| 424 |
-
}
|
| 425 |
-
},
|
| 426 |
-
"node_modules/@esbuild/win32-ia32": {
|
| 427 |
-
"version": "0.25.12",
|
| 428 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
|
| 429 |
-
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
| 430 |
-
"cpu": [
|
| 431 |
-
"ia32"
|
| 432 |
-
],
|
| 433 |
-
"dev": true,
|
| 434 |
-
"license": "MIT",
|
| 435 |
-
"optional": true,
|
| 436 |
-
"os": [
|
| 437 |
-
"win32"
|
| 438 |
-
],
|
| 439 |
-
"engines": {
|
| 440 |
-
"node": ">=18"
|
| 441 |
-
}
|
| 442 |
-
},
|
| 443 |
-
"node_modules/@esbuild/win32-x64": {
|
| 444 |
-
"version": "0.25.12",
|
| 445 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
|
| 446 |
-
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
| 447 |
-
"cpu": [
|
| 448 |
-
"x64"
|
| 449 |
-
],
|
| 450 |
-
"dev": true,
|
| 451 |
-
"license": "MIT",
|
| 452 |
-
"optional": true,
|
| 453 |
-
"os": [
|
| 454 |
-
"win32"
|
| 455 |
-
],
|
| 456 |
-
"engines": {
|
| 457 |
-
"node": ">=18"
|
| 458 |
-
}
|
| 459 |
-
},
|
| 460 |
-
"node_modules/@napi-rs/lzma-linux-x64-gnu": {
|
| 461 |
-
"version": "1.5.1",
|
| 462 |
-
"resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
|
| 463 |
-
"integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
|
| 464 |
-
"cpu": [
|
| 465 |
-
"x64"
|
| 466 |
-
],
|
| 467 |
-
"dev": true,
|
| 468 |
-
"libc": [
|
| 469 |
-
"glibc"
|
| 470 |
-
],
|
| 471 |
-
"license": "MIT",
|
| 472 |
-
"optional": true,
|
| 473 |
-
"os": [
|
| 474 |
-
"linux"
|
| 475 |
-
],
|
| 476 |
-
"engines": {
|
| 477 |
-
"node": "^22.20 || ^24.12 || >=25"
|
| 478 |
-
}
|
| 479 |
-
},
|
| 480 |
-
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 481 |
-
"version": "4.62.4",
|
| 482 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz",
|
| 483 |
-
"integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==",
|
| 484 |
-
"cpu": [
|
| 485 |
-
"arm"
|
| 486 |
-
],
|
| 487 |
-
"dev": true,
|
| 488 |
-
"license": "MIT",
|
| 489 |
-
"optional": true,
|
| 490 |
-
"os": [
|
| 491 |
-
"android"
|
| 492 |
-
]
|
| 493 |
-
},
|
| 494 |
-
"node_modules/@rollup/rollup-android-arm64": {
|
| 495 |
-
"version": "4.62.4",
|
| 496 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz",
|
| 497 |
-
"integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==",
|
| 498 |
-
"cpu": [
|
| 499 |
-
"arm64"
|
| 500 |
-
],
|
| 501 |
-
"dev": true,
|
| 502 |
-
"license": "MIT",
|
| 503 |
-
"optional": true,
|
| 504 |
-
"os": [
|
| 505 |
-
"android"
|
| 506 |
-
]
|
| 507 |
-
},
|
| 508 |
-
"node_modules/@rollup/rollup-darwin-arm64": {
|
| 509 |
-
"version": "4.62.4",
|
| 510 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz",
|
| 511 |
-
"integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==",
|
| 512 |
-
"cpu": [
|
| 513 |
-
"arm64"
|
| 514 |
-
],
|
| 515 |
-
"dev": true,
|
| 516 |
-
"license": "MIT",
|
| 517 |
-
"optional": true,
|
| 518 |
-
"os": [
|
| 519 |
-
"darwin"
|
| 520 |
-
]
|
| 521 |
-
},
|
| 522 |
-
"node_modules/@rollup/rollup-darwin-x64": {
|
| 523 |
-
"version": "4.62.4",
|
| 524 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz",
|
| 525 |
-
"integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==",
|
| 526 |
-
"cpu": [
|
| 527 |
-
"x64"
|
| 528 |
-
],
|
| 529 |
-
"dev": true,
|
| 530 |
-
"license": "MIT",
|
| 531 |
-
"optional": true,
|
| 532 |
-
"os": [
|
| 533 |
-
"darwin"
|
| 534 |
-
]
|
| 535 |
-
},
|
| 536 |
-
"node_modules/@rollup/rollup-freebsd-arm64": {
|
| 537 |
-
"version": "4.62.4",
|
| 538 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz",
|
| 539 |
-
"integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==",
|
| 540 |
-
"cpu": [
|
| 541 |
-
"arm64"
|
| 542 |
-
],
|
| 543 |
-
"dev": true,
|
| 544 |
-
"license": "MIT",
|
| 545 |
-
"optional": true,
|
| 546 |
-
"os": [
|
| 547 |
-
"freebsd"
|
| 548 |
-
]
|
| 549 |
-
},
|
| 550 |
-
"node_modules/@rollup/rollup-freebsd-x64": {
|
| 551 |
-
"version": "4.62.4",
|
| 552 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz",
|
| 553 |
-
"integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==",
|
| 554 |
-
"cpu": [
|
| 555 |
-
"x64"
|
| 556 |
-
],
|
| 557 |
-
"dev": true,
|
| 558 |
-
"license": "MIT",
|
| 559 |
-
"optional": true,
|
| 560 |
-
"os": [
|
| 561 |
-
"freebsd"
|
| 562 |
-
]
|
| 563 |
-
},
|
| 564 |
-
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
| 565 |
-
"version": "4.62.4",
|
| 566 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz",
|
| 567 |
-
"integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==",
|
| 568 |
-
"cpu": [
|
| 569 |
-
"arm"
|
| 570 |
-
],
|
| 571 |
-
"dev": true,
|
| 572 |
-
"libc": [
|
| 573 |
-
"glibc"
|
| 574 |
-
],
|
| 575 |
-
"license": "MIT",
|
| 576 |
-
"optional": true,
|
| 577 |
-
"os": [
|
| 578 |
-
"linux"
|
| 579 |
-
]
|
| 580 |
-
},
|
| 581 |
-
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
| 582 |
-
"version": "4.62.4",
|
| 583 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz",
|
| 584 |
-
"integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==",
|
| 585 |
-
"cpu": [
|
| 586 |
-
"arm"
|
| 587 |
-
],
|
| 588 |
-
"dev": true,
|
| 589 |
-
"libc": [
|
| 590 |
-
"musl"
|
| 591 |
-
],
|
| 592 |
-
"license": "MIT",
|
| 593 |
-
"optional": true,
|
| 594 |
-
"os": [
|
| 595 |
-
"linux"
|
| 596 |
-
]
|
| 597 |
-
},
|
| 598 |
-
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
| 599 |
-
"version": "4.62.4",
|
| 600 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz",
|
| 601 |
-
"integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==",
|
| 602 |
-
"cpu": [
|
| 603 |
-
"arm64"
|
| 604 |
-
],
|
| 605 |
-
"dev": true,
|
| 606 |
-
"libc": [
|
| 607 |
-
"glibc"
|
| 608 |
-
],
|
| 609 |
-
"license": "MIT",
|
| 610 |
-
"optional": true,
|
| 611 |
-
"os": [
|
| 612 |
-
"linux"
|
| 613 |
-
]
|
| 614 |
-
},
|
| 615 |
-
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
| 616 |
-
"version": "4.62.4",
|
| 617 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz",
|
| 618 |
-
"integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==",
|
| 619 |
-
"cpu": [
|
| 620 |
-
"arm64"
|
| 621 |
-
],
|
| 622 |
-
"dev": true,
|
| 623 |
-
"libc": [
|
| 624 |
-
"musl"
|
| 625 |
-
],
|
| 626 |
-
"license": "MIT",
|
| 627 |
-
"optional": true,
|
| 628 |
-
"os": [
|
| 629 |
-
"linux"
|
| 630 |
-
]
|
| 631 |
-
},
|
| 632 |
-
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
| 633 |
-
"version": "4.62.4",
|
| 634 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz",
|
| 635 |
-
"integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==",
|
| 636 |
-
"cpu": [
|
| 637 |
-
"loong64"
|
| 638 |
-
],
|
| 639 |
-
"dev": true,
|
| 640 |
-
"libc": [
|
| 641 |
-
"glibc"
|
| 642 |
-
],
|
| 643 |
-
"license": "MIT",
|
| 644 |
-
"optional": true,
|
| 645 |
-
"os": [
|
| 646 |
-
"linux"
|
| 647 |
-
]
|
| 648 |
-
},
|
| 649 |
-
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
| 650 |
-
"version": "4.62.4",
|
| 651 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz",
|
| 652 |
-
"integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==",
|
| 653 |
-
"cpu": [
|
| 654 |
-
"loong64"
|
| 655 |
-
],
|
| 656 |
-
"dev": true,
|
| 657 |
-
"libc": [
|
| 658 |
-
"musl"
|
| 659 |
-
],
|
| 660 |
-
"license": "MIT",
|
| 661 |
-
"optional": true,
|
| 662 |
-
"os": [
|
| 663 |
-
"linux"
|
| 664 |
-
]
|
| 665 |
-
},
|
| 666 |
-
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
| 667 |
-
"version": "4.62.4",
|
| 668 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz",
|
| 669 |
-
"integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==",
|
| 670 |
-
"cpu": [
|
| 671 |
-
"ppc64"
|
| 672 |
-
],
|
| 673 |
-
"dev": true,
|
| 674 |
-
"libc": [
|
| 675 |
-
"glibc"
|
| 676 |
-
],
|
| 677 |
-
"license": "MIT",
|
| 678 |
-
"optional": true,
|
| 679 |
-
"os": [
|
| 680 |
-
"linux"
|
| 681 |
-
]
|
| 682 |
-
},
|
| 683 |
-
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
| 684 |
-
"version": "4.62.4",
|
| 685 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz",
|
| 686 |
-
"integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==",
|
| 687 |
-
"cpu": [
|
| 688 |
-
"ppc64"
|
| 689 |
-
],
|
| 690 |
-
"dev": true,
|
| 691 |
-
"libc": [
|
| 692 |
-
"musl"
|
| 693 |
-
],
|
| 694 |
-
"license": "MIT",
|
| 695 |
-
"optional": true,
|
| 696 |
-
"os": [
|
| 697 |
-
"linux"
|
| 698 |
-
]
|
| 699 |
-
},
|
| 700 |
-
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
| 701 |
-
"version": "4.62.4",
|
| 702 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz",
|
| 703 |
-
"integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==",
|
| 704 |
-
"cpu": [
|
| 705 |
-
"riscv64"
|
| 706 |
-
],
|
| 707 |
-
"dev": true,
|
| 708 |
-
"libc": [
|
| 709 |
-
"glibc"
|
| 710 |
-
],
|
| 711 |
-
"license": "MIT",
|
| 712 |
-
"optional": true,
|
| 713 |
-
"os": [
|
| 714 |
-
"linux"
|
| 715 |
-
]
|
| 716 |
-
},
|
| 717 |
-
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
| 718 |
-
"version": "4.62.4",
|
| 719 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz",
|
| 720 |
-
"integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==",
|
| 721 |
-
"cpu": [
|
| 722 |
-
"riscv64"
|
| 723 |
-
],
|
| 724 |
-
"dev": true,
|
| 725 |
-
"libc": [
|
| 726 |
-
"musl"
|
| 727 |
-
],
|
| 728 |
-
"license": "MIT",
|
| 729 |
-
"optional": true,
|
| 730 |
-
"os": [
|
| 731 |
-
"linux"
|
| 732 |
-
]
|
| 733 |
-
},
|
| 734 |
-
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
| 735 |
-
"version": "4.62.4",
|
| 736 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz",
|
| 737 |
-
"integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==",
|
| 738 |
-
"cpu": [
|
| 739 |
-
"s390x"
|
| 740 |
-
],
|
| 741 |
-
"dev": true,
|
| 742 |
-
"libc": [
|
| 743 |
-
"glibc"
|
| 744 |
-
],
|
| 745 |
-
"license": "MIT",
|
| 746 |
-
"optional": true,
|
| 747 |
-
"os": [
|
| 748 |
-
"linux"
|
| 749 |
-
]
|
| 750 |
-
},
|
| 751 |
-
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
| 752 |
-
"version": "4.62.4",
|
| 753 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz",
|
| 754 |
-
"integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==",
|
| 755 |
-
"cpu": [
|
| 756 |
-
"x64"
|
| 757 |
-
],
|
| 758 |
-
"dev": true,
|
| 759 |
-
"libc": [
|
| 760 |
-
"glibc"
|
| 761 |
-
],
|
| 762 |
-
"license": "MIT",
|
| 763 |
-
"optional": true,
|
| 764 |
-
"os": [
|
| 765 |
-
"linux"
|
| 766 |
-
]
|
| 767 |
-
},
|
| 768 |
-
"node_modules/@rollup/rollup-linux-x64-musl": {
|
| 769 |
-
"version": "4.62.4",
|
| 770 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz",
|
| 771 |
-
"integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==",
|
| 772 |
-
"cpu": [
|
| 773 |
-
"x64"
|
| 774 |
-
],
|
| 775 |
-
"dev": true,
|
| 776 |
-
"libc": [
|
| 777 |
-
"musl"
|
| 778 |
-
],
|
| 779 |
-
"license": "MIT",
|
| 780 |
-
"optional": true,
|
| 781 |
-
"os": [
|
| 782 |
-
"linux"
|
| 783 |
-
]
|
| 784 |
-
},
|
| 785 |
-
"node_modules/@rollup/rollup-openbsd-x64": {
|
| 786 |
-
"version": "4.62.4",
|
| 787 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz",
|
| 788 |
-
"integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==",
|
| 789 |
-
"cpu": [
|
| 790 |
-
"x64"
|
| 791 |
-
],
|
| 792 |
-
"dev": true,
|
| 793 |
-
"license": "MIT",
|
| 794 |
-
"optional": true,
|
| 795 |
-
"os": [
|
| 796 |
-
"openbsd"
|
| 797 |
-
]
|
| 798 |
-
},
|
| 799 |
-
"node_modules/@rollup/rollup-openharmony-arm64": {
|
| 800 |
-
"version": "4.62.4",
|
| 801 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz",
|
| 802 |
-
"integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==",
|
| 803 |
-
"cpu": [
|
| 804 |
-
"arm64"
|
| 805 |
-
],
|
| 806 |
-
"dev": true,
|
| 807 |
-
"license": "MIT",
|
| 808 |
-
"optional": true,
|
| 809 |
-
"os": [
|
| 810 |
-
"openharmony"
|
| 811 |
-
]
|
| 812 |
-
},
|
| 813 |
-
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
| 814 |
-
"version": "4.62.4",
|
| 815 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz",
|
| 816 |
-
"integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==",
|
| 817 |
-
"cpu": [
|
| 818 |
-
"arm64"
|
| 819 |
-
],
|
| 820 |
-
"dev": true,
|
| 821 |
-
"license": "MIT",
|
| 822 |
-
"optional": true,
|
| 823 |
-
"os": [
|
| 824 |
-
"win32"
|
| 825 |
-
]
|
| 826 |
-
},
|
| 827 |
-
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
| 828 |
-
"version": "4.62.4",
|
| 829 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz",
|
| 830 |
-
"integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==",
|
| 831 |
-
"cpu": [
|
| 832 |
-
"ia32"
|
| 833 |
-
],
|
| 834 |
-
"dev": true,
|
| 835 |
-
"license": "MIT",
|
| 836 |
-
"optional": true,
|
| 837 |
-
"os": [
|
| 838 |
-
"win32"
|
| 839 |
-
]
|
| 840 |
-
},
|
| 841 |
-
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
| 842 |
-
"version": "4.62.4",
|
| 843 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz",
|
| 844 |
-
"integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==",
|
| 845 |
-
"cpu": [
|
| 846 |
-
"x64"
|
| 847 |
-
],
|
| 848 |
-
"dev": true,
|
| 849 |
-
"license": "MIT",
|
| 850 |
-
"optional": true,
|
| 851 |
-
"os": [
|
| 852 |
-
"win32"
|
| 853 |
-
]
|
| 854 |
-
},
|
| 855 |
-
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
| 856 |
-
"version": "4.62.4",
|
| 857 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz",
|
| 858 |
-
"integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==",
|
| 859 |
-
"cpu": [
|
| 860 |
-
"x64"
|
| 861 |
-
],
|
| 862 |
-
"dev": true,
|
| 863 |
-
"license": "MIT",
|
| 864 |
-
"optional": true,
|
| 865 |
-
"os": [
|
| 866 |
-
"win32"
|
| 867 |
-
]
|
| 868 |
-
},
|
| 869 |
-
"node_modules/@types/estree": {
|
| 870 |
-
"version": "1.0.9",
|
| 871 |
-
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
| 872 |
-
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
|
| 873 |
-
"dev": true,
|
| 874 |
-
"license": "MIT"
|
| 875 |
-
},
|
| 876 |
-
"node_modules/esbuild": {
|
| 877 |
-
"version": "0.25.12",
|
| 878 |
-
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
| 879 |
-
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
| 880 |
-
"dev": true,
|
| 881 |
-
"hasInstallScript": true,
|
| 882 |
-
"license": "MIT",
|
| 883 |
-
"bin": {
|
| 884 |
-
"esbuild": "bin/esbuild"
|
| 885 |
-
},
|
| 886 |
-
"engines": {
|
| 887 |
-
"node": ">=18"
|
| 888 |
-
},
|
| 889 |
-
"optionalDependencies": {
|
| 890 |
-
"@esbuild/aix-ppc64": "0.25.12",
|
| 891 |
-
"@esbuild/android-arm": "0.25.12",
|
| 892 |
-
"@esbuild/android-arm64": "0.25.12",
|
| 893 |
-
"@esbuild/android-x64": "0.25.12",
|
| 894 |
-
"@esbuild/darwin-arm64": "0.25.12",
|
| 895 |
-
"@esbuild/darwin-x64": "0.25.12",
|
| 896 |
-
"@esbuild/freebsd-arm64": "0.25.12",
|
| 897 |
-
"@esbuild/freebsd-x64": "0.25.12",
|
| 898 |
-
"@esbuild/linux-arm": "0.25.12",
|
| 899 |
-
"@esbuild/linux-arm64": "0.25.12",
|
| 900 |
-
"@esbuild/linux-ia32": "0.25.12",
|
| 901 |
-
"@esbuild/linux-loong64": "0.25.12",
|
| 902 |
-
"@esbuild/linux-mips64el": "0.25.12",
|
| 903 |
-
"@esbuild/linux-ppc64": "0.25.12",
|
| 904 |
-
"@esbuild/linux-riscv64": "0.25.12",
|
| 905 |
-
"@esbuild/linux-s390x": "0.25.12",
|
| 906 |
-
"@esbuild/linux-x64": "0.25.12",
|
| 907 |
-
"@esbuild/netbsd-arm64": "0.25.12",
|
| 908 |
-
"@esbuild/netbsd-x64": "0.25.12",
|
| 909 |
-
"@esbuild/openbsd-arm64": "0.25.12",
|
| 910 |
-
"@esbuild/openbsd-x64": "0.25.12",
|
| 911 |
-
"@esbuild/openharmony-arm64": "0.25.12",
|
| 912 |
-
"@esbuild/sunos-x64": "0.25.12",
|
| 913 |
-
"@esbuild/win32-arm64": "0.25.12",
|
| 914 |
-
"@esbuild/win32-ia32": "0.25.12",
|
| 915 |
-
"@esbuild/win32-x64": "0.25.12"
|
| 916 |
-
}
|
| 917 |
-
},
|
| 918 |
-
"node_modules/eventemitter3": {
|
| 919 |
-
"version": "5.0.4",
|
| 920 |
-
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
| 921 |
-
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
| 922 |
-
"license": "MIT"
|
| 923 |
-
},
|
| 924 |
-
"node_modules/fdir": {
|
| 925 |
-
"version": "6.5.0",
|
| 926 |
-
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
| 927 |
-
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
| 928 |
-
"dev": true,
|
| 929 |
-
"license": "MIT",
|
| 930 |
-
"engines": {
|
| 931 |
-
"node": ">=12.0.0"
|
| 932 |
-
},
|
| 933 |
-
"peerDependencies": {
|
| 934 |
-
"picomatch": "^3 || ^4"
|
| 935 |
-
},
|
| 936 |
-
"peerDependenciesMeta": {
|
| 937 |
-
"picomatch": {
|
| 938 |
-
"optional": true
|
| 939 |
-
}
|
| 940 |
-
}
|
| 941 |
-
},
|
| 942 |
-
"node_modules/fsevents": {
|
| 943 |
-
"version": "2.3.3",
|
| 944 |
-
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 945 |
-
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 946 |
-
"dev": true,
|
| 947 |
-
"hasInstallScript": true,
|
| 948 |
-
"license": "MIT",
|
| 949 |
-
"optional": true,
|
| 950 |
-
"os": [
|
| 951 |
-
"darwin"
|
| 952 |
-
],
|
| 953 |
-
"engines": {
|
| 954 |
-
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 955 |
-
}
|
| 956 |
-
},
|
| 957 |
-
"node_modules/nanoid": {
|
| 958 |
-
"version": "3.3.18",
|
| 959 |
-
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
| 960 |
-
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
| 961 |
-
"dev": true,
|
| 962 |
-
"funding": [
|
| 963 |
-
{
|
| 964 |
-
"type": "github",
|
| 965 |
-
"url": "https://github.com/sponsors/ai"
|
| 966 |
-
}
|
| 967 |
-
],
|
| 968 |
-
"license": "MIT",
|
| 969 |
-
"bin": {
|
| 970 |
-
"nanoid": "bin/nanoid.cjs"
|
| 971 |
-
},
|
| 972 |
-
"engines": {
|
| 973 |
-
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
| 974 |
-
}
|
| 975 |
-
},
|
| 976 |
-
"node_modules/phaser": {
|
| 977 |
-
"version": "3.90.0",
|
| 978 |
-
"resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz",
|
| 979 |
-
"integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==",
|
| 980 |
-
"license": "MIT",
|
| 981 |
-
"dependencies": {
|
| 982 |
-
"eventemitter3": "^5.0.1"
|
| 983 |
-
}
|
| 984 |
-
},
|
| 985 |
-
"node_modules/picocolors": {
|
| 986 |
-
"version": "1.1.1",
|
| 987 |
-
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
| 988 |
-
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
| 989 |
-
"dev": true,
|
| 990 |
-
"license": "ISC"
|
| 991 |
-
},
|
| 992 |
-
"node_modules/picomatch": {
|
| 993 |
-
"version": "4.0.5",
|
| 994 |
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
| 995 |
-
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
| 996 |
-
"dev": true,
|
| 997 |
-
"license": "MIT",
|
| 998 |
-
"engines": {
|
| 999 |
-
"node": ">=12"
|
| 1000 |
-
},
|
| 1001 |
-
"funding": {
|
| 1002 |
-
"url": "https://github.com/sponsors/jonschlinkert"
|
| 1003 |
-
}
|
| 1004 |
-
},
|
| 1005 |
-
"node_modules/postcss": {
|
| 1006 |
-
"version": "8.5.26",
|
| 1007 |
-
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
|
| 1008 |
-
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
|
| 1009 |
-
"dev": true,
|
| 1010 |
-
"funding": [
|
| 1011 |
-
{
|
| 1012 |
-
"type": "opencollective",
|
| 1013 |
-
"url": "https://opencollective.com/postcss/"
|
| 1014 |
-
},
|
| 1015 |
-
{
|
| 1016 |
-
"type": "tidelift",
|
| 1017 |
-
"url": "https://tidelift.com/funding/github/npm/postcss"
|
| 1018 |
-
},
|
| 1019 |
-
{
|
| 1020 |
-
"type": "github",
|
| 1021 |
-
"url": "https://github.com/sponsors/ai"
|
| 1022 |
-
}
|
| 1023 |
-
],
|
| 1024 |
-
"license": "MIT",
|
| 1025 |
-
"dependencies": {
|
| 1026 |
-
"nanoid": "^3.3.17",
|
| 1027 |
-
"picocolors": "^1.1.1",
|
| 1028 |
-
"source-map-js": "^1.2.1"
|
| 1029 |
-
},
|
| 1030 |
-
"engines": {
|
| 1031 |
-
"node": "^10 || ^12 || >=14"
|
| 1032 |
-
}
|
| 1033 |
-
},
|
| 1034 |
-
"node_modules/rollup": {
|
| 1035 |
-
"version": "4.62.4",
|
| 1036 |
-
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
|
| 1037 |
-
"integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
|
| 1038 |
-
"dev": true,
|
| 1039 |
-
"license": "MIT",
|
| 1040 |
-
"dependencies": {
|
| 1041 |
-
"@types/estree": "1.0.9"
|
| 1042 |
-
},
|
| 1043 |
-
"bin": {
|
| 1044 |
-
"rollup": "dist/bin/rollup"
|
| 1045 |
-
},
|
| 1046 |
-
"engines": {
|
| 1047 |
-
"node": ">=18.0.0",
|
| 1048 |
-
"npm": ">=8.0.0"
|
| 1049 |
-
},
|
| 1050 |
-
"optionalDependencies": {
|
| 1051 |
-
"@napi-rs/lzma-linux-x64-gnu": "1.5.1",
|
| 1052 |
-
"@rollup/rollup-android-arm-eabi": "4.62.4",
|
| 1053 |
-
"@rollup/rollup-android-arm64": "4.62.4",
|
| 1054 |
-
"@rollup/rollup-darwin-arm64": "4.62.4",
|
| 1055 |
-
"@rollup/rollup-darwin-x64": "4.62.4",
|
| 1056 |
-
"@rollup/rollup-freebsd-arm64": "4.62.4",
|
| 1057 |
-
"@rollup/rollup-freebsd-x64": "4.62.4",
|
| 1058 |
-
"@rollup/rollup-linux-arm-gnueabihf": "4.62.4",
|
| 1059 |
-
"@rollup/rollup-linux-arm-musleabihf": "4.62.4",
|
| 1060 |
-
"@rollup/rollup-linux-arm64-gnu": "4.62.4",
|
| 1061 |
-
"@rollup/rollup-linux-arm64-musl": "4.62.4",
|
| 1062 |
-
"@rollup/rollup-linux-loong64-gnu": "4.62.4",
|
| 1063 |
-
"@rollup/rollup-linux-loong64-musl": "4.62.4",
|
| 1064 |
-
"@rollup/rollup-linux-ppc64-gnu": "4.62.4",
|
| 1065 |
-
"@rollup/rollup-linux-ppc64-musl": "4.62.4",
|
| 1066 |
-
"@rollup/rollup-linux-riscv64-gnu": "4.62.4",
|
| 1067 |
-
"@rollup/rollup-linux-riscv64-musl": "4.62.4",
|
| 1068 |
-
"@rollup/rollup-linux-s390x-gnu": "4.62.4",
|
| 1069 |
-
"@rollup/rollup-linux-x64-gnu": "4.62.4",
|
| 1070 |
-
"@rollup/rollup-linux-x64-musl": "4.62.4",
|
| 1071 |
-
"@rollup/rollup-openbsd-x64": "4.62.4",
|
| 1072 |
-
"@rollup/rollup-openharmony-arm64": "4.62.4",
|
| 1073 |
-
"@rollup/rollup-win32-arm64-msvc": "4.62.4",
|
| 1074 |
-
"@rollup/rollup-win32-ia32-msvc": "4.62.4",
|
| 1075 |
-
"@rollup/rollup-win32-x64-gnu": "4.62.4",
|
| 1076 |
-
"@rollup/rollup-win32-x64-msvc": "4.62.4",
|
| 1077 |
-
"fsevents": "~2.3.2"
|
| 1078 |
-
}
|
| 1079 |
-
},
|
| 1080 |
-
"node_modules/source-map-js": {
|
| 1081 |
-
"version": "1.2.1",
|
| 1082 |
-
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
| 1083 |
-
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
| 1084 |
-
"dev": true,
|
| 1085 |
-
"license": "BSD-3-Clause",
|
| 1086 |
-
"engines": {
|
| 1087 |
-
"node": ">=0.10.0"
|
| 1088 |
-
}
|
| 1089 |
-
},
|
| 1090 |
-
"node_modules/tinyglobby": {
|
| 1091 |
-
"version": "0.2.17",
|
| 1092 |
-
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
| 1093 |
-
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
| 1094 |
-
"dev": true,
|
| 1095 |
-
"license": "MIT",
|
| 1096 |
-
"dependencies": {
|
| 1097 |
-
"fdir": "^6.5.0",
|
| 1098 |
-
"picomatch": "^4.0.4"
|
| 1099 |
-
},
|
| 1100 |
-
"engines": {
|
| 1101 |
-
"node": ">=12.0.0"
|
| 1102 |
-
},
|
| 1103 |
-
"funding": {
|
| 1104 |
-
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 1105 |
-
}
|
| 1106 |
-
},
|
| 1107 |
-
"node_modules/typescript": {
|
| 1108 |
-
"version": "5.7.3",
|
| 1109 |
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
| 1110 |
-
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
| 1111 |
-
"dev": true,
|
| 1112 |
-
"license": "Apache-2.0",
|
| 1113 |
-
"bin": {
|
| 1114 |
-
"tsc": "bin/tsc",
|
| 1115 |
-
"tsserver": "bin/tsserver"
|
| 1116 |
-
},
|
| 1117 |
-
"engines": {
|
| 1118 |
-
"node": ">=14.17"
|
| 1119 |
-
}
|
| 1120 |
-
},
|
| 1121 |
-
"node_modules/vite": {
|
| 1122 |
-
"version": "6.3.7",
|
| 1123 |
-
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.7.tgz",
|
| 1124 |
-
"integrity": "sha512-mQYaKepA0NGMBsz8Xktt3tJUG5ELE2iT7IJ+ssXI6nxVdE2sFc/d/6w/JByqMLvWg8hNKHpPgzjgOkrhpKFnrA==",
|
| 1125 |
-
"dev": true,
|
| 1126 |
-
"license": "MIT",
|
| 1127 |
-
"dependencies": {
|
| 1128 |
-
"esbuild": "^0.25.0",
|
| 1129 |
-
"fdir": "^6.4.4",
|
| 1130 |
-
"picomatch": "^4.0.2",
|
| 1131 |
-
"postcss": "^8.5.3",
|
| 1132 |
-
"rollup": "^4.34.9",
|
| 1133 |
-
"tinyglobby": "^0.2.13"
|
| 1134 |
-
},
|
| 1135 |
-
"bin": {
|
| 1136 |
-
"vite": "bin/vite.js"
|
| 1137 |
-
},
|
| 1138 |
-
"engines": {
|
| 1139 |
-
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
| 1140 |
-
},
|
| 1141 |
-
"funding": {
|
| 1142 |
-
"url": "https://github.com/vitejs/vite?sponsor=1"
|
| 1143 |
-
},
|
| 1144 |
-
"optionalDependencies": {
|
| 1145 |
-
"fsevents": "~2.3.3"
|
| 1146 |
-
},
|
| 1147 |
-
"peerDependencies": {
|
| 1148 |
-
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
| 1149 |
-
"jiti": ">=1.21.0",
|
| 1150 |
-
"less": "*",
|
| 1151 |
-
"lightningcss": "^1.21.0",
|
| 1152 |
-
"sass": "*",
|
| 1153 |
-
"sass-embedded": "*",
|
| 1154 |
-
"stylus": "*",
|
| 1155 |
-
"sugarss": "*",
|
| 1156 |
-
"terser": "^5.16.0",
|
| 1157 |
-
"tsx": "^4.8.1",
|
| 1158 |
-
"yaml": "^2.4.2"
|
| 1159 |
-
},
|
| 1160 |
-
"peerDependenciesMeta": {
|
| 1161 |
-
"@types/node": {
|
| 1162 |
-
"optional": true
|
| 1163 |
-
},
|
| 1164 |
-
"jiti": {
|
| 1165 |
-
"optional": true
|
| 1166 |
-
},
|
| 1167 |
-
"less": {
|
| 1168 |
-
"optional": true
|
| 1169 |
-
},
|
| 1170 |
-
"lightningcss": {
|
| 1171 |
-
"optional": true
|
| 1172 |
-
},
|
| 1173 |
-
"sass": {
|
| 1174 |
-
"optional": true
|
| 1175 |
-
},
|
| 1176 |
-
"sass-embedded": {
|
| 1177 |
-
"optional": true
|
| 1178 |
-
},
|
| 1179 |
-
"stylus": {
|
| 1180 |
-
"optional": true
|
| 1181 |
-
},
|
| 1182 |
-
"sugarss": {
|
| 1183 |
-
"optional": true
|
| 1184 |
-
},
|
| 1185 |
-
"terser": {
|
| 1186 |
-
"optional": true
|
| 1187 |
-
},
|
| 1188 |
-
"tsx": {
|
| 1189 |
-
"optional": true
|
| 1190 |
-
},
|
| 1191 |
-
"yaml": {
|
| 1192 |
-
"optional": true
|
| 1193 |
-
}
|
| 1194 |
-
}
|
| 1195 |
-
}
|
| 1196 |
-
}
|
| 1197 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "orbit-chase",
|
| 3 |
-
"private": true,
|
| 4 |
-
"version": "0.1.0",
|
| 5 |
-
"type": "module",
|
| 6 |
-
"scripts": {
|
| 7 |
-
"dev": "vite",
|
| 8 |
-
"build": "tsc --noEmit && vite build",
|
| 9 |
-
"preview": "vite preview",
|
| 10 |
-
"test": "tsc -p tsconfig.test.json && node --experimental-specifier-resolution=node --test test/*.test.mjs",
|
| 11 |
-
"test:python": ".venv/bin/python -m pytest",
|
| 12 |
-
"test:parity": "tsc -p tsconfig.test.json && PYTHONPATH=rl .venv/bin/python rl/parity/compare.py",
|
| 13 |
-
"deploy:hf": "bash deploy/huggingface/deploy.sh"
|
| 14 |
-
},
|
| 15 |
-
"dependencies": {
|
| 16 |
-
"phaser": "3.90.0"
|
| 17 |
-
},
|
| 18 |
-
"devDependencies": {
|
| 19 |
-
"typescript": "~5.7.2",
|
| 20 |
-
"vite": "~6.3.1"
|
| 21 |
-
}
|
| 22 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/debugPanel.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
| 1 |
-
import { GameSimulation } from './game/simulation.js';
|
| 2 |
-
|
| 3 |
-
const panel = document.querySelector<HTMLElement>('#debug-panel');
|
| 4 |
-
const content = document.querySelector<HTMLPreElement>('#debug-content');
|
| 5 |
-
const toggle = document.querySelector<HTMLButtonElement>('#debug-toggle');
|
| 6 |
-
let lastRender = 0;
|
| 7 |
-
|
| 8 |
-
if (toggle && panel) {
|
| 9 |
-
toggle.addEventListener('click', () => {
|
| 10 |
-
panel.hidden = !panel.hidden;
|
| 11 |
-
toggle.textContent = panel.hidden ? 'Show debug' : 'Hide debug';
|
| 12 |
-
toggle.setAttribute('aria-expanded', String(!panel.hidden));
|
| 13 |
-
lastRender = 0;
|
| 14 |
-
});
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
-
const point = (x: number, y: number): string => `(${x.toFixed(1)}, ${y.toFixed(1)})`;
|
| 18 |
-
|
| 19 |
-
export function updateDebugPanel(simulation: GameSimulation): void {
|
| 20 |
-
// Keep a fresh snapshot while hidden, so opening the panel after a capture
|
| 21 |
-
// immediately shows the last live state instead of a placeholder.
|
| 22 |
-
if (!panel || !content || performance.now() - lastRender < 120) return;
|
| 23 |
-
lastRender = performance.now();
|
| 24 |
-
const debug = simulation.enemyDebugState;
|
| 25 |
-
const distance = Math.hypot(simulation.player.x - simulation.enemy.x, simulation.player.y - simulation.enemy.y);
|
| 26 |
-
const fields = [
|
| 27 |
-
['Player', simulation.playerControllerName],
|
| 28 |
-
['Player direction', simulation.playerDirection],
|
| 29 |
-
['Enemy controller', simulation.enemyControllerName],
|
| 30 |
-
['Time remaining', `${Math.max(0, simulation.timeRemaining).toFixed(2)} s`],
|
| 31 |
-
['Direct distance', `${distance.toFixed(1)} px`],
|
| 32 |
-
['Player position', point(simulation.player.x, simulation.player.y)],
|
| 33 |
-
['Player velocity', point(simulation.playerVelocity.x, simulation.playerVelocity.y)],
|
| 34 |
-
['Enemy position', point(simulation.enemy.x, simulation.enemy.y)],
|
| 35 |
-
['Enemy direction', simulation.enemyDirection],
|
| 36 |
-
['Pellets / orbs', `${simulation.pellets.length} / ${simulation.powerOrbs.length}`],
|
| 37 |
-
['Surge remaining', `${simulation.surgeRemaining.toFixed(2)} s`],
|
| 38 |
-
...Object.entries(debug).map(([label, value]) => [label, String(value)]),
|
| 39 |
-
];
|
| 40 |
-
content.textContent = fields.map(([label, value]) => `${label.padEnd(18)} ${value}`).join('\n');
|
| 41 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/game/PlayScene.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
| 1 |
-
import Phaser from 'phaser';
|
| 2 |
-
import { GameSimulation, directions } from './simulation.js';
|
| 3 |
-
import { Point } from './arena.js';
|
| 4 |
-
import { ArcadeAudio } from './audio.js';
|
| 5 |
-
import { updateDebugPanel } from '../debugPanel.js';
|
| 6 |
-
import { SarsaPlayerController } from './SarsaPlayerController.js';
|
| 7 |
-
|
| 8 |
-
const TRAINED_PLAYER_URL = '/models/linear-sarsa-20260815-030554-ep8000.json';
|
| 9 |
-
|
| 10 |
-
const palette = { background: 0x071426, arena: 0x0b2941, outline: 0x84dcff, wall: 0x2b86bb, wallEdge: 0x82dcff, pellet: 0xf5dc83, player: 0xffda45, enemy: 0xfa637d };
|
| 11 |
-
type TrailDot = Point & { radius: number; alpha: number; color: number };
|
| 12 |
-
type Spark = Point & { vx: number; vy: number; life: number; color: number };
|
| 13 |
-
|
| 14 |
-
export class PlayScene extends Phaser.Scene {
|
| 15 |
-
private simulation = new GameSimulation();
|
| 16 |
-
private arenaGraphics!: Phaser.GameObjects.Graphics;
|
| 17 |
-
private actorGraphics!: Phaser.GameObjects.Graphics;
|
| 18 |
-
private hud!: Phaser.GameObjects.Text;
|
| 19 |
-
private message!: Phaser.GameObjects.Text;
|
| 20 |
-
private status!: Phaser.GameObjects.Text;
|
| 21 |
-
private keys!: Record<'up' | 'down' | 'left' | 'right' | 'w' | 'a' | 's' | 'd', Phaser.Input.Keyboard.Key>;
|
| 22 |
-
private touchDirection: Point = { x: 0, y: 0 };
|
| 23 |
-
private finished = false;
|
| 24 |
-
private playerTrail: TrailDot[] = [];
|
| 25 |
-
private enemyTrail: TrailDot[] = [];
|
| 26 |
-
private sparks: Spark[] = [];
|
| 27 |
-
private audio = new ArcadeAudio();
|
| 28 |
-
private nextDangerSound = 0;
|
| 29 |
-
private trainedPlayer: SarsaPlayerController | null = null;
|
| 30 |
-
private trainedEnabled = false;
|
| 31 |
-
|
| 32 |
-
constructor() { super('play'); }
|
| 33 |
-
|
| 34 |
-
create(): void {
|
| 35 |
-
this.arenaGraphics = this.add.graphics();
|
| 36 |
-
this.actorGraphics = this.add.graphics();
|
| 37 |
-
this.hud = this.add.text(28, 24, '', { fontFamily: 'system-ui', fontSize: '18px', color: '#e8f5ff' });
|
| 38 |
-
this.add.text(28, 52, 'Collect every pellet. Avoid the red enemy.', { fontFamily: 'system-ui', fontSize: '14px', color: '#a6c8da' });
|
| 39 |
-
this.status = this.add.text(400, 52, '', { fontFamily: 'system-ui', fontSize: '14px', color: '#ffd971' }).setOrigin(0.5, 0);
|
| 40 |
-
this.message = this.add.text(400, 318, '', { fontFamily: 'system-ui', fontSize: '34px', fontStyle: 'bold', color: '#ffffff', align: 'center' }).setOrigin(0.5).setDepth(3);
|
| 41 |
-
this.keys = this.input.keyboard!.addKeys({ up: 'UP', down: 'DOWN', left: 'LEFT', right: 'RIGHT', w: 'W', a: 'A', s: 'S', d: 'D' }) as Record<'up' | 'down' | 'left' | 'right' | 'w' | 'a' | 's' | 'd', Phaser.Input.Keyboard.Key>;
|
| 42 |
-
this.input.keyboard!.on('keydown', () => this.audio.unlock());
|
| 43 |
-
this.input.keyboard!.addKey('R').on('down', () => this.restart());
|
| 44 |
-
this.createTouchPad();
|
| 45 |
-
this.bindPlayerToggle();
|
| 46 |
-
this.restart();
|
| 47 |
-
void this.loadTrainedPlayer();
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
update(_: number, delta: number): void {
|
| 51 |
-
if (this.finished) return;
|
| 52 |
-
const input = this.currentInput();
|
| 53 |
-
const result = this.simulation.step(delta / 1000, input);
|
| 54 |
-
for (const pellet of result.pelletsCollected) { this.burst(pellet, palette.pellet, 7); this.audio.pellet(); }
|
| 55 |
-
for (const orb of result.powerCollected) { this.burst(orb, 0x91efff, 18); this.audio.surge(); }
|
| 56 |
-
this.playerTrail.unshift({ ...this.simulation.player, radius: 9, alpha: 0.42, color: palette.player });
|
| 57 |
-
this.enemyTrail.unshift({ ...this.simulation.enemy, radius: 9, alpha: 0.3, color: palette.enemy });
|
| 58 |
-
this.playerTrail = this.playerTrail.slice(0, 12).map((dot) => ({ ...dot, alpha: dot.alpha * 0.84 }));
|
| 59 |
-
this.enemyTrail = this.enemyTrail.slice(0, 10).map((dot) => ({ ...dot, alpha: dot.alpha * 0.82 }));
|
| 60 |
-
this.sparks = this.sparks.map((spark) => ({ ...spark, x: spark.x + spark.vx * delta / 1000, y: spark.y + spark.vy * delta / 1000, life: spark.life - delta / 1000 })).filter((spark) => spark.life > 0);
|
| 61 |
-
const danger = Math.hypot(this.simulation.player.x - this.simulation.enemy.x, this.simulation.player.y - this.simulation.enemy.y) < 105;
|
| 62 |
-
if (danger && this.time.now >= this.nextDangerSound) { this.audio.danger(); this.nextDangerSound = this.time.now + 650; }
|
| 63 |
-
if (result.captured) { this.audio.captured(); this.endRound('Captured!'); }
|
| 64 |
-
else if (result.cleared) { this.audio.cleared(); this.endRound('Arena cleared!'); }
|
| 65 |
-
else if (result.timedOut) { this.audio.timeout(); this.endRound('Time up!'); }
|
| 66 |
-
this.render();
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
-
private restart(): void {
|
| 70 |
-
this.audio.unlock(); this.finished = false; this.touchDirection = { x: 0, y: 0 }; this.playerTrail = []; this.enemyTrail = []; this.sparks = []; this.nextDangerSound = 0;
|
| 71 |
-
this.simulation.reset(Math.floor(Math.random() * 0xffffffff));
|
| 72 |
-
this.message.setText(''); this.render();
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
private endRound(text: string): void {
|
| 76 |
-
this.finished = true;
|
| 77 |
-
this.message.setText(`${text}\nScore ${this.simulation.score}\n\nPress R or Restart`);
|
| 78 |
-
}
|
| 79 |
-
|
| 80 |
-
private currentInput(): Point {
|
| 81 |
-
let x = this.touchDirection.x;
|
| 82 |
-
let y = this.touchDirection.y;
|
| 83 |
-
if (this.keys.left.isDown || this.keys.a.isDown) x -= 1;
|
| 84 |
-
if (this.keys.right.isDown || this.keys.d.isDown) x += 1;
|
| 85 |
-
if (this.keys.up.isDown || this.keys.w.isDown) y -= 1;
|
| 86 |
-
if (this.keys.down.isDown || this.keys.s.isDown) y += 1;
|
| 87 |
-
return { x, y };
|
| 88 |
-
}
|
| 89 |
-
|
| 90 |
-
private render(): void {
|
| 91 |
-
const { arena, pellets, powerOrbs, player, enemy, timeRemaining, score, multiplier, comboRemaining, surgeRemaining } = this.simulation;
|
| 92 |
-
const g = this.arenaGraphics.clear();
|
| 93 |
-
g.fillStyle(palette.arena).fillCircle(arena.center.x, arena.center.y, arena.radius);
|
| 94 |
-
g.lineStyle(4, palette.outline).strokeCircle(arena.center.x, arena.center.y, arena.radius);
|
| 95 |
-
g.lineStyle(1, palette.outline, 0.15).strokeCircle(arena.center.x, arena.center.y, 155);
|
| 96 |
-
for (const bar of arena.bars) {
|
| 97 |
-
g.lineStyle(bar.width, palette.wall).lineBetween(bar.from.x, bar.from.y, bar.to.x, bar.to.y);
|
| 98 |
-
g.lineStyle(2, palette.wallEdge).lineBetween(bar.from.x, bar.from.y, bar.to.x, bar.to.y);
|
| 99 |
-
}
|
| 100 |
-
g.fillStyle(palette.wall).fillCircle(arena.center.x, arena.center.y, arena.coreRadius);
|
| 101 |
-
g.lineStyle(2, palette.wallEdge).strokeCircle(arena.center.x, arena.center.y, arena.coreRadius);
|
| 102 |
-
g.fillStyle(palette.pellet); for (const pellet of pellets) g.fillCircle(pellet.x, pellet.y, 4);
|
| 103 |
-
const pulse = 9 + Math.sin(this.time.now / 120) * 2;
|
| 104 |
-
for (const orb of powerOrbs) {
|
| 105 |
-
g.fillStyle(0x72dfff, 0.16).fillCircle(orb.x, orb.y, pulse + 6);
|
| 106 |
-
g.fillStyle(0x91efff).fillCircle(orb.x, orb.y, pulse);
|
| 107 |
-
g.lineStyle(2, 0xffffff, 0.8).strokeCircle(orb.x, orb.y, pulse);
|
| 108 |
-
}
|
| 109 |
-
const actors = this.actorGraphics.clear();
|
| 110 |
-
for (const dot of this.enemyTrail) actors.fillStyle(dot.color, dot.alpha).fillCircle(dot.x, dot.y, dot.radius);
|
| 111 |
-
for (const dot of this.playerTrail) actors.fillStyle(dot.color, dot.alpha).fillCircle(dot.x, dot.y, dot.radius);
|
| 112 |
-
for (const spark of this.sparks) actors.fillStyle(spark.color, Math.min(1, spark.life * 2)).fillCircle(spark.x, spark.y, 3);
|
| 113 |
-
if (surgeRemaining > 0) actors.fillStyle(0x91efff, 0.22).fillCircle(player.x, player.y, player.radius + 13 + Math.sin(this.time.now / 90) * 3);
|
| 114 |
-
actors.fillStyle(palette.player).fillCircle(player.x, player.y, player.radius);
|
| 115 |
-
actors.lineStyle(2, 0xfff0a9).strokeCircle(player.x, player.y, player.radius);
|
| 116 |
-
actors.fillStyle(palette.enemy).fillCircle(enemy.x, enemy.y, enemy.radius);
|
| 117 |
-
actors.lineStyle(2, 0xffb2be).strokeCircle(enemy.x, enemy.y, enemy.radius);
|
| 118 |
-
this.hud.setText(`Score ${score.toString().padStart(4, '0')} • Pellets ${pellets.length} • Time ${Math.max(0, Math.ceil(timeRemaining))} • Player ${this.trainedEnabled ? 'Trained' : 'Human'} • Enemy ${this.simulation.enemyControllerName}`);
|
| 119 |
-
if (surgeRemaining > 0) this.status.setText(`SURGE ×${multiplier} · ${surgeRemaining.toFixed(1)}s`);
|
| 120 |
-
else if (comboRemaining > 0 && multiplier > 1) this.status.setText(`STREAK ×${multiplier} · ${comboRemaining.toFixed(1)}s`);
|
| 121 |
-
else if (Math.hypot(player.x - enemy.x, player.y - enemy.y) < 105) this.status.setText('DANGER ZONE');
|
| 122 |
-
else this.status.setText('');
|
| 123 |
-
updateDebugPanel(this.simulation);
|
| 124 |
-
}
|
| 125 |
-
|
| 126 |
-
private createTouchPad(): void {
|
| 127 |
-
const positions: Record<keyof typeof directions, Point> = {
|
| 128 |
-
'up-left': { x: 638, y: 492 }, up: { x: 688, y: 492 }, 'up-right': { x: 738, y: 492 },
|
| 129 |
-
left: { x: 638, y: 542 }, right: { x: 738, y: 542 },
|
| 130 |
-
'down-left': { x: 638, y: 592 }, down: { x: 688, y: 592 }, 'down-right': { x: 738, y: 592 },
|
| 131 |
-
};
|
| 132 |
-
for (const [direction, position] of Object.entries(positions) as [keyof typeof directions, Point][]) {
|
| 133 |
-
const button = this.add.circle(position.x, position.y, 25, 0x173a55, 0.95).setStrokeStyle(1, 0x6baed0).setInteractive({ useHandCursor: true });
|
| 134 |
-
this.add.text(position.x, position.y - 2, { up: '↑', 'up-right': '↗', right: '→', 'down-right': '↘', down: '↓', 'down-left': '↙', left: '←', 'up-left': '↖' }[direction], { fontFamily: 'system-ui', fontSize: '21px', color: '#e8f5ff' }).setOrigin(0.5);
|
| 135 |
-
button.on('pointerdown', () => { this.audio.unlock(); this.touchDirection = directions[direction]; });
|
| 136 |
-
button.on('pointerup', () => { this.touchDirection = { x: 0, y: 0 }; });
|
| 137 |
-
button.on('pointerout', () => { this.touchDirection = { x: 0, y: 0 }; });
|
| 138 |
-
}
|
| 139 |
-
const restart = this.add.text(52, 584, 'Restart', { fontFamily: 'system-ui', fontSize: '17px', color: '#e8f5ff', backgroundColor: '#173a55', padding: { x: 12, y: 7 } }).setInteractive({ useHandCursor: true });
|
| 140 |
-
restart.on('pointerdown', () => this.restart());
|
| 141 |
-
const sound = this.add.text(165, 584, 'Sound: On', { fontFamily: 'system-ui', fontSize: '17px', color: '#e8f5ff', backgroundColor: '#173a55', padding: { x: 12, y: 7 } }).setInteractive({ useHandCursor: true });
|
| 142 |
-
sound.on('pointerdown', () => { this.audio.unlock(); this.audio.enabled = !this.audio.enabled; sound.setText(`Sound: ${this.audio.enabled ? 'On' : 'Off'}`); });
|
| 143 |
-
}
|
| 144 |
-
|
| 145 |
-
private bindPlayerToggle(): void {
|
| 146 |
-
document.getElementById('player-toggle')?.addEventListener('click', () => this.toggleTrainedPlayer());
|
| 147 |
-
}
|
| 148 |
-
|
| 149 |
-
private async loadTrainedPlayer(): Promise<void> {
|
| 150 |
-
try {
|
| 151 |
-
this.trainedPlayer = await SarsaPlayerController.load(TRAINED_PLAYER_URL);
|
| 152 |
-
const toggle = document.getElementById('player-toggle');
|
| 153 |
-
if (toggle instanceof HTMLButtonElement) toggle.disabled = false;
|
| 154 |
-
} catch (error) {
|
| 155 |
-
console.error('Could not load the trained player.', error);
|
| 156 |
-
}
|
| 157 |
-
}
|
| 158 |
-
|
| 159 |
-
private toggleTrainedPlayer(): void {
|
| 160 |
-
if (this.trainedPlayer === null) return;
|
| 161 |
-
this.trainedEnabled = !this.trainedEnabled;
|
| 162 |
-
this.simulation.setPlayerController(this.trainedEnabled ? this.trainedPlayer : null);
|
| 163 |
-
const toggle = document.getElementById('player-toggle');
|
| 164 |
-
if (toggle) toggle.textContent = this.trainedEnabled ? 'Player: Trained' : 'Player: Human';
|
| 165 |
-
}
|
| 166 |
-
|
| 167 |
-
private burst(point: Point, color: number, amount: number): void {
|
| 168 |
-
for (let index = 0; index < amount; index += 1) {
|
| 169 |
-
const angle = Phaser.Math.FloatBetween(0, Math.PI * 2);
|
| 170 |
-
const speed = Phaser.Math.Between(45, 150);
|
| 171 |
-
this.sparks.push({ x: point.x, y: point.y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, life: Phaser.Math.FloatBetween(0.35, 0.8), color });
|
| 172 |
-
}
|
| 173 |
-
}
|
| 174 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/game/SarsaPlayerController.ts
DELETED
|
@@ -1,327 +0,0 @@
|
|
| 1 |
-
import {
|
| 2 |
-
Direction,
|
| 3 |
-
GameSimulation,
|
| 4 |
-
PLAYER_OBSERVATION_SIZE,
|
| 5 |
-
PlayerController,
|
| 6 |
-
PlayerObservation,
|
| 7 |
-
actionOrder,
|
| 8 |
-
} from './simulation.js';
|
| 9 |
-
|
| 10 |
-
export const BROWSER_LINEAR_SARSA_FORMAT = 'orbit-chase-linear-sarsa-browser-v1';
|
| 11 |
-
|
| 12 |
-
const GROUP_PLAYER_POSITION = 1;
|
| 13 |
-
const GROUP_PLAYER_VELOCITY = 2;
|
| 14 |
-
const GROUP_ENEMY_RELATIVE = 3;
|
| 15 |
-
const GROUP_ENEMY_CLOCK = 4;
|
| 16 |
-
const GROUP_PELLET_RANK = 10;
|
| 17 |
-
const GROUP_ORB_RANK = 20;
|
| 18 |
-
const GROUP_BAR = 30;
|
| 19 |
-
const GROUP_SURGE_TIME = 40;
|
| 20 |
-
const GROUP_ENEMY_HEADING = 50;
|
| 21 |
-
const GROUP_ACTION_MASK = 60;
|
| 22 |
-
const NEAREST_PELLET_COUNT = 4;
|
| 23 |
-
const PELLET_COUNT = 32;
|
| 24 |
-
const SURGE_ORB_COUNT = 3;
|
| 25 |
-
const OCCUPANCY_SALT = -1;
|
| 26 |
-
const FNV_OFFSET = 0xCBF29CE484222325n;
|
| 27 |
-
const FNV_PRIME = 0x100000001B3n;
|
| 28 |
-
const UINT64_MASK = 0xFFFFFFFFFFFFFFFFn;
|
| 29 |
-
|
| 30 |
-
type EncoderConfig = { capacity: number; bins: number; tilings: number };
|
| 31 |
-
type WeightRow = { indices: number[]; values: number[] };
|
| 32 |
-
|
| 33 |
-
export type BrowserSarsaArtifact = {
|
| 34 |
-
format: string;
|
| 35 |
-
source_format?: string;
|
| 36 |
-
checkpoint?: string;
|
| 37 |
-
observation_size: number;
|
| 38 |
-
action_order: string[];
|
| 39 |
-
encoder: EncoderConfig;
|
| 40 |
-
weights: WeightRow[];
|
| 41 |
-
};
|
| 42 |
-
|
| 43 |
-
/** Sparse hashed tile encoder matching `orbit_chase.sarsa.FeatureEncoder`. */
|
| 44 |
-
export class FeatureEncoder {
|
| 45 |
-
constructor(
|
| 46 |
-
readonly capacity: number,
|
| 47 |
-
readonly bins: number,
|
| 48 |
-
readonly tilings: number,
|
| 49 |
-
) {}
|
| 50 |
-
|
| 51 |
-
encode(observation: readonly number[], actionMask: readonly boolean[]): number[] {
|
| 52 |
-
if (observation.length !== PLAYER_OBSERVATION_SIZE) {
|
| 53 |
-
throw new Error(`Expected observation length ${PLAYER_OBSERVATION_SIZE}, got ${observation.length}.`);
|
| 54 |
-
}
|
| 55 |
-
if (actionMask.length !== actionOrder.length) {
|
| 56 |
-
throw new Error(`Expected action mask length ${actionOrder.length}, got ${actionMask.length}.`);
|
| 57 |
-
}
|
| 58 |
-
if (actionMask.some((value) => value !== true && value !== false)) {
|
| 59 |
-
throw new Error('Action mask values must be binary.');
|
| 60 |
-
}
|
| 61 |
-
if (!actionMask.some(Boolean)) {
|
| 62 |
-
throw new Error('Action mask must contain at least one valid action.');
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
const features = new Set<number>();
|
| 66 |
-
const player = [observation[0], observation[1]];
|
| 67 |
-
this.addTiles(features, GROUP_PLAYER_POSITION, player, -2, 2);
|
| 68 |
-
this.addTiles(features, GROUP_PLAYER_VELOCITY, [observation[2], observation[3]], -1, 1);
|
| 69 |
-
this.addTiles(features, GROUP_ENEMY_RELATIVE, [observation[5] - player[0], observation[6] - player[1]], -2, 2);
|
| 70 |
-
this.addTiles(features, GROUP_ENEMY_CLOCK, [observation[15]], 0, 1);
|
| 71 |
-
this.addTiles(features, GROUP_SURGE_TIME, [observation[4], observation[129]], 0, 1);
|
| 72 |
-
this.addActiveDirectionFeatures(features, GROUP_ENEMY_HEADING, observation.slice(7, 15));
|
| 73 |
-
this.addActiveDirectionFeatures(features, GROUP_ACTION_MASK, actionMask.map((valid) => valid ? 1 : 0));
|
| 74 |
-
this.addNearestCollectibles(features, observation.slice(24, 120), PELLET_COUNT, NEAREST_PELLET_COUNT, player, GROUP_PELLET_RANK);
|
| 75 |
-
this.addNearestCollectibles(features, observation.slice(120, 129), SURGE_ORB_COUNT, SURGE_ORB_COUNT, player, GROUP_ORB_RANK);
|
| 76 |
-
this.addBarFeatures(features, observation.slice(16, 24), player);
|
| 77 |
-
return [...features].sort((left, right) => left - right);
|
| 78 |
-
}
|
| 79 |
-
|
| 80 |
-
private addTiles(features: Set<number>, group: number, values: readonly number[], lower: number, upper: number): void {
|
| 81 |
-
const normalized = values.map((value) => (clip(value, lower, upper) - lower) / (upper - lower));
|
| 82 |
-
for (let tiling = 0; tiling < this.tilings; tiling += 1) {
|
| 83 |
-
const offset = tiling / this.tilings;
|
| 84 |
-
const coordinates = normalized.map((value) => clip(Math.floor(value * this.bins + offset), 0, this.bins - 1));
|
| 85 |
-
features.add(this.hash(group, tiling, ...coordinates));
|
| 86 |
-
}
|
| 87 |
-
}
|
| 88 |
-
|
| 89 |
-
private addActiveDirectionFeatures(features: Set<number>, group: number, values: readonly number[]): void {
|
| 90 |
-
values.forEach((value, direction) => {
|
| 91 |
-
if (value > 0) features.add(this.hash(group, direction));
|
| 92 |
-
});
|
| 93 |
-
}
|
| 94 |
-
|
| 95 |
-
private addNearestCollectibles(
|
| 96 |
-
features: Set<number>,
|
| 97 |
-
encoded: readonly number[],
|
| 98 |
-
count: number,
|
| 99 |
-
nearestCount: number,
|
| 100 |
-
player: readonly number[],
|
| 101 |
-
group: number,
|
| 102 |
-
): void {
|
| 103 |
-
const active: number[][] = [];
|
| 104 |
-
for (let index = 0; index < count; index += 1) {
|
| 105 |
-
const offset = index * 3;
|
| 106 |
-
if (encoded[offset + 2] > 0.5) active.push([encoded[offset], encoded[offset + 1]]);
|
| 107 |
-
}
|
| 108 |
-
active.sort((left, right) => comparePoints(left, right, player));
|
| 109 |
-
for (let rank = 0; rank < nearestCount; rank += 1) {
|
| 110 |
-
const groupId = group + rank;
|
| 111 |
-
const occupied = rank < active.length;
|
| 112 |
-
features.add(this.hash(groupId, OCCUPANCY_SALT, occupied ? 1 : 0));
|
| 113 |
-
if (occupied) {
|
| 114 |
-
this.addTiles(features, groupId, [active[rank][0] - player[0], active[rank][1] - player[1]], -2, 2);
|
| 115 |
-
}
|
| 116 |
-
}
|
| 117 |
-
}
|
| 118 |
-
|
| 119 |
-
private addBarFeatures(features: Set<number>, encoded: readonly number[], player: readonly number[]): void {
|
| 120 |
-
const bars: Array<[number[], number[]]> = [];
|
| 121 |
-
for (let index = 0; index < 2; index += 1) {
|
| 122 |
-
const offset = index * 4;
|
| 123 |
-
let start = [encoded[offset], encoded[offset + 1]];
|
| 124 |
-
let end = [encoded[offset + 2], encoded[offset + 3]];
|
| 125 |
-
if (lexLess(end, start)) [start, end] = [end, start];
|
| 126 |
-
bars.push([start, end]);
|
| 127 |
-
}
|
| 128 |
-
bars.sort((left, right) => compareBars(left, right));
|
| 129 |
-
bars.forEach(([start, end], index) => {
|
| 130 |
-
const closest = closestPoint(start, end, player);
|
| 131 |
-
this.addTiles(features, GROUP_BAR + index, [closest[0] - player[0], closest[1] - player[1]], -2, 2);
|
| 132 |
-
});
|
| 133 |
-
}
|
| 134 |
-
|
| 135 |
-
private hash(...components: number[]): number {
|
| 136 |
-
let value = FNV_OFFSET;
|
| 137 |
-
const bytes = new Uint8Array(8);
|
| 138 |
-
const view = new DataView(bytes.buffer);
|
| 139 |
-
for (const component of components) {
|
| 140 |
-
view.setBigInt64(0, BigInt(component), true);
|
| 141 |
-
for (let index = 0; index < 8; index += 1) {
|
| 142 |
-
value ^= BigInt(bytes[index]);
|
| 143 |
-
value = (value * FNV_PRIME) & UINT64_MASK;
|
| 144 |
-
}
|
| 145 |
-
}
|
| 146 |
-
return Number(value % BigInt(this.capacity));
|
| 147 |
-
}
|
| 148 |
-
}
|
| 149 |
-
|
| 150 |
-
/** Browser inference for a frozen linear Sarsa player checkpoint. */
|
| 151 |
-
export class SarsaPlayerController implements PlayerController {
|
| 152 |
-
readonly name = 'linear-sarsa';
|
| 153 |
-
private readonly lookups: ReadonlyArray<ReadonlyMap<number, number>>;
|
| 154 |
-
|
| 155 |
-
private constructor(
|
| 156 |
-
private readonly encoder: FeatureEncoder,
|
| 157 |
-
lookups: ReadonlyArray<ReadonlyMap<number, number>>,
|
| 158 |
-
) {
|
| 159 |
-
this.lookups = lookups;
|
| 160 |
-
}
|
| 161 |
-
|
| 162 |
-
static fromArtifact(artifact: unknown): SarsaPlayerController {
|
| 163 |
-
const document = validateArtifact(artifact);
|
| 164 |
-
const encoder = new FeatureEncoder(document.encoder.capacity, document.encoder.bins, document.encoder.tilings);
|
| 165 |
-
const lookups = document.weights.map((row) => {
|
| 166 |
-
const lookup = new Map<number, number>();
|
| 167 |
-
row.indices.forEach((index, offset) => lookup.set(index, row.values[offset]));
|
| 168 |
-
return lookup;
|
| 169 |
-
});
|
| 170 |
-
return new SarsaPlayerController(encoder, lookups);
|
| 171 |
-
}
|
| 172 |
-
|
| 173 |
-
static async load(url: string): Promise<SarsaPlayerController> {
|
| 174 |
-
const response = await fetch(url);
|
| 175 |
-
if (!response.ok) throw new Error(`Could not load linear Sarsa artifact (${response.status}).`);
|
| 176 |
-
return SarsaPlayerController.fromArtifact(await response.json());
|
| 177 |
-
}
|
| 178 |
-
|
| 179 |
-
encode(observation: PlayerObservation): number[] {
|
| 180 |
-
return this.encoder.encode(observation.features, observation.actionMask);
|
| 181 |
-
}
|
| 182 |
-
|
| 183 |
-
qValues(indices: readonly number[]): number[] {
|
| 184 |
-
return this.lookups.map((lookup) => {
|
| 185 |
-
let total = 0;
|
| 186 |
-
for (const index of indices) total += lookup.get(index) ?? 0;
|
| 187 |
-
return total;
|
| 188 |
-
});
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
choose(observation: PlayerObservation): Direction {
|
| 192 |
-
const indices = this.encode(observation);
|
| 193 |
-
return actionOrder[selectGreedyAction(this.qValues(indices), observation.actionMask)];
|
| 194 |
-
}
|
| 195 |
-
|
| 196 |
-
selectAction(simulation: GameSimulation): Direction {
|
| 197 |
-
return this.choose(simulation.observe());
|
| 198 |
-
}
|
| 199 |
-
}
|
| 200 |
-
|
| 201 |
-
/** Masked greedy action: highest valid Q, lowest index on exact ties. */
|
| 202 |
-
export function selectGreedyAction(qValues: readonly number[], actionMask: readonly boolean[]): number {
|
| 203 |
-
if (qValues.length !== actionOrder.length || actionMask.length !== actionOrder.length) {
|
| 204 |
-
throw new Error(`Expected ${actionOrder.length} Q-values and mask entries.`);
|
| 205 |
-
}
|
| 206 |
-
let bestIndex = -1;
|
| 207 |
-
let bestValue = -Infinity;
|
| 208 |
-
for (let index = 0; index < actionOrder.length; index += 1) {
|
| 209 |
-
if (!actionMask[index]) continue;
|
| 210 |
-
if (bestIndex < 0 || qValues[index] > bestValue) {
|
| 211 |
-
bestIndex = index;
|
| 212 |
-
bestValue = qValues[index];
|
| 213 |
-
}
|
| 214 |
-
}
|
| 215 |
-
if (bestIndex < 0) throw new Error('Action mask must contain at least one valid action.');
|
| 216 |
-
return bestIndex;
|
| 217 |
-
}
|
| 218 |
-
|
| 219 |
-
function validateArtifact(artifact: unknown): BrowserSarsaArtifact {
|
| 220 |
-
if (!isRecord(artifact)) throw new Error('Linear Sarsa artifact must be an object.');
|
| 221 |
-
if (artifact.format !== BROWSER_LINEAR_SARSA_FORMAT) {
|
| 222 |
-
throw new Error(`Unsupported browser artifact format: ${String(artifact.format)}.`);
|
| 223 |
-
}
|
| 224 |
-
if (artifact.observation_size !== PLAYER_OBSERVATION_SIZE) {
|
| 225 |
-
throw new Error(`Browser artifact observation_size must be ${PLAYER_OBSERVATION_SIZE}.`);
|
| 226 |
-
}
|
| 227 |
-
if (!Array.isArray(artifact.action_order) || artifact.action_order.join(',') !== actionOrder.join(',')) {
|
| 228 |
-
throw new Error('Browser artifact action_order does not match the game contract.');
|
| 229 |
-
}
|
| 230 |
-
const encoder = artifact.encoder;
|
| 231 |
-
if (!isRecord(encoder) || !isPositiveInt(encoder.capacity) || !isPositiveInt(encoder.bins) || !isPositiveInt(encoder.tilings)) {
|
| 232 |
-
throw new Error('Browser artifact encoder metadata is invalid.');
|
| 233 |
-
}
|
| 234 |
-
const capacity = encoder.capacity;
|
| 235 |
-
const bins = encoder.bins;
|
| 236 |
-
const tilings = encoder.tilings;
|
| 237 |
-
if (!Array.isArray(artifact.weights) || artifact.weights.length !== actionOrder.length) {
|
| 238 |
-
throw new Error(`Browser artifact must contain ${actionOrder.length} weight rows.`);
|
| 239 |
-
}
|
| 240 |
-
const rows = artifact.weights.map((row, action) => validateWeightRow(row, capacity, action));
|
| 241 |
-
return {
|
| 242 |
-
format: BROWSER_LINEAR_SARSA_FORMAT,
|
| 243 |
-
source_format: typeof artifact.source_format === 'string' ? artifact.source_format : undefined,
|
| 244 |
-
checkpoint: typeof artifact.checkpoint === 'string' ? artifact.checkpoint : undefined,
|
| 245 |
-
observation_size: PLAYER_OBSERVATION_SIZE,
|
| 246 |
-
action_order: [...actionOrder],
|
| 247 |
-
encoder: { capacity, bins, tilings },
|
| 248 |
-
weights: rows,
|
| 249 |
-
};
|
| 250 |
-
}
|
| 251 |
-
|
| 252 |
-
function validateWeightRow(row: unknown, capacity: number, action: number): WeightRow {
|
| 253 |
-
if (!isRecord(row) || !Array.isArray(row.indices) || !Array.isArray(row.values)) {
|
| 254 |
-
throw new Error(`Weight row ${action} must contain indices and values arrays.`);
|
| 255 |
-
}
|
| 256 |
-
const rawIndices = row.indices;
|
| 257 |
-
const rawValues = row.values;
|
| 258 |
-
if (rawIndices.length !== rawValues.length) {
|
| 259 |
-
throw new Error(`Weight row ${action} indices and values must have matching lengths.`);
|
| 260 |
-
}
|
| 261 |
-
const indices: number[] = [];
|
| 262 |
-
const values: number[] = [];
|
| 263 |
-
rawIndices.forEach((index, offset) => {
|
| 264 |
-
if (typeof index !== 'number' || !Number.isInteger(index) || index < 0 || index >= capacity) {
|
| 265 |
-
throw new Error(`Weight row ${action} has an index outside [0, ${capacity}).`);
|
| 266 |
-
}
|
| 267 |
-
if (offset > 0 && !(index > indices[offset - 1])) {
|
| 268 |
-
throw new Error(`Weight row ${action} indices must be strictly increasing.`);
|
| 269 |
-
}
|
| 270 |
-
const value = rawValues[offset];
|
| 271 |
-
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
| 272 |
-
throw new Error(`Weight row ${action} values must be finite numbers.`);
|
| 273 |
-
}
|
| 274 |
-
indices.push(index);
|
| 275 |
-
values.push(value);
|
| 276 |
-
});
|
| 277 |
-
return { indices, values };
|
| 278 |
-
}
|
| 279 |
-
|
| 280 |
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
| 281 |
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
| 282 |
-
}
|
| 283 |
-
|
| 284 |
-
function isPositiveInt(value: unknown): value is number {
|
| 285 |
-
return typeof value === 'number' && Number.isInteger(value) && value > 0;
|
| 286 |
-
}
|
| 287 |
-
|
| 288 |
-
function clip(value: number, lower: number, upper: number): number {
|
| 289 |
-
return Math.min(upper, Math.max(lower, value));
|
| 290 |
-
}
|
| 291 |
-
|
| 292 |
-
function squaredDistance(point: readonly number[], player: readonly number[]): number {
|
| 293 |
-
const dx = point[0] - player[0];
|
| 294 |
-
const dy = point[1] - player[1];
|
| 295 |
-
return dx * dx + dy * dy;
|
| 296 |
-
}
|
| 297 |
-
|
| 298 |
-
function comparePoints(left: readonly number[], right: readonly number[], player: readonly number[]): number {
|
| 299 |
-
const distance = squaredDistance(left, player) - squaredDistance(right, player);
|
| 300 |
-
if (distance !== 0) return distance;
|
| 301 |
-
if (left[0] !== right[0]) return left[0] < right[0] ? -1 : 1;
|
| 302 |
-
if (left[1] !== right[1]) return left[1] < right[1] ? -1 : 1;
|
| 303 |
-
return 0;
|
| 304 |
-
}
|
| 305 |
-
|
| 306 |
-
function lexLess(left: readonly number[], right: readonly number[]): boolean {
|
| 307 |
-
if (left[0] !== right[0]) return left[0] < right[0];
|
| 308 |
-
return left[1] < right[1];
|
| 309 |
-
}
|
| 310 |
-
|
| 311 |
-
function compareBars(left: [number[], number[]], right: [number[], number[]]): number {
|
| 312 |
-
const keysLeft = [...left[0], ...left[1]];
|
| 313 |
-
const keysRight = [...right[0], ...right[1]];
|
| 314 |
-
for (let index = 0; index < keysLeft.length; index += 1) {
|
| 315 |
-
if (keysLeft[index] !== keysRight[index]) return keysLeft[index] < keysRight[index] ? -1 : 1;
|
| 316 |
-
}
|
| 317 |
-
return 0;
|
| 318 |
-
}
|
| 319 |
-
|
| 320 |
-
function closestPoint(start: readonly number[], end: readonly number[], point: readonly number[]): number[] {
|
| 321 |
-
const sx = end[0] - start[0];
|
| 322 |
-
const sy = end[1] - start[1];
|
| 323 |
-
const lengthSquared = sx * sx + sy * sy;
|
| 324 |
-
if (lengthSquared === 0) return [start[0], start[1]];
|
| 325 |
-
const fraction = clip(((point[0] - start[0]) * sx + (point[1] - start[1]) * sy) / lengthSquared, 0, 1);
|
| 326 |
-
return [start[0] + fraction * sx, start[1] + fraction * sy];
|
| 327 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/game/arena.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
| 1 |
-
export type Point = { x: number; y: number };
|
| 2 |
-
export type Segment = { from: Point; to: Point; width: number };
|
| 3 |
-
|
| 4 |
-
export type Arena = {
|
| 5 |
-
center: Point;
|
| 6 |
-
radius: number;
|
| 7 |
-
coreRadius: number;
|
| 8 |
-
bars: Segment[];
|
| 9 |
-
};
|
| 10 |
-
|
| 11 |
-
export function seededRandom(seed: number): () => number {
|
| 12 |
-
let value = seed >>> 0;
|
| 13 |
-
return () => {
|
| 14 |
-
value += 0x6d2b79f5;
|
| 15 |
-
let result = value;
|
| 16 |
-
result = Math.imul(result ^ (result >>> 15), result | 1);
|
| 17 |
-
result ^= result + Math.imul(result ^ (result >>> 7), result | 61);
|
| 18 |
-
return ((result ^ (result >>> 14)) >>> 0) / 4294967296;
|
| 19 |
-
};
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
export function makeArena(seed: number): Arena {
|
| 23 |
-
const random = seededRandom(seed);
|
| 24 |
-
const center = { x: 400, y: 322 };
|
| 25 |
-
const angle = random() * Math.PI * 2;
|
| 26 |
-
const unit = { x: Math.cos(angle), y: Math.sin(angle) };
|
| 27 |
-
const perpendicular = { x: -unit.y, y: unit.x };
|
| 28 |
-
const makeBar = (sign: number): Segment => {
|
| 29 |
-
const offset = 92 * sign;
|
| 30 |
-
const length = 112 + random() * 38;
|
| 31 |
-
return {
|
| 32 |
-
from: {
|
| 33 |
-
x: center.x + unit.x * offset - perpendicular.x * length / 2,
|
| 34 |
-
y: center.y + unit.y * offset - perpendicular.y * length / 2,
|
| 35 |
-
},
|
| 36 |
-
to: {
|
| 37 |
-
x: center.x + unit.x * offset + perpendicular.x * length / 2,
|
| 38 |
-
y: center.y + unit.y * offset + perpendicular.y * length / 2,
|
| 39 |
-
},
|
| 40 |
-
width: 18,
|
| 41 |
-
};
|
| 42 |
-
};
|
| 43 |
-
return { center, radius: 242, coreRadius: 36, bars: [makeBar(-1), makeBar(1)] };
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
export function pointSegmentDistance(point: Point, segment: Segment): number {
|
| 47 |
-
const dx = segment.to.x - segment.from.x;
|
| 48 |
-
const dy = segment.to.y - segment.from.y;
|
| 49 |
-
const denominator = dx * dx + dy * dy;
|
| 50 |
-
const t = denominator === 0 ? 0 : Math.max(0, Math.min(1, ((point.x - segment.from.x) * dx + (point.y - segment.from.y) * dy) / denominator));
|
| 51 |
-
return Math.hypot(point.x - (segment.from.x + t * dx), point.y - (segment.from.y + t * dy));
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
export function isBlocked(arena: Arena, point: Point, bodyRadius: number): boolean {
|
| 55 |
-
if (Math.hypot(point.x - arena.center.x, point.y - arena.center.y) > arena.radius - bodyRadius) return true;
|
| 56 |
-
if (Math.hypot(point.x - arena.center.x, point.y - arena.center.y) < arena.coreRadius + bodyRadius) return true;
|
| 57 |
-
return arena.bars.some((bar) => pointSegmentDistance(point, bar) < bar.width / 2 + bodyRadius);
|
| 58 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/game/audio.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
export class ArcadeAudio {
|
| 2 |
-
private context?: AudioContext;
|
| 3 |
-
enabled = true;
|
| 4 |
-
|
| 5 |
-
unlock(): void {
|
| 6 |
-
this.context ??= new AudioContext();
|
| 7 |
-
if (this.context.state === 'suspended') void this.context.resume();
|
| 8 |
-
}
|
| 9 |
-
|
| 10 |
-
pellet(): void { this.tone(700, 0.055, 'square', 0.045, 980); }
|
| 11 |
-
surge(): void { this.tone(310, 0.11, 'triangle', 0.07, 620); window.setTimeout(() => this.tone(780, 0.14, 'triangle', 0.06, 1180), 75); }
|
| 12 |
-
danger(): void { this.tone(150, 0.06, 'sine', 0.025, 135); }
|
| 13 |
-
captured(): void { this.tone(260, 0.35, 'sawtooth', 0.09, 65); }
|
| 14 |
-
cleared(): void { this.tone(420, 0.1, 'triangle', 0.055, 620); window.setTimeout(() => this.tone(620, 0.18, 'triangle', 0.055, 980), 105); }
|
| 15 |
-
timeout(): void { this.tone(240, 0.16, 'sine', 0.05, 180); }
|
| 16 |
-
|
| 17 |
-
private tone(start: number, duration: number, type: OscillatorType, gain: number, end: number): void {
|
| 18 |
-
if (!this.enabled || !this.context || this.context.state !== 'running') return;
|
| 19 |
-
const now = this.context.currentTime;
|
| 20 |
-
const oscillator = this.context.createOscillator();
|
| 21 |
-
const envelope = this.context.createGain();
|
| 22 |
-
oscillator.type = type;
|
| 23 |
-
oscillator.frequency.setValueAtTime(start, now);
|
| 24 |
-
oscillator.frequency.exponentialRampToValueAtTime(Math.max(1, end), now + duration);
|
| 25 |
-
envelope.gain.setValueAtTime(0.0001, now);
|
| 26 |
-
envelope.gain.exponentialRampToValueAtTime(gain, now + 0.012);
|
| 27 |
-
envelope.gain.exponentialRampToValueAtTime(0.0001, now + duration);
|
| 28 |
-
oscillator.connect(envelope).connect(this.context.destination);
|
| 29 |
-
oscillator.start(now);
|
| 30 |
-
oscillator.stop(now + duration + 0.02);
|
| 31 |
-
}
|
| 32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/game/simulation.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
| 1 |
-
import { Arena, Point, isBlocked, makeArena, seededRandom } from './arena.js';
|
| 2 |
-
|
| 3 |
-
export type Direction = 'up' | 'up-right' | 'right' | 'down-right' | 'down' | 'down-left' | 'left' | 'up-left';
|
| 4 |
-
export const directions: Record<Direction, Point> = {
|
| 5 |
-
up: { x: 0, y: -1 }, 'up-right': { x: 1, y: -1 }, right: { x: 1, y: 0 }, 'down-right': { x: 1, y: 1 },
|
| 6 |
-
down: { x: 0, y: 1 }, 'down-left': { x: -1, y: 1 }, left: { x: -1, y: 0 }, 'up-left': { x: -1, y: -1 },
|
| 7 |
-
};
|
| 8 |
-
|
| 9 |
-
export const actionOrder = Object.keys(directions) as Direction[];
|
| 10 |
-
export const FIXED_TIMESTEP = 0.01;
|
| 11 |
-
export const PLAYER_DECISION_INTERVAL = 0.1;
|
| 12 |
-
export const ENEMY_DECISION_INTERVAL = 0.28;
|
| 13 |
-
export const PLAYER_OBSERVATION_SIZE = 130;
|
| 14 |
-
|
| 15 |
-
export type Actor = Point & { radius: number; speed: number };
|
| 16 |
-
export type CollectibleSlot = { point: Point; active: boolean };
|
| 17 |
-
export type StepResult = { captured: boolean; cleared: boolean; timedOut: boolean; pelletsCollected: Point[]; powerCollected: Point[] };
|
| 18 |
-
export type EnemyDebugState = Record<string, string | number | boolean>;
|
| 19 |
-
export interface EnemyController { readonly name: string; selectAction(simulation: GameSimulation): Direction; getDebugState?(): EnemyDebugState; }
|
| 20 |
-
export interface PlayerController { readonly name: string; selectAction(simulation: GameSimulation): Direction; }
|
| 21 |
-
|
| 22 |
-
/** Exact, fixed-order input supplied to the player policy. */
|
| 23 |
-
export type PlayerObservation = { features: number[]; actionMask: boolean[] };
|
| 24 |
-
|
| 25 |
-
const emptyResult = (): StepResult => ({ captured: false, cleared: false, timedOut: false, pelletsCollected: [], powerCollected: [] });
|
| 26 |
-
|
| 27 |
-
/**
|
| 28 |
-
* Deterministic game rules shared by the browser UI and future headless agents.
|
| 29 |
-
* `step()` accepts browser elapsed time but advances only in fixed 10 ms ticks;
|
| 30 |
-
* training code should call `stepFixed()` directly.
|
| 31 |
-
*/
|
| 32 |
-
export class GameSimulation {
|
| 33 |
-
arena: Arena;
|
| 34 |
-
player: Actor;
|
| 35 |
-
enemy: Actor;
|
| 36 |
-
pellets: Point[] = [];
|
| 37 |
-
powerOrbs: Point[] = [];
|
| 38 |
-
pelletSlots: CollectibleSlot[] = [];
|
| 39 |
-
orbSlots: CollectibleSlot[] = [];
|
| 40 |
-
playerVelocity: Point = { x: 0, y: 0 };
|
| 41 |
-
timeRemaining = 60;
|
| 42 |
-
score = 0;
|
| 43 |
-
multiplier = 1;
|
| 44 |
-
comboRemaining = 0;
|
| 45 |
-
surgeRemaining = 0;
|
| 46 |
-
enemyDirection: Direction = 'left';
|
| 47 |
-
playerDirection: Direction = 'up';
|
| 48 |
-
private enemyDecisionRemaining = 0;
|
| 49 |
-
private playerDecisionTicksRemaining = 0;
|
| 50 |
-
private elapsedAccumulator = 0;
|
| 51 |
-
private enemyController: EnemyController | null = null;
|
| 52 |
-
private playerController: PlayerController | null = null;
|
| 53 |
-
|
| 54 |
-
constructor(seed = Date.now()) {
|
| 55 |
-
this.arena = makeArena(seed);
|
| 56 |
-
this.player = { x: 0, y: 0, radius: 12, speed: 175 };
|
| 57 |
-
this.enemy = { x: 0, y: 0, radius: 13, speed: 110 };
|
| 58 |
-
this.reset(seed);
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
/** Reset every procedural element from `seed`, making episode starts reproducible. */
|
| 62 |
-
reset(seed = Date.now()): void {
|
| 63 |
-
this.arena = makeArena(seed);
|
| 64 |
-
const { center } = this.arena;
|
| 65 |
-
this.player = { x: center.x - 165, y: center.y + 85, radius: 12, speed: 175 };
|
| 66 |
-
this.enemy = { x: center.x + 165, y: center.y - 85, radius: 13, speed: 110 };
|
| 67 |
-
this.enemyDirection = 'left';
|
| 68 |
-
this.playerDirection = 'up';
|
| 69 |
-
this.enemyDecisionRemaining = 0;
|
| 70 |
-
this.playerDecisionTicksRemaining = 0;
|
| 71 |
-
this.elapsedAccumulator = 0;
|
| 72 |
-
this.timeRemaining = 60;
|
| 73 |
-
this.pellets = []; this.powerOrbs = []; this.pelletSlots = []; this.orbSlots = [];
|
| 74 |
-
this.playerVelocity = { x: 0, y: 0 };
|
| 75 |
-
this.score = 0; this.multiplier = 1; this.comboRemaining = 0; this.surgeRemaining = 0;
|
| 76 |
-
const random = seededRandom(seed ^ 0xa5a5a5a5);
|
| 77 |
-
while (this.pellets.length < 32) {
|
| 78 |
-
const angle = random() * Math.PI * 2;
|
| 79 |
-
const distance = 52 + random() * 158;
|
| 80 |
-
const candidate = { x: center.x + Math.cos(angle) * distance, y: center.y + Math.sin(angle) * distance };
|
| 81 |
-
if (!isBlocked(this.arena, candidate, 8) && Math.hypot(candidate.x - this.player.x, candidate.y - this.player.y) > 32 && Math.hypot(candidate.x - this.enemy.x, candidate.y - this.enemy.y) > 32) {
|
| 82 |
-
this.pellets.push(candidate); this.pelletSlots.push({ point: candidate, active: true });
|
| 83 |
-
}
|
| 84 |
-
}
|
| 85 |
-
while (this.powerOrbs.length < 3) {
|
| 86 |
-
const angle = random() * Math.PI * 2;
|
| 87 |
-
const distance = 65 + random() * 125;
|
| 88 |
-
const candidate = { x: center.x + Math.cos(angle) * distance, y: center.y + Math.sin(angle) * distance };
|
| 89 |
-
if (!isBlocked(this.arena, candidate, 13) && this.pellets.every((pellet) => Math.hypot(pellet.x - candidate.x, pellet.y - candidate.y) > 34)) {
|
| 90 |
-
this.powerOrbs.push(candidate); this.orbSlots.push({ point: candidate, active: true });
|
| 91 |
-
}
|
| 92 |
-
}
|
| 93 |
-
}
|
| 94 |
-
|
| 95 |
-
setEnemyController(controller: EnemyController | null): void { this.enemyController = controller; }
|
| 96 |
-
setPlayerController(controller: PlayerController | null): void {
|
| 97 |
-
this.playerController = controller;
|
| 98 |
-
this.playerDecisionTicksRemaining = 0;
|
| 99 |
-
}
|
| 100 |
-
get enemyControllerName(): string { return this.enemyController?.name ?? 'Greedy'; }
|
| 101 |
-
get playerControllerName(): string { return this.playerController?.name ?? 'Human'; }
|
| 102 |
-
get enemyDebugState(): EnemyDebugState { return this.enemyController?.getDebugState?.() ?? { Navigation: 'Built-in greedy pursuit' }; }
|
| 103 |
-
get enemyDecisionFraction(): number { return Math.max(0, this.enemyDecisionRemaining) / ENEMY_DECISION_INTERVAL; }
|
| 104 |
-
|
| 105 |
-
/** Advance browser time in fixed ticks, preserving sub-tick remainder between render frames. */
|
| 106 |
-
step(dt: number, playerInput: Point): StepResult {
|
| 107 |
-
this.elapsedAccumulator += Math.min(Math.max(0, dt), 0.25);
|
| 108 |
-
const result = emptyResult();
|
| 109 |
-
while (this.elapsedAccumulator + 1e-9 >= FIXED_TIMESTEP && !result.captured && !result.cleared && !result.timedOut) {
|
| 110 |
-
this.elapsedAccumulator -= FIXED_TIMESTEP;
|
| 111 |
-
this.mergeResult(result, this.stepFixed(playerInput));
|
| 112 |
-
}
|
| 113 |
-
return result;
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
/** Advance precisely one physics tick. This is the headless-training contract. */
|
| 117 |
-
stepFixed(playerInput: Point): StepResult {
|
| 118 |
-
const dt = FIXED_TIMESTEP;
|
| 119 |
-
this.comboRemaining = Math.max(0, this.comboRemaining - dt);
|
| 120 |
-
if (this.comboRemaining === 0) this.multiplier = 1;
|
| 121 |
-
this.surgeRemaining = Math.max(0, this.surgeRemaining - dt);
|
| 122 |
-
const playerMultiplier = this.surgeRemaining > 0 ? 1.32 : 1;
|
| 123 |
-
let movement = playerInput;
|
| 124 |
-
if (this.playerController !== null) {
|
| 125 |
-
if (this.playerDecisionTicksRemaining <= 0) {
|
| 126 |
-
this.playerDirection = this.playerController.selectAction(this);
|
| 127 |
-
this.playerDecisionTicksRemaining = Math.round(PLAYER_DECISION_INTERVAL / FIXED_TIMESTEP);
|
| 128 |
-
}
|
| 129 |
-
movement = directions[this.playerDirection];
|
| 130 |
-
}
|
| 131 |
-
this.move(this.player, movement, dt, playerMultiplier);
|
| 132 |
-
this.playerVelocity = this.velocityFor(movement, this.player.speed * playerMultiplier);
|
| 133 |
-
if (this.playerController !== null) this.playerDecisionTicksRemaining -= 1;
|
| 134 |
-
|
| 135 |
-
if (this.enemyDecisionRemaining <= 0) {
|
| 136 |
-
this.enemyDirection = this.enemyController?.selectAction(this) ?? this.chooseBaselineAction();
|
| 137 |
-
this.enemyDecisionRemaining = ENEMY_DECISION_INTERVAL;
|
| 138 |
-
}
|
| 139 |
-
this.move(this.enemy, directions[this.enemyDirection], dt, this.surgeRemaining > 0 ? 0.78 : 1);
|
| 140 |
-
this.enemyDecisionRemaining -= dt;
|
| 141 |
-
|
| 142 |
-
const pelletsCollected = this.pelletSlots.filter((slot) => slot.active && Math.hypot(slot.point.x - this.player.x, slot.point.y - this.player.y) <= 18).map((slot) => slot.point);
|
| 143 |
-
if (pelletsCollected.length) {
|
| 144 |
-
this.score += 10 * this.multiplier * pelletsCollected.length;
|
| 145 |
-
this.multiplier = Math.min(5, this.multiplier + pelletsCollected.length);
|
| 146 |
-
this.comboRemaining = 2.4;
|
| 147 |
-
for (const slot of this.pelletSlots) if (pelletsCollected.includes(slot.point)) slot.active = false;
|
| 148 |
-
this.pellets = this.pelletSlots.filter((slot) => slot.active).map((slot) => slot.point);
|
| 149 |
-
}
|
| 150 |
-
const powerCollected = this.orbSlots.filter((slot) => slot.active && Math.hypot(slot.point.x - this.player.x, slot.point.y - this.player.y) <= 22).map((slot) => slot.point);
|
| 151 |
-
if (powerCollected.length) {
|
| 152 |
-
this.score += 75 * this.multiplier * powerCollected.length;
|
| 153 |
-
this.surgeRemaining = 4;
|
| 154 |
-
for (const slot of this.orbSlots) if (powerCollected.includes(slot.point)) slot.active = false;
|
| 155 |
-
this.powerOrbs = this.orbSlots.filter((slot) => slot.active).map((slot) => slot.point);
|
| 156 |
-
}
|
| 157 |
-
this.timeRemaining -= dt;
|
| 158 |
-
return { captured: Math.hypot(this.player.x - this.enemy.x, this.player.y - this.enemy.y) < this.player.radius + this.enemy.radius, cleared: this.pellets.length === 0, timedOut: this.timeRemaining <= 0, pelletsCollected, powerCollected };
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
/** Vector observation and swept-path action mask for a player policy. */
|
| 162 |
-
observe(): PlayerObservation {
|
| 163 |
-
const normalized = (point: Point): [number, number] => [(point.x - this.arena.center.x) / this.arena.radius, (point.y - this.arena.center.y) / this.arena.radius];
|
| 164 |
-
const features: number[] = [
|
| 165 |
-
...normalized(this.player), this.playerVelocity.x / this.player.speed, this.playerVelocity.y / this.player.speed, this.surgeRemaining / 4,
|
| 166 |
-
...normalized(this.enemy), ...actionOrder.map((direction) => direction === this.enemyDirection ? 1 : 0), this.enemyDecisionFraction,
|
| 167 |
-
];
|
| 168 |
-
for (const bar of this.arena.bars) features.push(...normalized(bar.from), ...normalized(bar.to));
|
| 169 |
-
for (const slot of this.pelletSlots) features.push(...normalized(slot.point), slot.active ? 1 : 0);
|
| 170 |
-
for (const slot of this.orbSlots) features.push(...normalized(slot.point), slot.active ? 1 : 0);
|
| 171 |
-
features.push(Math.max(0, this.timeRemaining) / 60);
|
| 172 |
-
if (features.length !== PLAYER_OBSERVATION_SIZE) throw new Error(`Player observation mismatch: expected ${PLAYER_OBSERVATION_SIZE}, got ${features.length}.`);
|
| 173 |
-
return { features, actionMask: this.validActions() };
|
| 174 |
-
}
|
| 175 |
-
|
| 176 |
-
/** Actions safe for the player's next 100 ms decision interval. */
|
| 177 |
-
validActions(): boolean[] {
|
| 178 |
-
return actionOrder.map((direction) => this.canCompletePlayerDecision(directions[direction]));
|
| 179 |
-
}
|
| 180 |
-
|
| 181 |
-
/** Dry-run ten ticks of player motion with mid-interval Surge expiry and Orb pickups. */
|
| 182 |
-
private canCompletePlayerDecision(input: Point): boolean {
|
| 183 |
-
const player: Actor = { x: this.player.x, y: this.player.y, radius: this.player.radius, speed: this.player.speed };
|
| 184 |
-
let surge = this.surgeRemaining;
|
| 185 |
-
const orbs = this.orbSlots.map((slot) => slot.active);
|
| 186 |
-
const ticks = Math.round(PLAYER_DECISION_INTERVAL / FIXED_TIMESTEP);
|
| 187 |
-
for (let tick = 0; tick < ticks; tick += 1) {
|
| 188 |
-
surge = Math.max(0, surge - FIXED_TIMESTEP);
|
| 189 |
-
const multiplier = surge > 0 ? 1.32 : 1;
|
| 190 |
-
if (!this.canTravel(player, input, FIXED_TIMESTEP, multiplier)) return false;
|
| 191 |
-
const length = Math.hypot(input.x, input.y);
|
| 192 |
-
if (length > 0) {
|
| 193 |
-
const travel = player.speed * multiplier * FIXED_TIMESTEP;
|
| 194 |
-
player.x += input.x / length * travel;
|
| 195 |
-
player.y += input.y / length * travel;
|
| 196 |
-
}
|
| 197 |
-
for (let index = 0; index < this.orbSlots.length; index += 1) {
|
| 198 |
-
if (orbs[index] && Math.hypot(this.orbSlots[index].point.x - player.x, this.orbSlots[index].point.y - player.y) <= 22) {
|
| 199 |
-
orbs[index] = false;
|
| 200 |
-
surge = 4;
|
| 201 |
-
}
|
| 202 |
-
}
|
| 203 |
-
}
|
| 204 |
-
return true;
|
| 205 |
-
}
|
| 206 |
-
|
| 207 |
-
private mergeResult(target: StepResult, next: StepResult): void {
|
| 208 |
-
target.captured ||= next.captured; target.cleared ||= next.cleared; target.timedOut ||= next.timedOut;
|
| 209 |
-
target.pelletsCollected.push(...next.pelletsCollected); target.powerCollected.push(...next.powerCollected);
|
| 210 |
-
}
|
| 211 |
-
|
| 212 |
-
private canTravel(actor: Actor, input: Point, duration: number, speedMultiplier = 1): boolean {
|
| 213 |
-
const length = Math.hypot(input.x, input.y);
|
| 214 |
-
if (length === 0) return true;
|
| 215 |
-
const distance = actor.speed * speedMultiplier * duration;
|
| 216 |
-
const steps = Math.max(1, Math.ceil(distance / 4));
|
| 217 |
-
for (let step = 1; step <= steps; step += 1) {
|
| 218 |
-
const candidate = { x: actor.x + input.x / length * distance * step / steps, y: actor.y + input.y / length * distance * step / steps };
|
| 219 |
-
if (isBlocked(this.arena, candidate, actor.radius)) return false;
|
| 220 |
-
}
|
| 221 |
-
return true;
|
| 222 |
-
}
|
| 223 |
-
|
| 224 |
-
private move(actor: Actor, input: Point, dt: number, speedMultiplier = 1): void {
|
| 225 |
-
const length = Math.hypot(input.x, input.y);
|
| 226 |
-
if (length === 0) return;
|
| 227 |
-
const speed = actor.speed * speedMultiplier;
|
| 228 |
-
const steps = Math.max(1, Math.ceil(speed * dt / 4));
|
| 229 |
-
const dx = input.x / length * speed * dt / steps;
|
| 230 |
-
const dy = input.y / length * speed * dt / steps;
|
| 231 |
-
for (let index = 0; index < steps; index += 1) {
|
| 232 |
-
const candidate = { x: actor.x + dx, y: actor.y + dy };
|
| 233 |
-
if (isBlocked(this.arena, candidate, actor.radius)) return;
|
| 234 |
-
actor.x = candidate.x; actor.y = candidate.y;
|
| 235 |
-
}
|
| 236 |
-
}
|
| 237 |
-
|
| 238 |
-
private velocityFor(input: Point, speed: number): Point {
|
| 239 |
-
const length = Math.hypot(input.x, input.y);
|
| 240 |
-
return length === 0 ? { x: 0, y: 0 } : { x: input.x / length * speed, y: input.y / length * speed };
|
| 241 |
-
}
|
| 242 |
-
|
| 243 |
-
private chooseBaselineAction(): Direction {
|
| 244 |
-
const choices = actionOrder.filter((direction) => this.canTravel(this.enemy, directions[direction], ENEMY_DECISION_INTERVAL));
|
| 245 |
-
if (choices.length === 0) return this.enemyDirection;
|
| 246 |
-
choices.sort((a, b) => {
|
| 247 |
-
const av = directions[a]; const bv = directions[b];
|
| 248 |
-
return Math.hypot(this.player.x - (this.enemy.x + av.x * 42), this.player.y - (this.enemy.y + av.y * 42)) - Math.hypot(this.player.x - (this.enemy.x + bv.x * 42), this.player.y - (this.enemy.y + bv.y * 42));
|
| 249 |
-
});
|
| 250 |
-
return choices[0];
|
| 251 |
-
}
|
| 252 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
import Phaser from 'phaser';
|
| 2 |
-
import './styles.css';
|
| 3 |
-
import { PlayScene } from './game/PlayScene.js';
|
| 4 |
-
|
| 5 |
-
new Phaser.Game({
|
| 6 |
-
type: Phaser.AUTO,
|
| 7 |
-
parent: 'game',
|
| 8 |
-
width: 800,
|
| 9 |
-
height: 640,
|
| 10 |
-
backgroundColor: '#071426',
|
| 11 |
-
scene: [PlayScene],
|
| 12 |
-
scale: {
|
| 13 |
-
mode: Phaser.Scale.FIT,
|
| 14 |
-
autoCenter: Phaser.Scale.CENTER_BOTH,
|
| 15 |
-
width: 800,
|
| 16 |
-
height: 640,
|
| 17 |
-
},
|
| 18 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/styles.css
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
:root { color: #e8f5ff; background: #071426; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
|
| 2 |
-
* { box-sizing: border-box; }
|
| 3 |
-
body { min-width: 320px; min-height: 100vh; margin: 0; display: grid; place-items: center; }
|
| 4 |
-
main { width: min(100vw, 1180px); padding: 16px; }
|
| 5 |
-
.app-shell { display: flex; align-items: flex-start; justify-content: center; gap: 14px; }
|
| 6 |
-
.game-column { width: min(100%, 800px); }
|
| 7 |
-
.game-toolbar { display: flex; justify-content: flex-end; gap: 8px; margin: 0 0 8px; }
|
| 8 |
-
.game-toolbar button { border: 1px solid #447b9b; border-radius: 7px; background: #173a55; color: #e8f5ff; cursor: pointer; font: inherit; font-size: 14px; padding: 7px 10px; }
|
| 9 |
-
.game-toolbar button:hover { background: #214d6d; }
|
| 10 |
-
.game-toolbar button:disabled { opacity: 0.55; cursor: default; }
|
| 11 |
-
#game { overflow: hidden; border: 1px solid #2d607e; border-radius: 14px; box-shadow: 0 20px 70px #0008; }
|
| 12 |
-
canvas { display: block; width: 100% !important; height: auto !important; }
|
| 13 |
-
#debug-panel { width: 300px; max-height: 640px; overflow: auto; border: 1px solid #2d607e; border-radius: 14px; background: #0a1c2d; box-shadow: 0 20px 70px #0008; padding: 16px; }
|
| 14 |
-
.debug-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; border-bottom: 1px solid #28546f; padding-bottom: 10px; }
|
| 15 |
-
.debug-heading h2 { color: #91efff; font-size: 17px; margin: 0; }
|
| 16 |
-
.debug-heading span { color: #8daaba; font-size: 11px; white-space: nowrap; }
|
| 17 |
-
#debug-content { color: #d9efff; font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; margin: 13px 0 0; white-space: pre-wrap; }
|
| 18 |
-
@media (max-width: 1080px) { .app-shell { align-items: stretch; flex-direction: column; } .game-column { width: 100%; } #debug-panel { width: 100%; max-height: 340px; } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"compilerOptions": {
|
| 3 |
-
"target": "ES2022",
|
| 4 |
-
"useDefineForClassFields": true,
|
| 5 |
-
"module": "ESNext",
|
| 6 |
-
"lib": ["ES2022", "DOM"],
|
| 7 |
-
"skipLibCheck": true,
|
| 8 |
-
"moduleResolution": "bundler",
|
| 9 |
-
"allowImportingTsExtensions": false,
|
| 10 |
-
"noEmit": true,
|
| 11 |
-
"strict": true
|
| 12 |
-
},
|
| 13 |
-
"include": ["src"]
|
| 14 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|