@supalosa/chronodivide-bot 0.2.0 → 0.2.2-a
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 +11 -3
- package/dist/bot/bot.js +17 -9
- package/dist/bot/bot.js.map +1 -0
- package/dist/bot/logic/awareness.js +12 -2
- package/dist/bot/logic/awareness.js.map +1 -0
- package/dist/bot/logic/building/ArtilleryUnit.js +29 -8
- package/dist/bot/logic/building/antiGroundStaticDefence.js +3 -2
- package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -0
- package/dist/bot/logic/building/artilleryUnit.js.map +1 -0
- package/dist/bot/logic/building/basicAirUnit.js +2 -1
- package/dist/bot/logic/building/basicAirUnit.js.map +1 -0
- package/dist/bot/logic/building/basicBuilding.js +2 -1
- package/dist/bot/logic/building/basicBuilding.js.map +1 -0
- package/dist/bot/logic/building/basicGroundUnit.js +2 -1
- package/dist/bot/logic/building/basicGroundUnit.js.map +1 -0
- package/dist/bot/logic/building/building.js +2 -0
- package/dist/bot/logic/building/buildingRules.js +168 -0
- package/dist/bot/logic/building/buildingRules.js.map +1 -0
- package/dist/bot/logic/building/harvester.js +1 -0
- package/dist/bot/logic/building/harvester.js.map +1 -0
- package/dist/bot/logic/building/powerPlant.js +2 -1
- package/dist/bot/logic/building/powerPlant.js.map +1 -0
- package/dist/bot/logic/building/queueController.js +2 -1
- package/dist/bot/logic/building/queueController.js.map +1 -0
- package/dist/bot/logic/building/resourceCollectionBuilding.js +2 -1
- package/dist/bot/logic/building/resourceCollectionBuilding.js.map +1 -0
- package/dist/bot/logic/common/scout.js +100 -0
- package/dist/bot/logic/common/scout.js.map +1 -0
- package/dist/bot/logic/common/utils.js +14 -0
- package/dist/bot/logic/common/utils.js.map +1 -0
- package/dist/bot/logic/map/map.js +9 -25
- package/dist/bot/logic/map/map.js.map +1 -0
- package/dist/bot/logic/map/sector.js +33 -1
- package/dist/bot/logic/map/sector.js.map +1 -0
- package/dist/bot/logic/mission/mission.js +3 -1
- package/dist/bot/logic/mission/mission.js.map +1 -0
- package/dist/bot/logic/mission/missionController.js +5 -4
- package/dist/bot/logic/mission/missionController.js.map +1 -0
- package/dist/bot/logic/mission/missionFactories.js +3 -0
- package/dist/bot/logic/mission/missionFactories.js.map +1 -0
- package/dist/bot/logic/mission/missions/attackMission.js +9 -10
- package/dist/bot/logic/mission/missions/attackMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/defenceMission.js +12 -7
- package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/engineerMission.js +34 -0
- package/dist/bot/logic/mission/missions/engineerMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/expansionMission.js +5 -4
- package/dist/bot/logic/mission/missions/expansionMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/oneTimeMission.js +3 -2
- package/dist/bot/logic/mission/missions/oneTimeMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/retreatMission.js +3 -2
- package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/scoutingMission.js +8 -9
- package/dist/bot/logic/mission/missions/scoutingMission.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/combatSquad.js +20 -18
- package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/common.js +25 -5
- package/dist/bot/logic/squad/behaviours/common.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/engineerSquad.js +36 -0
- package/dist/bot/logic/squad/behaviours/engineerSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/expansionSquad.js +1 -0
- package/dist/bot/logic/squad/behaviours/expansionSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/retreatSquad.js +6 -10
- package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +66 -18
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -0
- package/dist/bot/logic/squad/squad.js +4 -5
- package/dist/bot/logic/squad/squad.js.map +1 -0
- package/dist/bot/logic/squad/squadBehaviour.js +1 -0
- package/dist/bot/logic/squad/squadBehaviour.js.map +1 -0
- package/dist/bot/logic/squad/squadBehaviours.js +1 -0
- package/dist/bot/logic/squad/squadBehaviours.js.map +1 -0
- package/dist/bot/logic/squad/squadController.js +73 -23
- package/dist/bot/logic/squad/squadController.js.map +1 -0
- package/dist/bot/logic/threat/threat.js +1 -0
- package/dist/bot/logic/threat/threat.js.map +1 -0
- package/dist/bot/logic/threat/threatCalculator.js +1 -0
- package/dist/bot/logic/threat/threatCalculator.js.map +1 -0
- package/dist/exampleBot.js +29 -12
- package/dist/exampleBot.js.map +1 -0
- package/package.json +15 -7
- package/src/bot/bot.ts +21 -16
- package/src/bot/logic/awareness.ts +22 -5
- package/src/bot/logic/building/antiGroundStaticDefence.ts +60 -60
- package/src/bot/logic/building/artilleryUnit.ts +68 -0
- package/src/bot/logic/building/basicAirUnit.ts +68 -68
- package/src/bot/logic/building/basicBuilding.ts +47 -47
- package/src/bot/logic/building/basicGroundUnit.ts +1 -1
- package/src/bot/logic/building/buildingRules.ts +233 -0
- package/src/bot/logic/building/powerPlant.ts +32 -32
- package/src/bot/logic/building/queueController.ts +1 -1
- package/src/bot/logic/building/resourceCollectionBuilding.ts +56 -56
- package/src/bot/logic/common/scout.ts +127 -1
- package/src/bot/logic/common/utils.ts +17 -0
- package/src/bot/logic/map/map.ts +70 -84
- package/src/bot/logic/map/sector.ts +46 -4
- package/src/bot/logic/mission/mission.ts +2 -2
- package/src/bot/logic/mission/missionController.ts +7 -6
- package/src/bot/logic/mission/missionFactories.ts +5 -0
- package/src/bot/logic/mission/missions/attackMission.ts +19 -12
- package/src/bot/logic/mission/missions/defenceMission.ts +35 -15
- package/src/bot/logic/mission/missions/engineerMission.ts +61 -0
- package/src/bot/logic/mission/missions/expansionMission.ts +6 -4
- package/src/bot/logic/mission/missions/oneTimeMission.ts +3 -2
- package/src/bot/logic/mission/missions/retreatMission.ts +3 -2
- package/src/bot/logic/mission/missions/scoutingMission.ts +9 -6
- package/src/bot/logic/squad/behaviours/combatSquad.ts +22 -18
- package/src/bot/logic/squad/behaviours/common.ts +38 -5
- package/src/bot/logic/squad/behaviours/engineerSquad.ts +53 -0
- package/src/bot/logic/squad/behaviours/retreatSquad.ts +10 -12
- package/src/bot/logic/squad/behaviours/scoutingSquad.ts +79 -26
- package/src/bot/logic/squad/squad.ts +4 -4
- package/src/bot/logic/squad/squadBehaviour.ts +3 -1
- package/src/bot/logic/squad/squadController.ts +135 -70
- package/src/exampleBot.ts +31 -12
- package/tsconfig.json +73 -73
- package/src/bot/logic/building/ArtilleryUnit.ts +0 -43
- package/src/bot/logic/building/building.ts +0 -125
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
+
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
|
|
4
|
+
|
|
5
|
+
export class ArtilleryUnit implements AiBuildingRules {
|
|
6
|
+
constructor(private basePriority: number,
|
|
7
|
+
private artilleryPower: number,
|
|
8
|
+
private antiGroundPower: number,
|
|
9
|
+
private baseAmount: number) {}
|
|
10
|
+
|
|
11
|
+
getPlacementLocation(
|
|
12
|
+
game: GameApi,
|
|
13
|
+
playerData: PlayerData,
|
|
14
|
+
technoRules: TechnoRules
|
|
15
|
+
): { rx: number; ry: number } | undefined {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getPriority(
|
|
20
|
+
game: GameApi,
|
|
21
|
+
playerData: PlayerData,
|
|
22
|
+
technoRules: TechnoRules,
|
|
23
|
+
threatCache: GlobalThreat | null
|
|
24
|
+
): number {
|
|
25
|
+
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
26
|
+
let priority = this.basePriority;
|
|
27
|
+
// If the enemy's defensive power is increasing we will start to build these.
|
|
28
|
+
if (threatCache && threatCache.totalDefensivePower > threatCache.totalAvailableAntiGroundFirepower) {
|
|
29
|
+
priority += (
|
|
30
|
+
this.artilleryPower *
|
|
31
|
+
(threatCache.totalAvailableAntiGroundFirepower / Math.max(1, threatCache.totalDefensivePower))
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (threatCache && this.antiGroundPower > 0) {
|
|
35
|
+
// If the enemy's power is increasing we should try to keep up.
|
|
36
|
+
if (threatCache.totalOffensiveLandThreat > threatCache.totalAvailableAntiGroundFirepower) {
|
|
37
|
+
priority +=
|
|
38
|
+
this.antiGroundPower *
|
|
39
|
+
this.basePriority *
|
|
40
|
+
(threatCache.totalOffensiveLandThreat /
|
|
41
|
+
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
|
+
Math.sqrt(
|
|
48
|
+
threatCache.totalAvailableAntiGroundFirepower /
|
|
49
|
+
Math.max(
|
|
50
|
+
1,
|
|
51
|
+
threatCache.totalOffensiveLandThreat + threatCache.totalDefensiveThreat,
|
|
52
|
+
),
|
|
53
|
+
)) /
|
|
54
|
+
(numOwned + 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return priority * (1.0 - numOwned / this.baseAmount);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getMaxCount(
|
|
61
|
+
game: GameApi,
|
|
62
|
+
playerData: PlayerData,
|
|
63
|
+
technoRules: TechnoRules,
|
|
64
|
+
threatCache: GlobalThreat | null
|
|
65
|
+
): number | null {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
-
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
-
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./
|
|
4
|
-
|
|
5
|
-
export class BasicAirUnit implements AiBuildingRules {
|
|
6
|
-
constructor(
|
|
7
|
-
private basePriority: number,
|
|
8
|
-
private baseAmount: number,
|
|
9
|
-
private antiGroundPower: number = 1, // boolean for now, but will eventually be used in weighting.
|
|
10
|
-
private antiAirPower: number = 0
|
|
11
|
-
) {}
|
|
12
|
-
|
|
13
|
-
getPlacementLocation(
|
|
14
|
-
game: GameApi,
|
|
15
|
-
playerData: PlayerData,
|
|
16
|
-
technoRules: TechnoRules
|
|
17
|
-
): { rx: number; ry: number } | undefined {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
getPriority(
|
|
22
|
-
game: GameApi,
|
|
23
|
-
playerData: PlayerData,
|
|
24
|
-
technoRules: TechnoRules,
|
|
25
|
-
threatCache: GlobalThreat | null
|
|
26
|
-
): number {
|
|
27
|
-
// If the enemy's anti-air power is low we might build more.
|
|
28
|
-
if (threatCache) {
|
|
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
|
-
Math.sqrt(threatCache.totalAvailableAirPower / Math.max(1, threatCache.totalOffensiveAntiAirThreat))
|
|
52
|
-
)
|
|
53
|
-
);
|
|
54
|
-
return this.baseAmount * priority;
|
|
55
|
-
}
|
|
56
|
-
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
57
|
-
return this.basePriority * (1.0 - numOwned / this.baseAmount);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
getMaxCount(
|
|
61
|
-
game: GameApi,
|
|
62
|
-
playerData: PlayerData,
|
|
63
|
-
technoRules: TechnoRules,
|
|
64
|
-
threatCache: GlobalThreat | null
|
|
65
|
-
): number | null {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
1
|
+
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
+
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
|
|
4
|
+
|
|
5
|
+
export class BasicAirUnit implements AiBuildingRules {
|
|
6
|
+
constructor(
|
|
7
|
+
private basePriority: number,
|
|
8
|
+
private baseAmount: number,
|
|
9
|
+
private antiGroundPower: number = 1, // boolean for now, but will eventually be used in weighting.
|
|
10
|
+
private antiAirPower: number = 0
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
getPlacementLocation(
|
|
14
|
+
game: GameApi,
|
|
15
|
+
playerData: PlayerData,
|
|
16
|
+
technoRules: TechnoRules
|
|
17
|
+
): { rx: number; ry: number } | undefined {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getPriority(
|
|
22
|
+
game: GameApi,
|
|
23
|
+
playerData: PlayerData,
|
|
24
|
+
technoRules: TechnoRules,
|
|
25
|
+
threatCache: GlobalThreat | null
|
|
26
|
+
): number {
|
|
27
|
+
// If the enemy's anti-air power is low we might build more.
|
|
28
|
+
if (threatCache) {
|
|
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
|
+
Math.sqrt(threatCache.totalAvailableAirPower / Math.max(1, threatCache.totalOffensiveAntiAirThreat))
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
return this.baseAmount * priority;
|
|
55
|
+
}
|
|
56
|
+
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
57
|
+
return this.basePriority * (1.0 - numOwned / this.baseAmount);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getMaxCount(
|
|
61
|
+
game: GameApi,
|
|
62
|
+
playerData: PlayerData,
|
|
63
|
+
technoRules: TechnoRules,
|
|
64
|
+
threatCache: GlobalThreat | null
|
|
65
|
+
): number | null {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
-
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./
|
|
3
|
-
import { GlobalThreat } from "../threat/threat.js";
|
|
4
|
-
|
|
5
|
-
export class BasicBuilding implements AiBuildingRules {
|
|
6
|
-
constructor(
|
|
7
|
-
protected basePriority: number,
|
|
8
|
-
protected maxNeeded: number,
|
|
9
|
-
protected onlyBuildWhenFloatingCreditsAmount?: number
|
|
10
|
-
) {}
|
|
11
|
-
|
|
12
|
-
getPlacementLocation(
|
|
13
|
-
game: GameApi,
|
|
14
|
-
playerData: PlayerData,
|
|
15
|
-
technoRules: TechnoRules
|
|
16
|
-
): { rx: number; ry: number } | undefined {
|
|
17
|
-
return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
getPriority(
|
|
21
|
-
game: GameApi,
|
|
22
|
-
playerData: PlayerData,
|
|
23
|
-
technoRules: TechnoRules,
|
|
24
|
-
threatCache: GlobalThreat | null
|
|
25
|
-
): number {
|
|
26
|
-
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
27
|
-
const calcMaxCount = this.getMaxCount(game, playerData, technoRules, threatCache);
|
|
28
|
-
if (numOwned >= (calcMaxCount ?? this.maxNeeded)) {
|
|
29
|
-
return -100;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (this.onlyBuildWhenFloatingCreditsAmount && playerData.credits < this.onlyBuildWhenFloatingCreditsAmount) {
|
|
33
|
-
return -100;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return this.basePriority * (1.0 - numOwned / this.maxNeeded);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
getMaxCount(
|
|
40
|
-
game: GameApi,
|
|
41
|
-
playerData: PlayerData,
|
|
42
|
-
technoRules: TechnoRules,
|
|
43
|
-
threatCache: GlobalThreat | null
|
|
44
|
-
): number | null {
|
|
45
|
-
return this.maxNeeded;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
1
|
+
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
+
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
|
|
3
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
4
|
+
|
|
5
|
+
export class BasicBuilding implements AiBuildingRules {
|
|
6
|
+
constructor(
|
|
7
|
+
protected basePriority: number,
|
|
8
|
+
protected maxNeeded: number,
|
|
9
|
+
protected onlyBuildWhenFloatingCreditsAmount?: number
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
getPlacementLocation(
|
|
13
|
+
game: GameApi,
|
|
14
|
+
playerData: PlayerData,
|
|
15
|
+
technoRules: TechnoRules
|
|
16
|
+
): { rx: number; ry: number } | undefined {
|
|
17
|
+
return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getPriority(
|
|
21
|
+
game: GameApi,
|
|
22
|
+
playerData: PlayerData,
|
|
23
|
+
technoRules: TechnoRules,
|
|
24
|
+
threatCache: GlobalThreat | null
|
|
25
|
+
): number {
|
|
26
|
+
const numOwned = numBuildingsOwnedOfType(game, playerData, technoRules);
|
|
27
|
+
const calcMaxCount = this.getMaxCount(game, playerData, technoRules, threatCache);
|
|
28
|
+
if (numOwned >= (calcMaxCount ?? this.maxNeeded)) {
|
|
29
|
+
return -100;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (this.onlyBuildWhenFloatingCreditsAmount && playerData.credits < this.onlyBuildWhenFloatingCreditsAmount) {
|
|
33
|
+
return -100;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this.basePriority * (1.0 - numOwned / this.maxNeeded);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getMaxCount(
|
|
40
|
+
game: GameApi,
|
|
41
|
+
playerData: PlayerData,
|
|
42
|
+
technoRules: TechnoRules,
|
|
43
|
+
threatCache: GlobalThreat | null
|
|
44
|
+
): number | null {
|
|
45
|
+
return this.maxNeeded;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
2
|
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
-
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./
|
|
3
|
+
import { AiBuildingRules, getDefaultPlacementLocation, numBuildingsOwnedOfType } from "./buildingRules.js";
|
|
4
4
|
|
|
5
5
|
export class BasicGroundUnit implements AiBuildingRules {
|
|
6
6
|
constructor(
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BuildingPlacementData,
|
|
3
|
+
GameApi,
|
|
4
|
+
ObjectType,
|
|
5
|
+
PlayerData,
|
|
6
|
+
Point2D,
|
|
7
|
+
Size,
|
|
8
|
+
TechnoRules,
|
|
9
|
+
Tile,
|
|
10
|
+
} from "@chronodivide/game-api";
|
|
11
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
12
|
+
import { AntiGroundStaticDefence } from "./antiGroundStaticDefence.js";
|
|
13
|
+
import { ArtilleryUnit } from "./artilleryUnit.js";
|
|
14
|
+
import { BasicAirUnit } from "./basicAirUnit.js";
|
|
15
|
+
import { BasicBuilding } from "./basicBuilding.js";
|
|
16
|
+
import { BasicGroundUnit } from "./basicGroundUnit.js";
|
|
17
|
+
import { PowerPlant } from "./powerPlant.js";
|
|
18
|
+
import { ResourceCollectionBuilding } from "./resourceCollectionBuilding.js";
|
|
19
|
+
import { Harvester } from "./harvester.js";
|
|
20
|
+
import uniqBy from "lodash.uniqby";
|
|
21
|
+
|
|
22
|
+
export interface AiBuildingRules {
|
|
23
|
+
getPriority(
|
|
24
|
+
game: GameApi,
|
|
25
|
+
playerData: PlayerData,
|
|
26
|
+
technoRules: TechnoRules,
|
|
27
|
+
threatCache: GlobalThreat | null,
|
|
28
|
+
): number;
|
|
29
|
+
|
|
30
|
+
getPlacementLocation(
|
|
31
|
+
game: GameApi,
|
|
32
|
+
playerData: PlayerData,
|
|
33
|
+
technoRules: TechnoRules,
|
|
34
|
+
): { rx: number; ry: number } | undefined;
|
|
35
|
+
|
|
36
|
+
getMaxCount(
|
|
37
|
+
game: GameApi,
|
|
38
|
+
playerData: PlayerData,
|
|
39
|
+
technoRules: TechnoRules,
|
|
40
|
+
threatCache: GlobalThreat | null,
|
|
41
|
+
): number | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function numBuildingsOwnedOfType(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
|
|
45
|
+
return game.getVisibleUnits(playerData.name, "self", (r) => r == technoRules).length;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function numBuildingsOwnedOfName(game: GameApi, playerData: PlayerData, name: string): number {
|
|
49
|
+
return game.getVisibleUnits(playerData.name, "self", (r) => r.name === name).length;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Computes a rect 'centered' around a structure of a certain size with additional radius.
|
|
54
|
+
*
|
|
55
|
+
* This is essentially the placeable area around a given structure.
|
|
56
|
+
*
|
|
57
|
+
* @param point Top-left location of the inner rect.
|
|
58
|
+
* @param t Size of the inner rect.
|
|
59
|
+
* @param adjacent Size of the outer rect.
|
|
60
|
+
* @returns
|
|
61
|
+
*/
|
|
62
|
+
function computeAdjacentRect(point: Point2D, t: Size, adjacent: number) {
|
|
63
|
+
return {
|
|
64
|
+
x: point.x - adjacent,
|
|
65
|
+
y: point.y - adjacent,
|
|
66
|
+
width: t.width + 2 * adjacent,
|
|
67
|
+
height: t.height + 2 * adjacent,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getAdjacencyTiles(
|
|
72
|
+
game: GameApi,
|
|
73
|
+
playerData: PlayerData,
|
|
74
|
+
technoRules: TechnoRules,
|
|
75
|
+
minimumSpace: number,
|
|
76
|
+
) {
|
|
77
|
+
const placementRules = game.getBuildingPlacementData(technoRules.name);
|
|
78
|
+
const { width: newBuildingWidth, height: newBuildingHeight } = placementRules.foundation;
|
|
79
|
+
const tiles = [];
|
|
80
|
+
const buildings = game.getVisibleUnits(playerData.name, "self", (r: TechnoRules) => r.type === ObjectType.Building);
|
|
81
|
+
const removedTiles = new Set<string>();
|
|
82
|
+
for (let buildingId of buildings) {
|
|
83
|
+
const building = game.getUnitData(buildingId);
|
|
84
|
+
if (building?.rules?.baseNormal) {
|
|
85
|
+
const { foundation, tile } = building;
|
|
86
|
+
const buildingBase = {
|
|
87
|
+
x: tile.rx,
|
|
88
|
+
y: tile.ry,
|
|
89
|
+
};
|
|
90
|
+
const buildingSize = {
|
|
91
|
+
width: foundation?.width,
|
|
92
|
+
height: foundation?.height,
|
|
93
|
+
};
|
|
94
|
+
const range = computeAdjacentRect(buildingBase, buildingSize, technoRules.adjacent);
|
|
95
|
+
const baseTile = game.mapApi.getTile(range.x, range.y);
|
|
96
|
+
if (!baseTile) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const adjacentTiles = game.mapApi.getTilesInRect(baseTile, {
|
|
100
|
+
width: range.width,
|
|
101
|
+
height: range.height,
|
|
102
|
+
});
|
|
103
|
+
tiles.push(...adjacentTiles);
|
|
104
|
+
|
|
105
|
+
// Prevent placing the new building on tiles that would cause it to overlap with this building.
|
|
106
|
+
const modifiedBase = {
|
|
107
|
+
x: buildingBase.x - (newBuildingWidth - 1),
|
|
108
|
+
y: buildingBase.y - (newBuildingHeight - 1),
|
|
109
|
+
};
|
|
110
|
+
const modifiedSize = {
|
|
111
|
+
width: buildingSize.width + (newBuildingWidth - 1),
|
|
112
|
+
height: buildingSize.height + (newBuildingHeight - 1),
|
|
113
|
+
};
|
|
114
|
+
const blockedRect = computeAdjacentRect(modifiedBase, modifiedSize, minimumSpace);
|
|
115
|
+
const buildingTiles = adjacentTiles.filter((tile) => {
|
|
116
|
+
return (
|
|
117
|
+
tile.rx >= blockedRect.x &&
|
|
118
|
+
tile.rx < blockedRect.x + blockedRect.width &&
|
|
119
|
+
tile.ry >= blockedRect.y &&
|
|
120
|
+
tile.ry < blockedRect.y + blockedRect.height
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
buildingTiles.forEach((buildingTile) => removedTiles.add(buildingTile.id));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Remove duplicate tiles.
|
|
127
|
+
const withDuplicatesRemoved = uniqBy(tiles, (tile) => tile.id);
|
|
128
|
+
// Remove tiles containing buildings and potentially area around them removed as well.
|
|
129
|
+
return withDuplicatesRemoved.filter((tile) => !removedTiles.has(tile.id));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getTileDistances(startPoint: Point2D, tiles: Tile[]) {
|
|
133
|
+
return tiles
|
|
134
|
+
.map((tile) => ({
|
|
135
|
+
tile,
|
|
136
|
+
distance: distance(tile.rx, tile.ry, startPoint.x, startPoint.y),
|
|
137
|
+
}))
|
|
138
|
+
.sort((a, b) => {
|
|
139
|
+
return a.distance - b.distance;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function distance(x1: number, y1: number, x2: number, y2: number) {
|
|
144
|
+
var dx = x1 - x2;
|
|
145
|
+
var dy = y1 - y2;
|
|
146
|
+
let tmp = dx * dx + dy * dy;
|
|
147
|
+
if (0 === tmp) {
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
return Math.sqrt(tmp);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function getDefaultPlacementLocation(
|
|
154
|
+
game: GameApi,
|
|
155
|
+
playerData: PlayerData,
|
|
156
|
+
startPoint: Point2D,
|
|
157
|
+
technoRules: TechnoRules,
|
|
158
|
+
minSpace: number = 1,
|
|
159
|
+
): { rx: number; ry: number } | undefined {
|
|
160
|
+
// Random location, preferably near start location.
|
|
161
|
+
const size: BuildingPlacementData = game.getBuildingPlacementData(technoRules.name);
|
|
162
|
+
if (!size) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
const tiles = getAdjacencyTiles(game, playerData, technoRules, minSpace);
|
|
166
|
+
const tileDistances = getTileDistances(startPoint, tiles);
|
|
167
|
+
|
|
168
|
+
for (let tileDistance of tileDistances) {
|
|
169
|
+
if (tileDistance.tile && game.canPlaceBuilding(playerData.name, technoRules.name, tileDistance.tile)) {
|
|
170
|
+
return tileDistance.tile;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Priority 0 = don't build.
|
|
177
|
+
export type TechnoRulesWithPriority = { unit: TechnoRules; priority: number };
|
|
178
|
+
|
|
179
|
+
export const DEFAULT_BUILDING_PRIORITY = 1;
|
|
180
|
+
|
|
181
|
+
export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
182
|
+
// Allied
|
|
183
|
+
["GAPOWR", new PowerPlant()],
|
|
184
|
+
["GAREFN", new ResourceCollectionBuilding(10, 3)], // Refinery
|
|
185
|
+
["GAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
186
|
+
["GAPILE", new BasicBuilding(12, 1)], // Barracks
|
|
187
|
+
["CMIN", new Harvester(15, 4, 2)], // Chrono Miner
|
|
188
|
+
["ENGINEER", new BasicBuilding(10, 1, 1000)], // Engineer
|
|
189
|
+
["GADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
190
|
+
["GAAIRC", new BasicBuilding(10, 1, 500)], // Airforce Command
|
|
191
|
+
|
|
192
|
+
["GATECH", new BasicBuilding(20, 1, 4000)], // Allied Battle Lab
|
|
193
|
+
["GAYARD", new BasicBuilding(0, 0, 0)], // Naval Yard, disabled
|
|
194
|
+
|
|
195
|
+
["GAPILL", new AntiGroundStaticDefence(5, 1, 5)], // Pillbox
|
|
196
|
+
["ATESLA", new AntiGroundStaticDefence(5, 1, 10)], // Prism Cannon
|
|
197
|
+
["GAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
198
|
+
|
|
199
|
+
["E1", new BasicGroundUnit(2, 3, 0.25, 0)], // GI
|
|
200
|
+
["MTNK", new BasicGroundUnit(10, 3, 2, 0)], // Grizzly Tank
|
|
201
|
+
["MGTK", new BasicGroundUnit(10, 1, 2.5, 0)], // Mirage Tank
|
|
202
|
+
["FV", new BasicGroundUnit(5, 2, 0.5, 1)], // IFV
|
|
203
|
+
["JUMPJET", new BasicAirUnit(10, 1, 1, 1)], // Rocketeer
|
|
204
|
+
["ORCA", new BasicAirUnit(7, 1, 2, 0)], // Rocketeer
|
|
205
|
+
["SREF", new ArtilleryUnit(10, 5, 3, 3)], // Prism Tank
|
|
206
|
+
["CLEG", new BasicGroundUnit(0, 0)], // Chrono Legionnaire (Disabled - we don't handle the warped out phase properly and it tends to bug both bots out)
|
|
207
|
+
["SHAD", new BasicGroundUnit(0, 0)], // Nighthawk (Disabled)
|
|
208
|
+
|
|
209
|
+
// Soviet
|
|
210
|
+
["NAPOWR", new PowerPlant()],
|
|
211
|
+
["NAREFN", new ResourceCollectionBuilding(10, 3)], // Refinery
|
|
212
|
+
["NAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
213
|
+
["NAHAND", new BasicBuilding(12, 1)], // Barracks
|
|
214
|
+
["HARV", new Harvester(15, 4, 2)], // War Miner
|
|
215
|
+
["SENGINEER", new BasicBuilding(10, 1, 1000)], // Soviet Engineer
|
|
216
|
+
["NADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
217
|
+
["NARADR", new BasicBuilding(10, 1, 500)], // Radar
|
|
218
|
+
["NANRCT", new PowerPlant()], // Nuclear Reactor
|
|
219
|
+
["NAYARD", new BasicBuilding(0, 0, 0)], // Naval Yard, disabled
|
|
220
|
+
|
|
221
|
+
["NATECH", new BasicBuilding(20, 1, 4000)], // Soviet Battle Lab
|
|
222
|
+
|
|
223
|
+
["NALASR", new AntiGroundStaticDefence(5, 1, 5)], // Sentry Gun
|
|
224
|
+
["TESLA", new AntiGroundStaticDefence(5, 1, 10)], // Tesla Coil
|
|
225
|
+
["NAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
226
|
+
|
|
227
|
+
["E2", new BasicGroundUnit(2, 3, 0.25, 0)], // Conscript
|
|
228
|
+
["HTNK", new BasicGroundUnit(10, 3, 3, 0)], // Rhino Tank
|
|
229
|
+
["APOC", new BasicGroundUnit(6, 1, 5, 0)], // Apocalypse Tank
|
|
230
|
+
["HTK", new BasicGroundUnit(5, 2, 0.33, 1.5)], // Flak Track
|
|
231
|
+
["ZEP", new BasicAirUnit(5, 1, 5, 1)], // Kirov
|
|
232
|
+
["V3", new ArtilleryUnit(9, 10, 0, 3)], // V3 Rocket Launcher
|
|
233
|
+
]);
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
-
import { AiBuildingRules, getDefaultPlacementLocation } from "./
|
|
3
|
-
import { GlobalThreat } from "../threat/threat.js";
|
|
4
|
-
|
|
5
|
-
export class PowerPlant implements AiBuildingRules {
|
|
6
|
-
getPlacementLocation(
|
|
7
|
-
game: GameApi,
|
|
8
|
-
playerData: PlayerData,
|
|
9
|
-
technoRules: TechnoRules
|
|
10
|
-
): { rx: number; ry: number } | undefined {
|
|
11
|
-
return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
getPriority(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
|
|
15
|
-
if (playerData.power.total < playerData.power.drain) {
|
|
16
|
-
return 100;
|
|
17
|
-
} else if (playerData.power.total < playerData.power.drain + technoRules.power / 2) {
|
|
18
|
-
return 20;
|
|
19
|
-
} else {
|
|
20
|
-
return 0;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
getMaxCount(
|
|
25
|
-
game: GameApi,
|
|
26
|
-
playerData: PlayerData,
|
|
27
|
-
technoRules: TechnoRules,
|
|
28
|
-
threatCache: GlobalThreat | null
|
|
29
|
-
): number | null {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
import { GameApi, PlayerData, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
+
import { AiBuildingRules, getDefaultPlacementLocation } from "./buildingRules.js";
|
|
3
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
4
|
+
|
|
5
|
+
export class PowerPlant implements AiBuildingRules {
|
|
6
|
+
getPlacementLocation(
|
|
7
|
+
game: GameApi,
|
|
8
|
+
playerData: PlayerData,
|
|
9
|
+
technoRules: TechnoRules
|
|
10
|
+
): { rx: number; ry: number } | undefined {
|
|
11
|
+
return getDefaultPlacementLocation(game, playerData, playerData.startLocation, technoRules);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getPriority(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
|
|
15
|
+
if (playerData.power.total < playerData.power.drain) {
|
|
16
|
+
return 100;
|
|
17
|
+
} else if (playerData.power.total < playerData.power.drain + technoRules.power / 2) {
|
|
18
|
+
return 20;
|
|
19
|
+
} else {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getMaxCount(
|
|
25
|
+
game: GameApi,
|
|
26
|
+
playerData: PlayerData,
|
|
27
|
+
technoRules: TechnoRules,
|
|
28
|
+
threatCache: GlobalThreat | null
|
|
29
|
+
): number | null {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|