@supalosa/chronodivide-bot 0.2.0 → 0.2.2-a

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.
Files changed (118) hide show
  1. package/README.md +11 -3
  2. package/dist/bot/bot.js +17 -9
  3. package/dist/bot/bot.js.map +1 -0
  4. package/dist/bot/logic/awareness.js +12 -2
  5. package/dist/bot/logic/awareness.js.map +1 -0
  6. package/dist/bot/logic/building/ArtilleryUnit.js +29 -8
  7. package/dist/bot/logic/building/antiGroundStaticDefence.js +3 -2
  8. package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -0
  9. package/dist/bot/logic/building/artilleryUnit.js.map +1 -0
  10. package/dist/bot/logic/building/basicAirUnit.js +2 -1
  11. package/dist/bot/logic/building/basicAirUnit.js.map +1 -0
  12. package/dist/bot/logic/building/basicBuilding.js +2 -1
  13. package/dist/bot/logic/building/basicBuilding.js.map +1 -0
  14. package/dist/bot/logic/building/basicGroundUnit.js +2 -1
  15. package/dist/bot/logic/building/basicGroundUnit.js.map +1 -0
  16. package/dist/bot/logic/building/building.js +2 -0
  17. package/dist/bot/logic/building/buildingRules.js +168 -0
  18. package/dist/bot/logic/building/buildingRules.js.map +1 -0
  19. package/dist/bot/logic/building/harvester.js +1 -0
  20. package/dist/bot/logic/building/harvester.js.map +1 -0
  21. package/dist/bot/logic/building/powerPlant.js +2 -1
  22. package/dist/bot/logic/building/powerPlant.js.map +1 -0
  23. package/dist/bot/logic/building/queueController.js +2 -1
  24. package/dist/bot/logic/building/queueController.js.map +1 -0
  25. package/dist/bot/logic/building/resourceCollectionBuilding.js +2 -1
  26. package/dist/bot/logic/building/resourceCollectionBuilding.js.map +1 -0
  27. package/dist/bot/logic/common/scout.js +100 -0
  28. package/dist/bot/logic/common/scout.js.map +1 -0
  29. package/dist/bot/logic/common/utils.js +14 -0
  30. package/dist/bot/logic/common/utils.js.map +1 -0
  31. package/dist/bot/logic/map/map.js +9 -25
  32. package/dist/bot/logic/map/map.js.map +1 -0
  33. package/dist/bot/logic/map/sector.js +33 -1
  34. package/dist/bot/logic/map/sector.js.map +1 -0
  35. package/dist/bot/logic/mission/mission.js +3 -1
  36. package/dist/bot/logic/mission/mission.js.map +1 -0
  37. package/dist/bot/logic/mission/missionController.js +5 -4
  38. package/dist/bot/logic/mission/missionController.js.map +1 -0
  39. package/dist/bot/logic/mission/missionFactories.js +3 -0
  40. package/dist/bot/logic/mission/missionFactories.js.map +1 -0
  41. package/dist/bot/logic/mission/missions/attackMission.js +9 -10
  42. package/dist/bot/logic/mission/missions/attackMission.js.map +1 -0
  43. package/dist/bot/logic/mission/missions/defenceMission.js +12 -7
  44. package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -0
  45. package/dist/bot/logic/mission/missions/engineerMission.js +34 -0
  46. package/dist/bot/logic/mission/missions/engineerMission.js.map +1 -0
  47. package/dist/bot/logic/mission/missions/expansionMission.js +5 -4
  48. package/dist/bot/logic/mission/missions/expansionMission.js.map +1 -0
  49. package/dist/bot/logic/mission/missions/oneTimeMission.js +3 -2
  50. package/dist/bot/logic/mission/missions/oneTimeMission.js.map +1 -0
  51. package/dist/bot/logic/mission/missions/retreatMission.js +3 -2
  52. package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -0
  53. package/dist/bot/logic/mission/missions/scoutingMission.js +8 -9
  54. package/dist/bot/logic/mission/missions/scoutingMission.js.map +1 -0
  55. package/dist/bot/logic/squad/behaviours/combatSquad.js +20 -18
  56. package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -0
  57. package/dist/bot/logic/squad/behaviours/common.js +25 -5
  58. package/dist/bot/logic/squad/behaviours/common.js.map +1 -0
  59. package/dist/bot/logic/squad/behaviours/engineerSquad.js +36 -0
  60. package/dist/bot/logic/squad/behaviours/engineerSquad.js.map +1 -0
  61. package/dist/bot/logic/squad/behaviours/expansionSquad.js +1 -0
  62. package/dist/bot/logic/squad/behaviours/expansionSquad.js.map +1 -0
  63. package/dist/bot/logic/squad/behaviours/retreatSquad.js +6 -10
  64. package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -0
  65. package/dist/bot/logic/squad/behaviours/scoutingSquad.js +66 -18
  66. package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -0
  67. package/dist/bot/logic/squad/squad.js +4 -5
  68. package/dist/bot/logic/squad/squad.js.map +1 -0
  69. package/dist/bot/logic/squad/squadBehaviour.js +1 -0
  70. package/dist/bot/logic/squad/squadBehaviour.js.map +1 -0
  71. package/dist/bot/logic/squad/squadBehaviours.js +1 -0
  72. package/dist/bot/logic/squad/squadBehaviours.js.map +1 -0
  73. package/dist/bot/logic/squad/squadController.js +73 -23
  74. package/dist/bot/logic/squad/squadController.js.map +1 -0
  75. package/dist/bot/logic/threat/threat.js +1 -0
  76. package/dist/bot/logic/threat/threat.js.map +1 -0
  77. package/dist/bot/logic/threat/threatCalculator.js +1 -0
  78. package/dist/bot/logic/threat/threatCalculator.js.map +1 -0
  79. package/dist/exampleBot.js +29 -12
  80. package/dist/exampleBot.js.map +1 -0
  81. package/package.json +15 -7
  82. package/src/bot/bot.ts +21 -16
  83. package/src/bot/logic/awareness.ts +22 -5
  84. package/src/bot/logic/building/antiGroundStaticDefence.ts +60 -60
  85. package/src/bot/logic/building/artilleryUnit.ts +68 -0
  86. package/src/bot/logic/building/basicAirUnit.ts +68 -68
  87. package/src/bot/logic/building/basicBuilding.ts +47 -47
  88. package/src/bot/logic/building/basicGroundUnit.ts +1 -1
  89. package/src/bot/logic/building/buildingRules.ts +233 -0
  90. package/src/bot/logic/building/powerPlant.ts +32 -32
  91. package/src/bot/logic/building/queueController.ts +1 -1
  92. package/src/bot/logic/building/resourceCollectionBuilding.ts +56 -56
  93. package/src/bot/logic/common/scout.ts +127 -1
  94. package/src/bot/logic/common/utils.ts +17 -0
  95. package/src/bot/logic/map/map.ts +70 -84
  96. package/src/bot/logic/map/sector.ts +46 -4
  97. package/src/bot/logic/mission/mission.ts +2 -2
  98. package/src/bot/logic/mission/missionController.ts +7 -6
  99. package/src/bot/logic/mission/missionFactories.ts +5 -0
  100. package/src/bot/logic/mission/missions/attackMission.ts +19 -12
  101. package/src/bot/logic/mission/missions/defenceMission.ts +35 -15
  102. package/src/bot/logic/mission/missions/engineerMission.ts +61 -0
  103. package/src/bot/logic/mission/missions/expansionMission.ts +6 -4
  104. package/src/bot/logic/mission/missions/oneTimeMission.ts +3 -2
  105. package/src/bot/logic/mission/missions/retreatMission.ts +3 -2
  106. package/src/bot/logic/mission/missions/scoutingMission.ts +9 -6
  107. package/src/bot/logic/squad/behaviours/combatSquad.ts +22 -18
  108. package/src/bot/logic/squad/behaviours/common.ts +38 -5
  109. package/src/bot/logic/squad/behaviours/engineerSquad.ts +53 -0
  110. package/src/bot/logic/squad/behaviours/retreatSquad.ts +10 -12
  111. package/src/bot/logic/squad/behaviours/scoutingSquad.ts +79 -26
  112. package/src/bot/logic/squad/squad.ts +4 -4
  113. package/src/bot/logic/squad/squadBehaviour.ts +3 -1
  114. package/src/bot/logic/squad/squadController.ts +135 -70
  115. package/src/exampleBot.ts +31 -12
  116. package/tsconfig.json +73 -73
  117. package/src/bot/logic/building/ArtilleryUnit.ts +0 -43
  118. package/src/bot/logic/building/building.ts +0 -125
