@vibemancer/core 0.1.3 → 0.1.5
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-W7HWJFQ4.js +10599 -0
- package/dist/chunk-W7HWJFQ4.js.map +1 -0
- package/dist/index-browser.d.ts +1300 -1051
- package/dist/index-browser.js +55 -17
- package/dist/index.d.ts +53 -3
- package/dist/index.js +200 -54
- package/dist/index.js.map +1 -1
- package/package.json +21 -15
- 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.ts +2 -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-B7YYYBEC.js +0 -9140
- package/dist/chunk-B7YYYBEC.js.map +0 -1
- package/src/hooks/bot-wrapper.ts +0 -84
package/src/engine/optimizer.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import type {ParamDeclaration} from './params-runtime.js';
|
|
19
19
|
import type {FightResult} from './simulation.js';
|
|
20
|
-
import {
|
|
20
|
+
import {RULES} from '../rules.js';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Compute the effective min/max search window for a parameter.
|
|
@@ -169,7 +169,7 @@ export function scoreFight(result: FightResult): number
|
|
|
169
169
|
if (match.winner === 'wizard-1')
|
|
170
170
|
{
|
|
171
171
|
// Won: 3.0 base + HP bonus
|
|
172
|
-
score += 3.0 + (match.finalState.health / WIZARD_HEALTH) * 0.5;
|
|
172
|
+
score += 3.0 + (match.finalState.health / RULES.WIZARD_HEALTH) * 0.5;
|
|
173
173
|
}
|
|
174
174
|
else if (match.winner === 'draw' || match.winner === null)
|
|
175
175
|
{
|
|
@@ -178,8 +178,8 @@ export function scoreFight(result: FightResult): number
|
|
|
178
178
|
else
|
|
179
179
|
{
|
|
180
180
|
// Lost: small bonus for low enemy HP
|
|
181
|
-
const enemyHP = match.finalState.enemies[0]?.health ?? WIZARD_HEALTH;
|
|
182
|
-
score += (1 - enemyHP / WIZARD_HEALTH) * 0.5;
|
|
181
|
+
const enemyHP = match.finalState.enemies[0]?.health ?? RULES.WIZARD_HEALTH;
|
|
182
|
+
score += (1 - enemyHP / RULES.WIZARD_HEALTH) * 0.5;
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -201,8 +201,8 @@ export function scoreFightAsWizard2(result: FightResult): number
|
|
|
201
201
|
if (match.winner === 'wizard-2')
|
|
202
202
|
{
|
|
203
203
|
// Won: 3.0 base + HP bonus (wizard-2's remaining HP)
|
|
204
|
-
const hp = match.finalState.enemies[0]?.health ?? WIZARD_HEALTH;
|
|
205
|
-
score += 3.0 + (hp / WIZARD_HEALTH) * 0.5;
|
|
204
|
+
const hp = match.finalState.enemies[0]?.health ?? RULES.WIZARD_HEALTH;
|
|
205
|
+
score += 3.0 + (hp / RULES.WIZARD_HEALTH) * 0.5;
|
|
206
206
|
}
|
|
207
207
|
else if (match.winner === 'draw' || match.winner === null)
|
|
208
208
|
{
|
|
@@ -212,7 +212,7 @@ export function scoreFightAsWizard2(result: FightResult): number
|
|
|
212
212
|
{
|
|
213
213
|
// Lost: small bonus for low wizard-1 HP
|
|
214
214
|
const enemyHP = match.finalState.health;
|
|
215
|
-
score += (1 - enemyHP / WIZARD_HEALTH) * 0.5;
|
|
215
|
+
score += (1 - enemyHP / RULES.WIZARD_HEALTH) * 0.5;
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
* - Violations are detected and throw errors
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
|
-
import type {WizardFunction} from '../types.js';
|
|
53
|
+
import type {WizardFunction} from '../hooks/types.js';
|
|
54
54
|
import {validateHookCall} from './hooks-runtime.js';
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -174,12 +174,12 @@ export function stopDiscovery(): ParamDeclaration[]
|
|
|
174
174
|
*/
|
|
175
175
|
export function wrapWithParams(bot: WizardFunction, params: Record<string, number>): WizardFunction
|
|
176
176
|
{
|
|
177
|
-
return (
|
|
177
|
+
return () =>
|
|
178
178
|
{
|
|
179
179
|
setParamValues(params);
|
|
180
180
|
try
|
|
181
181
|
{
|
|
182
|
-
return bot(
|
|
182
|
+
return bot();
|
|
183
183
|
}
|
|
184
184
|
finally
|
|
185
185
|
{
|
package/src/engine/physics.ts
CHANGED
|
@@ -1,38 +1,29 @@
|
|
|
1
1
|
import {Position, WizardState, ProjectileState} from '../types.js';
|
|
2
|
-
import {
|
|
2
|
+
import {RULES, ARENA_SIZE, WIZARD_RADIUS, ARENA_MIN, ARENA_MAX} from '../rules.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Move a wizard based on world-space input direction.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - y: +100 = down, -100 = up
|
|
10
|
-
* - Values are clamped to [-100, 100]
|
|
11
|
-
* - Diagonal movement is normalized (magnitude capped at 100)
|
|
12
|
-
* - No rotation tracking - just output (x, y) direction
|
|
7
|
+
* Any vector works: direction is preserved, speed is clamped to [0, 1].
|
|
8
|
+
* (0.5, 0) = half speed right. (300, 200) = full speed at 33.7°.
|
|
13
9
|
*/
|
|
14
10
|
export function moveWizard(wizard: WizardState, move: {x: number; y: number}, deltaTicks: number): Position
|
|
15
11
|
{
|
|
16
|
-
|
|
17
|
-
let
|
|
18
|
-
let dy = Math.max(-100, Math.min(100, move.y || 0));
|
|
12
|
+
let dx = move.x || 0;
|
|
13
|
+
let dy = move.y || 0;
|
|
19
14
|
|
|
20
|
-
// Normalize diagonal movement (cap magnitude at 100)
|
|
21
15
|
const magnitude = Math.sqrt(dx * dx + dy * dy);
|
|
22
|
-
|
|
16
|
+
// Stryker disable next-line EqualityOperator: > vs >= equivalent (dividing by exactly 1 is identity)
|
|
17
|
+
if (magnitude > 1)
|
|
23
18
|
{
|
|
24
|
-
dx = dx
|
|
25
|
-
dy = dy
|
|
19
|
+
dx = dx / magnitude;
|
|
20
|
+
dy = dy / magnitude;
|
|
26
21
|
}
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
dx = dx / 100;
|
|
30
|
-
dy = dy / 100;
|
|
31
|
-
|
|
32
|
-
let speed = MOVEMENT_SPEED;
|
|
23
|
+
let speed = RULES.MOVEMENT_SPEED;
|
|
33
24
|
if (wizard.state === 'casting')
|
|
34
25
|
{
|
|
35
|
-
speed *= CASTING_MOVEMENT_MULT;
|
|
26
|
+
speed *= RULES.CASTING_MOVEMENT_MULT;
|
|
36
27
|
}
|
|
37
28
|
else if (wizard.state === 'channeling')
|
|
38
29
|
{
|
|
@@ -61,16 +52,31 @@ export function moveProjectile(projectile: ProjectileState, deltaTicks: number):
|
|
|
61
52
|
}
|
|
62
53
|
|
|
63
54
|
/**
|
|
64
|
-
* Clamp a position to the arena
|
|
55
|
+
* Clamp a position to the full arena bounds (0-860).
|
|
56
|
+
* No playfield clamping — wizards CAN walk/blink into lava.
|
|
65
57
|
*/
|
|
66
58
|
export function clampToArena(position: Position, radius: number): Position
|
|
67
59
|
{
|
|
68
60
|
return {
|
|
69
|
-
x: Math.max(radius, Math.min(
|
|
70
|
-
y: Math.max(radius, Math.min(
|
|
61
|
+
x: Math.max(radius, Math.min(ARENA_SIZE - radius, position.x)),
|
|
62
|
+
y: Math.max(radius, Math.min(ARENA_SIZE - radius, position.y)),
|
|
71
63
|
};
|
|
72
64
|
}
|
|
73
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Check if a position is in the lava zone (outside the playfield).
|
|
68
|
+
* Lava zones: [0, ARENA_MIN] and [ARENA_MAX, ARENA_SIZE] on each axis.
|
|
69
|
+
*/
|
|
70
|
+
export function isInLava(position: Position, radius: number): boolean
|
|
71
|
+
{
|
|
72
|
+
return (
|
|
73
|
+
position.x - radius < ARENA_MIN ||
|
|
74
|
+
position.x + radius > ARENA_MAX ||
|
|
75
|
+
position.y - radius < ARENA_MIN ||
|
|
76
|
+
position.y + radius > ARENA_MAX
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
74
80
|
/**
|
|
75
81
|
* Resolve body collision between two wizards.
|
|
76
82
|
* Pushes both apart equally so they don't overlap. Neither is blocked — they just can't stack.
|
|
@@ -81,12 +87,14 @@ export function resolveWizardCollision(wizard1: WizardState, wizard2: WizardStat
|
|
|
81
87
|
const minDist = WIZARD_RADIUS * 2;
|
|
82
88
|
const maxIterations = 5; // Prevents infinite loops in edge cases
|
|
83
89
|
|
|
90
|
+
// Stryker disable next-line EqualityOperator: < vs <= equivalent (one extra iteration produces same result)
|
|
84
91
|
for (let i = 0; i < maxIterations; i++)
|
|
85
92
|
{
|
|
86
93
|
const dx = wizard2.position.x - wizard1.position.x;
|
|
87
94
|
const dy = wizard2.position.y - wizard1.position.y;
|
|
88
95
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
89
96
|
|
|
97
|
+
// Stryker disable next-line EqualityOperator: >= vs > equivalent (at exactly minDist, no push needed either way)
|
|
90
98
|
if (dist >= minDist) return; // No overlap — done
|
|
91
99
|
|
|
92
100
|
if (dist === 0)
|
|
@@ -128,6 +136,7 @@ export function sweptCircleCollision(oldPos: Position, newPos: Position, radius:
|
|
|
128
136
|
if (d2 === 0)
|
|
129
137
|
{
|
|
130
138
|
const dist2 = (oldPos.x - targetPos.x) ** 2 + (oldPos.y - targetPos.y) ** 2;
|
|
139
|
+
// Stryker disable next-line EqualityOperator: <= vs < equivalent (exact equality of float dist² and integer radius² is vanishingly rare)
|
|
131
140
|
return dist2 <= totalRadius * totalRadius;
|
|
132
141
|
}
|
|
133
142
|
|
|
@@ -139,5 +148,6 @@ export function sweptCircleCollision(oldPos: Position, newPos: Position, radius:
|
|
|
139
148
|
const closestY = oldPos.y + t * dy;
|
|
140
149
|
|
|
141
150
|
const dist2 = (targetPos.x - closestX) ** 2 + (targetPos.y - closestY) ** 2;
|
|
151
|
+
// Stryker disable next-line EqualityOperator: <= vs < equivalent (float dist² rarely equals exact integer radius²)
|
|
142
152
|
return dist2 <= totalRadius * totalRadius;
|
|
143
153
|
}
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
* NOTE: This file has ZERO Node.js dependencies. It works in any JS environment.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import type {GameState,
|
|
22
|
+
import type {GameState, ProjectileState, WizardActions} from '../types.js';
|
|
23
|
+
import type {MissileFunction} from '../hooks/types.js';
|
|
23
24
|
import type {BotError, FightResult, MatchWinner, SimulateResult} from './simulation.js';
|
|
24
25
|
import type {StepResult} from './manual-match.js';
|
|
25
26
|
|
|
@@ -580,7 +581,7 @@ export class BrowserManualMatchSandbox
|
|
|
580
581
|
|
|
581
582
|
/**
|
|
582
583
|
* Advance the match by `request.count` ticks (default 1), updating
|
|
583
|
-
* the player's human actions and any
|
|
584
|
+
* the player's human actions and any guided missile targets first.
|
|
584
585
|
*/
|
|
585
586
|
async step(request: ManualMatchStepRequest = {}): Promise<StepResult>
|
|
586
587
|
{
|
|
@@ -589,18 +590,18 @@ export class BrowserManualMatchSandbox
|
|
|
589
590
|
}
|
|
590
591
|
|
|
591
592
|
/**
|
|
592
|
-
* Replace a missile's AI with a worker-local
|
|
593
|
+
* Replace a missile's AI with a worker-local guide stub that reads
|
|
593
594
|
* from `latestHumanMissileTargets[projectileId]`. Subsequent step()
|
|
594
595
|
* calls with `humanMissileTargets` populated for this id steer the
|
|
595
596
|
* missile.
|
|
596
597
|
*/
|
|
597
|
-
async
|
|
598
|
+
async guideMissile(projectileId: string): Promise<void>
|
|
598
599
|
{
|
|
599
|
-
await this.inner.callRaw('
|
|
600
|
+
await this.inner.callRaw('manualMatchGuide', {projectileId});
|
|
600
601
|
}
|
|
601
602
|
|
|
602
603
|
/**
|
|
603
|
-
* Restore a
|
|
604
|
+
* Restore a guided missile's original AI.
|
|
604
605
|
*/
|
|
605
606
|
async releaseMissile(projectileId: string): Promise<void>
|
|
606
607
|
{
|
|
@@ -664,7 +665,7 @@ export type {
|
|
|
664
665
|
BotError,
|
|
665
666
|
GameState,
|
|
666
667
|
MatchWinner,
|
|
667
|
-
|
|
668
|
+
MissileFunction,
|
|
668
669
|
ProjectileState,
|
|
669
670
|
StepResult,
|
|
670
671
|
WizardActions,
|
|
@@ -148,7 +148,7 @@ export async function compileMatchBundle(
|
|
|
148
148
|
* the host).
|
|
149
149
|
*
|
|
150
150
|
* Returns a plain JS string that, when loaded into a Web Worker, exposes the
|
|
151
|
-
* `__manualMatchInit`, `__manualMatchStep`, `
|
|
151
|
+
* `__manualMatchInit`, `__manualMatchStep`, `__manualMatchGuide`,
|
|
152
152
|
* `__manualMatchRelease`, `__manualMatchSetInvincible`,
|
|
153
153
|
* `__manualMatchGetState`, `__manualMatchGetResult`, and `__manualMatchDispose`
|
|
154
154
|
* globals on the worker's globalThis.
|