Compare commits
	
		
			10 Commits
		
	
	
		
			c03b0ccea6
			...
			d69e0ffa9c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						d69e0ffa9c
	
				 | 
					
					
						|||
| 9dac06627f | |||
| 29f653f77a | |||
| 8979e42150 | |||
| b2a52fd4f5 | |||
| db60d70ee8 | |||
| ea5ff7a63a | |||
| 3811e4fc56 | |||
| 895888664b | |||
| cc7ac0e06e | 
@@ -1,4 +1,4 @@
 | 
			
		||||
FROM node:10-alpine
 | 
			
		||||
FROM node:14-alpine
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache --virtual .gyp \
 | 
			
		||||
        python \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								Makefile
									
									
									
									
									
								
							@@ -3,6 +3,9 @@ all: format build
 | 
			
		||||
build-docker:
 | 
			
		||||
	docker build -t screeps-node .
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
	tools/npm install
 | 
			
		||||
 | 
			
		||||
build:
 | 
			
		||||
	tools/npm run build
 | 
			
		||||
 | 
			
		||||
@@ -17,3 +20,6 @@ coverage:
 | 
			
		||||
 | 
			
		||||
push:
 | 
			
		||||
	tools/npm run push-main
 | 
			
		||||
 | 
			
		||||
push-sim:
 | 
			
		||||
	tools/npm run push-sim
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								package.json
									
									
									
									
									
								
							@@ -32,35 +32,34 @@
 | 
			
		||||
    "node": "10.x"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@rollup/plugin-commonjs": "^11.1.0",
 | 
			
		||||
    "@rollup/plugin-multi-entry": "^3.0.0",
 | 
			
		||||
    "@rollup/plugin-node-resolve": "^7.1.3",
 | 
			
		||||
    "@rollup/plugin-buble": "^0.21.3",
 | 
			
		||||
    "@rollup/plugin-commonjs": "^20.0.0",
 | 
			
		||||
    "@rollup/plugin-multi-entry": "^4.1.0",
 | 
			
		||||
    "@rollup/plugin-node-resolve": "^13.0.4",
 | 
			
		||||
    "@types/chai": "^4.1.6",
 | 
			
		||||
    "@types/lodash": "3.10.2",
 | 
			
		||||
    "@types/mocha": "^5.2.5",
 | 
			
		||||
    "@types/node": "^13.13.14",
 | 
			
		||||
    "@types/lodash": "^4.14.172",
 | 
			
		||||
    "@types/mocha": "^9.0.0",
 | 
			
		||||
    "@types/node": "^16.6.1",
 | 
			
		||||
    "@types/screeps": "^3.1.3",
 | 
			
		||||
    "@types/sinon": "^5.0.5",
 | 
			
		||||
    "@types/sinon": "^10.0.2",
 | 
			
		||||
    "@types/sinon-chai": "^3.2.0",
 | 
			
		||||
    "chai": "^4.2.0",
 | 
			
		||||
    "lodash": "^3.10.1",
 | 
			
		||||
    "mocha": "^5.2.0",
 | 
			
		||||
    "prettier": "^2.0.4",
 | 
			
		||||
    "rollup": "^2.22.1",
 | 
			
		||||
    "rollup-plugin-buble": "^0.19.8",
 | 
			
		||||
    "lodash": "^4.17.21",
 | 
			
		||||
    "mocha": "^9.0.3",
 | 
			
		||||
    "prettier": "^2.3.2",
 | 
			
		||||
    "rollup": "^2.56.2",
 | 
			
		||||
    "rollup-plugin-clear": "^2.0.7",
 | 
			
		||||
    "rollup-plugin-nodent": "^0.2.2",
 | 
			
		||||
    "rollup-plugin-screeps": "^1.0.1",
 | 
			
		||||
    "rollup-plugin-typescript2": "^0.27.0",
 | 
			
		||||
    "sinon": "^6.3.5",
 | 
			
		||||
    "rollup-plugin-typescript2": "^0.30.0",
 | 
			
		||||
    "sinon": "^11.1.2",
 | 
			
		||||
    "sinon-chai": "^3.2.0",
 | 
			
		||||
    "ts-node": "^8.8.2",
 | 
			
		||||
    "source-map": "^0.7.3",
 | 
			
		||||
    "ts-node": "^10.2.0",
 | 
			
		||||
    "tslint": "^6.1.1",
 | 
			
		||||
    "tslint-config-prettier": "^1.18.0",
 | 
			
		||||
    "tslint-plugin-prettier": "^2.3.0",
 | 
			
		||||
    "typescript": "^3.9.7"
 | 
			
		||||
    "typescript": "^4.3.5"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "source-map": "~0.6.1"
 | 
			
		||||
  }
 | 
			
		||||
  "dependencies": {}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
