@vibemancer/core 0.1.4 → 0.1.6
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 +2 -2
- package/dist/chunk-G5T65F3W.js +10617 -0
- package/dist/chunk-G5T65F3W.js.map +1 -0
- package/dist/index-browser.d.ts +1328 -1051
- package/dist/index-browser.js +59 -17
- package/dist/index.d.ts +53 -3
- package/dist/index.js +204 -54
- package/dist/index.js.map +1 -1
- package/package.json +21 -15
- package/src/banned-bot-names.ts +53 -0
- package/src/bots/Hero.ts +867 -0
- package/src/bots/berserker/01_Stormchaser.ts +162 -120
- package/src/bots/berserker/02_Stormcaller.ts +160 -116
- package/src/bots/berserker/03_Stormforger.ts +162 -129
- package/src/bots/caster/01_Flamecaller.ts +126 -84
- package/src/bots/caster/02_Pyromancer.ts +148 -101
- package/src/bots/caster/03_Infernalist.ts +180 -160
- package/src/bots/defensive/01_Turtle.ts +48 -44
- package/src/bots/defensive/02_Sentinel.ts +57 -53
- package/src/bots/defensive/03_Golem.ts +85 -97
- package/src/bots/duelist/01_Battlemage.ts +157 -122
- package/src/bots/duelist/02_Warmage.ts +166 -121
- package/src/bots/duelist/03_Archmage.ts +198 -178
- package/src/bots/homing/01_Bonemancer.ts +20 -32
- package/src/bots/homing/02_Lich.ts +145 -93
- package/src/bots/homing/03_Archlich.ts +129 -60
- package/src/bots/kiter/01_Spellspinner.ts +145 -107
- package/src/bots/kiter/02_Spellweaver.ts +153 -105
- package/src/bots/kiter/03_Spellbinder.ts +168 -123
- package/src/bots/melee/01_Shadowblade.ts +131 -75
- package/src/bots/melee/02_Nightblade.ts +174 -130
- package/src/bots/melee/03_Voidblade.ts +266 -168
- package/src/bots/registry.ts +44 -37
- package/src/bots/shared.ts +185 -25
- package/src/bots/sniper/01_Spellshot.ts +151 -98
- package/src/bots/sniper/02_Spelltracer.ts +173 -128
- package/src/bots/sniper/03_Spellseeker.ts +177 -152
- package/src/bots/standalone/Critter.ts +46 -52
- package/src/bots/standalone/Doombringer.ts +36 -40
- package/src/bots/standalone/Hogger.ts +120 -89
- package/src/bots/standalone/Rookie.ts +21 -23
- package/src/bots/standalone/TargetDummy.ts +5 -6
- package/src/bots/test/cheater.ts +91 -85
- package/src/bots/test/crasher.ts +26 -28
- package/src/engine/bundle-fight.ts +242 -0
- package/src/engine/hooks-runtime.ts +58 -18
- package/src/engine/manual-match.ts +33 -25
- package/src/engine/missile-templates.ts +25 -43
- package/src/engine/optimizer.ts +7 -7
- package/src/engine/params-runtime.ts +3 -3
- package/src/engine/physics.ts +33 -23
- package/src/engine/sandbox-browser.ts +8 -7
- package/src/engine/sandbox-compile.ts +1 -1
- package/src/engine/sandbox-harness.ts +299 -367
- package/src/engine/sandbox.ts +3 -3
- package/src/engine/simulation.ts +291 -76
- package/src/engine/spells.ts +9 -12
- package/src/engine-version.ts +1 -1
- package/src/hooks/action-builders.ts +76 -21
- package/src/hooks/index.ts +17 -9
- package/src/hooks/state-hooks.ts +61 -25
- package/src/hooks/threat-analysis.ts +44 -20
- package/src/hooks/types.ts +62 -5
- package/src/index-browser.ts +1 -0
- package/src/index.ts +3 -0
- package/src/rules.ts +209 -63
- package/src/stats.ts +2 -2
- package/src/testing.ts +6 -30
- package/src/trace.ts +63 -3
- package/src/types.ts +127 -14
- package/src/utils/angles.ts +1 -0
- package/src/utils/combat.ts +98 -6
- package/src/utils/spatial.ts +3 -3
- package/dist/chunk-74FFJCFF.js +0 -9140
- package/dist/chunk-74FFJCFF.js.map +0 -1
- package/src/hooks/bot-wrapper.ts +0 -84
package/dist/index-browser.d.ts
CHANGED
|
@@ -1,3 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VIBEMANCER - HOOKS API TYPES
|
|
3
|
+
*
|
|
4
|
+
* Types for the hooks-based bot API.
|
|
5
|
+
*
|
|
6
|
+
* UNITS REFERENCE (100 ticks = 1 second):
|
|
7
|
+
* Position: absolute world coordinates, 0-800 on each axis (800×800 arena)
|
|
8
|
+
* Velocity: units per tick on each axis (player max speed = 1 u/t)
|
|
9
|
+
* Health: hit points (max 60)
|
|
10
|
+
* Ticks: game ticks (10ms each, 100/sec). Divide by 100 for seconds.
|
|
11
|
+
* Angles: degrees (0°=right, 90°=down, 180°=left, 270°=up)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Enemy wizard state as seen by your bot.
|
|
16
|
+
*
|
|
17
|
+
* Note: you cannot see the enemy's missile configs, cooldown timers, or
|
|
18
|
+
* damage history — only what's visible on the battlefield.
|
|
19
|
+
*/
|
|
20
|
+
interface EnemyState {
|
|
21
|
+
/** Enemy position in world coordinates (0-800). */
|
|
22
|
+
position: Position;
|
|
23
|
+
/** Enemy velocity in units/tick. */
|
|
24
|
+
velocity: Velocity;
|
|
25
|
+
/** Enemy current HP (0-60). */
|
|
26
|
+
health: number;
|
|
27
|
+
/** Enemy status: 'idle', 'casting', 'channeling' (shield), or 'gcd_locked'. */
|
|
28
|
+
status: 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
29
|
+
/** Which spell enemy is casting, or null. */
|
|
30
|
+
castingSpell: 'missile' | 'shield' | 'blink' | null;
|
|
31
|
+
/** Cast progress in ticks (0 if not casting). */
|
|
32
|
+
castProgress: number;
|
|
33
|
+
/** Total cast duration in ticks (0 if not casting). */
|
|
34
|
+
castDuration: number;
|
|
35
|
+
/** Remaining GCD ticks (0 if not in GCD). */
|
|
36
|
+
gcdRemaining: number;
|
|
37
|
+
/** Duration the enemy has been channeling in ticks (0 if not channeling). */
|
|
38
|
+
channelDuration: number;
|
|
39
|
+
/** Enemy shield block multiplier (0 if not shielding, 0.3-0.9 if shielding). */
|
|
40
|
+
shieldStrength: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Pre-computed analysis of an incoming enemy projectile.
|
|
44
|
+
*
|
|
45
|
+
* All timing values are in ticks (100 ticks = 1 second).
|
|
46
|
+
* Dodge directions are relative to missile heading, not world axes.
|
|
47
|
+
*/
|
|
48
|
+
interface AnalyzedThreat {
|
|
49
|
+
/** Unique projectile ID. */
|
|
50
|
+
id: string;
|
|
51
|
+
/** Raw projectile state (position, rotation in degrees, speed in u/t, turnRate, remainingTicks). */
|
|
52
|
+
projectile: ProjectileState;
|
|
53
|
+
/** Ticks until missile hits your current position. Infinity if predicted to miss. */
|
|
54
|
+
ticksToImpact: number;
|
|
55
|
+
/** Whether the missile will hit if you stand still. */
|
|
56
|
+
willHit: boolean;
|
|
57
|
+
/** Whether strafing left (perpendicular to missile heading) avoids it. */
|
|
58
|
+
canDodgeLeft: boolean;
|
|
59
|
+
/** Whether strafing right (perpendicular to missile heading) avoids it. */
|
|
60
|
+
canDodgeRight: boolean;
|
|
61
|
+
/** Whether moving directly away from the missile avoids it. */
|
|
62
|
+
canOutrun: boolean;
|
|
63
|
+
/** Optimal dodge direction as a unit vector {x, y}, or null if undodgeable. */
|
|
64
|
+
bestDodgeDirection: Position | null;
|
|
65
|
+
/** Whether you can channel shield before the missile arrives. */
|
|
66
|
+
canBlockInTime: boolean;
|
|
67
|
+
/** Ticks from now when you should START channeling shield to block in time. */
|
|
68
|
+
ticksToStartShield: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Final action that can be returned from a bot.
|
|
72
|
+
* Cannot be further chained.
|
|
73
|
+
*/
|
|
74
|
+
interface FinalAction {
|
|
75
|
+
/** Internal: extract the WizardActions */
|
|
76
|
+
readonly _toAction: () => WizardActions;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Action builder that allows chaining .move() for simultaneous movement.
|
|
80
|
+
* Returned by shield(), missile(), and cancel().
|
|
81
|
+
*/
|
|
82
|
+
interface ActionBuilder extends FinalAction {
|
|
83
|
+
/**
|
|
84
|
+
* Add movement to this action (e.g., move while casting).
|
|
85
|
+
* Direction vector, not absolute position. Auto-normalized.
|
|
86
|
+
* Positive X = right, positive Y = down.
|
|
87
|
+
*/
|
|
88
|
+
move(x: number, y: number): FinalAction;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Wizard function type for the hooks API.
|
|
92
|
+
* Called every tick. Read state with hooks, return an action.
|
|
93
|
+
*/
|
|
94
|
+
type WizardFunction = () => FinalAction;
|
|
95
|
+
/**
|
|
96
|
+
* Internal context for game state hooks.
|
|
97
|
+
*/
|
|
98
|
+
interface WizardContext {
|
|
99
|
+
entityId: string;
|
|
100
|
+
tick: number;
|
|
101
|
+
position: Position;
|
|
102
|
+
velocity: Velocity;
|
|
103
|
+
health: number;
|
|
104
|
+
maxHealth: number;
|
|
105
|
+
state: 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
106
|
+
castingSpell?: 'missile' | 'shield' | 'blink';
|
|
107
|
+
castProgress?: number;
|
|
108
|
+
castDuration?: number;
|
|
109
|
+
channelingSpell?: 'shield';
|
|
110
|
+
channelDuration?: number;
|
|
111
|
+
gcdRemaining?: number;
|
|
112
|
+
blinkCooldown: number;
|
|
113
|
+
lastMissileConfig?: MissileConfig;
|
|
114
|
+
enemies: Array<{
|
|
115
|
+
id: string;
|
|
116
|
+
position: Position;
|
|
117
|
+
velocity: Velocity;
|
|
118
|
+
health: number;
|
|
119
|
+
state: 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
120
|
+
castingSpell?: 'missile' | 'shield' | 'blink';
|
|
121
|
+
castProgress?: number;
|
|
122
|
+
castDuration?: number;
|
|
123
|
+
gcdRemaining?: number;
|
|
124
|
+
channelingSpell?: 'shield';
|
|
125
|
+
channelDuration?: number;
|
|
126
|
+
}>;
|
|
127
|
+
projectiles: ProjectileState[];
|
|
128
|
+
myProjectiles: ProjectileState[];
|
|
129
|
+
arenaWidth: number;
|
|
130
|
+
arenaHeight: number;
|
|
131
|
+
damageDealt: number;
|
|
132
|
+
damageTaken: number;
|
|
133
|
+
lastHitTick: number;
|
|
134
|
+
random: () => number;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Context available to missile AI functions via getMissileContext().
|
|
138
|
+
* Provides missile state, the full game state from the owner's perspective,
|
|
139
|
+
* and a seeded PRNG.
|
|
140
|
+
*/
|
|
141
|
+
interface MissileContext {
|
|
142
|
+
/** Missile position in world coordinates. */
|
|
143
|
+
position: Position;
|
|
144
|
+
/** Missile heading in degrees (0=right, 90=down). */
|
|
145
|
+
rotation: number;
|
|
146
|
+
/** Missile speed in units/tick. */
|
|
147
|
+
speed: number;
|
|
148
|
+
/** Missile turn rate in degrees/tick. */
|
|
149
|
+
turnRate: number;
|
|
150
|
+
/** Missile damage on hit. */
|
|
151
|
+
damage: number;
|
|
152
|
+
/** Ticks remaining before the missile expires. */
|
|
153
|
+
remainingTicks: number;
|
|
154
|
+
/** ID of the wizard who owns this missile. */
|
|
155
|
+
ownerId: string;
|
|
156
|
+
/** Full game state from the missile owner's perspective. */
|
|
157
|
+
worldState: GameState;
|
|
158
|
+
/** Seeded PRNG [0, 1). Deterministic per missile per tick. */
|
|
159
|
+
random: () => number;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Action returned by a missile AI function.
|
|
163
|
+
* Call turnToward(x, y) to steer, or flyStraight() to coast.
|
|
164
|
+
*/
|
|
165
|
+
interface MissileAction {
|
|
166
|
+
/** Internal: extract the MissileActions */
|
|
167
|
+
readonly _toMissileAction: () => MissileActions;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Missile AI function type for the hooks API.
|
|
171
|
+
* Called every tick for each in-flight missile.
|
|
172
|
+
* Read state with getMissileContext(), return a MissileAction.
|
|
173
|
+
*/
|
|
174
|
+
type MissileFunction$1 = () => MissileAction;
|
|
175
|
+
|
|
1
176
|
/**
|
|
2
177
|
* VIBEMANCER - TYPES
|
|
3
178
|
*
|
|
@@ -90,6 +265,8 @@ interface GameState {
|
|
|
90
265
|
damageDealt: number;
|
|
91
266
|
damageTaken: number;
|
|
92
267
|
lastHitTick: number;
|
|
268
|
+
/** Events emitted during the most recent tick (empty for the initial state). */
|
|
269
|
+
events: SimEvent[];
|
|
93
270
|
}
|
|
94
271
|
/**
|
|
95
272
|
* Actions returned by wizard each tick.
|
|
@@ -116,7 +293,7 @@ interface WizardActions {
|
|
|
116
293
|
startCast?: {
|
|
117
294
|
spell: 'missile';
|
|
118
295
|
config: MissileConfig;
|
|
119
|
-
missileAI:
|
|
296
|
+
missileAI: MissileFunction;
|
|
120
297
|
direction?: number;
|
|
121
298
|
} | {
|
|
122
299
|
spell: 'shield';
|
|
@@ -134,6 +311,22 @@ interface WizardActions {
|
|
|
134
311
|
* Only applies while state === 'casting' and castingSpell === 'missile'.
|
|
135
312
|
*/
|
|
136
313
|
aimDirection?: number;
|
|
314
|
+
/**
|
|
315
|
+
* Missile guide — manual play mode only. Takes direct control of a
|
|
316
|
+
* specific missile using WASD/joystick input. While active, the wizard
|
|
317
|
+
* is invulnerable and frozen (move ignored), and the missile steers
|
|
318
|
+
* using its turnRate toward the given direction.
|
|
319
|
+
*
|
|
320
|
+
* direction: {x, y} → converted to target angle via atan2(y, x)
|
|
321
|
+
* direction: null → missile flies straight (no steering input)
|
|
322
|
+
*/
|
|
323
|
+
missileGuide?: {
|
|
324
|
+
missileId: string;
|
|
325
|
+
direction: {
|
|
326
|
+
x: number;
|
|
327
|
+
y: number;
|
|
328
|
+
} | null;
|
|
329
|
+
} | null;
|
|
137
330
|
}
|
|
138
331
|
/**
|
|
139
332
|
* Missile configuration.
|
|
@@ -145,80 +338,184 @@ interface MissileConfig {
|
|
|
145
338
|
duration: number;
|
|
146
339
|
}
|
|
147
340
|
/**
|
|
148
|
-
* Missile AI function type.
|
|
341
|
+
* Missile AI function type — re-exported from hooks/types.ts.
|
|
149
342
|
*/
|
|
150
|
-
type
|
|
151
|
-
missileState: ProjectileState;
|
|
152
|
-
worldState: GameState;
|
|
153
|
-
random: () => number;
|
|
154
|
-
}) => MissileActions;
|
|
343
|
+
type MissileFunction = MissileFunction$1;
|
|
155
344
|
/**
|
|
156
345
|
* Actions returned by missile each tick.
|
|
157
346
|
*/
|
|
158
347
|
interface MissileActions {
|
|
159
348
|
turnToward?: Position;
|
|
349
|
+
turnToAngle?: number;
|
|
160
350
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
351
|
+
interface MissileHitEvent {
|
|
352
|
+
type: 'missile-hit';
|
|
353
|
+
position: Position;
|
|
354
|
+
damage: number;
|
|
355
|
+
actualDamage: number;
|
|
356
|
+
speed: number;
|
|
357
|
+
ownerId: string;
|
|
358
|
+
targetId: string;
|
|
359
|
+
}
|
|
360
|
+
interface MissileExpiredEvent {
|
|
361
|
+
type: 'missile-expired';
|
|
362
|
+
position: Position;
|
|
363
|
+
}
|
|
364
|
+
interface MissileOobEvent {
|
|
365
|
+
type: 'missile-oob';
|
|
366
|
+
position: Position;
|
|
367
|
+
}
|
|
368
|
+
interface BlinkEvent {
|
|
369
|
+
type: 'blink';
|
|
370
|
+
wizardId: string;
|
|
371
|
+
from: Position;
|
|
372
|
+
to: Position;
|
|
373
|
+
}
|
|
374
|
+
interface ShieldStartEvent {
|
|
375
|
+
type: 'shield-start';
|
|
376
|
+
wizardId: string;
|
|
377
|
+
position: Position;
|
|
378
|
+
}
|
|
379
|
+
interface CastStartEvent {
|
|
380
|
+
type: 'cast-start';
|
|
381
|
+
wizardId: string;
|
|
382
|
+
position: Position;
|
|
383
|
+
spell: 'missile' | 'shield' | 'blink';
|
|
384
|
+
}
|
|
385
|
+
interface WizardDeathEvent {
|
|
386
|
+
type: 'wizard-death';
|
|
387
|
+
wizardId: string;
|
|
388
|
+
position: Position;
|
|
389
|
+
}
|
|
390
|
+
interface WizardLavaDeathEvent {
|
|
391
|
+
type: 'wizard-lava-death';
|
|
392
|
+
wizardId: string;
|
|
393
|
+
position: Position;
|
|
394
|
+
}
|
|
395
|
+
interface CastCancelEvent {
|
|
396
|
+
type: 'cast-cancel';
|
|
397
|
+
wizardId: string;
|
|
398
|
+
position: Position;
|
|
399
|
+
spell: 'missile' | 'shield' | 'blink';
|
|
400
|
+
}
|
|
401
|
+
interface MissileLaunchEvent {
|
|
402
|
+
type: 'missile-launch';
|
|
403
|
+
position: Position;
|
|
404
|
+
damage: number;
|
|
405
|
+
speed: number;
|
|
406
|
+
ownerId: string;
|
|
407
|
+
rotation: number;
|
|
408
|
+
}
|
|
409
|
+
interface ShieldBlockEvent {
|
|
410
|
+
type: 'shield-block';
|
|
411
|
+
wizardId: string;
|
|
412
|
+
position: Position;
|
|
413
|
+
damageBlocked: number;
|
|
414
|
+
damageThrough: number;
|
|
415
|
+
}
|
|
416
|
+
type SimEvent = MissileHitEvent | MissileExpiredEvent | MissileOobEvent | MissileLaunchEvent | BlinkEvent | ShieldStartEvent | ShieldBlockEvent | CastStartEvent | CastCancelEvent | WizardDeathEvent | WizardLavaDeathEvent;
|
|
169
417
|
|
|
170
418
|
/**
|
|
171
419
|
* VIBEMANCER - GAME RULES
|
|
172
420
|
*
|
|
173
421
|
* This is the single source of truth for all game constants.
|
|
174
422
|
* All game logic imports from here. Read this to understand the game.
|
|
423
|
+
*
|
|
424
|
+
* SWEEPABLE CONSTANTS — the balance-search ruleset:
|
|
425
|
+
* Every combat-balance constant lives on the mutable `RULES` object. The engine
|
|
426
|
+
* and bots read `RULES.X` so the balance-search optimizer can override any of
|
|
427
|
+
* them at runtime via applyRulesetOverrides() — object property reads are live
|
|
428
|
+
* across every module and survive bundling (unlike a reassigned `let`, which
|
|
429
|
+
* esbuild/Vite snapshot at the import site). Structural constants (arena size,
|
|
430
|
+
* tick rate, hitbox radius, missile floors) stay plain `const`.
|
|
431
|
+
*
|
|
432
|
+
* The UPPER_CASE named exports below (WIZARD_HEALTH, SHIELD_CAST_TIME, …) are
|
|
433
|
+
* default SNAPSHOTS for external/UI/MCP consumers that only need the factory
|
|
434
|
+
* value. They do NOT track overrides — anything that must respond to a sweep
|
|
435
|
+
* reads `RULES.X`.
|
|
175
436
|
*/
|
|
176
437
|
|
|
177
438
|
declare const TICKS_PER_SECOND = 100;
|
|
178
439
|
declare const TICK_DURATION_MS = 10;
|
|
179
|
-
|
|
440
|
+
/**
|
|
441
|
+
* The mutable ruleset: the single source of truth for every sweepable combat
|
|
442
|
+
* constant. Engine + bots read these via `RULES.X`. Override with
|
|
443
|
+
* applyRulesetOverrides(); restore with resetRuleset().
|
|
444
|
+
*/
|
|
445
|
+
declare const RULES: {
|
|
446
|
+
WIZARD_HEALTH: number;
|
|
447
|
+
MOVEMENT_SPEED: number;
|
|
448
|
+
CASTING_MOVEMENT_MULT: number;
|
|
449
|
+
GCD_DURATION: number;
|
|
450
|
+
SHIELD_CAST_TIME: number;
|
|
451
|
+
SHIELD_MAX_BLOCK: number;
|
|
452
|
+
SHIELD_DECAY_PER_SECOND: number;
|
|
453
|
+
SHIELD_MIN_BLOCK: number;
|
|
454
|
+
BLINK_CAST_TIME: number;
|
|
455
|
+
BLINK_RANGE: number;
|
|
456
|
+
BLINK_MAX_COOLDOWN: number;
|
|
457
|
+
BLINK_MIN_COOLDOWN: number;
|
|
458
|
+
KNOCKBACK_DAMAGE_THRESHOLD: number;
|
|
459
|
+
KNOCKBACK_SPEED_PER_DAMAGE: number;
|
|
460
|
+
KNOCKBACK_DELAY: number;
|
|
461
|
+
KNOCKBACK_DECAY: number;
|
|
462
|
+
MISSILE_MIN_CAST_TIME: number;
|
|
463
|
+
MISSILE_BASE_RADIUS: number;
|
|
464
|
+
MISSILE_DAMAGE_RADIUS_SCALE: number;
|
|
465
|
+
MISSILE_BASE_CAST: number;
|
|
466
|
+
MISSILE_DAMAGE_SCALE: number;
|
|
467
|
+
MISSILE_DAMAGE_POWER: number;
|
|
468
|
+
MISSILE_HOMING_COEFF: number;
|
|
469
|
+
MISSILE_TURN_DURATION_COEFF: number;
|
|
470
|
+
MISSILE_SPEED_DURATION_BASELINE: number;
|
|
471
|
+
MISSILE_SPEED_DURATION_COEFF: number;
|
|
472
|
+
WARMUP_MAX_BONUS: number;
|
|
473
|
+
WARMUP_MAX_PENALTY: number;
|
|
474
|
+
};
|
|
475
|
+
declare const WIZARD_HEALTH: number;
|
|
180
476
|
declare const WIZARD_RADIUS = 5;
|
|
181
|
-
declare const MOVEMENT_SPEED
|
|
182
|
-
declare const CASTING_MOVEMENT_MULT
|
|
183
|
-
declare const GCD_DURATION
|
|
184
|
-
declare const SHIELD_CAST_TIME
|
|
185
|
-
declare const SHIELD_MAX_BLOCK
|
|
186
|
-
declare const SHIELD_DECAY_PER_SECOND
|
|
187
|
-
declare const SHIELD_MIN_BLOCK
|
|
188
|
-
declare const BLINK_CAST_TIME
|
|
189
|
-
declare const BLINK_RANGE
|
|
190
|
-
declare const BLINK_MAX_COOLDOWN
|
|
191
|
-
declare const BLINK_MIN_COOLDOWN
|
|
477
|
+
declare const MOVEMENT_SPEED: number;
|
|
478
|
+
declare const CASTING_MOVEMENT_MULT: number;
|
|
479
|
+
declare const GCD_DURATION: number;
|
|
480
|
+
declare const SHIELD_CAST_TIME: number;
|
|
481
|
+
declare const SHIELD_MAX_BLOCK: number;
|
|
482
|
+
declare const SHIELD_DECAY_PER_SECOND: number;
|
|
483
|
+
declare const SHIELD_MIN_BLOCK: number;
|
|
484
|
+
declare const BLINK_CAST_TIME: number;
|
|
485
|
+
declare const BLINK_RANGE: number;
|
|
486
|
+
declare const BLINK_MAX_COOLDOWN: number;
|
|
487
|
+
declare const BLINK_MIN_COOLDOWN: number;
|
|
192
488
|
/** @deprecated Use BLINK_MAX_COOLDOWN */
|
|
193
|
-
declare const BLINK_COOLDOWN
|
|
194
|
-
declare const
|
|
195
|
-
declare const
|
|
196
|
-
declare const
|
|
489
|
+
declare const BLINK_COOLDOWN: number;
|
|
490
|
+
declare const KNOCKBACK_DAMAGE_THRESHOLD: number;
|
|
491
|
+
declare const KNOCKBACK_SPEED_PER_DAMAGE: number;
|
|
492
|
+
declare const KNOCKBACK_DELAY: number;
|
|
493
|
+
declare const KNOCKBACK_DECAY: number;
|
|
494
|
+
declare const ARENA_SIZE = 860;
|
|
495
|
+
declare const LAVA_BORDER_WIDTH = 30;
|
|
496
|
+
declare const ARENA_MIN = 30;
|
|
497
|
+
declare const ARENA_MAX: number;
|
|
197
498
|
declare const SPAWN_DISTANCE = 600;
|
|
198
499
|
declare const MISSILE_MIN_DAMAGE = 1;
|
|
199
500
|
declare const MISSILE_MIN_SPEED = 1.5;
|
|
200
501
|
declare const MISSILE_MIN_DURATION = 10;
|
|
201
|
-
declare const MISSILE_MIN_CAST_TIME
|
|
202
|
-
declare const MISSILE_BASE_RADIUS
|
|
203
|
-
declare const MISSILE_DAMAGE_RADIUS_SCALE
|
|
502
|
+
declare const MISSILE_MIN_CAST_TIME: number;
|
|
503
|
+
declare const MISSILE_BASE_RADIUS: number;
|
|
504
|
+
declare const MISSILE_DAMAGE_RADIUS_SCALE: number;
|
|
204
505
|
/**
|
|
205
506
|
* Calculate missile hitbox radius based on damage.
|
|
206
507
|
*/
|
|
207
508
|
declare function calculateMissileRadius(damage: number): number;
|
|
208
|
-
declare const MISSILE_BASE_CAST
|
|
209
|
-
declare const MISSILE_DAMAGE_SCALE
|
|
509
|
+
declare const MISSILE_BASE_CAST: number;
|
|
510
|
+
declare const MISSILE_DAMAGE_SCALE: number;
|
|
210
511
|
declare const MISSILE_DAMAGE_POWER: number;
|
|
211
|
-
declare const MISSILE_HOMING_COEFF
|
|
212
|
-
declare const MISSILE_TURN_DURATION_COEFF
|
|
213
|
-
declare const MISSILE_SPEED_DURATION_BASELINE
|
|
214
|
-
declare const MISSILE_SPEED_DURATION_COEFF
|
|
512
|
+
declare const MISSILE_HOMING_COEFF: number;
|
|
513
|
+
declare const MISSILE_TURN_DURATION_COEFF: number;
|
|
514
|
+
declare const MISSILE_SPEED_DURATION_BASELINE: number;
|
|
515
|
+
declare const MISSILE_SPEED_DURATION_COEFF: number;
|
|
215
516
|
/**
|
|
216
517
|
* Maps turnRate to effective cost for the cast time formula.
|
|
217
|
-
*
|
|
218
|
-
* - Negative: diminishing returns via -|t|/(1+|t|), saturating at -1.
|
|
219
|
-
* turnRate -0.5 → -0.33, -1 → -0.5, -5 → -0.83, -10 → -0.91
|
|
220
|
-
* Big cast speed gains from 0 to -1, worthwhile to -5, negligible after.
|
|
221
|
-
* No hard floor — the curve naturally caps the benefit.
|
|
518
|
+
* Higher turn rate = more expensive cast. Negative values clamped to 0.
|
|
222
519
|
*/
|
|
223
520
|
declare function effectiveTurnRateCost(turnRate: number): number;
|
|
224
521
|
/**
|
|
@@ -236,15 +533,15 @@ declare function validateMissileConfig(config: MissileConfig): MissileConfig;
|
|
|
236
533
|
* + 0.10 × (effectiveTurnRateCost(turnRate) × durationSeconds)
|
|
237
534
|
* + 0.025 × (speed × durationSeconds - 1.5)
|
|
238
535
|
*
|
|
239
|
-
*
|
|
536
|
+
* turnRate is clamped to >= 0. Cost is linear.
|
|
240
537
|
*
|
|
241
538
|
* If lastMissileConfig is a MissileConfig, applies warmup multiplier:
|
|
242
539
|
* - Similar to previous: up to 20% faster
|
|
243
540
|
* - Very different: up to 20% slower (switching penalty)
|
|
244
541
|
*/
|
|
245
542
|
declare function calculateMissileCastTime(config: MissileConfig, lastMissileConfig?: MissileConfig | undefined | null): number;
|
|
246
|
-
declare const WARMUP_MAX_BONUS
|
|
247
|
-
declare const WARMUP_MAX_PENALTY
|
|
543
|
+
declare const WARMUP_MAX_BONUS: number;
|
|
544
|
+
declare const WARMUP_MAX_PENALTY: number;
|
|
248
545
|
declare const WARMUP_SPEED_TOLERANCE = 3;
|
|
249
546
|
declare const WARMUP_TURN_TOLERANCE = 1;
|
|
250
547
|
declare const WARMUP_DURATION_TOLERANCE = 50;
|
|
@@ -268,20 +565,35 @@ declare function calculateMissileSimilarity(prev: MissileConfig | undefined, cur
|
|
|
268
565
|
*/
|
|
269
566
|
declare function calculateWarmupMultiplier(prev: MissileConfig | undefined, current: MissileConfig): number;
|
|
270
567
|
declare const MATCH_DURATION = 30000;
|
|
271
|
-
declare const MAX_HEALTH
|
|
568
|
+
declare const MAX_HEALTH: number;
|
|
272
569
|
declare const COLLISION_RADIUS = 5;
|
|
273
|
-
declare const MOVE_SPEED
|
|
274
|
-
declare const MISSILE_RADIUS_PER_DAMAGE
|
|
275
|
-
declare const SHIELD_MAX_STRENGTH
|
|
276
|
-
declare const SHIELD_MIN_STRENGTH
|
|
277
|
-
declare const SHIELD_DECAY_RATE
|
|
278
|
-
declare const BLINK_MAX_RANGE
|
|
570
|
+
declare const MOVE_SPEED: number;
|
|
571
|
+
declare const MISSILE_RADIUS_PER_DAMAGE: number;
|
|
572
|
+
declare const SHIELD_MAX_STRENGTH: number;
|
|
573
|
+
declare const SHIELD_MIN_STRENGTH: number;
|
|
574
|
+
declare const SHIELD_DECAY_RATE: number;
|
|
575
|
+
declare const BLINK_MAX_RANGE: number;
|
|
279
576
|
/**
|
|
280
577
|
* Calculate blink cooldown based on distance traveled.
|
|
281
578
|
* Short blinks get short cooldowns, full-range blinks get the maximum.
|
|
282
579
|
*/
|
|
283
580
|
declare function calculateBlinkCooldown(distance: number): number;
|
|
284
581
|
declare const ARENA_WATER_BUFFER = 200;
|
|
582
|
+
/**
|
|
583
|
+
* Search bounds for each sweepable constant. The current value is the start.
|
|
584
|
+
* Every key here is read by the engine/bots through `RULES.X`, so overrides
|
|
585
|
+
* take effect across the whole simulation.
|
|
586
|
+
*/
|
|
587
|
+
declare const RULESET_RANGES: Record<string, {
|
|
588
|
+
min: number;
|
|
589
|
+
max: number;
|
|
590
|
+
}>;
|
|
591
|
+
/** Merge overrides onto the RULES object and apply them globally. */
|
|
592
|
+
declare function applyRulesetOverrides(overrides: Record<string, number>): void;
|
|
593
|
+
/** Restore all swept constants to their factory defaults. */
|
|
594
|
+
declare function resetRuleset(): void;
|
|
595
|
+
/** Current value of every sweepable constant (the optimizer's starting point). */
|
|
596
|
+
declare function currentRuleset(): Record<string, number>;
|
|
285
597
|
|
|
286
598
|
/**
|
|
287
599
|
* Engine version — milliseconds since the Unix epoch.
|
|
@@ -293,11 +605,11 @@ declare const ARENA_WATER_BUFFER = 200;
|
|
|
293
605
|
* Used to gate spectator replays: a recorded match can only be re-simulated when
|
|
294
606
|
* the runtime engine version matches the version that produced the match.
|
|
295
607
|
*/
|
|
296
|
-
declare const ENGINE_VERSION =
|
|
608
|
+
declare const ENGINE_VERSION = 1782047842068;
|
|
297
609
|
|
|
298
610
|
interface InternalWizardState extends WizardState {
|
|
299
611
|
missileConfig?: MissileConfig;
|
|
300
|
-
missileAI?:
|
|
612
|
+
missileAI?: MissileFunction$1;
|
|
301
613
|
blinkTarget?: {
|
|
302
614
|
x: number;
|
|
303
615
|
y: number;
|
|
@@ -305,6 +617,11 @@ interface InternalWizardState extends WizardState {
|
|
|
305
617
|
damageDealt: number;
|
|
306
618
|
damageTaken: number;
|
|
307
619
|
lastHitTick: number;
|
|
620
|
+
knockbackVx?: number;
|
|
621
|
+
knockbackVy?: number;
|
|
622
|
+
knockbackDelay?: number;
|
|
623
|
+
knockbackPendingVx?: number;
|
|
624
|
+
knockbackPendingVy?: number;
|
|
308
625
|
}
|
|
309
626
|
/**
|
|
310
627
|
* Initialize a new match state.
|
|
@@ -313,10 +630,11 @@ declare function createInitialState(_seed: number, spawnDist?: number): GameStat
|
|
|
313
630
|
/**
|
|
314
631
|
* Process one game tick.
|
|
315
632
|
*/
|
|
316
|
-
declare function tick(currentTick: number, wizard1AI: WizardFunction, wizard2AI: WizardFunction, config: GameConfig, wizards: InternalWizardState[], projectiles: ProjectileState[], missileAIs: Map<string,
|
|
633
|
+
declare function tick(currentTick: number, wizard1AI: WizardFunction, wizard2AI: WizardFunction, config: GameConfig, wizards: InternalWizardState[], projectiles: ProjectileState[], missileAIs: Map<string, MissileFunction$1>, matchSeed: number): {
|
|
317
634
|
nextTick: number;
|
|
318
635
|
wizards: InternalWizardState[];
|
|
319
636
|
projectiles: ProjectileState[];
|
|
637
|
+
events: SimEvent[];
|
|
320
638
|
errors: BotError[];
|
|
321
639
|
};
|
|
322
640
|
/**
|
|
@@ -324,7 +642,7 @@ declare function tick(currentTick: number, wizard1AI: WizardFunction, wizard2AI:
|
|
|
324
642
|
* Returns a deep clone to prevent mutation of history entries.
|
|
325
643
|
* Used for history recording where independent snapshots are needed.
|
|
326
644
|
*/
|
|
327
|
-
declare function getPlayerState(playerIndex: number, wizards: InternalWizardState[], projectiles: ProjectileState[], tick: number): GameState;
|
|
645
|
+
declare function getPlayerState(playerIndex: number, wizards: InternalWizardState[], projectiles: ProjectileState[], tick: number, events?: SimEvent[]): GameState;
|
|
328
646
|
/** Winner of a single match: a wizard ID, 'draw' (simultaneous kill), or null (timeout). */
|
|
329
647
|
type MatchWinner = 'wizard-1' | 'wizard-2' | 'draw' | null;
|
|
330
648
|
/** Winner of a fight (aggregate): a wizard ID or 'draw'. Never null. */
|
|
@@ -395,190 +713,76 @@ declare function simulate(wizard1AI: WizardFunction, wizard2AI: WizardFunction,
|
|
|
395
713
|
}): SimulateResult;
|
|
396
714
|
|
|
397
715
|
/**
|
|
398
|
-
* VIBEMANCER - HOOKS
|
|
716
|
+
* VIBEMANCER - HOOKS RUNTIME
|
|
399
717
|
*
|
|
400
|
-
*
|
|
718
|
+
* This file implements a minimal React-like hooks runtime for AI programming.
|
|
719
|
+
* It supports useState, useEffect, useMemo, useRef, and useParam with entity isolation.
|
|
401
720
|
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
721
|
+
* RULES OF HOOKS (same as React):
|
|
722
|
+
* - Hooks must be called at the top level of the bot function
|
|
723
|
+
* - Hooks must be called in the same order every tick
|
|
724
|
+
* - Hooks must NOT be called conditionally
|
|
725
|
+
*
|
|
726
|
+
* Violating these rules throws an error (detected via hook index validation).
|
|
408
727
|
*/
|
|
409
728
|
|
|
729
|
+
interface HookState {
|
|
730
|
+
values: unknown[];
|
|
731
|
+
effects: {
|
|
732
|
+
callback: () => void | (() => void);
|
|
733
|
+
deps?: unknown[];
|
|
734
|
+
cleanup?: () => void;
|
|
735
|
+
}[];
|
|
736
|
+
memos: {
|
|
737
|
+
value: unknown;
|
|
738
|
+
deps?: unknown[];
|
|
739
|
+
}[];
|
|
740
|
+
/** Type of each hook call in order (for validation). */
|
|
741
|
+
hookTypes: string[];
|
|
742
|
+
/** Total hooks called on first successful tick. */
|
|
743
|
+
hookCount: number;
|
|
744
|
+
/** Whether the first tick has completed successfully (hook pattern established). */
|
|
745
|
+
initialized: boolean;
|
|
746
|
+
}
|
|
410
747
|
/**
|
|
411
|
-
*
|
|
748
|
+
* Validate a hook call and return its sequential index.
|
|
749
|
+
* Ensures hooks are called in the same order every tick.
|
|
412
750
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
751
|
+
* On the first tick: records the hook type at this index.
|
|
752
|
+
* On subsequent ticks: validates the hook type matches.
|
|
753
|
+
*
|
|
754
|
+
* @param type - The hook type name (e.g., 'useState', 'useEffect', 'useParam')
|
|
755
|
+
* @returns The sequential hook index
|
|
756
|
+
* @throws If called outside runWithHooks or if hook order changed
|
|
415
757
|
*/
|
|
416
|
-
|
|
417
|
-
/** Enemy position in world coordinates (0-800). */
|
|
418
|
-
position: Position;
|
|
419
|
-
/** Enemy velocity in units/tick. */
|
|
420
|
-
velocity: Velocity;
|
|
421
|
-
/** Enemy current HP (0-60). */
|
|
422
|
-
health: number;
|
|
423
|
-
/** Enemy status: 'idle', 'casting', 'channeling' (shield), or 'gcd_locked'. */
|
|
424
|
-
status: 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
425
|
-
/** Which spell enemy is casting, or null. */
|
|
426
|
-
castingSpell: 'missile' | 'shield' | 'blink' | null;
|
|
427
|
-
/** Enemy shield block multiplier (0 if not shielding, 0.3-0.9 if shielding). */
|
|
428
|
-
shieldStrength: number;
|
|
429
|
-
}
|
|
758
|
+
declare function validateHookCall(type: string): number;
|
|
430
759
|
/**
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
* All timing values are in ticks (100 ticks = 1 second).
|
|
434
|
-
* Dodge directions are relative to missile heading, not world axes.
|
|
435
|
-
*/
|
|
436
|
-
interface AnalyzedThreat {
|
|
437
|
-
/** Unique projectile ID. */
|
|
438
|
-
id: string;
|
|
439
|
-
/** Raw projectile state (position, rotation in degrees, speed in u/t, turnRate, remainingTicks). */
|
|
440
|
-
projectile: ProjectileState;
|
|
441
|
-
/** Ticks until missile hits your current position. Infinity if predicted to miss. */
|
|
442
|
-
ticksToImpact: number;
|
|
443
|
-
/** Whether the missile will hit if you stand still. */
|
|
444
|
-
willHit: boolean;
|
|
445
|
-
/** Whether strafing left (perpendicular to missile heading) avoids it. */
|
|
446
|
-
canDodgeLeft: boolean;
|
|
447
|
-
/** Whether strafing right (perpendicular to missile heading) avoids it. */
|
|
448
|
-
canDodgeRight: boolean;
|
|
449
|
-
/** Whether moving directly away from the missile avoids it. */
|
|
450
|
-
canOutrun: boolean;
|
|
451
|
-
/** Optimal dodge direction as a unit vector {x, y}, or null if undodgeable. */
|
|
452
|
-
bestDodgeDirection: Position | null;
|
|
453
|
-
/** Whether you can channel shield before the missile arrives. */
|
|
454
|
-
canBlockInTime: boolean;
|
|
455
|
-
/** Ticks from now when you should START channeling shield to block in time. */
|
|
456
|
-
ticksToStartShield: number;
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Final action that can be returned from a bot.
|
|
460
|
-
* Cannot be further chained.
|
|
461
|
-
*/
|
|
462
|
-
interface FinalAction {
|
|
463
|
-
/** Internal: extract the WizardActions */
|
|
464
|
-
readonly _toAction: () => WizardActions;
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Action builder that allows chaining .move() for simultaneous movement.
|
|
468
|
-
* Returned by shield(), missile(), and cancel().
|
|
469
|
-
*/
|
|
470
|
-
interface ActionBuilder extends FinalAction {
|
|
471
|
-
/**
|
|
472
|
-
* Add movement to this action (e.g., move while casting).
|
|
473
|
-
* Direction vector, not absolute position. Auto-normalized.
|
|
474
|
-
* Positive X = right, positive Y = down.
|
|
475
|
-
*/
|
|
476
|
-
move(x: number, y: number): FinalAction;
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Bot function type for the hooks API.
|
|
480
|
-
* Called every tick. Read state with hooks, return an action.
|
|
481
|
-
*/
|
|
482
|
-
type BotFunction = () => FinalAction;
|
|
483
|
-
/**
|
|
484
|
-
* Internal context for game state hooks.
|
|
485
|
-
*/
|
|
486
|
-
interface BotContext {
|
|
487
|
-
entityId: string;
|
|
488
|
-
tick: number;
|
|
489
|
-
position: Position;
|
|
490
|
-
velocity: Velocity;
|
|
491
|
-
health: number;
|
|
492
|
-
maxHealth: number;
|
|
493
|
-
state: 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
494
|
-
castingSpell?: 'missile' | 'shield' | 'blink';
|
|
495
|
-
castProgress?: number;
|
|
496
|
-
castDuration?: number;
|
|
497
|
-
channelingSpell?: 'shield';
|
|
498
|
-
channelDuration?: number;
|
|
499
|
-
gcdRemaining?: number;
|
|
500
|
-
blinkCooldown: number;
|
|
501
|
-
enemies: Array<{
|
|
502
|
-
id: string;
|
|
503
|
-
position: Position;
|
|
504
|
-
velocity: Velocity;
|
|
505
|
-
health: number;
|
|
506
|
-
state: 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
507
|
-
castingSpell?: 'missile' | 'shield' | 'blink';
|
|
508
|
-
channelingSpell?: 'shield';
|
|
509
|
-
channelDuration?: number;
|
|
510
|
-
}>;
|
|
511
|
-
projectiles: ProjectileState[];
|
|
512
|
-
myProjectiles: ProjectileState[];
|
|
513
|
-
arenaWidth: number;
|
|
514
|
-
arenaHeight: number;
|
|
515
|
-
damageDealt: number;
|
|
516
|
-
damageTaken: number;
|
|
517
|
-
lastHitTick: number;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* VIBEMANCER - HOOKS RUNTIME
|
|
522
|
-
*
|
|
523
|
-
* This file implements a minimal React-like hooks runtime for AI programming.
|
|
524
|
-
* It supports useState, useEffect, useMemo, useRef, and useParam with entity isolation.
|
|
525
|
-
*
|
|
526
|
-
* RULES OF HOOKS (same as React):
|
|
527
|
-
* - Hooks must be called at the top level of the bot function
|
|
528
|
-
* - Hooks must be called in the same order every tick
|
|
529
|
-
* - Hooks must NOT be called conditionally
|
|
530
|
-
*
|
|
531
|
-
* Violating these rules throws an error (detected via hook index validation).
|
|
532
|
-
*/
|
|
533
|
-
|
|
534
|
-
interface HookState {
|
|
535
|
-
values: unknown[];
|
|
536
|
-
effects: {
|
|
537
|
-
callback: () => void | (() => void);
|
|
538
|
-
deps?: unknown[];
|
|
539
|
-
cleanup?: () => void;
|
|
540
|
-
}[];
|
|
541
|
-
memos: {
|
|
542
|
-
value: unknown;
|
|
543
|
-
deps?: unknown[];
|
|
544
|
-
}[];
|
|
545
|
-
/** Type of each hook call in order (for validation). */
|
|
546
|
-
hookTypes: string[];
|
|
547
|
-
/** Total hooks called on first successful tick. */
|
|
548
|
-
hookCount: number;
|
|
549
|
-
/** Whether the first tick has completed successfully (hook pattern established). */
|
|
550
|
-
initialized: boolean;
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Validate a hook call and return its sequential index.
|
|
554
|
-
* Ensures hooks are called in the same order every tick.
|
|
555
|
-
*
|
|
556
|
-
* On the first tick: records the hook type at this index.
|
|
557
|
-
* On subsequent ticks: validates the hook type matches.
|
|
558
|
-
*
|
|
559
|
-
* @param type - The hook type name (e.g., 'useState', 'useEffect', 'useParam')
|
|
560
|
-
* @returns The sequential hook index
|
|
561
|
-
* @throws If called outside runWithHooks or if hook order changed
|
|
562
|
-
*/
|
|
563
|
-
declare function validateHookCall(type: string): number;
|
|
564
|
-
/**
|
|
565
|
-
* Run a function with a specific entity's hook context.
|
|
760
|
+
* Run a function with a specific entity's hook context.
|
|
566
761
|
*/
|
|
567
762
|
declare function runWithHooks<T>(entityId: string, fn: () => T): T;
|
|
568
763
|
/**
|
|
569
764
|
* Run a bot function with full context (game state + persistence hooks).
|
|
570
765
|
* Use this when you need to set BOTH the entity ID and the game context.
|
|
571
766
|
*/
|
|
572
|
-
declare function
|
|
767
|
+
declare function runWizardWithContext<T>(entityId: string, context: WizardContext, fn: () => T): T;
|
|
573
768
|
/**
|
|
574
769
|
* Run a function with game state context only, preserving the current entity ID.
|
|
575
|
-
*
|
|
770
|
+
* Used by the simulation to set up WizardContext before calling hooks-style bots.
|
|
576
771
|
*/
|
|
577
|
-
declare function
|
|
772
|
+
declare function withWizardContext<T>(context: WizardContext, fn: () => T): T;
|
|
578
773
|
/**
|
|
579
774
|
* Get the current bot context. Throws if called outside bot execution.
|
|
580
775
|
*/
|
|
581
|
-
declare function
|
|
776
|
+
declare function getWizardContext(): WizardContext;
|
|
777
|
+
/**
|
|
778
|
+
* Run a function with missile context set, preserving the current entity ID.
|
|
779
|
+
* Used by the simulation to set up MissileContext before calling hooks-style missile AIs.
|
|
780
|
+
*/
|
|
781
|
+
declare function withMissileContext<T>(context: MissileContext, fn: () => T): T;
|
|
782
|
+
/**
|
|
783
|
+
* Get the current missile context. Throws if called outside missile AI execution.
|
|
784
|
+
*/
|
|
785
|
+
declare function getMissileContext(): MissileContext;
|
|
582
786
|
/**
|
|
583
787
|
* Persist state between ticks.
|
|
584
788
|
*/
|
|
@@ -611,12 +815,8 @@ declare function resetAllHooks(): void;
|
|
|
611
815
|
/**
|
|
612
816
|
* Move a wizard based on world-space input direction.
|
|
613
817
|
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
* - y: +100 = down, -100 = up
|
|
617
|
-
* - Values are clamped to [-100, 100]
|
|
618
|
-
* - Diagonal movement is normalized (magnitude capped at 100)
|
|
619
|
-
* - No rotation tracking - just output (x, y) direction
|
|
818
|
+
* Any vector works: direction is preserved, speed is clamped to [0, 1].
|
|
819
|
+
* (0.5, 0) = half speed right. (300, 200) = full speed at 33.7°.
|
|
620
820
|
*/
|
|
621
821
|
declare function moveWizard(wizard: WizardState, move: {
|
|
622
822
|
x: number;
|
|
@@ -627,9 +827,15 @@ declare function moveWizard(wizard: WizardState, move: {
|
|
|
627
827
|
*/
|
|
628
828
|
declare function moveProjectile(projectile: ProjectileState, deltaTicks: number): Position;
|
|
629
829
|
/**
|
|
630
|
-
* Clamp a position to the arena
|
|
830
|
+
* Clamp a position to the full arena bounds (0-860).
|
|
831
|
+
* No playfield clamping — wizards CAN walk/blink into lava.
|
|
631
832
|
*/
|
|
632
833
|
declare function clampToArena(position: Position, radius: number): Position;
|
|
834
|
+
/**
|
|
835
|
+
* Check if a position is in the lava zone (outside the playfield).
|
|
836
|
+
* Lava zones: [0, ARENA_MIN] and [ARENA_MAX, ARENA_SIZE] on each axis.
|
|
837
|
+
*/
|
|
838
|
+
declare function isInLava(position: Position, radius: number): boolean;
|
|
633
839
|
/**
|
|
634
840
|
* Resolve body collision between two wizards.
|
|
635
841
|
* Pushes both apart equally so they don't overlap. Neither is blocked — they just can't stack.
|
|
@@ -686,7 +892,7 @@ declare function applyDamage(wizard: WizardState, damage: number, _projectile?:
|
|
|
686
892
|
* Extra capabilities over `simulate()`:
|
|
687
893
|
* - step(N) advances N ticks at a time (default 1) — caller controls pacing
|
|
688
894
|
* - replaceMissileAI(id, ai) hot-swaps a missile's AI mid-flight (used by
|
|
689
|
-
* the missile-
|
|
895
|
+
* the missile-guide feature in manual play)
|
|
690
896
|
* - setInvincible(wizardIndex, on) toggles damage immunity per wizard
|
|
691
897
|
*/
|
|
692
898
|
|
|
@@ -709,12 +915,14 @@ declare class ManualMatch {
|
|
|
709
915
|
private wizards;
|
|
710
916
|
private projectiles;
|
|
711
917
|
private missileAIs;
|
|
712
|
-
/** First-replacement originals for
|
|
918
|
+
/** First-replacement originals for guided missiles. Used by restoreMissileAI. */
|
|
713
919
|
private originalMissileAIs;
|
|
714
920
|
private currentTick;
|
|
715
921
|
private done;
|
|
922
|
+
private deathTick;
|
|
716
923
|
private allErrors;
|
|
717
924
|
private history;
|
|
925
|
+
private lastTickEvents;
|
|
718
926
|
constructor(wizard1AI: WizardFunction, wizard2AI: WizardFunction, options?: ManualMatchOptions);
|
|
719
927
|
/**
|
|
720
928
|
* Advance the match by `count` ticks (default 1). Stops early if the
|
|
@@ -722,17 +930,17 @@ declare class ManualMatch {
|
|
|
722
930
|
*/
|
|
723
931
|
step(count?: number): StepResult;
|
|
724
932
|
/**
|
|
725
|
-
* Hot-swap a missile's AI function. Used by the missile-
|
|
933
|
+
* Hot-swap a missile's AI function. Used by the missile-guide feature
|
|
726
934
|
* in manual play. The first replacement remembers the original AI so
|
|
727
935
|
* `restoreMissileAI` can put it back. Subsequent replacements update
|
|
728
936
|
* the active AI but leave the remembered original alone.
|
|
729
937
|
*
|
|
730
938
|
* No-op if the projectile id doesn't exist.
|
|
731
939
|
*/
|
|
732
|
-
replaceMissileAI(projectileId: string, ai:
|
|
940
|
+
replaceMissileAI(projectileId: string, ai: MissileFunction$1): void;
|
|
733
941
|
/**
|
|
734
|
-
* Restore a previously
|
|
735
|
-
* No-op if the projectile id doesn't exist or was never
|
|
942
|
+
* Restore a previously guided missile's original AI function.
|
|
943
|
+
* No-op if the projectile id doesn't exist or was never guided.
|
|
736
944
|
*/
|
|
737
945
|
restoreMissileAI(projectileId: string): void;
|
|
738
946
|
/**
|
|
@@ -773,7 +981,6 @@ interface TurningParams extends BaseParams {
|
|
|
773
981
|
}
|
|
774
982
|
type StraightParams = BaseParams;
|
|
775
983
|
type HomingParams = TurningParams;
|
|
776
|
-
type AntiHomingParams = TurningParams;
|
|
777
984
|
interface SpiralParams extends BaseParams {
|
|
778
985
|
spiralRadius: number;
|
|
779
986
|
spiralFreq: number;
|
|
@@ -783,7 +990,7 @@ interface SeekerParams extends TurningParams {
|
|
|
783
990
|
}
|
|
784
991
|
interface MissileTemplate {
|
|
785
992
|
config: MissileConfig;
|
|
786
|
-
ai:
|
|
993
|
+
ai: MissileFunction$1;
|
|
787
994
|
}
|
|
788
995
|
/**
|
|
789
996
|
* Fire-and-forget missile. No steering — flies in a straight line.
|
|
@@ -793,11 +1000,6 @@ declare function straightMissile(p: StraightParams): MissileTemplate;
|
|
|
793
1000
|
* Homing missile. Steers toward the enemy each tick.
|
|
794
1001
|
*/
|
|
795
1002
|
declare function homingMissile(p: HomingParams): MissileTemplate;
|
|
796
|
-
/**
|
|
797
|
-
* Anti-homing missile. Steers AWAY from the enemy — useful as a feint or
|
|
798
|
-
* area-denial pattern.
|
|
799
|
-
*/
|
|
800
|
-
declare function antiHomingMissile(p: AntiHomingParams): MissileTemplate;
|
|
801
1003
|
/**
|
|
802
1004
|
* Spiral missile. Continuously orbits its current heading while advancing.
|
|
803
1005
|
* Uses missileState.remainingTicks as a deterministic phase counter so the
|
|
@@ -979,1041 +1181,1087 @@ declare function getAdaptiveMissileConfig(targetVelocity: Position, distance: nu
|
|
|
979
1181
|
* If `lastMissileConfig` is provided, accounts for warmup bonus: similar
|
|
980
1182
|
* missiles cast faster, so more damage can fit in the same budget.
|
|
981
1183
|
*/
|
|
1184
|
+
/**
|
|
1185
|
+
* Simulate a missile trajectory to find the minimum duration (ticks) needed
|
|
1186
|
+
* to reach a target at the given distance. Works for all turnRate values:
|
|
1187
|
+
* positive (homing) and zero (straight).
|
|
1188
|
+
*
|
|
1189
|
+
* The simulation starts the missile aimed directly at the target and steps
|
|
1190
|
+
* through the trajectory tick by tick. For homing, the missile tracks the
|
|
1191
|
+
* target each tick matching the engine's steering physics.
|
|
1192
|
+
*/
|
|
1193
|
+
/**
|
|
1194
|
+
* Simulate a missile trajectory to find the minimum duration (ticks) needed
|
|
1195
|
+
* to reach a target at the given distance. Works for straight (turnRate=0)
|
|
1196
|
+
* and homing (turnRate>0) missiles.
|
|
1197
|
+
*
|
|
1198
|
+
* The missile starts aimed directly at the target at (dist, 0) and steps
|
|
1199
|
+
* through the trajectory tick by tick. For homing, the missile tracks the
|
|
1200
|
+
* target each tick matching the engine's steering physics.
|
|
1201
|
+
*
|
|
1202
|
+
* Returns 500 if the missile cannot reach the target within 500 ticks.
|
|
1203
|
+
*/
|
|
1204
|
+
declare function simulateMinDuration(speed: number, turnRateDeg: number, dist: number, collisionRadius?: number): number;
|
|
982
1205
|
declare function fitMissileToBudget(budgetTicks: number, distance: number, options?: {
|
|
983
1206
|
minTurnRate?: number;
|
|
984
1207
|
maxDamage?: number;
|
|
985
1208
|
lastMissileConfig?: MissileConfig;
|
|
986
1209
|
}): MissileConfig | null;
|
|
987
|
-
|
|
988
1210
|
/**
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
* BEHAVIOR: Does absolutely nothing. No movement, no spells, no AI.
|
|
1211
|
+
* Fit a missile config that accounts for the enemy escaping during cast time.
|
|
992
1212
|
*
|
|
993
|
-
*
|
|
994
|
-
*
|
|
995
|
-
*
|
|
996
|
-
*
|
|
997
|
-
* Nobody calls them "training dummies"; the player term is always "target dummy."
|
|
1213
|
+
* During casting, the caster moves at CASTING_MOVEMENT_MULT speed while
|
|
1214
|
+
* the enemy moves at full MOVEMENT_SPEED. This means the effective distance
|
|
1215
|
+
* at launch is larger than the current distance. This function iteratively
|
|
1216
|
+
* converges on a missile config whose range covers the escape distance.
|
|
998
1217
|
*
|
|
999
|
-
*
|
|
1218
|
+
* @param currentDistance - Current distance to enemy
|
|
1219
|
+
* @param budgetTicks - Maximum cast time budget in ticks
|
|
1220
|
+
* @param options - Same options as fitMissileToBudget, plus:
|
|
1221
|
+
* - enemyApproaching: if true, enemy is moving toward caster (reduces escape)
|
|
1222
|
+
* - distanceBuffer: flat units added to target distance for safety margin (default 20)
|
|
1223
|
+
* - maxIterations: convergence iterations (default 5)
|
|
1000
1224
|
*/
|
|
1001
|
-
declare
|
|
1225
|
+
declare function fitMissileForEscapingTarget(currentDistance: number, budgetTicks: number, options?: {
|
|
1226
|
+
minTurnRate?: number;
|
|
1227
|
+
maxDamage?: number;
|
|
1228
|
+
lastMissileConfig?: MissileConfig;
|
|
1229
|
+
enemyApproaching?: boolean;
|
|
1230
|
+
distanceBuffer?: number;
|
|
1231
|
+
maxIterations?: number;
|
|
1232
|
+
}): MissileConfig | null;
|
|
1002
1233
|
|
|
1003
1234
|
/**
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
* the enemy. No movement, no dodging, no shielding. Knows one spell and uses
|
|
1008
|
-
* it on cooldown. The wizard equivalent of an FPS player who stands in the open
|
|
1009
|
-
* and holds left-click.
|
|
1010
|
-
*
|
|
1011
|
-
* NAMING RATIONALE: "Rookie" is the universal term for a first-timer who barely
|
|
1012
|
-
* knows what they're doing. This bot is a day-one player who learned how to cast
|
|
1013
|
-
* missile and nothing else. No movement, no defense, just raw "I press the button."
|
|
1014
|
-
* We considered "Noob" (more accurate) but Rookie is less abrasive while conveying
|
|
1015
|
-
* the same thing — a beginner who doesn't know any better.
|
|
1016
|
-
*
|
|
1017
|
-
* STANDALONE — no tier progression. Rookies either learn to play a real class
|
|
1018
|
-
* or quit. This bot represents the rock-bottom of "at least it shoots."
|
|
1235
|
+
* Get a seeded random number generator. Returns a function that produces
|
|
1236
|
+
* deterministic values in [0, 1) — same seed + same tick = same sequence.
|
|
1237
|
+
* Use this instead of Math.random() so replays are deterministic.
|
|
1019
1238
|
*/
|
|
1020
|
-
declare
|
|
1021
|
-
|
|
1239
|
+
declare function useRandom(): () => number;
|
|
1022
1240
|
/**
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
1025
|
-
* BEHAVIOR: Picks random valid actions each tick — random movement, random spells,
|
|
1026
|
-
* random missile configs, random directions. Occasionally cancels its own casts.
|
|
1027
|
-
* Uses engine-provided seeded random for deterministic behavior. Useful for
|
|
1028
|
-
* finding edge cases in the engine, but completely useless in combat.
|
|
1029
|
-
*
|
|
1030
|
-
* NAMING RATIONALE: In WoW, critters are the 1-HP ambient mobs (rabbits, squirrels,
|
|
1031
|
-
* prairie dogs) that wander around doing nothing useful and die to literally anything.
|
|
1032
|
-
* This bot is the wizard equivalent — it flails around randomly and gets destroyed by
|
|
1033
|
-
* anyone with a plan. The word "Critter" immediately tells any gamer "this thing is
|
|
1034
|
-
* helpless and exists only to fill space."
|
|
1035
|
-
*
|
|
1036
|
-
* STANDALONE — no tier progression. Critters don't level up. However, a future
|
|
1037
|
-
* "Hogger" bot could be an elite critter: same chaotic spirit but actually dangerous
|
|
1038
|
-
* (like the famous WoW elite that wipes unprepared lowbies).
|
|
1241
|
+
* Get your current health (0-60). Wizard dies at 0.
|
|
1039
1242
|
*/
|
|
1040
|
-
declare
|
|
1041
|
-
|
|
1243
|
+
declare function useHealth(): number;
|
|
1042
1244
|
/**
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1045
|
-
* BEHAVIOR: The elite critter. Chaotic and unpredictable but genuinely dangerous.
|
|
1046
|
-
* Randomly varies missile configs each cast (damage 7-15, speed 3-8, random homing),
|
|
1047
|
-
* moves erratically but still somewhat toward/away from the enemy, shields when
|
|
1048
|
-
* in real danger, and blinks unpredictably. The randomness makes Hogger hard to
|
|
1049
|
-
* predict — you never know if the next missile will be a slow tracker or a fast
|
|
1050
|
-
* snipe. Unlike Critter's pure randomness, Hogger has enough combat awareness
|
|
1051
|
-
* to actually win fights.
|
|
1052
|
-
*
|
|
1053
|
-
* NAMING RATIONALE: In WoW, Hogger is the iconic level 11 elite gnoll in Elwynn
|
|
1054
|
-
* Forest who infamously kills unprepared lowbies. He's technically a basic mob
|
|
1055
|
-
* but hits way harder than expected. This bot is the Critter that learned to
|
|
1056
|
-
* fight — still chaotic, still a bit dumb, but capable of ending you if you
|
|
1057
|
-
* underestimate it. "Hogger" is one of WoW's most recognizable references and
|
|
1058
|
-
* perfectly captures "deceptively dangerous chaos."
|
|
1059
|
-
*
|
|
1060
|
-
* STANDALONE — no tier progression. There's only one Hogger.
|
|
1245
|
+
* Get your current position as {x, y} in world coordinates (0-800).
|
|
1246
|
+
* Position is clamped to [5, 795] (arena bounds minus wizard radius).
|
|
1061
1247
|
*/
|
|
1062
|
-
declare
|
|
1063
|
-
|
|
1248
|
+
declare function usePosition(): Position;
|
|
1064
1249
|
/**
|
|
1065
|
-
*
|
|
1066
|
-
*
|
|
1067
|
-
* BEHAVIOR: Fires a single maximum-damage homing missile with infinite budget.
|
|
1068
|
-
* No damage cap — goes for the biggest possible hit. Exists as a benchmark to
|
|
1069
|
-
* demonstrate why lower-damage + shield play is superior. Has basic shield
|
|
1070
|
-
* defense but no sophisticated tactics. One fat cast, one fat hit.
|
|
1071
|
-
*
|
|
1072
|
-
* STANDALONE — no tier progression. Benchmark/test bot.
|
|
1250
|
+
* Get your current velocity as {x, y} in units/tick.
|
|
1251
|
+
* Max magnitude is 1 u/t when idle, 0.5 u/t when casting, 0 when channeling shield.
|
|
1073
1252
|
*/
|
|
1074
|
-
declare
|
|
1075
|
-
|
|
1076
|
-
declare const Turtle: WizardFunction;
|
|
1077
|
-
|
|
1253
|
+
declare function useVelocity(): Velocity;
|
|
1078
1254
|
/**
|
|
1079
|
-
*
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
|
|
1255
|
+
* Get your current status:
|
|
1256
|
+
* - 'idle': free to act
|
|
1257
|
+
* - 'casting': casting a spell (missile or blink). Can move at 50% speed.
|
|
1258
|
+
* - 'channeling': channeling shield. Cannot move. Cancel anytime with cancel().
|
|
1259
|
+
* - 'gcd_locked': global cooldown after spell. Can move at full speed, but cannot cast.
|
|
1260
|
+
*/
|
|
1261
|
+
declare function useStatus(): 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
1262
|
+
/**
|
|
1263
|
+
* Get ticks until you can start a new spell.
|
|
1085
1264
|
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
* - Sentinel (tier 2): Stationary, two-tier offense (big + fast missiles)
|
|
1089
|
-
* - Golem (tier 3): Immovable fortress, perfect shield timing
|
|
1265
|
+
* Returns 0 when idle or channeling (shield can be canceled immediately).
|
|
1266
|
+
* During casting: remaining cast ticks. During GCD: remaining GCD ticks.
|
|
1090
1267
|
*
|
|
1091
|
-
*
|
|
1268
|
+
* Note: 100 ticks = 1 second.
|
|
1092
1269
|
*/
|
|
1093
|
-
declare
|
|
1094
|
-
|
|
1270
|
+
declare function useTicksUntilReady(): number;
|
|
1095
1271
|
/**
|
|
1096
|
-
*
|
|
1272
|
+
* Get current shield block multiplier.
|
|
1097
1273
|
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
* Uses progressive cast-cancel thresholds for optimal damage trading.
|
|
1103
|
-
*
|
|
1104
|
-
* KEY IMPROVEMENTS OVER SENTINEL:
|
|
1105
|
-
* - Counterattack punish: fires during enemy cast/GCD recovery
|
|
1106
|
-
* - Multi-threat volley awareness: holds shield through consecutive hits
|
|
1107
|
-
* - Progressive cast-cancel: graduated damage thresholds
|
|
1108
|
-
* - Perfect shield timing: uses ticksToStartShield precisely
|
|
1109
|
-
*
|
|
1110
|
-
* PROGRESSION LINE: Turtle → Sentinel → Golem
|
|
1111
|
-
* TIER: 3 (elite Defensive line)
|
|
1274
|
+
* Returns 0 if not channeling shield.
|
|
1275
|
+
* Returns 0.3-0.9 if channeling: starts at 0.9 (blocks 90%), decays by 0.2/sec,
|
|
1276
|
+
* minimum 0.3 (blocks 30%). The remaining damage gets through:
|
|
1277
|
+
* actualDamage = incomingDamage × (1 - shieldStrength).
|
|
1112
1278
|
*/
|
|
1113
|
-
declare
|
|
1114
|
-
|
|
1279
|
+
declare function useShieldStrength(): number;
|
|
1115
1280
|
/**
|
|
1116
|
-
*
|
|
1117
|
-
*
|
|
1118
|
-
* BEHAVIOR: Melee assassin. Blinks to the enemy, then lands devastating point-blank
|
|
1119
|
-
* stab attacks (15 damage, 30u range, ~60 tick cast = 2-hit kill). Runs directly
|
|
1120
|
-
* at the enemy with minimal strafe, shields undodgeable threats. The entire
|
|
1121
|
-
* strategy is: get close, stab, kill. Simple and brutal.
|
|
1122
|
-
*
|
|
1123
|
-
* PROGRESSION LINE: Shadowblade → Nightblade → Voidblade
|
|
1124
|
-
* - Shadowblade (tier 1): Offensive blink, melee stabs, basic shield
|
|
1125
|
-
* - Nightblade (tier 2): + missile-aware blinks, adaptive stabs, timed defense
|
|
1126
|
-
* - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages
|
|
1127
|
-
*
|
|
1128
|
-
* TIER: 1 (base)
|
|
1281
|
+
* Get all projectiles currently in flight (yours and enemy's).
|
|
1282
|
+
* Used for blink safety calculations and threat analysis.
|
|
1129
1283
|
*/
|
|
1130
|
-
declare
|
|
1131
|
-
|
|
1284
|
+
declare function useProjectiles(): ProjectileState[];
|
|
1132
1285
|
/**
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
*
|
|
1139
|
-
* arrive almost instantly after launch — too fast to react. Nightblade anticipates
|
|
1140
|
-
* the threat. Also has emergency blink and proper channeling management.
|
|
1286
|
+
* Get the config of the last missile you fired, or undefined if none fired yet.
|
|
1287
|
+
* Used for the warmup system: consecutive similar missiles cast faster.
|
|
1288
|
+
*/
|
|
1289
|
+
declare function useLastMissileConfig(): MissileConfig | undefined;
|
|
1290
|
+
/**
|
|
1291
|
+
* Get blink cooldown remaining in ticks. Returns 0 if ready to blink.
|
|
1141
1292
|
*
|
|
1142
|
-
*
|
|
1143
|
-
* -
|
|
1144
|
-
* -
|
|
1145
|
-
* - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages
|
|
1293
|
+
* Cooldown scales with distance used:
|
|
1294
|
+
* - 100 units → ~100 ticks (1s)
|
|
1295
|
+
* - 300 units (max range) → 2000 ticks (20s)
|
|
1146
1296
|
*
|
|
1147
|
-
*
|
|
1297
|
+
* Note: 100 ticks = 1 second.
|
|
1148
1298
|
*/
|
|
1149
|
-
declare
|
|
1150
|
-
|
|
1299
|
+
declare function useBlinkCooldown(): number;
|
|
1151
1300
|
/**
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
*
|
|
1301
|
+
* Get currently casting spell, or null if not casting.
|
|
1302
|
+
* Returns 'missile', 'shield', or 'blink'.
|
|
1303
|
+
*/
|
|
1304
|
+
declare function useCastingSpell(): 'missile' | 'shield' | 'blink' | null;
|
|
1305
|
+
/**
|
|
1306
|
+
* Get cast progress as {current, total} in ticks, or null if not casting.
|
|
1158
1307
|
*
|
|
1159
|
-
*
|
|
1160
|
-
*
|
|
1161
|
-
|
|
1162
|
-
|
|
1308
|
+
* current = ticks elapsed, total = ticks needed. When current >= total, spell fires.
|
|
1309
|
+
* Note: 100 ticks = 1 second.
|
|
1310
|
+
*/
|
|
1311
|
+
declare function useCastProgress(): {
|
|
1312
|
+
current: number;
|
|
1313
|
+
total: number;
|
|
1314
|
+
} | null;
|
|
1315
|
+
/**
|
|
1316
|
+
* Get enemy wizard state.
|
|
1163
1317
|
*
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1167
|
-
|
|
1168
|
-
|
|
1318
|
+
* Returns position, velocity, health, status, casting spell, and shield strength.
|
|
1319
|
+
* Note: you cannot see the enemy's missile configs or exact cooldown timers —
|
|
1320
|
+
* only their status and what's visible on the field.
|
|
1321
|
+
*/
|
|
1322
|
+
declare function useEnemy(): EnemyState;
|
|
1323
|
+
/**
|
|
1324
|
+
* Get all your active (in-flight) projectiles.
|
|
1325
|
+
* Each has position, rotation (degrees), speed (u/t), turnRate, remainingTicks.
|
|
1326
|
+
*/
|
|
1327
|
+
declare function useMyProjectiles(): ProjectileState[];
|
|
1328
|
+
/**
|
|
1329
|
+
* Get total damage you've dealt this match.
|
|
1330
|
+
*/
|
|
1331
|
+
declare function useDamageDealt(): number;
|
|
1332
|
+
/**
|
|
1333
|
+
* Get total damage you've taken this match.
|
|
1334
|
+
*/
|
|
1335
|
+
declare function useDamageTaken(): number;
|
|
1336
|
+
/**
|
|
1337
|
+
* Get the tick number when you last took damage. Returns 0 if never hit.
|
|
1338
|
+
* Compare with useTick() to get ticks since last hit.
|
|
1339
|
+
*/
|
|
1340
|
+
declare function useLastHitTick(): number;
|
|
1341
|
+
/**
|
|
1342
|
+
* Get arena dimensions. Default: {width: 800, height: 800}.
|
|
1343
|
+
* Wizards are clamped to [5, 795] on each axis (radius = 5).
|
|
1344
|
+
*/
|
|
1345
|
+
declare function useArenaSize(): {
|
|
1346
|
+
width: number;
|
|
1347
|
+
height: number;
|
|
1348
|
+
};
|
|
1349
|
+
/**
|
|
1350
|
+
* Get current game tick (starts at 0, increments each tick).
|
|
1351
|
+
* 100 ticks = 1 second. Match ends at 30,000 ticks (5 minutes).
|
|
1352
|
+
*/
|
|
1353
|
+
declare function useTick(): number;
|
|
1354
|
+
/**
|
|
1355
|
+
* Get analyzed threats from all incoming enemy projectiles.
|
|
1356
|
+
* Sorted by ticksToImpact (soonest first). Only includes missiles within 500 units
|
|
1357
|
+
* or that are predicted to hit.
|
|
1169
1358
|
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1359
|
+
* Each threat includes:
|
|
1360
|
+
* - ticksToImpact: ticks until hit (Infinity if will miss)
|
|
1361
|
+
* - willHit: true if missile hits your current position
|
|
1362
|
+
* - canDodgeLeft/Right: whether strafing perpendicular to missile heading works
|
|
1363
|
+
* - canOutrun: whether moving away from missile escapes it
|
|
1364
|
+
* - bestDodgeDirection: {x, y} unit vector to dodge optimally, or null if undodgeable
|
|
1365
|
+
* - canBlockInTime: whether you can raise shield before impact
|
|
1366
|
+
* - ticksToStartShield: when to START channeling shield to block in time
|
|
1172
1367
|
*/
|
|
1173
|
-
declare
|
|
1368
|
+
declare function useThreats(): AnalyzedThreat[];
|
|
1369
|
+
/**
|
|
1370
|
+
* Get the most imminent threat, or null if no threats.
|
|
1371
|
+
* Shorthand for useThreats()[0].
|
|
1372
|
+
*/
|
|
1373
|
+
declare function useClosestThreat(): AnalyzedThreat | null;
|
|
1374
|
+
/**
|
|
1375
|
+
* Get your missiles analyzed from the enemy's perspective.
|
|
1376
|
+
* Useful to predict when enemy will shield/dodge your attacks.
|
|
1377
|
+
*/
|
|
1378
|
+
declare function useMyThreatsToEnemy(): AnalyzedThreat[];
|
|
1174
1379
|
|
|
1175
1380
|
/**
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1178
|
-
* BEHAVIOR: Stands still and fires slow, homing missiles constantly. Every missile
|
|
1179
|
-
* tracks the enemy with turnRate 2 — they curve relentlessly toward the target.
|
|
1180
|
-
* No movement, no shields, just an unending stream of seeking projectiles. The
|
|
1181
|
-
* missiles are slow (speed 3) but long-lived (300 ticks) and will chase you across
|
|
1182
|
-
* the entire arena.
|
|
1183
|
-
*
|
|
1184
|
-
* NAMING RATIONALE: Named after Diablo 2's Bone Necromancer ("Bonemancer"), whose
|
|
1185
|
-
* signature spell Bone Spirit is a slow-moving, auto-tracking projectile that hunts
|
|
1186
|
-
* enemies relentlessly. That's exactly what this bot does — it stands in place and
|
|
1187
|
-
* spams seeking missiles. The homing behavior is the key identity: these aren't
|
|
1188
|
-
* aimed shots, they're heat-seeking spirits that chase you down. Every D2 player
|
|
1189
|
-
* knows the Bonemancer — it's one of the most iconic builds.
|
|
1190
|
-
*
|
|
1191
|
-
* PROGRESSION LINE: Bonemancer → Lich → Archlich
|
|
1192
|
-
* - Bonemancer (tier 1): Stationary, spams slow homing missiles
|
|
1193
|
-
* - Lich (tier 2): Future — enhanced homing with adaptive missiles and defense
|
|
1194
|
-
* - Archlich (tier 3): Future — master of tracking magic, undodgeable death swarm
|
|
1195
|
-
* The progression follows the D2 necromancer power fantasy: from bone apprentice
|
|
1196
|
-
* to undead overlord, each tier's missiles become harder to escape.
|
|
1381
|
+
* VIBEMANCER - THREAT ANALYSIS
|
|
1197
1382
|
*
|
|
1198
|
-
*
|
|
1383
|
+
* Pre-computes threat information for incoming projectiles.
|
|
1384
|
+
* This handles the "subconscious" perception of missile trajectories.
|
|
1199
1385
|
*/
|
|
1200
|
-
declare const Bonemancer: WizardFunction;
|
|
1201
1386
|
|
|
1202
1387
|
/**
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
* BEHAVIOR: Homing missile specialist with strong-tracking adaptive missiles.
|
|
1206
|
-
* Uses fitMissileToBudget with minTurnRate 1.0 — higher than other bots (0.5) —
|
|
1207
|
-
* producing missiles with superior tracking at the cost of some damage/speed.
|
|
1208
|
-
* Strafing launches missiles from different angles, creating multi-angle pressure.
|
|
1209
|
-
*
|
|
1210
|
-
* Shields undodgeable/critical threats, emergency blinks. Cancels missile cast
|
|
1211
|
-
* only for lethal incoming damage.
|
|
1212
|
-
*
|
|
1213
|
-
* KEY DIFFERENCES FROM BONEMANCER:
|
|
1214
|
-
* - Bonemancer: stationary, no defense, fixed d=7/s=3/t=2/dur=300
|
|
1215
|
-
* - Lich: mobile, full defense, adaptive strong-tracking homing missiles
|
|
1216
|
-
*
|
|
1217
|
-
* PROGRESSION LINE: Bonemancer → Lich → Archlich
|
|
1218
|
-
* - Bonemancer (tier 1): Stationary, spams fixed slow homing missiles, no defense
|
|
1219
|
-
* - Lich (tier 2): Mobile + defense, adaptive strong-tracking missiles (minTurnRate 1.0)
|
|
1220
|
-
* - Archlich (tier 3): Future — converging web patterns, impossible to escape
|
|
1388
|
+
* Analyze all threats from enemy projectiles.
|
|
1221
1389
|
*
|
|
1222
|
-
*
|
|
1390
|
+
* @param myPos - Current position of the wizard
|
|
1391
|
+
* @param projectiles - All projectiles in the game
|
|
1392
|
+
* @param myProjectiles - Only the bot's own projectiles (used for filtering)
|
|
1393
|
+
* @param ticksUntilReady - Ticks until wizard can start a new action
|
|
1394
|
+
* @returns Array of analyzed threats sorted by ticksToImpact (soonest first)
|
|
1223
1395
|
*/
|
|
1224
|
-
declare
|
|
1396
|
+
declare function analyzeThreats(myPos: Position, projectiles: ProjectileState[], myProjectiles: ProjectileState[], ticksUntilReady: number): AnalyzedThreat[];
|
|
1225
1397
|
|
|
1226
1398
|
/**
|
|
1227
|
-
*
|
|
1399
|
+
* VIBEMANCER - ACTION BUILDERS
|
|
1228
1400
|
*
|
|
1229
|
-
*
|
|
1230
|
-
* exploitation. Defense, movement, and standard offense are identical to Lich.
|
|
1231
|
-
* The T3 advantage: when the enemy is locked in GCD/cast, fires fast straight
|
|
1232
|
-
* punish missiles that land during the vulnerability window.
|
|
1401
|
+
* Fluent API for constructing bot actions with type-safe chaining.
|
|
1233
1402
|
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1403
|
+
* UNITS REFERENCE (100 ticks = 1 second):
|
|
1404
|
+
* Position: absolute world coordinates, 0-800 on each axis (800×800 arena)
|
|
1405
|
+
* Movement: direction vector, magnitude clamped to max 1 (speed in [0, 1])
|
|
1406
|
+
* Speed: units per tick (player moves at 1 unit/tick = 100 units/sec)
|
|
1407
|
+
* Duration: ticks (divide by 100 for seconds)
|
|
1408
|
+
* Angles: degrees (0°=right, 90°=down, 180°=left, 270°=up)
|
|
1409
|
+
* Damage: raw HP removed on hit (wizard has 60 HP)
|
|
1410
|
+
* Turn rate: degrees per tick the missile can rotate
|
|
1236
1411
|
*/
|
|
1237
|
-
declare const Archlich: WizardFunction;
|
|
1238
1412
|
|
|
1239
1413
|
/**
|
|
1240
|
-
*
|
|
1414
|
+
* Channel a shield that blocks incoming damage.
|
|
1241
1415
|
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
* emergency blinks. A straightforward ranged caster that trades
|
|
1246
|
-
* consistency for adaptability.
|
|
1416
|
+
* Starts at 90% block, decays by 20% per second, minimum 30%.
|
|
1417
|
+
* Takes 20 ticks (0.2s) to activate. Movement is disabled while channeling.
|
|
1418
|
+
* Cancel anytime with cancel(). Triggers 100-tick (1s) GCD after cancel.
|
|
1247
1419
|
*
|
|
1248
|
-
*
|
|
1249
|
-
* - Flamecaller (tier 1): Fixed homing missiles, basic strafe and defense
|
|
1250
|
-
* - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks
|
|
1251
|
-
* - Infernalist (tier 3): Future — overwhelming adaptive fire
|
|
1420
|
+
* Can chain .move() — movement applies during the 20-tick cast, NOT during channel.
|
|
1252
1421
|
*
|
|
1253
|
-
*
|
|
1422
|
+
* @example
|
|
1423
|
+
* return shield(); // shield and stay still
|
|
1424
|
+
* return shield().move(1, 0); // move right while cast starts
|
|
1254
1425
|
*/
|
|
1255
|
-
declare
|
|
1256
|
-
|
|
1426
|
+
declare function shield(): ActionBuilder;
|
|
1257
1427
|
/**
|
|
1258
|
-
*
|
|
1428
|
+
* Cast a missile spell.
|
|
1259
1429
|
*
|
|
1260
|
-
*
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
* Shields undodgeable threats, emergency blinks. A versatile ranged caster
|
|
1264
|
-
* that adapts its missiles to the situation.
|
|
1430
|
+
* Cast time scales with damage, speed, duration, and turn rate — bigger missiles
|
|
1431
|
+
* take longer to cast. While casting you move at 50% speed. After firing, 100-tick
|
|
1432
|
+
* (1s) GCD before next spell.
|
|
1265
1433
|
*
|
|
1266
|
-
*
|
|
1267
|
-
*
|
|
1268
|
-
* - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks
|
|
1269
|
-
* - Infernalist (tier 3): Future — overwhelming adaptive fire
|
|
1434
|
+
* Repeated similar missiles cast 20% faster (warmup bonus). Switching styles
|
|
1435
|
+
* incurs a 20% penalty.
|
|
1270
1436
|
*
|
|
1271
|
-
*
|
|
1272
|
-
*/
|
|
1273
|
-
declare const Pyromancer: WizardFunction;
|
|
1274
|
-
|
|
1275
|
-
/**
|
|
1276
|
-
* Bot: Infernalist
|
|
1437
|
+
* Can chain .move() for simultaneous movement while casting.
|
|
1277
1438
|
*
|
|
1278
|
-
*
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
1439
|
+
* @param config - Missile stats:
|
|
1440
|
+
* - damage: HP removed on hit (1-60 typical). Also sets hitbox: radius = 2 + 0.1×damage.
|
|
1441
|
+
* - speed: units/tick (min 1.5). Player moves at 1 u/t, so 5 = 5× player speed.
|
|
1442
|
+
* - duration: ticks the missile lives (min 10). Range ≈ speed × duration.
|
|
1443
|
+
* - turnRate: degrees/tick of homing (0 = straight line, 3 = moderate homing, 5+ = strong).
|
|
1444
|
+
* Negative = no homing + minor speed cost reduction.
|
|
1445
|
+
* @param ai - Called every tick to control missile steering. Use getMissileContext()
|
|
1446
|
+
* to read the missile's state (position, rotation, speed, etc.), the full game
|
|
1447
|
+
* state (worldState), and a seeded PRNG (random). Return turnToward(x, y) to home
|
|
1448
|
+
* toward a position, or flyStraight() to fly straight.
|
|
1449
|
+
* @param direction - Launch angle in degrees (0°=right, 90°=down, 180°=left, 270°=up).
|
|
1450
|
+
* Tip: use Math.atan2(dy, dx) * (180 / Math.PI) to aim at a target.
|
|
1281
1451
|
*
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
1285
|
-
*
|
|
1286
|
-
* - Progressive cast-cancel: graduated thresholds
|
|
1452
|
+
* @example
|
|
1453
|
+
* // Straight missile aimed at enemy
|
|
1454
|
+
* const angle = Math.atan2(dy, dx) * (180 / Math.PI);
|
|
1455
|
+
* return missile({damage: 15, speed: 6, duration: 200, turnRate: 0}, () => flyStraight(), angle);
|
|
1287
1456
|
*
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1457
|
+
* // Homing missile that tracks enemy
|
|
1458
|
+
* return missile(
|
|
1459
|
+
* {damage: 10, speed: 5, duration: 300, turnRate: 3},
|
|
1460
|
+
* () => {
|
|
1461
|
+
* const ctx = getMissileContext();
|
|
1462
|
+
* const enemy = ctx.worldState.enemies[0];
|
|
1463
|
+
* return enemy ? turnToward(enemy.position.x, enemy.position.y) : flyStraight();
|
|
1464
|
+
* },
|
|
1465
|
+
* angle,
|
|
1466
|
+
* );
|
|
1290
1467
|
*/
|
|
1291
|
-
declare
|
|
1292
|
-
|
|
1468
|
+
declare function missile(config: MissileConfig, ai: MissileFunction$1, direction: number): ActionBuilder;
|
|
1293
1469
|
/**
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
* BEHAVIOR: Uses interceptAngle to calculate where the enemy will be and fires
|
|
1297
|
-
* fast, non-homing missiles (speed 8, turnRate 0) along the predicted path.
|
|
1298
|
-
* Strafes at medium range (300-400), shields undodgeable threats, emergency
|
|
1299
|
-
* blinks when shield isn't available. The key mechanic is PREDICTION — these
|
|
1300
|
-
* missiles don't track, they go exactly where you calculated the enemy would be.
|
|
1470
|
+
* Teleport to an absolute position on the arena.
|
|
1301
1471
|
*
|
|
1302
|
-
*
|
|
1303
|
-
*
|
|
1304
|
-
*
|
|
1305
|
-
*
|
|
1306
|
-
* and skill-based aiming — everything this bot is about.
|
|
1472
|
+
* Max range: 300 units from current position (clamped by engine if further).
|
|
1473
|
+
* Cast time: 10 ticks (0.1s). Cooldown scales with distance:
|
|
1474
|
+
* - 100 units → 100 ticks (1s)
|
|
1475
|
+
* - 300 units → 2000 ticks (20s)
|
|
1307
1476
|
*
|
|
1308
|
-
*
|
|
1309
|
-
* - Spellshot (tier 1): Basic intercept prediction, non-homing missiles
|
|
1310
|
-
* - Spelltracer (tier 2): Future — predictive homing (missiles that lead AND track)
|
|
1311
|
-
* - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks
|
|
1312
|
-
* The naming progression: shot (single bullet) → tracer (bullet that tracks a path)
|
|
1313
|
-
* → seeker (actively hunts). Each tier adds more intelligence to the projectile,
|
|
1314
|
-
* evolving from "I calculate where you'll be" to "my missile calculates where you'll be."
|
|
1477
|
+
* Cannot chain .move() — blink IS the movement.
|
|
1315
1478
|
*
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1479
|
+
* @param x - Target X position (0-800, absolute world coordinate)
|
|
1480
|
+
* @param y - Target Y position (0-800, absolute world coordinate)
|
|
1318
1481
|
*
|
|
1319
|
-
*
|
|
1482
|
+
* @example
|
|
1483
|
+
* return blink(400, 400); // blink to center
|
|
1484
|
+
* return blink(enemy.position.x, enemy.position.y); // blink to enemy
|
|
1320
1485
|
*/
|
|
1321
|
-
declare
|
|
1322
|
-
|
|
1486
|
+
declare function blink(x: number, y: number): FinalAction;
|
|
1323
1487
|
/**
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1326
|
-
* BEHAVIOR: Enhanced ranged sniper with adaptive missile fitting and intercept
|
|
1327
|
-
* prediction. Uses fitMissileToBudget to find the highest-damage fast missile
|
|
1328
|
-
* that fits the safe window, then fires it along the predicted intercept angle.
|
|
1329
|
-
* Maintains medium-long range (300-450), shields undodgeable threats with proper
|
|
1330
|
-
* timing, emergency blinks, and distance blinks when cornered. The key mechanic
|
|
1331
|
-
* is still PREDICTION — but now with adaptive damage optimization.
|
|
1488
|
+
* Cancel current cast or channel (e.g. stop shielding to attack).
|
|
1332
1489
|
*
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1335
|
-
* - Spelltracer (tier 2): + adaptive fitting, timed defense, distance management
|
|
1336
|
-
* - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks
|
|
1490
|
+
* Canceling a cast/channel triggers 100-tick (1s) GCD.
|
|
1491
|
+
* Can chain .move() for simultaneous movement.
|
|
1337
1492
|
*
|
|
1338
|
-
*
|
|
1493
|
+
* @example
|
|
1494
|
+
* return cancel().move(-1, 0); // cancel and dodge left
|
|
1339
1495
|
*/
|
|
1340
|
-
declare
|
|
1341
|
-
|
|
1496
|
+
declare function cancel(): ActionBuilder;
|
|
1342
1497
|
/**
|
|
1343
|
-
*
|
|
1498
|
+
* Move in a direction without casting any spell.
|
|
1344
1499
|
*
|
|
1345
|
-
*
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
1348
|
-
* unavoidable. Proactive distance control via closing rate detection.
|
|
1500
|
+
* This is a **direction vector**, not a target position. Values are in the
|
|
1501
|
+
* range [-1, 1] where 1 = full speed. Larger values (like raw position
|
|
1502
|
+
* deltas) are clamped to full speed automatically.
|
|
1349
1503
|
*
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
1352
|
-
* - Proactive distance blink: monitors closing rate, blinks before danger zone
|
|
1353
|
-
* - Progressive cast-cancel: graduated damage thresholds
|
|
1354
|
-
* - Warmup exploitation: always passes lastMissileConfig
|
|
1504
|
+
* @param x - Horizontal direction (positive = right, negative = left)
|
|
1505
|
+
* @param y - Vertical direction (positive = down, negative = up)
|
|
1355
1506
|
*
|
|
1356
|
-
*
|
|
1357
|
-
*
|
|
1507
|
+
* @example
|
|
1508
|
+
* return move(1, 0); // move right at full speed
|
|
1509
|
+
* return move(dx / dist, dy / dist); // normalized unit vector = full speed
|
|
1510
|
+
* return move(enemy.position.x - myPos.x, enemy.position.y - myPos.y); // raw delta = clamped to full speed
|
|
1358
1511
|
*/
|
|
1359
|
-
declare
|
|
1360
|
-
|
|
1512
|
+
declare function move(x: number, y: number): FinalAction;
|
|
1361
1513
|
/**
|
|
1362
|
-
*
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
*
|
|
1367
|
-
*
|
|
1368
|
-
* The unique trait is cast-interruption: fires quick missiles specifically when the
|
|
1369
|
-
* enemy is casting, punishing long cast times.
|
|
1514
|
+
* Do nothing — no action, no movement.
|
|
1515
|
+
*/
|
|
1516
|
+
declare function idle(): FinalAction;
|
|
1517
|
+
/**
|
|
1518
|
+
* Steer the missile toward a world position.
|
|
1519
|
+
* The engine clamps the actual turn to the missile's turnRate.
|
|
1370
1520
|
*
|
|
1371
|
-
*
|
|
1372
|
-
*
|
|
1373
|
-
|
|
1374
|
-
|
|
1521
|
+
* @param x - Target X position (world coordinates)
|
|
1522
|
+
* @param y - Target Y position (world coordinates)
|
|
1523
|
+
*/
|
|
1524
|
+
declare function turnToward(x: number, y: number): MissileAction;
|
|
1525
|
+
/**
|
|
1526
|
+
* Steer the missile toward a specific angle (degrees).
|
|
1527
|
+
* The engine clamps the actual turn to the missile's turnRate.
|
|
1375
1528
|
*
|
|
1376
|
-
*
|
|
1529
|
+
* @param degrees - Target rotation in degrees (0=right, 90=down, 180=left, 270=up)
|
|
1530
|
+
*/
|
|
1531
|
+
declare function turnToAngle(degrees: number): MissileAction;
|
|
1532
|
+
/**
|
|
1533
|
+
* Fly straight — no steering this tick.
|
|
1534
|
+
*/
|
|
1535
|
+
declare function flyStraight(): MissileAction;
|
|
1536
|
+
/**
|
|
1537
|
+
* Extract MissileActions from a MissileAction.
|
|
1538
|
+
* Used by the engine to get the actual missile action.
|
|
1539
|
+
*/
|
|
1540
|
+
declare function extractMissileAction(action: MissileAction): MissileActions;
|
|
1541
|
+
/**
|
|
1542
|
+
* Extract WizardActions from a FinalAction.
|
|
1543
|
+
* Used by the engine to get the actual action.
|
|
1377
1544
|
*/
|
|
1378
|
-
declare
|
|
1545
|
+
declare function extractAction(finalAction: FinalAction): WizardActions;
|
|
1379
1546
|
|
|
1380
1547
|
/**
|
|
1381
|
-
*
|
|
1548
|
+
* VIBEMANCER - PARAMETER RUNTIME
|
|
1382
1549
|
*
|
|
1383
|
-
*
|
|
1384
|
-
*
|
|
1385
|
-
* Same close-range playstyle: shields undodgeable threats, blinks to close distance
|
|
1386
|
-
* or escape, aggressive hit-trading when health allows. The adaptive fitting means
|
|
1387
|
-
* every attack is optimized for the current situation — no wasted cast time.
|
|
1550
|
+
* Provides the useParam() hook for bots to declare tunable parameters,
|
|
1551
|
+
* and the infrastructure for the optimizer to inject/discover parameter values.
|
|
1388
1552
|
*
|
|
1389
|
-
*
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
* - Archmage (tier 3): Future — supreme duelist, perfect tactical mastery
|
|
1553
|
+
* Design: Module-level state (JS is single-threaded, no race conditions).
|
|
1554
|
+
* The optimizer sets param values before running a bot, and clears them after.
|
|
1555
|
+
* During discovery, all useParam calls are recorded.
|
|
1393
1556
|
*
|
|
1394
|
-
*
|
|
1395
|
-
*/
|
|
1396
|
-
declare const Warmage: WizardFunction;
|
|
1397
|
-
|
|
1398
|
-
/**
|
|
1399
|
-
* Bot: Archmage
|
|
1557
|
+
* ## useParam API
|
|
1400
1558
|
*
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
* HP-aware: ahead → aggressive close range; behind → defensive ranged kiting.
|
|
1559
|
+
* ```typescript
|
|
1560
|
+
* // Basic: just a value, no optimizer config
|
|
1561
|
+
* const damage = useParam('damage', 15);
|
|
1405
1562
|
*
|
|
1406
|
-
*
|
|
1407
|
-
*
|
|
1408
|
-
* - Vulnerability-timed blinks: gap-close during enemy cast/GCD
|
|
1409
|
-
* - HP-aware aggression: adjusts distance + risk tolerance based on HP differential
|
|
1410
|
-
* - Progressive cast-cancel: graduated thresholds
|
|
1563
|
+
* // With range: optimizer searches value ± range (sliding window)
|
|
1564
|
+
* const distance = useParam('distance', 350, {range: 150, min: 0});
|
|
1411
1565
|
*
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1566
|
+
* // With fixed min/max: optimizer searches [min, max] (fixed bounds)
|
|
1567
|
+
* const damage = useParam('damage', 15, {min: 5, max: 25});
|
|
1568
|
+
*
|
|
1569
|
+
* // With all: range defines search radius, min/max clamp it
|
|
1570
|
+
* const fraction = useParam('fraction', 0.25, {range: 0.2, min: 0, max: 1});
|
|
1571
|
+
*
|
|
1572
|
+
* // With custom step count: optimizer tests 20 values instead of default 10
|
|
1573
|
+
* const distance = useParam('distance', 500, {range: 200, min: 0, steps: 20});
|
|
1574
|
+
* ```
|
|
1575
|
+
*
|
|
1576
|
+
* - **Arg 1** `name` — unique parameter name (must be consistent across ticks)
|
|
1577
|
+
* - **Arg 2** `value` — the actual value used in gameplay. This is what your bot
|
|
1578
|
+
* uses during matches. The optimizer script automatically updates this value.
|
|
1579
|
+
* - **Arg 3** `config` (optional) — optimizer search configuration:
|
|
1580
|
+
* - `range` — search radius: optimizer checks `value ± range`. The auto-optimizer
|
|
1581
|
+
* rewrites `value` after each run, so the search window slides automatically.
|
|
1582
|
+
* - `min` / `max` — hard constraints (e.g., distance ≥ 0, fraction ≤ 1).
|
|
1583
|
+
* When `range` is omitted, these define fixed search bounds (old-style).
|
|
1584
|
+
* - `steps` — how many evenly-spaced values the optimizer tests per pass (default: 10).
|
|
1585
|
+
* - `substeps` — steps to use in refinement passes (passes 2+). Set to 0 to freeze
|
|
1586
|
+
* after pass 1 (ideal for boolean params). Defaults to `steps` if not specified.
|
|
1587
|
+
* - At least `range` or both `min` + `max` must be provided.
|
|
1588
|
+
*
|
|
1589
|
+
* Without optimizer config, useParam simply returns `value` every tick.
|
|
1590
|
+
* With optimizer config, the offline optimizer script can override the value during search.
|
|
1591
|
+
*
|
|
1592
|
+
* ## Rules of Hooks
|
|
1593
|
+
* useParam follows the same rules as useState/useEffect/etc:
|
|
1594
|
+
* - Must be called at the top level of your bot function (not inside conditionals)
|
|
1595
|
+
* - Must be called in the same order every tick
|
|
1596
|
+
* - Violations are detected and throw errors
|
|
1414
1597
|
*/
|
|
1415
|
-
declare const Archmage: WizardFunction;
|
|
1416
1598
|
|
|
1417
1599
|
/**
|
|
1418
|
-
*
|
|
1600
|
+
* Declaration of a tunable parameter, as discovered by the optimizer.
|
|
1601
|
+
*/
|
|
1602
|
+
interface ParamDeclaration {
|
|
1603
|
+
name: string;
|
|
1604
|
+
value: number;
|
|
1605
|
+
range?: number;
|
|
1606
|
+
min?: number;
|
|
1607
|
+
max?: number;
|
|
1608
|
+
steps: number;
|
|
1609
|
+
/** Steps to use in refinement passes (passes 2+). 0 = freeze after pass 1. Defaults to `steps`. */
|
|
1610
|
+
substeps?: number;
|
|
1611
|
+
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Declare a tunable parameter. Returns the current value (optimizer-injected or the provided value).
|
|
1419
1614
|
*
|
|
1420
|
-
*
|
|
1421
|
-
*
|
|
1422
|
-
*
|
|
1423
|
-
*
|
|
1424
|
-
* management (350 units, ±30 band).
|
|
1615
|
+
* @param name - Unique parameter name (consistent across ticks)
|
|
1616
|
+
* @param value - The gameplay value. The auto-optimizer rewrites this in source code.
|
|
1617
|
+
* @param config - Optional optimizer search configuration
|
|
1618
|
+
* @returns The optimizer-injected value during optimization, or `value` during normal play
|
|
1425
1619
|
*
|
|
1426
|
-
*
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1429
|
-
* - Stormforger (tier 3): Future — supreme berserker, perfect aggression
|
|
1620
|
+
* @example
|
|
1621
|
+
* // Simple: no optimizer config
|
|
1622
|
+
* const damage = useParam('damage', 15);
|
|
1430
1623
|
*
|
|
1431
|
-
*
|
|
1624
|
+
* // With range: optimizer searches value ± range (sliding window)
|
|
1625
|
+
* const distance = useParam('distance', 350, {range: 150, min: 0});
|
|
1626
|
+
*
|
|
1627
|
+
* // With fixed min/max: optimizer searches [min, max]
|
|
1628
|
+
* const damage = useParam('damage', 15, {min: 5, max: 25});
|
|
1629
|
+
*/
|
|
1630
|
+
declare function useParam(name: string, value: number, config?: {
|
|
1631
|
+
range?: number;
|
|
1632
|
+
min?: number;
|
|
1633
|
+
max?: number;
|
|
1634
|
+
steps?: number;
|
|
1635
|
+
substeps?: number;
|
|
1636
|
+
}): number;
|
|
1637
|
+
/**
|
|
1638
|
+
* Inject parameter values for the next bot execution.
|
|
1639
|
+
* The wrapped bot will read these values via useParam().
|
|
1640
|
+
*/
|
|
1641
|
+
declare function setParamValues(values: Record<string, number>): void;
|
|
1642
|
+
/**
|
|
1643
|
+
* Clear injected parameter values. useParam() will return its provided value.
|
|
1644
|
+
*/
|
|
1645
|
+
declare function clearParamValues(): void;
|
|
1646
|
+
/**
|
|
1647
|
+
* Start discovery mode. All subsequent useParam() calls with optimizer config
|
|
1648
|
+
* will register their declarations.
|
|
1432
1649
|
*/
|
|
1433
|
-
declare
|
|
1650
|
+
declare function startDiscovery(): void;
|
|
1651
|
+
/**
|
|
1652
|
+
* Stop discovery mode and return all discovered parameter declarations.
|
|
1653
|
+
*/
|
|
1654
|
+
declare function stopDiscovery(): ParamDeclaration[];
|
|
1655
|
+
/**
|
|
1656
|
+
* Wrap a bot function to inject specific parameter values.
|
|
1657
|
+
* The returned function sets params before calling the bot and clears them after.
|
|
1658
|
+
*/
|
|
1659
|
+
declare function wrapWithParams(bot: WizardFunction, params: Record<string, number>): WizardFunction;
|
|
1434
1660
|
|
|
1435
1661
|
/**
|
|
1436
|
-
* Bot:
|
|
1662
|
+
* Bot: TargetDummy
|
|
1437
1663
|
*
|
|
1438
|
-
* BEHAVIOR:
|
|
1439
|
-
* AND predictive homing missiles. Combines aggressive fighting philosophy with
|
|
1440
|
-
* optimized damage output. Uses budget-based missile fitting to maximize damage
|
|
1441
|
-
* within safe windows. Falls back to quick missiles under pressure. Same smart
|
|
1442
|
-
* trade/shield decisions as Stormchaser but with better resource usage.
|
|
1664
|
+
* BEHAVIOR: Does absolutely nothing. No movement, no spells, no AI.
|
|
1443
1665
|
*
|
|
1444
|
-
*
|
|
1445
|
-
*
|
|
1446
|
-
*
|
|
1447
|
-
*
|
|
1666
|
+
* NAMING RATIONALE: "Target Dummy" is universal MMO player vocabulary for the
|
|
1667
|
+
* practice objects found in capital cities. Every WoW/FFXIV player has beaten
|
|
1668
|
+
* on a target dummy to test DPS rotations. That's exactly what this bot is —
|
|
1669
|
+
* a punching bag for testing missile mechanics and baseline damage output.
|
|
1670
|
+
* Nobody calls them "training dummies"; the player term is always "target dummy."
|
|
1448
1671
|
*
|
|
1449
|
-
*
|
|
1672
|
+
* STANDALONE — no tier progression. It's a test fixture, not a combatant.
|
|
1450
1673
|
*/
|
|
1451
|
-
declare
|
|
1674
|
+
declare function TargetDummy(): FinalAction;
|
|
1452
1675
|
|
|
1453
1676
|
/**
|
|
1454
|
-
* Bot:
|
|
1677
|
+
* Bot: Rookie
|
|
1455
1678
|
*
|
|
1456
|
-
* BEHAVIOR:
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
1459
|
-
*
|
|
1460
|
-
* accurate straight shots that arrive before the enemy can react.
|
|
1679
|
+
* BEHAVIOR: Stands perfectly still and fires straight (non-homing) missiles at
|
|
1680
|
+
* the enemy. No movement, no dodging, no shielding. Knows one spell and uses
|
|
1681
|
+
* it on cooldown. The wizard equivalent of an FPS player who stands in the open
|
|
1682
|
+
* and holds left-click.
|
|
1461
1683
|
*
|
|
1462
|
-
*
|
|
1463
|
-
*
|
|
1464
|
-
*
|
|
1465
|
-
*
|
|
1684
|
+
* NAMING RATIONALE: "Rookie" is the universal term for a first-timer who barely
|
|
1685
|
+
* knows what they're doing. This bot is a day-one player who learned how to cast
|
|
1686
|
+
* missile and nothing else. No movement, no defense, just raw "I press the button."
|
|
1687
|
+
* We considered "Noob" (more accurate) but Rookie is less abrasive while conveying
|
|
1688
|
+
* the same thing — a beginner who doesn't know any better.
|
|
1466
1689
|
*
|
|
1467
|
-
*
|
|
1690
|
+
* STANDALONE — no tier progression. Rookies either learn to play a real class
|
|
1691
|
+
* or quit. This bot represents the rock-bottom of "at least it shoots."
|
|
1468
1692
|
*/
|
|
1469
|
-
declare
|
|
1693
|
+
declare function Rookie(): FinalAction;
|
|
1470
1694
|
|
|
1471
1695
|
/**
|
|
1472
|
-
* Bot:
|
|
1473
|
-
*
|
|
1474
|
-
* BEHAVIOR: Maintains medium range (350 units), strafes constantly to dodge
|
|
1475
|
-
* missiles, and fires homing missiles (damage 10, speed 4, turnRate 2). Heavy
|
|
1476
|
-
* emphasis on movement — 80% strafe intensity when not dodging, 100% when dodging.
|
|
1477
|
-
* Shields only undodgeable threats, emergency blinks when shield isn't available.
|
|
1478
|
-
* The constant circular strafing motion traces patterns like thread being spun.
|
|
1696
|
+
* Bot: Critter
|
|
1479
1697
|
*
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1482
|
-
*
|
|
1483
|
-
*
|
|
1484
|
-
* a web-like pattern of missiles and movement that traps opponents.
|
|
1698
|
+
* BEHAVIOR: Picks random valid actions each tick — random movement, random spells,
|
|
1699
|
+
* random missile configs, random directions. Occasionally cancels its own casts.
|
|
1700
|
+
* Uses engine-provided seeded random for deterministic behavior. Useful for
|
|
1701
|
+
* finding edge cases in the engine, but completely useless in combat.
|
|
1485
1702
|
*
|
|
1486
|
-
*
|
|
1487
|
-
*
|
|
1488
|
-
*
|
|
1489
|
-
*
|
|
1490
|
-
*
|
|
1491
|
-
* (constrains and traps). Each tier's projectile web becomes harder to escape.
|
|
1703
|
+
* NAMING RATIONALE: In WoW, critters are the 1-HP ambient mobs (rabbits, squirrels,
|
|
1704
|
+
* prairie dogs) that wander around doing nothing useful and die to literally anything.
|
|
1705
|
+
* This bot is the wizard equivalent — it flails around randomly and gets destroyed by
|
|
1706
|
+
* anyone with a plan. The word "Critter" immediately tells any gamer "this thing is
|
|
1707
|
+
* helpless and exists only to fill space."
|
|
1492
1708
|
*
|
|
1493
|
-
*
|
|
1709
|
+
* STANDALONE — no tier progression. Critters don't level up. However, a future
|
|
1710
|
+
* "Hogger" bot could be an elite critter: same chaotic spirit but actually dangerous
|
|
1711
|
+
* (like the famous WoW elite that wipes unprepared lowbies).
|
|
1494
1712
|
*/
|
|
1495
|
-
declare
|
|
1713
|
+
declare function Critter(): FinalAction;
|
|
1496
1714
|
|
|
1497
1715
|
/**
|
|
1498
|
-
* Bot:
|
|
1499
|
-
*
|
|
1500
|
-
* BEHAVIOR: Enhanced Spellspinner with adaptive missile fitting. Same medium-range
|
|
1501
|
-
* kiting playstyle — maintains distance, strafes heavily — but uses fitMissileToBudget
|
|
1502
|
-
* to maximize damage within safe attack windows. Always uses homing missiles since
|
|
1503
|
-
* kiting means enemies are always moving. More sophisticated than Spellspinner's
|
|
1504
|
-
* fixed damage/speed/turnRate configuration.
|
|
1716
|
+
* Bot: Hogger
|
|
1505
1717
|
*
|
|
1506
|
-
*
|
|
1507
|
-
*
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
1510
|
-
*
|
|
1718
|
+
* BEHAVIOR: The elite critter. Chaotic and unpredictable but genuinely dangerous.
|
|
1719
|
+
* Randomly varies missile configs each cast (damage 7-15, speed 3-8, random homing),
|
|
1720
|
+
* moves erratically but still somewhat toward/away from the enemy, shields when
|
|
1721
|
+
* in real danger, and blinks unpredictably. The randomness makes Hogger hard to
|
|
1722
|
+
* predict — you never know if the next missile will be a slow tracker or a fast
|
|
1723
|
+
* snipe. Unlike Critter's pure randomness, Hogger has enough combat awareness
|
|
1724
|
+
* to actually win fights.
|
|
1511
1725
|
*
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1514
|
-
*
|
|
1515
|
-
*
|
|
1726
|
+
* NAMING RATIONALE: In WoW, Hogger is the iconic level 11 elite gnoll in Elwynn
|
|
1727
|
+
* Forest who infamously kills unprepared lowbies. He's technically a basic mob
|
|
1728
|
+
* but hits way harder than expected. This bot is the Critter that learned to
|
|
1729
|
+
* fight — still chaotic, still a bit dumb, but capable of ending you if you
|
|
1730
|
+
* underestimate it. "Hogger" is one of WoW's most recognizable references and
|
|
1731
|
+
* perfectly captures "deceptively dangerous chaos."
|
|
1516
1732
|
*
|
|
1517
|
-
*
|
|
1733
|
+
* STANDALONE — no tier progression. There's only one Hogger.
|
|
1518
1734
|
*/
|
|
1519
|
-
declare
|
|
1735
|
+
declare function Hogger(): FinalAction;
|
|
1520
1736
|
|
|
1521
1737
|
/**
|
|
1522
|
-
* Bot:
|
|
1523
|
-
*
|
|
1524
|
-
* BEHAVIOR: Enhanced Spellweaver with vulnerability exploitation. Same medium-range
|
|
1525
|
-
* kiting playstyle — maintains distance, strafes heavily, uses fitMissileToBudget
|
|
1526
|
-
* for adaptive homing missiles. The T3 upgrade adds a punish mode that fires fast
|
|
1527
|
-
* straight missiles timed to land while the enemy is locked in a cast or GCD,
|
|
1528
|
-
* when they cannot shield. Defense, movement, and standard offense are identical
|
|
1529
|
-
* to Spellweaver.
|
|
1530
|
-
*
|
|
1531
|
-
* NAMING RATIONALE: A binder constrains and locks down opponents. Where the
|
|
1532
|
-
* Spellweaver optimizes missile patterns (adaptive fitting), the Spellbinder
|
|
1533
|
-
* reads the enemy's state and punishes vulnerability windows — binding them
|
|
1534
|
-
* to their commitments with unavoidable damage.
|
|
1738
|
+
* Bot: Doombringer
|
|
1535
1739
|
*
|
|
1536
|
-
*
|
|
1537
|
-
*
|
|
1538
|
-
*
|
|
1539
|
-
*
|
|
1740
|
+
* BEHAVIOR: Fires a single maximum-damage homing missile with infinite budget.
|
|
1741
|
+
* No damage cap — goes for the biggest possible hit. Exists as a benchmark to
|
|
1742
|
+
* demonstrate why lower-damage + shield play is superior. Has basic shield
|
|
1743
|
+
* defense but no sophisticated tactics. One fat cast, one fat hit.
|
|
1540
1744
|
*
|
|
1541
|
-
*
|
|
1745
|
+
* STANDALONE — no tier progression. Benchmark/test bot.
|
|
1542
1746
|
*/
|
|
1543
|
-
declare
|
|
1747
|
+
declare function Doombringer(): FinalAction;
|
|
1748
|
+
|
|
1749
|
+
declare function Turtle(): FinalAction;
|
|
1544
1750
|
|
|
1545
1751
|
/**
|
|
1546
|
-
*
|
|
1547
|
-
*
|
|
1548
|
-
* Single source of truth for all bots, ordered from weakest to strongest.
|
|
1549
|
-
* Run the tournament test to determine the correct ordering.
|
|
1550
|
-
*
|
|
1551
|
-
* To reorder: run `npx vitest run tests/bots/tournament.test.ts`
|
|
1552
|
-
* and update the list below based on the results.
|
|
1553
|
-
*
|
|
1554
|
-
* BOT NAMING SCHEME (3-tier progression):
|
|
1752
|
+
* Bot: Sentinel
|
|
1555
1753
|
*
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
1558
|
-
*
|
|
1559
|
-
*
|
|
1560
|
-
* | Homing | Bonemancer | Lich | Archlich |
|
|
1561
|
-
* | Caster | Flamecaller | Pyromancer | Infernalist |
|
|
1562
|
-
* | Melee | Shadowblade | Nightblade | Voidblade |
|
|
1563
|
-
* | Sniper | Spellshot | Spelltracer | Spellseeker |
|
|
1564
|
-
* | Berserker | Stormchaser | Stormcaller | Stormforger |
|
|
1565
|
-
* | Kiter | Spellspinner | Spellweaver | Spellbinder |
|
|
1754
|
+
* BEHAVIOR: Stationary tank with last-moment shielding AND two-tier offense.
|
|
1755
|
+
* Like Turtle, never moves and shields at the last moment. Unlike Turtle,
|
|
1756
|
+
* fires bigger missiles (damage 20) when the safe window is large enough,
|
|
1757
|
+
* falling back to Turtle's fast missile (damage 12) when pressed.
|
|
1566
1758
|
*
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1759
|
+
* PROGRESSION LINE: Turtle → Sentinel → Golem
|
|
1760
|
+
* - Turtle (tier 1): Stationary, fixed missiles, reactive shield timing
|
|
1761
|
+
* - Sentinel (tier 2): Stationary, two-tier offense (big + fast missiles)
|
|
1762
|
+
* - Golem (tier 3): Immovable fortress, perfect shield timing
|
|
1569
1763
|
*
|
|
1764
|
+
* TIER: 2 (enhanced Turtle)
|
|
1570
1765
|
*/
|
|
1766
|
+
declare function Sentinel(): FinalAction;
|
|
1571
1767
|
|
|
1572
|
-
interface BotEntry {
|
|
1573
|
-
name: string;
|
|
1574
|
-
ai: WizardFunction;
|
|
1575
|
-
description: string;
|
|
1576
|
-
tier?: number;
|
|
1577
|
-
group: string;
|
|
1578
|
-
}
|
|
1579
|
-
interface BotGroup {
|
|
1580
|
-
label: string;
|
|
1581
|
-
bots: BotEntry[];
|
|
1582
|
-
}
|
|
1583
1768
|
/**
|
|
1584
|
-
*
|
|
1585
|
-
*
|
|
1769
|
+
* Bot: Golem
|
|
1770
|
+
*
|
|
1771
|
+
* BEHAVIOR: Stationary fortress with perfect shield timing and devastating
|
|
1772
|
+
* counterattacks during enemy vulnerability windows. Reads enemy cast/GCD
|
|
1773
|
+
* state to time punish missiles that land when the enemy can't shield.
|
|
1774
|
+
* Handles multi-missile volleys by holding shield through consecutive impacts.
|
|
1775
|
+
* Uses progressive cast-cancel thresholds for optimal damage trading.
|
|
1776
|
+
*
|
|
1777
|
+
* KEY IMPROVEMENTS OVER SENTINEL:
|
|
1778
|
+
* - Counterattack punish: fires during enemy cast/GCD recovery
|
|
1779
|
+
* - Multi-threat volley awareness: holds shield through consecutive hits
|
|
1780
|
+
* - Progressive cast-cancel: graduated damage thresholds
|
|
1781
|
+
* - Perfect shield timing: uses ticksToStartShield precisely
|
|
1782
|
+
*
|
|
1783
|
+
* PROGRESSION LINE: Turtle → Sentinel → Golem
|
|
1784
|
+
* TIER: 3 (elite Defensive line)
|
|
1586
1785
|
*/
|
|
1587
|
-
declare
|
|
1588
|
-
declare const ALL_BOTS: BotEntry[];
|
|
1786
|
+
declare function Golem(): FinalAction;
|
|
1589
1787
|
|
|
1590
1788
|
/**
|
|
1591
|
-
*
|
|
1789
|
+
* Bot: Shadowblade
|
|
1592
1790
|
*
|
|
1593
|
-
*
|
|
1594
|
-
*
|
|
1791
|
+
* BEHAVIOR: Melee assassin. Blinks to the enemy, then lands devastating point-blank
|
|
1792
|
+
* stab attacks (15 damage, 30u range, ~60 tick cast = 2-hit kill). Runs directly
|
|
1793
|
+
* at the enemy with minimal strafe, shields undodgeable threats. The entire
|
|
1794
|
+
* strategy is: get close, stab, kill. Simple and brutal.
|
|
1595
1795
|
*
|
|
1596
|
-
*
|
|
1597
|
-
*
|
|
1598
|
-
*
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1796
|
+
* PROGRESSION LINE: Shadowblade → Nightblade → Voidblade
|
|
1797
|
+
* - Shadowblade (tier 1): Offensive blink, melee stabs, basic shield
|
|
1798
|
+
* - Nightblade (tier 2): + missile-aware blinks, adaptive stabs, timed defense
|
|
1799
|
+
* - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages
|
|
1800
|
+
*
|
|
1801
|
+
* TIER: 1 (base)
|
|
1602
1802
|
*/
|
|
1803
|
+
declare function Shadowblade(): FinalAction;
|
|
1603
1804
|
|
|
1604
1805
|
/**
|
|
1605
|
-
*
|
|
1606
|
-
*/
|
|
1607
|
-
declare function useHealth(): number;
|
|
1608
|
-
/**
|
|
1609
|
-
* Get your current position as {x, y} in world coordinates (0-800).
|
|
1610
|
-
* Position is clamped to [5, 795] (arena bounds minus wizard radius).
|
|
1611
|
-
*/
|
|
1612
|
-
declare function usePosition(): Position;
|
|
1613
|
-
/**
|
|
1614
|
-
* Get your current velocity as {x, y} in units/tick.
|
|
1615
|
-
* Max magnitude is 1 u/t when idle, 0.5 u/t when casting, 0 when channeling shield.
|
|
1616
|
-
*/
|
|
1617
|
-
declare function useVelocity(): Velocity;
|
|
1618
|
-
/**
|
|
1619
|
-
* Get your current status:
|
|
1620
|
-
* - 'idle': free to act
|
|
1621
|
-
* - 'casting': casting a spell (missile or blink). Can move at 50% speed.
|
|
1622
|
-
* - 'channeling': channeling shield. Cannot move. Cancel anytime with cancel().
|
|
1623
|
-
* - 'gcd_locked': global cooldown after spell. Can move at full speed, but cannot cast.
|
|
1624
|
-
*/
|
|
1625
|
-
declare function useStatus(): 'idle' | 'casting' | 'channeling' | 'gcd_locked';
|
|
1626
|
-
/**
|
|
1627
|
-
* Get ticks until you can start a new spell.
|
|
1806
|
+
* Bot: Nightblade
|
|
1628
1807
|
*
|
|
1629
|
-
*
|
|
1630
|
-
*
|
|
1808
|
+
* BEHAVIOR: Enhanced melee assassin. Same aggressive engagement as Shadowblade —
|
|
1809
|
+
* blinks directly to the enemy and stabs for 15 damage (2-hit kill). The tier 2
|
|
1810
|
+
* upgrade is PREEMPTIVE DEFENSE: Nightblade watches the enemy's cast bar and
|
|
1811
|
+
* shields before a point-blank missile is even launched. At melee range, missiles
|
|
1812
|
+
* arrive almost instantly after launch — too fast to react. Nightblade anticipates
|
|
1813
|
+
* the threat. Also has emergency blink and proper channeling management.
|
|
1631
1814
|
*
|
|
1632
|
-
*
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
* Get current shield block multiplier.
|
|
1815
|
+
* PROGRESSION LINE: Shadowblade → Nightblade → Voidblade
|
|
1816
|
+
* - Shadowblade (tier 1): Offensive blink, melee stabs, basic shield (reactive only)
|
|
1817
|
+
* - Nightblade (tier 2): + preemptive shield vs enemy casts, emergency blink
|
|
1818
|
+
* - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages
|
|
1637
1819
|
*
|
|
1638
|
-
*
|
|
1639
|
-
* Returns 0.3-0.9 if channeling: starts at 0.9 (blocks 90%), decays by 0.2/sec,
|
|
1640
|
-
* minimum 0.3 (blocks 30%). The remaining damage gets through:
|
|
1641
|
-
* actualDamage = incomingDamage × (1 - shieldStrength).
|
|
1820
|
+
* TIER: 2 (enhanced Shadowblade)
|
|
1642
1821
|
*/
|
|
1643
|
-
declare function
|
|
1822
|
+
declare function Nightblade(): FinalAction;
|
|
1823
|
+
|
|
1644
1824
|
/**
|
|
1645
|
-
*
|
|
1825
|
+
* Bot: Voidblade
|
|
1646
1826
|
*
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1827
|
+
* BEHAVIOR: Reactive counter-puncher. Shields everything, then fires quick stabs
|
|
1828
|
+
* during enemy vulnerability windows (GCD/casting) when they can't shield back.
|
|
1829
|
+
* At melee range, the shield blocks ~90% of incoming damage while Voidblade's
|
|
1830
|
+
* counter-stabs land at full damage — winning through attrition.
|
|
1650
1831
|
*
|
|
1651
|
-
*
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
* Get currently casting spell, or null if not casting.
|
|
1656
|
-
* Returns 'missile', 'shield', or 'blink'.
|
|
1657
|
-
*/
|
|
1658
|
-
declare function useCastingSpell(): 'missile' | 'shield' | 'blink' | null;
|
|
1659
|
-
/**
|
|
1660
|
-
* Get cast progress as {current, total} in ticks, or null if not casting.
|
|
1832
|
+
* CORE LOOP (melee range):
|
|
1833
|
+
* 1. Enemy casts missile → Voidblade blink-dodges (100% avoid) or shields (90% block)
|
|
1834
|
+
* 2. Enemy enters GCD → Voidblade fires quick stab (lands unblocked)
|
|
1835
|
+
* 3. Voidblade enters GCD → enemy recovers → repeat
|
|
1661
1836
|
*
|
|
1662
|
-
*
|
|
1663
|
-
*
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
total: number;
|
|
1668
|
-
} | null;
|
|
1669
|
-
/**
|
|
1670
|
-
* Get enemy wizard state.
|
|
1837
|
+
* KEY IMPROVEMENTS OVER NIGHTBLADE:
|
|
1838
|
+
* - Blink-dodge priority: avoids 100% of damage when blink available, shields as fallback
|
|
1839
|
+
* - Reads enemy vulnerability to time counter-stabs perfectly
|
|
1840
|
+
* - Cancel-into-defense: aborts own cast if enemy missile incoming
|
|
1841
|
+
* - Punish budget: sizes stabs to fit exactly in the vulnerability window
|
|
1671
1842
|
*
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
1674
|
-
* only their status and what's visible on the field.
|
|
1675
|
-
*/
|
|
1676
|
-
declare function useEnemy(): EnemyState;
|
|
1677
|
-
/**
|
|
1678
|
-
* Get all your active (in-flight) projectiles.
|
|
1679
|
-
* Each has position, rotation (degrees), speed (u/t), turnRate, remainingTicks.
|
|
1680
|
-
*/
|
|
1681
|
-
declare function useMyProjectiles(): ProjectileState[];
|
|
1682
|
-
/**
|
|
1683
|
-
* Get total damage you've dealt this match.
|
|
1843
|
+
* PROGRESSION LINE: Shadowblade → Nightblade → Voidblade
|
|
1844
|
+
* TIER: 3 (elite Melee line)
|
|
1684
1845
|
*/
|
|
1685
|
-
declare function
|
|
1846
|
+
declare function Voidblade(): FinalAction;
|
|
1847
|
+
|
|
1686
1848
|
/**
|
|
1687
|
-
*
|
|
1849
|
+
* Bot: Bonemancer
|
|
1850
|
+
*
|
|
1851
|
+
* BEHAVIOR: Stands still and fires slow, homing missiles constantly. Every missile
|
|
1852
|
+
* tracks the enemy with turnRate 2 — they curve relentlessly toward the target.
|
|
1853
|
+
* No movement, no shields, just an unending stream of seeking projectiles. The
|
|
1854
|
+
* missiles are slow (speed 3) but long-lived (300 ticks) and will chase you across
|
|
1855
|
+
* the entire arena.
|
|
1856
|
+
*
|
|
1857
|
+
* NAMING RATIONALE: Named after Diablo 2's Bone Necromancer ("Bonemancer"), whose
|
|
1858
|
+
* signature spell Bone Spirit is a slow-moving, auto-tracking projectile that hunts
|
|
1859
|
+
* enemies relentlessly. That's exactly what this bot does — it stands in place and
|
|
1860
|
+
* spams seeking missiles. The homing behavior is the key identity: these aren't
|
|
1861
|
+
* aimed shots, they're heat-seeking spirits that chase you down. Every D2 player
|
|
1862
|
+
* knows the Bonemancer — it's one of the most iconic builds.
|
|
1863
|
+
*
|
|
1864
|
+
* PROGRESSION LINE: Bonemancer → Lich → Archlich
|
|
1865
|
+
* - Bonemancer (tier 1): Stationary, spams slow homing missiles
|
|
1866
|
+
* - Lich (tier 2): Future — enhanced homing with adaptive missiles and defense
|
|
1867
|
+
* - Archlich (tier 3): Future — master of tracking magic, undodgeable death swarm
|
|
1868
|
+
* The progression follows the D2 necromancer power fantasy: from bone apprentice
|
|
1869
|
+
* to undead overlord, each tier's missiles become harder to escape.
|
|
1870
|
+
*
|
|
1871
|
+
* TIER: 1 (base)
|
|
1688
1872
|
*/
|
|
1689
|
-
declare function
|
|
1873
|
+
declare function Bonemancer(): FinalAction;
|
|
1874
|
+
|
|
1690
1875
|
/**
|
|
1691
|
-
*
|
|
1692
|
-
*
|
|
1876
|
+
* Bot: Lich
|
|
1877
|
+
*
|
|
1878
|
+
* BEHAVIOR: Homing missile specialist with strong-tracking adaptive missiles.
|
|
1879
|
+
* Uses fitMissileToBudget with minTurnRate 1.0 — higher than other bots (0.5) —
|
|
1880
|
+
* producing missiles with superior tracking at the cost of some damage/speed.
|
|
1881
|
+
* Strafing launches missiles from different angles, creating multi-angle pressure.
|
|
1882
|
+
*
|
|
1883
|
+
* Shields undodgeable/critical threats, emergency blinks. Cancels missile cast
|
|
1884
|
+
* only for lethal incoming damage.
|
|
1885
|
+
*
|
|
1886
|
+
* KEY DIFFERENCES FROM BONEMANCER:
|
|
1887
|
+
* - Bonemancer: stationary, no defense, fixed d=7/s=3/t=2/dur=300
|
|
1888
|
+
* - Lich: mobile, full defense, adaptive strong-tracking homing missiles
|
|
1889
|
+
*
|
|
1890
|
+
* PROGRESSION LINE: Bonemancer → Lich → Archlich
|
|
1891
|
+
* - Bonemancer (tier 1): Stationary, spams fixed slow homing missiles, no defense
|
|
1892
|
+
* - Lich (tier 2): Mobile + defense, adaptive strong-tracking missiles (minTurnRate 1.0)
|
|
1893
|
+
* - Archlich (tier 3): Future — converging web patterns, impossible to escape
|
|
1894
|
+
*
|
|
1895
|
+
* TIER: 2 (enhanced Bonemancer)
|
|
1693
1896
|
*/
|
|
1694
|
-
declare function
|
|
1897
|
+
declare function Lich(): FinalAction;
|
|
1898
|
+
|
|
1695
1899
|
/**
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1900
|
+
* Bot: Archlich
|
|
1901
|
+
*
|
|
1902
|
+
* BEHAVIOR: Lich's proven core (mobile homing specialist) plus vulnerability
|
|
1903
|
+
* exploitation. Defense, movement, and standard offense are identical to Lich.
|
|
1904
|
+
* The T3 advantage: when the enemy is locked in GCD/cast, fires fast straight
|
|
1905
|
+
* punish missiles that land during the vulnerability window.
|
|
1906
|
+
*
|
|
1907
|
+
* PROGRESSION LINE: Bonemancer → Lich → Archlich
|
|
1908
|
+
* TIER: 3 (elite Homing line)
|
|
1698
1909
|
*/
|
|
1699
|
-
declare function
|
|
1700
|
-
|
|
1701
|
-
height: number;
|
|
1702
|
-
};
|
|
1910
|
+
declare function Archlich(): FinalAction;
|
|
1911
|
+
|
|
1703
1912
|
/**
|
|
1704
|
-
*
|
|
1705
|
-
*
|
|
1913
|
+
* Bot: Flamecaller
|
|
1914
|
+
*
|
|
1915
|
+
* BEHAVIOR: Long-range homing missile caster with fixed missile config.
|
|
1916
|
+
* Maintains 350u distance, strafes to dodge, and fires standard homing
|
|
1917
|
+
* missiles (d=10, s=5, t=1, dur=180). Shields undodgeable threats,
|
|
1918
|
+
* emergency blinks. A straightforward ranged caster that trades
|
|
1919
|
+
* consistency for adaptability.
|
|
1920
|
+
*
|
|
1921
|
+
* PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist
|
|
1922
|
+
* - Flamecaller (tier 1): Fixed homing missiles, basic strafe and defense
|
|
1923
|
+
* - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks
|
|
1924
|
+
* - Infernalist (tier 3): Future — overwhelming adaptive fire
|
|
1925
|
+
*
|
|
1926
|
+
* TIER: 1 (base)
|
|
1706
1927
|
*/
|
|
1707
|
-
declare function
|
|
1928
|
+
declare function Flamecaller(): FinalAction;
|
|
1929
|
+
|
|
1708
1930
|
/**
|
|
1709
|
-
*
|
|
1710
|
-
* Sorted by ticksToImpact (soonest first). Only includes missiles within 500 units
|
|
1711
|
-
* or that are predicted to hit.
|
|
1931
|
+
* Bot: Pyromancer
|
|
1712
1932
|
*
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1717
|
-
*
|
|
1718
|
-
*
|
|
1719
|
-
*
|
|
1720
|
-
* -
|
|
1933
|
+
* BEHAVIOR: Adaptive homing missile specialist at long range. Maintains 400u
|
|
1934
|
+
* distance, strafes to dodge, and uses fitMissileToBudget with minTurnRate 0.5
|
|
1935
|
+
* to fire the highest-damage homing missile that fits in the safe window.
|
|
1936
|
+
* Shields undodgeable threats, emergency blinks. A versatile ranged caster
|
|
1937
|
+
* that adapts its missiles to the situation.
|
|
1938
|
+
*
|
|
1939
|
+
* PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist
|
|
1940
|
+
* - Flamecaller (tier 1): Fixed homing missiles, basic strafe and defense
|
|
1941
|
+
* - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks
|
|
1942
|
+
* - Infernalist (tier 3): Future — overwhelming adaptive fire
|
|
1943
|
+
*
|
|
1944
|
+
* TIER: 2 (enhanced Flamecaller)
|
|
1721
1945
|
*/
|
|
1722
|
-
declare function
|
|
1946
|
+
declare function Pyromancer(): FinalAction;
|
|
1947
|
+
|
|
1723
1948
|
/**
|
|
1724
|
-
*
|
|
1725
|
-
*
|
|
1949
|
+
* Bot: Infernalist
|
|
1950
|
+
*
|
|
1951
|
+
* BEHAVIOR: Rapid-fire caster that exploits warmup bonus for accelerating DPS.
|
|
1952
|
+
* Fires consistent homing missiles to build warmup, punishes vulnerability windows
|
|
1953
|
+
* with warmup-boosted fast casts. Proactive blink kiting when enemy closes.
|
|
1954
|
+
*
|
|
1955
|
+
* KEY IMPROVEMENTS OVER PYROMANCER:
|
|
1956
|
+
* - Warmup exploitation: always passes lastMissileConfig for bonus
|
|
1957
|
+
* - Punish mode: straight missiles during enemy vulnerability
|
|
1958
|
+
* - Proactive blink kiting: monitors closing rate
|
|
1959
|
+
* - Progressive cast-cancel: graduated thresholds
|
|
1960
|
+
*
|
|
1961
|
+
* PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist
|
|
1962
|
+
* TIER: 3 (elite Caster line)
|
|
1726
1963
|
*/
|
|
1727
|
-
declare function
|
|
1964
|
+
declare function Infernalist(): FinalAction;
|
|
1965
|
+
|
|
1728
1966
|
/**
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
1967
|
+
* Bot: Spellshot
|
|
1968
|
+
*
|
|
1969
|
+
* BEHAVIOR: Uses interceptAngle to calculate where the enemy will be and fires
|
|
1970
|
+
* fast, non-homing missiles (speed 8, turnRate 0) along the predicted path.
|
|
1971
|
+
* Strafes at medium range (300-400), shields undodgeable threats, emergency
|
|
1972
|
+
* blinks when shield isn't available. The key mechanic is PREDICTION — these
|
|
1973
|
+
* missiles don't track, they go exactly where you calculated the enemy would be.
|
|
1974
|
+
*
|
|
1975
|
+
* NAMING RATIONALE: "Spellshot" — a spell that is a single, precisely aimed shot.
|
|
1976
|
+
* Like a sniper's "called shot" but magical. The defining feature is the intercept
|
|
1977
|
+
* calculation: this bot doesn't fire tracking missiles, it calculates the exact
|
|
1978
|
+
* angle needed to hit a moving target. "Shot" implies precision, singular impact,
|
|
1979
|
+
* and skill-based aiming — everything this bot is about.
|
|
1980
|
+
*
|
|
1981
|
+
* PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker
|
|
1982
|
+
* - Spellshot (tier 1): Basic intercept prediction, non-homing missiles
|
|
1983
|
+
* - Spelltracer (tier 2): Future — predictive homing (missiles that lead AND track)
|
|
1984
|
+
* - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks
|
|
1985
|
+
* The naming progression: shot (single bullet) → tracer (bullet that tracks a path)
|
|
1986
|
+
* → seeker (actively hunts). Each tier adds more intelligence to the projectile,
|
|
1987
|
+
* evolving from "I calculate where you'll be" to "my missile calculates where you'll be."
|
|
1988
|
+
*
|
|
1989
|
+
* NOTE: A separate future archetype "Spellslinger" (volume-of-fire) is reserved
|
|
1990
|
+
* for a rapid-fire bot that prioritizes quantity over prediction.
|
|
1991
|
+
*
|
|
1992
|
+
* TIER: 1 (base)
|
|
1731
1993
|
*/
|
|
1732
|
-
declare function
|
|
1994
|
+
declare function Spellshot(): FinalAction;
|
|
1733
1995
|
|
|
1734
1996
|
/**
|
|
1735
|
-
*
|
|
1997
|
+
* Bot: Spelltracer
|
|
1736
1998
|
*
|
|
1737
|
-
*
|
|
1738
|
-
*
|
|
1999
|
+
* BEHAVIOR: Enhanced ranged sniper with adaptive missile fitting and intercept
|
|
2000
|
+
* prediction. Uses fitMissileToBudget to find the highest-damage fast missile
|
|
2001
|
+
* that fits the safe window, then fires it along the predicted intercept angle.
|
|
2002
|
+
* Maintains medium-long range (300-450), shields undodgeable threats with proper
|
|
2003
|
+
* timing, emergency blinks, and distance blinks when cornered. The key mechanic
|
|
2004
|
+
* is still PREDICTION — but now with adaptive damage optimization.
|
|
2005
|
+
*
|
|
2006
|
+
* PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker
|
|
2007
|
+
* - Spellshot (tier 1): Fixed config intercept prediction, non-homing missiles
|
|
2008
|
+
* - Spelltracer (tier 2): + adaptive fitting, timed defense, distance management
|
|
2009
|
+
* - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks
|
|
2010
|
+
*
|
|
2011
|
+
* TIER: 2 (enhanced Spellshot)
|
|
1739
2012
|
*/
|
|
2013
|
+
declare function Spelltracer(): FinalAction;
|
|
1740
2014
|
|
|
1741
2015
|
/**
|
|
1742
|
-
*
|
|
2016
|
+
* Bot: Spellseeker
|
|
1743
2017
|
*
|
|
1744
|
-
*
|
|
1745
|
-
*
|
|
1746
|
-
*
|
|
1747
|
-
*
|
|
1748
|
-
*
|
|
2018
|
+
* BEHAVIOR: Elite sniper that uses intercept-aimed straight missiles during vulnerability
|
|
2019
|
+
* windows. Combines Spelltracer's adaptive fitting with precise lead-position aiming
|
|
2020
|
+
* and vulnerability exploitation. Straight punish missiles at sniper range are nearly
|
|
2021
|
+
* unavoidable. Proactive distance control via closing rate detection.
|
|
2022
|
+
*
|
|
2023
|
+
* KEY IMPROVEMENTS OVER SPELLTRACER:
|
|
2024
|
+
* - Intercept-aimed punish: getLeadPosition + straight missiles during vulnerability
|
|
2025
|
+
* - Proactive distance blink: monitors closing rate, blinks before danger zone
|
|
2026
|
+
* - Progressive cast-cancel: graduated damage thresholds
|
|
2027
|
+
* - Warmup exploitation: always passes lastMissileConfig
|
|
2028
|
+
*
|
|
2029
|
+
* PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker
|
|
2030
|
+
* TIER: 3 (elite Sniper line)
|
|
1749
2031
|
*/
|
|
1750
|
-
declare function
|
|
2032
|
+
declare function Spellseeker(): FinalAction;
|
|
1751
2033
|
|
|
1752
2034
|
/**
|
|
1753
|
-
*
|
|
2035
|
+
* Bot: Battlemage
|
|
1754
2036
|
*
|
|
1755
|
-
*
|
|
2037
|
+
* BEHAVIOR: Balanced mid-range duelist. Shields undodgeable threats, interrupts
|
|
2038
|
+
* enemy casts with quick missiles, saves blink for emergencies OR gap-closing.
|
|
2039
|
+
* Switches between quick (10 dmg, fast) and heavy (15 dmg, slow) missile configs
|
|
2040
|
+
* based on safety window and range. Will aggressively trade hits when health allows.
|
|
2041
|
+
* The unique trait is cast-interruption: fires quick missiles specifically when the
|
|
2042
|
+
* enemy is casting, punishing long cast times.
|
|
1756
2043
|
*
|
|
1757
|
-
*
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
1760
|
-
*
|
|
1761
|
-
*
|
|
1762
|
-
*
|
|
1763
|
-
* Damage: raw HP removed on hit (wizard has 60 HP)
|
|
1764
|
-
* Turn rate: degrees per tick the missile can rotate
|
|
2044
|
+
* PROGRESSION LINE: Battlemage → Warmage → Archmage
|
|
2045
|
+
* - Battlemage (tier 1): Quick/heavy fixed configs, cast interruption, basic defense
|
|
2046
|
+
* - Warmage (tier 2): Adaptive missile fitting (fitMissileToBudget), smarter attacks
|
|
2047
|
+
* - Archmage (tier 3): Future — supreme duelist, perfect tactical mastery
|
|
2048
|
+
*
|
|
2049
|
+
* TIER: 1 (base)
|
|
1765
2050
|
*/
|
|
2051
|
+
declare function Battlemage(): FinalAction;
|
|
1766
2052
|
|
|
1767
2053
|
/**
|
|
1768
|
-
*
|
|
2054
|
+
* Bot: Warmage
|
|
1769
2055
|
*
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
1772
|
-
*
|
|
2056
|
+
* BEHAVIOR: Enhanced Battlemage with adaptive missile fitting. Uses fitMissileToBudget
|
|
2057
|
+
* to maximize damage within safe attack windows instead of fixed quick/heavy configs.
|
|
2058
|
+
* Same close-range playstyle: shields undodgeable threats, blinks to close distance
|
|
2059
|
+
* or escape, aggressive hit-trading when health allows. The adaptive fitting means
|
|
2060
|
+
* every attack is optimized for the current situation — no wasted cast time.
|
|
1773
2061
|
*
|
|
1774
|
-
*
|
|
2062
|
+
* PROGRESSION LINE: Battlemage → Warmage → Archmage
|
|
2063
|
+
* - Battlemage (tier 1): Quick/heavy fixed configs, cast interruption, basic defense
|
|
2064
|
+
* - Warmage (tier 2): Adaptive missile fitting, optimized damage windows
|
|
2065
|
+
* - Archmage (tier 3): Future — supreme duelist, perfect tactical mastery
|
|
1775
2066
|
*
|
|
1776
|
-
*
|
|
1777
|
-
* return shield(); // shield and stay still
|
|
1778
|
-
* return shield().move(100, 0); // move right while cast starts
|
|
2067
|
+
* TIER: 2 (enhanced Battlemage)
|
|
1779
2068
|
*/
|
|
1780
|
-
declare function
|
|
2069
|
+
declare function Warmage(): FinalAction;
|
|
2070
|
+
|
|
1781
2071
|
/**
|
|
1782
|
-
*
|
|
2072
|
+
* Bot: Archmage
|
|
1783
2073
|
*
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
* (
|
|
2074
|
+
* BEHAVIOR: Versatile duelist that adapts missile choice based on distance and HP.
|
|
2075
|
+
* Close range → straight missiles (no turn cost = more damage). Mid/far range → homing.
|
|
2076
|
+
* Uses dual-blink aggressively (gap-close during vulnerability, escape when trade is bad).
|
|
2077
|
+
* HP-aware: ahead → aggressive close range; behind → defensive ranged kiting.
|
|
1787
2078
|
*
|
|
1788
|
-
*
|
|
1789
|
-
*
|
|
2079
|
+
* KEY IMPROVEMENTS OVER WARMAGE:
|
|
2080
|
+
* - Range-adaptive missiles: straight close, homing far
|
|
2081
|
+
* - Vulnerability-timed blinks: gap-close during enemy cast/GCD
|
|
2082
|
+
* - HP-aware aggression: adjusts distance + risk tolerance based on HP differential
|
|
2083
|
+
* - Progressive cast-cancel: graduated thresholds
|
|
1790
2084
|
*
|
|
1791
|
-
*
|
|
2085
|
+
* PROGRESSION LINE: Battlemage → Warmage → Archmage
|
|
2086
|
+
* TIER: 3 (elite Duelist line)
|
|
2087
|
+
*/
|
|
2088
|
+
declare function Archmage(): FinalAction;
|
|
2089
|
+
|
|
2090
|
+
/**
|
|
2091
|
+
* Bot: Stormchaser
|
|
1792
2092
|
*
|
|
1793
|
-
*
|
|
1794
|
-
*
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1797
|
-
*
|
|
1798
|
-
* Negative = no homing + minor speed cost reduction.
|
|
1799
|
-
* @param ai - Called every tick to control missile steering. Receives:
|
|
1800
|
-
* - missileState: the missile's position, rotation (degrees), speed, remainingTicks
|
|
1801
|
-
* - worldState: full game state (all wizards, projectiles)
|
|
1802
|
-
* - random(): seeded PRNG [0, 1)
|
|
1803
|
-
* Return { turnToward: {x, y} } to home toward a position, or {} to fly straight.
|
|
1804
|
-
* @param direction - Launch angle in degrees (0°=right, 90°=down, 180°=left, 270°=up).
|
|
1805
|
-
* Tip: use Math.atan2(dy, dx) * (180 / Math.PI) to aim at a target.
|
|
2093
|
+
* BEHAVIOR: Fights aggressively while managing defense intelligently. Uses
|
|
2094
|
+
* two fixed missile configs (standard homing + quick attack) with predictive
|
|
2095
|
+
* homing missile AI (interceptAngle on the missile itself). Blink-dodges
|
|
2096
|
+
* incoming threats, shields when blink is on cooldown. Tight distance
|
|
2097
|
+
* management (350 units, ±30 band).
|
|
1806
2098
|
*
|
|
1807
|
-
*
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1810
|
-
*
|
|
2099
|
+
* PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger
|
|
2100
|
+
* - Stormchaser (tier 1): Fixed missiles, predictive homing AI, blink-dodge
|
|
2101
|
+
* - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage
|
|
2102
|
+
* - Stormforger (tier 3): Future — supreme berserker, perfect aggression
|
|
1811
2103
|
*
|
|
1812
|
-
*
|
|
1813
|
-
* return missile(
|
|
1814
|
-
* {damage: 10, speed: 5, duration: 300, turnRate: 3},
|
|
1815
|
-
* ({worldState}) => ({turnToward: worldState.enemies[0]?.position}),
|
|
1816
|
-
* angle,
|
|
1817
|
-
* );
|
|
2104
|
+
* TIER: 1 (base)
|
|
1818
2105
|
*/
|
|
1819
|
-
declare function
|
|
2106
|
+
declare function Stormchaser(): FinalAction;
|
|
2107
|
+
|
|
1820
2108
|
/**
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
* Max range: 300 units from current position (clamped by engine if further).
|
|
1824
|
-
* Cast time: 10 ticks (0.1s). Cooldown scales with distance:
|
|
1825
|
-
* - 100 units → 100 ticks (1s)
|
|
1826
|
-
* - 300 units → 2000 ticks (20s)
|
|
2109
|
+
* Bot: Stormcaller
|
|
1827
2110
|
*
|
|
1828
|
-
*
|
|
2111
|
+
* BEHAVIOR: Enhanced Stormchaser with adaptive missile fitting (fitMissileToBudget)
|
|
2112
|
+
* AND predictive homing missiles. Combines aggressive fighting philosophy with
|
|
2113
|
+
* optimized damage output. Uses budget-based missile fitting to maximize damage
|
|
2114
|
+
* within safe windows. Falls back to quick missiles under pressure. Same smart
|
|
2115
|
+
* trade/shield decisions as Stormchaser but with better resource usage.
|
|
1829
2116
|
*
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
2117
|
+
* PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger
|
|
2118
|
+
* - Stormchaser (tier 1): Adaptive missiles, smart trading, aggressive defense
|
|
2119
|
+
* - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage
|
|
2120
|
+
* - Stormforger (tier 3): Future — supreme berserker, perfect aggression
|
|
1832
2121
|
*
|
|
1833
|
-
*
|
|
1834
|
-
* return blink(400, 400); // blink to center
|
|
1835
|
-
* return blink(enemy.position.x, enemy.position.y); // blink to enemy
|
|
2122
|
+
* TIER: 2 (enhanced Stormchaser)
|
|
1836
2123
|
*/
|
|
1837
|
-
declare function
|
|
2124
|
+
declare function Stormcaller(): FinalAction;
|
|
2125
|
+
|
|
1838
2126
|
/**
|
|
1839
|
-
*
|
|
2127
|
+
* Bot: Stormforger
|
|
1840
2128
|
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
2129
|
+
* BEHAVIOR: Enhanced Stormcaller with vulnerability exploitation. Takes the exact
|
|
2130
|
+
* Stormcaller foundation (adaptive missile fitting + predictive homing) and adds
|
|
2131
|
+
* a punish mode that fires fast straight missiles when the enemy is locked in
|
|
2132
|
+
* GCD or cast animation. During vulnerability windows, uses getLeadPosition for
|
|
2133
|
+
* accurate straight shots that arrive before the enemy can react.
|
|
1843
2134
|
*
|
|
1844
|
-
*
|
|
1845
|
-
*
|
|
2135
|
+
* PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger
|
|
2136
|
+
* - Stormchaser (tier 1): Adaptive missiles, smart trading, aggressive defense
|
|
2137
|
+
* - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage
|
|
2138
|
+
* - Stormforger (tier 3): + vulnerability exploitation, punish missiles during enemy GCD
|
|
2139
|
+
*
|
|
2140
|
+
* TIER: 3 (elite Berserker line)
|
|
1846
2141
|
*/
|
|
1847
|
-
declare function
|
|
2142
|
+
declare function Stormforger(): FinalAction;
|
|
2143
|
+
|
|
1848
2144
|
/**
|
|
1849
|
-
*
|
|
2145
|
+
* Bot: Spellspinner
|
|
1850
2146
|
*
|
|
1851
|
-
*
|
|
1852
|
-
*
|
|
1853
|
-
*
|
|
2147
|
+
* BEHAVIOR: Maintains medium range (350 units), strafes constantly to dodge
|
|
2148
|
+
* missiles, and fires homing missiles (damage 10, speed 4, turnRate 2). Heavy
|
|
2149
|
+
* emphasis on movement — 80% strafe intensity when not dodging, 100% when dodging.
|
|
2150
|
+
* Shields only undodgeable threats, emergency blinks when shield isn't available.
|
|
2151
|
+
* The constant circular strafing motion traces patterns like thread being spun.
|
|
1854
2152
|
*
|
|
1855
|
-
*
|
|
1856
|
-
*
|
|
2153
|
+
* NAMING RATIONALE: Like a spider spinning a web of projectiles while circling its
|
|
2154
|
+
* prey. The constant strafing movement pattern traces circles — spinning thread
|
|
2155
|
+
* around the arena. "Spell" + "spinner" = a wizard who spins spells around the
|
|
2156
|
+
* battlefield. The kiting behavior (maintaining distance while attacking) creates
|
|
2157
|
+
* a web-like pattern of missiles and movement that traps opponents.
|
|
1857
2158
|
*
|
|
1858
|
-
*
|
|
1859
|
-
*
|
|
2159
|
+
* PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder
|
|
2160
|
+
* - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense
|
|
2161
|
+
* - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns
|
|
2162
|
+
* - Spellbinder (tier 3): Future — inescapable web of magic, perfect distance control
|
|
2163
|
+
* The progression: spinner (raw thread) → weaver (creates patterns) → binder
|
|
2164
|
+
* (constrains and traps). Each tier's projectile web becomes harder to escape.
|
|
1860
2165
|
*
|
|
1861
|
-
*
|
|
1862
|
-
* return move(100, 0); // move right
|
|
1863
|
-
* return move(enemy.position.x - myPos.x, enemy.position.y - myPos.y); // move toward enemy
|
|
1864
|
-
*/
|
|
1865
|
-
declare function move(x: number, y: number): FinalAction;
|
|
1866
|
-
/**
|
|
1867
|
-
* Do nothing — no action, no movement.
|
|
1868
|
-
*/
|
|
1869
|
-
declare function idle(): FinalAction;
|
|
1870
|
-
/**
|
|
1871
|
-
* Extract WizardActions from a FinalAction.
|
|
1872
|
-
* Used by the engine to get the actual action.
|
|
2166
|
+
* TIER: 1 (base)
|
|
1873
2167
|
*/
|
|
1874
|
-
declare function
|
|
2168
|
+
declare function Spellspinner(): FinalAction;
|
|
1875
2169
|
|
|
1876
2170
|
/**
|
|
1877
|
-
*
|
|
2171
|
+
* Bot: Spellweaver
|
|
1878
2172
|
*
|
|
1879
|
-
*
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
*
|
|
2173
|
+
* BEHAVIOR: Enhanced Spellspinner with adaptive missile fitting. Same medium-range
|
|
2174
|
+
* kiting playstyle — maintains distance, strafes heavily — but uses fitMissileToBudget
|
|
2175
|
+
* to maximize damage within safe attack windows. Always uses homing missiles since
|
|
2176
|
+
* kiting means enemies are always moving. More sophisticated than Spellspinner's
|
|
2177
|
+
* fixed damage/speed/turnRate configuration.
|
|
1884
2178
|
*
|
|
1885
|
-
*
|
|
2179
|
+
* NAMING RATIONALE: A weaver creates intricate patterns from raw thread. Where the
|
|
2180
|
+
* Spellspinner produces raw threads of magic (fixed missiles), the Spellweaver
|
|
2181
|
+
* combines them into optimized patterns (adaptive fitting). The name suggests
|
|
2182
|
+
* craftsmanship and sophistication — the same kiting web, but deliberately woven
|
|
2183
|
+
* rather than chaotically spun.
|
|
1886
2184
|
*
|
|
1887
|
-
*
|
|
1888
|
-
*
|
|
1889
|
-
*
|
|
1890
|
-
*
|
|
1891
|
-
* };
|
|
2185
|
+
* PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder
|
|
2186
|
+
* - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense
|
|
2187
|
+
* - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns
|
|
2188
|
+
* - Spellbinder (tier 3): Future — inescapable web, perfect distance control
|
|
1892
2189
|
*
|
|
1893
|
-
*
|
|
1894
|
-
* const oldStyleBot = wrapNewBot(NewBot);
|
|
1895
|
-
* simulate(oldStyleBot, opponent);
|
|
1896
|
-
*/
|
|
1897
|
-
declare function wrapNewBot(newBot: BotFunction): WizardFunction;
|
|
1898
|
-
/**
|
|
1899
|
-
* Type guard to check if a bot is a new-style BotFunction.
|
|
1900
|
-
* New-style bots have 0 parameters, old-style bots have at least 1.
|
|
2190
|
+
* TIER: 2 (enhanced Spellspinner)
|
|
1901
2191
|
*/
|
|
1902
|
-
declare function
|
|
2192
|
+
declare function Spellweaver(): FinalAction;
|
|
1903
2193
|
|
|
1904
2194
|
/**
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
1907
|
-
* Provides the useParam() hook for bots to declare tunable parameters,
|
|
1908
|
-
* and the infrastructure for the optimizer to inject/discover parameter values.
|
|
2195
|
+
* Bot: Spellbinder
|
|
1909
2196
|
*
|
|
1910
|
-
*
|
|
1911
|
-
*
|
|
1912
|
-
*
|
|
2197
|
+
* BEHAVIOR: Enhanced Spellweaver with vulnerability exploitation. Same medium-range
|
|
2198
|
+
* kiting playstyle — maintains distance, strafes heavily, uses fitMissileToBudget
|
|
2199
|
+
* for adaptive homing missiles. The T3 upgrade adds a punish mode that fires fast
|
|
2200
|
+
* straight missiles timed to land while the enemy is locked in a cast or GCD,
|
|
2201
|
+
* when they cannot shield. Defense, movement, and standard offense are identical
|
|
2202
|
+
* to Spellweaver.
|
|
1913
2203
|
*
|
|
1914
|
-
*
|
|
2204
|
+
* NAMING RATIONALE: A binder constrains and locks down opponents. Where the
|
|
2205
|
+
* Spellweaver optimizes missile patterns (adaptive fitting), the Spellbinder
|
|
2206
|
+
* reads the enemy's state and punishes vulnerability windows — binding them
|
|
2207
|
+
* to their commitments with unavoidable damage.
|
|
1915
2208
|
*
|
|
1916
|
-
*
|
|
1917
|
-
*
|
|
1918
|
-
*
|
|
2209
|
+
* PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder
|
|
2210
|
+
* - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense
|
|
2211
|
+
* - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns
|
|
2212
|
+
* - Spellbinder (tier 3): + vulnerability punish mode with fast straight missiles
|
|
1919
2213
|
*
|
|
1920
|
-
*
|
|
1921
|
-
|
|
2214
|
+
* TIER: 3 (elite Spellspinner line)
|
|
2215
|
+
*/
|
|
2216
|
+
declare function Spellbinder(): FinalAction;
|
|
2217
|
+
|
|
2218
|
+
/**
|
|
2219
|
+
* VIBEMANCER - BOT REGISTRY
|
|
1922
2220
|
*
|
|
1923
|
-
*
|
|
1924
|
-
*
|
|
2221
|
+
* Single source of truth for all bots, ordered from weakest to strongest.
|
|
2222
|
+
* Run the tournament test to determine the correct ordering.
|
|
1925
2223
|
*
|
|
1926
|
-
*
|
|
1927
|
-
*
|
|
2224
|
+
* To reorder: run `npx vitest run tests/bots/tournament.test.ts`
|
|
2225
|
+
* and update the list below based on the results.
|
|
1928
2226
|
*
|
|
1929
|
-
*
|
|
1930
|
-
* const distance = useParam('distance', 500, {range: 200, min: 0, steps: 20});
|
|
1931
|
-
* ```
|
|
2227
|
+
* BOT NAMING SCHEME (3-tier progression):
|
|
1932
2228
|
*
|
|
1933
|
-
*
|
|
1934
|
-
*
|
|
1935
|
-
*
|
|
1936
|
-
*
|
|
1937
|
-
*
|
|
1938
|
-
*
|
|
1939
|
-
*
|
|
1940
|
-
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1943
|
-
* after pass 1 (ideal for boolean params). Defaults to `steps` if not specified.
|
|
1944
|
-
* - At least `range` or both `min` + `max` must be provided.
|
|
2229
|
+
* | Group | Tier 1 (base) | Tier 2 (enhanced) | Tier 3 (elite) |
|
|
2230
|
+
* |------------|----------------|-------------------|-----------------|
|
|
2231
|
+
* | Defensive | Turtle | Sentinel | Golem |
|
|
2232
|
+
* | Duelist | Battlemage | Warmage | Archmage |
|
|
2233
|
+
* | Homing | Bonemancer | Lich | Archlich |
|
|
2234
|
+
* | Caster | Flamecaller | Pyromancer | Infernalist |
|
|
2235
|
+
* | Melee | Shadowblade | Nightblade | Voidblade |
|
|
2236
|
+
* | Sniper | Spellshot | Spelltracer | Spellseeker |
|
|
2237
|
+
* | Berserker | Stormchaser | Stormcaller | Stormforger |
|
|
2238
|
+
* | Kiter | Spellspinner | Spellweaver | Spellbinder |
|
|
1945
2239
|
*
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
2240
|
+
* Standalone: TargetDummy, Critter, Hogger, Rookie, Doombringer, Hero
|
|
2241
|
+
* (Hero is the adaptive SHOWCASE — the #1 reference bot users build their own to beat.
|
|
2242
|
+
* Deliberately kept as a single isolated bot, NOT a tier line: writing adaptive bots is
|
|
2243
|
+
* the game, so the roster ships one exemplar and players make the rest.)
|
|
2244
|
+
* Reserved: Spellslinger (future volume-of-fire archetype)
|
|
1948
2245
|
*
|
|
1949
|
-
* ## Rules of Hooks
|
|
1950
|
-
* useParam follows the same rules as useState/useEffect/etc:
|
|
1951
|
-
* - Must be called at the top level of your bot function (not inside conditionals)
|
|
1952
|
-
* - Must be called in the same order every tick
|
|
1953
|
-
* - Violations are detected and throw errors
|
|
1954
2246
|
*/
|
|
1955
2247
|
|
|
1956
|
-
|
|
1957
|
-
* Declaration of a tunable parameter, as discovered by the optimizer.
|
|
1958
|
-
*/
|
|
1959
|
-
interface ParamDeclaration {
|
|
2248
|
+
interface WizardEntry {
|
|
1960
2249
|
name: string;
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2250
|
+
ai: WizardFunction;
|
|
2251
|
+
description: string;
|
|
2252
|
+
tier?: number;
|
|
2253
|
+
group: string;
|
|
2254
|
+
}
|
|
2255
|
+
interface WizardGroup {
|
|
2256
|
+
label: string;
|
|
2257
|
+
bots: WizardEntry[];
|
|
1968
2258
|
}
|
|
1969
2259
|
/**
|
|
1970
|
-
*
|
|
1971
|
-
*
|
|
1972
|
-
* @param name - Unique parameter name (consistent across ticks)
|
|
1973
|
-
* @param value - The gameplay value. The auto-optimizer rewrites this in source code.
|
|
1974
|
-
* @param config - Optional optimizer search configuration
|
|
1975
|
-
* @returns The optimizer-injected value during optimization, or `value` during normal play
|
|
1976
|
-
*
|
|
1977
|
-
* @example
|
|
1978
|
-
* // Simple: no optimizer config
|
|
1979
|
-
* const damage = useParam('damage', 15);
|
|
1980
|
-
*
|
|
1981
|
-
* // With range: optimizer searches value ± range (sliding window)
|
|
1982
|
-
* const distance = useParam('distance', 350, {range: 150, min: 0});
|
|
1983
|
-
*
|
|
1984
|
-
* // With fixed min/max: optimizer searches [min, max]
|
|
1985
|
-
* const damage = useParam('damage', 15, {min: 5, max: 25});
|
|
1986
|
-
*/
|
|
1987
|
-
declare function useParam(name: string, value: number, config?: {
|
|
1988
|
-
range?: number;
|
|
1989
|
-
min?: number;
|
|
1990
|
-
max?: number;
|
|
1991
|
-
steps?: number;
|
|
1992
|
-
substeps?: number;
|
|
1993
|
-
}): number;
|
|
1994
|
-
/**
|
|
1995
|
-
* Inject parameter values for the next bot execution.
|
|
1996
|
-
* The wrapped bot will read these values via useParam().
|
|
1997
|
-
*/
|
|
1998
|
-
declare function setParamValues(values: Record<string, number>): void;
|
|
1999
|
-
/**
|
|
2000
|
-
* Clear injected parameter values. useParam() will return its provided value.
|
|
2001
|
-
*/
|
|
2002
|
-
declare function clearParamValues(): void;
|
|
2003
|
-
/**
|
|
2004
|
-
* Start discovery mode. All subsequent useParam() calls with optimizer config
|
|
2005
|
-
* will register their declarations.
|
|
2006
|
-
*/
|
|
2007
|
-
declare function startDiscovery(): void;
|
|
2008
|
-
/**
|
|
2009
|
-
* Stop discovery mode and return all discovered parameter declarations.
|
|
2010
|
-
*/
|
|
2011
|
-
declare function stopDiscovery(): ParamDeclaration[];
|
|
2012
|
-
/**
|
|
2013
|
-
* Wrap a bot function to inject specific parameter values.
|
|
2014
|
-
* The returned function sets params before calling the bot and clears them after.
|
|
2260
|
+
* All bots organized by progression line.
|
|
2261
|
+
* Each group contains bots from the same archetype, ordered by tier.
|
|
2015
2262
|
*/
|
|
2016
|
-
declare
|
|
2263
|
+
declare const BOT_GROUPS: WizardGroup[];
|
|
2264
|
+
declare const ALL_BOTS: WizardEntry[];
|
|
2017
2265
|
|
|
2018
2266
|
/**
|
|
2019
2267
|
* VIBEMANCER - OPTIMIZER UTILITIES
|
|
@@ -2099,6 +2347,34 @@ declare function scoreFight(result: FightResult): number;
|
|
|
2099
2347
|
*/
|
|
2100
2348
|
declare function scoreFightAsWizard2(result: FightResult): number;
|
|
2101
2349
|
|
|
2350
|
+
/**
|
|
2351
|
+
* Bot-name filter — the single source of truth for which bot names are
|
|
2352
|
+
* disallowed. Called everywhere a bot name is served or displayed (matchmaker,
|
|
2353
|
+
* upload, handle/botname selectors, leaderboard, CLI) so the rule is consistent.
|
|
2354
|
+
*
|
|
2355
|
+
* Deliberately a pure function over stateless rules: nothing is written to the
|
|
2356
|
+
* bot, so adding a rule hides matching bots everywhere at once, and REMOVING a
|
|
2357
|
+
* rule brings them straight back — no data to migrate or un-flag. Editing the
|
|
2358
|
+
* lists below is a (rare) release; the bots themselves are never mutated.
|
|
2359
|
+
*
|
|
2360
|
+
* On a takedown (trademark / defamation / illegal content), add the exact name
|
|
2361
|
+
* to BANNED_EXACT, or a pattern to BANNED_PATTERNS for a family of names.
|
|
2362
|
+
*
|
|
2363
|
+
* FULL RUNBOOK (read this before banning anything): docs/BANNING_BOT_NAMES.md
|
|
2364
|
+
* — it explains the why, the exact-match semantics, and the release steps
|
|
2365
|
+
* required for the change to reach the (published-core) Cloud Functions.
|
|
2366
|
+
*/
|
|
2367
|
+
/**
|
|
2368
|
+
* Pure matcher — exported for testing. Returns true if `name` matches any of the
|
|
2369
|
+
* given exact names or patterns (case-insensitive, trimmed).
|
|
2370
|
+
*/
|
|
2371
|
+
declare function matchesAnyBanRule(name: string, exact: ReadonlySet<string>, patterns: readonly RegExp[]): boolean;
|
|
2372
|
+
/**
|
|
2373
|
+
* True if a bot name is disallowed. Call this at every surface that serves or
|
|
2374
|
+
* displays bot names; never persist the result onto the bot.
|
|
2375
|
+
*/
|
|
2376
|
+
declare function isBannedBotName(name: string): boolean;
|
|
2377
|
+
|
|
2102
2378
|
/**
|
|
2103
2379
|
* VIBEMANCER — BROWSER SANDBOX
|
|
2104
2380
|
*
|
|
@@ -2317,18 +2593,18 @@ declare class BrowserManualMatchSandbox {
|
|
|
2317
2593
|
init(options?: ManualMatchInitOptions): Promise<GameState>;
|
|
2318
2594
|
/**
|
|
2319
2595
|
* Advance the match by `request.count` ticks (default 1), updating
|
|
2320
|
-
* the player's human actions and any
|
|
2596
|
+
* the player's human actions and any guided missile targets first.
|
|
2321
2597
|
*/
|
|
2322
2598
|
step(request?: ManualMatchStepRequest): Promise<StepResult>;
|
|
2323
2599
|
/**
|
|
2324
|
-
* Replace a missile's AI with a worker-local
|
|
2600
|
+
* Replace a missile's AI with a worker-local guide stub that reads
|
|
2325
2601
|
* from `latestHumanMissileTargets[projectileId]`. Subsequent step()
|
|
2326
2602
|
* calls with `humanMissileTargets` populated for this id steer the
|
|
2327
2603
|
* missile.
|
|
2328
2604
|
*/
|
|
2329
|
-
|
|
2605
|
+
guideMissile(projectileId: string): Promise<void>;
|
|
2330
2606
|
/**
|
|
2331
|
-
* Restore a
|
|
2607
|
+
* Restore a guided missile's original AI.
|
|
2332
2608
|
*/
|
|
2333
2609
|
releaseMissile(projectileId: string): Promise<void>;
|
|
2334
2610
|
/**
|
|
@@ -2528,7 +2804,7 @@ interface TestBotBuilder {
|
|
|
2528
2804
|
* });
|
|
2529
2805
|
* ```
|
|
2530
2806
|
*/
|
|
2531
|
-
declare function testBot(bot: WizardFunction
|
|
2807
|
+
declare function testBot(bot: WizardFunction): TestBotBuilder;
|
|
2532
2808
|
|
|
2533
2809
|
/**
|
|
2534
2810
|
* VIBEMANCER - FIGHT TRACE
|
|
@@ -2550,7 +2826,7 @@ interface TraceEvent {
|
|
|
2550
2826
|
type: TraceEventType;
|
|
2551
2827
|
detail: string;
|
|
2552
2828
|
}
|
|
2553
|
-
type TraceEventType = 'STATE' | 'FIRE' | 'HIT' | 'HURT' | 'DODGE_START' | 'DODGE_CLOSE' | 'MOVE' | 'ERROR' | 'WARNING';
|
|
2829
|
+
type TraceEventType = 'STATE' | 'FIRE' | 'HIT' | 'HURT' | 'DEATH' | 'LAVA_DEATH' | 'SHIELD_BLOCK' | 'KNOCKBACK' | 'BLINK' | 'DODGE_START' | 'DODGE_CLOSE' | 'MOVE' | 'ERROR' | 'WARNING';
|
|
2554
2830
|
interface TraceSummary {
|
|
2555
2831
|
winner: string;
|
|
2556
2832
|
ticks: number;
|
|
@@ -2569,6 +2845,7 @@ interface TraceBotSummary {
|
|
|
2569
2845
|
shields: number;
|
|
2570
2846
|
blinks: number;
|
|
2571
2847
|
dodgeEncounters: number;
|
|
2848
|
+
causeOfDeath: 'missile' | 'lava' | 'alive' | 'timeout';
|
|
2572
2849
|
movement: {
|
|
2573
2850
|
strafe: number;
|
|
2574
2851
|
approach: number;
|
|
@@ -2599,4 +2876,4 @@ declare function diagnoseTrace(events: TraceEvent[], summary: TraceSummary): str
|
|
|
2599
2876
|
/** Format diagnostic tips as a human-readable string. */
|
|
2600
2877
|
declare function formatDiagnosis(tips: string[]): string;
|
|
2601
2878
|
|
|
2602
|
-
export { ALL_BOTS,
|
|
2879
|
+
export { ALL_BOTS, ARENA_MAX, ARENA_MIN, ARENA_SIZE, ARENA_WATER_BUFFER, type ActionBuilder, type AnalyzedThreat, Archlich, Archmage, BLINK_CAST_TIME, BLINK_COOLDOWN, BLINK_MAX_COOLDOWN, BLINK_MAX_RANGE, BLINK_MIN_COOLDOWN, BLINK_RANGE, BOT_GROUPS, Battlemage, type BlinkEvent, Bonemancer, type BotError, BrowserManualMatchSandbox, BrowserMatchSandbox, type BrowserSandboxOptions, CASTING_MOVEMENT_MULT, COLLISION_RADIUS, type CastCancelEvent, type CastStartEvent, Critter, Doombringer, ENGINE_VERSION, type EnemyState, FIGHT_SPAWN_DISTANCES, type FightResult, type FightStats, type FightWinner, type FinalAction, Flamecaller, GCD_DURATION, type GameConfig, type GameState, Golem, Hogger, type HomingParams, type HookState, Infernalist, type InternalWizardState, KNOCKBACK_DAMAGE_THRESHOLD, KNOCKBACK_DECAY, KNOCKBACK_DELAY, KNOCKBACK_SPEED_PER_DAMAGE, LAVA_BORDER_WIDTH, Lich, MATCH_DURATION, MAX_HEALTH, MISSILE_BASE_CAST, MISSILE_BASE_RADIUS, MISSILE_DAMAGE_POWER, MISSILE_DAMAGE_RADIUS_SCALE, MISSILE_DAMAGE_SCALE, MISSILE_HOMING_COEFF, MISSILE_MIN_CAST_TIME, MISSILE_MIN_DAMAGE, MISSILE_MIN_DURATION, MISSILE_MIN_SPEED, MISSILE_RADIUS_PER_DAMAGE, MISSILE_SPEED_DURATION_BASELINE, MISSILE_SPEED_DURATION_COEFF, MISSILE_TURN_DURATION_COEFF, MOVEMENT_SPEED, MOVE_SPEED, ManualMatch, type ManualMatchInitOptions, type ManualMatchOptions, type ManualMatchStepRequest, type MatchWinner, type MissileAction, type MissileActions, type MissileConfig, type MissileContext, type MissileExpiredEvent, type MissileFunction$1 as MissileFunction, type MissileHitEvent, type MissileLaunchEvent, type MissileOobEvent, type MissileTemplate, Nightblade, type ParamDeclaration, type Position, type ProjectileState, Pyromancer, RULES, RULESET_RANGES, type RefObject, Rookie, SHIELD_CAST_TIME, SHIELD_DECAY_PER_SECOND, SHIELD_DECAY_RATE, SHIELD_MAX_BLOCK, SHIELD_MAX_STRENGTH, SHIELD_MIN_BLOCK, SHIELD_MIN_STRENGTH, SPAWN_DISTANCE, type SeekerParams, Sentinel, Shadowblade, type ShieldBlockEvent, type ShieldStartEvent, type SimEvent, type SimulateResult, Spellbinder, Spellseeker, Spellshot, Spellspinner, Spelltracer, Spellweaver, type SpiralParams, type StepResult, Stormcaller, Stormchaser, Stormforger, type StraightParams, TICKS_PER_SECOND, TICK_DURATION_MS, TargetDummy, type TestBotBuilder, type TestFightResult, type TestSimulateResult, type TraceBotSummary, type TraceEvent, type TraceEventType, type TraceSummary, Turtle, type Velocity, Voidblade, WARMUP_DURATION_TOLERANCE, WARMUP_MAX_BONUS, WARMUP_MAX_PENALTY, WARMUP_SPEED_TOLERANCE, WARMUP_TURN_TOLERANCE, WIZARD_HEALTH, WIZARD_RADIUS, Warmage, type WizardActions, type WizardContext, type WizardDeathEvent, type WizardEntry, type WizardFunction, type WizardGroup, type WizardLavaDeathEvent, type WizardState, type WorkerFactory, type WorkerLike, analyzeThreats, angleDiff, angleInRange, angleTo, applyDamage, applyRulesetOverrides, blink, browserSandboxFight, browserSandboxSimulate, calculateBlinkCooldown, calculateMissileCastTime, calculateMissileRadius, calculateMissileSimilarity, calculateShieldBlock, calculateWarmupMultiplier, cancel, cancelCast, clampPositionToArena, clampToArena, clearHooks, clearParamValues, completeCast, createEntitySeed, createInitialState, createRandom, createWorkerScript, currentRuleset, diagnoseTrace, directionAway, directionTo, distanceTo, effectiveTurnRateCost, extractAction, extractMissileAction, extractStats, extractTraceEvents, fight, findInRange, findNearest, fitMissileForEscapingTarget, fitMissileToBudget, flyStraight, formatDiagnosis, formatStats, formatTraceEvents, formatTraceSummary, generateCandidates, generateCombos, getAdaptiveMissileConfig, getEffectiveRange, getLeadPosition, getMissileCastTime, getMissileContext, getPlayerState, getWizardContext, hashCombine, homingMissile, idle, inRange, interceptAngle, isBannedBotName, isInLava, magnitude, matchesAnyBanRule, missile, move, moveInDirection, moveProjectile, moveWizard, narrowRange, nextRandom, normalize, normalizeAngle, predictPosition, resetAllHooks, resetRuleset, resolveWizardCollision, runWithHooks, runWizardWithContext, scoreFight, scoreFightAsWizard2, seekerMissile, setParamValues, shield, simulate, simulateMinDuration, sortByDistance, spiralMissile, startCast, startDiscovery, stopDiscovery, straightMissile, summarizeTrace, sweptCircleCollision, testBot, tick, turnToAngle, turnToward, updateShield, useArenaSize, useBlinkCooldown, useCastProgress, useCastingSpell, useClosestThreat, useDamageDealt, useDamageTaken, useEffect, useEnemy, useHealth, useLastHitTick, useLastMissileConfig, useMemo, useMyProjectiles, useMyThreatsToEnemy, useParam, usePosition, useProjectiles, useRandom, useRef, useShieldStrength, useState, useStatus, useThreats, useTick, useTicksUntilReady, useVelocity, validateHookCall, validateMissileConfig, withMissileContext, withWizardContext, wrapWithParams };
|