@supalosa/chronodivide-bot 0.4.0 → 0.5.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.
Files changed (123) hide show
  1. package/.env.template +5 -0
  2. package/README.md +54 -47
  3. package/dist/bot/bot.js +14 -35
  4. package/dist/bot/bot.js.map +1 -1
  5. package/dist/bot/logic/awareness.js +13 -8
  6. package/dist/bot/logic/awareness.js.map +1 -1
  7. package/dist/bot/logic/building/ArtilleryUnit.js +2 -29
  8. package/dist/bot/logic/building/antiAirStaticDefence.js +43 -0
  9. package/dist/bot/logic/building/antiAirStaticDefence.js.map +1 -0
  10. package/dist/bot/logic/building/antiGroundStaticDefence.js +10 -20
  11. package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
  12. package/dist/bot/logic/building/artilleryUnit.js.map +1 -1
  13. package/dist/bot/logic/building/basicAirUnit.js +2 -23
  14. package/dist/bot/logic/building/basicAirUnit.js.map +1 -1
  15. package/dist/bot/logic/building/basicBuilding.js +3 -2
  16. package/dist/bot/logic/building/basicBuilding.js.map +1 -1
  17. package/dist/bot/logic/building/basicGroundUnit.js +2 -43
  18. package/dist/bot/logic/building/basicGroundUnit.js.map +1 -1
  19. package/dist/bot/logic/building/buildingRules.js +15 -9
  20. package/dist/bot/logic/building/buildingRules.js.map +1 -1
  21. package/dist/bot/logic/building/common.js +19 -0
  22. package/dist/bot/logic/building/common.js.map +1 -0
  23. package/dist/bot/logic/building/harvester.js +2 -1
  24. package/dist/bot/logic/building/harvester.js.map +1 -1
  25. package/dist/bot/logic/building/queueController.js +69 -42
  26. package/dist/bot/logic/building/queueController.js.map +1 -1
  27. package/dist/bot/logic/common/utils.js +21 -0
  28. package/dist/bot/logic/common/utils.js.map +1 -1
  29. package/dist/bot/logic/composition/alliedCompositions.js +13 -0
  30. package/dist/bot/logic/composition/alliedCompositions.js.map +1 -0
  31. package/dist/bot/logic/composition/common.js +2 -0
  32. package/dist/bot/logic/composition/common.js.map +1 -0
  33. package/dist/bot/logic/composition/sovietCompositions.js +13 -0
  34. package/dist/bot/logic/composition/sovietCompositions.js.map +1 -0
  35. package/dist/bot/logic/mission/actionBatcher.js +92 -0
  36. package/dist/bot/logic/mission/actionBatcher.js.map +1 -0
  37. package/dist/bot/logic/mission/behaviours/combatSquad.js +124 -0
  38. package/dist/bot/logic/mission/behaviours/combatSquad.js.map +1 -0
  39. package/dist/bot/logic/mission/behaviours/common.js +56 -0
  40. package/dist/bot/logic/mission/behaviours/common.js.map +1 -0
  41. package/dist/bot/logic/mission/behaviours/engineerSquad.js +39 -0
  42. package/dist/bot/logic/mission/behaviours/engineerSquad.js.map +1 -0
  43. package/dist/bot/logic/mission/behaviours/expansionSquad.js +46 -0
  44. package/dist/bot/logic/mission/behaviours/expansionSquad.js.map +1 -0
  45. package/dist/bot/logic/mission/behaviours/retreatSquad.js +31 -0
  46. package/dist/bot/logic/mission/behaviours/retreatSquad.js.map +1 -0
  47. package/{src/bot/logic/squad/behaviours/scoutingSquad.ts → dist/bot/logic/mission/behaviours/scoutingSquad.js} +27 -51
  48. package/dist/bot/logic/mission/behaviours/scoutingSquad.js.map +1 -0
  49. package/dist/bot/logic/mission/mission.js +91 -19
  50. package/dist/bot/logic/mission/mission.js.map +1 -1
  51. package/dist/bot/logic/mission/missionController.js +262 -21
  52. package/dist/bot/logic/mission/missionController.js.map +1 -1
  53. package/dist/bot/logic/mission/missions/attackMission.js +113 -39
  54. package/dist/bot/logic/mission/missions/attackMission.js.map +1 -1
  55. package/dist/bot/logic/mission/missions/basicMission.js +13 -0
  56. package/dist/bot/logic/mission/missions/basicMission.js.map +1 -0
  57. package/dist/bot/logic/mission/missions/defenceMission.js +43 -28
  58. package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -1
  59. package/dist/bot/logic/mission/missions/engineerMission.js +37 -7
  60. package/dist/bot/logic/mission/missions/engineerMission.js.map +1 -1
  61. package/dist/bot/logic/mission/missions/expansionMission.js +42 -6
  62. package/dist/bot/logic/mission/missions/expansionMission.js.map +1 -1
  63. package/dist/bot/logic/mission/missions/missionBehaviour.js +2 -0
  64. package/dist/bot/logic/mission/missions/missionBehaviour.js.map +1 -0
  65. package/dist/bot/logic/mission/missions/retreatMission.js +31 -5
  66. package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -1
  67. package/dist/bot/logic/mission/missions/scoutingMission.js +103 -6
  68. package/dist/bot/logic/mission/missions/scoutingMission.js.map +1 -1
  69. package/dist/bot/logic/mission/missions/squads/combatSquad.js +116 -0
  70. package/dist/bot/logic/mission/missions/squads/combatSquad.js.map +1 -0
  71. package/dist/bot/logic/mission/missions/squads/common.js +58 -0
  72. package/dist/bot/logic/mission/missions/squads/common.js.map +1 -0
  73. package/dist/bot/logic/mission/missions/squads/squad.js +2 -0
  74. package/dist/bot/logic/mission/missions/squads/squad.js.map +1 -0
  75. package/dist/bot/logic/squad/squadController.js +6 -2
  76. package/dist/bot/logic/squad/squadController.js.map +1 -1
  77. package/dist/bot/logic/threat/threatCalculator.js +9 -9
  78. package/dist/bot/logic/threat/threatCalculator.js.map +1 -1
  79. package/dist/exampleBot.js +50 -18
  80. package/dist/exampleBot.js.map +1 -1
  81. package/package.json +5 -4
  82. package/src/bot/bot.ts +19 -51
  83. package/src/bot/logic/awareness.ts +34 -22
  84. package/src/bot/logic/building/antiAirStaticDefence.ts +64 -0
  85. package/src/bot/logic/building/antiGroundStaticDefence.ts +7 -20
  86. package/src/bot/logic/building/artilleryUnit.ts +2 -28
  87. package/src/bot/logic/building/basicAirUnit.ts +2 -33
  88. package/src/bot/logic/building/basicBuilding.ts +8 -6
  89. package/src/bot/logic/building/basicGroundUnit.ts +2 -46
  90. package/src/bot/logic/building/buildingRules.ts +15 -9
  91. package/src/bot/logic/building/common.ts +23 -0
  92. package/src/bot/logic/building/harvester.ts +2 -1
  93. package/src/bot/logic/building/queueController.ts +98 -43
  94. package/src/bot/logic/common/utils.ts +28 -0
  95. package/src/bot/logic/composition/alliedCompositions.ts +22 -0
  96. package/src/bot/logic/composition/common.ts +3 -0
  97. package/src/bot/logic/composition/sovietCompositions.ts +21 -0
  98. package/src/bot/logic/{squad/behaviours → mission}/actionBatcher.ts +66 -7
  99. package/src/bot/logic/mission/mission.ts +186 -37
  100. package/src/bot/logic/mission/missionController.ts +340 -31
  101. package/src/bot/logic/mission/missionFactories.ts +3 -3
  102. package/src/bot/logic/mission/missions/attackMission.ts +181 -44
  103. package/src/bot/logic/mission/missions/defenceMission.ts +72 -45
  104. package/src/bot/logic/mission/missions/engineerMission.ts +67 -15
  105. package/src/bot/logic/mission/missions/expansionMission.ts +67 -14
  106. package/src/bot/logic/mission/missions/retreatMission.ts +50 -6
  107. package/src/bot/logic/mission/missions/scoutingMission.ts +138 -14
  108. package/src/bot/logic/{squad/behaviours → mission/missions/squads}/combatSquad.ts +56 -33
  109. package/src/bot/logic/{squad/behaviours → mission/missions/squads}/common.ts +11 -17
  110. package/src/bot/logic/mission/missions/squads/squad.ts +19 -0
  111. package/src/bot/logic/threat/threatCalculator.ts +10 -10
  112. package/src/exampleBot.ts +56 -24
  113. package/.prettierrc +0 -5
  114. package/TODO.md +0 -15
  115. package/rules.ini +0 -23126
  116. package/src/bot/logic/mission/missions/oneTimeMission.ts +0 -33
  117. package/src/bot/logic/squad/behaviours/engineerSquad.ts +0 -58
  118. package/src/bot/logic/squad/behaviours/expansionSquad.ts +0 -64
  119. package/src/bot/logic/squad/behaviours/retreatSquad.ts +0 -50
  120. package/src/bot/logic/squad/squad.ts +0 -165
  121. package/src/bot/logic/squad/squadBehaviour.ts +0 -66
  122. package/src/bot/logic/squad/squadBehaviours.ts +0 -8
  123. package/src/bot/logic/squad/squadController.ts +0 -271