import { selectSource } from './common';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
import { harvestEnergyFromTombstone, selectSource } from './common';
 | 
			
		||||
 | 
			
		||||
interface BuilderMemory extends CreepMemory {
 | 
			
		||||
  building: boolean | undefined;
 | 
			
		||||
  sourceId: Id<Source> | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function runAsBuilder(creep: Creep) {
 | 
			
		||||
@@ -10,16 +10,19 @@ export function runAsBuilder(creep: Creep) {
 | 
			
		||||
 | 
			
		||||
  if (memory.building && creep.store[RESOURCE_ENERGY] === 0) {
 | 
			
		||||
    memory.building = false;
 | 
			
		||||
    memory.repairTargetId = '';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!memory.building && creep.store.getFreeCapacity() === 0) {
 | 
			
		||||
    memory.building = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (memory.sourceId === undefined) {
 | 
			
		||||
    memory.sourceId = selectSource(creep);
 | 
			
		||||
  if (!memory.sourceId) {
 | 
			
		||||
    memory.sourceId = selectSource(creep.room);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  harvestEnergyFromTombstone(creep);
 | 
			
		||||
 | 
			
		||||
  if (memory.building) {
 | 
			
		||||
    if (build(creep)) {
 | 
			
		||||
      return;
 | 
			
		||||
@@ -53,14 +56,62 @@ function build(creep: Creep): boolean {
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Починить.
 | 
			
		||||
 *
 | 
			
		||||
 * Если цель нужно чинить, то дойти до цели и произвести починку.
 | 
			
		||||
 * Если цель починена, найти ближайшую цель в радиусе починки и чинить ее.
 | 
			
		||||
 *
 | 
			
		||||
 * @param creep
 | 
			
		||||
 */
 | 
			
		||||
function repair(creep: Creep) {
 | 
			
		||||
  const targets = creep.room.find(FIND_STRUCTURES).filter((t) => t.hits < t.hitsMax);
 | 
			
		||||
  if (targets.length === 0) {
 | 
			
		||||
  const memory = creep.memory as BuilderMemory;
 | 
			
		||||
  let repairTarget;
 | 
			
		||||
  // Если уже установлена цель, то получаем объект,
 | 
			
		||||
  // иначе ищем подходящую цель для ремонта.
 | 
			
		||||
  if (memory.repairTargetId) {
 | 
			
		||||
    repairTarget = Game.getObjectById(memory.repairTargetId);
 | 
			
		||||
  } else {
 | 
			
		||||
    repairTarget = findBestRepairTarget(creep);
 | 
			
		||||
    memory.repairTargetId = repairTarget ? repairTarget.id : '';
 | 
			
		||||
  }
 | 
			
		||||
  // Если цель ремонта не найдена, то ничего не делаем.
 | 
			
		||||
  if (!repairTarget) {
 | 
			
		||||
    memory.repairTargetId = '';
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (creep.repair(targets[0]) == ERR_NOT_IN_RANGE) {
 | 
			
		||||
  if (repairTarget.hits < repairTarget.hitsMax) {
 | 
			
		||||
    if (creep.repair(repairTarget) == ERR_NOT_IN_RANGE) {
 | 
			
		||||
      creep.say('🚧 repair');
 | 
			
		||||
    creep.moveTo(targets[0], { visualizePathStyle: { stroke: '#ffffff' } });
 | 
			
		||||
      creep.moveTo(repairTarget, { visualizePathStyle: { stroke: '#ffffff' } });
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    const closestRepairTarget = findClosestRepairTarget(creep);
 | 
			
		||||
    if (closestRepairTarget) {
 | 
			
		||||
      if (creep.repair(closestRepairTarget) !== OK) {
 | 
			
		||||
        memory.repairTargetId = '';
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      memory.repairTargetId = '';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findBestRepairTarget(creep: Creep): Structure | undefined {
 | 
			
		||||
  const targets = creep.room.find(FIND_STRUCTURES, {
 | 
			
		||||
    filter: (t) => t.hits < t.hitsMax && t.structureType !== STRUCTURE_ROAD
 | 
			
		||||
  });
 | 
			
		||||
  if (targets.length > 0) {
 | 
			
		||||
    targets.sort((t1, t2) => t1.hits - t2.hits);
 | 
			
		||||
    return targets[0];
 | 
			
		||||
  }
 | 
			
		||||
  return undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findClosestRepairTarget(creep: Creep): Structure | undefined {
 | 
			
		||||
  const targets = creep.pos.findInRange(FIND_STRUCTURES, 3, {
 | 
			
		||||
    filter: (t) => t.hits < t.hitsMax && t.structureType !== STRUCTURE_ROAD
 | 
			
		||||
  });
 | 
			
		||||
  return _.first(targets);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,39 @@
 | 
			
		||||
import { randomValueFromArray } from '../utils/Random';
 | 
			
		||||
 | 
			
		||||
export function selectSource(creep: Creep): Id<Source> {
 | 
			
		||||
  const sources = creep.room.find(FIND_SOURCES);
 | 
			
		||||
  const choices: Array<String> = [];
 | 
			
		||||
  for (let source of sources) {
 | 
			
		||||
export function selectSource(room: Room): Id<Source> {
 | 
			
		||||
  console.log('Select source for room', room.name);
 | 
			
		||||
  const creepsInRoom = room.find(FIND_MY_CREEPS);
 | 
			
		||||
  const sourcesInRoom = room.find(FIND_SOURCES);
 | 
			
		||||
  const rates: Weights = {};
 | 
			
		||||
  for (let source of sourcesInRoom) {
 | 
			
		||||
    const creepCount = creepsInRoom.filter((c) => c.memory.sourceId === source.id).length;
 | 
			
		||||
    const weight = getSourceWeight(source);
 | 
			
		||||
    for (let i = 0; i < weight; ++i) {
 | 
			
		||||
      choices.push(source.id);
 | 
			
		||||
    rates[source.id] = creepCount / weight;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
  console.log('Sources', choices);
 | 
			
		||||
  return randomValueFromArray(choices) as Id<Source>;
 | 
			
		||||
  console.log('Sources', JSON.stringify(rates));
 | 
			
		||||
  return getKeyWithMinWeight(rates) as Id<Source>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getSourceWeight(source: Source) {
 | 
			
		||||
function getSourceWeight(source: Source): number {
 | 
			
		||||
  const pos = source.pos;
 | 
			
		||||
  const terrains = source.room.lookForAtArea(LOOK_TERRAIN, pos.y - 1, pos.x - 1, pos.y + 1, pos.x + 1, true);
 | 
			
		||||
  const walked = terrains.filter((t) => t.terrain !== 'wall');
 | 
			
		||||
  return Math.max(walked.length - 1, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Weights {
 | 
			
		||||
  [key: string]: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getKeyWithMinWeight(weights: Weights): string {
 | 
			
		||||
  let keys = Object.keys(weights);
 | 
			
		||||
  return keys.reduce((key, v) => (weights[v] < weights[key] ? v : key));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function harvestEnergyFromTombstone(creep: Creep) {
 | 
			
		||||
  const tombstones = creep.pos.findInRange(FIND_TOMBSTONES, 1, {
 | 
			
		||||
    filter: (t) => t.store[RESOURCE_ENERGY] > 0
 | 
			
		||||
  });
 | 
			
		||||
  for (let tomb of tombstones) {
 | 
			
		||||
    const res = creep.withdraw(tomb, RESOURCE_ENERGY);
 | 
			
		||||
    console.log('Withdraw', res);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,21 @@
 | 
			
		||||
import { selectSource } from './common';
 | 
			
		||||
import { harvestEnergyFromTombstone, selectSource } from './common';
 | 
			
		||||
 | 
			
		||||
const TARGET_TYPES = [STRUCTURE_EXTENSION, STRUCTURE_SPAWN, STRUCTURE_TOWER];
 | 
			
		||||
 | 
			
		||||
interface HarvesterMemory extends CreepMemory {
 | 
			
		||||
  sourceId: Id<Source> | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum Action {
 | 
			
		||||
  Harvest = 'harvest',
 | 
			
		||||
  Charge = 'charge'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function runAsHarvester(creep: Creep) {
 | 
			
		||||
  const memory = creep.memory as HarvesterMemory;
 | 
			
		||||
  const memory = creep.memory as CreepMemory;
 | 
			
		||||
 | 
			
		||||
  if (memory.action === undefined) {
 | 
			
		||||
    memory.action = Action.Harvest;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (memory.sourceId === undefined) {
 | 
			
		||||
    memory.sourceId = selectSource(creep);
 | 
			
		||||
  if (!memory.sourceId) {
 | 
			
		||||
    memory.sourceId = selectSource(creep.room);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (memory.action) {
 | 
			
		||||
@@ -35,12 +31,14 @@ export function runAsHarvester(creep: Creep) {
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  harvestEnergyFromTombstone(creep);
 | 
			
		||||
 | 
			
		||||
  switch (memory.action) {
 | 
			
		||||
    case Action.Harvest:
 | 
			
		||||
      const source = Game.getObjectById(memory.sourceId);
 | 
			
		||||
      if (source && creep.harvest(source) === ERR_NOT_IN_RANGE) {
 | 
			
		||||
        creep.say('🔄 harvest');
 | 
			
		||||
        creep.moveTo(source, { visualizePathStyle: { stroke: '#ffaa00' } });
 | 
			
		||||
        creep.moveTo(source, { reusePath: 2, visualizePathStyle: { stroke: '#ffaa00' } });
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case Action.Charge:
 | 
			
		||||
@@ -56,13 +54,13 @@ export function runAsHarvester(creep: Creep) {
 | 
			
		||||
 | 
			
		||||
function findTarget(creep: Creep): Structure | undefined {
 | 
			
		||||
  for (let type of TARGET_TYPES) {
 | 
			
		||||
    const targets = creep.room.find(FIND_STRUCTURES, {
 | 
			
		||||
    const target = creep.pos.findClosestByPath(FIND_STRUCTURES, {
 | 
			
		||||
      filter: (structure) => {
 | 
			
		||||
        return structure.structureType === type && structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    if (targets.length) {
 | 
			
		||||
      return targets[0];
 | 
			
		||||
    if (target) {
 | 
			
		||||
      return target;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return undefined;
 | 
			
		||||
@@ -71,7 +69,7 @@ function findTarget(creep: Creep): Structure | undefined {
 | 
			
		||||
function moveEnergyToTarget(creep: Creep, target: Structure) {
 | 
			
		||||
  if (creep.transfer(target, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) {
 | 
			
		||||
    creep.say('🚛 transfer');
 | 
			
		||||
    creep.moveTo(target, { visualizePathStyle: { stroke: '#ffffff' } });
 | 
			
		||||
    creep.moveTo(target, { reusePath: 2, visualizePathStyle: { stroke: '#ffffff' } });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
import { selectSource } from './common';
 | 
			
		||||
import { harvestEnergyFromTombstone, selectSource } from './common';
 | 
			
		||||
 | 
			
		||||
interface UpgraderMemory extends CreepMemory {
 | 
			
		||||
  upgrading: boolean | undefined;
 | 
			
		||||
  sourceId: Id<Source> | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function runAsUpgrader(creep: Creep) {
 | 
			
		||||
@@ -16,10 +15,12 @@ export function runAsUpgrader(creep: Creep) {
 | 
			
		||||
    memory.upgrading = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (memory.sourceId === undefined) {
 | 
			
		||||
    memory.sourceId = selectSource(creep);
 | 
			
		||||
  if (!memory.sourceId) {
 | 
			
		||||
    memory.sourceId = selectSource(creep.room);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  harvestEnergyFromTombstone(creep);
 | 
			
		||||
 | 
			
		||||
  if (memory.upgrading) {
 | 
			
		||||
    if (creep.room.controller && creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
 | 
			
		||||
      creep.say('⚡ upgrade');
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								src/main.ts
									
									
									
									
									
								
							@@ -3,51 +3,18 @@ import { runAsHarvester } from './creep/harvester';
 | 
			
		||||
import { runAsBuilder } from './creep/builder';
 | 
			
		||||
import { runAsUpgrader } from './creep/upgrader';
 | 
			
		||||
import { uniqId } from './utils/Identity';
 | 
			
		||||
 | 
			
		||||
function makeName(): string {
 | 
			
		||||
  return uniqId();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum CreepRole {
 | 
			
		||||
  HARVESTER = 'harvester',
 | 
			
		||||
  UPGRADER = 'upgrader',
 | 
			
		||||
  BUILDER = 'builder'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makeCreep(role: CreepRole, count: number) {
 | 
			
		||||
  const creeps = Object.values(Game.creeps).filter((c) => c.memory.role === role);
 | 
			
		||||
  console.log(`Make creep "${role}"`, 'need', count, 'has', creeps.length);
 | 
			
		||||
  if (creeps.length < count) {
 | 
			
		||||
    const firstSpawn = _.first(Object.values(Game.spawns));
 | 
			
		||||
    const name = makeName();
 | 
			
		||||
    const memory = { role: role } as CreepMemory;
 | 
			
		||||
    const err = firstSpawn.spawnCreep([WORK, WORK, CARRY, CARRY, MOVE, MOVE], name, { memory });
 | 
			
		||||
    console.log(`Make creep "${role}"`, 'err', err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function callHarvestersFromOthers(minHarvCount: number) {
 | 
			
		||||
  const harvesters = Object.values(Game.creeps).filter((c) => c.memory.role === CreepRole.HARVESTER);
 | 
			
		||||
  if (harvesters.length < minHarvCount) {
 | 
			
		||||
    const others = Object.values(Game.creeps).filter((c) => c.memory.role !== CreepRole.HARVESTER);
 | 
			
		||||
    const required = Math.min(minHarvCount, others.length);
 | 
			
		||||
    console.log('Call harvesters', required);
 | 
			
		||||
    let count = 0;
 | 
			
		||||
    for (let creep of others) {
 | 
			
		||||
      if (count >= required) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      count++;
 | 
			
		||||
      creep.memory.role = CreepRole.HARVESTER;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
import { runTower } from './tower';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
// 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('');
 | 
			
		||||
  console.log(`Current game tick is ${Game.time}`);
 | 
			
		||||
  console.log(`Current game tick is ${Game.time}, ${Game.cpu.bucket}`);
 | 
			
		||||
 | 
			
		||||
  for (let room of Object.values(Game.rooms)) {
 | 
			
		||||
    writeRoomInfo(room);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Automatically delete memory of missing creeps
 | 
			
		||||
  for (const name in Memory.creeps) {
 | 
			
		||||
@@ -56,7 +23,7 @@ export const loop = ErrorMapper.wrapLoop(() => {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  callHarvestersFromOthers(2);
 | 
			
		||||
  callHarvestersFromOthers(4);
 | 
			
		||||
 | 
			
		||||
  makeCreep(CreepRole.HARVESTER, 4);
 | 
			
		||||
  makeCreep(CreepRole.UPGRADER, 4);
 | 
			
		||||
@@ -75,4 +42,92 @@ export const loop = ErrorMapper.wrapLoop(() => {
 | 
			
		||||
      runAsBuilder(creep);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (let room of Object.values(Game.rooms)) {
 | 
			
		||||
    for (let structure of room.find(FIND_MY_STRUCTURES)) {
 | 
			
		||||
      if (structure.structureType === STRUCTURE_TOWER) {
 | 
			
		||||
        runTower(structure);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function writeRoomInfo(room: Room) {
 | 
			
		||||
  console.log('Room:', room.name, 'energy', room.energyCapacityAvailable, room.energyAvailable);
 | 
			
		||||
  const roads = room.find(FIND_STRUCTURES, {
 | 
			
		||||
    filter: (s) => s.structureType === STRUCTURE_ROAD && s.hits < s.hitsMax
 | 
			
		||||
  });
 | 
			
		||||
  const minRoadHits = _.min(roads.map((r) => r.hits));
 | 
			
		||||
  const structures = room.find(FIND_STRUCTURES, {
 | 
			
		||||
    filter: (s) => s.structureType !== STRUCTURE_ROAD && s.hits < s.hitsMax
 | 
			
		||||
  });
 | 
			
		||||
  const minStructHits = _.min(structures.map((r) => r.hits));
 | 
			
		||||
  console.log('Repairs:', 'roads', minRoadHits, 'structures', minStructHits);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makeName(): string {
 | 
			
		||||
  return uniqId();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum CreepRole {
 | 
			
		||||
  HARVESTER = 'harvester',
 | 
			
		||||
  UPGRADER = 'upgrader',
 | 
			
		||||
  BUILDER = 'builder'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makeCreep(role: CreepRole, count: number) {
 | 
			
		||||
  const creeps = Object.values(Game.creeps).filter((c) => c.memory.role === role);
 | 
			
		||||
  console.log(`Make creep "${role}"`, 'need', count, 'has', creeps.length);
 | 
			
		||||
  if (creeps.length < count) {
 | 
			
		||||
    const firstSpawn = _.first(Object.values(Game.spawns));
 | 
			
		||||
    if (firstSpawn !== undefined) {
 | 
			
		||||
      const capacity = firstSpawn.room.energyCapacityAvailable;
 | 
			
		||||
      const body = selectBody(capacity);
 | 
			
		||||
      const name = makeName();
 | 
			
		||||
      const memory = { role: role, sourceId: '' } as CreepMemory;
 | 
			
		||||
      const err = firstSpawn.spawnCreep(body, name, {
 | 
			
		||||
        memory,
 | 
			
		||||
        directions: [BOTTOM, BOTTOM_RIGHT, RIGHT, BOTTOM_LEFT, LEFT]
 | 
			
		||||
      });
 | 
			
		||||
      console.log(`Make creep "${role}"`, 'body', calcBodyCost(body), 'err', err);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function selectBody(capacity: number): Array<BodyPartConstant> {
 | 
			
		||||
  const bodies = [
 | 
			
		||||
    [WORK, WORK, CARRY, CARRY, CARRY, CARRY, MOVE, MOVE, MOVE, MOVE],
 | 
			
		||||
    [WORK, WORK, CARRY, CARRY, CARRY, MOVE, MOVE, MOVE],
 | 
			
		||||
    [WORK, WORK, CARRY, CARRY, MOVE, MOVE],
 | 
			
		||||
    [WORK, CARRY, CARRY, MOVE, MOVE]
 | 
			
		||||
  ];
 | 
			
		||||
  const fallback = [WORK, CARRY, MOVE];
 | 
			
		||||
  for (let body of bodies) {
 | 
			
		||||
    const cost = calcBodyCost(body);
 | 
			
		||||
    if (cost < capacity) {
 | 
			
		||||
      return body;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return fallback;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function calcBodyCost(body: Array<BodyPartConstant>): number {
 | 
			
		||||
  return _.sum(body.map((p) => BODYPART_COST[p]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function callHarvestersFromOthers(minHarvCount: number) {
 | 
			
		||||
  const harvesters = Object.values(Game.creeps).filter((c) => c.memory.role === CreepRole.HARVESTER);
 | 
			
		||||
  const curHarvCount = harvesters.length;
 | 
			
		||||
  if (curHarvCount < minHarvCount) {
 | 
			
		||||
    const others = Object.values(Game.creeps).filter((c) => c.memory.role !== CreepRole.HARVESTER);
 | 
			
		||||
    const required = Math.min(minHarvCount - curHarvCount, others.length);
 | 
			
		||||
    let count = 0;
 | 
			
		||||
    for (let creep of others) {
 | 
			
		||||
      if (count >= required) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      count++;
 | 
			
		||||
      creep.memory.role = CreepRole.HARVESTER;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								src/tower.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/tower.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
const REPAIR_ENERGY_LIMIT = 500;
 | 
			
		||||
 | 
			
		||||
export function runTower(tower: StructureTower) {
 | 
			
		||||
  const room = tower.room;
 | 
			
		||||
  const hostiles = room.find(FIND_HOSTILE_CREEPS);
 | 
			
		||||
  if (hostiles.length > 0) {
 | 
			
		||||
    const hostile = _.minBy(hostiles, (h) => h.hits);
 | 
			
		||||
    if (hostile !== undefined) {
 | 
			
		||||
      const res = tower.attack(hostile);
 | 
			
		||||
      console.log('Attack', res);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const creeps = room.find(FIND_MY_CREEPS, {
 | 
			
		||||
    filter: (c) => c.hits < c.hitsMax
 | 
			
		||||
  });
 | 
			
		||||
  if (creeps.length > 0) {
 | 
			
		||||
    const creep = _.minBy(creeps, (c) => c.hits);
 | 
			
		||||
    if (creep !== undefined) {
 | 
			
		||||
      const res = tower.heal(creep);
 | 
			
		||||
      console.log('Heal', res);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Energy only for attack and heal
 | 
			
		||||
  if (tower.store[RESOURCE_ENERGY] < REPAIR_ENERGY_LIMIT) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const roads = room.find(FIND_STRUCTURES, {
 | 
			
		||||
    filter: (s) => s.structureType === STRUCTURE_ROAD && s.hits < s.hitsMax
 | 
			
		||||
  });
 | 
			
		||||
  if (roads.length > 0) {
 | 
			
		||||
    const road = _.minBy(roads, (r) => r.hits);
 | 
			
		||||
    if (road !== undefined) {
 | 
			
		||||
      const res = tower.repair(road);
 | 
			
		||||
      console.log('Repair', res);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const structures = room.find(FIND_STRUCTURES, {
 | 
			
		||||
    filter: (s) => s.hits < s.hitsMax
 | 
			
		||||
  });
 | 
			
		||||
  if (structures.length > 0) {
 | 
			
		||||
    const structure = _.minBy(structures, (s) => s.hits);
 | 
			
		||||
    if (structure !== undefined) {
 | 
			
		||||
      const res = tower.repair(structure);
 | 
			
		||||
      console.log('Repair', res);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								src/types.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -11,6 +11,14 @@ interface CreepMemory {
 | 
			
		||||
   * Действие, которое крип будет выполнять.
 | 
			
		||||
   */
 | 
			
		||||
  action: string | undefined;
 | 
			
		||||
  /**
 | 
			
		||||
   * Идентификатор источника энергии для крипа.
 | 
			
		||||
   */
 | 
			
		||||
  sourceId: Id<Source> | '';
 | 
			
		||||
  /**
 | 
			
		||||
   * Цель для починки.
 | 
			
		||||
   */
 | 
			
		||||
  repairTargetId: Id<Structure> | '';
 | 
			
		||||
  room: string;
 | 
			
		||||
  working: boolean;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,15 @@
 | 
			
		||||
// tslint:disable:no-conditional-assignment
 | 
			
		||||
import { SourceMapConsumer } from 'source-map';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
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'));
 | 
			
		||||
  public static async consumer(): Promise<SourceMapConsumer> {
 | 
			
		||||
    if (this._consumer === undefined) {
 | 
			
		||||
      this._consumer = await new SourceMapConsumer(require('main.js.map'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this._consumer;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +25,7 @@ export class ErrorMapper {
 | 
			
		||||
   * @param {Error | string} error The error or original stack trace
 | 
			
		||||
   * @returns {string} The source-mapped stack trace
 | 
			
		||||
   */
 | 
			
		||||
  public static sourceMappedStackTrace(error: Error | string): string {
 | 
			
		||||
  public static async sourceMappedStackTrace(error: Error | string): Promise<string> {
 | 
			
		||||
    const stack: string = error instanceof Error ? (error.stack as string) : error;
 | 
			
		||||
    if (this.cache.hasOwnProperty(stack)) {
 | 
			
		||||
      return this.cache[stack];
 | 
			
		||||
@@ -37,7 +37,8 @@ export class ErrorMapper {
 | 
			
		||||
 | 
			
		||||
    while ((match = re.exec(stack))) {
 | 
			
		||||
      if (match[2] === 'main') {
 | 
			
		||||
        const pos = this.consumer.originalPositionFor({
 | 
			
		||||
        const consumer = await this.consumer();
 | 
			
		||||
        const pos = consumer.originalPositionFor({
 | 
			
		||||
          column: parseInt(match[4], 10),
 | 
			
		||||
          line: parseInt(match[3], 10)
 | 
			
		||||
        });
 | 
			
		||||
@@ -78,7 +79,9 @@ export class ErrorMapper {
 | 
			
		||||
            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>`);
 | 
			
		||||
            this.sourceMappedStackTrace(e).then((s) => {
 | 
			
		||||
              console.log(`<span style='color:red'>${_.escape(s)}</span>`);
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          // can't handle it
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user