Init files
This commit is contained in:
		
							
								
								
									
										18
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # editorconfig.org | ||||
|  | ||||
| root = true | ||||
|  | ||||
| [*] | ||||
| indent_style = space | ||||
| indent_size = 2 | ||||
| charset = utf-8 | ||||
| trim_trailing_whitespace = true | ||||
| insert_final_newline = true | ||||
|  | ||||
| [*.py] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
|  | ||||
| [*.rb] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
							
								
								
									
										92
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| # yarn lock file | ||||
| /yarn.lock | ||||
|  | ||||
| # npm lock file (v5.0.0+) | ||||
| /package-lock.json | ||||
|  | ||||
| # Ignore basic folders | ||||
| /dist | ||||
| /.rpt2_cache | ||||
| /tsc-out | ||||
| /node_modules | ||||
| /_book | ||||
| /build/* | ||||
|  | ||||
| # TypeScript definitions installed by Typings | ||||
| /typings | ||||
|  | ||||
| # Screeps Config | ||||
| screeps.json | ||||
|  | ||||
| # ScreepsServer data from integration tests | ||||
| /server | ||||
|  | ||||
| # Numerous always-ignore extensions | ||||
| *.diff | ||||
| *.err | ||||
| *.orig | ||||
| *.log | ||||
| *.rej | ||||
| *.swo | ||||
| *.swp | ||||
| *.zip | ||||
| *.vi | ||||
| *~ | ||||
|  | ||||
| # Editor folders | ||||
| .cache | ||||
| .project | ||||
| .settings | ||||
| .tmproj | ||||
| *.esproj | ||||
| nbproject | ||||
| *.sublime-project | ||||
| *.sublime-workspace | ||||
| .idea | ||||
|  | ||||
| # ========================= | ||||
| # Operating System Files | ||||
| # ========================= | ||||
|  | ||||
| # OSX | ||||
| # ========================= | ||||
|  | ||||
| .DS_Store | ||||
| .AppleDouble | ||||
| .LSOverride | ||||
|  | ||||
| # Thumbnails | ||||
| ._* | ||||
|  | ||||
| # Files that might appear on external disk | ||||
| .Spotlight-V100 | ||||
| .Trashes | ||||
|  | ||||
| # Directories potentially created on remote AFP share | ||||
| .AppleDB | ||||
| .AppleDesktop | ||||
| Network Trash Folder | ||||
| Temporary Items | ||||
| .apdisk | ||||
|  | ||||
| # Windows | ||||
| # ========================= | ||||
|  | ||||
| # Windows image file caches | ||||
| Thumbs.db | ||||
| ehthumbs.db | ||||
|  | ||||
| # Folder config file | ||||
| Desktop.ini | ||||
|  | ||||
| # Recycle Bin used on file shares | ||||
| $RECYCLE.BIN/ | ||||
|  | ||||
| # Windows Installer files | ||||
| *.cab | ||||
| *.msi | ||||
| *.msm | ||||
| *.msp | ||||
|  | ||||
| # Windows shortcuts | ||||
| *.lnk | ||||
							
								
								
									
										7
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "semi": true, | ||||
|   "tabWidth": 2, | ||||
|   "printWidth": 120, | ||||
|   "singleQuote": true, | ||||
|   "trailingComma": "none" | ||||
| } | ||||
							
								
								
									
										6
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| FROM node:10-alpine | ||||
|  | ||||
| RUN apk add --no-cache --virtual .gyp \ | ||||
|         python \ | ||||
|         make \ | ||||
|         g++ | ||||
							
								
								
									
										16
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| all: format build | ||||
|  | ||||
| build-docker: | ||||
| 	docker build -t screeps-node . | ||||
|  | ||||
| build: | ||||
| 	tools/npm run build:dev | ||||
|  | ||||
| format: | ||||
| 	tools/npm run format | ||||
|  | ||||
| test: | ||||
| 	tools/npm run test | ||||
|  | ||||
| coverage: | ||||
| 	tools/npm run coverage | ||||
							
								
								
									
										55
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| # Screeps Typescript Starter | ||||
|  | ||||
| Screeps Typescript Starter is a starting point for a Screeps AI written in Typescript. It provides everything you need to start writing your AI whilst leaving `main.ts` as empty as possible. | ||||
|  | ||||
| ## Basic Usage | ||||
|  | ||||
| You will need: | ||||
|  | ||||
| - [Node.JS](https://nodejs.org/en/download) (10.x) | ||||
| - A Package Manager ([Yarn](https://yarnpkg.com/en/docs/getting-started) or [npm](https://docs.npmjs.com/getting-started/installing-node)) | ||||
| - Rollup CLI (Optional, install via `npm install -g rollup`) | ||||
|  | ||||
| Download the latest source [here](https://github.com/screepers/screeps-typescript-starter/archive/master.zip) and extract it to a folder. | ||||
|  | ||||
| Open the folder in your terminal and run your package manager to install install the required packages and TypeScript declaration files: | ||||
|  | ||||
| ```bash | ||||
| # npm | ||||
| npm install | ||||
|  | ||||
| # yarn | ||||
| yarn | ||||
| ``` | ||||
|  | ||||
| Fire up your preferred editor with typescript installed and you are good to go! | ||||
|  | ||||
| ### Rollup and code upload | ||||
|  | ||||
| Screeps Typescript Starter uses rollup to compile your typescript and upload it to a screeps server. | ||||
|  | ||||
| Move or copy `screeps.sample.json` to `screeps.json` and edit it, changing the credentials and optionally adding or removing some of the destinations. | ||||
|  | ||||
| Running `rollup -c` will compile your code and do a "dry run", preparing the code for upload but not actually pushing it. Running `rollup -c --environment DEST:main` will compile your code, and then upload it to a screeps server using the `main` config from `screeps.json`. | ||||
|  | ||||
| You can use `-cw` instead of `-c` to automatically re-run when your source code changes - for example, `rollup -cw --environment DEST:main` will automatically upload your code to the `main` configuration every time your code is changed. | ||||
|  | ||||
| Finally, there are also NPM scripts that serve as aliases for these commands in `package.json` for IDE integration. Running `npm run push-main` is equivalent to `rollup -c --environment DEST:main`, and `npm run watch-sim` is equivalent to `rollup -cw --dest sim`. | ||||
|  | ||||
| #### Important! To upload code to a private server, you must have [screepsmod-auth](https://github.com/ScreepsMods/screepsmod-auth) installed and configured! | ||||
|  | ||||
| ## Typings | ||||
|  | ||||
| The type definitions for Screeps come from [typed-screeps](https://github.com/screepers/typed-screeps). If you find a problem or have a suggestion, please open an issue there. | ||||
|  | ||||
| ## Documentation | ||||
|  | ||||
| We've also spent some time reworking the documentation from the ground-up, which is now generated through [Gitbooks](https://www.gitbook.com/). Includes all the essentials to get you up and running with Screeps AI development in TypeScript, as well as various other tips and tricks to further improve your development workflow. | ||||
|  | ||||
| Maintaining the docs will also become a more community-focused effort, which means you too, can take part in improving the docs for this starter kit. | ||||
|  | ||||
| To visit the docs, [click here](https://screepers.gitbook.io/screeps-typescript-starter/). | ||||
|  | ||||
| ## Contributing | ||||
|  | ||||
| Issues, Pull Requests, and contribution to the docs are welcome! See our [Contributing Guidelines](CONTRIBUTING.md) for more details. | ||||
							
								
								
									
										66
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| { | ||||
|   "name": "screeps-typescript-starter", | ||||
|   "version": "3.0.0", | ||||
|   "description": "", | ||||
|   "main": "index.js", | ||||
|   "//": "If you add or change the names of destinations in screeps.json, make sure you update these scripts to reflect the changes", | ||||
|   "scripts": { | ||||
|     "lint": "tslint -p tsconfig.json \"src/**/*.ts\"", | ||||
|     "build": "rollup -c", | ||||
|     "push-main": "rollup -c --environment DEST:main", | ||||
|     "push-pserver": "rollup -c --environment DEST:pserver", | ||||
|     "push-sim": "rollup -c --environment DEST:sim", | ||||
|     "format": "prettier --write \"src/**/*.ts\"", | ||||
|     "test": "npm run test-unit", | ||||
|     "test-unit": "rollup -c rollup.test-unit-config.js && mocha dist/test-unit.bundle.js", | ||||
|     "test-integration": "echo 'See docs/in-depth/testing.md for instructions on enabling integration tests'", | ||||
|     "watch-main": "rollup -cw --environment DEST:main", | ||||
|     "watch-pserver": "rollup -cw --environment DEST:pserver", | ||||
|     "watch-sim": "rollup -cw --environment DEST:sim" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "git+https://github.com/screepers/screeps-typescript-starter.git" | ||||
|   }, | ||||
|   "author": "", | ||||
|   "license": "Unlicense", | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/screepers/screeps-typescript-starter/issues" | ||||
|   }, | ||||
|   "homepage": "https://github.com/screepers/screeps-typescript-starter#readme", | ||||
|   "engines": { | ||||
|     "node": "10.x" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@rollup/plugin-commonjs": "^11.1.0", | ||||
|     "@rollup/plugin-multi-entry": "^3.0.0", | ||||
|     "@rollup/plugin-node-resolve": "^7.1.3", | ||||
|     "@types/chai": "^4.1.6", | ||||
|     "@types/lodash": "3.10.2", | ||||
|     "@types/mocha": "^5.2.5", | ||||
|     "@types/node": "^13.13.1", | ||||
|     "@types/screeps": "^3.1.0", | ||||
|     "@types/sinon": "^5.0.5", | ||||
|     "@types/sinon-chai": "^3.2.0", | ||||
|     "chai": "^4.2.0", | ||||
|     "lodash": "^3.10.1", | ||||
|     "mocha": "^5.2.0", | ||||
|     "prettier": "^2.0.4", | ||||
|     "rollup": "^2.6.1", | ||||
|     "rollup-plugin-buble": "^0.19.8", | ||||
|     "rollup-plugin-clear": "^2.0.7", | ||||
|     "rollup-plugin-nodent": "^0.2.2", | ||||
|     "rollup-plugin-screeps": "^1.0.0", | ||||
|     "rollup-plugin-typescript2": "^0.27.0", | ||||
|     "sinon": "^6.3.5", | ||||
|     "sinon-chai": "^3.2.0", | ||||
|     "ts-node": "^8.8.2", | ||||
|     "tslint": "^6.1.1", | ||||
|     "tslint-config-prettier": "^1.18.0", | ||||
|     "tslint-plugin-prettier": "^2.3.0", | ||||
|     "typescript": "^3.8.3" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "source-map": "~0.6.1" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										32
									
								
								rollup.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								rollup.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import clear from 'rollup-plugin-clear'; | ||||
| import resolve from '@rollup/plugin-node-resolve'; | ||||
| import commonjs from '@rollup/plugin-commonjs'; | ||||
| import typescript from 'rollup-plugin-typescript2'; | ||||
| import screeps from 'rollup-plugin-screeps'; | ||||
|  | ||||
| let cfg; | ||||
| const dest = process.env.DEST; | ||||
| if (!dest) { | ||||
|   console.log("No destination specified - code will be compiled but not uploaded"); | ||||
| } else if ((cfg = require("./screeps.json")[dest]) == null) { | ||||
|   throw new Error("Invalid upload destination"); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|   input: "src/main.ts", | ||||
|   output: { | ||||
|     file: "dist/main.js", | ||||
|     format: "cjs", | ||||
|     sourcemap: true | ||||
|   }, | ||||
|  | ||||
|   plugins: [ | ||||
|     clear({ targets: ["dist"] }), | ||||
|     resolve(), | ||||
|     commonjs(), | ||||
|     typescript({tsconfig: "./tsconfig.json"}), | ||||
|     screeps({config: cfg, dryRun: cfg == null}) | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										34
									
								
								rollup.test-integration-config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								rollup.test-integration-config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import clear from 'rollup-plugin-clear'; | ||||
| import resolve from '@rollup/plugin-node-resolve'; | ||||
| import commonjs from '@rollup/plugin-commonjs'; | ||||
| import typescript from 'rollup-plugin-typescript2'; | ||||
| import buble      from 'rollup-plugin-buble'; | ||||
| import multiEntry from '@rollup/plugin-multi-entry'; | ||||
| import nodent from 'rollup-plugin-nodent'; | ||||
|  | ||||
| export default { | ||||
|   input: 'test/integration/**/*.test.ts', | ||||
|   output: { | ||||
|     file: 'dist/test-integration.bundle.js', | ||||
|     name: 'lib', | ||||
|     sourcemap: true, | ||||
|     format: 'iife', | ||||
|     globals: { | ||||
|       chai: 'chai', | ||||
|       it: 'it', | ||||
|       describe: 'describe' | ||||
|     } | ||||
|   }, | ||||
|   external: ['chai', 'it', 'describe'], | ||||
|   plugins: [ | ||||
|     clear({ targets: ["dist/test.bundle.js"] }), | ||||
|     resolve(), | ||||
|     commonjs(), | ||||
|     typescript({tsconfig: "./tsconfig.test-integration.json"}), | ||||
|     nodent(), | ||||
|     multiEntry(), | ||||
|     buble() | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										32
									
								
								rollup.test-unit-config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								rollup.test-unit-config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import clear from 'rollup-plugin-clear'; | ||||
| import resolve from '@rollup/plugin-node-resolve'; | ||||
| import commonjs from '@rollup/plugin-commonjs'; | ||||
| import typescript from 'rollup-plugin-typescript2'; | ||||
| import buble      from 'rollup-plugin-buble'; | ||||
| import multiEntry from '@rollup/plugin-multi-entry'; | ||||
|  | ||||
| export default { | ||||
|   input: 'test/unit/**/*.test.ts', | ||||
|   output: { | ||||
|     file: 'dist/test-unit.bundle.js', | ||||
|     name: 'lib', | ||||
|     sourcemap: true, | ||||
|     format: 'iife', | ||||
|     globals: { | ||||
|       chai: 'chai', | ||||
|       it: 'it', | ||||
|       describe: 'describe' | ||||
|     } | ||||
|   }, | ||||
|   external: ['chai', 'it', 'describe'], | ||||
|   plugins: [ | ||||
|     clear({ targets: ["dist/test.bundle.js"] }), | ||||
|     resolve(), | ||||
|     commonjs(), | ||||
|     typescript({tsconfig: "./tsconfig.json"}), | ||||
|     multiEntry(), | ||||
|     buble() | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										27
									
								
								screeps.sample.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								screeps.sample.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| { | ||||
|   "main": { | ||||
|     "token": "YOUR_TOKEN", | ||||
|     "protocol": "https", | ||||
|     "hostname": "screeps.com", | ||||
|     "port": 443, | ||||
|     "path": "/", | ||||
|     "branch": "main" | ||||
|   }, | ||||
|   "sim": { | ||||
|     "token": "YOUR_TOKEN", | ||||
|     "protocol": "https", | ||||
|     "hostname": "screeps.com", | ||||
|     "port": 443, | ||||
|     "path": "/", | ||||
|     "branch": "sim" | ||||
|   }, | ||||
|   "pserver": { | ||||
|     "email": "username", | ||||
|     "password": "Password", | ||||
|     "protocol": "http", | ||||
|     "hostname": "1.2.3.4", | ||||
|     "port": 21025, | ||||
|     "path": "/", | ||||
|     "branch": "main" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										93
									
								
								src/harvester.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/harvester.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /** @param {Creep} creep **/ | ||||
| export function runAsHarvester(creep: Creep) { | ||||
|   if (creep.store.getFreeCapacity() > 0) { | ||||
|     let sources = creep.room.find(FIND_SOURCES); | ||||
|     if (creep.harvest(sources[0]) === ERR_NOT_IN_RANGE) { | ||||
|       creep.moveTo(sources[0], { visualizePathStyle: { stroke: '#ffaa00' } }); | ||||
|     } | ||||
|   } else { | ||||
|     let targets = creep.room.find(FIND_STRUCTURES, { | ||||
|       filter: (structure) => { | ||||
|         return ( | ||||
|           (structure.structureType === STRUCTURE_EXTENSION || | ||||
|             structure.structureType === STRUCTURE_SPAWN || | ||||
|             structure.structureType === STRUCTURE_TOWER) && | ||||
|           structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0 | ||||
|         ); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     if (targets.length > 0) { | ||||
|       if (creep.transfer(targets[0], RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) { | ||||
|         creep.moveTo(targets[0], { visualizePathStyle: { stroke: '#ffffff' } }); | ||||
|       } | ||||
|     } else { | ||||
|       const spawns = creep.room.find(FIND_MY_SPAWNS); | ||||
|       if (spawns.length > 0) { | ||||
|         creep.say('to spawn'); | ||||
|         creep.moveTo(spawns[0]); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** @param {Creep} creep **/ | ||||
| export function runAsBuilder(creep: Creep) { | ||||
|   const memory = creep.memory as CreepMemory & { building: boolean | undefined }; | ||||
|  | ||||
|   if (memory.building && creep.store[RESOURCE_ENERGY] === 0) { | ||||
|     memory.building = false; | ||||
|     creep.say('🔄 harvest'); | ||||
|   } | ||||
|  | ||||
|   if (!memory.building && creep.store.getFreeCapacity() === 0) { | ||||
|     memory.building = true; | ||||
|     creep.say('🚧 build'); | ||||
|   } | ||||
|  | ||||
|   if (memory.building) { | ||||
|     const targets = creep.room.find(FIND_CONSTRUCTION_SITES); | ||||
|     if (targets.length > 0) { | ||||
|       if (creep.build(targets[0]) == ERR_NOT_IN_RANGE) { | ||||
|         creep.moveTo(targets[0], { visualizePathStyle: { stroke: '#ffffff' } }); | ||||
|       } | ||||
|     } else { | ||||
|       const spawns = creep.room.find(FIND_MY_SPAWNS); | ||||
|       if (spawns.length > 0) { | ||||
|         creep.say('to spawn'); | ||||
|         creep.moveTo(spawns[0]); | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     const sources = creep.room.find(FIND_SOURCES); | ||||
|     if (creep.harvest(sources[0]) == ERR_NOT_IN_RANGE) { | ||||
|       creep.moveTo(sources[0], { visualizePathStyle: { stroke: '#ffaa00' } }); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** @param {Creep} creep **/ | ||||
| export function runAsUpgrader(creep: Creep) { | ||||
|   const memory = creep.memory as CreepMemory & { upgrading: boolean | undefined }; | ||||
|  | ||||
|   if (memory.upgrading && creep.store[RESOURCE_ENERGY] === 0) { | ||||
|     memory.upgrading = false; | ||||
|     creep.say('🔄 harvest'); | ||||
|   } | ||||
|  | ||||
|   if (!memory.upgrading && creep.store.getFreeCapacity() === 0) { | ||||
|     memory.upgrading = true; | ||||
|     creep.say('⚡ upgrade'); | ||||
|   } | ||||
|  | ||||
|   if (memory.upgrading) { | ||||
|     if (creep.room.controller && creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) { | ||||
|       creep.moveTo(creep.room.controller, { visualizePathStyle: { stroke: '#ffffff' } }); | ||||
|     } | ||||
|   } else { | ||||
|     const sources = creep.room.find(FIND_SOURCES); | ||||
|     if (creep.harvest(sources[0]) == ERR_NOT_IN_RANGE) { | ||||
|       creep.moveTo(sources[0], { visualizePathStyle: { stroke: '#ffaa00' } }); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										77
									
								
								src/main.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/main.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| import { ErrorMapper } from 'utils/ErrorMapper'; | ||||
| import { runAsBuilder, runAsHarvester, runAsUpgrader } from './harvester'; | ||||
|  | ||||
| const ALPHABET = 'abcdefghijklmnopqrstuvwxyz1234567890'; | ||||
| const ALPHABET_LENGTH = ALPHABET.length - 1; | ||||
|  | ||||
| function generateId(count: number): string { | ||||
|   let str = ''; | ||||
|   for (let i = 0; i < count; ++i) { | ||||
|     let symbolIndex = Math.floor(Math.random() * ALPHABET_LENGTH); | ||||
|     str += ALPHABET[symbolIndex]; | ||||
|   } | ||||
|   return str; | ||||
| } | ||||
|  | ||||
| export function uniqId(prefix: string = 'id'): string { | ||||
|   return prefix + generateId(16); | ||||
| } | ||||
|  | ||||
| const ROLE_HARVESTER = 'harvester'; | ||||
| const ROLE_UPGRADER = 'upgrader'; | ||||
| const ROLE_BUILDER = 'builder'; | ||||
|  | ||||
| // When compiling TS to JS and bundling with rollup, the line numbers and file names in error messages change | ||||
| // This utility uses source maps to get the line numbers and file names of the original, TS source code | ||||
| export const loop = ErrorMapper.wrapLoop(() => { | ||||
|   console.log(`Current game tick is ${Game.time}`); | ||||
|  | ||||
|   // Automatically delete memory of missing creeps | ||||
|   for (const name in Memory.creeps) { | ||||
|     if (!(name in Game.creeps)) { | ||||
|       delete Memory.creeps[name]; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Create new creeps | ||||
|   const HARVESTER_CREEP_COUNT = 1; | ||||
|   const harvesterCreeps = Object.values(Game.creeps).filter((c) => c.memory.role === ROLE_HARVESTER); | ||||
|   if (harvesterCreeps.length < HARVESTER_CREEP_COUNT) { | ||||
|     const firstSpawn = _.first(Object.values(Game.spawns)); | ||||
|     const name = uniqId(ROLE_HARVESTER); | ||||
|     const err = firstSpawn.spawnCreep([WORK, CARRY, MOVE], name, { memory: { role: ROLE_HARVESTER } as CreepMemory }); | ||||
|     console.log('Err', err); | ||||
|   } | ||||
|  | ||||
|   const UPGRADER_CREEP_COUNT = 2; | ||||
|   const upgraderCreeps = Object.values(Game.creeps).filter((c) => c.memory.role === ROLE_UPGRADER); | ||||
|   if (upgraderCreeps.length < UPGRADER_CREEP_COUNT) { | ||||
|     const firstSpawn = _.first(Object.values(Game.spawns)); | ||||
|     const name = uniqId(ROLE_UPGRADER); | ||||
|     const err = firstSpawn.spawnCreep([WORK, CARRY, MOVE], name, { memory: { role: ROLE_UPGRADER } as CreepMemory }); | ||||
|     console.log('Err', err); | ||||
|   } | ||||
|  | ||||
|   const BUILDER_CREEP_COUNT = 2; | ||||
|   const builderCreeps = Object.values(Game.creeps).filter((c) => c.memory.role === ROLE_BUILDER); | ||||
|   if (builderCreeps.length < BUILDER_CREEP_COUNT) { | ||||
|     const firstSpawn = _.first(Object.values(Game.spawns)); | ||||
|     const name = uniqId(ROLE_BUILDER); | ||||
|     const err = firstSpawn.spawnCreep([WORK, CARRY, MOVE], name, { memory: { role: ROLE_BUILDER } as CreepMemory }); | ||||
|     console.log('Err', err); | ||||
|   } | ||||
|  | ||||
|   // Process current creeps | ||||
|   for (let name in Game.creeps) { | ||||
|     const creep = Game.creeps[name]; | ||||
|     if (creep.memory.role === ROLE_HARVESTER) { | ||||
|       runAsHarvester(creep); | ||||
|     } | ||||
|     if (creep.memory.role === ROLE_UPGRADER) { | ||||
|       runAsUpgrader(creep); | ||||
|     } | ||||
|     if (creep.memory.role === ROLE_BUILDER) { | ||||
|       runAsBuilder(creep); | ||||
|     } | ||||
|   } | ||||
| }); | ||||
							
								
								
									
										20
									
								
								src/types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| // example declaration file - remove these and add your own custom typings | ||||
|  | ||||
| // memory extension samples | ||||
| interface CreepMemory { | ||||
|   role: string; | ||||
|   room: string; | ||||
|   working: boolean; | ||||
| } | ||||
|  | ||||
| interface Memory { | ||||
|   uuid: number; | ||||
|   log: any; | ||||
| } | ||||
|  | ||||
| // `global` extension samples | ||||
| declare namespace NodeJS { | ||||
|   interface Global { | ||||
|     log: any; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										90
									
								
								src/utils/ErrorMapper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/utils/ErrorMapper.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| // tslint:disable:no-conditional-assignment | ||||
| import { SourceMapConsumer } from 'source-map'; | ||||
|  | ||||
| export class ErrorMapper { | ||||
|   // Cache consumer | ||||
|   private static _consumer?: SourceMapConsumer; | ||||
|  | ||||
|   public static get consumer(): SourceMapConsumer { | ||||
|     if (this._consumer == null) { | ||||
|       this._consumer = new SourceMapConsumer(require('main.js.map')); | ||||
|     } | ||||
|  | ||||
|     return this._consumer; | ||||
|   } | ||||
|  | ||||
|   // Cache previously mapped traces to improve performance | ||||
|   public static cache: { [key: string]: string } = {}; | ||||
|  | ||||
|   /** | ||||
|    * Generates a stack trace using a source map generate original symbol names. | ||||
|    * | ||||
|    * WARNING - EXTREMELY high CPU cost for first call after reset - >30 CPU! Use sparingly! | ||||
|    * (Consecutive calls after a reset are more reasonable, ~0.1 CPU/ea) | ||||
|    * | ||||
|    * @param {Error | string} error The error or original stack trace | ||||
|    * @returns {string} The source-mapped stack trace | ||||
|    */ | ||||
|   public static sourceMappedStackTrace(error: Error | string): string { | ||||
|     const stack: string = error instanceof Error ? (error.stack as string) : error; | ||||
|     if (this.cache.hasOwnProperty(stack)) { | ||||
|       return this.cache[stack]; | ||||
|     } | ||||
|  | ||||
|     const re = /^\s+at\s+(.+?\s+)?\(?([0-z._\-\\\/]+):(\d+):(\d+)\)?$/gm; | ||||
|     let match: RegExpExecArray | null; | ||||
|     let outStack = error.toString(); | ||||
|  | ||||
|     while ((match = re.exec(stack))) { | ||||
|       if (match[2] === 'main') { | ||||
|         const pos = this.consumer.originalPositionFor({ | ||||
|           column: parseInt(match[4], 10), | ||||
|           line: parseInt(match[3], 10) | ||||
|         }); | ||||
|  | ||||
|         if (pos.line != null) { | ||||
|           if (pos.name) { | ||||
|             outStack += `\n    at ${pos.name} (${pos.source}:${pos.line}:${pos.column})`; | ||||
|           } else { | ||||
|             if (match[1]) { | ||||
|               // no original source file name known - use file name from given trace | ||||
|               outStack += `\n    at ${match[1]} (${pos.source}:${pos.line}:${pos.column})`; | ||||
|             } else { | ||||
|               // no original source file name known or in given trace - omit name | ||||
|               outStack += `\n    at ${pos.source}:${pos.line}:${pos.column}`; | ||||
|             } | ||||
|           } | ||||
|         } else { | ||||
|           // no known position | ||||
|           break; | ||||
|         } | ||||
|       } else { | ||||
|         // no more parseable lines | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     this.cache[stack] = outStack; | ||||
|     return outStack; | ||||
|   } | ||||
|  | ||||
|   public static wrapLoop(loop: () => void): () => void { | ||||
|     return () => { | ||||
|       try { | ||||
|         loop(); | ||||
|       } catch (e) { | ||||
|         if (e instanceof Error) { | ||||
|           if ('sim' in Game.rooms) { | ||||
|             const message = `Source maps don't work in the simulator - displaying original error`; | ||||
|             console.log(`<span style='color:red'>${message}<br>${_.escape(e.stack)}</span>`); | ||||
|           } else { | ||||
|             console.log(`<span style='color:red'>${_.escape(this.sourceMappedStackTrace(e))}</span>`); | ||||
|           } | ||||
|         } else { | ||||
|           // can't handle it | ||||
|           throw e; | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										59
									
								
								test/integration/helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								test/integration/helper.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| const { readFileSync } = require('fs'); | ||||
| const _ = require('lodash'); | ||||
| const { ScreepsServer, stdHooks } = require('screeps-server-mockup'); | ||||
| const DIST_MAIN_JS = 'dist/main.js'; | ||||
|  | ||||
| /* | ||||
|  * Helper class for creating a ScreepsServer and resetting it between tests. | ||||
|  * See https://github.com/Hiryus/screeps-server-mockup for instructions on | ||||
|  * manipulating the terrain and game state. | ||||
|  */ | ||||
| class IntegrationTestHelper { | ||||
|   private _server: any; | ||||
|   private _player: any; | ||||
|  | ||||
|   get server() { | ||||
|     return this._server; | ||||
|   } | ||||
|  | ||||
|   get player() { | ||||
|     return this._player; | ||||
|   } | ||||
|  | ||||
|   async beforeEach() { | ||||
|     this._server = new ScreepsServer(); | ||||
|  | ||||
|     // reset world but add invaders and source keepers bots | ||||
|     await this._server.world.reset(); | ||||
|  | ||||
|     // create a stub world composed of 9 rooms with sources and controller | ||||
|     await this._server.world.stubWorld(); | ||||
|  | ||||
|     // add a player with the built dist/main.js file | ||||
|     const modules = { | ||||
|         main: readFileSync(DIST_MAIN_JS).toString(), | ||||
|     }; | ||||
|     this._player = await this._server.world.addBot({ username: 'player', room: 'W0N1', x: 15, y: 15, modules }); | ||||
|  | ||||
|     // Start server | ||||
|     await this._server.start(); | ||||
|   } | ||||
|  | ||||
|   async afterEach() { | ||||
|     await this._server.stop(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| beforeEach(async () => { | ||||
|   await helper.beforeEach(); | ||||
| }); | ||||
|  | ||||
| afterEach(async () => { | ||||
|   await helper.afterEach(); | ||||
| }); | ||||
|  | ||||
| before(() => { | ||||
|   stdHooks.hookWrite(); | ||||
| }); | ||||
|  | ||||
| export const helper = new IntegrationTestHelper(); | ||||
							
								
								
									
										18
									
								
								test/integration/integration.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								test/integration/integration.test.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import {assert} from "chai"; | ||||
| import {helper} from "./helper"; | ||||
|  | ||||
| describe("main", () => { | ||||
|   it("runs a server and matches the game tick", async function () { | ||||
|     for (let i = 1; i < 10; i += 1) { | ||||
|       assert.equal(await helper.server.world.gameTime, i); | ||||
|       await helper.server.tick(); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   it("writes and reads to memory", async function () { | ||||
|     await helper.player.console(`Memory.foo = 'bar'`); | ||||
|     await helper.server.tick(); | ||||
|     const memory = JSON.parse(await helper.player.memory); | ||||
|     assert.equal(memory.foo, 'bar'); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										13
									
								
								test/mocha.opts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/mocha.opts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| --require test/setup-node.js | ||||
| --require ts-node/register | ||||
| --ui bdd | ||||
|  | ||||
| --reporter spec | ||||
| --bail | ||||
| --full-trace | ||||
| --watch-extensions tsx,ts | ||||
| --colors | ||||
|  | ||||
| --recursive | ||||
| --timeout 5000 | ||||
| --exit | ||||
							
								
								
									
										6
									
								
								test/setup-node.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								test/setup-node.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| //inject mocha globally to allow custom interface refer without direct import - bypass bundle issue | ||||
| global._ = require('lodash'); | ||||
| global.mocha = require('mocha'); | ||||
| global.chai = require('chai'); | ||||
| global.sinon = require('sinon'); | ||||
| global.chai.use(require('sinon-chai')); | ||||
							
								
								
									
										25
									
								
								test/unit/main.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								test/unit/main.test.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| import {assert} from "chai"; | ||||
| import {loop} from "../../src/main"; | ||||
| import {Game, Memory} from "./mock" | ||||
|  | ||||
| describe("main", () => { | ||||
|   before(() => { | ||||
|     // runs before all test in this block | ||||
|   }); | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     // runs before each test in this block | ||||
|     // @ts-ignore : allow adding Game to global | ||||
|     global.Game = _.clone(Game); | ||||
|     // @ts-ignore : allow adding Memory to global | ||||
|     global.Memory = _.clone(Memory); | ||||
|   }); | ||||
|  | ||||
|   it("should export a loop function", () => { | ||||
|     assert.isTrue(typeof loop === "function"); | ||||
|   }); | ||||
|  | ||||
|   it("should return void when called with no context", () => { | ||||
|     assert.isUndefined(loop()); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										10
									
								
								test/unit/mock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								test/unit/mock.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| export const Game = { | ||||
|   creeps: [], | ||||
|   rooms: [], | ||||
|   spawns: {}, | ||||
|   time: 12345 | ||||
| }; | ||||
|  | ||||
| export const Memory = { | ||||
|   creeps: [] | ||||
| }; | ||||
							
								
								
									
										21
									
								
								tools/node
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										21
									
								
								tools/node
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| set -eu | ||||
|  | ||||
| source .env | ||||
|  | ||||
| TTY= | ||||
| if [ -t 1 ] ; then | ||||
| 	TTY=--tty | ||||
| fi | ||||
|  | ||||
| docker run \ | ||||
|     --rm \ | ||||
|     --interactive \ | ||||
|     ${TTY} \ | ||||
|     --init \ | ||||
|     --user "$(id -u):$(id -g)" \ | ||||
|     --volume "$PWD:/app" \ | ||||
|     --workdir /app \ | ||||
|     ${NODE_IMAGE} \ | ||||
|     node "$@" | ||||
							
								
								
									
										27
									
								
								tools/npm
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										27
									
								
								tools/npm
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| set -eu | ||||
|  | ||||
| source .env | ||||
|  | ||||
| HOST_CACHE_DIR=$PWD/var/docker-cache/.npm | ||||
| CONTAINER_CACHE_DIR=/tmp/.npm | ||||
|  | ||||
| mkdir -p ${HOST_CACHE_DIR} | ||||
|  | ||||
| TTY= | ||||
| if [ -t 1 ] ; then | ||||
| 	TTY=--tty | ||||
| fi | ||||
|  | ||||
| docker run \ | ||||
|     --rm \ | ||||
|     --interactive \ | ||||
|     ${TTY} \ | ||||
|     --init \ | ||||
|     --user "$UID:$(id -g)" \ | ||||
|     --volume "$PWD:/app" \ | ||||
|     --env npm_config_cache="${CONTAINER_CACHE_DIR}" \ | ||||
|     --workdir /app \ | ||||
|     ${NODE_IMAGE} \ | ||||
|     npm "$@" | ||||
							
								
								
									
										16
									
								
								tools/tsc
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								tools/tsc
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| set -eu | ||||
|  | ||||
| source .env | ||||
|  | ||||
| docker run \ | ||||
|     --rm \ | ||||
|     --interactive \ | ||||
|     --tty \ | ||||
|     --init \ | ||||
|     --user "$(id -u):$(id -g)" \ | ||||
|     --volume "$PWD:/app" \ | ||||
|     --workdir /app \ | ||||
|     ${NODE_IMAGE} \ | ||||
|     ./node_modules/.bin/tsc "$@" | ||||
							
								
								
									
										26
									
								
								tools/yarn
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										26
									
								
								tools/yarn
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| set -eu | ||||
|  | ||||
| source .env | ||||
|  | ||||
| HOST_CACHE_DIR=$PWD/var/docker-cache/.npm | ||||
| CONTAINER_CACHE_DIR=/tmp/.npm | ||||
|  | ||||
| mkdir -p ${HOST_CACHE_DIR} | ||||
|  | ||||
| TTY= | ||||
| if [ -t 1 ] ; then | ||||
| 	TTY=--tty | ||||
| fi | ||||
|  | ||||
| docker run \ | ||||
|     --rm \ | ||||
|     --interactive \ | ||||
|     ${TTY} \ | ||||
|     --init \ | ||||
|     --user "$UID:$(id -g)" \ | ||||
|     --volume "$PWD:/app" \ | ||||
|     --workdir /app \ | ||||
|     ${NODE_IMAGE} \ | ||||
|     yarn "$@" | ||||
							
								
								
									
										19
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "module": "esnext", | ||||
|     "lib": ["esnext"], | ||||
|     "target": "es2017", | ||||
|     "moduleResolution": "Node", | ||||
|     "outDir": "dist", | ||||
|     "baseUrl": "src/", | ||||
|     "sourceMap": true, | ||||
|     "strict": true, | ||||
|     "experimentalDecorators": true, | ||||
|     "noImplicitReturns": true, | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "allowUnreachableCode": false | ||||
|   }, | ||||
|   "exclude": [ | ||||
|     "node_modules" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										20
									
								
								tsconfig.test-integration.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tsconfig.test-integration.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "module": "esnext", | ||||
|     "lib": ["esnext"], | ||||
|     "target": "es5", | ||||
|     "moduleResolution": "Node", | ||||
|     "outDir": "dist", | ||||
|     "baseUrl": "src/", | ||||
|     "sourceMap": true, | ||||
|     "strict": true, | ||||
|     "experimentalDecorators": true, | ||||
|     "noImplicitReturns": true, | ||||
|     "noImplicitAny": false, | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "allowUnreachableCode": false | ||||
|   }, | ||||
|   "exclude": [ | ||||
|     "node_modules" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										21
									
								
								tslint.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tslint.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| { | ||||
|   "rulesDirectory": "tslint-plugin-prettier", | ||||
|   "extends" : [ | ||||
|     "tslint:recommended", | ||||
|     "tslint-config-prettier" | ||||
|   ], | ||||
|   "rules": { | ||||
|     "forin": false, | ||||
|     "interface-name": [true, "never-prefix"], | ||||
|     "member-ordering": [false], | ||||
|     "no-console": [false], | ||||
|     "no-namespace": [true, "allow-declarations"], | ||||
|     "variable-name": [ | ||||
|       true, | ||||
|       "ban-keywords", | ||||
|       "check-format", | ||||
|       "allow-pascal-case", | ||||
|       "allow-leading-underscore" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user