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/ node_modules/
rethinkdb_data/
.idea/
*.iml
.npm/
.gitconfig
.bash_history
.bash_login
static/img/*.png
*.db
*.db-journal
.env .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" "push": "yarn build && node ./upload.js"
}, },
"dependencies": { "dependencies": {
"@types/screeps": "^3.3.0" "@types/screeps": "^3.3.8"
}, },
"devDependencies": { "devDependencies": {
"@swc/cli": "^0.1.57", "@eslint/js": "^9.39.2",
"@swc/core": "^1.3.24", "@stylistic/eslint-plugin": "^5.6.1",
"@swc/jest": "^0.2.24", "@swc/cli": "^0.7.9",
"@types/jest": "^29.2.5", "@swc/core": "^1.15.5",
"@types/node": "^18.11.18", "@swc/jest": "^0.2.39",
"@typescript-eslint/eslint-plugin": "^5.47.0", "@types/jest": "^30.0.0",
"@typescript-eslint/parser": "^5.47.0", "@types/node": "^25.0.3",
"dotenv": "^16.0.3", "@typescript-eslint/eslint-plugin": "^8.50.0",
"eslint": "^8.30.0", "@typescript-eslint/parser": "^8.50.0",
"husky": "^8.0.0", "dotenv": "^17.2.3",
"jest": "^29.3.1", "eslint": "^9.39.2",
"lodash": "^4.17.21", "globals": "^16.5.0",
"screeps-jest": "^2.0.2", "husky": "^9.1.7",
"ts-node": "^10.9.1", "jest": "^30.2.0",
"typescript": "^4.9.4" "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 { declare global {
interface CreepMemory { interface CreepMemory {
state?: number state?: number;
} }
} }
export interface ChainableAction { export interface ChainableAction {
or: (action: Action) => ChainableAction, or: (action: Action) => ChainableAction;
and: (action: Action) => ChainableAction, and: (action: Action) => ChainableAction;
andThen: (action: Action) => ChainableAction, andThen: (action: Action) => ChainableAction;
repeat: () => void repeat: () => void;
} }
export const NoOp: Action = (creep: Creep, state: number = 0) => ({ export const NoOp: Action = (creep: Creep, state: number = 0) => ({
or: () => NoOp(creep, state), or: () => NoOp(creep, state),
and: () => NoOp(creep, state), and: () => NoOp(creep, state),
andThen: () => NoOp(creep, state), andThen: () => NoOp(creep, state),
repeat: () => {} repeat: () => {},
}); });
export const Success: Action = (creep: Creep, state: number = 0) => ({ export const Success: Action = (creep: Creep, state: number = 0) => ({
@ -27,14 +27,14 @@ export const Success: Action = (creep: Creep, state: number = 0) => ({
}, },
repeat: () => { repeat: () => {
creep.memory.state = 0; creep.memory.state = 0;
} },
}); });
export const InProgress: Action = (creep: Creep, state: number = 0) => ({ export const InProgress: Action = (creep: Creep, state: number = 0) => ({
or: () => InProgress(creep, state), or: () => InProgress(creep, state),
and: (action: Action) => action(creep, state), and: (action: Action) => action(creep, state),
andThen: () => NoOp(creep, state), andThen: () => NoOp(creep, state),
repeat: () => {} repeat: () => {},
}); });
export const Fail: Action = (creep: Creep, state: number = 0) => ({ 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), and: () => NoOp(creep, state),
andThen: () => NoOp(creep, state), andThen: () => NoOp(creep, state),
repeat: () => { 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) => { return (creep: Creep, state: number = 0) => {
if ((creep.memory.state ?? 0) > state) { if ((creep.memory.state ?? 0) > state) {
return Success(creep, state); return Success(creep, state);
} }
return action(creep)(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; 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 { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo"; 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); const cs = creep.pos.findClosestByRange(FIND_CONSTRUCTION_SITES);
if (!cs) { if (!cs) {
return Fail; return Fail;
@ -20,4 +21,4 @@ export const buildConstructionSite = () => createAction('buildConstructionSite',
return Fail; return Fail;
} }
} }
}) });

View file

@ -1,7 +1,8 @@
import { createAction, Fail, InProgress, Success } from "./Action"; import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo"; 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); const source = creep.pos.findClosestByPath(FIND_SOURCES_ACTIVE);
if (!source) { if (!source) {
return Fail; return Fail;

View file

@ -1,6 +1,7 @@
import { createAction, Fail, InProgress } from "./Action"; 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)) { switch (creep.moveTo(pos)) {
case OK: { case OK: {
return InProgress; return InProgress;

View file

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

View file

@ -1,7 +1,15 @@
import { createAction, Fail, InProgress, Success } from "./Action"; import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo"; 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) { if (target == null) {
return Fail; return Fail;
} }

View file

@ -1,7 +1,8 @@
import { createAction, Fail, InProgress, Success } from "./Action"; import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo"; 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)) { switch (creep.upgradeController(controller)) {
case OK: { case OK: {
return InProgress; return InProgress;

View file

@ -1,7 +1,9 @@
import { createAction, Fail, InProgress, Success } from "./Action"; import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo"; 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) { if (target == null) {
return Fail; return Fail;
} }

View file

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

View file

@ -3,27 +3,45 @@ export const buildContainers = (room: Room) => {
if (controller == null) { if (controller == null) {
return; return;
} }
if (controller.level < 2) { if (controller.level < 2
&& room.find(FIND_MY_STRUCTURES,
{ filter: STRUCTURE_EXTENSION }).length > 1) {
return; return;
} }
const sources = room.find(FIND_SOURCES); const sources = room.find(FIND_SOURCES);
for (const source of sources) { for (const source of sources) {
room.createConstructionSite(source.pos.x - 2, source.pos.y, STRUCTURE_CONTAINER); const pos = source.pos;
room.createConstructionSite(source.pos.x + 2, source.pos.y, STRUCTURE_CONTAINER); room.createConstructionSite(pos.x - 1, pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(source.pos.x, source.pos.y - 2, STRUCTURE_CONTAINER); room.createConstructionSite(pos.x + 1, pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(source.pos.x, source.pos.y + 2, 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) { if (controller.level < 4) {
return; return;
} }
const terrain = room.getTerrain(); const terrain = room.getTerrain();
if (terrain.get(controller.pos.x, controller.pos.y - 3) !== TERRAIN_MASK_WALL) { if (terrain.get(controller.pos.x, controller.pos.y - 3)
room.createConstructionSite(controller.pos.x, controller.pos.y - 3, STRUCTURE_STORAGE); !== TERRAIN_MASK_WALL) {
} else if (terrain.get(controller.pos.x, controller.pos.y + 3) !== TERRAIN_MASK_WALL) { room.createConstructionSite(
room.createConstructionSite(controller.pos.x, controller.pos.y + 3, STRUCTURE_STORAGE); 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);
} }
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) => { const extentionsAvailable = (roomlevel: number) => {
return roomlevel > 2 ? roomlevel * 10 - 20 : (roomlevel - 1) * 5; return roomlevel > 2 ? roomlevel * 10 - 20 : (roomlevel - 1) * 5;
} };
export const buildExtentions = (room: Room) => { export const buildExtentions = (room: Room) => {
const spawns = room.find(FIND_MY_SPAWNS); const spawns = room.find(FIND_MY_SPAWNS);
@ -8,19 +8,27 @@ export const buildExtentions = (room: Room) => {
const spawn = spawns[0]; const spawn = spawns[0];
const exts = extentionsAvailable(room.controller?.level ?? 0); const exts = extentionsAvailable(room.controller?.level ?? 0);
const terrain = room.getTerrain(); const terrain = room.getTerrain();
for (let x = -Math.floor(Math.sqrt(exts) / 2); x < Math.sqrt(exts) / 2; x++) { const sqroffset = Math.sqrt(exts) / 2;
for (let y = -Math.floor(Math.sqrt(exts) / 2); y < Math.sqrt(exts) / 2; y++) { 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); 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) { if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1)
room.createConstructionSite(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD); !== 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) { if (terrain.get(spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1)
room.createConstructionSite(spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD); !== 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) { if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2)
room.createConstructionSite(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2, STRUCTURE_ROAD); !== TERRAIN_MASK_WALL) {
} room.createConstructionSite(
room.createConstructionSite(spawn.pos.x + x * 2, spawn.pos.y + y * 2, STRUCTURE_EXTENSION); 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) => { export const buildRoads = (room: Room) => {
if ((room.controller?.level ?? 0) < 2) { const rc = room.controller?.level ?? 0;
if (rc < 2) {
return; return;
} }
const sources: _HasRoomPosition[] = room.find(FIND_SOURCES); const sources: _HasRoomPosition[] = room.find(FIND_SOURCES);
@ -7,18 +8,20 @@ export const buildRoads = (room: Room) => {
const sourcesAndMinerals = sources.concat(room.find(FIND_MINERALS)); const sourcesAndMinerals = sources.concat(room.find(FIND_MINERALS));
room.visual.clear(); room.visual.clear();
for (const source of sourcesAndSpawns) { 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, { const path = source.pos.findPathTo(source2, {
ignoreCreeps: true, ignoreCreeps: true,
ignoreRoads: true ignoreRoads: true,
}); });
for (const point of path) { 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; 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); 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 { harvestFromClosestActiveSource } from "../Actions/harvest";
import { transferEnergy } from "../Actions/transferEnergy"; import { transferEnergy } from "../Actions/transferEnergy";
import { upgradeController } from "../Actions/upgradeController"; import { upgradeController } from "../Actions/upgradeController";
import {
closestContainerWithEnergy,
closestExtensionToFill,
closestStorageToFill,
closestTowerToFill,
notNull,
} from "../Actions/Util";
import { withdrawEnergy } from "../Actions/withdrawEnergy"; import { withdrawEnergy } from "../Actions/withdrawEnergy";
import { WorkerDefinition } from "./worker"; import { WorkerDefinition } from "./worker";
export const Clerk: WorkerDefinition = { const action = (creep: Creep, spawn: StructureSpawn) => runAction(creep,
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)) withdrawEnergy(closestContainerWithEnergy(creep.pos)))
.or(harvestFromClosestActiveSource()) .or(harvestFromClosestActiveSource())
.andThen(transferEnergy(spawn)) .andThen(transferEnergy(closestExtensionToFill(creep.pos)))
.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(spawn))
.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(transferEnergy(closestTowerToFill(creep.pos)))
.or(creep.room.controller ? upgradeController(creep.room.controller) : Fail) .or(transferEnergy(closestStorageToFill(creep.pos, RESOURCE_ENERGY)))
.repeat(), .or(notNull(creep.room.controller, upgradeController))
name: 'clerk', .repeat();
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), [])), 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: [ motivationalThougts: [
"Carrying 🎒", "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 { buildConstructionSite } from "../Actions/buildConstructionSite";
import { harvestFromClosestActiveSource } from "../Actions/harvest"; import { harvestFromClosestActiveSource } from "../Actions/harvest";
import { repairStructure } from "../Actions/repairStructure"; import { repairStructure } from "../Actions/repairStructure";
import { upgradeController } from "../Actions/upgradeController"; import { upgradeController } from "../Actions/upgradeController";
import {
closestContainerWithEnergy,
closestStorageWithResource,
notNull,
} from "../Actions/Util";
import { withdrawEnergy } from "../Actions/withdrawEnergy"; import { withdrawEnergy } from "../Actions/withdrawEnergy";
import { WorkerDefinition } from "./worker"; import { WorkerDefinition } from "./worker";
export const Constructor: WorkerDefinition = { const action = (creep: Creep) => runAction(creep,
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)) withdrawEnergy(closestStorageWithResource(creep.pos, RESOURCE_ENERGY)))
.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)) .or(withdrawEnergy(closestContainerWithEnergy(creep.pos)))
.or(harvestFromClosestActiveSource()) .or(harvestFromClosestActiveSource())
.andThen(buildConstructionSite()) .andThen(buildConstructionSite())
.or(repairStructure()) .or(repairStructure())
.or(creep.room.controller ? upgradeController(creep.room.controller) : Fail) .or(notNull(creep.room.controller, upgradeController))
.repeat(), .repeat();
name: 'constructor',
requiredCreeps: (room: Room) => 1, const body = (energy: number) =>
bodyDefinition: (energy: number) => new Array(Math.floor(energy / 250)).fill([WORK, MOVE, CARRY, CARRY]).reduce((x, y) => x.concat(y), []), 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: [ motivationalThougts: [
"I 💗 making", "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 { harvestFromClosestActiveSource } from "../Actions/harvest";
import { transferEnergy } from "../Actions/transferEnergy"; import { transferEnergy } from "../Actions/transferEnergy";
import { closestContainerToFill } from "../Actions/Util";
import { WorkerDefinition } from "./worker"; 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 = { export const Miner: WorkerDefinition = {
runAction: (creep: Creep) => runAction(creep, harvestFromClosestActiveSource()) runAction: action,
.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)) name: "miner",
.repeat(), requiredCreeps: (room: Room) =>
name: 'miner', room.find(FIND_STRUCTURES,
requiredCreeps: (room: Room) => room.find(FIND_STRUCTURES, { filter: { structureType: STRUCTURE_CONTAINER }}).length > 0 ? 4 : 0, { filter: { structureType: STRUCTURE_CONTAINER } }).length > 0
bodyDefinition: (energy: number) => energy < 300 ? [] : [WORK, WORK, MOVE, CARRY].concat(new Array(Math.floor((energy - 300) / 100)).fill(WORK)), ? 4
: 0,
bodyDefinition: body,
motivationalThougts: [ 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 { harvestFromClosestActiveSource } from "../Actions/harvest";
import { transferEnergy } from "../Actions/transferEnergy";
import { upgradeController } from "../Actions/upgradeController"; import { upgradeController } from "../Actions/upgradeController";
import {
closestContainerWithEnergy,
closestStorageWithResource,
notNull,
} from "../Actions/Util";
import { withdrawEnergy } from "../Actions/withdrawEnergy"; import { withdrawEnergy } from "../Actions/withdrawEnergy";
import { WorkerDefinition } from "./worker"; import { WorkerDefinition } from "./worker";
export const Upgrader: WorkerDefinition = { const action = (creep: Creep) => runAction(creep,
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)) withdrawEnergy(closestStorageWithResource(creep.pos, RESOURCE_ENERGY)))
.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)) .or(withdrawEnergy(closestContainerWithEnergy(creep.pos)))
.or(harvestFromClosestActiveSource()) .or(harvestFromClosestActiveSource())
.andThen(creep.room.controller ? upgradeController(creep.room.controller) : Fail) .andThen(notNull(creep.room.controller, upgradeController))
.repeat(), .repeat();
name: 'upgrader',
requiredCreeps: (room: Room) => 1, const body = (energy: number) =>
bodyDefinition: (energy: number) => new Array(Math.floor(energy / 300)).fill([WORK, WORK, MOVE, CARRY]).reduce((x, y) => x.concat(y), []), 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: [ motivationalThougts: [
'🏳️‍⚧️' "🏳️‍⚧️",
] ],
} };

View file

@ -1,12 +1,15 @@
import BodyPartCosts from '../Constants/BodyPartCosts'; import BodyPartCosts from "../Constants/BodyPartCosts";
import * as Workers from './index'; import * as Workers from "./index";
describe('Test Creep Workers', () => { describe("Test Creep Workers", () => {
console.log(Workers.Clerk) console.log(Workers.Clerk);
for (const [moduleName, worker] of Object.entries(Workers)) { for (const [moduleName, worker] of Object.entries(Workers)) {
test(`${moduleName}: Body parts cost calculation is correct`, () => { test(`${moduleName}: Body parts cost calculation is correct`, () => {
for (let cost = 0; cost < 1500; cost++) { 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 { export interface WorkerDefinition {
runAction: (creep: Creep, spawn: StructureSpawn) => void, runAction: (creep: Creep, spawn: StructureSpawn) => void;
name: string, name: string;
requiredCreeps: (room: Room) => number, requiredCreeps: (room: Room) => number;
bodyDefinition: (energy: number) => BodyPartConstant[], bodyDefinition: (energy: number) => BodyPartConstant[];
motivationalThougts?: string[] motivationalThougts?: string[];
} }
export const spawnWorkers = (spawn: StructureSpawn, workers: WorkerDefinition[]): void => { export const spawnWorkers
= (spawn: StructureSpawn, workers: WorkerDefinition[]): void => {
for (const worker of workers) { for (const worker of workers) {
for (let i = 0; i < worker.requiredCreeps(spawn.room); i++) { for (let i = 0; i < worker.requiredCreeps(spawn.room); i++) {
const ret = spawn.spawnCreep(worker.bodyDefinition( 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) { if (ret === OK || ret === ERR_NOT_ENOUGH_ENERGY) {
return; 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 worker of workers) {
for (const creep of Object.values(Game.creeps)) { for (const creep of Object.values(Game.creeps)) {
if (creep.spawning) { if (creep.spawning) {
@ -27,7 +34,10 @@ export const runWorkers = (spawn: StructureSpawn, workers: WorkerDefinition[]):
if (creep.name.startsWith(worker.name)) { if (creep.name.startsWith(worker.name)) {
worker.runAction(creep, spawn); worker.runAction(creep, spawn);
if (worker.motivationalThougts != null && Math.random() < 0.1) { 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 { buildContainers } from "./RoomPlanner/Blueprints/Containers";
import { buildExtentions } from "./RoomPlanner/Blueprints/Extensions"; import { buildExtentions } from "./RoomPlanner/Blueprints/Extensions";
import { buildRoads } from "./RoomPlanner/Blueprints/Roads"; import { buildRoads } from "./RoomPlanner/Blueprints/Roads";
@ -12,6 +7,34 @@ import { Miner } from "./Workers/Miner";
import { Upgrader } from "./Workers/Upgrader"; import { Upgrader } from "./Workers/Upgrader";
import { runWorkers, spawnWorkers } from "./Workers/worker"; 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() { export function loop() {
const spawn = Game.spawns.Spawn1; const spawn = Game.spawns.Spawn1;
const controller = spawn.room.controller; const controller = spawn.room.controller;
@ -21,6 +44,7 @@ export function loop() {
const workerTypes = [Clerk, Upgrader, Miner, Constructor]; const workerTypes = [Clerk, Upgrader, Miner, Constructor];
spawnWorkers(spawn, workerTypes); spawnWorkers(spawn, workerTypes);
runWorkers(spawn, workerTypes); runWorkers(spawn, workerTypes);
runTowers(spawn);
if (Game.time % 100 === 0) { if (Game.time % 100 === 0) {
buildRoads(spawn.room); buildRoads(spawn.room);
} }

View file

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

3718
yarn.lock

File diff suppressed because it is too large Load diff