@supalosa/chronodivide-bot 0.1.1 → 0.2.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 +71 -46
- package/dist/bot/bot.js +27 -183
- package/dist/bot/logic/awareness.js +122 -0
- package/dist/bot/logic/building/basicGroundUnit.js +8 -6
- package/dist/bot/logic/building/building.js +6 -3
- package/dist/bot/logic/building/harvester.js +1 -1
- package/dist/bot/logic/building/queueController.js +4 -21
- package/dist/bot/logic/common/scout.js +10 -0
- package/dist/bot/logic/knowledge.js +1 -0
- package/dist/bot/logic/map/map.js +6 -0
- package/dist/bot/logic/map/sector.js +6 -1
- package/dist/bot/logic/mission/basicMission.js +1 -5
- package/dist/bot/logic/mission/expansionMission.js +22 -4
- package/dist/bot/logic/mission/mission.js +49 -2
- package/dist/bot/logic/mission/missionController.js +67 -34
- package/dist/bot/logic/mission/missionFactories.js +10 -0
- package/dist/bot/logic/mission/missions/attackMission.js +109 -0
- package/dist/bot/logic/mission/missions/defenceMission.js +62 -0
- package/dist/bot/logic/mission/missions/expansionMission.js +24 -0
- package/dist/bot/logic/mission/missions/oneTimeMission.js +26 -0
- package/dist/bot/logic/mission/missions/retreatMission.js +7 -0
- package/dist/bot/logic/mission/missions/scoutingMission.js +38 -0
- package/dist/bot/logic/squad/behaviours/attackSquad.js +82 -0
- package/dist/bot/logic/squad/behaviours/combatSquad.js +99 -0
- package/dist/bot/logic/squad/behaviours/common.js +37 -0
- package/dist/bot/logic/squad/behaviours/defenceSquad.js +48 -0
- package/dist/bot/logic/squad/behaviours/expansionSquad.js +42 -0
- package/dist/bot/logic/squad/behaviours/retreatSquad.js +32 -0
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +38 -0
- package/dist/bot/logic/squad/behaviours/squadExpansion.js +26 -13
- package/dist/bot/logic/squad/squad.js +68 -15
- package/dist/bot/logic/squad/squadBehaviour.js +5 -5
- package/dist/bot/logic/squad/squadBehaviours.js +6 -0
- package/dist/bot/logic/squad/squadController.js +106 -15
- package/dist/exampleBot.js +22 -7
- package/package.json +29 -24
- package/src/bot/bot.ts +178 -378
- package/src/bot/logic/awareness.ts +220 -0
- package/src/bot/logic/building/ArtilleryUnit.ts +2 -2
- package/src/bot/logic/building/antiGroundStaticDefence.ts +2 -2
- package/src/bot/logic/building/basicAirUnit.ts +2 -2
- package/src/bot/logic/building/basicBuilding.ts +2 -2
- package/src/bot/logic/building/basicGroundUnit.ts +83 -78
- package/src/bot/logic/building/building.ts +125 -120
- package/src/bot/logic/building/harvester.ts +27 -27
- package/src/bot/logic/building/powerPlant.ts +1 -1
- package/src/bot/logic/building/queueController.ts +17 -38
- package/src/bot/logic/building/resourceCollectionBuilding.ts +1 -1
- package/src/bot/logic/common/scout.ts +12 -0
- package/src/bot/logic/map/map.ts +11 -3
- package/src/bot/logic/map/sector.ts +136 -130
- package/src/bot/logic/mission/mission.ts +83 -47
- package/src/bot/logic/mission/missionController.ts +103 -51
- package/src/bot/logic/mission/missionFactories.ts +46 -0
- package/src/bot/logic/mission/missions/attackMission.ts +152 -0
- package/src/bot/logic/mission/missions/defenceMission.ts +104 -0
- package/src/bot/logic/mission/missions/expansionMission.ts +49 -0
- package/src/bot/logic/mission/missions/oneTimeMission.ts +32 -0
- package/src/bot/logic/mission/missions/retreatMission.ts +9 -0
- package/src/bot/logic/mission/missions/scoutingMission.ts +59 -0
- package/src/bot/logic/squad/behaviours/combatSquad.ts +125 -0
- package/src/bot/logic/squad/behaviours/common.ts +37 -0
- package/src/bot/logic/squad/behaviours/expansionSquad.ts +59 -0
- package/src/bot/logic/squad/behaviours/retreatSquad.ts +46 -0
- package/src/bot/logic/squad/behaviours/scoutingSquad.ts +56 -0
- package/src/bot/logic/squad/squad.ts +163 -97
- package/src/bot/logic/squad/squadBehaviour.ts +61 -43
- package/src/bot/logic/squad/squadBehaviours.ts +8 -0
- package/src/bot/logic/squad/squadController.ts +190 -66
- package/src/exampleBot.ts +19 -4
- package/tsconfig.json +1 -1
- package/src/bot/logic/mission/basicMission.ts +0 -42
- package/src/bot/logic/mission/expansionMission.ts +0 -25
- package/src/bot/logic/squad/behaviours/squadExpansion.ts +0 -33
|
@@ -1,120 +1,125 @@
|
|
|
1
|
-
import { BuildingPlacementData, GameApi, PlayerData, Point2D, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
-
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
-
import { AntiGroundStaticDefence } from "./antiGroundStaticDefence.js";
|
|
4
|
-
import { ArtilleryUnit } from "./ArtilleryUnit.js";
|
|
5
|
-
import { BasicAirUnit } from "./basicAirUnit.js";
|
|
6
|
-
import { BasicBuilding } from "./basicBuilding.js";
|
|
7
|
-
import { BasicGroundUnit } from "./basicGroundUnit.js";
|
|
8
|
-
import { PowerPlant } from "./powerPlant.js";
|
|
9
|
-
import { ResourceCollectionBuilding } from "./resourceCollectionBuilding.js";
|
|
10
|
-
import { Harvester } from "./harvester.js";
|
|
11
|
-
|
|
12
|
-
export interface AiBuildingRules {
|
|
13
|
-
getPriority(
|
|
14
|
-
game: GameApi,
|
|
15
|
-
playerData: PlayerData,
|
|
16
|
-
technoRules: TechnoRules,
|
|
17
|
-
threatCache: GlobalThreat |
|
|
18
|
-
): number;
|
|
19
|
-
|
|
20
|
-
getPlacementLocation(
|
|
21
|
-
game: GameApi,
|
|
22
|
-
playerData: PlayerData,
|
|
23
|
-
technoRules: TechnoRules
|
|
24
|
-
): { rx: number; ry: number } | undefined;
|
|
25
|
-
|
|
26
|
-
getMaxCount(
|
|
27
|
-
game: GameApi,
|
|
28
|
-
playerData: PlayerData,
|
|
29
|
-
technoRules: TechnoRules,
|
|
30
|
-
threatCache: GlobalThreat |
|
|
31
|
-
): number | null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function numBuildingsOwnedOfType(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
|
|
35
|
-
return game.getVisibleUnits(playerData.name, "self", (r) => r == technoRules).length;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function numBuildingsOwnedOfName(game: GameApi, playerData: PlayerData, name: string): number {
|
|
39
|
-
return game.getVisibleUnits(playerData.name, "self", (r) => r.name === name).length;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function getDefaultPlacementLocation(
|
|
43
|
-
game: GameApi,
|
|
44
|
-
playerData: PlayerData,
|
|
45
|
-
startPoint: Point2D,
|
|
46
|
-
technoRules: TechnoRules,
|
|
47
|
-
space: number = 1
|
|
48
|
-
): { rx: number; ry: number } | undefined {
|
|
49
|
-
// Random location, preferably near start location.
|
|
50
|
-
let startX = startPoint.x;
|
|
51
|
-
let startY = startPoint.y;
|
|
52
|
-
let size: BuildingPlacementData = game.getBuildingPlacementData(technoRules.name);
|
|
53
|
-
if (!size) {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
let largestSize = Math.max(size.foundation.height, size.foundation.width);
|
|
57
|
-
for (let searchRadius = largestSize; searchRadius < 25 + largestSize; ++searchRadius) {
|
|
58
|
-
for (let xx = startX - searchRadius; xx < startX + searchRadius; ++xx) {
|
|
59
|
-
for (let yy = startY - searchRadius; yy < startY + searchRadius; ++yy) {
|
|
60
|
-
let tile = game.mapApi.getTile(xx, yy);
|
|
61
|
-
if (tile && game.canPlaceBuilding(playerData.name, technoRules.name, tile)) {
|
|
62
|
-
return { rx: xx, ry: yy };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
["
|
|
79
|
-
["
|
|
80
|
-
["
|
|
81
|
-
["
|
|
82
|
-
["
|
|
83
|
-
["
|
|
84
|
-
["
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
["
|
|
89
|
-
["
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
["
|
|
93
|
-
["
|
|
94
|
-
["
|
|
95
|
-
["
|
|
96
|
-
["
|
|
97
|
-
["
|
|
98
|
-
["
|
|
99
|
-
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
["
|
|
104
|
-
["
|
|
105
|
-
["
|
|
106
|
-
["
|
|
107
|
-
["
|
|
108
|
-
["
|
|
109
|
-
|
|
110
|
-
["
|
|
111
|
-
["
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
["
|
|
116
|
-
["
|
|
117
|
-
["
|
|
118
|
-
|
|
119
|
-
["
|
|
120
|
-
]
|
|
1
|
+
import { BuildingPlacementData, GameApi, PlayerData, Point2D, TechnoRules } from "@chronodivide/game-api";
|
|
2
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
+
import { AntiGroundStaticDefence } from "./antiGroundStaticDefence.js";
|
|
4
|
+
import { ArtilleryUnit } from "./ArtilleryUnit.js";
|
|
5
|
+
import { BasicAirUnit } from "./basicAirUnit.js";
|
|
6
|
+
import { BasicBuilding } from "./basicBuilding.js";
|
|
7
|
+
import { BasicGroundUnit } from "./basicGroundUnit.js";
|
|
8
|
+
import { PowerPlant } from "./powerPlant.js";
|
|
9
|
+
import { ResourceCollectionBuilding } from "./resourceCollectionBuilding.js";
|
|
10
|
+
import { Harvester } from "./harvester.js";
|
|
11
|
+
|
|
12
|
+
export interface AiBuildingRules {
|
|
13
|
+
getPriority(
|
|
14
|
+
game: GameApi,
|
|
15
|
+
playerData: PlayerData,
|
|
16
|
+
technoRules: TechnoRules,
|
|
17
|
+
threatCache: GlobalThreat | null,
|
|
18
|
+
): number;
|
|
19
|
+
|
|
20
|
+
getPlacementLocation(
|
|
21
|
+
game: GameApi,
|
|
22
|
+
playerData: PlayerData,
|
|
23
|
+
technoRules: TechnoRules,
|
|
24
|
+
): { rx: number; ry: number } | undefined;
|
|
25
|
+
|
|
26
|
+
getMaxCount(
|
|
27
|
+
game: GameApi,
|
|
28
|
+
playerData: PlayerData,
|
|
29
|
+
technoRules: TechnoRules,
|
|
30
|
+
threatCache: GlobalThreat | null,
|
|
31
|
+
): number | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function numBuildingsOwnedOfType(game: GameApi, playerData: PlayerData, technoRules: TechnoRules): number {
|
|
35
|
+
return game.getVisibleUnits(playerData.name, "self", (r) => r == technoRules).length;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function numBuildingsOwnedOfName(game: GameApi, playerData: PlayerData, name: string): number {
|
|
39
|
+
return game.getVisibleUnits(playerData.name, "self", (r) => r.name === name).length;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getDefaultPlacementLocation(
|
|
43
|
+
game: GameApi,
|
|
44
|
+
playerData: PlayerData,
|
|
45
|
+
startPoint: Point2D,
|
|
46
|
+
technoRules: TechnoRules,
|
|
47
|
+
space: number = 1,
|
|
48
|
+
): { rx: number; ry: number } | undefined {
|
|
49
|
+
// Random location, preferably near start location.
|
|
50
|
+
let startX = startPoint.x;
|
|
51
|
+
let startY = startPoint.y;
|
|
52
|
+
let size: BuildingPlacementData = game.getBuildingPlacementData(technoRules.name);
|
|
53
|
+
if (!size) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
let largestSize = Math.max(size.foundation.height, size.foundation.width);
|
|
57
|
+
for (let searchRadius = largestSize; searchRadius < 25 + largestSize; ++searchRadius) {
|
|
58
|
+
for (let xx = startX - searchRadius; xx < startX + searchRadius; ++xx) {
|
|
59
|
+
for (let yy = startY - searchRadius; yy < startY + searchRadius; ++yy) {
|
|
60
|
+
let tile = game.mapApi.getTile(xx, yy);
|
|
61
|
+
if (tile && game.canPlaceBuilding(playerData.name, technoRules.name, tile)) {
|
|
62
|
+
return { rx: xx, ry: yy };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Priority 0 = don't build.
|
|
71
|
+
export type TechnoRulesWithPriority = { unit: TechnoRules; priority: number };
|
|
72
|
+
|
|
73
|
+
export const DEFAULT_BUILDING_PRIORITY = 1;
|
|
74
|
+
|
|
75
|
+
export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
76
|
+
// Allied
|
|
77
|
+
["GAPOWR", new PowerPlant()],
|
|
78
|
+
["GAREFN", new ResourceCollectionBuilding(10, 3)], // Refinery
|
|
79
|
+
["GAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
80
|
+
["GAPILE", new BasicBuilding(12, 1)], // Barracks
|
|
81
|
+
["CMIN", new Harvester(15, 4, 2)], // Chrono Miner
|
|
82
|
+
["ENGINEER", new BasicBuilding(1, 1, 10000)], // Engineer
|
|
83
|
+
["GADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
84
|
+
["GAAIRC", new BasicBuilding(8, 1, 6000)], // Airforce Command
|
|
85
|
+
|
|
86
|
+
["GATECH", new BasicBuilding(20, 1, 4000)], // Allied Battle Lab
|
|
87
|
+
|
|
88
|
+
["GAPILL", new AntiGroundStaticDefence(5, 1, 5)], // Pillbox
|
|
89
|
+
["ATESLA", new AntiGroundStaticDefence(5, 1, 10)], // Prism Cannon
|
|
90
|
+
["GAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
91
|
+
|
|
92
|
+
["E1", new BasicGroundUnit(2, 3, 0.25, 0)], // GI
|
|
93
|
+
["MTNK", new BasicGroundUnit(10, 3, 2, 0)], // Grizzly Tank
|
|
94
|
+
["MGTK", new BasicGroundUnit(10, 1, 2.5, 0)], // Mirage Tank
|
|
95
|
+
["FV", new BasicGroundUnit(5, 2, 0.5, 1)], // IFV
|
|
96
|
+
["JUMPJET", new BasicAirUnit(10, 1, 1, 1)], // Rocketeer
|
|
97
|
+
["ORCA", new BasicAirUnit(7, 1, 2, 0)], // Rocketeer
|
|
98
|
+
["SREF", new ArtilleryUnit(9, 1)], // Prism Tank
|
|
99
|
+
["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)
|
|
100
|
+
["SHAD", new BasicGroundUnit(0, 0)], // Nighthawk (Disabled)
|
|
101
|
+
|
|
102
|
+
// Soviet
|
|
103
|
+
["NAPOWR", new PowerPlant()],
|
|
104
|
+
["NAREFN", new ResourceCollectionBuilding(10, 3)], // Refinery
|
|
105
|
+
["NAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
106
|
+
["NAHAND", new BasicBuilding(12, 1)], // Barracks
|
|
107
|
+
["HARV", new Harvester(15, 4, 2)], // War Miner
|
|
108
|
+
["SENGINEER", new BasicBuilding(1, 1, 10000)], // Soviet Engineer
|
|
109
|
+
["NADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
110
|
+
["NARADR", new BasicBuilding(8, 1, 4000)], // Radar
|
|
111
|
+
["NANRCT", new PowerPlant()], // Nuclear Reactor
|
|
112
|
+
|
|
113
|
+
["NATECH", new BasicBuilding(20, 1, 4000)], // Soviet Battle Lab
|
|
114
|
+
|
|
115
|
+
["NALASR", new AntiGroundStaticDefence(5, 1, 5)], // Sentry Gun
|
|
116
|
+
["TESLA", new AntiGroundStaticDefence(5, 1, 10)], // Tesla Coil
|
|
117
|
+
["NAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
118
|
+
|
|
119
|
+
["E2", new BasicGroundUnit(2, 3, 0.25, 0)], // Conscript
|
|
120
|
+
["HTNK", new BasicGroundUnit(10, 3, 3, 0)], // Rhino Tank
|
|
121
|
+
["APOC", new BasicGroundUnit(6, 1, 5, 0)], // Apocalypse Tank
|
|
122
|
+
["HTK", new BasicGroundUnit(5, 2, 0.33, 1.5)], // Flak Track
|
|
123
|
+
["ZEP", new BasicAirUnit(5, 1, 5, 1)], // Kirov
|
|
124
|
+
["V3", new ArtilleryUnit(9, 1)], // V3 Rocket Launcher
|
|
125
|
+
]);
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { GameApi, PlayerData, Point2D, TechnoRules, Tile } from "@chronodivide/game-api";
|
|
2
|
-
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
-
import { BasicBuilding } from "./basicBuilding.js";
|
|
4
|
-
import { BasicGroundUnit } from "./basicGroundUnit.js";
|
|
5
|
-
|
|
6
|
-
const IDEAL_HARVESTERS_PER_REFINERY = 2;
|
|
7
|
-
|
|
8
|
-
export class Harvester extends BasicGroundUnit {
|
|
9
|
-
constructor(basePriority: number, baseAmount: number, private minNeeded: number) {
|
|
10
|
-
super(basePriority, baseAmount, 0, 0);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Priority goes up when we have fewer than this many refineries.
|
|
14
|
-
getPriority(
|
|
15
|
-
game: GameApi,
|
|
16
|
-
playerData: PlayerData,
|
|
17
|
-
technoRules: TechnoRules,
|
|
18
|
-
threatCache: GlobalThreat |
|
|
19
|
-
): number {
|
|
20
|
-
const refineries = game.getVisibleUnits(playerData.name, "self", (r) => r.refinery).length;
|
|
21
|
-
const harvesters = game.getVisibleUnits(playerData.name, "self", (r) => r.harvester).length;
|
|
22
|
-
|
|
23
|
-
const boost = harvesters < this.minNeeded ?
|
|
24
|
-
|
|
25
|
-
return this.basePriority * (refineries / Math.max(harvesters / IDEAL_HARVESTERS_PER_REFINERY, 1)) * boost;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
1
|
+
import { GameApi, PlayerData, Point2D, TechnoRules, Tile } from "@chronodivide/game-api";
|
|
2
|
+
import { GlobalThreat } from "../threat/threat.js";
|
|
3
|
+
import { BasicBuilding } from "./basicBuilding.js";
|
|
4
|
+
import { BasicGroundUnit } from "./basicGroundUnit.js";
|
|
5
|
+
|
|
6
|
+
const IDEAL_HARVESTERS_PER_REFINERY = 2;
|
|
7
|
+
|
|
8
|
+
export class Harvester extends BasicGroundUnit {
|
|
9
|
+
constructor(basePriority: number, baseAmount: number, private minNeeded: number) {
|
|
10
|
+
super(basePriority, baseAmount, 0, 0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Priority goes up when we have fewer than this many refineries.
|
|
14
|
+
getPriority(
|
|
15
|
+
game: GameApi,
|
|
16
|
+
playerData: PlayerData,
|
|
17
|
+
technoRules: TechnoRules,
|
|
18
|
+
threatCache: GlobalThreat | null
|
|
19
|
+
): number {
|
|
20
|
+
const refineries = game.getVisibleUnits(playerData.name, "self", (r) => r.refinery).length;
|
|
21
|
+
const harvesters = game.getVisibleUnits(playerData.name, "self", (r) => r.harvester).length;
|
|
22
|
+
|
|
23
|
+
const boost = harvesters < this.minNeeded ? 3 : 1;
|
|
24
|
+
|
|
25
|
+
return this.basePriority * (refineries / Math.max(harvesters / IDEAL_HARVESTERS_PER_REFINERY, 1)) * boost;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -43,27 +43,18 @@ export const queueTypeToName = (queue: QueueType) => {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
// Repair buildings at this ratio of the maxHitpoints.
|
|
47
|
-
const REPAIR_HITPOINTS_RATIO = 0.9;
|
|
48
|
-
|
|
49
|
-
// Don't repair buildings more often than this.
|
|
50
|
-
const REPAIR_COOLDOWN_TICKS = 15;
|
|
51
|
-
|
|
52
46
|
const DEBUG_BUILD_QUEUES = true;
|
|
53
47
|
|
|
54
48
|
export class QueueController {
|
|
55
|
-
constructor(
|
|
56
|
-
private buildingIdToLastHitpoints: Map<number, number> = new Map(),
|
|
57
|
-
private buildingIdLastRepairedAtTick: Map<number, number> = new Map()
|
|
58
|
-
) {}
|
|
49
|
+
constructor() {}
|
|
59
50
|
|
|
60
51
|
public onAiUpdate(
|
|
61
52
|
game: GameApi,
|
|
62
53
|
productionApi: ProductionApi,
|
|
63
54
|
actionsApi: ActionsApi,
|
|
64
55
|
playerData: PlayerData,
|
|
65
|
-
threatCache: GlobalThreat |
|
|
66
|
-
logger: (message: string) => void
|
|
56
|
+
threatCache: GlobalThreat | null,
|
|
57
|
+
logger: (message: string) => void,
|
|
67
58
|
) {
|
|
68
59
|
const decisions = QUEUES.map((queueType) => {
|
|
69
60
|
const options = productionApi.getAvailableObjects(queueType);
|
|
@@ -90,29 +81,17 @@ export class QueueController {
|
|
|
90
81
|
decision.decision,
|
|
91
82
|
totalWeightAcrossQueues,
|
|
92
83
|
totalCostAcrossQueues,
|
|
93
|
-
logger
|
|
84
|
+
logger,
|
|
94
85
|
);
|
|
95
86
|
});
|
|
96
87
|
|
|
97
88
|
// Repair is simple - just repair everything that's damaged.
|
|
98
|
-
// Unfortunately there doesn't seem to be an API to determine if something is being repaired, so we have to remember it.
|
|
99
89
|
game.getVisibleUnits(playerData.name, "self", (r) => r.repairable).forEach((unitId) => {
|
|
100
90
|
const unit = game.getUnitData(unitId);
|
|
101
|
-
if (!unit || !unit.hitPoints || !unit.maxHitPoints) {
|
|
91
|
+
if (!unit || !unit.hitPoints || !unit.maxHitPoints || unit.hasWrenchRepair) {
|
|
102
92
|
return;
|
|
103
93
|
}
|
|
104
|
-
|
|
105
|
-
const buildingLastRepairedAt = this.buildingIdLastRepairedAtTick.get(unitId) || 0;
|
|
106
|
-
// Only repair if HP is going down and if we haven't recently repaired it
|
|
107
|
-
if (
|
|
108
|
-
unit.hitPoints <= lastKnownHitpoints &&
|
|
109
|
-
game.getCurrentTick() > buildingLastRepairedAt + REPAIR_COOLDOWN_TICKS &&
|
|
110
|
-
unit.hitPoints < unit.maxHitPoints * REPAIR_HITPOINTS_RATIO
|
|
111
|
-
) {
|
|
112
|
-
actionsApi.toggleRepairWrench(unitId);
|
|
113
|
-
this.buildingIdLastRepairedAtTick.set(unitId, game.getCurrentTick());
|
|
114
|
-
}
|
|
115
|
-
this.buildingIdToLastHitpoints.set(unitId, unit.hitPoints);
|
|
94
|
+
actionsApi.toggleRepairWrench(unitId);
|
|
116
95
|
});
|
|
117
96
|
}
|
|
118
97
|
|
|
@@ -121,16 +100,16 @@ export class QueueController {
|
|
|
121
100
|
productionApi: ProductionApi,
|
|
122
101
|
actionsApi: ActionsApi,
|
|
123
102
|
playerData: PlayerData,
|
|
124
|
-
threatCache: GlobalThreat |
|
|
103
|
+
threatCache: GlobalThreat | null,
|
|
125
104
|
queueType: QueueType,
|
|
126
105
|
decision: TechnoRulesWithPriority | undefined,
|
|
127
106
|
totalWeightAcrossQueues: number,
|
|
128
107
|
totalCostAcrossQueues: number,
|
|
129
|
-
logger: (message: string) => void
|
|
108
|
+
logger: (message: string) => void,
|
|
130
109
|
): void {
|
|
131
110
|
const myCredits = playerData.credits;
|
|
132
111
|
|
|
133
|
-
|
|
112
|
+
const queueData = productionApi.getQueueData(queueType);
|
|
134
113
|
if (queueData.status == QueueStatus.Idle) {
|
|
135
114
|
// Start building the decided item.
|
|
136
115
|
if (decision !== undefined) {
|
|
@@ -145,7 +124,7 @@ export class QueueController {
|
|
|
145
124
|
let location: { rx: number; ry: number } | undefined = this.getBestLocationForStructure(
|
|
146
125
|
game,
|
|
147
126
|
playerData,
|
|
148
|
-
objectReady
|
|
127
|
+
objectReady,
|
|
149
128
|
);
|
|
150
129
|
if (location !== undefined) {
|
|
151
130
|
actionsApi.placeBuilding(objectReady.name, location.rx, location.ry);
|
|
@@ -163,7 +142,7 @@ export class QueueController {
|
|
|
163
142
|
logger(
|
|
164
143
|
`Dequeueing queue ${queueTypeToName(queueData.type)} unit ${current.name} because ${
|
|
165
144
|
decision.unit.name
|
|
166
|
-
} has 2x higher priority
|
|
145
|
+
} has 2x higher priority.`,
|
|
167
146
|
);
|
|
168
147
|
actionsApi.unqueueFromProduction(queueData.type, current.name, current.type, 1);
|
|
169
148
|
}
|
|
@@ -173,7 +152,7 @@ export class QueueController {
|
|
|
173
152
|
logger(
|
|
174
153
|
`Pausing queue ${queueTypeToName(queueData.type)} because weight is low (${
|
|
175
154
|
decision.priority
|
|
176
|
-
}/${totalWeightAcrossQueues})
|
|
155
|
+
}/${totalWeightAcrossQueues})`,
|
|
177
156
|
);
|
|
178
157
|
actionsApi.pauseProduction(queueData.type);
|
|
179
158
|
}
|
|
@@ -187,7 +166,7 @@ export class QueueController {
|
|
|
187
166
|
logger(
|
|
188
167
|
`Resuming queue ${queueTypeToName(queueData.type)} because weight is high (${
|
|
189
168
|
decision.priority
|
|
190
|
-
}/${totalWeightAcrossQueues})
|
|
169
|
+
}/${totalWeightAcrossQueues})`,
|
|
191
170
|
);
|
|
192
171
|
actionsApi.resumeProduction(queueData.type);
|
|
193
172
|
}
|
|
@@ -197,9 +176,9 @@ export class QueueController {
|
|
|
197
176
|
private getBestOptionForBuilding(
|
|
198
177
|
game: GameApi,
|
|
199
178
|
options: TechnoRules[],
|
|
200
|
-
threatCache: GlobalThreat |
|
|
179
|
+
threatCache: GlobalThreat | null,
|
|
201
180
|
playerData: PlayerData,
|
|
202
|
-
logger: (message: string) => void
|
|
181
|
+
logger: (message: string) => void,
|
|
203
182
|
): TechnoRulesWithPriority | undefined {
|
|
204
183
|
let priorityQueue: TechnoRulesWithPriority[] = [];
|
|
205
184
|
options.forEach((option) => {
|
|
@@ -226,7 +205,7 @@ export class QueueController {
|
|
|
226
205
|
option: TechnoRules,
|
|
227
206
|
game: GameApi,
|
|
228
207
|
playerStatus: PlayerData,
|
|
229
|
-
threatCache: GlobalThreat |
|
|
208
|
+
threatCache: GlobalThreat | null,
|
|
230
209
|
) {
|
|
231
210
|
if (BUILDING_NAME_TO_RULES.has(option.name)) {
|
|
232
211
|
let logic = BUILDING_NAME_TO_RULES.get(option.name)!;
|
|
@@ -242,7 +221,7 @@ export class QueueController {
|
|
|
242
221
|
private getBestLocationForStructure(
|
|
243
222
|
game: GameApi,
|
|
244
223
|
playerData: PlayerData,
|
|
245
|
-
objectReady: TechnoRules
|
|
224
|
+
objectReady: TechnoRules,
|
|
246
225
|
): { rx: number; ry: number } | undefined {
|
|
247
226
|
if (BUILDING_NAME_TO_RULES.has(objectReady.name)) {
|
|
248
227
|
let logic = BUILDING_NAME_TO_RULES.get(objectReady.name)!;
|
|
@@ -48,7 +48,7 @@ export class ResourceCollectionBuilding extends BasicBuilding {
|
|
|
48
48
|
game: GameApi,
|
|
49
49
|
playerData: PlayerData,
|
|
50
50
|
technoRules: TechnoRules,
|
|
51
|
-
threatCache: GlobalThreat |
|
|
51
|
+
threatCache: GlobalThreat | null
|
|
52
52
|
): number | null {
|
|
53
53
|
const harvesters = game.getVisibleUnits(playerData.name, "self", (r) => r.harvester).length;
|
|
54
54
|
return Math.max(1, harvesters * 2);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GameApi, PlayerData } from "@chronodivide/game-api";
|
|
2
|
+
|
|
3
|
+
export const getUnseenStartingLocations = (gameApi: GameApi, playerData: PlayerData) => {
|
|
4
|
+
const unseenStartingLocations = gameApi.mapApi.getStartingLocations().filter((startingLocation) => {
|
|
5
|
+
if (startingLocation == playerData.startLocation) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
let tile = gameApi.mapApi.getTile(startingLocation.x, startingLocation.y);
|
|
9
|
+
return tile ? !gameApi.mapApi.isVisibleTile(tile, playerData.name) : false;
|
|
10
|
+
});
|
|
11
|
+
return unseenStartingLocations;
|
|
12
|
+
};
|
package/src/bot/logic/map/map.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GameApi, MapApi, PlayerData, Point2D } from "@chronodivide/game-api";
|
|
1
|
+
import { GameApi, MapApi, PlayerData, Point2D, UnitData } from "@chronodivide/game-api";
|
|
2
2
|
|
|
3
3
|
// Expensive one-time call to determine the size of the map.
|
|
4
4
|
// The result is a point just outside the bounds of the map.
|
|
@@ -35,7 +35,7 @@ export function calculateAreaVisibility(
|
|
|
35
35
|
mapApi: MapApi,
|
|
36
36
|
playerData: PlayerData,
|
|
37
37
|
startPoint: Point2D,
|
|
38
|
-
endPoint: Point2D
|
|
38
|
+
endPoint: Point2D
|
|
39
39
|
): { visibleTiles: number; validTiles: number } {
|
|
40
40
|
let validTiles: number = 0,
|
|
41
41
|
visibleTiles: number = 0;
|
|
@@ -60,7 +60,7 @@ export function getPointTowardsOtherPoint(
|
|
|
60
60
|
endLocation: Point2D,
|
|
61
61
|
minRadius: number,
|
|
62
62
|
maxRadius: number,
|
|
63
|
-
randomAngle: number
|
|
63
|
+
randomAngle: number
|
|
64
64
|
): Point2D {
|
|
65
65
|
let radius = minRadius + Math.round(gameApi.generateRandom() * (maxRadius - minRadius));
|
|
66
66
|
let directionToSpawn = Math.atan2(endLocation.y - startLocation.y, endLocation.x - startLocation.x);
|
|
@@ -74,3 +74,11 @@ export function getPointTowardsOtherPoint(
|
|
|
74
74
|
export function getDistanceBetweenPoints(startLocation: Point2D, endLocation: Point2D): number {
|
|
75
75
|
return Math.sqrt((startLocation.x - endLocation.x) ** 2 + (startLocation.y - endLocation.y) ** 2);
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
export function getDistanceBetweenUnits(unit1: UnitData, unit2: UnitData): number {
|
|
79
|
+
return getDistanceBetweenPoints({ x: unit1.tile.rx, y: unit1.tile.ry }, { x: unit2.tile.rx, y: unit2.tile.ry });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function getDistanceBetween(unit: UnitData, point: Point2D): number {
|
|
83
|
+
return getDistanceBetweenPoints({ x: unit.tile.rx, y: unit.tile.ry }, point);
|
|
84
|
+
}
|