@@ -1,271 +0,0 @@
1
- // Meta-controller for forming and controlling squads.
2
-
3
- import { ActionsApi, GameApi, PlayerData, UnitData } from "@chronodivide/game-api";
4
- import { Squad, SquadLiveness } from "./squad.js";
5
- import {
6
- SquadAction,
7
- SquadActionDisband,
8
- SquadActionGrabFreeCombatants,
9
- SquadActionMergeInto,
10
- SquadActionRequestSpecificUnits,
11
- SquadActionRequestUnits,
12
- } from "./squadBehaviour.js";
13
- import { MatchAwareness } from "../awareness.js";
14
- import { getDistanceBetween } from "../map/map.js";
15
- import { countBy } from "../common/utils.js";
16
- import { ActionBatcher } from "./behaviours/actionBatcher.js";
17
-
18
- type SquadWithAction<T> = {
19
- squad: Squad;
20
- action: T;
21
- };
22
-
23
- export class SquadController {
24
- private squads: Squad[] = [];
25
- private unitIdToSquad: Map<number, Squad> = new Map();
26
-
27
- constructor(private logger: (message: string, sayInGame?: boolean) => void) {}
28
-
29
- public onAiUpdate(
30
- gameApi: GameApi,
31
- actionsApi: ActionsApi,
32
- playerData: PlayerData,
33
- matchAwareness: MatchAwareness,
34
- ) {
35
- // Remove dead squads or those where the mission is dead.
36
- this.squads = this.squads.filter((squad) => squad.getLiveness() !== SquadLiveness.SquadDead);
37
- this.squads.sort((a, b) => a.getName().localeCompare(b.getName()));
38
-
39
- // Check for units in multiple squads, this shouldn't happen.
40
- this.unitIdToSquad = new Map();
41
- this.squads.forEach((squad) => {
42
- squad.getUnitIds().forEach((unitId) => {
43
- if (this.unitIdToSquad.has(unitId)) {
44
- this.logger(`WARNING: unit ${unitId} is in multiple squads, please debug.`);
45
- } else {
46
- this.unitIdToSquad.set(unitId, squad);
47
- }
48
- });
49
- });
50
-
51
- // Batch actions to reduce spamming of actions for larger armies.
52
- const actionBatcher = new ActionBatcher();
53
-
54
- const squadActions: SquadWithAction<SquadAction>[] = this.squads.map((squad) => {
55
- return {
56
- squad,
57
- action: squad.onAiUpdate(gameApi, actionsApi, actionBatcher, playerData, matchAwareness, this.logger),
58
- };
59
- });
60
-
61
- // Handle disbands and merges.
62
- const isDisband = (a: SquadAction): a is SquadActionDisband => a.type === "disband";
63
- const isMerge = (a: SquadAction): a is SquadActionMergeInto => a.type === "mergeInto";
64
- let disbandedSquads: Set<string> = new Set();
65
- squadActions
66
- .filter((a) => isDisband(a.action))
67
- .forEach((a) => {
68
- this.logger(`Squad ${a.squad.getName()} disbanding as requested.`);
69
- a.squad.getMission()?.removeSquad();
70
- a.squad.getUnitIds().forEach((unitId) => {
71
- this.unitIdToSquad.delete(unitId);
72
- actionsApi.setUnitDebugText(unitId, undefined);
73
- });
74
- disbandedSquads.add(a.squad.getName());
75
- });
76
- squadActions
77
- .filter((a) => isMerge(a.action))
78
- .forEach((a) => {
79
- let mergeInto = a.action as SquadActionMergeInto;
80
- if (disbandedSquads.has(mergeInto.mergeInto.getName())) {
81
- this.logger(
82
- `Squad ${a.squad.getName()} tried to merge into disbanded squad ${mergeInto.mergeInto.getName()}, cancelling.`,
83
- );
84
- return;
85
- }
86
- a.squad.getUnitIds().forEach((unitId) => mergeInto.mergeInto.addUnit(unitId));
87
- disbandedSquads.add(a.squad.getName());
88
- });
89
- // remove disbanded and merged squads.
90
- this.squads = this.squads.filter((squad) => !disbandedSquads.has(squad.getName()));
91
-
92
- // Request specific units by ID
93
- const isRequestSpecific = (a: SquadAction) => a.type === "requestSpecific";
94
- const unitIdToHighestRequest = squadActions
95
- .filter((a) => isRequestSpecific(a.action))
96
- .reduce(
97
- (prev, a) => {
98
- const squadWithAction = a as SquadWithAction<SquadActionRequestSpecificUnits>;
99
- const { unitIds } = squadWithAction.action;
100
- unitIds.forEach((unitId) => {
101
- if (prev.hasOwnProperty(unitId)) {
102
- if (prev[unitId].action.priority > prev[unitId].action.priority) {
103
- prev[unitId] = squadWithAction;
104
- }
105
- } else {
106
- prev[unitId] = squadWithAction;
107
- }
108
- });
109
- return prev;
110
- },
111
- {} as Record<number, SquadWithAction<SquadActionRequestSpecificUnits>>,
112
- );
113
-
114
- // Map of Squad ID to Unit Type to Count.
115
- const newSquadAssignments = Object.entries(unitIdToHighestRequest)
116
- .flatMap(([id, request]) => {
117
- const unitId = Number.parseInt(id);
118
- const unit = gameApi.getUnitData(unitId);
119
- const { squad: requestingSquad } = request;
120
- const missionName = requestingSquad.getMission()?.getUniqueName();
121
- if (!unit) {
122
- this.logger(`mission ${missionName} requested non-existent unit ${unitId}`);
123
- return [];
124
- }
125
- if (!this.unitIdToSquad.has(unitId)) {
126
- this.addUnitToSquad(requestingSquad, unit);
127
- return [{ unitName: unit?.name, squad: requestingSquad.getName() }];
128
- }
129
- return [];
130
- })
131
- .reduce(
132
- (acc, curr) => {
133
- if (!acc[curr.squad]) {
134
- acc[curr.squad] = {};
135
- }
136
- if (!acc[curr.squad][curr.unitName]) {
137
- acc[curr.squad][curr.unitName] = 0;
138
- }
139
- acc[curr.squad][curr.unitName] = acc[curr.squad][curr.unitName] + 1;
140
- return acc;
141
- },
142
- {} as Record<string, Record<string, number>>,
143
- );
144
- Object.entries(newSquadAssignments).forEach(([squad, assignments]) => {
145
- this.logger(
146
- `Squad ${squad} received: ${Object.entries(assignments)
147
- .map(([unitType, count]) => unitType + " x " + count)
148
- .join(", ")}`,
149
- );
150
- });
151
-
152
- // Request units by type
153
- const isRequest = (a: SquadAction) => a.type === "request";
154
- const unitTypeToHighestRequest = squadActions
155
- .filter((a) => isRequest(a.action))
156
- .reduce(
157
- (prev, a) => {
158
- const squadWithAction = a as SquadWithAction<SquadActionRequestUnits>;
159
- const { unitNames } = squadWithAction.action;
160
- unitNames.forEach((unitName) => {
161
- if (prev.hasOwnProperty(unitName)) {
162
- if (prev[unitName].action.priority > prev[unitName].action.priority) {
163
- prev[unitName] = squadWithAction;
164
- }
165
- } else {
166
- prev[unitName] = squadWithAction;
167
- }
168
- });
169
- return prev;
170
- },
171
- {} as Record<string, SquadWithAction<SquadActionRequestUnits>>,
172
- );
173
-
174
- // Request combat-capable units in an area
175
- const isGrab = (a: SquadAction) => a.type === "requestCombatants";
176
- const grabRequests = squadActions.filter((a) =>
177
- isGrab(a.action),
178
- ) as SquadWithAction<SquadActionGrabFreeCombatants>[];
179
-
180
- // Find loose units
181
- const unitIds = gameApi.getVisibleUnits(playerData.name, "self");
182
- const freeUnits = unitIds
183
- .map((unitId) => gameApi.getUnitData(unitId))
184
- .filter((unit) => !!unit && !this.unitIdToSquad.has(unit.id || 0))
185
- .map((unit) => unit!);
186
-
187
- type AssignmentWithType = { unitName: string; squad: string; method: "type" | "grab" };
188
- const newAssignmentsByType = freeUnits
189
- .flatMap((freeUnit) => {
190
- if (unitTypeToHighestRequest.hasOwnProperty(freeUnit.name)) {
191
- const { squad: requestingSquad } = unitTypeToHighestRequest[freeUnit.name];
192
- this.logger(`granting unit ${freeUnit.id}#${freeUnit.name} to squad ${requestingSquad.getName()}`);
193
- this.addUnitToSquad(requestingSquad, freeUnit);
194
- delete unitTypeToHighestRequest[freeUnit.name];
195
- return [
196
- { unitName: freeUnit.name, squad: requestingSquad.getName(), method: "type" },
197
- ] as AssignmentWithType[];
198
- } else if (grabRequests.length > 0) {
199
- const grantedSquad = grabRequests.find((request) => {
200
- return (
201
- freeUnit.rules.isSelectableCombatant &&
202
- getDistanceBetween(freeUnit, request.action.point) <= request.action.radius
203
- );
204
- });
205
- if (grantedSquad) {
206
- this.addUnitToSquad(grantedSquad.squad, freeUnit);
207
- return [
208
- { unitName: freeUnit.name, squad: grantedSquad.squad.getName(), method: "grab" },
209
- ] as AssignmentWithType[];
210
- }
211
- }
212
- return [];
213
- })
214
- .reduce(
215
- (acc, curr) => {
216
- if (!acc[curr.squad]) {
217
- acc[curr.squad] = {};
218
- }
219
- if (!acc[curr.squad][curr.unitName]) {
220
- acc[curr.squad][curr.unitName] = { grab: 0, type: 0 };
221
- }
222
- acc[curr.squad][curr.unitName][curr.method] = acc[curr.squad][curr.unitName][curr.method] + 1;
223
- return acc;
224
- },
225
- {} as Record<string, Record<string, Record<"type" | "grab", number>>>,
226
- );
227
- Object.entries(newAssignmentsByType).forEach(([squad, assignments]) => {
228
- this.logger(
229
- `Squad ${squad} received: ${Object.entries(assignments)
230
- .flatMap(([unitType, methodToCount]) =>
231
- Object.entries(methodToCount)
232
- .filter(([, count]) => count > 0)
233
- .map(([method, count]) => unitType + " x " + count + " (by " + method + ")"),
234
- )
235
- .join(", ")}`,
236
- );
237
- });
238
-
239
- actionBatcher.resolve(actionsApi);
240
- }
241
-
242
- private addUnitToSquad(squad: Squad, unit: UnitData) {
243
- squad.addUnit(unit.id);
244
- this.unitIdToSquad.set(unit.id, squad);
245
- }
246
-
247
- public registerSquad(squad: Squad) {
248
- this.squads.push(squad);
249
- }
250
-
251
- // return text to display for global debug
252
- public debugSquads(gameApi: GameApi, actionsApi: ActionsApi): string {
253
- const unitsInSquad = (unitIds: number[]) => countBy(unitIds, (unitId) => gameApi.getUnitData(unitId)?.name);
254
-
255
- let globalDebugText = "";
256
-
257
- this.squads.forEach((squad) => {
258
- this.logger(
259
- `Squad ${squad.getName()}: ${Object.entries(unitsInSquad(squad.getUnitIds()))
260
- .map(([unitName, count]) => `${unitName} x ${count}`)
261
- .join(", ")}`,
262
- );
263
- squad.getUnitIds().forEach((unitId) => actionsApi.setUnitDebugText(unitId, squad.getName()));
264
- const squadDebugText = squad.getGlobalDebugText();
265
- if (squadDebugText) {
266
- globalDebugText += squad.getName() + ": " + squadDebugText + "\n";
267
- }
268
- });
269
- return globalDebugText;
270
- }
271
- }