@supalosa/chronodivide-bot 0.3.1 → 0.4.0

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 (53) hide show
  1. package/README.md +12 -1
  2. package/TODO.md +0 -3
  3. package/dist/bot/bot.js +17 -11
  4. package/dist/bot/bot.js.map +1 -1
  5. package/dist/bot/logic/building/antiGroundStaticDefence.js +1 -1
  6. package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
  7. package/dist/bot/logic/building/buildingRules.js +52 -45
  8. package/dist/bot/logic/building/buildingRules.js.map +1 -1
  9. package/dist/bot/logic/building/queueController.js +7 -2
  10. package/dist/bot/logic/building/queueController.js.map +1 -1
  11. package/dist/bot/logic/common/utils.js +14 -0
  12. package/dist/bot/logic/common/utils.js.map +1 -1
  13. package/dist/bot/logic/mission/missions/attackMission.js +59 -26
  14. package/dist/bot/logic/mission/missions/attackMission.js.map +1 -1
  15. package/dist/bot/logic/squad/behaviours/actionBatcher.js +36 -0
  16. package/dist/bot/logic/squad/behaviours/actionBatcher.js.map +1 -0
  17. package/dist/bot/logic/squad/behaviours/combatSquad.js +9 -4
  18. package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -1
  19. package/dist/bot/logic/squad/behaviours/common.js +7 -9
  20. package/dist/bot/logic/squad/behaviours/common.js.map +1 -1
  21. package/dist/bot/logic/squad/behaviours/engineerSquad.js +4 -2
  22. package/dist/bot/logic/squad/behaviours/engineerSquad.js.map +1 -1
  23. package/dist/bot/logic/squad/behaviours/expansionSquad.js +4 -2
  24. package/dist/bot/logic/squad/behaviours/expansionSquad.js.map +1 -1
  25. package/dist/bot/logic/squad/behaviours/retreatSquad.js +4 -1
  26. package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -1
  27. package/dist/bot/logic/squad/behaviours/scoutingSquad.js +4 -1
  28. package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -1
  29. package/dist/bot/logic/squad/squad.js +5 -2
  30. package/dist/bot/logic/squad/squad.js.map +1 -1
  31. package/dist/bot/logic/squad/squadBehaviour.js.map +1 -1
  32. package/dist/bot/logic/squad/squadController.js +15 -2
  33. package/dist/bot/logic/squad/squadController.js.map +1 -1
  34. package/dist/exampleBot.js +16 -6
  35. package/dist/exampleBot.js.map +1 -1
  36. package/package.json +3 -3
  37. package/src/bot/bot.ts +24 -13
  38. package/src/bot/logic/building/antiGroundStaticDefence.ts +1 -1
  39. package/src/bot/logic/building/buildingRules.ts +58 -48
  40. package/src/bot/logic/building/queueController.ts +10 -2
  41. package/src/bot/logic/common/utils.ts +19 -0
  42. package/src/bot/logic/mission/missions/attackMission.ts +72 -31
  43. package/src/bot/logic/squad/behaviours/actionBatcher.ts +65 -0
  44. package/src/bot/logic/squad/behaviours/combatSquad.ts +13 -3
  45. package/src/bot/logic/squad/behaviours/common.ts +9 -9
  46. package/src/bot/logic/squad/behaviours/engineerSquad.ts +9 -4
  47. package/src/bot/logic/squad/behaviours/expansionSquad.ts +9 -4
  48. package/src/bot/logic/squad/behaviours/retreatSquad.ts +6 -0
  49. package/src/bot/logic/squad/behaviours/scoutingSquad.ts +6 -0
  50. package/src/bot/logic/squad/squad.ts +7 -1
  51. package/src/bot/logic/squad/squadBehaviour.ts +4 -0
  52. package/src/bot/logic/squad/squadController.ts +19 -2
  53. package/src/exampleBot.ts +20 -6
