@supalosa/chronodivide-bot 0.2.2-b → 0.3.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/TODO.md +0 -2
- package/dist/bot/bot.js +2 -2
- package/dist/bot/bot.js.map +1 -1
- package/dist/bot/logic/awareness.js +7 -6
- package/dist/bot/logic/awareness.js.map +1 -1
- package/dist/bot/logic/building/ArtilleryUnit.js +6 -5
- package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
- package/dist/bot/logic/building/artilleryUnit.js.map +1 -1
- package/dist/bot/logic/building/basicAirUnit.js +2 -1
- package/dist/bot/logic/building/basicAirUnit.js.map +1 -1
- package/dist/bot/logic/building/basicGroundUnit.js +3 -2
- package/dist/bot/logic/building/basicGroundUnit.js.map +1 -1
- package/dist/bot/logic/building/buildingRules.js +4 -10
- package/dist/bot/logic/building/buildingRules.js.map +1 -1
- package/dist/bot/logic/building/harvester.js.map +1 -1
- package/dist/bot/logic/building/resourceCollectionBuilding.js +5 -3
- package/dist/bot/logic/building/resourceCollectionBuilding.js.map +1 -1
- package/dist/bot/logic/common/scout.js +49 -32
- package/dist/bot/logic/common/scout.js.map +1 -1
- package/dist/bot/logic/map/map.js +17 -19
- package/dist/bot/logic/map/map.js.map +1 -1
- package/dist/bot/logic/map/sector.js +10 -13
- package/dist/bot/logic/map/sector.js.map +1 -1
- package/dist/bot/logic/mission/missions/attackMission.js +2 -2
- package/dist/bot/logic/mission/missions/attackMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/defenceMission.js +2 -1
- package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/combatSquad.js +3 -3
- package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/common.js +2 -2
- package/dist/bot/logic/squad/behaviours/common.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +21 -17
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -1
- package/dist/bot/logic/squad/squad.js +4 -6
- 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 +35 -23
- package/dist/bot/logic/squad/squadController.js.map +1 -1
- package/dist/bot/logic/threat/threatCalculator.js +4 -3
- package/dist/bot/logic/threat/threatCalculator.js.map +1 -1
- package/dist/exampleBot.js +1 -1
- package/package.json +3 -3
- package/src/bot/bot.ts +4 -5
- package/src/bot/logic/awareness.ts +12 -12
- package/src/bot/logic/building/antiGroundStaticDefence.ts +11 -7
- package/src/bot/logic/building/artilleryUnit.ts +14 -17
- package/src/bot/logic/building/basicAirUnit.ts +9 -7
- package/src/bot/logic/building/basicGroundUnit.ts +3 -3
- package/src/bot/logic/building/buildingRules.ts +11 -13
- package/src/bot/logic/building/harvester.ts +7 -4
- package/src/bot/logic/building/resourceCollectionBuilding.ts +8 -12
- package/src/bot/logic/common/scout.ts +83 -38
- package/src/bot/logic/map/map.ts +26 -30
- package/src/bot/logic/map/sector.ts +17 -21
- package/src/bot/logic/mission/missions/attackMission.ts +5 -5
- package/src/bot/logic/mission/missions/defenceMission.ts +3 -3
- package/src/bot/logic/mission/missions/retreatMission.ts +2 -2
- package/src/bot/logic/squad/behaviours/combatSquad.ts +6 -6
- package/src/bot/logic/squad/behaviours/common.ts +3 -3
- package/src/bot/logic/squad/behaviours/retreatSquad.ts +2 -2
- package/src/bot/logic/squad/behaviours/scoutingSquad.ts +25 -22
- package/src/bot/logic/squad/squad.ts +6 -10
- package/src/bot/logic/squad/squadBehaviour.ts +9 -10
- package/src/bot/logic/squad/squadController.ts +0 -1
- package/src/bot/logic/threat/threatCalculator.ts +100 -99
- package/src/exampleBot.ts +1 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { ActionsApi, GameApi, PlayerData,
|
|
1
|
+
import { ActionsApi, GameApi, PlayerData, TechnoRules, Tile, UnitData, Vector2 } from "@chronodivide/game-api";
|
|
2
2
|
import { Mission } from "../mission/mission.js";
|
|
3
|
-
import { GlobalThreat } from "../threat/threat.js";
|
|
4
3
|
import { SquadAction, SquadBehaviour, disband } from "./squadBehaviour.js";
|
|
5
4
|
import { MatchAwareness } from "../awareness.js";
|
|
6
|
-
import {
|
|
5
|
+
import { getDistanceBetweenTileAndPoint } from "../map/map.js";
|
|
7
6
|
import { DebugLogger } from "../common/utils.js";
|
|
8
7
|
|
|
9
8
|
export enum SquadLiveness {
|
|
@@ -18,7 +17,7 @@ export type SquadConstructionRequest = {
|
|
|
18
17
|
};
|
|
19
18
|
|
|
20
19
|
const calculateCenterOfMass: (unitTiles: Tile[]) => {
|
|
21
|
-
centerOfMass:
|
|
20
|
+
centerOfMass: Vector2;
|
|
22
21
|
maxDistance: number;
|
|
23
22
|
} | null = (unitTiles) => {
|
|
24
23
|
if (unitTiles.length === 0) {
|
|
@@ -34,13 +33,10 @@ const calculateCenterOfMass: (unitTiles: Tile[]) => {
|
|
|
34
33
|
},
|
|
35
34
|
{ x: 0, y: 0 },
|
|
36
35
|
);
|
|
37
|
-
const centerOfMass =
|
|
38
|
-
x: Math.round(sums.x / unitTiles.length),
|
|
39
|
-
y: Math.round(sums.y / unitTiles.length),
|
|
40
|
-
};
|
|
36
|
+
const centerOfMass = new Vector2(Math.round(sums.x / unitTiles.length), Math.round(sums.y / unitTiles.length));
|
|
41
37
|
|
|
42
38
|
// max distance of units to the center of mass
|
|
43
|
-
const distances = unitTiles.map((tile) =>
|
|
39
|
+
const distances = unitTiles.map((tile) => getDistanceBetweenTileAndPoint(tile, centerOfMass));
|
|
44
40
|
const maxDistance = Math.max(...distances);
|
|
45
41
|
return { centerOfMass, maxDistance };
|
|
46
42
|
};
|
|
@@ -49,7 +45,7 @@ export class Squad {
|
|
|
49
45
|
private unitIds: number[] = [];
|
|
50
46
|
private liveness: SquadLiveness = SquadLiveness.SquadActive;
|
|
51
47
|
private lastLivenessUpdateTick: number = 0;
|
|
52
|
-
private centerOfMass:
|
|
48
|
+
private centerOfMass: Vector2 | null = null;
|
|
53
49
|
private maxDistanceToCenterOfMass: number | null = null;
|
|
54
50
|
|
|
55
51
|
constructor(
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { ActionsApi, GameApi, PlayerData,
|
|
2
|
-
import { GlobalThreat } from "../threat/threat.js";
|
|
1
|
+
import { ActionsApi, GameApi, PlayerData, Vector2 } from "@chronodivide/game-api";
|
|
3
2
|
import { Squad } from "./squad.js";
|
|
4
3
|
import { MatchAwareness } from "../awareness.js";
|
|
5
4
|
import { DebugLogger } from "../common/utils.js";
|
|
@@ -26,22 +25,22 @@ export type SquadActionRequestSpecificUnits = {
|
|
|
26
25
|
};
|
|
27
26
|
export type SquadActionGrabFreeCombatants = {
|
|
28
27
|
type: "requestCombatants";
|
|
29
|
-
point:
|
|
28
|
+
point: Vector2;
|
|
30
29
|
radius: number;
|
|
31
30
|
};
|
|
32
31
|
|
|
33
|
-
export const noop = () => ({ type: "noop" } as SquadActionNoop
|
|
32
|
+
export const noop = () => ({ type: "noop" }) as SquadActionNoop;
|
|
34
33
|
|
|
35
|
-
export const disband = () => ({ type: "disband" } as SquadActionDisband
|
|
34
|
+
export const disband = () => ({ type: "disband" }) as SquadActionDisband;
|
|
36
35
|
|
|
37
36
|
export const requestUnits = (unitNames: string[], priority: number) =>
|
|
38
|
-
({ type: "request", unitNames, priority } as SquadActionRequestUnits
|
|
37
|
+
({ type: "request", unitNames, priority }) as SquadActionRequestUnits;
|
|
39
38
|
|
|
40
39
|
export const requestSpecificUnits = (unitIds: number[], priority: number) =>
|
|
41
|
-
({ type: "requestSpecific", unitIds, priority } as SquadActionRequestSpecificUnits
|
|
40
|
+
({ type: "requestSpecific", unitIds, priority }) as SquadActionRequestSpecificUnits;
|
|
42
41
|
|
|
43
|
-
export const grabCombatants = (point:
|
|
44
|
-
({ type: "requestCombatants", point, radius } as SquadActionGrabFreeCombatants
|
|
42
|
+
export const grabCombatants = (point: Vector2, radius: number) =>
|
|
43
|
+
({ type: "requestCombatants", point, radius }) as SquadActionGrabFreeCombatants;
|
|
45
44
|
|
|
46
45
|
export type SquadAction =
|
|
47
46
|
| SquadActionNoop
|
|
@@ -58,6 +57,6 @@ export interface SquadBehaviour {
|
|
|
58
57
|
playerData: PlayerData,
|
|
59
58
|
squad: Squad,
|
|
60
59
|
matchAwareness: MatchAwareness,
|
|
61
|
-
logger: DebugLogger
|
|
60
|
+
logger: DebugLogger,
|
|
62
61
|
): SquadAction;
|
|
63
62
|
}
|
|
@@ -179,7 +179,6 @@ export class SquadController {
|
|
|
179
179
|
.map((unit) => unit!);
|
|
180
180
|
|
|
181
181
|
type AssignmentWithType = { unitName: string; squad: string; method: "type" | "grab" };
|
|
182
|
-
// [squadName][unitName]['type' | 'grab']
|
|
183
182
|
const newAssignmentsByType = freeUnits
|
|
184
183
|
.flatMap((freeUnit) => {
|
|
185
184
|
if (unitTypeToHighestRequest.hasOwnProperty(freeUnit.name)) {
|
|
@@ -1,99 +1,100 @@
|
|
|
1
|
-
import { GameApi, MovementZone, ObjectType, PlayerData, UnitData } from "@chronodivide/game-api";
|
|
2
|
-
import { GlobalThreat } from "./threat.js";
|
|
3
|
-
|
|
4
|
-
export function calculateGlobalThreat(game: GameApi, playerData: PlayerData, visibleAreaPercent: number): GlobalThreat {
|
|
5
|
-
let groundUnits = game.getVisibleUnits(
|
|
6
|
-
playerData.name,
|
|
7
|
-
"hostile",
|
|
8
|
-
(r) => r.type == ObjectType.Vehicle || r.type == ObjectType.Infantry,
|
|
9
|
-
);
|
|
10
|
-
let airUnits = game.getVisibleUnits(playerData.name, "hostile", (r) => r.movementZone == MovementZone.Fly);
|
|
11
|
-
let groundDefence = game
|
|
12
|
-
.getVisibleUnits(playerData.name, "hostile", (r) => r.type == ObjectType.Building)
|
|
13
|
-
.filter((unitId) => isAntiGround(game, unitId));
|
|
14
|
-
let antiAirPower = game
|
|
15
|
-
.getVisibleUnits(playerData.name, "hostile", (r) => r.type != ObjectType.Building)
|
|
16
|
-
.filter((unitId) => isAntiAir(game, unitId));
|
|
17
|
-
|
|
18
|
-
let ourAntiGroundUnits = game
|
|
19
|
-
.getVisibleUnits(playerData.name, "self", (r) => r.isSelectableCombatant)
|
|
20
|
-
.filter((unitId) => isAntiGround(game, unitId));
|
|
21
|
-
let ourAntiAirUnits = game
|
|
22
|
-
.getVisibleUnits(playerData.name, "self", (r) => r.isSelectableCombatant)
|
|
23
|
-
.filter((unitId) => isAntiAir(game, unitId));
|
|
24
|
-
let ourGroundDefence = game
|
|
25
|
-
.getVisibleUnits(playerData.name, "self", (r) => r.type == ObjectType.Building)
|
|
26
|
-
.filter((unitId) => isAntiGround(game, unitId));
|
|
27
|
-
let ourAirUnits = game.getVisibleUnits(
|
|
28
|
-
playerData.name,
|
|
29
|
-
"self",
|
|
30
|
-
(r) => r.movementZone == MovementZone.Fly && r.isSelectableCombatant,
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
let observedGroundThreat = calculateFirepowerForUnits(game, groundUnits);
|
|
34
|
-
let observedAirThreat = calculateFirepowerForUnits(game, airUnits);
|
|
35
|
-
let observedAntiAirThreat = calculateFirepowerForUnits(game, antiAirPower);
|
|
36
|
-
let observedGroundDefence = calculateFirepowerForUnits(game, groundDefence);
|
|
37
|
-
|
|
38
|
-
let ourAntiGroundPower = calculateFirepowerForUnits(game, ourAntiGroundUnits);
|
|
39
|
-
let ourAntiAirPower = calculateFirepowerForUnits(game, ourAntiAirUnits);
|
|
40
|
-
let ourAirPower = calculateFirepowerForUnits(game, ourAirUnits);
|
|
41
|
-
let ourGroundDefencePower = calculateFirepowerForUnits(game, ourGroundDefence);
|
|
42
|
-
|
|
43
|
-
return new GlobalThreat(
|
|
44
|
-
visibleAreaPercent,
|
|
45
|
-
observedGroundThreat,
|
|
46
|
-
observedAirThreat,
|
|
47
|
-
observedAntiAirThreat,
|
|
48
|
-
observedGroundDefence * 0.25,
|
|
49
|
-
ourGroundDefencePower * 0.25,
|
|
50
|
-
ourAntiGroundPower,
|
|
51
|
-
ourAntiAirPower,
|
|
52
|
-
ourAirPower,
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function isAntiGround(gameApi: GameApi, unitId: number): boolean {
|
|
57
|
-
let unit = gameApi.getUnitData(unitId);
|
|
58
|
-
if (unit && unit.primaryWeapon) {
|
|
59
|
-
return unit.primaryWeapon.projectileRules.isAntiGround;
|
|
60
|
-
}
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function isAntiAir(gameApi: GameApi, unitId: number): boolean {
|
|
65
|
-
let unit = gameApi.getUnitData(unitId);
|
|
66
|
-
if (unit && unit.primaryWeapon) {
|
|
67
|
-
return unit.primaryWeapon.projectileRules.isAntiAir;
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function calculateFirepowerForUnit(unitData: UnitData): number {
|
|
73
|
-
let threat = 0;
|
|
74
|
-
let hpRatio = unitData.hitPoints / Math.max(1, unitData.maxHitPoints);
|
|
75
|
-
if (unitData.primaryWeapon) {
|
|
76
|
-
threat +=
|
|
77
|
-
(hpRatio *
|
|
78
|
-
((unitData.primaryWeapon.rules.damage + 1) *
|
|
79
|
-
Math.max(unitData.primaryWeapon.cooldownTicks, 1);
|
|
80
|
-
}
|
|
81
|
-
if (unitData.secondaryWeapon) {
|
|
82
|
-
threat +=
|
|
83
|
-
(hpRatio *
|
|
84
|
-
((unitData.secondaryWeapon.rules.damage + 1) *
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
1
|
+
import { GameApi, GameMath, MovementZone, ObjectType, PlayerData, UnitData } from "@chronodivide/game-api";
|
|
2
|
+
import { GlobalThreat } from "./threat.js";
|
|
3
|
+
|
|
4
|
+
export function calculateGlobalThreat(game: GameApi, playerData: PlayerData, visibleAreaPercent: number): GlobalThreat {
|
|
5
|
+
let groundUnits = game.getVisibleUnits(
|
|
6
|
+
playerData.name,
|
|
7
|
+
"hostile",
|
|
8
|
+
(r) => r.type == ObjectType.Vehicle || r.type == ObjectType.Infantry,
|
|
9
|
+
);
|
|
10
|
+
let airUnits = game.getVisibleUnits(playerData.name, "hostile", (r) => r.movementZone == MovementZone.Fly);
|
|
11
|
+
let groundDefence = game
|
|
12
|
+
.getVisibleUnits(playerData.name, "hostile", (r) => r.type == ObjectType.Building)
|
|
13
|
+
.filter((unitId) => isAntiGround(game, unitId));
|
|
14
|
+
let antiAirPower = game
|
|
15
|
+
.getVisibleUnits(playerData.name, "hostile", (r) => r.type != ObjectType.Building)
|
|
16
|
+
.filter((unitId) => isAntiAir(game, unitId));
|
|
17
|
+
|
|
18
|
+
let ourAntiGroundUnits = game
|
|
19
|
+
.getVisibleUnits(playerData.name, "self", (r) => r.isSelectableCombatant)
|
|
20
|
+
.filter((unitId) => isAntiGround(game, unitId));
|
|
21
|
+
let ourAntiAirUnits = game
|
|
22
|
+
.getVisibleUnits(playerData.name, "self", (r) => r.isSelectableCombatant)
|
|
23
|
+
.filter((unitId) => isAntiAir(game, unitId));
|
|
24
|
+
let ourGroundDefence = game
|
|
25
|
+
.getVisibleUnits(playerData.name, "self", (r) => r.type == ObjectType.Building)
|
|
26
|
+
.filter((unitId) => isAntiGround(game, unitId));
|
|
27
|
+
let ourAirUnits = game.getVisibleUnits(
|
|
28
|
+
playerData.name,
|
|
29
|
+
"self",
|
|
30
|
+
(r) => r.movementZone == MovementZone.Fly && r.isSelectableCombatant,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
let observedGroundThreat = calculateFirepowerForUnits(game, groundUnits);
|
|
34
|
+
let observedAirThreat = calculateFirepowerForUnits(game, airUnits);
|
|
35
|
+
let observedAntiAirThreat = calculateFirepowerForUnits(game, antiAirPower);
|
|
36
|
+
let observedGroundDefence = calculateFirepowerForUnits(game, groundDefence);
|
|
37
|
+
|
|
38
|
+
let ourAntiGroundPower = calculateFirepowerForUnits(game, ourAntiGroundUnits);
|
|
39
|
+
let ourAntiAirPower = calculateFirepowerForUnits(game, ourAntiAirUnits);
|
|
40
|
+
let ourAirPower = calculateFirepowerForUnits(game, ourAirUnits);
|
|
41
|
+
let ourGroundDefencePower = calculateFirepowerForUnits(game, ourGroundDefence);
|
|
42
|
+
|
|
43
|
+
return new GlobalThreat(
|
|
44
|
+
visibleAreaPercent,
|
|
45
|
+
observedGroundThreat,
|
|
46
|
+
observedAirThreat,
|
|
47
|
+
observedAntiAirThreat,
|
|
48
|
+
observedGroundDefence * 0.25,
|
|
49
|
+
ourGroundDefencePower * 0.25,
|
|
50
|
+
ourAntiGroundPower,
|
|
51
|
+
ourAntiAirPower,
|
|
52
|
+
ourAirPower,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isAntiGround(gameApi: GameApi, unitId: number): boolean {
|
|
57
|
+
let unit = gameApi.getUnitData(unitId);
|
|
58
|
+
if (unit && unit.primaryWeapon) {
|
|
59
|
+
return unit.primaryWeapon.projectileRules.isAntiGround;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isAntiAir(gameApi: GameApi, unitId: number): boolean {
|
|
65
|
+
let unit = gameApi.getUnitData(unitId);
|
|
66
|
+
if (unit && unit.primaryWeapon) {
|
|
67
|
+
return unit.primaryWeapon.projectileRules.isAntiAir;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function calculateFirepowerForUnit(unitData: UnitData): number {
|
|
73
|
+
let threat = 0;
|
|
74
|
+
let hpRatio = unitData.hitPoints / Math.max(1, unitData.maxHitPoints);
|
|
75
|
+
if (unitData.primaryWeapon) {
|
|
76
|
+
threat +=
|
|
77
|
+
(hpRatio *
|
|
78
|
+
((unitData.primaryWeapon.rules.damage + 1) * GameMath.sqrt(unitData.primaryWeapon.rules.range + 1))) /
|
|
79
|
+
Math.max(unitData.primaryWeapon.cooldownTicks, 1);
|
|
80
|
+
}
|
|
81
|
+
if (unitData.secondaryWeapon) {
|
|
82
|
+
threat +=
|
|
83
|
+
(hpRatio *
|
|
84
|
+
((unitData.secondaryWeapon.rules.damage + 1) *
|
|
85
|
+
GameMath.sqrt(unitData.secondaryWeapon.rules.range + 1))) /
|
|
86
|
+
Math.max(unitData.secondaryWeapon.cooldownTicks, 1);
|
|
87
|
+
}
|
|
88
|
+
return Math.min(800, threat);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function calculateFirepowerForUnits(game: GameApi, unitIds: number[]) {
|
|
92
|
+
let threat = 0;
|
|
93
|
+
unitIds.forEach((unitId) => {
|
|
94
|
+
let unitData = game.getUnitData(unitId);
|
|
95
|
+
if (unitData) {
|
|
96
|
+
threat += calculateFirepowerForUnit(unitData);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return threat;
|
|
100
|
+
}
|
package/src/exampleBot.ts
CHANGED
|
@@ -16,7 +16,7 @@ async function main() {
|
|
|
16
16
|
Other maps:
|
|
17
17
|
mp03t4 large map, no oil derricks
|
|
18
18
|
*/
|
|
19
|
-
const mapName = "
|
|
19
|
+
const mapName = "mp10s4.map";
|
|
20
20
|
// Bot names must be unique in online mode
|
|
21
21
|
const botName = `Joe${String(Date.now()).substr(-6)}`;
|
|
22
22
|
const otherBotName = `Bob${String(Date.now() + 1).substr(-6)}`;
|