@supalosa/chronodivide-bot 0.1.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/.prettierrc +5 -0
- package/README.md +46 -0
- package/dist/bot/bot.js +269 -0
- package/dist/bot/logic/building/ArtilleryUnit.js +24 -0
- package/dist/bot/logic/building/antiGroundStaticDefence.js +40 -0
- package/dist/bot/logic/building/basicAirUnit.js +39 -0
- package/dist/bot/logic/building/basicBuilding.js +25 -0
- package/dist/bot/logic/building/basicGroundUnit.js +57 -0
- package/dist/bot/logic/building/building.js +77 -0
- package/dist/bot/logic/building/harvester.js +15 -0
- package/dist/bot/logic/building/massedAntiGroundUnit.js +20 -0
- package/dist/bot/logic/building/powerPlant.js +20 -0
- package/dist/bot/logic/building/queueController.js +168 -0
- package/dist/bot/logic/building/queues.js +19 -0
- package/dist/bot/logic/building/resourceCollectionBuilding.js +34 -0
- package/dist/bot/logic/map/map.js +57 -0
- package/dist/bot/logic/map/sector.js +104 -0
- package/dist/bot/logic/mission/basicMission.js +30 -0
- package/dist/bot/logic/mission/expansionMission.js +14 -0
- package/dist/bot/logic/mission/mission.js +2 -0
- package/dist/bot/logic/mission/missionController.js +47 -0
- package/dist/bot/logic/squad/behaviours/squadExpansion.js +18 -0
- package/dist/bot/logic/squad/behaviours/squadScouters.js +8 -0
- package/dist/bot/logic/squad/squad.js +73 -0
- package/dist/bot/logic/squad/squadBehaviour.js +5 -0
- package/dist/bot/logic/squad/squadController.js +58 -0
- package/dist/bot/logic/threat/threat.js +22 -0
- package/dist/bot/logic/threat/threatCalculator.js +72 -0
- package/dist/exampleBot.js +38 -0
- package/package.json +24 -0
- package/rules.ini +23126 -0
- package/src/bot/bot.ts +378 -0
- package/src/bot/logic/building/ArtilleryUnit.ts +43 -0
- package/src/bot/logic/building/antiGroundStaticDefence.ts +60 -0
- package/src/bot/logic/building/basicAirUnit.ts +68 -0
- package/src/bot/logic/building/basicBuilding.ts +47 -0
- package/src/bot/logic/building/basicGroundUnit.ts +78 -0
- package/src/bot/logic/building/building.ts +120 -0
- package/src/bot/logic/building/harvester.ts +27 -0
- package/src/bot/logic/building/powerPlant.ts +32 -0
- package/src/bot/logic/building/queueController.ts +255 -0
- package/src/bot/logic/building/resourceCollectionBuilding.ts +56 -0
- package/src/bot/logic/map/map.ts +76 -0
- package/src/bot/logic/map/sector.ts +130 -0
- package/src/bot/logic/mission/basicMission.ts +42 -0
- package/src/bot/logic/mission/expansionMission.ts +25 -0
- package/src/bot/logic/mission/mission.ts +47 -0
- package/src/bot/logic/mission/missionController.ts +51 -0
- package/src/bot/logic/squad/behaviours/squadExpansion.ts +33 -0
- package/src/bot/logic/squad/squad.ts +97 -0
- package/src/bot/logic/squad/squadBehaviour.ts +43 -0
- package/src/bot/logic/squad/squadController.ts +66 -0
- package/src/bot/logic/threat/threat.ts +15 -0
- package/src/bot/logic/threat/threatCalculator.ts +99 -0
- package/src/exampleBot.ts +44 -0
- package/tsconfig.json +73 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { MovementZone, ObjectType } from "@chronodivide/game-api";
|
|
2
|
+
import { GlobalThreat } from "./threat.js";
|
|
3
|
+
export function calculateGlobalThreat(game, playerData, visibleAreaPercent) {
|
|
4
|
+
let groundUnits = game.getVisibleUnits(playerData.name, "hostile", (r) => r.type == ObjectType.Vehicle || r.type == ObjectType.Infantry);
|
|
5
|
+
let airUnits = game.getVisibleUnits(playerData.name, "hostile", (r) => r.movementZone == MovementZone.Fly);
|
|
6
|
+
let groundDefence = game
|
|
7
|
+
.getVisibleUnits(playerData.name, "hostile", (r) => r.type == ObjectType.Building)
|
|
8
|
+
.filter((unitId) => isAntiGround(game, unitId));
|
|
9
|
+
let antiAirPower = game
|
|
10
|
+
.getVisibleUnits(playerData.name, "hostile", (r) => r.type != ObjectType.Building)
|
|
11
|
+
.filter((unitId) => isAntiAir(game, unitId));
|
|
12
|
+
let ourAntiGroundUnits = game
|
|
13
|
+
.getVisibleUnits(playerData.name, "self", (r) => r.isSelectableCombatant)
|
|
14
|
+
.filter((unitId) => isAntiGround(game, unitId));
|
|
15
|
+
let ourAntiAirUnits = game
|
|
16
|
+
.getVisibleUnits(playerData.name, "self", (r) => r.isSelectableCombatant)
|
|
17
|
+
.filter((unitId) => isAntiAir(game, unitId));
|
|
18
|
+
let ourGroundDefence = game
|
|
19
|
+
.getVisibleUnits(playerData.name, "self", (r) => r.type == ObjectType.Building)
|
|
20
|
+
.filter((unitId) => isAntiGround(game, unitId));
|
|
21
|
+
let ourAirUnits = game.getVisibleUnits(playerData.name, "self", (r) => r.movementZone == MovementZone.Fly && r.isSelectableCombatant);
|
|
22
|
+
let observedGroundThreat = calculateFirepowerForUnits(game, groundUnits);
|
|
23
|
+
let observedAirThreat = calculateFirepowerForUnits(game, airUnits);
|
|
24
|
+
let observedAntiAirThreat = calculateFirepowerForUnits(game, antiAirPower);
|
|
25
|
+
let observedGroundDefence = calculateFirepowerForUnits(game, groundDefence);
|
|
26
|
+
let ourAntiGroundPower = calculateFirepowerForUnits(game, ourAntiGroundUnits);
|
|
27
|
+
let ourAntiAirPower = calculateFirepowerForUnits(game, ourAntiAirUnits);
|
|
28
|
+
let ourAirPower = calculateFirepowerForUnits(game, ourAirUnits);
|
|
29
|
+
let ourGroundDefencePower = calculateFirepowerForUnits(game, ourGroundDefence);
|
|
30
|
+
return new GlobalThreat(visibleAreaPercent, observedGroundThreat, observedAirThreat, observedAntiAirThreat, observedGroundDefence * 0.25, ourGroundDefencePower * 0.25, ourAntiGroundPower, ourAntiAirPower, ourAirPower);
|
|
31
|
+
}
|
|
32
|
+
function isAntiGround(gameApi, unitId) {
|
|
33
|
+
let unit = gameApi.getUnitData(unitId);
|
|
34
|
+
if (unit && unit.primaryWeapon) {
|
|
35
|
+
return unit.primaryWeapon.projectileRules.isAntiGround;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function isAntiAir(gameApi, unitId) {
|
|
40
|
+
let unit = gameApi.getUnitData(unitId);
|
|
41
|
+
if (unit && unit.primaryWeapon) {
|
|
42
|
+
return unit.primaryWeapon.projectileRules.isAntiAir;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function calculateFirepowerForUnit(unitData) {
|
|
47
|
+
let threat = 0;
|
|
48
|
+
let hpRatio = unitData.hitPoints / Math.max(1, unitData.maxHitPoints);
|
|
49
|
+
if (unitData.primaryWeapon) {
|
|
50
|
+
threat +=
|
|
51
|
+
(hpRatio *
|
|
52
|
+
((unitData.primaryWeapon.rules.damage + 1) * Math.sqrt(unitData.primaryWeapon.rules.range + 1))) /
|
|
53
|
+
Math.max(unitData.primaryWeapon.cooldownTicks, 1);
|
|
54
|
+
}
|
|
55
|
+
if (unitData.secondaryWeapon) {
|
|
56
|
+
threat +=
|
|
57
|
+
(hpRatio *
|
|
58
|
+
((unitData.secondaryWeapon.rules.damage + 1) * Math.sqrt(unitData.secondaryWeapon.rules.range + 1))) /
|
|
59
|
+
Math.max(unitData.secondaryWeapon.cooldownTicks, 1);
|
|
60
|
+
}
|
|
61
|
+
return Math.min(800, threat);
|
|
62
|
+
}
|
|
63
|
+
function calculateFirepowerForUnits(game, unitIds) {
|
|
64
|
+
let threat = 0;
|
|
65
|
+
unitIds.forEach((unitId) => {
|
|
66
|
+
let unitData = game.getUnitData(unitId);
|
|
67
|
+
if (unitData) {
|
|
68
|
+
threat += calculateFirepowerForUnit(unitData);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return threat;
|
|
72
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cdapi } from "@chronodivide/game-api";
|
|
2
|
+
import { ExampleBot } from "./bot/bot.js";
|
|
3
|
+
async function main() {
|
|
4
|
+
const mapName = "mp03t4.map";
|
|
5
|
+
// Bot names must be unique in online mode
|
|
6
|
+
const botName = `Joe${String(Date.now()).substr(-6)}`;
|
|
7
|
+
const otherBotName = `Bob${String(Date.now() + 1).substr(-6)}`;
|
|
8
|
+
await cdapi.init(process.env.MIX_DIR || "./");
|
|
9
|
+
console.log("Server URL: " + process.env.SERVER_URL);
|
|
10
|
+
console.log("Client URL: " + process.env.CLIENT_URL);
|
|
11
|
+
const game = await cdapi.createGame({
|
|
12
|
+
// Uncomment the following lines to play in real time versus the bot
|
|
13
|
+
online: true,
|
|
14
|
+
serverUrl: process.env.SERVER_URL,
|
|
15
|
+
clientUrl: process.env.CLIENT_URL,
|
|
16
|
+
agents: [new ExampleBot(botName, "Americans"), { name: otherBotName, country: "French" }],
|
|
17
|
+
//agents: [new ExampleBot(botName, "Russians", false), new ExampleBot(otherBotName, "Americans", true)],
|
|
18
|
+
buildOffAlly: false,
|
|
19
|
+
cratesAppear: false,
|
|
20
|
+
credits: 10000,
|
|
21
|
+
gameMode: cdapi.getAvailableGameModes(mapName)[0],
|
|
22
|
+
gameSpeed: 6,
|
|
23
|
+
mapName,
|
|
24
|
+
mcvRepacks: true,
|
|
25
|
+
shortGame: true,
|
|
26
|
+
superWeapons: false,
|
|
27
|
+
unitCount: 0,
|
|
28
|
+
});
|
|
29
|
+
while (!game.isFinished()) {
|
|
30
|
+
await game.update();
|
|
31
|
+
}
|
|
32
|
+
game.saveReplay();
|
|
33
|
+
game.dispose();
|
|
34
|
+
}
|
|
35
|
+
main().catch((e) => {
|
|
36
|
+
console.error(e);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@supalosa/chronodivide-bot",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Example bot for Chrono Divide",
|
|
5
|
+
"main": "dist/exampleBot.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc -p .",
|
|
9
|
+
"watch": "tsc -p . -w",
|
|
10
|
+
"start": "node . --es-module-specifier-resolution=node",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"license": "UNLICENSED",
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^14.17.32",
|
|
16
|
+
"typescript": "^4.3.5"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@chronodivide/game-api": "^0.43.0",
|
|
20
|
+
"@types/luxon": "^3.3.2",
|
|
21
|
+
"luxon": "^3.4.3",
|
|
22
|
+
"priority-queue-typescript": "^1.0.1"
|
|
23
|
+
}
|
|
24
|
+
}
|