@@ -0,0 +1,68 @@
1
+ import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
+ import { GlobalThreat } from "../threat/threat.js";
3
+ import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
4
+
5
+ export class ArtilleryUnit implements AiBuildingRules {
6
+ constructor(private basePriority: number,
7
+ private artilleryPower: number,
8
+ private antiGroundPower: number,
9
+ private baseAmount: number) {}
10
+
11
+ getPlacementLocation(
12
+ game: GameApi,
13
+ playerData: PlayerData,
14
+ technoRules: TechnoRules
15
+ ): { rx: number; ry: number } | undefined {
16
+ return undefined;
17
+ }
18
+
19
+ getPriority(
20
+ game: GameApi,
21
+ playerData: PlayerData,
22
+ technoRules: TechnoRules,
23
+ threatCache: GlobalThreat | null
24
+ ): number {
25
+ const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
26
+ let priority = this.basePriority;
27
+ // If the enemy's defensive power is increasing we will start to build these.
28
+ if (threatCache && threatCache.totalDefensivePower > threatCache.totalAvailableAntiGroundFirepower) {
29
+ priority += (
30
+ this.artilleryPower *
31
+ (threatCache.totalAvailableAntiGroundFirepower / Math.max(1, threatCache.totalDefensivePower))
32
+ );
33
+ }
34
+ if (threatCache && this.antiGroundPower > 0) {
35
+ // If the enemy's power is increasing we should try to keep up.
36
+ if (threatCache.totalOffensiveLandThreat > threatCache.totalAvailableAntiGroundFirepower) {
37
+ priority +=
38
+ this.antiGroundPower *
39
+ this.basePriority *
40
+ (threatCache.totalOffensiveLandThreat /
41
+ Math.max(1, threatCache.totalAvailableAntiGroundFirepower));
42
+ } else {
43
+ // But also, if our power dwarfs the enemy, keep pressing the advantage.
44
+ priority +=
45
+ (this.antiGroundPower *
46
+ this.basePriority *
47
+ Math.sqrt(
48
+ threatCache.totalAvailableAntiGroundFirepower /
49
+ Math.max(
50
+ 1,
51
+ threatCache.totalOffensiveLandThreat + threatCache.totalDefensiveThreat,
52
+ ),
53
+ )) /
54
+ (numOwned + 1);
55
+ }
56
+ }
57
+ return priority * (1.0 - numOwned / this.baseAmount);
58
+ }
59
+
60
+ getMaxCount(
61
+ game: GameApi,
62
+ playerData: PlayerData,
63
+ technoRules: TechnoRules,
64
+ threatCache: GlobalThreat | null
65
+ ): number | null {
66
+ return null;
67
+ }
68
+ }
@@ -1,68 +1,68 @@
1
- import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
- import { GlobalThreat } from "../threat/threat.js";
3
- import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./building.js";
4
-
5
- export class BasicAirUnit implements AiBuildingRules {
6
- constructor(
7
- private basePriority: number,
8
- private baseAmount: number,
9
- private antiGroundPower: number = 1, // boolean for now, but will eventually be used in weighting.
10
- private antiAirPower: number = 0
11
- ) {}
12
-
13
- getPlacementLocation(
14
- game: GameApi,
15
- playerData: PlayerData,
16
- technoRules: TechnoRules
17
- ): { rx: number; ry: number } | undefined {
18
- return undefined;
19
- }
20
-
21
- getPriority(
22
- game: GameApi,
23
- playerData: PlayerData,
24
- technoRules: TechnoRules,
25
- threatCache: GlobalThreat | null
26
- ): number {
27
- // If the enemy's anti-air power is low we might build more.
28
- if (threatCache) {
29
- let priority = 0;
30
- if (
31
- this.antiGroundPower > 0 &&
32
- threatCache.totalOffensiveLandThreat > threatCache.totalAvailableAntiGroundFirepower
33
- ) {
34
- priority +=
35
- this.basePriority *
36
- (threatCache.totalOffensiveLandThreat / Math.max(1, threatCache.totalAvailableAntiGroundFirepower));
37
- }
38
- if (
39
- this.antiAirPower > 0 &&
40
- threatCache.totalOffensiveAirThreat > threatCache.totalAvailableAntiAirFirepower
41
- ) {
42
- priority +=
43
- this.basePriority *
44
- (threatCache.totalOffensiveAirThreat / Math.max(1, threatCache.totalAvailableAntiAirFirepower));
45
- }
46
- // sqrt so we don't build too much of one unit type.
47
- priority += Math.min(
48
- 1.0,
49
- Math.max(
50
- 1,
51
- Math.sqrt(threatCache.totalAvailableAirPower / Math.max(1, threatCache.totalOffensiveAntiAirThreat))
52
- )
53
- );
54
- return this.baseAmount * priority;
55
- }
56
- const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
57
- return this.basePriority * (1.0 - numOwned / this.baseAmount);
58
- }
59
-
60
- getMaxCount(
61
- game: GameApi,
62
- playerData: PlayerData,
63
- technoRules: TechnoRules,
64
- threatCache: GlobalThreat | null
65
- ): number | null {
66
- return null;
67
- }
68
- }
1
+ import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
+ import { GlobalThreat } from "../threat/threat.js";
3
+ import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
4
+
5
+ export class BasicAirUnit implements AiBuildingRules {
6
+ constructor(
7
+ private basePriority: number,
8
+ private baseAmount: number,
9
+ private antiGroundPower: number = 1, // boolean for now, but will eventually be used in weighting.
10
+ private antiAirPower: number = 0
11
+ ) {}
12
+
13
+ getPlacementLocation(
14
+ game: GameApi,
15
+ playerData: PlayerData,
16
+ technoRules: TechnoRules
17
+ ): { rx: number; ry: number } | undefined {
18
+ return undefined;
19
+ }
20
+
21
+ getPriority(
22
+ game: GameApi,
23
+ playerData: PlayerData,
24
+ technoRules: TechnoRules,
25
+ threatCache: GlobalThreat | null
26
+ ): number {
27
+ // If the enemy's anti-air power is low we might build more.
28
+ if (threatCache) {
29
+ let priority = 0;
30
+ if (
31
+ this.antiGroundPower > 0 &&
32
+ threatCache.totalOffensiveLandThreat > threatCache.totalAvailableAntiGroundFirepower
33
+ ) {
34
+ priority +=
35
+ this.basePriority *
36
+ (threatCache.totalOffensiveLandThreat / Math.max(1, threatCache.totalAvailableAntiGroundFirepower));
37
+ }
38
+ if (
39
+ this.antiAirPower > 0 &&
40
+ threatCache.totalOffensiveAirThreat > threatCache.totalAvailableAntiAirFirepower
41
+ ) {
42
+ priority +=
43
+ this.basePriority *
44
+ (threatCache.totalOffensiveAirThreat / Math.max(1, threatCache.totalAvailableAntiAirFirepower));
45
+ }
46
+ // sqrt so we don't build too much of one unit type.
47
+ priority += Math.min(
48
+ 1.0,
49
+ Math.max(
50
+ 1,
51
+ Math.sqrt(threatCache.totalAvailableAirPower / Math.max(1, threatCache.totalOffensiveAntiAirThreat))
52
+ )
53
+ );
54
+ return this.baseAmount * priority;
55
+ }
56
+ const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
57
+ return this.basePriority * (1.0 - numOwned / this.baseAmount);
58
+ }
59
+
60
+ getMaxCount(
61
+ game: GameApi,
62
+ playerData: PlayerData,
63
+ technoRules: TechnoRules,
64
+ threatCache: GlobalThreat | null
65
+ ): number | null {
66
+ return null;
67
+ }
68
+ }
@@ -1,47 +1,47 @@
1
- import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
- import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./building.js";
3
- import { GlobalThreat } from "../threat/threat.js";
4
-
5
- export class BasicBuilding implements AiBuildingRules {
6
- constructor(
7
- protected basePriority: number,
8
- protected maxNeeded: number,
9
- protected onlyBuildWhenFloatingCreditsAmount?: number
10
- ) {}
11
-
12
- getPlacementLocation(
13
- game: GameApi,
14
- playerData: PlayerData,
15
- technoRules: TechnoRules
16
- ): { rx: number; ry: number } | undefined {
17
- return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
18
- }
19
-
20
- getPriority(
21
- game: GameApi,
22
- playerData: PlayerData,
23
- technoRules: TechnoRules,
24
- threatCache: GlobalThreat | null
25
- ): number {
26
- const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
27
- const calcMaxCount = this.getMaxCount(game, playerData, technoRules, threatCache);
28
- if (numOwned >= (calcMaxCount ?? this.maxNeeded)) {
29
- return -100;
30
- }
31
-
32
- if (this.onlyBuildWhenFloatingCreditsAmount && playerData.credits < this.onlyBuildWhenFloatingCreditsAmount) {
33
- return -100;
34
- }
35
-
36
- return this.basePriority * (1.0 - numOwned / this.maxNeeded);
37
- }
38
-
39
- getMaxCount(
40
- game: GameApi,
41
- playerData: PlayerData,
42
- technoRules: TechnoRules,
43
- threatCache: GlobalThreat | null
44
- ): number | null {
45
- return this.maxNeeded;
46
- }
47
- }
1
+ import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
+ import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
3
+ import { GlobalThreat } from "../threat/threat.js";
4
+
5
+ export class BasicBuilding implements AiBuildingRules {
6
+ constructor(
7
+ protected basePriority: number,
8
+ protected maxNeeded: number,
9
+ protected onlyBuildWhenFloatingCreditsAmount?: number
10
+ ) {}
11
+
12
+ getPlacementLocation(
13
+ game: GameApi,
14
+ playerData: PlayerData,
15
+ technoRules: TechnoRules
16
+ ): { rx: number; ry: number } | undefined {
17
+ return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
18
+ }
19
+
20
+ getPriority(
21
+ game: GameApi,
22
+ playerData: PlayerData,
23
+ technoRules: TechnoRules,
24
+ threatCache: GlobalThreat | null
25
+ ): number {
26
+ const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
27
+ const calcMaxCount = this.getMaxCount(game, playerData, technoRules, threatCache);
28
+ if (numOwned >= (calcMaxCount ?? this.maxNeeded)) {
29
+ return -100;
30
+ }
31
+
32
+ if (this.onlyBuildWhenFloatingCreditsAmount && playerData.credits < this.onlyBuildWhenFloatingCreditsAmount) {
33
+ return -100;
34
+ }
35
+
36
+ return this.basePriority * (1.0 - numOwned / this.maxNeeded);
37
+ }
38
+
39
+ getMaxCount(
40
+ game: GameApi,
41
+ playerData: PlayerData,
42
+ technoRules: TechnoRules,
43
+ threatCache: GlobalThreat | null
44
+ ): number | null {
45
+ return this.maxNeeded;
46
+ }
47
+ }
@@ -1,6 +1,6 @@
1
1
  import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
