@vibemancer/core 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -28
- package/dist/{chunk-L7Z7OFXD.js → chunk-OL7ETV6V.js} +3 -3
- package/dist/chunk-OL7ETV6V.js.map +1 -0
- package/dist/index-browser.d.ts +1 -1
- package/dist/index-browser.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +79 -78
- package/src/bots/berserker/01_Stormchaser.ts +457 -457
- package/src/bots/berserker/02_Stormcaller.ts +417 -417
- package/src/bots/berserker/03_Stormforger.ts +481 -481
- package/src/bots/caster/01_Flamecaller.ts +286 -286
- package/src/bots/caster/02_Pyromancer.ts +350 -350
- package/src/bots/caster/03_Infernalist.ts +492 -492
- package/src/bots/defensive/01_Turtle.ts +151 -151
- package/src/bots/defensive/02_Sentinel.ts +134 -134
- package/src/bots/defensive/03_Golem.ts +357 -357
- package/src/bots/duelist/01_Battlemage.ts +433 -433
- package/src/bots/duelist/02_Warmage.ts +438 -438
- package/src/bots/duelist/03_Archmage.ts +588 -588
- package/src/bots/homing/01_Bonemancer.ts +67 -67
- package/src/bots/homing/02_Lich.ts +356 -356
- package/src/bots/homing/03_Archlich.ts +220 -220
- package/src/bots/kiter/01_Spellspinner.ts +398 -398
- package/src/bots/kiter/02_Spellweaver.ts +378 -378
- package/src/bots/kiter/03_Spellbinder.ts +448 -448
- package/src/bots/melee/01_Shadowblade.ts +270 -270
- package/src/bots/melee/02_Nightblade.ts +437 -437
- package/src/bots/melee/03_Voidblade.ts +582 -582
- package/src/bots/sniper/01_Spellshot.ts +385 -385
- package/src/bots/sniper/02_Spelltracer.ts +441 -441
- package/src/bots/sniper/03_Spellseeker.ts +546 -546
- package/src/bots/standalone/Critter.ts +89 -89
- package/src/bots/standalone/Doombringer.ts +91 -91
- package/src/bots/standalone/Hogger.ts +228 -228
- package/src/bots/standalone/Rookie.ts +50 -50
- package/src/bots/standalone/TargetDummy.ts +21 -21
- package/src/bots/test/cheater.ts +405 -405
- package/src/bots/test/crasher.ts +81 -81
- package/src/engine/hooks-runtime.ts +394 -394
- package/src/engine/manual-match.ts +289 -289
- package/src/engine/missile-templates.ts +155 -155
- package/src/engine/physics.ts +143 -143
- package/src/engine/simulation.ts +828 -828
- package/src/engine/spells.ts +128 -128
- package/src/engine-version.ts +1 -1
- package/src/index.ts +23 -23
- package/src/rules.ts +254 -254
- package/src/types.ts +193 -193
- package/src/utils/index.ts +6 -6
- package/dist/chunk-L7Z7OFXD.js.map +0 -1
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import {WizardFunction} from '../../types.js';
|
|
2
|
-
import {ARENA_WIDTH, ARENA_HEIGHT} from '../../rules.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Bot: Critter
|
|
6
|
-
*
|
|
7
|
-
* BEHAVIOR: Picks random valid actions each tick — random movement, random spells,
|
|
8
|
-
* random missile configs, random directions. Occasionally cancels its own casts.
|
|
9
|
-
* Uses engine-provided seeded random for deterministic behavior. Useful for
|
|
10
|
-
* finding edge cases in the engine, but completely useless in combat.
|
|
11
|
-
*
|
|
12
|
-
* NAMING RATIONALE: In WoW, critters are the 1-HP ambient mobs (rabbits, squirrels,
|
|
13
|
-
* prairie dogs) that wander around doing nothing useful and die to literally anything.
|
|
14
|
-
* This bot is the wizard equivalent — it flails around randomly and gets destroyed by
|
|
15
|
-
* anyone with a plan. The word "Critter" immediately tells any gamer "this thing is
|
|
16
|
-
* helpless and exists only to fill space."
|
|
17
|
-
*
|
|
18
|
-
* STANDALONE — no tier progression. Critters don't level up. However, a future
|
|
19
|
-
* "Hogger" bot could be an elite critter: same chaotic spirit but actually dangerous
|
|
20
|
-
* (like the famous WoW elite that wipes unprepared lowbies).
|
|
21
|
-
*/
|
|
22
|
-
export const Critter: WizardFunction = ({state, random}) =>
|
|
23
|
-
{
|
|
24
|
-
// Use the engine-provided random function (seeded, isolated, deterministic)
|
|
25
|
-
// World-space movement: random x/y in [-100, 100] range
|
|
26
|
-
const move = {
|
|
27
|
-
x: random() * 200 - 100, // -100 to 100
|
|
28
|
-
y: random() * 200 - 100, // -100 to 100
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
if (state.state === 'idle' && random() > 0.95)
|
|
32
|
-
{
|
|
33
|
-
const spellRoll = random();
|
|
34
|
-
if (spellRoll > 0.6)
|
|
35
|
-
{
|
|
36
|
-
return {
|
|
37
|
-
move,
|
|
38
|
-
startCast: {
|
|
39
|
-
spell: 'missile',
|
|
40
|
-
config: {
|
|
41
|
-
damage: Math.floor(random() * 20) + 5,
|
|
42
|
-
speed: random() * 5 + 2,
|
|
43
|
-
turnRate: random() * 3,
|
|
44
|
-
duration: Math.floor(random() * 200) + 100,
|
|
45
|
-
},
|
|
46
|
-
// Missile AI gets its own random from engine - use it for random targeting
|
|
47
|
-
missileAI: ({random: missileRandom}) => ({
|
|
48
|
-
turnToward:
|
|
49
|
-
missileRandom() > 0.5
|
|
50
|
-
? {
|
|
51
|
-
x: missileRandom() * ARENA_WIDTH,
|
|
52
|
-
y: missileRandom() * ARENA_HEIGHT,
|
|
53
|
-
}
|
|
54
|
-
: undefined,
|
|
55
|
-
}),
|
|
56
|
-
direction: random() * 360,
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
else if (spellRoll > 0.3)
|
|
61
|
-
{
|
|
62
|
-
return {
|
|
63
|
-
move,
|
|
64
|
-
startCast: {spell: 'shield'},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
else if (state.blinkCooldown === 0)
|
|
68
|
-
{
|
|
69
|
-
return {
|
|
70
|
-
move,
|
|
71
|
-
startCast: {
|
|
72
|
-
spell: 'blink',
|
|
73
|
-
target: {x: random() * ARENA_WIDTH, y: random() * ARENA_HEIGHT},
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
(state.state === 'casting' || state.state === 'channeling') &&
|
|
81
|
-
random() > 0.98
|
|
82
|
-
)
|
|
83
|
-
{
|
|
84
|
-
return {move, cancel: true};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Override auto-aim with random direction — Critter doesn't know where anything is
|
|
88
|
-
return {move, aimDirection: random() * 360};
|
|
89
|
-
};
|
|
1
|
+
import {WizardFunction} from '../../types.js';
|
|
2
|
+
import {ARENA_WIDTH, ARENA_HEIGHT} from '../../rules.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bot: Critter
|
|
6
|
+
*
|
|
7
|
+
* BEHAVIOR: Picks random valid actions each tick — random movement, random spells,
|
|
8
|
+
* random missile configs, random directions. Occasionally cancels its own casts.
|
|
9
|
+
* Uses engine-provided seeded random for deterministic behavior. Useful for
|
|
10
|
+
* finding edge cases in the engine, but completely useless in combat.
|
|
11
|
+
*
|
|
12
|
+
* NAMING RATIONALE: In WoW, critters are the 1-HP ambient mobs (rabbits, squirrels,
|
|
13
|
+
* prairie dogs) that wander around doing nothing useful and die to literally anything.
|
|
14
|
+
* This bot is the wizard equivalent — it flails around randomly and gets destroyed by
|
|
15
|
+
* anyone with a plan. The word "Critter" immediately tells any gamer "this thing is
|
|
16
|
+
* helpless and exists only to fill space."
|
|
17
|
+
*
|
|
18
|
+
* STANDALONE — no tier progression. Critters don't level up. However, a future
|
|
19
|
+
* "Hogger" bot could be an elite critter: same chaotic spirit but actually dangerous
|
|
20
|
+
* (like the famous WoW elite that wipes unprepared lowbies).
|
|
21
|
+
*/
|
|
22
|
+
export const Critter: WizardFunction = ({state, random}) =>
|
|
23
|
+
{
|
|
24
|
+
// Use the engine-provided random function (seeded, isolated, deterministic)
|
|
25
|
+
// World-space movement: random x/y in [-100, 100] range
|
|
26
|
+
const move = {
|
|
27
|
+
x: random() * 200 - 100, // -100 to 100
|
|
28
|
+
y: random() * 200 - 100, // -100 to 100
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (state.state === 'idle' && random() > 0.95)
|
|
32
|
+
{
|
|
33
|
+
const spellRoll = random();
|
|
34
|
+
if (spellRoll > 0.6)
|
|
35
|
+
{
|
|
36
|
+
return {
|
|
37
|
+
move,
|
|
38
|
+
startCast: {
|
|
39
|
+
spell: 'missile',
|
|
40
|
+
config: {
|
|
41
|
+
damage: Math.floor(random() * 20) + 5,
|
|
42
|
+
speed: random() * 5 + 2,
|
|
43
|
+
turnRate: random() * 3,
|
|
44
|
+
duration: Math.floor(random() * 200) + 100,
|
|
45
|
+
},
|
|
46
|
+
// Missile AI gets its own random from engine - use it for random targeting
|
|
47
|
+
missileAI: ({random: missileRandom}) => ({
|
|
48
|
+
turnToward:
|
|
49
|
+
missileRandom() > 0.5
|
|
50
|
+
? {
|
|
51
|
+
x: missileRandom() * ARENA_WIDTH,
|
|
52
|
+
y: missileRandom() * ARENA_HEIGHT,
|
|
53
|
+
}
|
|
54
|
+
: undefined,
|
|
55
|
+
}),
|
|
56
|
+
direction: random() * 360,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
else if (spellRoll > 0.3)
|
|
61
|
+
{
|
|
62
|
+
return {
|
|
63
|
+
move,
|
|
64
|
+
startCast: {spell: 'shield'},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else if (state.blinkCooldown === 0)
|
|
68
|
+
{
|
|
69
|
+
return {
|
|
70
|
+
move,
|
|
71
|
+
startCast: {
|
|
72
|
+
spell: 'blink',
|
|
73
|
+
target: {x: random() * ARENA_WIDTH, y: random() * ARENA_HEIGHT},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (
|
|
80
|
+
(state.state === 'casting' || state.state === 'channeling') &&
|
|
81
|
+
random() > 0.98
|
|
82
|
+
)
|
|
83
|
+
{
|
|
84
|
+
return {move, cancel: true};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Override auto-aim with random direction — Critter doesn't know where anything is
|
|
88
|
+
return {move, aimDirection: random() * 360};
|
|
89
|
+
};
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
import {WizardFunction, MissileAIFunction} from '../../types.js';
|
|
2
|
-
import {angleTo} from '../../utils/angles.js';
|
|
3
|
-
import {distanceTo} from '../../utils/distance.js';
|
|
4
|
-
import {fitMissileToBudget} from '../../utils/combat.js';
|
|
5
|
-
import {
|
|
6
|
-
getThreats,
|
|
7
|
-
getMostUrgentThreat,
|
|
8
|
-
getBlinkToCenter,
|
|
9
|
-
} from '../shared.js';
|
|
10
|
-
|
|
11
|
-
const HomingAI: MissileAIFunction = ({worldState}) =>
|
|
12
|
-
{
|
|
13
|
-
const enemy = worldState.enemies[0];
|
|
14
|
-
return {turnToward: enemy?.position};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Bot: Doombringer
|
|
19
|
-
*
|
|
20
|
-
* BEHAVIOR: Fires a single maximum-damage homing missile with infinite budget.
|
|
21
|
-
* No damage cap — goes for the biggest possible hit. Exists as a benchmark to
|
|
22
|
-
* demonstrate why lower-damage + shield play is superior. Has basic shield
|
|
23
|
-
* defense but no sophisticated tactics. One fat cast, one fat hit.
|
|
24
|
-
*
|
|
25
|
-
* STANDALONE — no tier progression. Benchmark/test bot.
|
|
26
|
-
*/
|
|
27
|
-
export const Doombringer: WizardFunction = ({state}) =>
|
|
28
|
-
{
|
|
29
|
-
const enemy = state.enemies[0];
|
|
30
|
-
if (!enemy)
|
|
31
|
-
{
|
|
32
|
-
return {move: {x: 0, y: 0}};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const distance = distanceTo(state.position, enemy.position);
|
|
36
|
-
const threats = getThreats(state);
|
|
37
|
-
const urgentThreat = getMostUrgentThreat(threats);
|
|
38
|
-
|
|
39
|
-
// === DEFENSE: Handle channeling ===
|
|
40
|
-
if (state.state === 'channeling')
|
|
41
|
-
{
|
|
42
|
-
if (!urgentThreat || urgentThreat.ticksToImpact > 150)
|
|
43
|
-
{
|
|
44
|
-
return {move: {x: 0, y: 0}, cancel: true};
|
|
45
|
-
}
|
|
46
|
-
return {move: {x: 0, y: 0}};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// === DEFENSE: Shield undodgeable threats ===
|
|
50
|
-
if (
|
|
51
|
-
state.state === 'idle' &&
|
|
52
|
-
urgentThreat &&
|
|
53
|
-
!urgentThreat.bestDodgeDirection
|
|
54
|
-
)
|
|
55
|
-
{
|
|
56
|
-
if (urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= 3)
|
|
57
|
-
{
|
|
58
|
-
return {
|
|
59
|
-
move: {x: 0, y: 0},
|
|
60
|
-
startCast: {spell: 'shield'},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
else if (!urgentThreat.canBlockInTime && state.blinkCooldown === 0)
|
|
64
|
-
{
|
|
65
|
-
return {
|
|
66
|
-
move: {x: 0, y: 0},
|
|
67
|
-
startCast: {spell: 'blink', target: getBlinkToCenter(state.position)},
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// === OFFENSE: Fire max-damage homing missile (no damage cap) ===
|
|
73
|
-
if (state.state === 'idle')
|
|
74
|
-
{
|
|
75
|
-
const config = fitMissileToBudget(Infinity, distance, {minTurnRate: 0.5});
|
|
76
|
-
if (config)
|
|
77
|
-
{
|
|
78
|
-
return {
|
|
79
|
-
move: {x: 0, y: 0},
|
|
80
|
-
startCast: {
|
|
81
|
-
spell: 'missile',
|
|
82
|
-
config,
|
|
83
|
-
missileAI: HomingAI,
|
|
84
|
-
direction: angleTo(state.position, enemy.position),
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {move: {x: 0, y: 0}};
|
|
91
|
-
};
|
|
1
|
+
import {WizardFunction, MissileAIFunction} from '../../types.js';
|
|
2
|
+
import {angleTo} from '../../utils/angles.js';
|
|
3
|
+
import {distanceTo} from '../../utils/distance.js';
|
|
4
|
+
import {fitMissileToBudget} from '../../utils/combat.js';
|
|
5
|
+
import {
|
|
6
|
+
getThreats,
|
|
7
|
+
getMostUrgentThreat,
|
|
8
|
+
getBlinkToCenter,
|
|
9
|
+
} from '../shared.js';
|
|
10
|
+
|
|
11
|
+
const HomingAI: MissileAIFunction = ({worldState}) =>
|
|
12
|
+
{
|
|
13
|
+
const enemy = worldState.enemies[0];
|
|
14
|
+
return {turnToward: enemy?.position};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Bot: Doombringer
|
|
19
|
+
*
|
|
20
|
+
* BEHAVIOR: Fires a single maximum-damage homing missile with infinite budget.
|
|
21
|
+
* No damage cap — goes for the biggest possible hit. Exists as a benchmark to
|
|
22
|
+
* demonstrate why lower-damage + shield play is superior. Has basic shield
|
|
23
|
+
* defense but no sophisticated tactics. One fat cast, one fat hit.
|
|
24
|
+
*
|
|
25
|
+
* STANDALONE — no tier progression. Benchmark/test bot.
|
|
26
|
+
*/
|
|
27
|
+
export const Doombringer: WizardFunction = ({state}) =>
|
|
28
|
+
{
|
|
29
|
+
const enemy = state.enemies[0];
|
|
30
|
+
if (!enemy)
|
|
31
|
+
{
|
|
32
|
+
return {move: {x: 0, y: 0}};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const distance = distanceTo(state.position, enemy.position);
|
|
36
|
+
const threats = getThreats(state);
|
|
37
|
+
const urgentThreat = getMostUrgentThreat(threats);
|
|
38
|
+
|
|
39
|
+
// === DEFENSE: Handle channeling ===
|
|
40
|
+
if (state.state === 'channeling')
|
|
41
|
+
{
|
|
42
|
+
if (!urgentThreat || urgentThreat.ticksToImpact > 150)
|
|
43
|
+
{
|
|
44
|
+
return {move: {x: 0, y: 0}, cancel: true};
|
|
45
|
+
}
|
|
46
|
+
return {move: {x: 0, y: 0}};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// === DEFENSE: Shield undodgeable threats ===
|
|
50
|
+
if (
|
|
51
|
+
state.state === 'idle' &&
|
|
52
|
+
urgentThreat &&
|
|
53
|
+
!urgentThreat.bestDodgeDirection
|
|
54
|
+
)
|
|
55
|
+
{
|
|
56
|
+
if (urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= 3)
|
|
57
|
+
{
|
|
58
|
+
return {
|
|
59
|
+
move: {x: 0, y: 0},
|
|
60
|
+
startCast: {spell: 'shield'},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
else if (!urgentThreat.canBlockInTime && state.blinkCooldown === 0)
|
|
64
|
+
{
|
|
65
|
+
return {
|
|
66
|
+
move: {x: 0, y: 0},
|
|
67
|
+
startCast: {spell: 'blink', target: getBlinkToCenter(state.position)},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// === OFFENSE: Fire max-damage homing missile (no damage cap) ===
|
|
73
|
+
if (state.state === 'idle')
|
|
74
|
+
{
|
|
75
|
+
const config = fitMissileToBudget(Infinity, distance, {minTurnRate: 0.5});
|
|
76
|
+
if (config)
|
|
77
|
+
{
|
|
78
|
+
return {
|
|
79
|
+
move: {x: 0, y: 0},
|
|
80
|
+
startCast: {
|
|
81
|
+
spell: 'missile',
|
|
82
|
+
config,
|
|
83
|
+
missileAI: HomingAI,
|
|
84
|
+
direction: angleTo(state.position, enemy.position),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {move: {x: 0, y: 0}};
|
|
91
|
+
};
|