@@ -35,11 +35,9 @@ export class NationNukeBehavior {
3535 ) { }
3636
3737 maybeSendNuke ( other : Player | null ) {
38- if ( this . attackBehavior === null ) throw new Error ( "not initialized" ) ;
3938 const silos = this . player . units ( UnitType . MissileSilo ) ;
4039 if (
4140 silos . length === 0 ||
42- this . player . gold ( ) < this . getPerceivedNukeCost ( UnitType . AtomBomb ) ||
4341 other === null ||
4442 other . type ( ) === PlayerType . Bot || // Don't nuke bots (as opposed to nations and humans)
4543 this . player . isOnSameTeam ( other ) ||
@@ -50,10 +48,15 @@ export class NationNukeBehavior {
5048
5149 const hydroCost = this . getPerceivedNukeCost ( UnitType . HydrogenBomb ) ;
5250 const atomCost = this . getPerceivedNukeCost ( UnitType . AtomBomb ) ;
53- const minCost = hydroCost < atomCost ? hydroCost : atomCost ;
54- const nukeType =
55- this . player . gold ( ) > minCost ? UnitType . HydrogenBomb : UnitType . AtomBomb ;
56- const range = nukeType === UnitType . HydrogenBomb ? 80 : 20 ;
51+ let nukeType : UnitType ;
52+ if ( this . player . gold ( ) >= hydroCost ) {
53+ nukeType = UnitType . HydrogenBomb ;
54+ } else if ( this . player . gold ( ) >= atomCost ) {
55+ nukeType = UnitType . AtomBomb ;
56+ } else {
57+ return ;
58+ }
59+ const range = this . mg . config ( ) . nukeMagnitudes ( nukeType ) . inner ;
5760
5861 const structures = other . units (
5962 UnitType . City ,
@@ -93,7 +96,7 @@ export class NationNukeBehavior {
9396 continue ;
9497 }
9598
96- const value = this . nukeTileScore ( tile , silos , structures ) ;
99+ const value = this . nukeTileScore ( tile , silos , structures , nukeType ) ;
97100 if ( value > bestValue ) {
98101 bestTile = tile ;
99102 bestValue = value ;
@@ -246,9 +249,14 @@ export class NationNukeBehavior {
246249 }
247250 }
248251
249- private nukeTileScore ( tile : TileRef , silos : Unit [ ] , targets : Unit [ ] ) : number {
250- // Potential damage in a 25-tile radius
251- const dist = euclDistFN ( tile , 25 , false ) ;
252+ private nukeTileScore (
253+ tile : TileRef ,
254+ silos : Unit [ ] ,
255+ targets : Unit [ ] ,
256+ nukeType : UnitType . AtomBomb | UnitType . HydrogenBomb ,
257+ ) : number {
258+ const magnitude = this . mg . config ( ) . nukeMagnitudes ( nukeType ) ;
259+ const dist = euclDistFN ( tile , magnitude . inner , false ) ;
252260 let tileValue = targets
253261 . filter ( ( unit ) => dist ( this . mg , unit . tile ( ) ) )
254262 . map ( ( unit ) : number => {
@@ -261,7 +269,9 @@ export class NationNukeBehavior {
261269 case UnitType . MissileSilo :
262270 return 50_000 * level ;
263271 case UnitType . Port :
264- return 10_000 * level ;
272+ return 15_000 * level ;
273+ case UnitType . Factory :
274+ return 15_000 * level ;
265275 default :
266276 return 0 ;
267277 }
@@ -274,13 +284,11 @@ export class NationNukeBehavior {
274284 // On Hard & Impossible we rely on trajectory-based interception checks instead. See maybeSendNuke().
275285 if ( difficulty === Difficulty . Medium ) {
276286 const dist50 = euclDistFN ( tile , 50 , false ) ;
277- tileValue -=
278- 50_000 *
279- targets . filter (
280- ( unit ) =>
281- unit . type ( ) === UnitType . SAMLauncher &&
282- dist50 ( this . mg , unit . tile ( ) ) ,
283- ) . length ;
287+ const hasSam = targets . some (
288+ ( unit ) =>
289+ unit . type ( ) === UnitType . SAMLauncher && dist50 ( this . mg , unit . tile ( ) ) ,
290+ ) ;
291+ if ( hasSam ) return - 1 ;
284292 }
285293
286294 // Prefer tiles that are closer to a silo
@@ -293,8 +301,9 @@ export class NationNukeBehavior {
293301 tileValue -= distanceToClosestSilo * 30 ;
294302
295303 // Don't target near recent targets
304+ const dist25 = euclDistFN ( tile , 25 , false ) ;
296305 tileValue -= this . lastNukeSent
297- . filter ( ( [ _tick , tile ] ) => dist ( this . mg , tile ) )
306+ . filter ( ( [ _tick , tile ] ) => dist25 ( this . mg , tile ) )
298307 . map ( ( _ ) => 1_000_000 )
299308 . reduce ( ( prev , cur ) => prev + cur , 0 ) ;
300309
@@ -306,19 +315,17 @@ export class NationNukeBehavior {
306315 nukeType : UnitType . AtomBomb | UnitType . HydrogenBomb ,
307316 targetPlayer : Player ,
308317 ) {
309- if ( this . attackBehavior === null || this . emojiBehavior === null )
310- throw new Error ( "not initialized" ) ;
311318 const tick = this . mg . ticks ( ) ;
312319 this . lastNukeSent . push ( [ tick , tile ] ) ;
313320 if ( nukeType === UnitType . AtomBomb ) {
314321 this . atomBombsLaunched ++ ;
315- // Increase perceived cost by 20 % each time to simulate saving up for a MIRV (higher than hydro to make atom bombs less attractive for the lategame)
316- this . atomBombPerceivedCost = ( this . atomBombPerceivedCost * 120n ) / 100n ;
322+ // Increase perceived cost by 25 % each time to simulate saving up for a MIRV (higher than hydro to make atom bombs less attractive for the lategame)
323+ this . atomBombPerceivedCost = ( this . atomBombPerceivedCost * 125n ) / 100n ;
317324 } else if ( nukeType === UnitType . HydrogenBomb ) {
318325 this . hydrogenBombsLaunched ++ ;
319- // Increase perceived cost by 10 % each time to simulate saving up for a MIRV
326+ // Increase perceived cost by 15 % each time to simulate saving up for a MIRV
320327 this . hydrogenBombPerceivedCost =
321- ( this . hydrogenBombPerceivedCost * 110n ) / 100n ;
328+ ( this . hydrogenBombPerceivedCost * 115n ) / 100n ;
322329 }
323330 this . mg . addExecution ( new NukeExecution ( nukeType , this . player , tile ) ) ;
324331 this . emojiBehavior . maybeSendEmoji ( targetPlayer , EMOJI_NUKE ) ;
0 commit comments