Skip to content

Commit 66bdcc2

Browse files
committed
Improve AI with weak gravity handling, nuke tactics, and defensive mines
- AI now evaluates weak gravity toggle options (normal/hard difficulty) - Hard AI launches nukes when outgunned and enemy is within range - AI drops defensive mines when fleeing in escape scenarios - Weak gravity choices passed through to astrogation orders
1 parent 83bd710 commit 66bdcc2

File tree

1 file changed

+56
-3
lines changed

1 file changed

+56
-3
lines changed

src/shared/ai.ts

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function aiAstrogation(
6464
const canOverload = difficulty !== 'easy' && stats?.canOverload && ship.fuel >= 2;
6565

6666
// Build list of (burn, overload) pairs to evaluate
67-
type BurnOption = { burn: number | null; overload: number | null };
67+
type BurnOption = { burn: number | null; overload: number | null; weakGravityChoices?: Record<string, boolean> };
6868
const options: BurnOption[] = [{ burn: null, overload: null }];
6969
for (let d = 0; d < 6; d++) {
7070
if (canBurnFuel) {
@@ -77,9 +77,11 @@ export function aiAstrogation(
7777
}
7878
}
7979

80+
let bestWeakGrav: Record<string, boolean> | undefined;
81+
8082
for (const opt of options) {
81-
const course = computeCourse(ship, opt.burn, map,
82-
opt.overload !== null ? { overload: opt.overload } : undefined);
83+
const courseOpts = opt.overload !== null ? { overload: opt.overload } : undefined;
84+
const course = computeCourse(ship, opt.burn, map, courseOpts);
8385

8486
// Skip crashed courses entirely
8587
if (course.crashed) continue;
@@ -95,10 +97,29 @@ export function aiAstrogation(
9597
score -= 1; // Small penalty for extra fuel cost of overloading
9698
}
9799

100+
// For normal/hard AI, also try ignoring weak gravity choices
101+
let bestLocalWG: Record<string, boolean> | undefined;
102+
if (difficulty !== 'easy' && course.gravityEffects.some(g => g.strength === 'weak')) {
103+
// Try toggling each weak gravity hex
104+
const weakHexes = course.gravityEffects.filter(g => g.strength === 'weak');
105+
for (const wg of weakHexes) {
106+
const wgChoices: Record<string, boolean> = { [hexKey(wg.hex)]: true };
107+
const altCourse = computeCourse(ship, opt.burn, map,
108+
{ ...(courseOpts ?? {}), weakGravityChoices: wgChoices });
109+
if (altCourse.crashed) continue;
110+
const altScore = scoreCourse(ship, altCourse, targetHex, escapeWins, enemyShips, difficulty);
111+
if (altScore > score) {
112+
score = altScore;
113+
bestLocalWG = wgChoices;
114+
}
115+
}
116+
}
117+
98118
if (score > bestScore) {
99119
bestScore = score;
100120
bestBurn = opt.burn;
101121
bestOverload = opt.overload;
122+
bestWeakGrav = bestLocalWG;
102123
}
103124
}
104125

@@ -116,6 +137,7 @@ export function aiAstrogation(
116137
shipId: ship.id,
117138
burn: bestBurn,
118139
...(bestOverload !== null ? { overload: bestOverload } : {}),
140+
...(bestWeakGrav ? { weakGravityChoices: bestWeakGrav } : {}),
119141
});
120142
}
121143

@@ -164,6 +186,23 @@ export function aiOrdnance(
164186
}
165187
if (!nearestEnemy) continue;
166188

189+
// Hard AI: launch nuke at enemies within range if cargo allows
190+
if (difficulty === 'hard' && nearestDist <= torpedoRange &&
191+
stats.canOverload && cargoFree >= ORDNANCE_MASS.nuke) {
192+
// Prefer nukes over torpedoes when enemy is strong
193+
const enemyStr = getCombatStrength([nearestEnemy]);
194+
const myStr = getCombatStrength([ship]);
195+
if (enemyStr >= myStr && nearestDist <= 6) {
196+
const bestDir = findDirectionToward(ship.position, nearestEnemy.position);
197+
launches.push({
198+
shipId: ship.id,
199+
ordnanceType: 'nuke',
200+
torpedoAccel: bestDir,
201+
});
202+
continue;
203+
}
204+
}
205+
167206
// Launch torpedo if enemy is within range and ship can
168207
if (nearestDist <= torpedoRange && stats.canOverload && cargoFree >= ORDNANCE_MASS.torpedo) {
169208
// Aim guidance toward enemy
@@ -182,6 +221,20 @@ export function aiOrdnance(
182221
shipId: ship.id,
183222
ordnanceType: 'mine',
184223
});
224+
continue;
225+
}
226+
227+
// Defensive mine-laying: drop mines behind when being pursued (escape scenarios)
228+
const player = state.players[playerId];
229+
if (player?.escapeWins && nearestDist <= 8 && cargoFree >= ORDNANCE_MASS.mine) {
230+
// Only if enemy is approaching from behind
231+
const speed = hexVecLength(ship.velocity);
232+
if (speed >= 2 && difficulty !== 'easy') {
233+
launches.push({
234+
shipId: ship.id,
235+
ordnanceType: 'mine',
236+
});
237+
}
185238
}
186239
}
187240

0 commit comments

Comments
 (0)