@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.
- package/README.md +28 -0
- package/dist/chunk-L7Z7OFXD.js +9140 -0
- package/dist/chunk-L7Z7OFXD.js.map +1 -0
- package/dist/index-browser.d.ts +2602 -0
- package/dist/index-browser.js +407 -0
- package/dist/index-browser.js.map +1 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.js +750 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
- package/src/bots/berserker/01_Stormchaser.ts +457 -0
- package/src/bots/berserker/02_Stormcaller.ts +417 -0
- package/src/bots/berserker/03_Stormforger.ts +481 -0
- package/src/bots/caster/01_Flamecaller.ts +286 -0
- package/src/bots/caster/02_Pyromancer.ts +350 -0
- package/src/bots/caster/03_Infernalist.ts +492 -0
- package/src/bots/defensive/01_Turtle.ts +151 -0
- package/src/bots/defensive/02_Sentinel.ts +134 -0
- package/src/bots/defensive/03_Golem.ts +357 -0
- package/src/bots/duelist/01_Battlemage.ts +433 -0
- package/src/bots/duelist/02_Warmage.ts +438 -0
- package/src/bots/duelist/03_Archmage.ts +588 -0
- package/src/bots/homing/01_Bonemancer.ts +67 -0
- package/src/bots/homing/02_Lich.ts +356 -0
- package/src/bots/homing/03_Archlich.ts +220 -0
- package/src/bots/index.ts +30 -0
- package/src/bots/kiter/01_Spellspinner.ts +398 -0
- package/src/bots/kiter/02_Spellweaver.ts +378 -0
- package/src/bots/kiter/03_Spellbinder.ts +448 -0
- package/src/bots/melee/01_Shadowblade.ts +270 -0
- package/src/bots/melee/02_Nightblade.ts +437 -0
- package/src/bots/melee/03_Voidblade.ts +582 -0
- package/src/bots/registry.ts +207 -0
- package/src/bots/shared.ts +472 -0
- package/src/bots/sniper/01_Spellshot.ts +385 -0
- package/src/bots/sniper/02_Spelltracer.ts +441 -0
- package/src/bots/sniper/03_Spellseeker.ts +546 -0
- package/src/bots/standalone/Critter.ts +89 -0
- package/src/bots/standalone/Doombringer.ts +91 -0
- package/src/bots/standalone/Hogger.ts +228 -0
- package/src/bots/standalone/Rookie.ts +50 -0
- package/src/bots/standalone/TargetDummy.ts +21 -0
- package/src/bots/test/cheater.ts +405 -0
- package/src/bots/test/crasher.ts +81 -0
- package/src/engine/hooks-runtime.ts +394 -0
- package/src/engine/manual-match.ts +289 -0
- package/src/engine/missile-templates.ts +155 -0
- package/src/engine/optimizer.ts +220 -0
- package/src/engine/params-runtime.ts +189 -0
- package/src/engine/physics.ts +143 -0
- package/src/engine/sandbox-browser.ts +671 -0
- package/src/engine/sandbox-compile.ts +197 -0
- package/src/engine/sandbox-harness.ts +367 -0
- package/src/engine/sandbox.ts +332 -0
- package/src/engine/simulation.ts +828 -0
- package/src/engine/spells.ts +128 -0
- package/src/engine-version.ts +11 -0
- package/src/hooks/action-builders.ts +210 -0
- package/src/hooks/bot-wrapper.ts +84 -0
- package/src/hooks/index.ts +75 -0
- package/src/hooks/state-hooks.ts +354 -0
- package/src/hooks/threat-analysis.ts +365 -0
- package/src/hooks/types.ts +142 -0
- package/src/index-browser.ts +30 -0
- package/src/index.ts +24 -0
- package/src/rules.ts +254 -0
- package/src/stats.ts +262 -0
- package/src/testing.ts +207 -0
- package/src/trace.ts +430 -0
- package/src/types.ts +193 -0
- package/src/utils/angles.ts +47 -0
- package/src/utils/combat.ts +279 -0
- package/src/utils/distance.ts +21 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/movement.ts +108 -0
- package/src/utils/random.ts +65 -0
- package/src/utils/spatial.ts +63 -0
- package/src/utils/targeting.ts +45 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ALL_BOTS,
|
|
3
|
+
ARENA_HEIGHT,
|
|
4
|
+
ARENA_SIZE,
|
|
5
|
+
ARENA_WATER_BUFFER,
|
|
6
|
+
ARENA_WIDTH,
|
|
7
|
+
Archlich,
|
|
8
|
+
Archmage,
|
|
9
|
+
BLINK_CAST_TIME,
|
|
10
|
+
BLINK_COOLDOWN,
|
|
11
|
+
BLINK_MAX_COOLDOWN,
|
|
12
|
+
BLINK_MAX_RANGE,
|
|
13
|
+
BLINK_MIN_COOLDOWN,
|
|
14
|
+
BLINK_RANGE,
|
|
15
|
+
BOT_GROUPS,
|
|
16
|
+
Battlemage,
|
|
17
|
+
Bonemancer,
|
|
18
|
+
BrowserManualMatchSandbox,
|
|
19
|
+
BrowserMatchSandbox,
|
|
20
|
+
CASTING_MOVEMENT_MULT,
|
|
21
|
+
COLLISION_RADIUS,
|
|
22
|
+
Critter,
|
|
23
|
+
Doombringer,
|
|
24
|
+
ENGINE_VERSION,
|
|
25
|
+
FIGHT_SPAWN_DISTANCES,
|
|
26
|
+
Flamecaller,
|
|
27
|
+
GCD_DURATION,
|
|
28
|
+
Golem,
|
|
29
|
+
Hogger,
|
|
30
|
+
Infernalist,
|
|
31
|
+
Lich,
|
|
32
|
+
MATCH_DURATION,
|
|
33
|
+
MAX_HEALTH,
|
|
34
|
+
MISSILE_BASE_CAST,
|
|
35
|
+
MISSILE_BASE_RADIUS,
|
|
36
|
+
MISSILE_DAMAGE_POWER,
|
|
37
|
+
MISSILE_DAMAGE_RADIUS_SCALE,
|
|
38
|
+
MISSILE_DAMAGE_SCALE,
|
|
39
|
+
MISSILE_HOMING_COEFF,
|
|
40
|
+
MISSILE_MIN_CAST_TIME,
|
|
41
|
+
MISSILE_MIN_DAMAGE,
|
|
42
|
+
MISSILE_MIN_DURATION,
|
|
43
|
+
MISSILE_MIN_SPEED,
|
|
44
|
+
MISSILE_RADIUS_PER_DAMAGE,
|
|
45
|
+
MISSILE_SPEED_DURATION_BASELINE,
|
|
46
|
+
MISSILE_SPEED_DURATION_COEFF,
|
|
47
|
+
MISSILE_TURN_DURATION_COEFF,
|
|
48
|
+
MOVEMENT_SPEED,
|
|
49
|
+
MOVE_SPEED,
|
|
50
|
+
ManualMatch,
|
|
51
|
+
Nightblade,
|
|
52
|
+
Pyromancer,
|
|
53
|
+
Rookie,
|
|
54
|
+
SHIELD_CAST_TIME,
|
|
55
|
+
SHIELD_DECAY_PER_SECOND,
|
|
56
|
+
SHIELD_DECAY_RATE,
|
|
57
|
+
SHIELD_MAX_BLOCK,
|
|
58
|
+
SHIELD_MAX_STRENGTH,
|
|
59
|
+
SHIELD_MIN_BLOCK,
|
|
60
|
+
SHIELD_MIN_STRENGTH,
|
|
61
|
+
SPAWN_DISTANCE,
|
|
62
|
+
Sentinel,
|
|
63
|
+
Shadowblade,
|
|
64
|
+
Spellbinder,
|
|
65
|
+
Spellseeker,
|
|
66
|
+
Spellshot,
|
|
67
|
+
Spellspinner,
|
|
68
|
+
Spelltracer,
|
|
69
|
+
Spellweaver,
|
|
70
|
+
Stormcaller,
|
|
71
|
+
Stormchaser,
|
|
72
|
+
Stormforger,
|
|
73
|
+
TICKS_PER_SECOND,
|
|
74
|
+
TICK_DURATION_MS,
|
|
75
|
+
TargetDummy,
|
|
76
|
+
Turtle,
|
|
77
|
+
Voidblade,
|
|
78
|
+
WARMUP_DURATION_TOLERANCE,
|
|
79
|
+
WARMUP_MAX_BONUS,
|
|
80
|
+
WARMUP_MAX_PENALTY,
|
|
81
|
+
WARMUP_SPEED_TOLERANCE,
|
|
82
|
+
WARMUP_TURN_TOLERANCE,
|
|
83
|
+
WIZARD_HEALTH,
|
|
84
|
+
WIZARD_RADIUS,
|
|
85
|
+
Warmage,
|
|
86
|
+
analyzeThreats,
|
|
87
|
+
angleDiff,
|
|
88
|
+
angleInRange,
|
|
89
|
+
angleTo,
|
|
90
|
+
antiHomingMissile,
|
|
91
|
+
applyDamage,
|
|
92
|
+
blink,
|
|
93
|
+
browserSandboxFight,
|
|
94
|
+
browserSandboxSimulate,
|
|
95
|
+
calculateBlinkCooldown,
|
|
96
|
+
calculateMissileCastTime,
|
|
97
|
+
calculateMissileRadius,
|
|
98
|
+
calculateMissileSimilarity,
|
|
99
|
+
calculateShieldBlock,
|
|
100
|
+
calculateWarmupMultiplier,
|
|
101
|
+
cancel,
|
|
102
|
+
cancelCast,
|
|
103
|
+
clampPositionToArena,
|
|
104
|
+
clampToArena,
|
|
105
|
+
clearHooks,
|
|
106
|
+
clearParamValues,
|
|
107
|
+
completeCast,
|
|
108
|
+
createEntitySeed,
|
|
109
|
+
createInitialState,
|
|
110
|
+
createRandom,
|
|
111
|
+
createWorkerScript,
|
|
112
|
+
diagnoseTrace,
|
|
113
|
+
directionAway,
|
|
114
|
+
directionTo,
|
|
115
|
+
distanceTo,
|
|
116
|
+
effectiveTurnRateCost,
|
|
117
|
+
extractAction,
|
|
118
|
+
extractStats,
|
|
119
|
+
extractTraceEvents,
|
|
120
|
+
fight,
|
|
121
|
+
findInRange,
|
|
122
|
+
findNearest,
|
|
123
|
+
fitMissileToBudget,
|
|
124
|
+
formatDiagnosis,
|
|
125
|
+
formatStats,
|
|
126
|
+
formatTraceEvents,
|
|
127
|
+
formatTraceSummary,
|
|
128
|
+
generateCandidates,
|
|
129
|
+
generateCombos,
|
|
130
|
+
getAdaptiveMissileConfig,
|
|
131
|
+
getBotContext,
|
|
132
|
+
getEffectiveRange,
|
|
133
|
+
getLeadPosition,
|
|
134
|
+
getMissileCastTime,
|
|
135
|
+
getPlayerState,
|
|
136
|
+
hashCombine,
|
|
137
|
+
homingMissile,
|
|
138
|
+
idle,
|
|
139
|
+
inRange,
|
|
140
|
+
interceptAngle,
|
|
141
|
+
isNewStyleBot,
|
|
142
|
+
magnitude,
|
|
143
|
+
missile,
|
|
144
|
+
move,
|
|
145
|
+
moveInDirection,
|
|
146
|
+
moveProjectile,
|
|
147
|
+
moveWizard,
|
|
148
|
+
narrowRange,
|
|
149
|
+
nextRandom,
|
|
150
|
+
normalize,
|
|
151
|
+
normalizeAngle,
|
|
152
|
+
predictPosition,
|
|
153
|
+
resetAllHooks,
|
|
154
|
+
resolveWizardCollision,
|
|
155
|
+
runBotWithContext,
|
|
156
|
+
runWithHooks,
|
|
157
|
+
scoreFight,
|
|
158
|
+
scoreFightAsWizard2,
|
|
159
|
+
seekerMissile,
|
|
160
|
+
setParamValues,
|
|
161
|
+
shield,
|
|
162
|
+
simulate,
|
|
163
|
+
sortByDistance,
|
|
164
|
+
spiralMissile,
|
|
165
|
+
startCast,
|
|
166
|
+
startDiscovery,
|
|
167
|
+
stopDiscovery,
|
|
168
|
+
straightMissile,
|
|
169
|
+
summarizeTrace,
|
|
170
|
+
sweptCircleCollision,
|
|
171
|
+
testBot,
|
|
172
|
+
tick,
|
|
173
|
+
updateShield,
|
|
174
|
+
useArenaSize,
|
|
175
|
+
useBlinkCooldown,
|
|
176
|
+
useCastProgress,
|
|
177
|
+
useCastingSpell,
|
|
178
|
+
useClosestThreat,
|
|
179
|
+
useDamageDealt,
|
|
180
|
+
useDamageTaken,
|
|
181
|
+
useEffect,
|
|
182
|
+
useEnemy,
|
|
183
|
+
useHealth,
|
|
184
|
+
useLastHitTick,
|
|
185
|
+
useMemo,
|
|
186
|
+
useMyProjectiles,
|
|
187
|
+
useMyThreatsToEnemy,
|
|
188
|
+
useParam,
|
|
189
|
+
usePosition,
|
|
190
|
+
useRef,
|
|
191
|
+
useShieldStrength,
|
|
192
|
+
useState,
|
|
193
|
+
useStatus,
|
|
194
|
+
useThreats,
|
|
195
|
+
useTick,
|
|
196
|
+
useTicksUntilReady,
|
|
197
|
+
useVelocity,
|
|
198
|
+
validateHookCall,
|
|
199
|
+
validateMissileConfig,
|
|
200
|
+
withBotContext,
|
|
201
|
+
wrapNewBot,
|
|
202
|
+
wrapWithParams
|
|
203
|
+
} from "./chunk-L7Z7OFXD.js";
|
|
204
|
+
|
|
205
|
+
// src/engine/sandbox.ts
|
|
206
|
+
import ivm from "isolated-vm";
|
|
207
|
+
|
|
208
|
+
// src/engine/sandbox-compile.ts
|
|
209
|
+
import { build } from "esbuild";
|
|
210
|
+
import path from "path";
|
|
211
|
+
import fs from "fs";
|
|
212
|
+
import { fileURLToPath } from "url";
|
|
213
|
+
|
|
214
|
+
// src/engine/sandbox-harness.ts
|
|
215
|
+
var PROTOTYPE_FREEZE_BANNER = `
|
|
216
|
+
Object.freeze(Object.prototype);
|
|
217
|
+
Object.freeze(Array.prototype);
|
|
218
|
+
Object.freeze(Function.prototype);
|
|
219
|
+
Object.freeze(String.prototype);
|
|
220
|
+
Object.freeze(Number.prototype);
|
|
221
|
+
Object.freeze(Boolean.prototype);
|
|
222
|
+
Object.freeze(RegExp.prototype);
|
|
223
|
+
Object.freeze(Date.prototype);
|
|
224
|
+
Object.freeze(Error.prototype);
|
|
225
|
+
Object.freeze(Map.prototype);
|
|
226
|
+
Object.freeze(Set.prototype);
|
|
227
|
+
Object.freeze(Math);
|
|
228
|
+
Object.freeze(JSON);
|
|
229
|
+
(function() {
|
|
230
|
+
var g = typeof globalThis !== 'undefined' ? globalThis : typeof self !== 'undefined' ? self : {};
|
|
231
|
+
var blocked = [
|
|
232
|
+
'fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource',
|
|
233
|
+
'importScripts', 'Worker', 'SharedWorker',
|
|
234
|
+
'Request', 'Response', 'Headers',
|
|
235
|
+
'navigator', 'BroadcastChannel',
|
|
236
|
+
'indexedDB', 'caches'
|
|
237
|
+
];
|
|
238
|
+
for (var i = 0; i < blocked.length; i++) {
|
|
239
|
+
try { Object.defineProperty(g, blocked[i], {value: undefined, writable: false, configurable: false}); }
|
|
240
|
+
catch(e) {}
|
|
241
|
+
}
|
|
242
|
+
})();
|
|
243
|
+
`;
|
|
244
|
+
function generateMatchEntryPoint(bot1SourcePath, bot1ExportName, bot2SourcePath, bot2ExportName, engineDir) {
|
|
245
|
+
const enginePath = engineDir.replace(/\\/g, "/");
|
|
246
|
+
const bot1Path = bot1SourcePath.replace(/\\/g, "/");
|
|
247
|
+
const bot2Path = bot2SourcePath.replace(/\\/g, "/");
|
|
248
|
+
const srcPath = enginePath.replace(/\/engine\/?$/, "");
|
|
249
|
+
return `
|
|
250
|
+
import {${bot1ExportName} as __RawBot1} from '${bot1Path}';
|
|
251
|
+
import {${bot2ExportName} as __RawBot2} from '${bot2Path}';
|
|
252
|
+
import {fight, simulate} from '${srcPath}/engine/simulation.ts';
|
|
253
|
+
import {wrapWithParams} from '${srcPath}/engine/params-runtime.ts';
|
|
254
|
+
import {wrapNewBot} from '${srcPath}/hooks/bot-wrapper.ts';
|
|
255
|
+
|
|
256
|
+
// Auto-detect new-style bots (hooks API) and wrap them for the simulation engine.
|
|
257
|
+
// New-style: uses hooks, returns FinalAction (has _toAction method).
|
|
258
|
+
// Old-style: accepts ({state, config, random}), returns WizardActions directly.
|
|
259
|
+
// We detect by doing a dry-run call \u2014 if the result has _toAction, it's new-style.
|
|
260
|
+
//
|
|
261
|
+
// Detection is deferred until the first fight/simulate call so that malicious
|
|
262
|
+
// bots (infinite loops, memory bombs) time out during fight(), not during
|
|
263
|
+
// sandbox creation. __resolveBots memoizes the wrapped functions.
|
|
264
|
+
function __wrap(bot) {
|
|
265
|
+
if (bot.length > 0) return bot; // Old-style: has parameters
|
|
266
|
+
// 0-param function could be either style. Try calling it to check return type.
|
|
267
|
+
try {
|
|
268
|
+
var result = bot();
|
|
269
|
+
if (result && typeof result._toAction === 'function') {
|
|
270
|
+
return wrapNewBot(bot);
|
|
271
|
+
}
|
|
272
|
+
} catch(e) {
|
|
273
|
+
// If it throws (e.g. hooks not initialized), it must be new-style
|
|
274
|
+
return wrapNewBot(bot);
|
|
275
|
+
}
|
|
276
|
+
return bot;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
var __Bot1 = null;
|
|
280
|
+
var __Bot2 = null;
|
|
281
|
+
function __resolveBots() {
|
|
282
|
+
if (__Bot1 === null) __Bot1 = __wrap(__RawBot1);
|
|
283
|
+
if (__Bot2 === null) __Bot2 = __wrap(__RawBot2);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
globalThis.__fight = function __fight(options) {
|
|
287
|
+
__resolveBots();
|
|
288
|
+
const result = fight(__Bot1, __Bot2, options);
|
|
289
|
+
return result;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
globalThis.__simulate = function __simulate(options) {
|
|
293
|
+
__resolveBots();
|
|
294
|
+
const {params1, params2, ...simOptions} = options;
|
|
295
|
+
const bot1 = params1 ? wrapWithParams(__Bot1, params1) : __Bot1;
|
|
296
|
+
const bot2 = params2 ? wrapWithParams(__Bot2, params2) : __Bot2;
|
|
297
|
+
const result = simulate(bot1, bot2, simOptions);
|
|
298
|
+
return result;
|
|
299
|
+
};
|
|
300
|
+
`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/engine/sandbox-compile.ts
|
|
304
|
+
var currentFilename = fileURLToPath(import.meta.url);
|
|
305
|
+
var currentDirname = path.dirname(currentFilename);
|
|
306
|
+
function getEngineDir() {
|
|
307
|
+
const directPath = path.resolve(currentDirname);
|
|
308
|
+
if (fs.existsSync(path.join(directPath, "simulation.ts"))) {
|
|
309
|
+
return directPath;
|
|
310
|
+
}
|
|
311
|
+
const packageRoot2 = path.resolve(currentDirname, "..", "..");
|
|
312
|
+
const srcEngine2 = path.join(packageRoot2, "src", "engine");
|
|
313
|
+
if (fs.existsSync(path.join(srcEngine2, "simulation.ts"))) {
|
|
314
|
+
return srcEngine2;
|
|
315
|
+
}
|
|
316
|
+
const packageRoot1 = path.resolve(currentDirname, "..");
|
|
317
|
+
const srcEngine1 = path.join(packageRoot1, "src", "engine");
|
|
318
|
+
if (fs.existsSync(path.join(srcEngine1, "simulation.ts"))) {
|
|
319
|
+
return srcEngine1;
|
|
320
|
+
}
|
|
321
|
+
throw new Error("Could not find engine source directory (src/engine/simulation.ts)");
|
|
322
|
+
}
|
|
323
|
+
var BotBundle = class {
|
|
324
|
+
sourcePath;
|
|
325
|
+
exportName;
|
|
326
|
+
constructor(sourcePath, exportName) {
|
|
327
|
+
if (!sourcePath || typeof sourcePath !== "string") {
|
|
328
|
+
throw new Error("sourcePath must be a non-empty string");
|
|
329
|
+
}
|
|
330
|
+
if (!exportName || typeof exportName !== "string") {
|
|
331
|
+
throw new Error("exportName must be a non-empty string");
|
|
332
|
+
}
|
|
333
|
+
this.sourcePath = path.resolve(sourcePath);
|
|
334
|
+
this.exportName = exportName;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
async function compileMatchBundle(bot1, bot2, options) {
|
|
338
|
+
const engineDir = getEngineDir();
|
|
339
|
+
const entryPoint = generateMatchEntryPoint(
|
|
340
|
+
bot1.sourcePath,
|
|
341
|
+
bot1.exportName,
|
|
342
|
+
bot2.sourcePath,
|
|
343
|
+
bot2.exportName,
|
|
344
|
+
engineDir
|
|
345
|
+
);
|
|
346
|
+
const result = await build({
|
|
347
|
+
stdin: {
|
|
348
|
+
contents: entryPoint,
|
|
349
|
+
resolveDir: engineDir,
|
|
350
|
+
loader: "ts"
|
|
351
|
+
},
|
|
352
|
+
bundle: true,
|
|
353
|
+
write: false,
|
|
354
|
+
format: "iife",
|
|
355
|
+
platform: "neutral",
|
|
356
|
+
target: "es2022",
|
|
357
|
+
banner: {
|
|
358
|
+
js: PROTOTYPE_FREEZE_BANNER
|
|
359
|
+
},
|
|
360
|
+
// Suppress warnings about top-level this in ESM
|
|
361
|
+
logLevel: "error",
|
|
362
|
+
// Match bundles run in sandboxed environments (Web Workers / isolated-vm)
|
|
363
|
+
// and should never include Node.js native modules
|
|
364
|
+
external: [
|
|
365
|
+
"isolated-vm",
|
|
366
|
+
"esbuild",
|
|
367
|
+
"node:path",
|
|
368
|
+
"node:fs",
|
|
369
|
+
"node:url",
|
|
370
|
+
"node:worker_threads",
|
|
371
|
+
"node:crypto",
|
|
372
|
+
"node:os",
|
|
373
|
+
"node:child_process",
|
|
374
|
+
...options?.external ?? []
|
|
375
|
+
],
|
|
376
|
+
...options?.alias ? { alias: options.alias } : {}
|
|
377
|
+
});
|
|
378
|
+
if (!result.outputFiles?.[0]) {
|
|
379
|
+
throw new Error("esbuild produced no output");
|
|
380
|
+
}
|
|
381
|
+
return result.outputFiles[0].text;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/engine/sandbox.ts
|
|
385
|
+
var MatchSandbox = class _MatchSandbox {
|
|
386
|
+
isolate;
|
|
387
|
+
context;
|
|
388
|
+
fightFn;
|
|
389
|
+
simulateFn;
|
|
390
|
+
timeout;
|
|
391
|
+
disposed = false;
|
|
392
|
+
constructor(isolate, context, fightFn, simulateFn, timeout) {
|
|
393
|
+
this.isolate = isolate;
|
|
394
|
+
this.context = context;
|
|
395
|
+
this.fightFn = fightFn;
|
|
396
|
+
this.simulateFn = simulateFn;
|
|
397
|
+
this.timeout = timeout;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Create a sandbox with both bots loaded. Compiles the match bundle
|
|
401
|
+
* automatically using esbuild.
|
|
402
|
+
*/
|
|
403
|
+
static async create(bot1, bot2, options) {
|
|
404
|
+
const memoryLimitMB = options?.memoryLimitMB ?? 512;
|
|
405
|
+
const timeoutMs = options?.timeoutMs ?? 3e4;
|
|
406
|
+
const bundle = await compileMatchBundle(bot1, bot2, options?.compileOptions);
|
|
407
|
+
const isolate = new ivm.Isolate({ memoryLimit: memoryLimitMB });
|
|
408
|
+
try {
|
|
409
|
+
const context = await isolate.createContext();
|
|
410
|
+
const script = await isolate.compileScript(bundle, { filename: "match-bundle.js" });
|
|
411
|
+
await script.run(context, { timeout: timeoutMs });
|
|
412
|
+
const global = context.global;
|
|
413
|
+
const fightFn = await global.get("__fight", { reference: true });
|
|
414
|
+
const simulateFn = await global.get("__simulate", { reference: true });
|
|
415
|
+
return new _MatchSandbox(isolate, context, fightFn, simulateFn, timeoutMs);
|
|
416
|
+
} catch (error) {
|
|
417
|
+
try {
|
|
418
|
+
isolate.dispose();
|
|
419
|
+
} catch {
|
|
420
|
+
}
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Create a sandbox from a pre-compiled bundle string.
|
|
426
|
+
* Useful for caching compiled bundles across multiple MatchSandbox instances.
|
|
427
|
+
*/
|
|
428
|
+
static async fromBundle(bundle, options) {
|
|
429
|
+
const memoryLimitMB = options?.memoryLimitMB ?? 512;
|
|
430
|
+
const timeoutMs = options?.timeoutMs ?? 3e4;
|
|
431
|
+
const isolate = new ivm.Isolate({ memoryLimit: memoryLimitMB });
|
|
432
|
+
try {
|
|
433
|
+
const context = await isolate.createContext();
|
|
434
|
+
const script = await isolate.compileScript(bundle, { filename: "match-bundle.js" });
|
|
435
|
+
await script.run(context, { timeout: timeoutMs });
|
|
436
|
+
const global = context.global;
|
|
437
|
+
const fightFn = await global.get("__fight", { reference: true });
|
|
438
|
+
const simulateFn = await global.get("__simulate", { reference: true });
|
|
439
|
+
return new _MatchSandbox(isolate, context, fightFn, simulateFn, timeoutMs);
|
|
440
|
+
} catch (error) {
|
|
441
|
+
try {
|
|
442
|
+
isolate.dispose();
|
|
443
|
+
} catch {
|
|
444
|
+
}
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Compile a match bundle without creating an isolate.
|
|
450
|
+
* Returns the compiled JS string for caching/reuse.
|
|
451
|
+
*/
|
|
452
|
+
static async compile(bot1, bot2, compileOptions) {
|
|
453
|
+
return compileMatchBundle(bot1, bot2, compileOptions);
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Run a full fight (10 matches: 5 spawn distances x 2 sides).
|
|
457
|
+
* Synchronous after isolate creation — runs entirely inside the isolate.
|
|
458
|
+
*/
|
|
459
|
+
fight(options) {
|
|
460
|
+
this.ensureNotDisposed();
|
|
461
|
+
return this.fightFn.applySync(void 0, [options ?? {}], {
|
|
462
|
+
arguments: { copy: true },
|
|
463
|
+
result: { copy: true },
|
|
464
|
+
timeout: this.timeout
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Run a single simulation.
|
|
469
|
+
* Synchronous after isolate creation.
|
|
470
|
+
*
|
|
471
|
+
* @param options.params1 - useParam overrides for bot 1 (wizard-1)
|
|
472
|
+
* @param options.params2 - useParam overrides for bot 2 (wizard-2)
|
|
473
|
+
*/
|
|
474
|
+
simulate(options) {
|
|
475
|
+
this.ensureNotDisposed();
|
|
476
|
+
return this.simulateFn.applySync(void 0, [options ?? {}], {
|
|
477
|
+
arguments: { copy: true },
|
|
478
|
+
result: { copy: true },
|
|
479
|
+
timeout: this.timeout
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Dispose the isolate and free all memory.
|
|
484
|
+
* The sandbox cannot be used after disposal.
|
|
485
|
+
*/
|
|
486
|
+
dispose() {
|
|
487
|
+
if (!this.disposed) {
|
|
488
|
+
this.disposed = true;
|
|
489
|
+
try {
|
|
490
|
+
this.fightFn.release();
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
this.simulateFn.release();
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
this.context.release();
|
|
499
|
+
} catch {
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
this.isolate.dispose();
|
|
503
|
+
} catch {
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Whether this sandbox has been disposed.
|
|
509
|
+
*/
|
|
510
|
+
get isDisposed() {
|
|
511
|
+
return this.disposed;
|
|
512
|
+
}
|
|
513
|
+
ensureNotDisposed() {
|
|
514
|
+
if (this.disposed) {
|
|
515
|
+
throw new Error("MatchSandbox has been disposed");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
async function sandboxFight(bot1, bot2, options) {
|
|
520
|
+
const sandbox = await MatchSandbox.create(bot1, bot2, options);
|
|
521
|
+
try {
|
|
522
|
+
return sandbox.fight({ seed: options?.seed, maxTicks: options?.maxTicks });
|
|
523
|
+
} finally {
|
|
524
|
+
sandbox.dispose();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async function sandboxSimulate(bot1, bot2, options) {
|
|
528
|
+
const sandbox = await MatchSandbox.create(bot1, bot2, options);
|
|
529
|
+
try {
|
|
530
|
+
return sandbox.simulate({
|
|
531
|
+
seed: options?.seed,
|
|
532
|
+
maxTicks: options?.maxTicks,
|
|
533
|
+
spawnDistance: options?.spawnDistance,
|
|
534
|
+
skipHistory: options?.skipHistory,
|
|
535
|
+
params1: options?.params1,
|
|
536
|
+
params2: options?.params2
|
|
537
|
+
});
|
|
538
|
+
} finally {
|
|
539
|
+
sandbox.dispose();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
export {
|
|
543
|
+
ALL_BOTS,
|
|
544
|
+
ARENA_HEIGHT,
|
|
545
|
+
ARENA_SIZE,
|
|
546
|
+
ARENA_WATER_BUFFER,
|
|
547
|
+
ARENA_WIDTH,
|
|
548
|
+
Archlich,
|
|
549
|
+
Archmage,
|
|
550
|
+
BLINK_CAST_TIME,
|
|
551
|
+
BLINK_COOLDOWN,
|
|
552
|
+
BLINK_MAX_COOLDOWN,
|
|
553
|
+
BLINK_MAX_RANGE,
|
|
554
|
+
BLINK_MIN_COOLDOWN,
|
|
555
|
+
BLINK_RANGE,
|
|
556
|
+
BOT_GROUPS,
|
|
557
|
+
Battlemage,
|
|
558
|
+
Bonemancer,
|
|
559
|
+
BotBundle,
|
|
560
|
+
BrowserManualMatchSandbox,
|
|
561
|
+
BrowserMatchSandbox,
|
|
562
|
+
CASTING_MOVEMENT_MULT,
|
|
563
|
+
COLLISION_RADIUS,
|
|
564
|
+
Critter,
|
|
565
|
+
Doombringer,
|
|
566
|
+
ENGINE_VERSION,
|
|
567
|
+
FIGHT_SPAWN_DISTANCES,
|
|
568
|
+
Flamecaller,
|
|
569
|
+
GCD_DURATION,
|
|
570
|
+
Golem,
|
|
571
|
+
Hogger,
|
|
572
|
+
Infernalist,
|
|
573
|
+
Lich,
|
|
574
|
+
MATCH_DURATION,
|
|
575
|
+
MAX_HEALTH,
|
|
576
|
+
MISSILE_BASE_CAST,
|
|
577
|
+
MISSILE_BASE_RADIUS,
|
|
578
|
+
MISSILE_DAMAGE_POWER,
|
|
579
|
+
MISSILE_DAMAGE_RADIUS_SCALE,
|
|
580
|
+
MISSILE_DAMAGE_SCALE,
|
|
581
|
+
MISSILE_HOMING_COEFF,
|
|
582
|
+
MISSILE_MIN_CAST_TIME,
|
|
583
|
+
MISSILE_MIN_DAMAGE,
|
|
584
|
+
MISSILE_MIN_DURATION,
|
|
585
|
+
MISSILE_MIN_SPEED,
|
|
586
|
+
MISSILE_RADIUS_PER_DAMAGE,
|
|
587
|
+
MISSILE_SPEED_DURATION_BASELINE,
|
|
588
|
+
MISSILE_SPEED_DURATION_COEFF,
|
|
589
|
+
MISSILE_TURN_DURATION_COEFF,
|
|
590
|
+
MOVEMENT_SPEED,
|
|
591
|
+
MOVE_SPEED,
|
|
592
|
+
ManualMatch,
|
|
593
|
+
MatchSandbox,
|
|
594
|
+
Nightblade,
|
|
595
|
+
Pyromancer,
|
|
596
|
+
Rookie,
|
|
597
|
+
SHIELD_CAST_TIME,
|
|
598
|
+
SHIELD_DECAY_PER_SECOND,
|
|
599
|
+
SHIELD_DECAY_RATE,
|
|
600
|
+
SHIELD_MAX_BLOCK,
|
|
601
|
+
SHIELD_MAX_STRENGTH,
|
|
602
|
+
SHIELD_MIN_BLOCK,
|
|
603
|
+
SHIELD_MIN_STRENGTH,
|
|
604
|
+
SPAWN_DISTANCE,
|
|
605
|
+
Sentinel,
|
|
606
|
+
Shadowblade,
|
|
607
|
+
Spellbinder,
|
|
608
|
+
Spellseeker,
|
|
609
|
+
Spellshot,
|
|
610
|
+
Spellspinner,
|
|
611
|
+
Spelltracer,
|
|
612
|
+
Spellweaver,
|
|
613
|
+
Stormcaller,
|
|
614
|
+
Stormchaser,
|
|
615
|
+
Stormforger,
|
|
616
|
+
TICKS_PER_SECOND,
|
|
617
|
+
TICK_DURATION_MS,
|
|
618
|
+
TargetDummy,
|
|
619
|
+
Turtle,
|
|
620
|
+
Voidblade,
|
|
621
|
+
WARMUP_DURATION_TOLERANCE,
|
|
622
|
+
WARMUP_MAX_BONUS,
|
|
623
|
+
WARMUP_MAX_PENALTY,
|
|
624
|
+
WARMUP_SPEED_TOLERANCE,
|
|
625
|
+
WARMUP_TURN_TOLERANCE,
|
|
626
|
+
WIZARD_HEALTH,
|
|
627
|
+
WIZARD_RADIUS,
|
|
628
|
+
Warmage,
|
|
629
|
+
analyzeThreats,
|
|
630
|
+
angleDiff,
|
|
631
|
+
angleInRange,
|
|
632
|
+
angleTo,
|
|
633
|
+
antiHomingMissile,
|
|
634
|
+
applyDamage,
|
|
635
|
+
blink,
|
|
636
|
+
browserSandboxFight,
|
|
637
|
+
browserSandboxSimulate,
|
|
638
|
+
calculateBlinkCooldown,
|
|
639
|
+
calculateMissileCastTime,
|
|
640
|
+
calculateMissileRadius,
|
|
641
|
+
calculateMissileSimilarity,
|
|
642
|
+
calculateShieldBlock,
|
|
643
|
+
calculateWarmupMultiplier,
|
|
644
|
+
cancel,
|
|
645
|
+
cancelCast,
|
|
646
|
+
clampPositionToArena,
|
|
647
|
+
clampToArena,
|
|
648
|
+
clearHooks,
|
|
649
|
+
clearParamValues,
|
|
650
|
+
compileMatchBundle,
|
|
651
|
+
completeCast,
|
|
652
|
+
createEntitySeed,
|
|
653
|
+
createInitialState,
|
|
654
|
+
createRandom,
|
|
655
|
+
createWorkerScript,
|
|
656
|
+
diagnoseTrace,
|
|
657
|
+
directionAway,
|
|
658
|
+
directionTo,
|
|
659
|
+
distanceTo,
|
|
660
|
+
effectiveTurnRateCost,
|
|
661
|
+
extractAction,
|
|
662
|
+
extractStats,
|
|
663
|
+
extractTraceEvents,
|
|
664
|
+
fight,
|
|
665
|
+
findInRange,
|
|
666
|
+
findNearest,
|
|
667
|
+
fitMissileToBudget,
|
|
668
|
+
formatDiagnosis,
|
|
669
|
+
formatStats,
|
|
670
|
+
formatTraceEvents,
|
|
671
|
+
formatTraceSummary,
|
|
672
|
+
generateCandidates,
|
|
673
|
+
generateCombos,
|
|
674
|
+
getAdaptiveMissileConfig,
|
|
675
|
+
getBotContext,
|
|
676
|
+
getEffectiveRange,
|
|
677
|
+
getLeadPosition,
|
|
678
|
+
getMissileCastTime,
|
|
679
|
+
getPlayerState,
|
|
680
|
+
hashCombine,
|
|
681
|
+
homingMissile,
|
|
682
|
+
idle,
|
|
683
|
+
inRange,
|
|
684
|
+
interceptAngle,
|
|
685
|
+
isNewStyleBot,
|
|
686
|
+
magnitude,
|
|
687
|
+
missile,
|
|
688
|
+
move,
|
|
689
|
+
moveInDirection,
|
|
690
|
+
moveProjectile,
|
|
691
|
+
moveWizard,
|
|
692
|
+
narrowRange,
|
|
693
|
+
nextRandom,
|
|
694
|
+
normalize,
|
|
695
|
+
normalizeAngle,
|
|
696
|
+
predictPosition,
|
|
697
|
+
resetAllHooks,
|
|
698
|
+
resolveWizardCollision,
|
|
699
|
+
runBotWithContext,
|
|
700
|
+
runWithHooks,
|
|
701
|
+
sandboxFight,
|
|
702
|
+
sandboxSimulate,
|
|
703
|
+
scoreFight,
|
|
704
|
+
scoreFightAsWizard2,
|
|
705
|
+
seekerMissile,
|
|
706
|
+
setParamValues,
|
|
707
|
+
shield,
|
|
708
|
+
simulate,
|
|
709
|
+
sortByDistance,
|
|
710
|
+
spiralMissile,
|
|
711
|
+
startCast,
|
|
712
|
+
startDiscovery,
|
|
713
|
+
stopDiscovery,
|
|
714
|
+
straightMissile,
|
|
715
|
+
summarizeTrace,
|
|
716
|
+
sweptCircleCollision,
|
|
717
|
+
testBot,
|
|
718
|
+
tick,
|
|
719
|
+
updateShield,
|
|
720
|
+
useArenaSize,
|
|
721
|
+
useBlinkCooldown,
|
|
722
|
+
useCastProgress,
|
|
723
|
+
useCastingSpell,
|
|
724
|
+
useClosestThreat,
|
|
725
|
+
useDamageDealt,
|
|
726
|
+
useDamageTaken,
|
|
727
|
+
useEffect,
|
|
728
|
+
useEnemy,
|
|
729
|
+
useHealth,
|
|
730
|
+
useLastHitTick,
|
|
731
|
+
useMemo,
|
|
732
|
+
useMyProjectiles,
|
|
733
|
+
useMyThreatsToEnemy,
|
|
734
|
+
useParam,
|
|
735
|
+
usePosition,
|
|
736
|
+
useRef,
|
|
737
|
+
useShieldStrength,
|
|
738
|
+
useState,
|
|
739
|
+
useStatus,
|
|
740
|
+
useThreats,
|
|
741
|
+
useTick,
|
|
742
|
+
useTicksUntilReady,
|
|
743
|
+
useVelocity,
|
|
744
|
+
validateHookCall,
|
|
745
|
+
validateMissileConfig,
|
|
746
|
+
withBotContext,
|
|
747
|
+
wrapNewBot,
|
|
748
|
+
wrapWithParams
|
|
749
|
+
};
|
|
750
|
+
//# sourceMappingURL=index.js.map
|