@@ -20,6 +20,7 @@ export class NukeExecution implements Execution {
2020 private active = true ;
2121 private mg : Game ;
2222 private nuke : Unit | null = null ;
23+ private tilesInRangeCache : Map < TileRef , number > ;
2324 private tilesToDestroyCache : Set < TileRef > | undefined ;
2425 private pathFinder : ParabolaPathFinder ;
2526
@@ -44,6 +45,25 @@ export class NukeExecution implements Execution {
4445 return this . mg . owner ( this . dst ) ;
4546 }
4647
48+ private tilesInRange ( ) : Map < TileRef , number > {
49+ if ( this . tilesInRangeCache !== undefined ) {
50+ return this . tilesInRangeCache ;
51+ }
52+ if ( this . nuke === null ) {
53+ throw new Error ( "Not initialized" ) ;
54+ }
55+ this . tilesInRangeCache = new Map < TileRef , number > ( ) ;
56+ const magnitude = this . mg . config ( ) . nukeMagnitudes ( this . nuke . type ( ) ) ;
57+ const inner2 = magnitude . inner * magnitude . inner ;
58+ const outer2 = magnitude . outer * magnitude . outer ;
59+ this . mg . bfs ( this . dst , ( _ , n : TileRef ) => {
60+ const d2 = this . mg ?. euclideanDistSquared ( this . dst , n ) ?? 0 ;
61+ this . tilesInRangeCache . set ( n , d2 <= inner2 ? 1 : 0.5 ) ;
62+ return d2 <= outer2 ;
63+ } ) ;
64+ return this . tilesInRangeCache ;
65+ }
66+
4767 private tilesToDestroy ( ) : Set < TileRef > {
4868 if ( this . tilesToDestroyCache !== undefined ) {
4969 return this . tilesToDestroyCache ;
@@ -62,16 +82,20 @@ export class NukeExecution implements Execution {
6282 return this . tilesToDestroyCache ;
6383 }
6484
65- private maybeBreakAlliances ( toDestroy : Set < TileRef > ) {
85+ /**
86+ * Break alliances based on all tiles in range.
87+ * Tiles are weighted based on their chance (1/odds) of being destroyed.
88+ */
89+ private maybeBreakAlliances ( inRange : Map < TileRef , number > ) {
6690 if ( this . nuke === null ) {
6791 throw new Error ( "Not initialized" ) ;
6892 }
6993 const attacked = new Map < Player , number > ( ) ;
70- for ( const tile of toDestroy ) {
94+ for ( const [ tile , weight ] of inRange . entries ( ) ) {
7195 const owner = this . mg . owner ( tile ) ;
7296 if ( owner . isPlayer ( ) ) {
7397 const prev = attacked . get ( owner ) ?? 0 ;
74- attacked . set ( owner , prev + 1 ) ;
98+ attacked . set ( owner , prev + weight ) ;
7599 }
76100 }
77101
@@ -82,7 +106,7 @@ export class NukeExecution implements Execution {
82106 this . nuke . type ( ) !== UnitType . MIRVWarhead
83107 ) {
84108 // Resolves exploit of alliance breaking in which a pending alliance request
85- // was accepeted in the middle of an missle attack.
109+ // was accepted in the middle of a missile attack.
86110 const allianceRequest = attackedPlayer
87111 . incomingAllianceRequests ( )
88112 . find ( ( ar ) => ar . requestor ( ) === this . player ) ;
@@ -120,7 +144,7 @@ export class NukeExecution implements Execution {
120144 targetTile : this . dst ,
121145 trajectory : this . getTrajectory ( this . dst ) ,
122146 } ) ;
123- this . maybeBreakAlliances ( this . tilesToDestroy ( ) ) ;
147+ this . maybeBreakAlliances ( this . tilesInRange ( ) ) ;
124148 if ( this . mg . hasOwner ( this . dst ) ) {
125149 const target = this . mg . owner ( this . dst ) ;
126150 if ( ! target . isPlayer ( ) ) {
@@ -233,7 +257,6 @@ export class NukeExecution implements Execution {
233257
234258 const magnitude = this . mg . config ( ) . nukeMagnitudes ( this . nuke . type ( ) ) ;
235259 const toDestroy = this . tilesToDestroy ( ) ;
236- this . maybeBreakAlliances ( toDestroy ) ;
237260
238261 const maxTroops = this . target ( ) . isPlayer ( )
239262 ? this . mg . config ( ) . maxTroops ( this . target ( ) as Player )
0 commit comments