2
  import { GlobalThreat } from "../threat/threat.js";
3
- import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./building.js";
3
+ import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
4
4
 
5
5
  export class BasicGroundUnit implements AiBuildingRules {
6
6
  constructor(
@@ -0,0 +1,233 @@
1
+ import {
2
+ BuildingPlacementData,
3
+ GameApi,
4
+ ObjectType,
5
+ PlayerData,
6
+ Point2D,
7
+ Size,
8
+ TechnoRules,
9
+ Tile,
10
+ } from "@chronodivide/game-api";
11
+ import { GlobalThreat } from "../threat/threat.js";
12
+ import { AntiGroundStaticDefence } from "./antiGroundStaticDefence.js";
13
+ import { ArtilleryUnit } from "./artilleryUnit.js";
14
+ import { BasicAirUnit } from "./basicAirUnit.js";
15
+ import { BasicBuilding } from "./basicBuilding.js";
16
+ import { BasicGroundUnit } from "./basicGroundUnit.js";
17
+ import { PowerPlant } from "./powerPlant.js";
18
+ import { ResourceCollectionBuilding } from "./resourceCollectionBuilding.js";
19
+ import { Harvester } from "./harvester.js";
20
+ import uniqBy from "lodash.uniqby";
21
+
22
+ export interface AiBuildingRules {
23
+ getPriority(
24
+ game: GameApi,
25
+ playerData: PlayerData,
26
+ technoRules: TechnoRules,
27
+ threatCache: GlobalThreat | null,
28
+ ): number;
29
+
30
+ getPlacementLocation(
31
+ game: GameApi,
32
+ playerData: PlayerData,
33
+ technoRules: TechnoRules,
34
+ ): { rx: number; ry: number } | undefined;
35
+
36
+ getMaxCount(
37
+ game: GameApi,
38
+ playerData: PlayerData,
39
+ technoRules: TechnoRules,
40
+ threatCache: GlobalThreat | null,
41
+ ): number | null;
42
+ }
43
+
44
+ export function numBuildingsOwnedOfType(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
45
+ return game.getVisibleUnits(playerData.name, "self", (r) => r == technoRules).length;
46
+ }
47
+
48
+ export function numBuildingsOwnedOfName(game: GameApi, playerData: PlayerData, name: string): number {
49
+ return game.getVisibleUnits(playerData.name, "self", (r) => r.name === name).length;
50
+ }
51
+
52
+ /**
53
+ * Computes a rect 'centered' around a structure of a certain size with additional radius.
54
+ *
55
+ * This is essentially the placeable area around a given structure.
56
+ *
57
+ * @param point Top-left location of the inner rect.
58
+ * @param t Size of the inner rect.
59
+ * @param adjacent Size of the outer rect.
60
+ * @returns
61
+ */
62
+ function computeAdjacentRect(point: Point2D, t: Size, adjacent: number) {
63
+ return {
64
+ x: point.x - adjacent,
65
+ y: point.y - adjacent,
66
+ width: t.width + 2 * adjacent,
67
+ height: t.height + 2 * adjacent,
68
+ };
69
+ }
70
+
71
+ export function getAdjacencyTiles(
72
+ game: GameApi,
73
+ playerData: PlayerData,
74
+ technoRules: TechnoRules,
75
+ minimumSpace: number,
76
+ ) {
77
+ const placementRules = game.getBuildingPlacementData(technoRules.name);
78
+ const { width: newBuildingWidth, height: newBuildingHeight } = placementRules.foundation;
79
+ const tiles = [];
80
+ const buildings = game.getVisibleUnits(playerData.name, "self", (r: TechnoRules) => r.type === ObjectType.Building);
81
+ const removedTiles = new Set<string>();
82
+ for (let buildingId of buildings) {
83
+ const building = game.getUnitData(buildingId);
84
+ if (building?.rules?.baseNormal) {
85
+ const { foundation, tile } = building;
86
+ const buildingBase = {
87
+ x: tile.rx,
88
+ y: tile.ry,
89
+ };
90
+ const buildingSize = {
91
+ width: foundation?.width,
92
+ height: foundation?.height,
93
+ };
94
+ const range = computeAdjacentRect(buildingBase, buildingSize, technoRules.adjacent);
95
+ const baseTile = game.mapApi.getTile(range.x, range.y);
96
+ if (!baseTile) {
97
+ continue;
98
+ }
99
+ const adjacentTiles = game.mapApi.getTilesInRect(baseTile, {
100
+ width: range.width,
101
+ height: range.height,
102
+ });
103
+ tiles.push(...adjacentTiles);
104
+
105
+ // Prevent placing the new building on tiles that would cause it to overlap with this building.
106
+ const modifiedBase = {
107
+ x: buildingBase.x - (newBuildingWidth - 1),
108
+ y: buildingBase.y - (newBuildingHeight - 1),
109
+ };
110
+ const modifiedSize = {
111
+ width: buildingSize.width + (newBuildingWidth - 1),
112
+ height: buildingSize.height + (newBuildingHeight - 1),
113
+ };
114
+ const blockedRect = computeAdjacentRect(modifiedBase, modifiedSize, minimumSpace);
115
+ const buildingTiles = adjacentTiles.filter((tile) => {
116
+ return (
117
+ tile.rx >= blockedRect.x &&
118
+ tile.rx < blockedRect.x + blockedRect.width &&
119
+ tile.ry >= blockedRect.y &&
120
+ tile.ry < blockedRect.y + blockedRect.height
121
+ );
122
+ });
123
+ buildingTiles.forEach((buildingTile) => removedTiles.add(buildingTile.id));
124
+ }
125
+ }
126
+ // Remove duplicate tiles.
127
+ const withDuplicatesRemoved = uniqBy(tiles, (tile) => tile.id);
128
+ // Remove tiles containing buildings and potentially area around them removed as well.
129
+ return withDuplicatesRemoved.filter((tile) => !removedTiles.has(tile.id));
130
+ }
131
+
132
+ function getTileDistances(startPoint: Point2D, tiles: Tile[]) {
133
+ return tiles
134
+ .map((tile) => ({
135
+ tile,
136
+ distance: distance(tile.rx, tile.ry, startPoint.x, startPoint.y),
137
+ }))
138
+ .sort((a, b) => {
139
+ return a.distance - b.distance;
140
+ });
141
+ }
142
+
143
+ function distance(x1: number, y1: number, x2: number, y2: number) {
144
+ var dx = x1 - x2;
145
+ var dy = y1 - y2;
146
+ let tmp = dx * dx + dy * dy;
147
+ if (0 === tmp) {
148
+ return 0;
149
+ }
150
+ return Math.sqrt(tmp);
151
+ }
152
+
153
+ export function getDefaultPlacementLocation(
154
+ game: GameApi,
155
+ playerData: PlayerData,
156
+ startPoint: Point2D,
157
+ technoRules: TechnoRules,
158
+ minSpace: number = 1,
159
+ ): { rx: number; ry: number } | undefined {
160
+ // Random location, preferably near start location.
161
+ const size: BuildingPlacementData = game.getBuildingPlacementData(technoRules.name);
162
+ if (!size) {
163
+ return undefined;
164
+ }
165
+ const tiles = getAdjacencyTiles(game, playerData, technoRules, minSpace);
166
+ const tileDistances = getTileDistances(startPoint, tiles);
167
+
168
+ for (let tileDistance of tileDistances) {
169
+ if (tileDistance.tile && game.canPlaceBuilding(playerData.name, technoRules.name, tileDistance.tile)) {
170
+ return tileDistance.tile;
171
+ }
172
+ }
173
+ return undefined;
174
+ }
175
+
176
+ // Priority 0 = don't build.
177
+ export type TechnoRulesWithPriority = { unit: TechnoRules; priority: number };
178
+
179
+ export const DEFAULT_BUILDING_PRIORITY = 1;
180
+
181
+ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
182
+ // Allied
183
+ ["GAPOWR", new PowerPlant()],
184
+ ["GAREFN", new ResourceCollectionBuilding(10, 3)], // Refinery
185
+ ["GAWEAP", new BasicBuilding(15, 1)], // War Factory
186
+ ["GAPILE", new BasicBuilding(12, 1)], // Barracks
187
+ ["CMIN", new Harvester(15, 4, 2)], // Chrono Miner
188
+ ["ENGINEER", new BasicBuilding(10, 1, 1000)], // Engineer
189
+ ["GADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
190
+ ["GAAIRC", new BasicBuilding(10, 1, 500)], // Airforce Command
191
+
192
+ ["GATECH", new BasicBuilding(20, 1, 4000)], // Allied Battle Lab
193
+ ["GAYARD", new BasicBuilding(0, 0, 0)], // Naval Yard, disabled
194
+
195
+ ["GAPILL", new AntiGroundStaticDefence(5, 1, 5)], // Pillbox
196
+ ["ATESLA", new AntiGroundStaticDefence(5, 1, 10)], // Prism Cannon
197
+ ["GAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
198
+
199
+ ["E1", new BasicGroundUnit(2, 3, 0.25, 0)], // GI
200
+ ["MTNK", new BasicGroundUnit(10, 3, 2, 0)], // Grizzly Tank
201
+ ["MGTK", new BasicGroundUnit(10, 1, 2.5, 0)], // Mirage Tank
202
+ ["FV", new BasicGroundUnit(5, 2, 0.5, 1)], // IFV
203
+ ["JUMPJET", new BasicAirUnit(10, 1, 1, 1)], // Rocketeer
204
+ ["ORCA", new BasicAirUnit(7, 1, 2, 0)], // Rocketeer
205
+ ["SREF", new ArtilleryUnit(10, 5, 3, 3)], // Prism Tank
206
+ ["CLEG", new BasicGroundUnit(0, 0)], // Chrono Legionnaire (Disabled - we don't handle the warped out phase properly and it tends to bug both bots out)
207
+ ["SHAD", new BasicGroundUnit(0, 0)], // Nighthawk (Disabled)
208
+
209
+ // Soviet
210
+ ["NAPOWR", new PowerPlant()],
211
+ ["NAREFN", new ResourceCollectionBuilding(10, 3)], // Refinery
212
+ ["NAWEAP", new BasicBuilding(15, 1)], // War Factory
213
+ ["NAHAND", new BasicBuilding(12, 1)], // Barracks
214
+ ["HARV", new Harvester(15, 4, 2)], // War Miner
215
+ ["SENGINEER", new BasicBuilding(10, 1, 1000)], // Soviet Engineer
216
+ ["NADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
217
+ ["NARADR", new BasicBuilding(10, 1, 500)], // Radar
218
+ ["NANRCT", new PowerPlant()], // Nuclear Reactor
219
+ ["NAYARD", new BasicBuilding(0, 0, 0)], // Naval Yard, disabled
220
+
221
+ ["NATECH", new BasicBuilding(20, 1, 4000)], // Soviet Battle Lab
222
+
223
+ ["NALASR", new AntiGroundStaticDefence(5, 1, 5)], // Sentry Gun
224
+ ["TESLA", new AntiGroundStaticDefence(5, 1, 10)], // Tesla Coil
225
+ ["NAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
226
+
227
+ ["E2", new BasicGroundUnit(2, 3, 0.25, 0)], // Conscript
228
+ ["HTNK", new BasicGroundUnit(10, 3, 3, 0)], // Rhino Tank
229
+ ["APOC", new BasicGroundUnit(6, 1, 5, 0)], // Apocalypse Tank
230
+ ["HTK", new BasicGroundUnit(5, 2, 0.33, 1.5)], // Flak Track
231
+ ["ZEP", new BasicAirUnit(5, 1, 5, 1)], // Kirov
232
+ ["V3", new ArtilleryUnit(9, 10, 0, 3)], // V3 Rocket Launcher
233
+ ]);
@@ -1,32 +1,32 @@
1
- import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
- import { AiBuildingRules, getDefaultPlacementLocation } from "./building.js";
3
- import { GlobalThreat } from "../threat/threat.js";
4
-
5
- export class PowerPlant implements AiBuildingRules {
6
- getPlacementLocation(
7
- game: GameApi,
8
- playerData: PlayerData,
9
- technoRules: TechnoRules
10
- ): { rx: number; ry: number } | undefined {
11
- return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
12
- }
13
-
14
- getPriority(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
15
- if (playerData.power.total < playerData.power.drain) {
16
- return 100;
17
- } else if (playerData.power.total < playerData.power.drain + technoRules.power / 2) {
18
- return 20;
19
- } else {
20
- return 0;
21
- }
22
- }
23
-
24
- getMaxCount(
25
- game: GameApi,
26
- playerData: PlayerData,
27
- technoRules: TechnoRules,
28
- threatCache: GlobalThreat | null
29
- ): number | null {
30
- return null;
31
- }
32
- }
1
+ import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
2
+ import { AiBuildingRules, getDefaultPlacementLocation } from "./buildingRules.js";
3
+ import { GlobalThreat } from "../threat/threat.js";
4
+
5
+ export class PowerPlant implements AiBuildingRules {
6
+ getPlacementLocation(
7
+ game: GameApi,
8
+ playerData: PlayerData,
9
+ technoRules: TechnoRules
10
+ ): { rx: number; ry: number } | undefined {
11
+ return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
12
+ }
13
+
14
+ getPriority(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
15
+ if (playerData.power.total < playerData.power.drain) {
16
+ return 100;
17
+ } else if (playerData.power.total < playerData.power.drain + technoRules.power / 2) {
18
+ return 20;
19
+ } else {
20
+ return 0;
21
+ }
22
+ }
23
+
24
+ getMaxCount(
25
+ game: GameApi,
26
+ playerData: PlayerData,
27
+ technoRules: TechnoRules,
28
+ threatCache: GlobalThreat | null
29
+ ): number | null {
30
+ return null;
31
+ }
32
+ }
@@ -13,7 +13,7 @@ import {
13
13
  BUILDING_NAME_TO_RULES,
14
14
  DEFAULT_BUILDING_PRIORITY,
15
15
  getDefaultPlacementLocation,
16
- } from "./building.js";
16
+ } from "./buildingRules.js";
17
17
 
18
18
  export const QUEUES = [
19
19
  QueueType.Structures,