├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── prettier.config.mjs ├── rollup.config.mjs ├── screeps.sample.json ├── src ├── classes │ └── order.ts ├── enums │ ├── logLevel.ts │ ├── operationType.ts │ ├── priority.ts │ ├── role.ts │ └── roomType.ts ├── main.ts ├── managers │ ├── build.ts │ ├── harvest.ts │ ├── manager.ts │ ├── memory.ts │ ├── operation.ts │ ├── spawn.ts │ ├── tower.ts │ └── upgrade.ts ├── operations │ └── test.ts ├── prototypes │ ├── creep.ts │ └── room.ts ├── roles │ ├── builder.ts │ ├── harvester.ts │ └── upgrader.ts ├── services │ ├── creep.ts │ └── room.ts ├── types │ └── index.ts └── utils │ ├── commands.ts │ ├── creep.ts │ ├── errorMapper.ts │ ├── log.ts │ ├── operation.ts │ ├── order.ts │ └── profile.ts └── tsconfig.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'monthly' 7 | ignore: 8 | - dependency-name: '@types/lodash' 9 | - dependency-name: '@types/node' 10 | - dependency-name: 'lodash' 11 | - dependency-name: 'rollup' 12 | - dependency-name: 'source-map' 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Use Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: '20.x' 18 | - name: Install deps 19 | run: npm ci 20 | - name: Lint 21 | run: npm run lint 22 | - name: Build 23 | run: npm run build 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # screeps config 2 | screeps.json 3 | 4 | # folders 5 | dist/ 6 | node_modules/ 7 | 8 | # system generated 9 | .DS_Store 10 | Thumbs.db 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "files.encoding": "utf8", 4 | "files.trimTrailingWhitespace": true, 5 | "files.insertFinalNewline": true, 6 | "search.exclude": { 7 | "dist/**": true, 8 | "docs/**": true, 9 | "node_modules/**": true 10 | }, 11 | "typescript.tsdk": "./node_modules/typescript/lib", 12 | "editor.codeActionsOnSave": { 13 | "source.fixAll": "explicit" 14 | }, 15 | "editor.formatOnSave": true, 16 | "editor.defaultFormatter": "esbenp.prettier-vscode", 17 | "eslint.workingDirectories": [{ "mode": "location" }] 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Screeps Autonomous Framework 2 | 3 | Screeps Autonomous Framework is a starter kit for developing your own Screeps bot with TypeScript. 4 | 5 | Begin your Screeps bot development journey with this minimal, well-structured framework, offering a solid foundation for you to innovate and implement your unique strategies. 6 | 7 | ## Get Started 8 | 9 | ### Prerequisites 10 | 11 | Make sure you have [Node.js](https://nodejs.org/en/download) (LTS) installed on your system. 12 | 13 | ### Install 14 | 15 | 1. Clone the source code or download and extract it. 16 | 2. Navigate to the project folder in your terminal. 17 | 3. Run `npm install` to install dependencies. 18 | 19 | ### Compile and Upload 20 | 21 | Use the NPM scripts as aliases for Rollup commands to simplify the build and upload process. 22 | 23 | 1. Configure your Screeps server destinations. 24 | - Rename `screeps.sample.json` to `screeps.json`. 25 | - Update `screeps.json` with your Screeps credentials. 26 | 2. Use the NPM scripts in `package.json` as aliases for Rollup commands. 27 | - `npm run build` will compile but not upload. 28 | - `npm run push-main` will compile and upload to the "main" destination in `screeps.json`. 29 | 30 | **Note:** For uploading code to a private server, you must create your username and password on the server with the help of [screepsmod-auth](https://github.com/ScreepsMods/screepsmod-auth). 31 | 32 | ## Kudos 33 | 34 | Special thanks to @kasami for inspiring this project with the original KasamiBot. 35 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslintPlugin from '@typescript-eslint/eslint-plugin'; 2 | import typescriptEslintParser from '@typescript-eslint/parser'; 3 | import importPlugin from 'eslint-plugin-import'; 4 | import prettierPlugin from 'eslint-plugin-prettier'; 5 | 6 | /** @type {import('eslint').Linter.Config} */ 7 | export default [ 8 | { 9 | files: ['src/**/*.ts'], 10 | languageOptions: { 11 | ecmaVersion: 2018, 12 | parser: typescriptEslintParser, 13 | parserOptions: { project: 'tsconfig.json', tsconfigRootDir: './', sourceType: 'module' }, 14 | globals: { es6: true, node: true } 15 | }, 16 | plugins: { '@typescript-eslint': typescriptEslintPlugin, import: importPlugin, prettier: prettierPlugin }, 17 | rules: { 18 | '@typescript-eslint/array-type': 'error', 19 | '@typescript-eslint/consistent-type-assertions': 'error', 20 | '@typescript-eslint/consistent-type-definitions': 'error', 21 | '@typescript-eslint/explicit-member-accessibility': 'off', 22 | '@typescript-eslint/no-shadow': ['error', { hoist: 'all' }], 23 | '@typescript-eslint/no-unused-expressions': 'error', 24 | '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], 25 | '@typescript-eslint/no-use-before-define': ['error', { functions: false }], 26 | '@typescript-eslint/prefer-for-of': 'error', 27 | '@typescript-eslint/unified-signatures': 'error', 28 | camelcase: 'error', 29 | eqeqeq: ['error', 'smart'], 30 | 'id-denylist': ['error', 'any', 'Number', 'number', 'String', 'string', 'Boolean', 'boolean', 'Undefined'], 31 | 'id-match': 'error', 32 | 'max-classes-per-file': ['error', 1], 33 | 'no-caller': 'error', 34 | 'no-eval': 'error', 35 | 'no-new-wrappers': 'error', 36 | 'no-undef-init': 'error', 37 | 'no-underscore-dangle': 'off', 38 | 'no-var': 'error', 39 | 'object-shorthand': 'error', 40 | 'one-var': ['error', 'never'], 41 | radix: 'error', 42 | 'spaced-comment': 'error' 43 | } 44 | } 45 | ]; 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "screeps-autonomous-framework", 3 | "version": "1.0.0", 4 | "description": "Screeps World Autonomous Bot Framework Base", 5 | "author": "admon84", 6 | "homepage": "https://admon84.github.io/screeps-autonomous-framework/", 7 | "repository": "github:admon84/screeps-autonomous-framework", 8 | "bugs": "https://github.com/admon84/screeps-autonomous-framework/issues", 9 | "license": "Unlicense", 10 | "main": "src/main.ts", 11 | "scripts": { 12 | "lint": "eslint src/**/*.ts", 13 | "lint-fix": "npm run lint -- --fix", 14 | "format": "prettier src/**/*.ts --write", 15 | "build": "rollup -c", 16 | "push-main": "rollup -c --environment DEST:main", 17 | "push-season": "rollup -c --environment DEST:season", 18 | "push-pserver": "rollup -c --environment DEST:pserver", 19 | "push-sim": "rollup -c --environment DEST:sim", 20 | "watch-sim": "rollup -cw --environment DEST:sim" 21 | }, 22 | "devDependencies": { 23 | "@rollup/plugin-commonjs": "^28.0.3", 24 | "@rollup/plugin-node-resolve": "^16.0.1", 25 | "@types/lodash": "3.10.9", 26 | "@types/node": "10.17.6", 27 | "@types/screeps": "^3.3.8", 28 | "@typescript-eslint/eslint-plugin": "^8.32.1", 29 | "@typescript-eslint/parser": "^8.32.1", 30 | "eslint": "^9.27.0", 31 | "eslint-config-prettier": "^10.1.5", 32 | "eslint-import-resolver-typescript": "^4.3.5", 33 | "eslint-plugin-import": "^2.31.0", 34 | "eslint-plugin-prettier": "^5.4.0", 35 | "prettier": "^3.5.3", 36 | "prettier-plugin-organize-imports": "^4.1.0", 37 | "rollup": "2.79.1", 38 | "rollup-plugin-delete": "^3.0.1", 39 | "rollup-plugin-screeps-world": "^1.0.4", 40 | "rollup-plugin-typescript2": "^0.36.0", 41 | "tsconfig-paths": "^4.2.0", 42 | "typescript": "^5.8.3" 43 | }, 44 | "dependencies": { 45 | "source-map": "0.6.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Config} */ 2 | export default { 3 | semi: true, 4 | tabWidth: 2, 5 | singleQuote: true, 6 | trailingComma: 'none', 7 | printWidth: 120, 8 | arrowParens: 'avoid', 9 | endOfLine: 'auto', 10 | organizeImportsSkipDestructiveCodeActions: true, 11 | plugins: ['prettier-plugin-organize-imports'] 12 | }; 13 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import resolve from '@rollup/plugin-node-resolve'; 5 | import fs from 'fs'; 6 | import del from 'rollup-plugin-delete'; 7 | import screeps from 'rollup-plugin-screeps-world'; 8 | import typescript from 'rollup-plugin-typescript2'; 9 | 10 | let config; 11 | const { DEST } = process.env; 12 | if (!DEST) { 13 | console.log('Compiling Screeps Autonomous Framework...'); 14 | } else if (!(config = JSON.parse(fs.readFileSync('./screeps.json'))[DEST])) { 15 | throw new Error(`Upload destination "${DEST}" not found in screeps.json`); 16 | } 17 | 18 | /** @type {import('rollup').RollupOptions} */ 19 | export default { 20 | input: 'src/main.ts', 21 | output: { file: 'dist/main.js', format: 'cjs', sourcemap: true }, 22 | plugins: [ 23 | del({ targets: 'dist/*' }), 24 | resolve({ rootDir: 'src' }), 25 | commonjs(), 26 | typescript({ tsconfig: './tsconfig.json' }), 27 | screeps({ config, dryRun: !config }) 28 | ] 29 | }; 30 | -------------------------------------------------------------------------------- /screeps.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "token": "YOUR_TOKEN", 4 | "protocol": "https", 5 | "hostname": "screeps.com", 6 | "port": 443, 7 | "path": "/", 8 | "branch": "main" 9 | }, 10 | "sim": { 11 | "token": "YOUR_TOKEN", 12 | "protocol": "https", 13 | "hostname": "screeps.com", 14 | "port": 443, 15 | "path": "/", 16 | "branch": "sim" 17 | }, 18 | "season": { 19 | "token": "YOUR_TOKEN", 20 | "protocol": "https", 21 | "hostname": "screeps.com", 22 | "port": 443, 23 | "path": "/season", 24 | "branch": "main" 25 | }, 26 | "pserver": { 27 | "email": "Username", 28 | "password": "Password", 29 | "protocol": "http", 30 | "hostname": "127.0.0.1", 31 | "port": 21025, 32 | "path": "/", 33 | "branch": "main" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/classes/order.ts: -------------------------------------------------------------------------------- 1 | import { Priority } from 'enums/priority'; 2 | import { Role } from 'enums/role'; 3 | 4 | /** 5 | * The `Orders` class is used to spawn creeps from a priority-based queue. 6 | */ 7 | 8 | export class Order { 9 | public priority: Priority; 10 | public body: BodyPartConstant[]; 11 | public memory: { 12 | role: Role; 13 | tier: number; 14 | target?: string; 15 | homeroom?: string; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/enums/logLevel.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `LogLevel` enum is used by the log utility to color console messages. 3 | */ 4 | 5 | export enum LogLevel { 6 | Alert = 1, 7 | Success = 2, 8 | Error = 3, 9 | Warn = 4, 10 | Info = 5, 11 | Debug = 6, 12 | Verbose = 7 13 | } 14 | -------------------------------------------------------------------------------- /src/enums/operationType.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `OperationType` enum is used to classify special missions. 3 | */ 4 | 5 | export enum OperationType { 6 | Test = 0 7 | } 8 | -------------------------------------------------------------------------------- /src/enums/priority.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `Priority` enum is used to ensure important tasks are performed first. 3 | * 4 | * Spawns use the priority on orders to spawn more important creeps first. 5 | */ 6 | 7 | export enum Priority { 8 | Blocker = 0, 9 | Critical = 1, 10 | Important = 2, 11 | Standard = 3, 12 | Low = 4, 13 | Trivial = 5, 14 | Overflow = 6 15 | } 16 | -------------------------------------------------------------------------------- /src/enums/role.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `Role` enum represents the various roles that a creep can have within the game. 3 | * 4 | * It is used to assign and distinguish between the different tasks and behaviors that creeps will exhibit. 5 | */ 6 | 7 | export enum Role { 8 | Harvester = 0, 9 | Upgrader = 1, 10 | Builder = 2 11 | } 12 | -------------------------------------------------------------------------------- /src/enums/roomType.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `RoomType` enum is used to distinguish rooms with different behaviors. 3 | */ 4 | 5 | export enum RoomType { 6 | Normal = 1 7 | } 8 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import 'prototypes/creep'; 2 | import 'prototypes/room'; 3 | import 'types'; 4 | import 'utils/commands'; 5 | 6 | import { LogLevel } from 'enums/logLevel'; 7 | import { Priority } from 'enums/priority'; 8 | import { BuildManager } from 'managers/build'; 9 | import { HarvestManager } from 'managers/harvest'; 10 | import { MemoryManager } from 'managers/memory'; 11 | import { OperationManager } from 'managers/operation'; 12 | import { SpawnManager } from 'managers/spawn'; 13 | import { TowerManager } from 'managers/tower'; 14 | import { UpgradeManager } from 'managers/upgrade'; 15 | import { CreepService } from 'services/creep'; 16 | import { RoomService } from 'services/room'; 17 | import { ErrorMapper, USE_ERROR_MAPPER } from 'utils/errorMapper'; 18 | import { alert, setLogLevel, warning } from 'utils/log'; 19 | 20 | /** 21 | * Display an alert when global resets. 22 | * @see https://wiki.screepspl.us/index.php/Global_reset 23 | */ 24 | alert('✨=== Global Reset ===✨'); 25 | 26 | /** 27 | * Wrap the main loop with the error mapper if enabled. 28 | */ 29 | const loopWrapper = USE_ERROR_MAPPER ? ErrorMapper.wrapLoop.bind(ErrorMapper) : (f: () => void) => f(); 30 | 31 | /** 32 | * The main loop is the entry point for the bot. 33 | * @see https://docs.screeps.com/game-loop.html 34 | */ 35 | export const loop = loopWrapper(() => { 36 | initSettings(); 37 | 38 | const creepService = new CreepService(); 39 | const roomService = new RoomService(); 40 | const cpuLimit = getCpuLimit(); 41 | 42 | const taskManagers = [ 43 | new MemoryManager(), 44 | new TowerManager(roomService), 45 | new HarvestManager(roomService, creepService), 46 | new UpgradeManager(roomService, creepService), 47 | new BuildManager(roomService, creepService), 48 | new OperationManager(roomService, creepService) 49 | ]; 50 | 51 | const priorityList = [Priority.Critical, Priority.Important, Priority.Standard, Priority.Low, Priority.Trivial]; 52 | for (const priority of priorityList) { 53 | for (const manager of taskManagers) { 54 | if (priority === Priority.Critical || Game.cpu.getUsed() < cpuLimit) { 55 | manager.run(priority); 56 | } 57 | } 58 | } 59 | 60 | if (Game.cpu.bucket > 9500) { 61 | for (const manager of taskManagers) { 62 | if (Game.cpu.getUsed() < cpuLimit) { 63 | manager.run(Priority.Overflow); 64 | } 65 | } 66 | } 67 | 68 | new SpawnManager(roomService).run(); 69 | }); 70 | 71 | /** 72 | * Initialize bot settings in memory. 73 | */ 74 | function initSettings() { 75 | if (!Memory.settings) { 76 | warning('💎=== Script Loaded ===💎'); 77 | Memory.settings = {}; 78 | } 79 | if (!Memory.settings.loglevel) { 80 | setLogLevel(LogLevel.Verbose); 81 | } 82 | if (!Memory.settings.user) { 83 | Memory.settings.user = getUserNameOnSpawn(); 84 | } 85 | } 86 | 87 | /** 88 | * Identify bot owner using `Game.spawns`. 89 | * @returns Owner of the first spawn structure. 90 | */ 91 | function getUserNameOnSpawn() { 92 | const spawns = Object.values(Game.spawns); 93 | return spawns[0]?.owner.username; 94 | } 95 | 96 | /** 97 | * Get the CPU limit based on CPU in bucket. 98 | * @returns CPU limit for this tick. 99 | */ 100 | function getCpuLimit() { 101 | const { bucket, limit } = Game.cpu; 102 | if (!limit) return 500; // Sim room 103 | 104 | const multipliers = [1.5, 1.3, 1.1, 1, 0.9, 0.8, 0.7, 0.6, 0.5]; 105 | const thresholds = [9500, 9000, 8000, 5000, 4000, 3000, 2000, 1000]; 106 | 107 | for (let i = 0; i < thresholds.length; i++) { 108 | if (bucket > thresholds[i]) { 109 | return limit * multipliers[i]; 110 | } 111 | } 112 | 113 | return limit * 0.5; 114 | } 115 | -------------------------------------------------------------------------------- /src/managers/build.ts: -------------------------------------------------------------------------------- 1 | import { Order } from 'classes/order'; 2 | import { Priority } from 'enums/priority'; 3 | import { Role } from 'enums/role'; 4 | import { Manager } from 'managers/manager'; 5 | import * as Builder from 'roles/builder'; 6 | import { CreepService } from 'services/creep'; 7 | import { RoomService } from 'services/room'; 8 | import { getCreepsInQueue, orderCreep } from 'utils/order'; 9 | import { getMaxTierSimpleWorker, getSimpleWorkerBody } from 'utils/profile'; 10 | 11 | /** 12 | * The `BuildManager` class orchestrates the build-related activities and behaviors of the bot. 13 | * 14 | * This class should be utilized whenever you need to control and manage Builder creeps and their 15 | * associated tasks within the framework. 16 | */ 17 | 18 | export class BuildManager extends Manager { 19 | private roomService: RoomService; 20 | private creepService: CreepService; 21 | 22 | readonly MEMORY_LASTRUN = 'lastRun'; 23 | 24 | constructor(roomService: RoomService, creepService: CreepService) { 25 | super('BuildManager'); 26 | this.roomService = roomService; 27 | this.creepService = creepService; 28 | } 29 | 30 | public run(pri: Priority) { 31 | if (pri === Priority.Low) { 32 | this.creepService.runCreeps(Role.Builder, Builder.run); 33 | 34 | const lastRun = this.getValue(this.MEMORY_LASTRUN); 35 | if (!lastRun || lastRun + 20 < Game.time) { 36 | const rooms = this.roomService.getNormalRooms(); 37 | this.organizeStructureBuilding(rooms); 38 | this.setValue(this.MEMORY_LASTRUN, Game.time); 39 | } 40 | } 41 | } 42 | 43 | private organizeStructureBuilding(rooms: Room[]) { 44 | for (const room of rooms) { 45 | this.orderBuilder(room); 46 | } 47 | } 48 | 49 | private orderBuilder(room: Room) { 50 | const active = this.creepService.getCreeps(Role.Builder).length; 51 | const ordered = getCreepsInQueue(room, Role.Builder); 52 | 53 | if (active + ordered === 0) { 54 | const order = new Order(); 55 | const maxTier = getMaxTierSimpleWorker(room.energyCapacityAvailable); 56 | order.body = getSimpleWorkerBody(maxTier); 57 | order.priority = Priority.Standard; 58 | order.memory = { 59 | role: Role.Builder, 60 | tier: maxTier 61 | }; 62 | orderCreep(room, order); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/managers/harvest.ts: -------------------------------------------------------------------------------- 1 | import { Order } from 'classes/order'; 2 | import { Priority } from 'enums/priority'; 3 | import { Role } from 'enums/role'; 4 | import { Manager } from 'managers/manager'; 5 | import * as Harvester from 'roles/harvester'; 6 | import { CreepService } from 'services/creep'; 7 | import { RoomService } from 'services/room'; 8 | import { getCreepsInQueue, orderCreep } from 'utils/order'; 9 | import { getMaxTierSimpleWorker, getSimpleWorkerBody } from 'utils/profile'; 10 | 11 | /** 12 | * The `HarvestManager` class orchestrates the energy gathering activities and behaviors of the bot. 13 | * 14 | * This class should be utilized whenever you need to control and manage Harvester creeps and their 15 | * associated tasks within the framework. 16 | */ 17 | 18 | export class HarvestManager extends Manager { 19 | private roomService: RoomService; 20 | private creepService: CreepService; 21 | 22 | readonly MEMORY_LASTRUN = 'lastRun'; 23 | 24 | constructor(roomService: RoomService, creepService: CreepService) { 25 | super('HarvestManager'); 26 | this.roomService = roomService; 27 | this.creepService = creepService; 28 | } 29 | 30 | public run(pri: Priority) { 31 | if (pri === Priority.Low) { 32 | this.creepService.runCreeps(Role.Harvester, Harvester.run); 33 | 34 | const lastRun = this.getValue(this.MEMORY_LASTRUN); 35 | if (!lastRun || lastRun + 20 < Game.time) { 36 | const rooms = this.roomService.getNormalRooms(); 37 | for (const room of rooms) { 38 | this.organizeEnergyHarvesting(room); 39 | } 40 | this.setValue(this.MEMORY_LASTRUN, Game.time); 41 | } 42 | } 43 | } 44 | 45 | private organizeEnergyHarvesting(room: Room) { 46 | const sources = room.find(FIND_SOURCES); 47 | for (const source of sources) { 48 | this.orderHarvesters(room, source.id, room.name); 49 | } 50 | } 51 | 52 | private orderHarvesters(room: Room, sourceId: string, sourceRoom: string) { 53 | const spawn = room.getMySpawn(); 54 | if (!spawn) { 55 | return; 56 | } 57 | 58 | const sourceTarget = sourceRoom + '-' + sourceId; 59 | const active = this.creepService.getCreeps(Role.Harvester, sourceTarget, room.name).length; 60 | const ordered = getCreepsInQueue(room, Role.Harvester, sourceTarget); 61 | 62 | if (active + ordered === 0) { 63 | const order = new Order(); 64 | const maxTier = getMaxTierSimpleWorker(room.energyCapacityAvailable); 65 | order.body = getSimpleWorkerBody(maxTier); 66 | if (room.name === sourceRoom) { 67 | order.priority = Priority.Important; 68 | } else { 69 | order.priority = Priority.Standard; 70 | } 71 | order.memory = { 72 | role: Role.Harvester, 73 | tier: maxTier, 74 | target: sourceTarget 75 | }; 76 | orderCreep(room, order); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/managers/manager.ts: -------------------------------------------------------------------------------- 1 | import { Priority } from 'enums/priority'; 2 | 3 | /** 4 | * The `Manager` class serves as the foundational abstract class for all manager-type classes within 5 | * the framework, orchestrating the general behaviors and functionalities shared amongst them. 6 | * 7 | * This class should not be instantiated directly but instead should be extended by other manager classes 8 | * requiring foundational functionalities and structures. 9 | */ 10 | 11 | export abstract class Manager { 12 | private name: string; 13 | 14 | constructor(name: string) { 15 | this.name = name; 16 | this.memoryCheck(); 17 | } 18 | 19 | public abstract run(pri: Priority): void; 20 | 21 | protected memoryCheck() { 22 | if (!Memory.managers) { 23 | Memory.managers = {}; 24 | } 25 | if (!Memory.managers[this.name]) { 26 | Memory.managers[this.name] = {}; 27 | } 28 | } 29 | 30 | protected getValue(name: string) { 31 | return Memory.managers[this.name][name]; 32 | } 33 | 34 | protected setValue(name: string, value: number) { 35 | Memory.managers[this.name][name] = value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/managers/memory.ts: -------------------------------------------------------------------------------- 1 | import { Priority } from 'enums/priority'; 2 | import { Manager } from 'managers/manager'; 3 | 4 | /** 5 | * The `MemoryManager` class orchestrates memory clean up efforts for the bot. 6 | * 7 | * Expired creeps will be removed from `Memory.creeps` when the creep is gone from `Game.creeps` 8 | */ 9 | 10 | export class MemoryManager extends Manager { 11 | readonly MEMORY_LASTRUN = 'lastRun'; 12 | 13 | constructor() { 14 | super('MemoryManager'); 15 | } 16 | 17 | public run(pri: Priority) { 18 | if (pri === Priority.Low) { 19 | const lastRun = this.getValue(this.MEMORY_LASTRUN); 20 | if (!lastRun || lastRun + 20 < Game.time) { 21 | this.deleteCreepsFromMemory(); 22 | this.setValue(this.MEMORY_LASTRUN, Game.time); 23 | } 24 | } 25 | } 26 | 27 | private deleteCreepsFromMemory() { 28 | for (const name in Memory.creeps) { 29 | if (!Game.creeps[name]) { 30 | delete Memory.creeps[name]; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/managers/operation.ts: -------------------------------------------------------------------------------- 1 | import { OperationType } from 'enums/operationType'; 2 | import { Priority } from 'enums/priority'; 3 | import { Manager } from 'managers/manager'; 4 | import * as TestOperation from 'operations/test'; 5 | import { CreepService } from 'services/creep'; 6 | import { RoomService } from 'services/room'; 7 | import { warning } from 'utils/log'; 8 | 9 | /** 10 | * The `OperationManager` class orchestrates the administration of operations or special missions. 11 | * 12 | * This class should be utilized to implement and perform any new missions. 13 | * 14 | * The `RoomService` and `CreepService` are readily available to provide critical infrastructure 15 | * management to your operation run sequence. 16 | */ 17 | 18 | export class OperationManager extends Manager { 19 | private roomService: RoomService; 20 | private creepService: CreepService; 21 | 22 | readonly MEMORY_MAINTAIN = 'lastRunMaintain'; 23 | 24 | constructor(roomService: RoomService, creepService: CreepService) { 25 | super('OperationManager'); 26 | this.roomService = roomService; 27 | this.creepService = creepService; 28 | } 29 | 30 | public run(pri: Priority) { 31 | if (pri === Priority.Trivial) { 32 | const lastRunMaintain = this.getValue(this.MEMORY_MAINTAIN); 33 | if (!lastRunMaintain || lastRunMaintain + 1000 < Game.time) { 34 | this.deleteOldOperations(); 35 | this.setValue(this.MEMORY_MAINTAIN, Game.time); 36 | } 37 | } 38 | 39 | if (!Memory.operations) { 40 | Memory.operations = []; 41 | } 42 | 43 | for (const op of Memory.operations) { 44 | switch (op.type) { 45 | case OperationType.Test: 46 | // Note: The Test operation is a minimal example 47 | if (op.active && !TestOperation.victoryConditionReached(op)) { 48 | TestOperation.run(op, pri); 49 | } else { 50 | op.active = false; 51 | } 52 | break; 53 | } 54 | } 55 | } 56 | 57 | private deleteOldOperations() { 58 | if (Memory.operations) { 59 | const inactive = Memory.operations.filter(op => !op.active); 60 | if (inactive.length > 0) { 61 | warning(`Removing ${inactive.length} inactive operations.`); 62 | Memory.operations = Memory.operations.filter(op => op.active); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/managers/spawn.ts: -------------------------------------------------------------------------------- 1 | import { Order } from 'classes/order'; 2 | import { Role } from 'enums/role'; 3 | import { Manager } from 'managers/manager'; 4 | import { RoomService } from 'services/room'; 5 | import { verbose } from 'utils/log'; 6 | import { getUniqueId } from 'utils/order'; 7 | 8 | /** 9 | * The `SpawnManager` class orchestrates the spawning of creeps using the priority-based orders queue. 10 | * 11 | * This class should be utilized whenever you need to control spawns and their associated logic 12 | * for creating, renewing and recycling creeps. 13 | */ 14 | 15 | export class SpawnManager extends Manager { 16 | private roomService: RoomService; 17 | 18 | constructor(roomService: RoomService) { 19 | super('SpawnManager'); 20 | this.roomService = roomService; 21 | } 22 | 23 | public run() { 24 | const rooms = this.roomService.getNormalRooms(); 25 | for (const room of rooms) { 26 | const spawns = room.find(FIND_MY_SPAWNS).filter(s => s.isActive() && !s.spawning); 27 | if (spawns.length) { 28 | this.processQueue(room, spawns); 29 | } 30 | } 31 | } 32 | 33 | private processQueue(room: Room, spawns: StructureSpawn[]) { 34 | if (!room.memory.orders) { 35 | room.memory.orders = []; 36 | return; 37 | } 38 | 39 | const spawn = spawns[0]; 40 | if (!spawn || spawn.spawning || room.memory.orders.length === 0) { 41 | return; 42 | } 43 | 44 | room.memory.orders = room.memory.orders.sort((a, b) => a.priority - b.priority); 45 | 46 | const order = room.memory.orders.shift() as Order; 47 | const name = Role[order.memory.role] + getUniqueId(); 48 | 49 | if (room.name !== spawn.room.name) { 50 | order.memory.homeroom = room.name; 51 | } 52 | 53 | const status = spawn.spawnCreep(order.body, name, { 54 | memory: order.memory 55 | }); 56 | 57 | if (status === OK) { 58 | verbose(`Spawned: ${Role[order.memory.role]} (${order.memory.target}) - ${name}`, spawn.room.name); 59 | } else { 60 | // Log.warning(`Unable to spawn ${Role[order.memory.role]} (status code: ${status})`, spawn.room.name); 61 | room.memory.orders.unshift(order); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/managers/tower.ts: -------------------------------------------------------------------------------- 1 | import { Priority } from 'enums/priority'; 2 | import { Manager } from 'managers/manager'; 3 | import { RoomService } from 'services/room'; 4 | 5 | /** 6 | * The `TowerManager` class orchestrates the defensive tower structure behaviors of the bot. 7 | * 8 | * This class should be utilized whenever you need to control towers and their associated logic 9 | * for attacking or healing creeps, or repairing structures. 10 | */ 11 | 12 | export class TowerManager extends Manager { 13 | private roomService: RoomService; 14 | 15 | readonly MEMORY_LASTRUN = 'lastRun'; 16 | 17 | constructor(roomService: RoomService) { 18 | super('TowerManager'); 19 | this.roomService = roomService; 20 | } 21 | 22 | public run(pri: Priority) { 23 | if (pri === Priority.Critical) { 24 | const normalRooms = this.roomService.getNormalRooms(); 25 | for (const room of normalRooms) { 26 | this.controlTowers(room); 27 | } 28 | } 29 | } 30 | 31 | private controlTowers(room: Room) { 32 | const towersWithEnergy = room.find(FIND_STRUCTURES, { 33 | filter: structure => 34 | structure.structureType === STRUCTURE_TOWER && structure.store[RESOURCE_ENERGY] > TOWER_ENERGY_COST 35 | }); 36 | 37 | for (const tower of towersWithEnergy) { 38 | const closestHostile = tower.pos.findClosestByRange(FIND_HOSTILE_CREEPS); 39 | if (closestHostile) { 40 | tower.attack(closestHostile); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/managers/upgrade.ts: -------------------------------------------------------------------------------- 1 | import { Order } from 'classes/order'; 2 | import { Priority } from 'enums/priority'; 3 | import { Role } from 'enums/role'; 4 | import { Manager } from 'managers/manager'; 5 | import * as Upgrader from 'roles/upgrader'; 6 | import { CreepService } from 'services/creep'; 7 | import { RoomService } from 'services/room'; 8 | import { getCreepsInQueue, orderCreep } from 'utils/order'; 9 | import { getHeavyWorkerBody, getMaxTierHeavyWorker } from 'utils/profile'; 10 | 11 | /** 12 | * The `UpgradeManager` class orchestrates the controller upgrading activities and behaviors of the bot. 13 | * 14 | * This class should be utilized whenever you need to control and manage Upgrader creeps and their 15 | * associated tasks within the framework. 16 | */ 17 | 18 | export class UpgradeManager extends Manager { 19 | private roomService: RoomService; 20 | private creepService: CreepService; 21 | 22 | readonly MEMORY_LASTRUN = 'lastRun'; 23 | 24 | constructor(roomService: RoomService, creepService: CreepService) { 25 | super('UpgradeManager'); 26 | this.roomService = roomService; 27 | this.creepService = creepService; 28 | } 29 | 30 | run(pri: Priority) { 31 | if (pri === Priority.Low) { 32 | this.creepService.runCreeps(Role.Upgrader, Upgrader.run); 33 | 34 | const lastRun = this.getValue(this.MEMORY_LASTRUN); 35 | if (!lastRun || lastRun + 20 < Game.time) { 36 | const rooms = this.roomService.getNormalRooms(); 37 | this.organizeControllerUpgrading(rooms); 38 | this.setValue(this.MEMORY_LASTRUN, Game.time); 39 | } 40 | } 41 | } 42 | 43 | private organizeControllerUpgrading(rooms: Room[]) { 44 | for (const room of rooms) { 45 | if (!room.controller) { 46 | continue; 47 | } 48 | this.orderUpgrader(room.controller); 49 | } 50 | } 51 | 52 | private orderUpgrader(controller: StructureController) { 53 | const room = controller.room; 54 | const active = this.creepService.getCreeps(Role.Upgrader, controller.id).length; 55 | const ordered = getCreepsInQueue(controller.room, Role.Upgrader, controller.id); 56 | 57 | if (active + ordered === 0) { 58 | const order = new Order(); 59 | const maxTier = getMaxTierHeavyWorker(room.energyCapacityAvailable); 60 | order.body = getHeavyWorkerBody(maxTier); 61 | order.priority = Priority.Standard; 62 | order.memory = { 63 | role: Role.Upgrader, 64 | target: controller.id, 65 | tier: maxTier 66 | }; 67 | orderCreep(controller.room, order); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/operations/test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Operations can be used to facilitate special missions. This operation provides an example template. 3 | * @module 4 | */ 5 | 6 | import { OperationType } from 'enums/operationType'; 7 | import { Priority } from 'enums/priority'; 8 | import { info, success } from 'utils/log'; 9 | 10 | /** 11 | * Conditions to indicate the operation is complete. 12 | */ 13 | export enum VictoryCondition { 14 | GameTime = 1 15 | } 16 | 17 | /** 18 | * Memory data used for the operation. 19 | */ 20 | export class Data implements OperationData { 21 | type: OperationType = OperationType.Test; 22 | active = true; 23 | victoryCondition: VictoryCondition; 24 | victoryValue: number; 25 | } 26 | 27 | /** 28 | * Perform tasks while the operation is active. 29 | */ 30 | export function run(operation: Data, pri: Priority) { 31 | if (pri === Priority.Low) { 32 | if (Game.time % 10 === 0) { 33 | helloWorld(operation); 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * Check if the operation has been completed. 40 | */ 41 | export function victoryConditionReached(operation: Data) { 42 | if (operation.victoryCondition === VictoryCondition.GameTime) { 43 | if (Game.time > operation.victoryValue) { 44 | success(`Test operation finished at ${Game.time}.`); 45 | operation.active = false; 46 | return true; 47 | } 48 | } 49 | return false; 50 | } 51 | 52 | function helloWorld(operation: Data) { 53 | if (Game.time > operation.victoryValue) { 54 | return; 55 | } 56 | info(`Test operation is active for ${operation.victoryValue - Game.time} more ticks.`); 57 | } 58 | -------------------------------------------------------------------------------- /src/prototypes/creep.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | interface Creep { 5 | /** Checks if a creep is carrying the max amount of resources. */ 6 | isFull: boolean; 7 | /** Checks if a creep had a specific state (task) this tick. */ 8 | hadState(state: number): boolean; 9 | /** Checks if a creep currently has a specific state (task). */ 10 | hasState(state?: number): boolean; 11 | /** Changes the current state (task) for a creep. */ 12 | setState(state: number): void; 13 | /** Changes the current state (task) for a creep and executes the runFunction. */ 14 | setStateAndRun(state: number, runFunction: (...args: any[]) => void, ...args: any[]): void; 15 | 16 | /** @private */ 17 | _states?: number[]; 18 | } 19 | } 20 | 21 | Object.defineProperty(Creep.prototype, 'isFull', { 22 | get(this: Creep) { 23 | return !this.store.getFreeCapacity(); 24 | } 25 | }); 26 | 27 | Creep.prototype.hadState = function (state) { 28 | if (!this._states) { 29 | this._states = []; 30 | } 31 | return this._states.includes(state); 32 | }; 33 | 34 | Creep.prototype.hasState = function (state) { 35 | if (state === undefined) { 36 | return this.memory.state !== state; 37 | } 38 | return this.memory.state === state; 39 | }; 40 | 41 | Creep.prototype.setState = function (state) { 42 | if (!this._states) { 43 | this._states = []; 44 | } 45 | this._states.push(state); 46 | this.memory.state = state; 47 | }; 48 | 49 | Creep.prototype.setStateAndRun = function (state, runFunction, ...args) { 50 | const canRunFunction = !this.hadState(state); 51 | this.setState(state); 52 | if (canRunFunction) { 53 | runFunction(this, args); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /src/prototypes/room.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | interface Room { 5 | /** Finds the first owned spawn in a room if available. */ 6 | getMySpawn(): StructureSpawn | undefined; 7 | 8 | /** @private */ 9 | _mySpawn?: StructureSpawn; 10 | } 11 | } 12 | 13 | Room.prototype.getMySpawn = function () { 14 | if (!this._mySpawn) { 15 | this._mySpawn = this.find(FIND_MY_SPAWNS)?.[0]; 16 | } 17 | return this._mySpawn; 18 | }; 19 | -------------------------------------------------------------------------------- /src/roles/builder.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A creep role that constructs structures. 3 | * @module 4 | */ 5 | 6 | import { logUnknownState } from 'utils/creep'; 7 | 8 | enum State { 9 | HarvestEnergy = 1, 10 | BuildConstruction = 2 11 | } 12 | 13 | export function run(creep: Creep) { 14 | if (!creep.hasState()) { 15 | creep.setState(State.HarvestEnergy); 16 | } 17 | 18 | switch (creep.memory.state) { 19 | case State.HarvestEnergy: 20 | runHarvestEnergy(creep); 21 | break; 22 | case State.BuildConstruction: 23 | runBuildConstruction(creep); 24 | break; 25 | default: 26 | logUnknownState(creep); 27 | creep.setStateAndRun(State.HarvestEnergy, runHarvestEnergy); 28 | break; 29 | } 30 | } 31 | 32 | function runHarvestEnergy(creep: Creep) { 33 | if (creep.isFull) { 34 | creep.say('🔨Build'); 35 | creep.setStateAndRun(State.BuildConstruction, runBuildConstruction); 36 | return; 37 | } 38 | 39 | const source = creep.room.find(FIND_SOURCES)?.[0]; 40 | if (source) { 41 | if (creep.harvest(source) === ERR_NOT_IN_RANGE) { 42 | creep.moveTo(source, { visualizePathStyle: { stroke: '#ffaa00' } }); 43 | } 44 | } 45 | } 46 | 47 | function runBuildConstruction(creep: Creep) { 48 | if (!creep.store[RESOURCE_ENERGY]) { 49 | creep.say('⚡Harvest'); 50 | creep.setStateAndRun(State.HarvestEnergy, runHarvestEnergy); 51 | return; 52 | } 53 | 54 | const constructionSite = creep.room.find(FIND_CONSTRUCTION_SITES)?.[0]; 55 | if (constructionSite) { 56 | if (creep.build(constructionSite) === ERR_NOT_IN_RANGE) { 57 | creep.moveTo(constructionSite, { visualizePathStyle: { stroke: '#ffffff' } }); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/roles/harvester.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A creep role responsible for collecting energy. 3 | * @module 4 | */ 5 | 6 | import { logUnknownState } from 'utils/creep'; 7 | 8 | enum State { 9 | HarvestEnergy = 1, 10 | TransferEnergy = 2 11 | } 12 | 13 | export function run(creep: Creep) { 14 | if (!creep.hasState()) { 15 | creep.setState(State.HarvestEnergy); 16 | } 17 | 18 | switch (creep.memory.state) { 19 | case State.HarvestEnergy: 20 | runHarvestEnergy(creep); 21 | break; 22 | case State.TransferEnergy: 23 | runTransferEnergy(creep); 24 | break; 25 | default: 26 | logUnknownState(creep); 27 | creep.setStateAndRun(State.HarvestEnergy, runHarvestEnergy); 28 | break; 29 | } 30 | } 31 | 32 | function runHarvestEnergy(creep: Creep) { 33 | if (creep.isFull) { 34 | creep.say('💫Transfer'); 35 | creep.setStateAndRun(State.TransferEnergy, runTransferEnergy); 36 | return; 37 | } 38 | 39 | const source = getTargetSource(creep); 40 | if (source) { 41 | if (creep.harvest(source) === ERR_NOT_IN_RANGE) { 42 | creep.moveTo(source, { visualizePathStyle: { stroke: '#ffaa00' } }); 43 | } 44 | } 45 | } 46 | 47 | function runTransferEnergy(creep: Creep) { 48 | if (!creep.store[RESOURCE_ENERGY]) { 49 | creep.say('⚡Harvest'); 50 | creep.setStateAndRun(State.HarvestEnergy, runHarvestEnergy); 51 | return; 52 | } 53 | 54 | const targetStructure = creep.room.find(FIND_STRUCTURES, { 55 | filter: structure => 56 | (structure.structureType === STRUCTURE_EXTENSION || 57 | structure.structureType === STRUCTURE_SPAWN || 58 | structure.structureType === STRUCTURE_TOWER) && 59 | structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0 60 | })?.[0]; 61 | 62 | if (targetStructure) { 63 | if (creep.transfer(targetStructure, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) { 64 | creep.moveTo(targetStructure, { visualizePathStyle: { stroke: '#ffffff' } }); 65 | } 66 | } 67 | } 68 | 69 | function getTargetSource(creep: Creep) { 70 | if (!creep.memory.source && creep.memory.target) { 71 | creep.memory.source = creep.memory.target.split('-')[1] as Id; 72 | } 73 | if (!creep.memory.source) { 74 | return null; 75 | } 76 | return Game.getObjectById(creep.memory.source); 77 | } 78 | -------------------------------------------------------------------------------- /src/roles/upgrader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A creep role that works on upgrading the controller. 3 | * @module 4 | */ 5 | 6 | import { logUnknownState } from 'utils/creep'; 7 | 8 | enum State { 9 | HarvestEnergy = 1, 10 | UpgradeController = 2 11 | } 12 | 13 | export function run(creep: Creep) { 14 | if (!creep.hasState()) { 15 | creep.setState(State.HarvestEnergy); 16 | } 17 | 18 | switch (creep.memory.state) { 19 | case State.HarvestEnergy: 20 | runHarvestEnergy(creep); 21 | break; 22 | case State.UpgradeController: 23 | runUpgradeController(creep); 24 | break; 25 | default: 26 | logUnknownState(creep); 27 | creep.setStateAndRun(State.HarvestEnergy, runHarvestEnergy); 28 | break; 29 | } 30 | } 31 | 32 | function runHarvestEnergy(creep: Creep) { 33 | if (creep.isFull) { 34 | creep.say('🙏Upgrade'); 35 | creep.setStateAndRun(State.UpgradeController, runUpgradeController); 36 | return; 37 | } 38 | 39 | const source = creep.room.find(FIND_SOURCES)?.[0]; 40 | if (creep.harvest(source) === ERR_NOT_IN_RANGE) { 41 | creep.moveTo(source, { visualizePathStyle: { stroke: '#ffaa00' } }); 42 | } 43 | } 44 | 45 | function runUpgradeController(creep: Creep) { 46 | if (!creep.store[RESOURCE_ENERGY]) { 47 | creep.say('⚡Harvest'); 48 | creep.setStateAndRun(State.HarvestEnergy, runHarvestEnergy); 49 | return; 50 | } 51 | 52 | const { controller } = creep.room; 53 | if (controller) { 54 | if (creep.upgradeController(controller) === ERR_NOT_IN_RANGE) { 55 | creep.moveTo(controller, { visualizePathStyle: { stroke: '#ffffff' } }); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/services/creep.ts: -------------------------------------------------------------------------------- 1 | import { Role } from 'enums/role'; 2 | import { warning } from 'utils/log'; 3 | 4 | /** 5 | * An object to organize creeps by role. 6 | */ 7 | type CreepDictionary = { [role in Role]?: Creep[] }; 8 | 9 | /** 10 | * A class to facilitate accessing creeps by role, target, or home room. 11 | */ 12 | export class CreepService { 13 | /** 14 | * An object containing all creeps in the colony. 15 | */ 16 | private creepDictionary: CreepDictionary; 17 | 18 | constructor() { 19 | this.creepDictionary = this.makeDictionary(); 20 | } 21 | 22 | /** 23 | * Checks if a creep should continue executing tasks. 24 | * @param creep The creep to consider. 25 | */ 26 | public creepShouldRun(creep: Creep) { 27 | if (!creep.memory.homeroom) { 28 | creep.memory.homeroom = creep.room.name; 29 | } 30 | 31 | // Wait to run when creep is spawning 32 | if (creep.spawning) { 33 | return false; 34 | } 35 | 36 | // Add current state to creep._stateEvents 37 | if (creep.memory.state) { 38 | creep.setState(creep.memory.state); 39 | } 40 | 41 | return true; 42 | } 43 | 44 | /** 45 | * Instructs all creeps with a matching role to perform tasks using a specific method. 46 | * @param role The role that will be used to find relavent creeps. 47 | * @param creepRunMethod The method that will be used to run tasks. 48 | */ 49 | public runCreeps(role: Role, creepRunMethod: Function) { 50 | const creepsWithRole = this.getAllOfRole(role); 51 | if (!creepsWithRole) { 52 | return; 53 | } 54 | for (const creep of creepsWithRole) { 55 | if (this.creepShouldRun(creep)) { 56 | creepRunMethod(creep); 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * Fetch creeps by role, target, and/or homeroom. 63 | * @param role (optional) The role of creeps to be fetched. 64 | * @param target (optional) The target of creeps to be fetched. 65 | * @param homeroom (optional) The home room of creeps to be fetched. 66 | */ 67 | public getCreeps(role: Role | null = null, target: string | null = null, homeroom: string | null = null) { 68 | const creeps: Creep[] = []; 69 | 70 | if (role !== null) { 71 | if (!this.creepDictionary[role]) { 72 | return creeps; 73 | } 74 | for (const creep of this.creepDictionary[role]!) { 75 | if ( 76 | (target === null || creep.memory.target === target) && 77 | (homeroom === null || creep.memory.homeroom === homeroom) 78 | ) { 79 | creeps.push(creep); 80 | } 81 | } 82 | return creeps; 83 | } 84 | 85 | for (const name in Game.creeps) { 86 | const creep = Game.creeps[name]; 87 | if ( 88 | (target === null || creep.memory.target === target) && 89 | (homeroom === null || creep.memory.homeroom === homeroom) && 90 | (role === null || creep.memory.role === role) 91 | ) { 92 | creeps.push(creep); 93 | } 94 | } 95 | return creeps; 96 | } 97 | 98 | /** 99 | * Fetch creeps by role. 100 | * @param role The role of creeps to be fetched. 101 | */ 102 | public getAllOfRole(role: Role) { 103 | if (this.creepDictionary[role]) { 104 | return this.creepDictionary[role]; 105 | } 106 | return []; 107 | } 108 | 109 | /** 110 | * Creates an object of all creeps organized by role using `Game.creeps`. 111 | */ 112 | protected makeDictionary() { 113 | const creeps: CreepDictionary = {}; 114 | 115 | for (const name in Game.creeps) { 116 | const creep = Game.creeps[name]; 117 | 118 | if (creep.memory.role === undefined) { 119 | warning(`Creep ${creep.name} has no role`, creep.room.name); 120 | continue; 121 | } 122 | 123 | if (!creeps[creep.memory.role]) { 124 | creeps[creep.memory.role] = []; 125 | } 126 | creeps[creep.memory.role]!.push(creep); 127 | } 128 | return creeps; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/services/room.ts: -------------------------------------------------------------------------------- 1 | import { RoomType } from 'enums/roomType'; 2 | 3 | /** 4 | * An object to manage rooms in the colony by type. 5 | */ 6 | type RoomDictionary = Record; 7 | 8 | /** 9 | * A class to facilitate accessing rooms in the colony. 10 | */ 11 | export class RoomService { 12 | /** 13 | * An object containing all rooms in the colony. 14 | */ 15 | private roomDictionary: RoomDictionary; 16 | 17 | constructor() { 18 | this.roomDictionary = this.makeDictionary(); 19 | } 20 | 21 | /** 22 | * Fetches all rooms in the colony with the Normal room type. 23 | */ 24 | public getNormalRooms() { 25 | const rooms: Room[] = []; 26 | if (this.roomDictionary[RoomType.Normal]) { 27 | rooms.push(...this.roomDictionary[RoomType.Normal]); 28 | } 29 | return rooms; 30 | } 31 | 32 | /** 33 | * Creates an object of all rooms in the colony indexed by type using `Game.rooms`. 34 | */ 35 | protected makeDictionary() { 36 | const rooms: RoomDictionary = { 37 | [RoomType.Normal]: [] 38 | }; 39 | 40 | for (const roomName in Game.rooms) { 41 | const room = Game.rooms[roomName]; 42 | 43 | if (!room.controller || !room.controller.my || room.controller.level < 1) { 44 | continue; 45 | } 46 | 47 | if (!room.memory.t) { 48 | room.memory.t = RoomType.Normal; 49 | } 50 | 51 | if (!rooms[room.memory.t]) { 52 | rooms[room.memory.t] = []; 53 | } 54 | rooms[room.memory.t].push(room); 55 | } 56 | return rooms; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | import { Order } from 'classes/order'; 4 | import { LogLevel } from 'enums/logLevel'; 5 | import { OperationType } from 'enums/operationType'; 6 | import { Role } from 'enums/role'; 7 | import { RoomType } from 'enums/roomType'; 8 | 9 | declare global { 10 | // Lodash 3.10 types 11 | // @ts-ignore 12 | const _: typeof _; 13 | 14 | /** 15 | * Global `Memory` object. 16 | * @see https://docs.screeps.com/global-objects.html#Memory-object 17 | */ 18 | interface Memory { 19 | creeps: Record; 20 | flags: Record; 21 | managers: Record; 22 | operations: OperationData[]; 23 | powerCreeps: Record; 24 | rooms: Record; 25 | settings: Partial; 26 | spawns: Record; 27 | } 28 | 29 | interface CreepMemory { 30 | role: Role; 31 | tier: number; 32 | state?: number; 33 | homeroom?: string; 34 | target?: string; 35 | source?: Id; 36 | } 37 | 38 | interface OperationData { 39 | type: OperationType; 40 | active: boolean; 41 | victoryCondition: number; 42 | victoryValue: number; 43 | } 44 | 45 | interface RoomMemory { 46 | orders?: Order[]; 47 | t?: RoomType; 48 | } 49 | 50 | interface SettingsMemory { 51 | user: string; 52 | loglevel: LogLevel; 53 | } 54 | 55 | type ManagerMemory = Record; 56 | } 57 | -------------------------------------------------------------------------------- /src/utils/commands.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The commands utility provides global commands for the Screeps game console. 3 | * @module 4 | */ 5 | 6 | import { error } from 'utils/log'; 7 | import { createTestOperation, isTestOperationActive } from 'utils/operation'; 8 | 9 | declare namespace global { 10 | /* eslint-disable no-var */ 11 | 12 | /** 13 | * A global command to create a Test operation from the Screeps game console. 14 | * @param duration (optional) The amount of time the operation will be active. 15 | */ 16 | var addTestOperation: (duration?: number) => string | void; 17 | 18 | /* eslint-enable no-var */ 19 | } 20 | 21 | /** 22 | * Return log messages to the console instead of calling `console.log()`. 23 | */ 24 | global.addTestOperation = (duration = 50) => { 25 | if (duration < 10) { 26 | return error('Test operation duration must be at least 10 ticks.', null, false); 27 | } 28 | if (isTestOperationActive()) { 29 | return error('Test operation is already active.', null, false); 30 | } 31 | return createTestOperation(duration, false); 32 | }; 33 | -------------------------------------------------------------------------------- /src/utils/creep.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The creep utility provides helper methods for creeps. 3 | * @module 4 | */ 5 | 6 | import { error } from 'utils/log'; 7 | 8 | /** 9 | * Log an error message when a creep has an unknown state. 10 | */ 11 | export function logUnknownState(creep: Creep) { 12 | error(`Creep ${creep.name} has unexpected state (${creep.memory.state})`, creep.room.name); 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/errorMapper.ts: -------------------------------------------------------------------------------- 1 | import { SourceMapConsumer } from 'source-map'; 2 | import * as log from './log'; 3 | 4 | /** 5 | * Set to `true` to enable error mapping. 6 | */ 7 | export const USE_ERROR_MAPPER = true; 8 | 9 | /** 10 | * ErrorMapper 11 | * 12 | * This error mapper is designed to wrap the main `loop` function in a try/catch block 13 | * and provide stack traces which are mapped to your source code via source maps. 14 | * 15 | * This allows you to see the original source code when an error occurs, rather than 16 | * the compiled code, making debugging much nicer. 17 | * 18 | * This does however come with a CPU cost, especially on the first call after a reset, 19 | * so you can disable this feature by setting `USE_ERROR_MAPPER` to `false` if needed. 20 | */ 21 | export class ErrorMapper { 22 | // Cache consumer 23 | private static _consumer?: SourceMapConsumer; 24 | private static _regexPrefix = /^\.\.\//; 25 | private static _regexSearch = /^\s+at\s+(.+?\s+)?\(?([0-z._\-\\/]+):(\d+):(\d+)\)?$/gm; 26 | 27 | public static get consumer(): SourceMapConsumer { 28 | if (!this._consumer) { 29 | this._consumer = new SourceMapConsumer(require('main.js.map')); 30 | } 31 | return this._consumer; 32 | } 33 | 34 | // Cache previously mapped traces to improve performance 35 | public static cache: { [key: string]: string } = {}; 36 | 37 | /** 38 | * Generates a stack trace using a source map generate original symbol names. 39 | * 40 | * WARNING - EXTREMELY high CPU cost for first call after reset - >30 CPU! 41 | * (Consecutive calls after a reset are more reasonable, ~0.1 CPU/ea) 42 | * 43 | * @param {Error | string} error The error or original stack trace 44 | * @returns {string} The source-mapped stack trace 45 | */ 46 | public static sourceMappedStackTrace(error: Error | string): string { 47 | const stack: string = error instanceof Error ? (error.stack as string) : error; 48 | if (Object.prototype.hasOwnProperty.call(this.cache, stack)) { 49 | return this.cache[stack]; 50 | } 51 | 52 | let match: RegExpExecArray | null; 53 | let outStack = error.toString(); 54 | 55 | while ((match = this._regexSearch.exec(stack))) { 56 | if (match[2] === 'main') { 57 | const pos = this.consumer.originalPositionFor({ column: parseInt(match[4], 10), line: parseInt(match[3], 10) }); 58 | 59 | if (pos.line != null) { 60 | const file = pos.source.replace(this._regexPrefix, ''); 61 | if (pos.name) { 62 | outStack += `\n at ${pos.name} (${file}:${pos.line}:${pos.column})`; 63 | } else { 64 | if (match[1]) { 65 | // no original source file name known - use file name from given trace 66 | outStack += `\n at ${match[1]} (${file}:${pos.line}:${pos.column})`; 67 | } else { 68 | // no original source file name known or in given trace - omit name 69 | outStack += `\n at ${file}:${pos.line}:${pos.column}`; 70 | } 71 | } 72 | } else { 73 | // no known position 74 | break; 75 | } 76 | } else { 77 | // no more parseable lines 78 | break; 79 | } 80 | } 81 | 82 | this.cache[stack] = outStack; 83 | return outStack; 84 | } 85 | 86 | public static wrapLoop(loop: () => void): () => void { 87 | return () => { 88 | try { 89 | loop(); 90 | } catch (e) { 91 | if (e instanceof Error) { 92 | if ('sim' in Game.rooms || Game.cpu.bucket < Game.cpu.tickLimit) { 93 | // Display original error in simulator or when bucket is very low 94 | log.error(_.escape(e.stack)); 95 | } else { 96 | log.error(_.escape(this.sourceMappedStackTrace(e))); 97 | } 98 | return; 99 | } 100 | 101 | // can't handle it 102 | throw e; 103 | } 104 | }; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/utils/log.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The log utility provides colorful log message functionality. 3 | * @module 4 | */ 5 | 6 | import { LogLevel } from 'enums/logLevel'; 7 | 8 | /** 9 | * Updates the log level setting of the bot. 10 | * @param level The new log level for rendering log messages. 11 | */ 12 | export function setLogLevel(level: LogLevel) { 13 | Memory.settings.loglevel = level; 14 | } 15 | 16 | /** 17 | * Log an alert message to the console. 18 | * @param message The message to log in the console. 19 | * @param roomName (optional) The room to link in the console. 20 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 21 | */ 22 | export function alert(message: string, roomName?: string | null, print = true) { 23 | return send(LogLevel.Alert, '#9dd2e7', message, roomName, print); 24 | } 25 | 26 | /** 27 | * Log a success message to the console. 28 | * @param message The message to log in the console. 29 | * @param roomName (optional) The room to link in the console. 30 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 31 | */ 32 | export function success(message: string, roomName?: string | null, print = true) { 33 | return send(LogLevel.Success, '#9de7a7', message, roomName, print); 34 | } 35 | 36 | /** 37 | * Log an error message to the console. 38 | * @param message The message to log in the console. 39 | * @param roomName (optional) The room to link in the console. 40 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 41 | */ 42 | export function error(message: string, roomName?: string | null, print = true) { 43 | return send(LogLevel.Error, '#e79da7', message, roomName, print); 44 | } 45 | 46 | /** 47 | * Log a warning message to the console. 48 | * @param message The message to log in the console. 49 | * @param roomName (optional) The room to link in the console. 50 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 51 | */ 52 | export function warning(message: string, roomName?: string | null, print = true) { 53 | return send(LogLevel.Warn, '#e6e79d', message, roomName, print); 54 | } 55 | 56 | /** 57 | * Log a message to the console. 58 | * @param message The message to log in the console. 59 | * @param roomName (optional) The room to link in the console. 60 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 61 | */ 62 | export function info(message: string, roomName?: string | null, print = true) { 63 | return send(LogLevel.Info, '#efefef', message, roomName, print); 64 | } 65 | 66 | /** 67 | * Log a debug message to the console. 68 | * @param message The message to log in the console. 69 | * @param roomName (optional) The room to link in the console. 70 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 71 | */ 72 | export function debug(message: string, roomName?: string | null, print = true) { 73 | return send(LogLevel.Debug, '#9a9a9a', message, roomName, print); 74 | } 75 | 76 | /** 77 | * Log a verbose debug message to the console. 78 | * @param message The message to log in the console. 79 | * @param roomName (optional) The room to link in the console. 80 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 81 | */ 82 | export function verbose(message: string, roomName?: string | null, print = true) { 83 | return send(LogLevel.Verbose, '#6e6770', message, roomName, print); 84 | } 85 | 86 | /** 87 | * Outputs a colorful log message. 88 | * @param level The level used to filter logs based on the log level setting. 89 | * @param color The color of the message. 90 | * @param message The log message. 91 | * @param roomName (optional) The room to link. 92 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 93 | */ 94 | function send(level: LogLevel, color: string, message: string, roomName?: string | null, print = true) { 95 | if (Memory.settings?.loglevel && Memory.settings.loglevel < level) { 96 | return; 97 | } 98 | let output = ''; 99 | if (roomName && roomName !== 'sim') { 100 | output += `${roomName}`; 101 | output += ''; 102 | } 103 | output += `${message}`; 104 | if (print) { 105 | return console.log(output); 106 | } 107 | return output; 108 | } 109 | -------------------------------------------------------------------------------- /src/utils/operation.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The operation utility provides methods for handling special missions. 3 | * @module 4 | */ 5 | 6 | import { OperationType } from 'enums/operationType'; 7 | import * as TestOperation from 'operations/test'; 8 | import { success } from 'utils/log'; 9 | 10 | /** 11 | * Initialize bot operations memory. 12 | */ 13 | function initOperations() { 14 | if (!Memory.operations) { 15 | Memory.operations = []; 16 | } 17 | } 18 | 19 | /** 20 | * Insert a new operation into memory. 21 | * @param operation The data of the new operation. 22 | */ 23 | function addOperation(operation: OperationData) { 24 | initOperations(); 25 | Memory.operations.push(operation); 26 | } 27 | 28 | /** 29 | * Checks if a Test operation is currently active. 30 | */ 31 | export function isTestOperationActive() { 32 | initOperations(); 33 | 34 | if (Memory.operations.length === 0) { 35 | return false; 36 | } 37 | 38 | for (const op of Memory.operations) { 39 | if (op.active && op.type === OperationType.Test) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | } 45 | 46 | /** 47 | * Creates a Test operation for a specified amount of time. 48 | * @param duration (optional) The amount of time the operation will be active. 49 | * @param print (optional) Print using `console.log()` if true, otherwise returns the colorful log message. 50 | */ 51 | export function createTestOperation(duration = 50, print = true) { 52 | const op = new TestOperation.Data(); 53 | op.type = OperationType.Test; 54 | op.victoryCondition = TestOperation.VictoryCondition.GameTime; 55 | op.victoryValue = Game.time + duration; 56 | addOperation(op); 57 | return success(`Test operation started at ${Game.time} for ${duration} ticks.`, null, print); 58 | } 59 | -------------------------------------------------------------------------------- /src/utils/order.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The order utility provides methods for managing orders to spawn creeps. 3 | * @module 4 | */ 5 | 6 | import { Order } from 'classes/order'; 7 | import { Role } from 'enums/role'; 8 | import { error, info, verbose } from 'utils/log'; 9 | import { getCostForBody } from 'utils/profile'; 10 | 11 | /** 12 | * Insert a new order into the orders queue for spawning a creep. 13 | * @param room The `Room` that will spawn the creep. 14 | * @param order The `Order` containing details about the creep. 15 | */ 16 | export function orderCreep(room: Room, order: Order) { 17 | if (!room.getMySpawn()) { 18 | return false; 19 | } 20 | 21 | const orderBodyCost = getCostForBody(order.body); 22 | if (orderBodyCost > room.energyCapacityAvailable) { 23 | error('Creep ordered is more expensive than the room energy capacity: ' + JSON.stringify(order.memory), room.name); 24 | return false; 25 | } 26 | 27 | if (order.body.length === 0) { 28 | error('Invalid creep ordered, empty body: ' + JSON.stringify(order.memory), room.name); 29 | return false; 30 | } 31 | 32 | if (order.body.length > 50) { 33 | error('Invalid creep ordered, body larger than 50: ' + JSON.stringify(order.memory), room.name); 34 | return false; 35 | } 36 | 37 | if (!room.memory.orders) { 38 | room.memory.orders = []; 39 | } 40 | 41 | room.memory.orders.push(order); 42 | 43 | const role = Role[order.memory.role]; 44 | const length = room.memory.orders.length - 1; 45 | verbose(`Ordered: ${role} (${order.memory.target}) - Queue: ${length}`, room.name); 46 | 47 | return true; 48 | } 49 | 50 | /** 51 | * Get number of creep orders with same role and target, where either can be null to skip matching. 52 | * @param room The `Room` used to find orders. 53 | * @param role (optional) The `Role` to search for matching orders. 54 | * @param target (optional) The target room name to search for matching orders. 55 | */ 56 | export function getCreepsInQueue(room: Room, role: Role | null = null, target: string | null = null) { 57 | if (!room.memory.orders) { 58 | room.memory.orders = []; 59 | } 60 | 61 | let count = 0; 62 | for (const order of room.memory.orders) { 63 | if ((target === null || order.memory.target === target) && (role === null || order.memory.role === role)) { 64 | count++; 65 | } 66 | } 67 | return count; 68 | } 69 | 70 | /** 71 | * Clear orders queue for the specified room. 72 | * @param room The `Room` used to clear orders. 73 | */ 74 | export function clearOrders(room: Room) { 75 | room.memory.orders = []; 76 | info('Clearing order queue for room', room.name); 77 | } 78 | 79 | /** 80 | * Create unique identifier. 81 | * @param prefix (optional) The string to prepend before the unique identifier. 82 | */ 83 | export function getUniqueId(prefix = '_') { 84 | let id = prefix; 85 | const charset = '0123456789ABCDEF'; 86 | for (let i = 0; i < 4; i++) { 87 | id += charset.charAt(Math.floor(Math.random() * charset.length)); 88 | } 89 | return id; 90 | } 91 | -------------------------------------------------------------------------------- /src/utils/profile.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The profile utility provides methods to assemble creep body arrays. 3 | * @module 4 | */ 5 | 6 | /** 7 | * Assembles a body for a Simple Worker creep which has 1:1 WORK to CARRY parts. 8 | * @param tier The scaling size of the creep body. 9 | * @returns The creep body array. 10 | */ 11 | export function getSimpleWorkerBody(tier: number) { 12 | const parts = [WORK, MOVE, CARRY, MOVE]; 13 | const maxTier = Math.floor(MAX_CREEP_SIZE / parts.length); 14 | if (tier > maxTier) { 15 | tier = maxTier; 16 | } 17 | return addToBody([], tier, parts); 18 | } 19 | 20 | /** 21 | * Determines the maximum size for a Simple Worker creep based on energy. 22 | * @param energy The maximum amount of energy to use for spawning the creep body. 23 | * @returns The maximum tier for the amount of energy. 24 | */ 25 | export function getMaxTierSimpleWorker(energy: number) { 26 | return getMaxTier(energy, getSimpleWorkerBody); 27 | } 28 | 29 | /** 30 | * Assembles a body for a Heavy Worker creep which has 3:1 WORK to CARRY parts. 31 | * @param tier The scaling size of the creep body. 32 | * @returns The creep body array. 33 | */ 34 | export function getHeavyWorkerBody(tier: number) { 35 | const workParts = [WORK, WORK, MOVE, MOVE]; 36 | const carryParts = [WORK, CARRY, MOVE, MOVE]; 37 | const partsPerTier = [...workParts, ...carryParts]; 38 | const maxTier = Math.floor(MAX_CREEP_SIZE / partsPerTier.length); 39 | if (tier > maxTier) { 40 | tier = maxTier; 41 | } 42 | let body: BodyPartConstant[] = []; 43 | body = addToBody(body, Math.floor(tier / 2), workParts); 44 | body = addToBody(body, Math.ceil(tier / 2), carryParts); 45 | return body; 46 | } 47 | 48 | /** 49 | * Determines the maximum size for a Heavy Worker creep based on energy. 50 | * @param energy The maximum amount of energy to use for spawning the creep body. 51 | * @returns The maximum tier for the amount of energy. 52 | */ 53 | export function getMaxTierHeavyWorker(energy: number) { 54 | return getMaxTier(energy, getHeavyWorkerBody); 55 | } 56 | 57 | /** 58 | * Calculate the total energy cost to spawn a creep based on a body array. 59 | * @param body The creep body array to evaluate. 60 | */ 61 | export function getCostForBody(body: BodyPartConstant[]) { 62 | let cost = 0; 63 | for (const bodypart of body) { 64 | cost += BODYPART_COST[bodypart]; 65 | } 66 | return cost; 67 | } 68 | 69 | /** 70 | * Determines the maximum size based on energy for a creep body method. 71 | * 72 | */ 73 | function getMaxTier(energy: number, bodyFunction: Function) { 74 | let tier = 0; 75 | let maxReached = false; 76 | for (let i = 1; !maxReached; i++) { 77 | const cost = getCostForBody(bodyFunction(i)); 78 | if (cost > energy || bodyFunction(i).length > MAX_CREEP_SIZE) { 79 | maxReached = true; 80 | } else { 81 | tier = i; 82 | } 83 | } 84 | return tier; 85 | } 86 | 87 | /** 88 | * Add parts to a creep body array. 89 | */ 90 | function addToBody(body: BodyPartConstant[], count: number, parts: BodyPartConstant[]) { 91 | for (let i = 0; i < count; i++) { 92 | body.push(...parts); 93 | } 94 | return body; 95 | } 96 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "lib": [ 5 | "es2018" 6 | ], 7 | "target": "es2018", 8 | "moduleResolution": "node10", 9 | "outDir": "./dist/", 10 | "baseUrl": "./src/", 11 | "sourceMap": true, 12 | "strict": true, 13 | "strictPropertyInitialization": false, 14 | "allowSyntheticDefaultImports": true, 15 | "typeRoots": [ 16 | "./node_modules/@types" 17 | ] 18 | }, 19 | "include": [ 20 | "./src/" 21 | ], 22 | "exclude": [ 23 | "node_modules" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------