@supalosa/chronodivide-bot 0.3.1 → 0.5.1
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/.env.template +5 -0
- package/README.md +57 -39
- package/dist/bot/bot.js +27 -37
- package/dist/bot/bot.js.map +1 -1
- package/dist/bot/logic/awareness.js +13 -8
- package/dist/bot/logic/awareness.js.map +1 -1
- package/dist/bot/logic/awarenessImpl.js +132 -0
- package/dist/bot/logic/awarenessImpl.js.map +1 -0
- package/dist/bot/logic/building/ArtilleryUnit.js +2 -29
- package/dist/bot/logic/building/ArtilleryUnit.js.map +1 -0
- package/dist/bot/logic/building/antiAirStaticDefence.js +43 -0
- package/dist/bot/logic/building/antiAirStaticDefence.js.map +1 -0
- package/dist/bot/logic/building/antiGroundStaticDefence.js +8 -5
- package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
- package/dist/bot/logic/building/basicAirUnit.js +2 -23
- package/dist/bot/logic/building/basicAirUnit.js.map +1 -1
- package/dist/bot/logic/building/basicBuilding.js +3 -2
- package/dist/bot/logic/building/basicBuilding.js.map +1 -1
- package/dist/bot/logic/building/basicGroundUnit.js +2 -43
- package/dist/bot/logic/building/basicGroundUnit.js.map +1 -1
- package/dist/bot/logic/building/building.js +55 -11
- package/dist/bot/logic/building/building.js.map +1 -0
- package/dist/bot/logic/building/buildingRules.js +62 -50
- package/dist/bot/logic/building/buildingRules.js.map +1 -1
- package/dist/bot/logic/building/common.js +19 -0
- package/dist/bot/logic/building/common.js.map +1 -0
- package/dist/bot/logic/building/harvester.js +2 -1
- package/dist/bot/logic/building/harvester.js.map +1 -1
- package/dist/bot/logic/building/queueController.js +73 -41
- package/dist/bot/logic/building/queueController.js.map +1 -1
- package/dist/bot/logic/common/utils.js +35 -0
- package/dist/bot/logic/common/utils.js.map +1 -1
- package/dist/bot/logic/composition/alliedCompositions.js +13 -0
- package/dist/bot/logic/composition/alliedCompositions.js.map +1 -0
- package/dist/bot/logic/composition/common.js +2 -0
- package/dist/bot/logic/composition/common.js.map +1 -0
- package/dist/bot/logic/composition/sovietCompositions.js +13 -0
- package/dist/bot/logic/composition/sovietCompositions.js.map +1 -0
- package/dist/bot/logic/mission/actionBatcher.js +92 -0
- package/dist/bot/logic/mission/actionBatcher.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/combatSquad.js +124 -0
- package/dist/bot/logic/mission/behaviours/combatSquad.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/common.js +58 -0
- package/dist/bot/logic/mission/behaviours/common.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/engineerSquad.js +39 -0
- package/dist/bot/logic/mission/behaviours/engineerSquad.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/expansionSquad.js +46 -0
- package/dist/bot/logic/mission/behaviours/expansionSquad.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/retreatSquad.js +31 -0
- package/dist/bot/logic/mission/behaviours/retreatSquad.js.map +1 -0
- package/{src/bot/logic/squad/behaviours/scoutingSquad.ts → dist/bot/logic/mission/behaviours/scoutingSquad.js} +29 -47
- package/dist/bot/logic/mission/behaviours/scoutingSquad.js.map +1 -0
- package/dist/bot/logic/mission/mission.js +91 -19
- package/dist/bot/logic/mission/mission.js.map +1 -1
- package/dist/bot/logic/mission/missionController.js +262 -21
- package/dist/bot/logic/mission/missionController.js.map +1 -1
- package/dist/bot/logic/mission/missions/attackMission.js +159 -52
- package/dist/bot/logic/mission/missions/attackMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/basicMission.js +13 -0
- package/dist/bot/logic/mission/missions/basicMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/defenceMission.js +43 -28
- package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/engineerMission.js +37 -7
- package/dist/bot/logic/mission/missions/engineerMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/expansionMission.js +42 -6
- package/dist/bot/logic/mission/missions/expansionMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/missionBehaviour.js +2 -0
- package/dist/bot/logic/mission/missions/missionBehaviour.js.map +1 -0
- package/dist/bot/logic/mission/missions/retreatMission.js +31 -5
- package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/scoutingMission.js +103 -6
- package/dist/bot/logic/mission/missions/scoutingMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/squads/combatSquad.js +116 -0
- package/dist/bot/logic/mission/missions/squads/combatSquad.js.map +1 -0
- package/dist/bot/logic/mission/missions/squads/common.js +58 -0
- package/dist/bot/logic/mission/missions/squads/common.js.map +1 -0
- package/dist/bot/logic/mission/missions/squads/squad.js +2 -0
- package/dist/bot/logic/mission/missions/squads/squad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/attackSquad.js +63 -56
- package/dist/bot/logic/squad/behaviours/combatSquad.js +19 -18
- package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/common.js +2 -19
- package/dist/bot/logic/squad/behaviours/common.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/defenceSquad.js +15 -2
- package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +17 -21
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -1
- package/dist/bot/logic/squad/squad.js +8 -5
- 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 +3 -2
- package/dist/bot/logic/squad/squadController.js.map +1 -1
- package/dist/bot/logic/threat/threatCalculator.js +5 -5
- package/dist/bot/logic/threat/threatCalculator.js.map +1 -1
- package/dist/exampleBot.js +53 -16
- package/dist/exampleBot.js.map +1 -1
- package/package.json +5 -4
- package/src/bot/bot.ts +38 -53
- package/src/bot/logic/awareness.ts +34 -22
- package/src/bot/logic/building/antiAirStaticDefence.ts +64 -0
- package/src/bot/logic/building/antiGroundStaticDefence.ts +7 -20
- package/src/bot/logic/building/artilleryUnit.ts +2 -28
- package/src/bot/logic/building/basicAirUnit.ts +2 -33
- package/src/bot/logic/building/basicBuilding.ts +8 -6
- package/src/bot/logic/building/basicGroundUnit.ts +2 -46
- package/src/bot/logic/building/buildingRules.ts +73 -57
- package/src/bot/logic/building/common.ts +23 -0
- package/src/bot/logic/building/harvester.ts +2 -1
- package/src/bot/logic/building/queueController.ts +105 -42
- package/src/bot/logic/common/utils.ts +47 -0
- package/src/bot/logic/composition/alliedCompositions.ts +22 -0
- package/src/bot/logic/composition/common.ts +3 -0
- package/src/bot/logic/composition/sovietCompositions.ts +21 -0
- package/src/bot/logic/mission/actionBatcher.ts +124 -0
- package/src/bot/logic/mission/mission.ts +186 -37
- package/src/bot/logic/mission/missionController.ts +340 -31
- package/src/bot/logic/mission/missionFactories.ts +3 -3
- package/src/bot/logic/mission/missions/attackMission.ts +234 -56
- package/src/bot/logic/mission/missions/defenceMission.ts +72 -45
- package/src/bot/logic/mission/missions/engineerMission.ts +67 -15
- package/src/bot/logic/mission/missions/expansionMission.ts +67 -14
- package/src/bot/logic/mission/missions/retreatMission.ts +50 -6
- package/src/bot/logic/mission/missions/scoutingMission.ts +138 -14
- package/src/bot/logic/mission/missions/squads/combatSquad.ts +160 -0
- package/src/bot/logic/{squad/behaviours → mission/missions/squads}/common.ts +14 -20
- package/src/bot/logic/mission/missions/squads/squad.ts +19 -0
- package/src/bot/logic/threat/threat.ts +15 -15
- package/src/bot/logic/threat/threatCalculator.ts +10 -10
- package/src/exampleBot.ts +59 -19
- package/.prettierrc +0 -5
- package/TODO.md +0 -18
- package/dist/bot/logic/building/artilleryUnit.js.map +0 -1
- package/dist/bot/logic/building/massedAntiGroundUnit.js +0 -20
- package/dist/bot/logic/building/queues.js +0 -19
- package/dist/bot/logic/knowledge.js +0 -1
- package/dist/bot/logic/mission/basicMission.js +0 -26
- package/dist/bot/logic/mission/expansionMission.js +0 -32
- package/dist/bot/logic/squad/behaviours/squadExpansion.js +0 -31
- package/dist/bot/logic/squad/behaviours/squadScouters.js +0 -8
- package/rules.ini +0 -23126
- package/src/bot/logic/mission/missions/oneTimeMission.ts +0 -33
- package/src/bot/logic/squad/behaviours/combatSquad.ts +0 -127
- package/src/bot/logic/squad/behaviours/engineerSquad.ts +0 -53
- package/src/bot/logic/squad/behaviours/expansionSquad.ts +0 -59
- package/src/bot/logic/squad/behaviours/retreatSquad.ts +0 -44
- package/src/bot/logic/squad/squad.ts +0 -159
- package/src/bot/logic/squad/squadBehaviour.ts +0 -62
- package/src/bot/logic/squad/squadBehaviours.ts +0 -8
- package/src/bot/logic/squad/squadController.ts +0 -254
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { GameApi, PlayerData, TechnoRules, Vector2 } from "@chronodivide/game-api";
|
|
2
|
+
import { getPointTowardsOtherPoint } from "../map/map.js";
|
|
3
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
4
|
+
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
|
|
5
|
+
|
|
6
|
+
export class AntiAirStaticDefence implements AiBuildingRules {
|
|
7
|
+
constructor(
|
|
8
|
+
private basePriority: number,
|
|
9
|
+
private baseAmount: number,
|
|
10
|
+
private airStrength: number,
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
getPlacementLocation(
|
|
14
|
+
game: GameApi,
|
|
15
|
+
playerData: PlayerData,
|
|
16
|
+
technoRules: TechnoRules,
|
|
17
|
+
): { rx: number; ry: number } | undefined {
|
|
18
|
+
// Prefer front towards enemy.
|
|
19
|
+
let startLocation = playerData.startLocation;
|
|
20
|
+
let players = game.getPlayers();
|
|
21
|
+
let enemyFacingLocationCandidates: Vector2[] = [];
|
|
22
|
+
for (let i = 0; i < players.length; ++i) {
|
|
23
|
+
let playerName = players[i];
|
|
24
|
+
if (playerName == playerData.name) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
let enemyPlayer = game.getPlayerData(playerName);
|
|
28
|
+
enemyFacingLocationCandidates.push(
|
|
29
|
+
getPointTowardsOtherPoint(game, startLocation, enemyPlayer.startLocation, 4, 16, 1.5),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
let selectedLocation =
|
|
33
|
+
enemyFacingLocationCandidates[Math.floor(game.generateRandom() * enemyFacingLocationCandidates.length)];
|
|
34
|
+
return getDefaultPlacementLocation(game, playerData, selectedLocation, technoRules, false, 0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getPriority(
|
|
38
|
+
game: GameApi,
|
|
39
|
+
playerData: PlayerData,
|
|
40
|
+
technoRules: TechnoRules,
|
|
41
|
+
threatCache: GlobalThreat | null,
|
|
42
|
+
): number {
|
|
43
|
+
if (threatCache) {
|
|
44
|
+
let denominator = threatCache.totalAvailableAntiAirFirepower + this.airStrength;
|
|
45
|
+
if (threatCache.totalOffensiveAirThreat > denominator * 1.1) {
|
|
46
|
+
return this.basePriority * (threatCache.totalOffensiveAirThreat / Math.max(1, denominator));
|
|
47
|
+
} else {
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const strengthPerCost = (this.airStrength / technoRules.cost) * 1000;
|
|
52
|
+
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
53
|
+
return this.basePriority * (1.0 - numOwned / this.baseAmount) * strengthPerCost;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getMaxCount(
|
|
57
|
+
game: GameApi,
|
|
58
|
+
playerData: PlayerData,
|
|
59
|
+
technoRules: TechnoRules,
|
|
60
|
+
threatCache: GlobalThreat | null,
|
|
61
|
+
): number | null {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -2,12 +2,13 @@ import { GameApi, PlayerData, TechnoRules, Vector2 } from "@chronodivide/game-ap
|
|
|
2
2
|
import { getPointTowardsOtherPoint } from "../map/map.js";
|
|
3
3
|
import { GlobalThreat } from "../threat/threat.js";
|
|
4
4
|
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
|
|
5
|
+
import { getStaticDefencePlacement } from "./common.js";
|
|
5
6
|
|
|
6
7
|
export class AntiGroundStaticDefence implements AiBuildingRules {
|
|
7
8
|
constructor(
|
|
8
9
|
private basePriority: number,
|
|
9
10
|
private baseAmount: number,
|
|
10
|
-
private
|
|
11
|
+
private groundStrength: number,
|
|
11
12
|
) {}
|
|
12
13
|
|
|
13
14
|
getPlacementLocation(
|
|
@@ -15,23 +16,7 @@ export class AntiGroundStaticDefence implements AiBuildingRules {
|
|
|
15
16
|
playerData: PlayerData,
|
|
16
17
|
technoRules: TechnoRules,
|
|
17
18
|
): { rx: number; ry: number } | undefined {
|
|
18
|
-
|
|
19
|
-
let startLocation = playerData.startLocation;
|
|
20
|
-
let players = game.getPlayers();
|
|
21
|
-
let enemyFacingLocationCandidates: Vector2[] = [];
|
|
22
|
-
for (let i = 0; i < players.length; ++i) {
|
|
23
|
-
let playerName = players[i];
|
|
24
|
-
if (playerName == playerData.name) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
let enemyPlayer = game.getPlayerData(playerName);
|
|
28
|
-
enemyFacingLocationCandidates.push(
|
|
29
|
-
getPointTowardsOtherPoint(game, startLocation, enemyPlayer.startLocation, 4, 16, 1.5),
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
let selectedLocation =
|
|
33
|
-
enemyFacingLocationCandidates[Math.floor(game.generateRandom() * enemyFacingLocationCandidates.length)];
|
|
34
|
-
return getDefaultPlacementLocation(game, playerData, selectedLocation, technoRules, 0);
|
|
19
|
+
return getStaticDefencePlacement(game, playerData, technoRules);
|
|
35
20
|
}
|
|
36
21
|
|
|
37
22
|
getPriority(
|
|
@@ -43,12 +28,14 @@ export class AntiGroundStaticDefence implements AiBuildingRules {
|
|
|
43
28
|
// If the enemy's ground power is increasing we should try to keep up.
|
|
44
29
|
if (threatCache) {
|
|
45
30
|
let denominator =
|
|
46
|
-
threatCache.totalAvailableAntiGroundFirepower + threatCache.totalDefensivePower + this.
|
|
31
|
+
threatCache.totalAvailableAntiGroundFirepower + threatCache.totalDefensivePower + this.groundStrength;
|
|
47
32
|
if (threatCache.totalOffensiveLandThreat > denominator * 1.1) {
|
|
48
33
|
return this.basePriority * (threatCache.totalOffensiveLandThreat / Math.max(1, denominator));
|
|
34
|
+
} else {
|
|
35
|
+
return 0;
|
|
49
36
|
}
|
|
50
37
|
}
|
|
51
|
-
const strengthPerCost = (this.
|
|
38
|
+
const strengthPerCost = (this.groundStrength / technoRules.cost) * 1000;
|
|
52
39
|
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
53
40
|
return this.basePriority * (1.0 - numOwned / this.baseAmount) * strengthPerCost;
|
|
54
41
|
}
|
|
@@ -24,34 +24,8 @@ export class ArtilleryUnit implements AiBuildingRules {
|
|
|
24
24
|
technoRules: TechnoRules,
|
|
25
25
|
threatCache: GlobalThreat | null,
|
|
26
26
|
): number {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// If the enemy's defensive power is increasing we will start to build these.
|
|
30
|
-
if (threatCache && threatCache.totalDefensivePower > threatCache.totalAvailableAntiGroundFirepower) {
|
|
31
|
-
priority +=
|
|
32
|
-
this.artilleryPower *
|
|
33
|
-
(threatCache.totalAvailableAntiGroundFirepower / Math.max(1, threatCache.totalDefensivePower));
|
|
34
|
-
}
|
|
35
|
-
if (threatCache && this.antiGroundPower > 0) {
|
|
36
|
-
// If the enemy's power is increasing we should try to keep up.
|
|
37
|
-
if (threatCache.totalOffensiveLandThreat > threatCache.totalAvailableAntiGroundFirepower) {
|
|
38
|
-
priority +=
|
|
39
|
-
this.antiGroundPower *
|
|
40
|
-
this.basePriority *
|
|
41
|
-
(threatCache.totalOffensiveLandThreat / 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
|
-
GameMath.sqrt(
|
|
48
|
-
threatCache.totalAvailableAntiGroundFirepower /
|
|
49
|
-
Math.max(1, threatCache.totalOffensiveLandThreat + threatCache.totalDefensiveThreat),
|
|
50
|
-
)) /
|
|
51
|
-
(numOwned + 1);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return priority * (1.0 - numOwned / this.baseAmount);
|
|
27
|
+
// Units aren't built automatically, but are instead requested by missions.
|
|
28
|
+
return 0;
|
|
55
29
|
}
|
|
56
30
|
|
|
57
31
|
getMaxCount(
|
|
@@ -24,39 +24,8 @@ export class BasicAirUnit implements AiBuildingRules {
|
|
|
24
24
|
technoRules: TechnoRules,
|
|
25
25
|
threatCache: GlobalThreat | null,
|
|
26
26
|
): number {
|
|
27
|
-
//
|
|
28
|
-
|
|
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
|
-
GameMath.sqrt(
|
|
52
|
-
threatCache.totalAvailableAirPower / Math.max(1, threatCache.totalOffensiveAntiAirThreat),
|
|
53
|
-
),
|
|
54
|
-
),
|
|
55
|
-
);
|
|
56
|
-
return this.baseAmount * priority;
|
|
57
|
-
}
|
|
58
|
-
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
59
|
-
return this.basePriority * (1.0 - numOwned / this.baseAmount);
|
|
27
|
+
// Units aren't built automatically, but are instead requested by missions.
|
|
28
|
+
return 0;
|
|
60
29
|
}
|
|
61
30
|
|
|
62
31
|
getMaxCount(
|
|
@@ -6,13 +6,13 @@ export class BasicBuilding implements AiBuildingRules {
|
|
|
6
6
|
constructor(
|
|
7
7
|
protected basePriority: number,
|
|
8
8
|
protected maxNeeded: number,
|
|
9
|
-
protected onlyBuildWhenFloatingCreditsAmount?: number
|
|
9
|
+
protected onlyBuildWhenFloatingCreditsAmount?: number,
|
|
10
10
|
) {}
|
|
11
11
|
|
|
12
12
|
getPlacementLocation(
|
|
13
13
|
game: GameApi,
|
|
14
14
|
playerData: PlayerData,
|
|
15
|
-
technoRules: TechnoRules
|
|
15
|
+
technoRules: TechnoRules,
|
|
16
16
|
): { rx: number; ry: number } | undefined {
|
|
17
17
|
return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
|
|
18
18
|
}
|
|
@@ -21,7 +21,7 @@ export class BasicBuilding implements AiBuildingRules {
|
|
|
21
21
|
game: GameApi,
|
|
22
22
|
playerData: PlayerData,
|
|
23
23
|
technoRules: TechnoRules,
|
|
24
|
-
threatCache: GlobalThreat | null
|
|
24
|
+
threatCache: GlobalThreat | null,
|
|
25
25
|
): number {
|
|
26
26
|
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
27
27
|
const calcMaxCount = this.getMaxCount(game, playerData, technoRules, threatCache);
|
|
@@ -29,18 +29,20 @@ export class BasicBuilding implements AiBuildingRules {
|
|
|
29
29
|
return -100;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const priority = this.basePriority * (1.0 - numOwned / this.maxNeeded);
|
|
33
|
+
|
|
32
34
|
if (this.onlyBuildWhenFloatingCreditsAmount && playerData.credits < this.onlyBuildWhenFloatingCreditsAmount) {
|
|
33
|
-
return
|
|
35
|
+
return priority * (playerData.credits / this.onlyBuildWhenFloatingCreditsAmount);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
return
|
|
38
|
+
return priority;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
getMaxCount(
|
|
40
42
|
game: GameApi,
|
|
41
43
|
playerData: PlayerData,
|
|
42
44
|
technoRules: TechnoRules,
|
|
43
|
-
threatCache: GlobalThreat | null
|
|
45
|
+
threatCache: GlobalThreat | null,
|
|
44
46
|
): number | null {
|
|
45
47
|
return this.maxNeeded;
|
|
46
48
|
}
|
|
@@ -24,52 +24,8 @@ export class BasicGroundUnit implements AiBuildingRules {
|
|
|
24
24
|
technoRules: TechnoRules,
|
|
25
25
|
threatCache: GlobalThreat | null,
|
|
26
26
|
): number {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let priority = this.basePriority;
|
|
30
|
-
if (this.antiGroundPower > 0) {
|
|
31
|
-
// If the enemy's power is increasing we should try to keep up.
|
|
32
|
-
if (threatCache.totalOffensiveLandThreat > threatCache.totalAvailableAntiGroundFirepower) {
|
|
33
|
-
priority +=
|
|
34
|
-
this.antiGroundPower *
|
|
35
|
-
this.basePriority *
|
|
36
|
-
(threatCache.totalOffensiveLandThreat /
|
|
37
|
-
Math.max(1, threatCache.totalAvailableAntiGroundFirepower));
|
|
38
|
-
} else {
|
|
39
|
-
// But also, if our power dwarfs the enemy, keep pressing the advantage.
|
|
40
|
-
priority +=
|
|
41
|
-
(this.antiGroundPower *
|
|
42
|
-
this.basePriority *
|
|
43
|
-
GameMath.sqrt(
|
|
44
|
-
threatCache.totalAvailableAntiGroundFirepower /
|
|
45
|
-
Math.max(
|
|
46
|
-
1,
|
|
47
|
-
threatCache.totalOffensiveLandThreat + threatCache.totalDefensiveThreat,
|
|
48
|
-
),
|
|
49
|
-
)) /
|
|
50
|
-
(numOwned + 1);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (this.antiAirPower > 0) {
|
|
54
|
-
if (threatCache.totalOffensiveAirThreat > threatCache.totalAvailableAntiAirFirepower) {
|
|
55
|
-
priority +=
|
|
56
|
-
this.antiAirPower *
|
|
57
|
-
this.basePriority *
|
|
58
|
-
(threatCache.totalOffensiveAirThreat / Math.max(1, threatCache.totalAvailableAntiAirFirepower));
|
|
59
|
-
} else {
|
|
60
|
-
priority +=
|
|
61
|
-
(this.antiAirPower *
|
|
62
|
-
this.basePriority *
|
|
63
|
-
GameMath.sqrt(
|
|
64
|
-
threatCache.totalAvailableAntiAirFirepower /
|
|
65
|
-
Math.max(1, threatCache.totalOffensiveAirThreat + threatCache.totalDefensiveThreat),
|
|
66
|
-
)) /
|
|
67
|
-
(numOwned + 1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return priority;
|
|
71
|
-
}
|
|
72
|
-
return this.basePriority * (1.0 - numOwned / this.baseAmount);
|
|
27
|
+
// Units aren't built automatically, but are instead requested by missions.
|
|
28
|
+
return 0;
|
|
73
29
|
}
|
|
74
30
|
|
|
75
31
|
getMaxCount(
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
BuildingPlacementData,
|
|
3
3
|
GameApi,
|
|
4
4
|
GameMath,
|
|
5
|
+
LandType,
|
|
5
6
|
ObjectType,
|
|
6
7
|
PlayerData,
|
|
7
8
|
Size,
|
|
@@ -19,6 +20,7 @@ import { PowerPlant } from "./powerPlant.js";
|
|
|
19
20
|
import { ResourceCollectionBuilding } from "./resourceCollectionBuilding.js";
|
|
20
21
|
import { Harvester } from "./harvester.js";
|
|
21
22
|
import { uniqBy } from "../common/utils.js";
|
|
23
|
+
import { AntiAirStaticDefence } from "./antiAirStaticDefence.js";
|
|
22
24
|
|
|
23
25
|
export interface AiBuildingRules {
|
|
24
26
|
getPriority(
|
|
@@ -51,21 +53,23 @@ export function numBuildingsOwnedOfName(game: GameApi, playerData: PlayerData, n
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
|
-
* Computes a rect 'centered' around a structure of a certain size with additional radius.
|
|
56
|
+
* Computes a rect 'centered' around a structure of a certain size with an additional radius (`adjacent`).
|
|
57
|
+
* The radius is optionally expanded by the size of the new building.
|
|
55
58
|
*
|
|
56
|
-
* This is essentially the
|
|
59
|
+
* This is essentially the candidate placement around a given structure.
|
|
57
60
|
*
|
|
58
61
|
* @param point Top-left location of the inner rect.
|
|
59
62
|
* @param t Size of the inner rect.
|
|
60
|
-
* @param adjacent
|
|
63
|
+
* @param adjacent Amount to expand the building's inner rect by (so buildings must be adjacent by this many tiles)
|
|
64
|
+
* @param newBuildingSize? Size of the new building
|
|
61
65
|
* @returns
|
|
62
66
|
*/
|
|
63
|
-
function computeAdjacentRect(point: Vector2, t: Size, adjacent: number) {
|
|
67
|
+
function computeAdjacentRect(point: Vector2, t: Size, adjacent: number, newBuildingSize?: Size) {
|
|
64
68
|
return {
|
|
65
|
-
x: point.x - adjacent,
|
|
66
|
-
y: point.y - adjacent,
|
|
67
|
-
width: t.width + 2 * adjacent,
|
|
68
|
-
height: t.height + 2 * adjacent,
|
|
69
|
+
x: point.x - adjacent - (newBuildingSize?.width || 0),
|
|
70
|
+
y: point.y - adjacent - (newBuildingSize?.height || 0),
|
|
71
|
+
width: t.width + 2 * adjacent + (newBuildingSize?.width || 0),
|
|
72
|
+
height: t.height + 2 * adjacent + (newBuildingSize?.height || 0),
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -73,8 +77,9 @@ export function getAdjacencyTiles(
|
|
|
73
77
|
game: GameApi,
|
|
74
78
|
playerData: PlayerData,
|
|
75
79
|
technoRules: TechnoRules,
|
|
80
|
+
onWater: boolean,
|
|
76
81
|
minimumSpace: number,
|
|
77
|
-
) {
|
|
82
|
+
): Tile[] {
|
|
78
83
|
const placementRules = game.getBuildingPlacementData(technoRules.name);
|
|
79
84
|
const { width: newBuildingWidth, height: newBuildingHeight } = placementRules.foundation;
|
|
80
85
|
const tiles = [];
|
|
@@ -82,44 +87,48 @@ export function getAdjacencyTiles(
|
|
|
82
87
|
const removedTiles = new Set<string>();
|
|
83
88
|
for (let buildingId of buildings) {
|
|
84
89
|
const building = game.getUnitData(buildingId);
|
|
85
|
-
if (building?.rules?.baseNormal) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
if (!building?.rules?.baseNormal) {
|
|
91
|
+
// This building is not considered for adjacency checks.
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const { foundation, tile } = building;
|
|
95
|
+
const buildingBase = new Vector2(tile.rx, tile.ry);
|
|
96
|
+
const buildingSize = {
|
|
97
|
+
width: foundation?.width,
|
|
98
|
+
height: foundation?.height,
|
|
99
|
+
};
|
|
100
|
+
const range = computeAdjacentRect(buildingBase, buildingSize, technoRules.adjacent, placementRules.foundation);
|
|
101
|
+
const baseTile = game.mapApi.getTile(range.x, range.y);
|
|
102
|
+
if (!baseTile) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const adjacentTiles = game.mapApi
|
|
106
|
+
.getTilesInRect(baseTile, {
|
|
98
107
|
width: range.width,
|
|
99
108
|
height: range.height,
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
})
|
|
110
|
+
.filter((tile) => !onWater || tile.landType === LandType.Water);
|
|
111
|
+
tiles.push(...adjacentTiles);
|
|
112
|
+
|
|
113
|
+
// Prevent placing the new building on tiles that would cause it to overlap with this building.
|
|
114
|
+
const modifiedBase = new Vector2(
|
|
115
|
+
buildingBase.x - (newBuildingWidth - 1),
|
|
116
|
+
buildingBase.y - (newBuildingHeight - 1),
|
|
117
|
+
);
|
|
118
|
+
const modifiedSize = {
|
|
119
|
+
width: buildingSize.width + (newBuildingWidth - 1),
|
|
120
|
+
height: buildingSize.height + (newBuildingHeight - 1),
|
|
121
|
+
};
|
|
122
|
+
const blockedRect = computeAdjacentRect(modifiedBase, modifiedSize, minimumSpace);
|
|
123
|
+
const buildingTiles = adjacentTiles.filter((tile) => {
|
|
124
|
+
return (
|
|
125
|
+
tile.rx >= blockedRect.x &&
|
|
126
|
+
tile.rx < blockedRect.x + blockedRect.width &&
|
|
127
|
+
tile.ry >= blockedRect.y &&
|
|
128
|
+
tile.ry < blockedRect.y + blockedRect.height
|
|
107
129
|
);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
height: buildingSize.height + (newBuildingHeight - 1),
|
|
111
|
-
};
|
|
112
|
-
const blockedRect = computeAdjacentRect(modifiedBase, modifiedSize, minimumSpace);
|
|
113
|
-
const buildingTiles = adjacentTiles.filter((tile) => {
|
|
114
|
-
return (
|
|
115
|
-
tile.rx >= blockedRect.x &&
|
|
116
|
-
tile.rx < blockedRect.x + blockedRect.width &&
|
|
117
|
-
tile.ry >= blockedRect.y &&
|
|
118
|
-
tile.ry < blockedRect.y + blockedRect.height
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
buildingTiles.forEach((buildingTile) => removedTiles.add(buildingTile.id));
|
|
122
|
-
}
|
|
130
|
+
});
|
|
131
|
+
buildingTiles.forEach((buildingTile) => removedTiles.add(buildingTile.id));
|
|
123
132
|
}
|
|
124
133
|
// Remove duplicate tiles.
|
|
125
134
|
const withDuplicatesRemoved = uniqBy(tiles, (tile) => tile.id);
|
|
@@ -151,17 +160,18 @@ function distance(x1: number, y1: number, x2: number, y2: number) {
|
|
|
151
160
|
export function getDefaultPlacementLocation(
|
|
152
161
|
game: GameApi,
|
|
153
162
|
playerData: PlayerData,
|
|
154
|
-
|
|
163
|
+
idealPoint: Vector2,
|
|
155
164
|
technoRules: TechnoRules,
|
|
165
|
+
onWater: boolean = false,
|
|
156
166
|
minSpace: number = 1,
|
|
157
167
|
): { rx: number; ry: number } | undefined {
|
|
158
|
-
//
|
|
168
|
+
// Closest possible location near `startPoint`.
|
|
159
169
|
const size: BuildingPlacementData = game.getBuildingPlacementData(technoRules.name);
|
|
160
170
|
if (!size) {
|
|
161
171
|
return undefined;
|
|
162
172
|
}
|
|
163
|
-
const tiles = getAdjacencyTiles(game, playerData, technoRules, minSpace);
|
|
164
|
-
const tileDistances = getTileDistances(
|
|
173
|
+
const tiles = getAdjacencyTiles(game, playerData, technoRules, onWater, minSpace);
|
|
174
|
+
const tileDistances = getTileDistances(idealPoint, tiles);
|
|
165
175
|
|
|
166
176
|
for (let tileDistance of tileDistances) {
|
|
167
177
|
if (tileDistance.tile && game.canPlaceBuilding(playerData.name, technoRules.name, tileDistance.tile)) {
|
|
@@ -174,7 +184,7 @@ export function getDefaultPlacementLocation(
|
|
|
174
184
|
// Priority 0 = don't build.
|
|
175
185
|
export type TechnoRulesWithPriority = { unit: TechnoRules; priority: number };
|
|
176
186
|
|
|
177
|
-
export const DEFAULT_BUILDING_PRIORITY =
|
|
187
|
+
export const DEFAULT_BUILDING_PRIORITY = 0;
|
|
178
188
|
|
|
179
189
|
export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
180
190
|
// Allied
|
|
@@ -183,18 +193,20 @@ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
|
183
193
|
["GAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
184
194
|
["GAPILE", new BasicBuilding(12, 1)], // Barracks
|
|
185
195
|
["CMIN", new Harvester(15, 4, 2)], // Chrono Miner
|
|
186
|
-
["ENGINEER", new BasicBuilding(10, 1, 1000)], // Engineer
|
|
187
196
|
["GADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
188
197
|
["GAAIRC", new BasicBuilding(10, 1, 500)], // Airforce Command
|
|
198
|
+
["AMRADR", new BasicBuilding(10, 1, 500)], // Airforce Command (USA)
|
|
189
199
|
|
|
190
200
|
["GATECH", new BasicBuilding(20, 1, 4000)], // Allied Battle Lab
|
|
191
201
|
["GAYARD", new BasicBuilding(0, 0, 0)], // Naval Yard, disabled
|
|
192
202
|
|
|
193
|
-
["GAPILL", new AntiGroundStaticDefence(
|
|
194
|
-
["ATESLA", new AntiGroundStaticDefence(
|
|
203
|
+
["GAPILL", new AntiGroundStaticDefence(2, 1, 5)], // Pillbox
|
|
204
|
+
["ATESLA", new AntiGroundStaticDefence(2, 1, 10)], // Prism Cannon
|
|
205
|
+
["NASAM", new AntiAirStaticDefence(2, 1, 5)], // Prism Cannon
|
|
195
206
|
["GAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
196
207
|
|
|
197
|
-
["E1", new BasicGroundUnit(2,
|
|
208
|
+
["E1", new BasicGroundUnit(2, 2, 0.2, 0)], // GI
|
|
209
|
+
["ENGINEER", new BasicGroundUnit(1, 0, 0)], // Engineer
|
|
198
210
|
["MTNK", new BasicGroundUnit(10, 3, 2, 0)], // Grizzly Tank
|
|
199
211
|
["MGTK", new BasicGroundUnit(10, 1, 2.5, 0)], // Mirage Tank
|
|
200
212
|
["FV", new BasicGroundUnit(5, 2, 0.5, 1)], // IFV
|
|
@@ -210,7 +222,6 @@ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
|
210
222
|
["NAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
211
223
|
["NAHAND", new BasicBuilding(12, 1)], // Barracks
|
|
212
224
|
["HARV", new Harvester(15, 4, 2)], // War Miner
|
|
213
|
-
["SENGINEER", new BasicBuilding(10, 1, 1000)], // Soviet Engineer
|
|
214
225
|
["NADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
215
226
|
["NARADR", new BasicBuilding(10, 1, 500)], // Radar
|
|
216
227
|
["NANRCT", new PowerPlant()], // Nuclear Reactor
|
|
@@ -218,11 +229,16 @@ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
|
218
229
|
|
|
219
230
|
["NATECH", new BasicBuilding(20, 1, 4000)], // Soviet Battle Lab
|
|
220
231
|
|
|
221
|
-
["NALASR", new AntiGroundStaticDefence(
|
|
222
|
-
["
|
|
232
|
+
["NALASR", new AntiGroundStaticDefence(2, 1, 5)], // Sentry Gun
|
|
233
|
+
["NAFLAK", new AntiAirStaticDefence(2, 1, 5)], // Flak Cannon
|
|
234
|
+
["TESLA", new AntiGroundStaticDefence(2, 1, 10)], // Tesla Coil
|
|
223
235
|
["NAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
224
236
|
|
|
225
|
-
["E2", new BasicGroundUnit(2,
|
|
237
|
+
["E2", new BasicGroundUnit(2, 2, 0.2, 0)], // Conscript
|
|
238
|
+
["SENGINEER", new BasicGroundUnit(1, 0, 0)], // Soviet Engineer
|
|
239
|
+
["FLAKT", new BasicGroundUnit(2, 2, 0.1, 0.3)], // Flak Trooper
|
|
240
|
+
["YURI", new BasicGroundUnit(1, 1, 1, 0)], // Yuri
|
|
241
|
+
["DOG", new BasicGroundUnit(1, 1, 0, 0)], // Soviet Attack Dog
|
|
226
242
|
["HTNK", new BasicGroundUnit(10, 3, 3, 0)], // Rhino Tank
|
|
227
243
|
["APOC", new BasicGroundUnit(6, 1, 5, 0)], // Apocalypse Tank
|
|
228
244
|
["HTK", new BasicGroundUnit(5, 2, 0.33, 1.5)], // Flak Track
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GameApi, PlayerData, TechnoRules, Vector2 } from "@chronodivide/game-api";
|
|
2
|
+
import { getPointTowardsOtherPoint } from "../map/map.js";
|
|
3
|
+
import { getDefaultPlacementLocation } from "./buildingRules.js";
|
|
4
|
+
|
|
5
|
+
export const getStaticDefencePlacement = (game: GameApi, playerData: PlayerData, technoRules: TechnoRules) => {
|
|
6
|
+
// Prefer front towards enemy.
|
|
7
|
+
let startLocation = playerData.startLocation;
|
|
8
|
+
let players = game.getPlayers();
|
|
9
|
+
let enemyFacingLocationCandidates: Vector2[] = [];
|
|
10
|
+
for (let i = 0; i < players.length; ++i) {
|
|
11
|
+
let playerName = players[i];
|
|
12
|
+
if (playerName == playerData.name) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
let enemyPlayer = game.getPlayerData(playerName);
|
|
16
|
+
enemyFacingLocationCandidates.push(
|
|
17
|
+
getPointTowardsOtherPoint(game, startLocation, enemyPlayer.startLocation, 4, 16, 1.5),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
let selectedLocation =
|
|
21
|
+
enemyFacingLocationCandidates[Math.floor(game.generateRandom() * enemyFacingLocationCandidates.length)];
|
|
22
|
+
return getDefaultPlacementLocation(game, playerData, selectedLocation, technoRules, false, 0);
|
|
23
|
+
};
|
|
@@ -3,6 +3,7 @@ import { GlobalThreat } from "../threat/threat.js";
|
|
|
3
3
|
import { BasicGroundUnit } from "./basicGroundUnit.js";
|
|
4
4
|
|
|
5
5
|
const IDEAL_HARVESTERS_PER_REFINERY = 2;
|
|
6
|
+
const MAX_HARVESTERS_PER_REFINERY = 4;
|
|
6
7
|
|
|
7
8
|
export class Harvester extends BasicGroundUnit {
|
|
8
9
|
constructor(
|
|
@@ -23,7 +24,7 @@ export class Harvester extends BasicGroundUnit {
|
|
|
23
24
|
const refineries = game.getVisibleUnits(playerData.name, "self", (r) => r.refinery).length;
|
|
24
25
|
const harvesters = game.getVisibleUnits(playerData.name, "self", (r) => r.harvester).length;
|
|
25
26
|
|
|
26
|
-
const boost = harvesters < this.minNeeded ? 3 : 1;
|
|
27
|
+
const boost = harvesters < this.minNeeded ? 3 : harvesters > refineries * MAX_HARVESTERS_PER_REFINERY ? 0 : 1;
|
|
27
28
|
|
|
28
29
|
return this.basePriority * (refineries / Math.max(harvesters / IDEAL_HARVESTERS_PER_REFINERY, 1)) * boost;
|
|
29
30
|
}
|