@vibemancer/core 1.0.9 → 1.0.11
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/dist/{chunk-OLGKLCCB.js → chunk-EO7JO2RZ.js} +199 -24
- package/dist/chunk-EO7JO2RZ.js.map +1 -0
- package/dist/{index-browser-BTHlrB_s.d.ts → index-browser-CM0uuzWp.d.ts} +156 -18
- package/dist/index-browser.d.ts +1 -1
- package/dist/index-browser.js +5 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.js +51 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/bot-compute-budget.ts +10 -0
- package/src/engine/bot-error-capture.ts +179 -0
- package/src/engine/manual-match.ts +2 -0
- package/src/engine/physics.ts +76 -0
- package/src/engine/sandbox-harness.ts +46 -0
- package/src/engine/sandbox.ts +347 -341
- package/src/engine/simulation.ts +120 -10
- package/src/engine-version.ts +1 -1
- package/src/hooks/action-builders.ts +112 -5
- package/src/hooks/state-hooks.ts +409 -407
- package/src/hooks/threat-analysis.ts +57 -18
- package/src/hooks/types.ts +16 -4
- package/src/rules.ts +8 -0
- package/src/types.ts +10 -6
- package/src/utils/combat.ts +379 -371
- package/dist/chunk-OLGKLCCB.js.map +0 -1
package/src/engine/sandbox.ts
CHANGED
|
@@ -1,341 +1,347 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VIBEMANCER — SANDBOX
|
|
3
|
-
*
|
|
4
|
-
* Provides isolated-vm sandboxing for bot code execution. Both bots + the
|
|
5
|
-
* entire simulation engine run inside a single V8 isolate, so there is ZERO
|
|
6
|
-
* per-tick boundary crossing overhead. The only data crossing the boundary
|
|
7
|
-
* is fight/simulate options going in and results coming out.
|
|
8
|
-
*
|
|
9
|
-
* Architecture:
|
|
10
|
-
* - Host: creates isolate, loads compiled bundle, calls __fight/__simulate
|
|
11
|
-
* - Isolate: contains both bots + full simulation engine, runs fight/simulate
|
|
12
|
-
*
|
|
13
|
-
* Safety:
|
|
14
|
-
* - Memory limit (default 512 MB) catches memory bombs
|
|
15
|
-
* - Timeout (default
|
|
16
|
-
* - Prototype freeze prevents cross-bot sabotage
|
|
17
|
-
* - platform: 'neutral' strips Node.js APIs (no fs/net/process)
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import ivm from 'isolated-vm';
|
|
21
|
-
import type {FightResult, SimulateResult} from './simulation.js';
|
|
22
|
-
import {BotBundle, compileMatchBundle} from './sandbox-compile.js';
|
|
23
|
-
import {DEFAULT_FIGHT_BACKSTOP_MS} from './bot-compute-budget.js';
|
|
24
|
-
import type {CompileOptions} from './sandbox-compile.js';
|
|
25
|
-
|
|
26
|
-
// Re-export BotBundle so existing imports from sandbox.ts keep working
|
|
27
|
-
export {BotBundle} from './sandbox-compile.js';
|
|
28
|
-
export {compileMatchBundle, compileManualMatchBundle} from './sandbox-compile.js';
|
|
29
|
-
export type {CompileOptions} from './sandbox-compile.js';
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Options for sandbox creation.
|
|
33
|
-
*/
|
|
34
|
-
export interface SandboxOptions
|
|
35
|
-
{
|
|
36
|
-
/** Memory limit in MB for the isolate (default: 512). */
|
|
37
|
-
memoryLimitMB?: number;
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
private
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* VIBEMANCER — SANDBOX
|
|
3
|
+
*
|
|
4
|
+
* Provides isolated-vm sandboxing for bot code execution. Both bots + the
|
|
5
|
+
* entire simulation engine run inside a single V8 isolate, so there is ZERO
|
|
6
|
+
* per-tick boundary crossing overhead. The only data crossing the boundary
|
|
7
|
+
* is fight/simulate options going in and results coming out.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* - Host: creates isolate, loads compiled bundle, calls __fight/__simulate
|
|
11
|
+
* - Isolate: contains both bots + full simulation engine, runs fight/simulate
|
|
12
|
+
*
|
|
13
|
+
* Safety:
|
|
14
|
+
* - Memory limit (default 512 MB) catches memory bombs
|
|
15
|
+
* - Timeout (default DEFAULT_FIGHT_BACKSTOP_MS) catches infinite loops
|
|
16
|
+
* - Prototype freeze prevents cross-bot sabotage
|
|
17
|
+
* - platform: 'neutral' strips Node.js APIs (no fs/net/process)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import ivm from 'isolated-vm';
|
|
21
|
+
import type {FightResult, SimulateResult} from './simulation.js';
|
|
22
|
+
import {BotBundle, compileMatchBundle} from './sandbox-compile.js';
|
|
23
|
+
import {DEFAULT_FIGHT_BACKSTOP_MS} from './bot-compute-budget.js';
|
|
24
|
+
import type {CompileOptions} from './sandbox-compile.js';
|
|
25
|
+
|
|
26
|
+
// Re-export BotBundle so existing imports from sandbox.ts keep working
|
|
27
|
+
export {BotBundle} from './sandbox-compile.js';
|
|
28
|
+
export {compileMatchBundle, compileManualMatchBundle} from './sandbox-compile.js';
|
|
29
|
+
export type {CompileOptions} from './sandbox-compile.js';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Options for sandbox creation.
|
|
33
|
+
*/
|
|
34
|
+
export interface SandboxOptions
|
|
35
|
+
{
|
|
36
|
+
/** Memory limit in MB for the isolate (default: 512). */
|
|
37
|
+
memoryLimitMB?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Timeout in ms for fight/simulate calls. Defaults to DEFAULT_FIGHT_BACKSTOP_MS.
|
|
40
|
+
*
|
|
41
|
+
* Named rather than written out, because this file previously gave THREE different
|
|
42
|
+
* numbers for one default — "30s" in the header, "60000" here, and the actual value used
|
|
43
|
+
* below — and a reader had no way to tell which was true.
|
|
44
|
+
*/
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
/** Options passed to esbuild compilation (aliases, externals). */
|
|
47
|
+
compileOptions?: CompileOptions;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A sandboxed match runner. Both bots + the entire simulation engine run
|
|
52
|
+
* inside a single isolated-vm isolate.
|
|
53
|
+
*
|
|
54
|
+
* Usage:
|
|
55
|
+
* ```ts
|
|
56
|
+
* const sandbox = await MatchSandbox.create(botA, botB);
|
|
57
|
+
* const result = sandbox.fight({ seed: 42 });
|
|
58
|
+
* sandbox.dispose();
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export class MatchSandbox
|
|
62
|
+
{
|
|
63
|
+
private isolate: ivm.Isolate;
|
|
64
|
+
private context: ivm.Context;
|
|
65
|
+
private fightFn: ivm.Reference;
|
|
66
|
+
private simulateFn: ivm.Reference;
|
|
67
|
+
private timeout: number;
|
|
68
|
+
private disposed = false;
|
|
69
|
+
|
|
70
|
+
private constructor(
|
|
71
|
+
isolate: ivm.Isolate,
|
|
72
|
+
context: ivm.Context,
|
|
73
|
+
fightFn: ivm.Reference,
|
|
74
|
+
simulateFn: ivm.Reference,
|
|
75
|
+
timeout: number,
|
|
76
|
+
)
|
|
77
|
+
{
|
|
78
|
+
this.isolate = isolate;
|
|
79
|
+
this.context = context;
|
|
80
|
+
this.fightFn = fightFn;
|
|
81
|
+
this.simulateFn = simulateFn;
|
|
82
|
+
this.timeout = timeout;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create a sandbox with both bots loaded. Compiles the match bundle
|
|
87
|
+
* automatically using esbuild.
|
|
88
|
+
*/
|
|
89
|
+
static async create(
|
|
90
|
+
bot1: BotBundle,
|
|
91
|
+
bot2: BotBundle,
|
|
92
|
+
options?: SandboxOptions,
|
|
93
|
+
): Promise<MatchSandbox>
|
|
94
|
+
{
|
|
95
|
+
const memoryLimitMB = options?.memoryLimitMB ?? 512;
|
|
96
|
+
// Was a bare 60000, which the per-bot compute budget made wrong: a budgeted fight can
|
|
97
|
+
// legitimately let one runaway bot spend 45s before it stops being called, and 60s
|
|
98
|
+
// here would kill the whole fight first — putting the wall clock back in charge of
|
|
99
|
+
// outcomes. Shared constant so this cannot drift from the other copies again.
|
|
100
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_FIGHT_BACKSTOP_MS;
|
|
101
|
+
|
|
102
|
+
// 1. Compile the match bundle
|
|
103
|
+
const bundle = await compileMatchBundle(bot1, bot2, options?.compileOptions);
|
|
104
|
+
|
|
105
|
+
// 2. Create isolate with memory limit
|
|
106
|
+
const isolate = new ivm.Isolate({memoryLimit: memoryLimitMB});
|
|
107
|
+
|
|
108
|
+
try
|
|
109
|
+
{
|
|
110
|
+
// 3. Create context and load the bundle
|
|
111
|
+
const context = await isolate.createContext();
|
|
112
|
+
const script = await isolate.compileScript(bundle, {filename: 'match-bundle.js'});
|
|
113
|
+
await script.run(context, {timeout: timeoutMs});
|
|
114
|
+
|
|
115
|
+
// 4. Get references to the exposed functions
|
|
116
|
+
const global = context.global;
|
|
117
|
+
const fightFn = await global.get('__fight', {reference: true});
|
|
118
|
+
const simulateFn = await global.get('__simulate', {reference: true});
|
|
119
|
+
|
|
120
|
+
return new MatchSandbox(isolate, context, fightFn, simulateFn, timeoutMs);
|
|
121
|
+
}
|
|
122
|
+
catch(error)
|
|
123
|
+
{
|
|
124
|
+
// OOM or timeout can auto-dispose the isolate, so guard the cleanup dispose.
|
|
125
|
+
try
|
|
126
|
+
{
|
|
127
|
+
isolate.dispose();
|
|
128
|
+
}
|
|
129
|
+
catch
|
|
130
|
+
{
|
|
131
|
+
// already disposed
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Create a sandbox from a pre-compiled bundle string.
|
|
139
|
+
* Useful for caching compiled bundles across multiple MatchSandbox instances.
|
|
140
|
+
*/
|
|
141
|
+
static async fromBundle(
|
|
142
|
+
bundle: string,
|
|
143
|
+
options?: SandboxOptions,
|
|
144
|
+
): Promise<MatchSandbox>
|
|
145
|
+
{
|
|
146
|
+
const memoryLimitMB = options?.memoryLimitMB ?? 512;
|
|
147
|
+
// Was a bare 60000, which the per-bot compute budget made wrong: a budgeted fight can
|
|
148
|
+
// legitimately let one runaway bot spend 45s before it stops being called, and 60s
|
|
149
|
+
// here would kill the whole fight first — putting the wall clock back in charge of
|
|
150
|
+
// outcomes. Shared constant so this cannot drift from the other copies again.
|
|
151
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_FIGHT_BACKSTOP_MS;
|
|
152
|
+
|
|
153
|
+
const isolate = new ivm.Isolate({memoryLimit: memoryLimitMB});
|
|
154
|
+
|
|
155
|
+
try
|
|
156
|
+
{
|
|
157
|
+
const context = await isolate.createContext();
|
|
158
|
+
const script = await isolate.compileScript(bundle, {filename: 'match-bundle.js'});
|
|
159
|
+
await script.run(context, {timeout: timeoutMs});
|
|
160
|
+
|
|
161
|
+
const global = context.global;
|
|
162
|
+
const fightFn = await global.get('__fight', {reference: true});
|
|
163
|
+
const simulateFn = await global.get('__simulate', {reference: true});
|
|
164
|
+
|
|
165
|
+
return new MatchSandbox(isolate, context, fightFn, simulateFn, timeoutMs);
|
|
166
|
+
}
|
|
167
|
+
catch(error)
|
|
168
|
+
{
|
|
169
|
+
try
|
|
170
|
+
{
|
|
171
|
+
isolate.dispose();
|
|
172
|
+
}
|
|
173
|
+
catch
|
|
174
|
+
{
|
|
175
|
+
// already disposed
|
|
176
|
+
}
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Compile a match bundle without creating an isolate.
|
|
183
|
+
* Returns the compiled JS string for caching/reuse.
|
|
184
|
+
*/
|
|
185
|
+
static async compile(bot1: BotBundle, bot2: BotBundle, compileOptions?: CompileOptions): Promise<string>
|
|
186
|
+
{
|
|
187
|
+
return compileMatchBundle(bot1, bot2, compileOptions);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Run a full fight (10 matches: 5 spawn distances x 2 sides).
|
|
192
|
+
* Synchronous after isolate creation — runs entirely inside the isolate.
|
|
193
|
+
*/
|
|
194
|
+
fight(options?: {seed?: number; maxTicks?: number}): FightResult
|
|
195
|
+
{
|
|
196
|
+
this.ensureNotDisposed();
|
|
197
|
+
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- isolated-vm returns unknown
|
|
199
|
+
return this.fightFn.applySync(undefined, [options ?? {}], {
|
|
200
|
+
arguments: {copy: true},
|
|
201
|
+
result: {copy: true},
|
|
202
|
+
timeout: this.timeout,
|
|
203
|
+
}) as FightResult;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Run a single simulation.
|
|
208
|
+
* Synchronous after isolate creation.
|
|
209
|
+
*
|
|
210
|
+
* @param options.params1 - useParam overrides for bot 1 (wizard-1)
|
|
211
|
+
* @param options.params2 - useParam overrides for bot 2 (wizard-2)
|
|
212
|
+
*/
|
|
213
|
+
simulate(options?: {
|
|
214
|
+
seed?: number;
|
|
215
|
+
maxTicks?: number;
|
|
216
|
+
spawnDistance?: number;
|
|
217
|
+
skipHistory?: boolean;
|
|
218
|
+
params1?: Record<string, number>;
|
|
219
|
+
params2?: Record<string, number>;
|
|
220
|
+
}): SimulateResult
|
|
221
|
+
{
|
|
222
|
+
this.ensureNotDisposed();
|
|
223
|
+
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- isolated-vm returns unknown
|
|
225
|
+
return this.simulateFn.applySync(undefined, [options ?? {}], {
|
|
226
|
+
arguments: {copy: true},
|
|
227
|
+
result: {copy: true},
|
|
228
|
+
timeout: this.timeout,
|
|
229
|
+
}) as SimulateResult;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Dispose the isolate and free all memory.
|
|
234
|
+
* The sandbox cannot be used after disposal.
|
|
235
|
+
*/
|
|
236
|
+
dispose(): void
|
|
237
|
+
{
|
|
238
|
+
if (!this.disposed)
|
|
239
|
+
{
|
|
240
|
+
this.disposed = true;
|
|
241
|
+
// OOM can auto-dispose the isolate, so guard all cleanup
|
|
242
|
+
try
|
|
243
|
+
{
|
|
244
|
+
this.fightFn.release();
|
|
245
|
+
}
|
|
246
|
+
catch
|
|
247
|
+
{
|
|
248
|
+
// isolate already disposed
|
|
249
|
+
}
|
|
250
|
+
try
|
|
251
|
+
{
|
|
252
|
+
this.simulateFn.release();
|
|
253
|
+
}
|
|
254
|
+
catch
|
|
255
|
+
{
|
|
256
|
+
// isolate already disposed
|
|
257
|
+
}
|
|
258
|
+
try
|
|
259
|
+
{
|
|
260
|
+
this.context.release();
|
|
261
|
+
}
|
|
262
|
+
catch
|
|
263
|
+
{
|
|
264
|
+
// isolate already disposed
|
|
265
|
+
}
|
|
266
|
+
try
|
|
267
|
+
{
|
|
268
|
+
this.isolate.dispose();
|
|
269
|
+
}
|
|
270
|
+
catch
|
|
271
|
+
{
|
|
272
|
+
// isolate already disposed
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Whether this sandbox has been disposed.
|
|
279
|
+
*/
|
|
280
|
+
get isDisposed(): boolean
|
|
281
|
+
{
|
|
282
|
+
return this.disposed;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private ensureNotDisposed(): void
|
|
286
|
+
{
|
|
287
|
+
if (this.disposed)
|
|
288
|
+
{
|
|
289
|
+
throw new Error('MatchSandbox has been disposed');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* One-shot sandboxed fight. Creates isolate, runs fight, disposes.
|
|
296
|
+
* Convenience wrapper for single-use scenarios.
|
|
297
|
+
*/
|
|
298
|
+
export async function sandboxFight(
|
|
299
|
+
bot1: BotBundle,
|
|
300
|
+
bot2: BotBundle,
|
|
301
|
+
options?: {seed?: number; maxTicks?: number} & SandboxOptions,
|
|
302
|
+
): Promise<FightResult>
|
|
303
|
+
{
|
|
304
|
+
const sandbox = await MatchSandbox.create(bot1, bot2, options);
|
|
305
|
+
try
|
|
306
|
+
{
|
|
307
|
+
return sandbox.fight({seed: options?.seed, maxTicks: options?.maxTicks});
|
|
308
|
+
}
|
|
309
|
+
finally
|
|
310
|
+
{
|
|
311
|
+
sandbox.dispose();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* One-shot sandboxed simulate. Creates isolate, runs simulate, disposes.
|
|
317
|
+
*/
|
|
318
|
+
export async function sandboxSimulate(
|
|
319
|
+
bot1: BotBundle,
|
|
320
|
+
bot2: BotBundle,
|
|
321
|
+
options?: {
|
|
322
|
+
seed?: number;
|
|
323
|
+
maxTicks?: number;
|
|
324
|
+
spawnDistance?: number;
|
|
325
|
+
skipHistory?: boolean;
|
|
326
|
+
params1?: Record<string, number>;
|
|
327
|
+
params2?: Record<string, number>;
|
|
328
|
+
} & SandboxOptions,
|
|
329
|
+
): Promise<SimulateResult>
|
|
330
|
+
{
|
|
331
|
+
const sandbox = await MatchSandbox.create(bot1, bot2, options);
|
|
332
|
+
try
|
|
333
|
+
{
|
|
334
|
+
return sandbox.simulate({
|
|
335
|
+
seed: options?.seed,
|
|
336
|
+
maxTicks: options?.maxTicks,
|
|
337
|
+
spawnDistance: options?.spawnDistance,
|
|
338
|
+
skipHistory: options?.skipHistory,
|
|
339
|
+
params1: options?.params1,
|
|
340
|
+
params2: options?.params2,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
finally
|
|
344
|
+
{
|
|
345
|
+
sandbox.dispose();
|
|
346
|
+
}
|
|
347
|
+
}
|