@@ -2,6 +2,7 @@ import { ActionsApi, GameApi, OrderType, PlayerData, SideType } from "@chronodiv
2
2
  import { Squad } from "../squad.js";
3
3
  import { SquadAction, SquadBehaviour, disband, noop, requestSpecificUnits, requestUnits } from "../squadBehaviour.js";
4
4
  import { MatchAwareness } from "../../awareness.js";
5
+ import { ActionBatcher } from "./actionBatcher.js";
5
6
 
6
7
  const CAPTURE_COOLDOWN_TICKS = 30;
7
8
 
@@ -15,15 +16,15 @@ export class EngineerSquad implements SquadBehaviour {
15
16
  /**
16
17
  * @param captureTarget ID of the target to try and capture/send engineer into.
17
18
  */
18
- constructor(private captureTarget: number) {
19
- };
19
+ constructor(private captureTarget: number) {}
20
20
 
21
21
  public onAiUpdate(
22
22
  gameApi: GameApi,
23
23
  actionsApi: ActionsApi,
24
+ actionBatcher: ActionBatcher,
24
25
  playerData: PlayerData,
25
26
  squad: Squad,
26
- matchAwareness: MatchAwareness
27
+ matchAwareness: MatchAwareness,
27
28
  ): SquadAction {
28
29
  const engineerTypes = ["ENGINEER", "SENGINEER"];
29
30
  const engineers = squad.getUnitsOfTypes(gameApi, ...engineerTypes);
@@ -40,7 +41,7 @@ export class EngineerSquad implements SquadBehaviour {
40
41
  actionsApi.orderUnits(
41
42
  engineers.map((engineer) => engineer.id),
42
43
  OrderType.Capture,
43
- this.captureTarget
44
+ this.captureTarget,
44
45
  );
45
46
  // Add a cooldown to deploy attempts.
46
47
  this.hasAttemptedCaptureWith = {
@@ -50,4 +51,8 @@ export class EngineerSquad implements SquadBehaviour {
50
51
  }
51
52
  return noop();
52
53
  }
54
+
55
+ public getGlobalDebugText(): string | undefined {
56
+ return undefined;
57
+ }
53
58
  }
@@ -3,6 +3,7 @@ import { GlobalThreat } from "../../threat/threat.js";
3
3
  import { Squad } from "../squad.js";
4
4
  import { SquadAction, SquadBehaviour, disband, noop, requestSpecificUnits, requestUnits } from "../squadBehaviour.js";
5
5
  import { MatchAwareness } from "../../awareness.js";
6
+ import { ActionBatcher } from "./actionBatcher.js";
6
7
 
7
8
  const DEPLOY_COOLDOWN_TICKS = 30;
8
9
 
@@ -17,15 +18,15 @@ export class ExpansionSquad implements SquadBehaviour {
17
18
  * @param selectedMcv ID of the MCV to try to expand with. If that unit dies, the squad will disband. If no value is provided,
18
19
  * the mission requests an MCV.
19
20
  */
20
- constructor(private selectedMcv: number | null) {
21
- };
21
+ constructor(private selectedMcv: number | null) {}
22
22
 
23
23
  public onAiUpdate(
24
24
  gameApi: GameApi,
25
25
  actionsApi: ActionsApi,
26
+ actionBatcher: ActionBatcher,
26
27
  playerData: PlayerData,
27
28
  squad: Squad,
28
- matchAwareness: MatchAwareness
29
+ matchAwareness: MatchAwareness,
29
30
  ): SquadAction {
30
31
  const mcvTypes = ["AMCV", "SMCV"];
31
32
  const mcvs = squad.getUnitsOfTypes(gameApi, ...mcvTypes);
@@ -46,7 +47,7 @@ export class ExpansionSquad implements SquadBehaviour {
46
47
  ) {
47
48
  actionsApi.orderUnits(
48
49
  mcvs.map((mcv) => mcv.id),
49
- OrderType.DeploySelected
50
+ OrderType.DeploySelected,
50
51
  );
51
52
  // Add a cooldown to deploy attempts.
52
53
  this.hasAttemptedDeployWith = {
@@ -56,4 +57,8 @@ export class ExpansionSquad implements SquadBehaviour {
56
57
  }
57
58
  return noop();
58
59
  }
60
+
61
+ public getGlobalDebugText(): string | undefined {
62
+ return undefined;
63
+ }
59
64
  }
@@ -3,6 +3,7 @@ import { GlobalThreat } from "../../threat/threat.js";
3
3
  import { Squad } from "../squad.js";
4
4
  import { SquadAction, SquadBehaviour, disband, noop, requestSpecificUnits, requestUnits } from "../squadBehaviour.js";
5
5
  import { MatchAwareness } from "../../awareness.js";
6
+ import { ActionBatcher } from "./actionBatcher.js";
6
7
 
7
8
  const SCOUT_MOVE_COOLDOWN_TICKS = 30;
8
9
 
@@ -17,6 +18,7 @@ export class RetreatSquad implements SquadBehaviour {
17
18
  public onAiUpdate(
18
19
  gameApi: GameApi,
19
20
  actionsApi: ActionsApi,
21
+ actionBatcher: ActionBatcher,
20
22
  playerData: PlayerData,
21
23
  squad: Squad,
22
24
  matchAwareness: MatchAwareness,
@@ -41,4 +43,8 @@ export class RetreatSquad implements SquadBehaviour {
41
43
  return requestSpecificUnits(this.unitIds, 1000);
42
44
  }
43
45
  }
46
+
47
+ public getGlobalDebugText(): string | undefined {
48
+ return undefined;
49
+ }
44
50
  }
@@ -5,6 +5,7 @@ import { MatchAwareness } from "../../awareness.js";
5
5
  import { DebugLogger } from "../../common/utils.js";
6
6
  import { getDistanceBetweenTileAndPoint } from "../../map/map.js";
7
7
  import { PrioritisedScoutTarget } from "../../common/scout.js";
8
+ import { ActionBatcher } from "./actionBatcher.js";
8
9
 
9
10
  const SCOUT_MOVE_COOLDOWN_TICKS = 30;
10
11
 
@@ -30,6 +31,7 @@ export class ScoutingSquad implements SquadBehaviour {
30
31
  public onAiUpdate(
31
32
  gameApi: GameApi,
32
33
  actionsApi: ActionsApi,
34
+ actionBatcher: ActionBatcher,
33
35
  playerData: PlayerData,
34
36
  squad: Squad,
35
37
  matchAwareness: MatchAwareness,
@@ -109,4 +111,8 @@ export class ScoutingSquad implements SquadBehaviour {
109
111
  this.scoutMinDistance = undefined;
110
112
  this.scoutTargetIsPermanent = target?.isPermanent ?? false;
111
113
  }
114
+
115
+ public getGlobalDebugText(): string | undefined {
116
+ return undefined;
117
+ }
112
118
  }
@@ -4,6 +4,7 @@ import { SquadAction, SquadBehaviour, disband } from "./squadBehaviour.js";
4
4
  import { MatchAwareness } from "../awareness.js";
5
5
  import { getDistanceBetweenTileAndPoint } from "../map/map.js";
6
6
  import { DebugLogger } from "../common/utils.js";
7
+ import { ActionBatcher } from "./behaviours/actionBatcher.js";
7
8
 
8
9
  export enum SquadLiveness {
9
10
  SquadDead,
@@ -70,6 +71,7 @@ export class Squad {
70
71
  public onAiUpdate(
71
72
  gameApi: GameApi,
72
73
  actionsApi: ActionsApi,
74
+ actionBatcher: ActionBatcher,
73
75
  playerData: PlayerData,
74
76
  matchAwareness: MatchAwareness,
75
77
  logger: DebugLogger,
@@ -97,7 +99,7 @@ export class Squad {
97
99
  } else if (!this.mission) {
98
100
  return disband();
99
101
  }
100
- return this.behaviour.onAiUpdate(gameApi, actionsApi, playerData, this, matchAwareness, logger);
102
+ return this.behaviour.onAiUpdate(gameApi, actionsApi, actionBatcher, playerData, this, matchAwareness, logger);
101
103
  }
102
104
  public getMission(): Mission | null {
103
105
  return this.mission;
@@ -156,4 +158,8 @@ export class Squad {
156
158
  public getLiveness() {
157
159
  return this.liveness;
158
160
  }
161
+
162
+ public getGlobalDebugText(): string | undefined {
163
+ return this.behaviour.getGlobalDebugText();
164
+ }
159
165
  }
@@ -2,6 +2,7 @@ import { ActionsApi, GameApi, PlayerData, Vector2 } from "@chronodivide/game-api
2
2
  import { Squad } from "./squad.js";
3
3
  import { MatchAwareness } from "../awareness.js";
4
4
  import { DebugLogger } from "../common/utils.js";
5
+ import { ActionBatcher } from "./behaviours/actionBatcher.js";
5
6
 
6
7
  export type SquadActionNoop = {
7
8
  type: "noop";
@@ -54,9 +55,12 @@ export interface SquadBehaviour {
54
55
  onAiUpdate(
55
56
  gameApi: GameApi,
56
57
  actionsApi: ActionsApi,
58
+ actionBatcher: ActionBatcher,
57
59
  playerData: PlayerData,
58
60
  squad: Squad,
59
61
  matchAwareness: MatchAwareness,
60
62
  logger: DebugLogger,
61
63
  ): SquadAction;
64
+
65
+ getGlobalDebugText(): string | undefined;
62
66
  }
@@ -13,6 +13,7 @@ import {
13
13
  import { MatchAwareness } from "../awareness.js";
14
14
  import { getDistanceBetween } from "../map/map.js";
15
15
  import { countBy } from "../common/utils.js";
16
+ import { ActionBatcher } from "./behaviours/actionBatcher.js";
16
17
 
17
18
  type SquadWithAction<T> = {
18
19
  squad: Squad;
@@ -47,12 +48,16 @@ export class SquadController {
47
48
  });
48
49
  });
49
50
 
51
+ // Batch actions to reduce spamming of actions for larger armies.
52
+ const actionBatcher = new ActionBatcher();
53
+
50
54
  const squadActions: SquadWithAction<SquadAction>[] = this.squads.map((squad) => {
51
55
  return {
52
56
  squad,
53
- action: squad.onAiUpdate(gameApi, actionsApi, playerData, matchAwareness, this.logger),
57
+ action: squad.onAiUpdate(gameApi, actionsApi, actionBatcher, playerData, matchAwareness, this.logger),
54
58
  };
55
59
  });
60
+
56
61
  // Handle disbands and merges.
57
62
  const isDisband = (a: SquadAction): a is SquadActionDisband => a.type === "disband";
58
63
  const isMerge = (a: SquadAction): a is SquadActionMergeInto => a.type === "mergeInto";
@@ -64,6 +69,7 @@ export class SquadController {
64
69
  a.squad.getMission()?.removeSquad();
65
70
  a.squad.getUnitIds().forEach((unitId) => {
66
71
  this.unitIdToSquad.delete(unitId);
72
+ actionsApi.setUnitDebugText(unitId, undefined);
67
73
  });
68
74
  disbandedSquads.add(a.squad.getName());
69
75
  });
@@ -229,6 +235,8 @@ export class SquadController {
229
235
  .join(", ")}`,
230
236
  );
231
237
  });
238
+
239
+ actionBatcher.resolve(actionsApi);
232
240
  }
233
241
 
234
242
  private addUnitToSquad(squad: Squad, unit: UnitData) {
@@ -240,15 +248,24 @@ export class SquadController {
240
248
  this.squads.push(squad);
241
249
  }
242
250
 
243
- public debugSquads(gameApi: GameApi) {
251
+ // return text to display for global debug
252
+ public debugSquads(gameApi: GameApi, actionsApi: ActionsApi): string {
244
253
  const unitsInSquad = (unitIds: number[]) => countBy(unitIds, (unitId) => gameApi.getUnitData(unitId)?.name);
245
254
 
255
+ let globalDebugText = "";
256
+
246
257
  this.squads.forEach((squad) => {
247
258
  this.logger(
248
259
  `Squad ${squad.getName()}: ${Object.entries(unitsInSquad(squad.getUnitIds()))
249
260
  .map(([unitName, count]) => `${unitName} x ${count}`)
250
261
  .join(", ")}`,
251
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
+ }
252
268
  });
269
+ return globalDebugText;
253
270
  }
254
271
  }
package/src/exampleBot.ts CHANGED
@@ -8,7 +8,7 @@ async function main() {
8
8
  CDR2 1v1 4_country_swing_le_v2.map
9
9
  CDR2 1v1 mp01t4.map, large map, oil derricks
10
10
  CDR2 1v1 tn04t2.map, small map
11
- CDR2 1v1 mp10s4.map <- depth charge, naval map (not supported)
11
+ CDR2 1v1 mp10s4.map <- depth charge, naval map (not supported). Cramped in position 1.
12
12
  CDR2 1v1 heckcorners.map
13
13
  CDR2 1v1 4_montana_dmz_le.map
14
14
  CDR2 1v1 barrel.map
@@ -16,10 +16,13 @@ async function main() {
16
16
  Other maps:
17
17
  mp03t4 large map, no oil derricks
18
18
  */
19
- const mapName = "mp10s4.map";
19
+ const mapName = "mp03t4.map";
20
20
  // Bot names must be unique in online mode
21
- const botName = `Joe${String(Date.now()).substr(-6)}`;
22
- const otherBotName = `Bob${String(Date.now() + 1).substr(-6)}`;
21
+ const timestamp = String(Date.now()).substr(-6);
22
+ const firstBotName = `Joe${timestamp}`;
23
+ const secondBotName = `Bob${timestamp}`;
24
+ const thirdBotName = `Mike${timestamp}`;
25
+ const fourthBotName = `Charlie${timestamp}`;
23
26
 
24
27
  await cdapi.init(process.env.MIX_DIR || "./");
25
28
 
@@ -44,11 +47,22 @@ async function main() {
44
47
  online: true as true,
45
48
  serverUrl: process.env.SERVER_URL!,
46
49
  clientUrl: process.env.CLIENT_URL!,
47
- agents: [new SupalosaBot(botName, "Americans"), { name: otherBotName, country: "French" }] as [Bot, ...Agent[]],
50
+ agents: [new SupalosaBot(firstBotName, "Americans"), { name: secondBotName, country: "French" }] as [
51
+ Bot,
52
+ ...Agent[],
53
+ ],
48
54
  };
49
55
 
56
+ const debugBot = new SupalosaBot(secondBotName, "Russians", [secondBotName], true);
57
+ debugBot.setDebugMode(true);
58
+
50
59
  const offlineSettings = {
51
- agents: [new SupalosaBot(botName, "French", false), new SupalosaBot(otherBotName, "Russians", true)],
60
+ agents: [
61
+ new SupalosaBot(firstBotName, "French", [firstBotName], false),
62
+ debugBot,
63
+ new SupalosaBot(thirdBotName, "Russians", [fourthBotName], false),
64
+ new SupalosaBot(fourthBotName, "French", [thirdBotName], false),
65
+ ],
52
66
  };
53
67
 
54
68
  const game = await cdapi.createGame({