@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.
- package/README.md +12 -1
- package/TODO.md +0 -3
- package/dist/bot/bot.js +17 -11
- package/dist/bot/bot.js.map +1 -1
- package/dist/bot/logic/building/antiGroundStaticDefence.js +1 -1
- package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
- package/dist/bot/logic/building/buildingRules.js +52 -45
- package/dist/bot/logic/building/buildingRules.js.map +1 -1
- package/dist/bot/logic/building/queueController.js +7 -2
- package/dist/bot/logic/building/queueController.js.map +1 -1
- package/dist/bot/logic/common/utils.js +14 -0
- package/dist/bot/logic/common/utils.js.map +1 -1
- package/dist/bot/logic/mission/missions/attackMission.js +59 -26
- package/dist/bot/logic/mission/missions/attackMission.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/actionBatcher.js +36 -0
- package/dist/bot/logic/squad/behaviours/actionBatcher.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/combatSquad.js +9 -4
- package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/common.js +7 -9
- package/dist/bot/logic/squad/behaviours/common.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/engineerSquad.js +4 -2
- package/dist/bot/logic/squad/behaviours/engineerSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/expansionSquad.js +4 -2
- package/dist/bot/logic/squad/behaviours/expansionSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/retreatSquad.js +4 -1
- package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +4 -1
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -1
- package/dist/bot/logic/squad/squad.js +5 -2
- package/dist/bot/logic/squad/squad.js.map +1 -1
- package/dist/bot/logic/squad/squadBehaviour.js.map +1 -1
- package/dist/bot/logic/squad/squadController.js +15 -2
- package/dist/bot/logic/squad/squadController.js.map +1 -1
- package/dist/exampleBot.js +16 -6
- package/dist/exampleBot.js.map +1 -1
- package/package.json +3 -3
- package/src/bot/bot.ts +24 -13
- package/src/bot/logic/building/antiGroundStaticDefence.ts +1 -1
- package/src/bot/logic/building/buildingRules.ts +58 -48
- package/src/bot/logic/building/queueController.ts +10 -2
- package/src/bot/logic/common/utils.ts +19 -0
- package/src/bot/logic/mission/missions/attackMission.ts +72 -31
- package/src/bot/logic/squad/behaviours/actionBatcher.ts +65 -0
- package/src/bot/logic/squad/behaviours/combatSquad.ts +13 -3
- package/src/bot/logic/squad/behaviours/common.ts +9 -9
- package/src/bot/logic/squad/behaviours/engineerSquad.ts +9 -4
- package/src/bot/logic/squad/behaviours/expansionSquad.ts +9 -4
- package/src/bot/logic/squad/behaviours/retreatSquad.ts +6 -0
- package/src/bot/logic/squad/behaviours/scoutingSquad.ts +6 -0
- package/src/bot/logic/squad/squad.ts +7 -1
- package/src/bot/logic/squad/squadBehaviour.ts +4 -0
- package/src/bot/logic/squad/squadController.ts +19 -2
- 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
|
-
|
|
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 = "
|
|
19
|
+
const mapName = "mp03t4.map";
|
|
20
20
|
// Bot names must be unique in online mode
|
|
21
|
-
const
|
|
22
|
-
const
|
|
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(
|
|
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: [
|
|
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({
|