Add ESLint, Add worker utils

This commit is contained in:
uwap 2025-12-21 09:48:53 +01:00
parent a64f40b6cc
commit c026087af1
29 changed files with 3042 additions and 1734 deletions

4
.envrc Normal file
View file

@ -0,0 +1,4 @@
if command -v nix-shell &> /dev/null
then
use flake
fi

28
.gitignore vendored
View file

@ -1,3 +1,29 @@
dist/
*.log
database.json
node_modules/
rethinkdb_data/
.idea/
*.iml
.npm/
.gitconfig
.bash_history
.bash_login
static/img/*.png
*.db
*.db-journal
.env
/.next/
/out/
/build
.DS_Store
*.pem
/.pnp
.pnp.js
.env.local
.env.development.local
.env.test.local
.env.production.local
.direnv
tsconfig.tsbuildinfo
/data/
dist/

24
eslint.config.mts Normal file
View file

@ -0,0 +1,24 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { defineConfig } from "eslint/config";
import stylistic from "@stylistic/eslint-plugin";
export default defineConfig([
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
plugins: { js,
"@stylistic": stylistic
},
extends: ["js/recommended"],
languageOptions: { globals: globals.browser },
rules: {
"@stylistic/max-len": ["error", { code: 80, tabWidth: 2 }],
}
},
tseslint.configs.recommended,
stylistic.configs.customize({
quotes: "double",
semi: true,
})
]);

61
flake.lock generated Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1765929117,
"narHash": "sha256-Z1FXlaWWA5Vq6lwiW7bwJFgPLX5vBC0eyiiGLhIAlhY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d2d63347f51ab2867c08a08e0fddb00001930c42",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

19
flake.nix Normal file
View file

@ -0,0 +1,19 @@
{
description = "Screeps Bot Hurricane";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/master";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShell = pkgs.mkShell {
nativeBuildInputs = [ pkgs.bashInteractive ];
buildInputs = with pkgs; [
(yarn.override { nodejs = nodejs_24; }) nodejs_24
nodePackages.typescript
nodePackages.typescript-language-server
];
};
});
}

View file

@ -12,23 +12,27 @@
"push": "yarn build && node ./upload.js"
},
"dependencies": {
"@types/screeps": "^3.3.0"
"@types/screeps": "^3.3.8"
},
"devDependencies": {
"@swc/cli": "^0.1.57",
"@swc/core": "^1.3.24",
"@swc/jest": "^0.2.24",
"@types/jest": "^29.2.5",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.47.0",
"dotenv": "^16.0.3",
"eslint": "^8.30.0",
"husky": "^8.0.0",
"jest": "^29.3.1",
"lodash": "^4.17.21",
"screeps-jest": "^2.0.2",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
"@eslint/js": "^9.39.2",
"@stylistic/eslint-plugin": "^5.6.1",
"@swc/cli": "^0.7.9",
"@swc/core": "^1.15.5",
"@swc/jest": "^0.2.39",
"@types/jest": "^30.0.0",
"@types/node": "^25.0.3",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@typescript-eslint/parser": "^8.50.0",
"dotenv": "^17.2.3",
"eslint": "^9.39.2",
"globals": "^16.5.0",
"husky": "^9.1.7",
"jest": "^30.2.0",
"jiti": "^2.6.1",
"screeps-jest": "^2.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.50.0"
}
}

View file

@ -1,21 +1,21 @@
declare global {
interface CreepMemory {
state?: number
state?: number;
}
}
export interface ChainableAction {
or: (action: Action) => ChainableAction,
and: (action: Action) => ChainableAction,
andThen: (action: Action) => ChainableAction,
repeat: () => void
or: (action: Action) => ChainableAction;
and: (action: Action) => ChainableAction;
andThen: (action: Action) => ChainableAction;
repeat: () => void;
}
export const NoOp: Action = (creep: Creep, state: number = 0) => ({
or: () => NoOp(creep, state),
and: () => NoOp(creep, state),
andThen: () => NoOp(creep, state),
repeat: () => {}
repeat: () => {},
});
export const Success: Action = (creep: Creep, state: number = 0) => ({
@ -27,14 +27,14 @@ export const Success: Action = (creep: Creep, state: number = 0) => ({
},
repeat: () => {
creep.memory.state = 0;
}
},
});
export const InProgress: Action = (creep: Creep, state: number = 0) => ({
or: () => InProgress(creep, state),
and: (action: Action) => action(creep, state),
andThen: () => NoOp(creep, state),
repeat: () => {}
repeat: () => {},
});
export const Fail: Action = (creep: Creep, state: number = 0) => ({
@ -42,19 +42,21 @@ export const Fail: Action = (creep: Creep, state: number = 0) => ({
and: () => NoOp(creep, state),
andThen: () => NoOp(creep, state),
repeat: () => {
console.log('Warning: Last task in series failed for creep ' + creep.name);
}
})
console.log("Warning: Last task in series failed for creep " + creep.name);
},
});
export const createAction = (name: string, action: (creep: Creep) => Action): Action => {
export const createAction
= (name: string, action: (c: Creep) => Action): Action => {
return (creep: Creep, state: number = 0) => {
if ((creep.memory.state ?? 0) > state) {
return Success(creep, state);
}
return action(creep)(creep, state);
}
}
};
};
export const runAction = (creep: Creep, action: Action): ChainableAction => action(creep);
export const runAction = (creep: Creep, action: Action): ChainableAction =>
action(creep);
export type Action = (creep: Creep, state?: number) => ChainableAction;

53
src/Actions/Util.ts Normal file
View file

@ -0,0 +1,53 @@
import { Action, Fail } from "../Actions/Action";
export const notNull
= <T>(x: T | null | undefined, f: (y: T) => Action): Action => {
if (x == null) {
return Fail;
}
return f(x);
};
export const closestTowerToFill = (pos: RoomPosition): StructureTower | null =>
pos.findClosestByRange(FIND_MY_STRUCTURES, {
filter: structure => structure.structureType === STRUCTURE_TOWER
&& (structure as StructureTower).store
.getFreeCapacity(RESOURCE_ENERGY) > 0,
});
export const closestExtensionToFill
= (pos: RoomPosition): StructureExtension | null =>
pos.findClosestByRange(FIND_MY_STRUCTURES, {
filter: structure => structure.structureType === STRUCTURE_EXTENSION
&& (structure as StructureExtension).store
.getFreeCapacity(RESOURCE_ENERGY) > 0,
});
export const closestContainerWithEnergy
= (pos: RoomPosition): StructureContainer | null =>
pos.findClosestByRange(FIND_STRUCTURES, {
filter: structure => structure.structureType === STRUCTURE_CONTAINER
&& (structure as StructureContainer).store
.getUsedCapacity(RESOURCE_ENERGY) > 0,
});
export const closestContainerToFill
= (pos: RoomPosition): StructureContainer | null =>
pos.findClosestByRange(FIND_STRUCTURES, {
filter: structure => structure.structureType === STRUCTURE_CONTAINER
&& (structure as StructureContainer).store
.getFreeCapacity(RESOURCE_ENERGY) > 0,
});
export const closestStorageWithResource
= (pos: RoomPosition, t: ResourceConstant): StructureStorage | null =>
pos.findClosestByRange(FIND_MY_STRUCTURES, {
filter: structure => structure.structureType === STRUCTURE_STORAGE
&& (structure as StructureStorage).store
.getUsedCapacity(t) > 0,
});
export const closestStorageToFill
= (pos: RoomPosition, t: ResourceConstant): StructureStorage | null =>
pos.findClosestByRange(FIND_MY_STRUCTURES, {
filter: structure => structure.structureType === STRUCTURE_STORAGE
&& (structure as StructureStorage).store.getFreeCapacity(t) > 0,
});

View file

@ -1,7 +1,8 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const buildConstructionSite = () => createAction('buildConstructionSite', (creep: Creep) => {
export const buildConstructionSite = () =>
createAction("buildConstructionSite", (creep: Creep) => {
const cs = creep.pos.findClosestByRange(FIND_CONSTRUCTION_SITES);
if (!cs) {
return Fail;
@ -20,4 +21,4 @@ export const buildConstructionSite = () => createAction('buildConstructionSite',
return Fail;
}
}
})
});

View file

@ -1,7 +1,8 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const harvestFromClosestActiveSource = () => createAction('harvestFromClosestActiveSource', (creep: Creep) => {
export const harvestFromClosestActiveSource = () =>
createAction("harvestFromClosestActiveSource", (creep: Creep) => {
const source = creep.pos.findClosestByPath(FIND_SOURCES_ACTIVE);
if (!source) {
return Fail;

View file

@ -1,6 +1,7 @@
import { createAction, Fail, InProgress } from "./Action";
export const moveTo = (pos: _HasRoomPosition | RoomPosition) => createAction('moveTo', (creep: Creep) => {
export const moveTo = (pos: _HasRoomPosition | RoomPosition) =>
createAction("moveTo", (creep: Creep) => {
switch (creep.moveTo(pos)) {
case OK: {
return InProgress;

View file

@ -1,8 +1,10 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const repairStructure = () => createAction('repairStructure', (creep: Creep) => {
const cs = creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str) => str.hits < str.hitsMax * 0.8 });
export const repairStructure = () =>
createAction("repairStructure", (creep: Creep) => {
const cs = creep.pos.findClosestByRange(FIND_STRUCTURES,
{ filter: str => str.hits < str.hitsMax * 0.8 });
if (!cs) {
return Fail;
}
@ -20,4 +22,4 @@ export const repairStructure = () => createAction('repairStructure', (creep: Cre
return Fail;
}
}
})
});

View file

@ -1,7 +1,15 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const transferEnergy = (target: Creep | StructureSpawn | StructureContainer | StructureExtension | StructureStorage | null) => createAction('transferEnergy', (creep: Creep) => {
type TransferTarget = Creep
| StructureSpawn
| StructureContainer
| StructureStorage
| StructureExtension
| StructureTower;
export const transferEnergy = (target: TransferTarget | null) =>
createAction("transferEnergy", (creep: Creep) => {
if (target == null) {
return Fail;
}

View file

@ -1,7 +1,8 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const upgradeController = (controller: StructureController) => createAction('upgradeController', (creep: Creep) => {
export const upgradeController = (controller: StructureController) =>
createAction("upgradeController", (creep: Creep) => {
switch (creep.upgradeController(controller)) {
case OK: {
return InProgress;

View file

@ -1,7 +1,9 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const withdrawEnergy = (target: StructureContainer | StructureStorage | null) => createAction('transferEnergy', (creep: Creep) => {
export const withdrawEnergy
= (target: StructureContainer | StructureStorage | null) =>
createAction("withdrawEnergy", (creep: Creep) => {
if (target == null) {
return Fail;
}

View file

@ -6,7 +6,7 @@ const BodyPartCosts = {
[RANGED_ATTACK]: 150,
[HEAL]: 250,
[CLAIM]: 600,
[TOUGH]: 10
}
[TOUGH]: 10,
};
export default BodyPartCosts;

View file

@ -3,27 +3,45 @@ export const buildContainers = (room: Room) => {
if (controller == null) {
return;
}
if (controller.level < 2) {
if (controller.level < 2
&& room.find(FIND_MY_STRUCTURES,
{ filter: STRUCTURE_EXTENSION }).length > 1) {
return;
}
const sources = room.find(FIND_SOURCES);
for (const source of sources) {
room.createConstructionSite(source.pos.x - 2, source.pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(source.pos.x + 2, source.pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(source.pos.x, source.pos.y - 2, STRUCTURE_CONTAINER);
room.createConstructionSite(source.pos.x, source.pos.y + 2, STRUCTURE_CONTAINER);
const pos = source.pos;
room.createConstructionSite(pos.x - 1, pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x + 1, pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x, pos.y - 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x, pos.y + 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x - 1, pos.y + 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x + 1, pos.y - 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x - 1, pos.y - 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x + 1, pos.y + 1, STRUCTURE_CONTAINER);
}
if (controller.level < 4) {
return;
}
const terrain = room.getTerrain();
if (terrain.get(controller.pos.x, controller.pos.y - 3) !== TERRAIN_MASK_WALL) {
room.createConstructionSite(controller.pos.x, controller.pos.y - 3, STRUCTURE_STORAGE);
} else if (terrain.get(controller.pos.x, controller.pos.y + 3) !== TERRAIN_MASK_WALL) {
room.createConstructionSite(controller.pos.x, controller.pos.y + 3, STRUCTURE_STORAGE);
} else if (terrain.get(controller.pos.x - 3, controller.pos.y) !== TERRAIN_MASK_WALL) {
room.createConstructionSite(controller.pos.x - 3, controller.pos.y, STRUCTURE_STORAGE);
} else if (terrain.get(controller.pos.x + 3, controller.pos.y) !== TERRAIN_MASK_WALL) {
room.createConstructionSite(controller.pos.x + 3, controller.pos.y, STRUCTURE_STORAGE);
if (terrain.get(controller.pos.x, controller.pos.y - 3)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x, controller.pos.y - 3, STRUCTURE_STORAGE);
}
else if (terrain.get(controller.pos.x, controller.pos.y + 3)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x, controller.pos.y + 3, STRUCTURE_STORAGE);
}
else if (terrain.get(controller.pos.x - 3, controller.pos.y)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x - 3, controller.pos.y, STRUCTURE_STORAGE);
}
else if (terrain.get(controller.pos.x + 3, controller.pos.y)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x + 3, controller.pos.y, STRUCTURE_STORAGE);
}
};

View file

@ -1,6 +1,6 @@
const extentionsAvailable = (roomlevel: number) => {
return roomlevel > 2 ? roomlevel * 10 - 20 : (roomlevel - 1) * 5;
}
};
export const buildExtentions = (room: Room) => {
const spawns = room.find(FIND_MY_SPAWNS);
@ -8,19 +8,27 @@ export const buildExtentions = (room: Room) => {
const spawn = spawns[0];
const exts = extentionsAvailable(room.controller?.level ?? 0);
const terrain = room.getTerrain();
for (let x = -Math.floor(Math.sqrt(exts) / 2); x < Math.sqrt(exts) / 2; x++) {
for (let y = -Math.floor(Math.sqrt(exts) / 2); y < Math.sqrt(exts) / 2; y++) {
const sqroffset = Math.sqrt(exts) / 2;
for (let x = -Math.floor(sqroffset); x < sqroffset; x++) {
for (let y = -Math.floor(sqroffset); y < sqroffset; y++) {
room.visual.circle(spawn.pos.x + x * 2, spawn.pos.y + y * 2);
if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1) !== TERRAIN_MASK_WALL) {
room.createConstructionSite(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD);
if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD);
}
if (terrain.get(spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1) !== TERRAIN_MASK_WALL) {
room.createConstructionSite(spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD);
if (terrain.get(spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD);
}
if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2) !== TERRAIN_MASK_WALL) {
room.createConstructionSite(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2, STRUCTURE_ROAD);
}
room.createConstructionSite(spawn.pos.x + x * 2, spawn.pos.y + y * 2, STRUCTURE_EXTENSION);
if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2, STRUCTURE_ROAD);
}
room.createConstructionSite(
spawn.pos.x + x * 2, spawn.pos.y + y * 2, STRUCTURE_EXTENSION);
}
}
};

View file

@ -1,5 +1,6 @@
export const buildRoads = (room: Room) => {
if ((room.controller?.level ?? 0) < 2) {
const rc = room.controller?.level ?? 0;
if (rc < 2) {
return;
}
const sources: _HasRoomPosition[] = room.find(FIND_SOURCES);
@ -7,18 +8,20 @@ export const buildRoads = (room: Room) => {
const sourcesAndMinerals = sources.concat(room.find(FIND_MINERALS));
room.visual.clear();
for (const source of sourcesAndSpawns) {
for (const source2 of (room.controller?.level ?? 0) > 4 ? sourcesAndMinerals : sources) {
for (const source2 of rc > 4 ? sourcesAndMinerals : sources) {
const path = source.pos.findPathTo(source2, {
ignoreCreeps: true,
ignoreRoads: true
ignoreRoads: true,
});
for (const point of path) {
if ((point.x === source.pos.x && point.y === source.pos.y) || (point.x === source2.pos.x && point.y === source2.pos.y)) {
if ((point.x === source.pos.x && point.y === source.pos.y)
|| (point.x === source2.pos.x && point.y === source2.pos.y)) {
continue;
}
room.visual.line(point.x, point.y, point.x - point.dx, point.y - point.dy);
room.visual.line(point.x, point.y,
point.x - point.dx, point.y - point.dy);
room.createConstructionSite(point.x, point.y, STRUCTURE_ROAD);
}
}
}
}
};

View file

@ -1,23 +1,41 @@
import { Fail, runAction } from "../Actions/Action";
import { runAction } from "../Actions/Action";
import { harvestFromClosestActiveSource } from "../Actions/harvest";
import { transferEnergy } from "../Actions/transferEnergy";
import { upgradeController } from "../Actions/upgradeController";
import {
closestContainerWithEnergy,
closestExtensionToFill,
closestStorageToFill,
closestTowerToFill,
notNull,
} from "../Actions/Util";
import { withdrawEnergy } from "../Actions/withdrawEnergy";
import { WorkerDefinition } from "./worker";
export const Clerk: WorkerDefinition = {
runAction: (creep: Creep, spawn: StructureSpawn) => runAction(creep, withdrawEnergy(creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str: Structure) => str.structureType === STRUCTURE_CONTAINER && (str as StructureContainer).store.getUsedCapacity(RESOURCE_ENERGY) > 0 }) as StructureContainer | null))
const action = (creep: Creep, spawn: StructureSpawn) => runAction(creep,
withdrawEnergy(closestContainerWithEnergy(creep.pos)))
.or(harvestFromClosestActiveSource())
.andThen(transferEnergy(spawn))
.or(transferEnergy(creep.pos.findClosestByRange(FIND_MY_STRUCTURES, { filter: (structure: AnyOwnedStructure) => structure.structureType === STRUCTURE_EXTENSION && structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0}) as StructureExtension | null))
.or(transferEnergy(creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str: Structure) => str.structureType === STRUCTURE_STORAGE && (str as StructureStorage).store.getFreeCapacity(RESOURCE_ENERGY) > 0 }) as StructureStorage | null))
.or(creep.room.controller ? upgradeController(creep.room.controller) : Fail)
.repeat(),
name: 'clerk',
requiredCreeps: (room: Room) => 2,
bodyDefinition: (energy: number) => energy < 100 ? [] : [WORK].concat(new Array(Math.floor((energy - 100) / 150)).fill([MOVE, CARRY, CARRY]).reduce((x, y) => x.concat(y), [])),
.andThen(transferEnergy(closestExtensionToFill(creep.pos)))
.or(transferEnergy(spawn))
.or(transferEnergy(closestTowerToFill(creep.pos)))
.or(transferEnergy(closestStorageToFill(creep.pos, RESOURCE_ENERGY)))
.or(notNull(creep.room.controller, upgradeController))
.repeat();
const body = (energy: number) => (
energy < 100
? []
: [WORK].concat(new Array(Math.floor((energy - 100) / 150))
.fill([MOVE, CARRY, CARRY]).reduce((x, y) => x.concat(y), []))
);
export const Clerk: WorkerDefinition = {
runAction: action,
name: "clerk",
requiredCreeps: () => 3,
bodyDefinition: body,
motivationalThougts: [
"Carrying 🎒",
"💗 working"
]
}
"💗 working",
],
};

View file

@ -1,24 +1,37 @@
import { Fail, runAction } from "../Actions/Action";
import { runAction } from "../Actions/Action";
import { buildConstructionSite } from "../Actions/buildConstructionSite";
import { harvestFromClosestActiveSource } from "../Actions/harvest";
import { repairStructure } from "../Actions/repairStructure";
import { upgradeController } from "../Actions/upgradeController";
import {
closestContainerWithEnergy,
closestStorageWithResource,
notNull,
} from "../Actions/Util";
import { withdrawEnergy } from "../Actions/withdrawEnergy";
import { WorkerDefinition } from "./worker";
export const Constructor: WorkerDefinition = {
runAction: (creep: Creep) => runAction(creep, withdrawEnergy(creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str: Structure) => str.structureType === STRUCTURE_STORAGE && (str as StructureStorage).store.getUsedCapacity(RESOURCE_ENERGY) > 0 }) as StructureStorage | null))
.or(withdrawEnergy(creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str: Structure) => str.structureType === STRUCTURE_CONTAINER && (str as StructureContainer).store.getUsedCapacity(RESOURCE_ENERGY) > 0 }) as StructureContainer | null))
const action = (creep: Creep) => runAction(creep,
withdrawEnergy(closestStorageWithResource(creep.pos, RESOURCE_ENERGY)))
.or(withdrawEnergy(closestContainerWithEnergy(creep.pos)))
.or(harvestFromClosestActiveSource())
.andThen(buildConstructionSite())
.or(repairStructure())
.or(creep.room.controller ? upgradeController(creep.room.controller) : Fail)
.repeat(),
name: 'constructor',
requiredCreeps: (room: Room) => 1,
bodyDefinition: (energy: number) => new Array(Math.floor(energy / 250)).fill([WORK, MOVE, CARRY, CARRY]).reduce((x, y) => x.concat(y), []),
.or(notNull(creep.room.controller, upgradeController))
.repeat();
const body = (energy: number) =>
new Array(Math.floor(energy / 250))
.fill([WORK, MOVE, CARRY, CARRY]).reduce((x, y) => x.concat(y), []);
export const Constructor: WorkerDefinition = {
runAction: action,
name: "constructor",
requiredCreeps: (room: Room) =>
room.find(FIND_CONSTRUCTION_SITES).length > 10 ? 2 : 1,
bodyDefinition: body,
motivationalThougts: [
"I 💗 making",
"Fixin' 🔧"
]
}
"Fixin' 🔧",
],
};

View file

@ -1,16 +1,31 @@
import { Fail, runAction } from "../Actions/Action";
import { runAction } from "../Actions/Action";
import { harvestFromClosestActiveSource } from "../Actions/harvest";
import { transferEnergy } from "../Actions/transferEnergy";
import { closestContainerToFill } from "../Actions/Util";
import { WorkerDefinition } from "./worker";
const action = (creep: Creep) => runAction(creep,
harvestFromClosestActiveSource())
.andThen(transferEnergy(closestContainerToFill(creep.pos)))
.repeat();
const body = (energy: number) => (
energy < 300
? []
: ([WORK, WORK, MOVE, CARRY]
.concat(new Array(Math.floor((energy - 300) / 100)).fill(WORK)))
);
export const Miner: WorkerDefinition = {
runAction: (creep: Creep) => runAction(creep, harvestFromClosestActiveSource())
.andThen(transferEnergy(creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str: Structure) => str.structureType === STRUCTURE_CONTAINER && (str as StructureContainer).store.getFreeCapacity(RESOURCE_ENERGY) > 0}) as StructureContainer | null))
.repeat(),
name: 'miner',
requiredCreeps: (room: Room) => room.find(FIND_STRUCTURES, { filter: { structureType: STRUCTURE_CONTAINER }}).length > 0 ? 4 : 0,
bodyDefinition: (energy: number) => energy < 300 ? [] : [WORK, WORK, MOVE, CARRY].concat(new Array(Math.floor((energy - 300) / 100)).fill(WORK)),
runAction: action,
name: "miner",
requiredCreeps: (room: Room) =>
room.find(FIND_STRUCTURES,
{ filter: { structureType: STRUCTURE_CONTAINER } }).length > 0
? 4
: 0,
bodyDefinition: body,
motivationalThougts: [
"RocknStone"
]
}
"RocknStone",
],
};

View file

@ -1,20 +1,31 @@
import { Fail, runAction } from "../Actions/Action";
import { runAction } from "../Actions/Action";
import { harvestFromClosestActiveSource } from "../Actions/harvest";
import { transferEnergy } from "../Actions/transferEnergy";
import { upgradeController } from "../Actions/upgradeController";
import {
closestContainerWithEnergy,
closestStorageWithResource,
notNull,
} from "../Actions/Util";
import { withdrawEnergy } from "../Actions/withdrawEnergy";
import { WorkerDefinition } from "./worker";
export const Upgrader: WorkerDefinition = {
runAction: (creep: Creep, spawn: StructureSpawn) => runAction(creep, withdrawEnergy(creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str: Structure) => str.structureType === STRUCTURE_STORAGE && (str as StructureStorage).store.getUsedCapacity(RESOURCE_ENERGY) > 0 }) as StructureStorage | null))
.or(withdrawEnergy(creep.pos.findClosestByRange(FIND_STRUCTURES, { filter: (str: Structure) => str.structureType === STRUCTURE_CONTAINER && (str as StructureContainer).store.getUsedCapacity(RESOURCE_ENERGY) > 0 }) as StructureContainer | null))
const action = (creep: Creep) => runAction(creep,
withdrawEnergy(closestStorageWithResource(creep.pos, RESOURCE_ENERGY)))
.or(withdrawEnergy(closestContainerWithEnergy(creep.pos)))
.or(harvestFromClosestActiveSource())
.andThen(creep.room.controller ? upgradeController(creep.room.controller) : Fail)
.repeat(),
name: 'upgrader',
requiredCreeps: (room: Room) => 1,
bodyDefinition: (energy: number) => new Array(Math.floor(energy / 300)).fill([WORK, WORK, MOVE, CARRY]).reduce((x, y) => x.concat(y), []),
.andThen(notNull(creep.room.controller, upgradeController))
.repeat();
const body = (energy: number) =>
new Array(Math.floor(energy / 300))
.fill([WORK, WORK, MOVE, CARRY]).reduce((x, y) => x.concat(y), []);
export const Upgrader: WorkerDefinition = {
runAction: action,
name: "upgrader",
requiredCreeps: () => 1,
bodyDefinition: body,
motivationalThougts: [
'🏳️‍⚧️'
]
}
"🏳️‍⚧️",
],
};

View file

@ -1,12 +1,15 @@
import BodyPartCosts from '../Constants/BodyPartCosts';
import * as Workers from './index';
import BodyPartCosts from "../Constants/BodyPartCosts";
import * as Workers from "./index";
describe('Test Creep Workers', () => {
console.log(Workers.Clerk)
describe("Test Creep Workers", () => {
console.log(Workers.Clerk);
for (const [moduleName, worker] of Object.entries(Workers)) {
test(`${moduleName}: Body parts cost calculation is correct`, () => {
for (let cost = 0; cost < 1500; cost++) {
expect(worker.bodyDefinition(cost).map((x) => BodyPartCosts[x]).reduce((x, y) => x + y, 0)).toBeLessThanOrEqual(cost);
expect(
worker.bodyDefinition(cost)
.map(x => BodyPartCosts[x]).reduce((x, y) => x + y, 0),
).toBeLessThanOrEqual(cost);
}
});
}

View file

@ -1,16 +1,22 @@
export interface WorkerDefinition {
runAction: (creep: Creep, spawn: StructureSpawn) => void,
name: string,
requiredCreeps: (room: Room) => number,
bodyDefinition: (energy: number) => BodyPartConstant[],
motivationalThougts?: string[]
runAction: (creep: Creep, spawn: StructureSpawn) => void;
name: string;
requiredCreeps: (room: Room) => number;
bodyDefinition: (energy: number) => BodyPartConstant[];
motivationalThougts?: string[];
}
export const spawnWorkers = (spawn: StructureSpawn, workers: WorkerDefinition[]): void => {
export const spawnWorkers
= (spawn: StructureSpawn, workers: WorkerDefinition[]): void => {
for (const worker of workers) {
for (let i = 0; i < worker.requiredCreeps(spawn.room); i++) {
const ret = spawn.spawnCreep(worker.bodyDefinition(
spawn.store.getCapacity(RESOURCE_ENERGY) + (spawn.room.find(FIND_MY_STRUCTURES, { filter: { structureType: STRUCTURE_EXTENSION }}).length * (Object.keys(Game.creeps).length < 2 ? (25 * Object.keys(Game.creeps).length) : 50))), worker.name + i.toString());
spawn.store.getCapacity(RESOURCE_ENERGY)
+ (spawn.room.find(FIND_MY_STRUCTURES,
{ filter: { structureType: STRUCTURE_EXTENSION } }).length
* (Object.keys(Game.creeps).length < 2
? (25 * Object.keys(Game.creeps).length)
: 50))), worker.name + i.toString());
if (ret === OK || ret === ERR_NOT_ENOUGH_ENERGY) {
return;
}
@ -18,7 +24,8 @@ export const spawnWorkers = (spawn: StructureSpawn, workers: WorkerDefinition[])
}
};
export const runWorkers = (spawn: StructureSpawn, workers: WorkerDefinition[]): void => {
export const runWorkers
= (spawn: StructureSpawn, workers: WorkerDefinition[]): void => {
for (const worker of workers) {
for (const creep of Object.values(Game.creeps)) {
if (creep.spawning) {
@ -27,7 +34,10 @@ export const runWorkers = (spawn: StructureSpawn, workers: WorkerDefinition[]):
if (creep.name.startsWith(worker.name)) {
worker.runAction(creep, spawn);
if (worker.motivationalThougts != null && Math.random() < 0.1) {
creep.say(worker.motivationalThougts[Math.floor(worker.motivationalThougts.length * Math.random())], true);
creep.say(
worker.motivationalThougts[
Math.floor(worker.motivationalThougts.length * Math.random())
], true);
}
}
}

View file

@ -1,8 +1,3 @@
import { runAction } from "./Actions/Action";
import { buildConstructionSite } from "./Actions/buildConstructionSite";
import { harvestFromClosestActiveSource } from "./Actions/harvest";
import { transferEnergy } from "./Actions/transferEnergy";
import { upgradeController } from "./Actions/upgradeController";
import { buildContainers } from "./RoomPlanner/Blueprints/Containers";
import { buildExtentions } from "./RoomPlanner/Blueprints/Extensions";
import { buildRoads } from "./RoomPlanner/Blueprints/Roads";
@ -12,6 +7,34 @@ import { Miner } from "./Workers/Miner";
import { Upgrader } from "./Workers/Upgrader";
import { runWorkers, spawnWorkers } from "./Workers/worker";
const runTowers = (spawn: StructureSpawn) => {
const towers: StructureTower[] = spawn.room.find(FIND_MY_STRUCTURES,
{ filter: s => s.structureType === STRUCTURE_TOWER });
for (const tower of towers) {
const enemy = tower.pos.findClosestByRange(FIND_HOSTILE_CREEPS);
if (enemy != null) {
if (tower.attack(enemy) === OK) {
continue;
}
}
const creep = tower.pos.findClosestByRange(FIND_MY_CREEPS, {
filter: (creep: Creep) => creep.hits < creep.hitsMax,
});
if (creep != null) {
if (tower.heal(creep) === OK) {
continue;
}
}
const str = tower.pos.findClosestByRange(FIND_STRUCTURES, {
filter: s => s.hits < s.hitsMax * 0.8,
});
console.log(str);
if (str != null) {
console.log(tower.repair(str));
}
};
};
export function loop() {
const spawn = Game.spawns.Spawn1;
const controller = spawn.room.controller;
@ -21,6 +44,7 @@ export function loop() {
const workerTypes = [Clerk, Upgrader, Miner, Constructor];
spawnWorkers(spawn, workerTypes);
runWorkers(spawn, workerTypes);
runTowers(spawn);
if (Game.time % 100 === 0) {
buildRoads(spawn.room);
}

View file

@ -4,7 +4,7 @@ const fs = require('fs');
var https = require('https');
var data = {
branch: 'world',
branch: 'hurricane',
modules: {
main: fs.readFileSync('./dist/screeps.js', 'utf-8').toString(),
}

3718
yarn.lock

File diff suppressed because it is too large Load diff