Compare commits
No commits in common. "d69e0ffa9c582c63fd4a2e65b34bbd3fb8ab0e94" and "c03b0ccea6b36609546b1ec16c1b5da90e9eb927" have entirely different histories.
d69e0ffa9c
...
c03b0ccea6
@ -1,4 +1,4 @@
|
|||||||
FROM node:14-alpine
|
FROM node:10-alpine
|
||||||
|
|
||||||
RUN apk add --no-cache --virtual .gyp \
|
RUN apk add --no-cache --virtual .gyp \
|
||||||
python \
|
python \
|
||||||
|
6
Makefile
6
Makefile
@ -3,9 +3,6 @@ all: format build
|
|||||||
build-docker:
|
build-docker:
|
||||||
docker build -t screeps-node .
|
docker build -t screeps-node .
|
||||||
|
|
||||||
install:
|
|
||||||
tools/npm install
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
tools/npm run build
|
tools/npm run build
|
||||||
|
|
||||||
@ -20,6 +17,3 @@ coverage:
|
|||||||
|
|
||||||
push:
|
push:
|
||||||
tools/npm run push-main
|
tools/npm run push-main
|
||||||
|
|
||||||
push-sim:
|
|
||||||
tools/npm run push-sim
|
|
||||||
|
37
package.json
37
package.json
@ -32,34 +32,35 @@
|
|||||||
"node": "10.x"
|
"node": "10.x"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-buble": "^0.21.3",
|
"@rollup/plugin-commonjs": "^11.1.0",
|
||||||
"@rollup/plugin-commonjs": "^20.0.0",
|
"@rollup/plugin-multi-entry": "^3.0.0",
|
||||||
"@rollup/plugin-multi-entry": "^4.1.0",
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
||||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
|
||||||
"@types/chai": "^4.1.6",
|
"@types/chai": "^4.1.6",
|
||||||
"@types/lodash": "^4.14.172",
|
"@types/lodash": "3.10.2",
|
||||||
"@types/mocha": "^9.0.0",
|
"@types/mocha": "^5.2.5",
|
||||||
"@types/node": "^16.6.1",
|
"@types/node": "^13.13.14",
|
||||||
"@types/screeps": "^3.1.3",
|
"@types/screeps": "^3.1.3",
|
||||||
"@types/sinon": "^10.0.2",
|
"@types/sinon": "^5.0.5",
|
||||||
"@types/sinon-chai": "^3.2.0",
|
"@types/sinon-chai": "^3.2.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^3.10.1",
|
||||||
"mocha": "^9.0.3",
|
"mocha": "^5.2.0",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.0.4",
|
||||||
"rollup": "^2.56.2",
|
"rollup": "^2.22.1",
|
||||||
|
"rollup-plugin-buble": "^0.19.8",
|
||||||
"rollup-plugin-clear": "^2.0.7",
|
"rollup-plugin-clear": "^2.0.7",
|
||||||
"rollup-plugin-nodent": "^0.2.2",
|
"rollup-plugin-nodent": "^0.2.2",
|
||||||
"rollup-plugin-screeps": "^1.0.1",
|
"rollup-plugin-screeps": "^1.0.1",
|
||||||
"rollup-plugin-typescript2": "^0.30.0",
|
"rollup-plugin-typescript2": "^0.27.0",
|
||||||
"sinon": "^11.1.2",
|
"sinon": "^6.3.5",
|
||||||
"sinon-chai": "^3.2.0",
|
"sinon-chai": "^3.2.0",
|
||||||
"source-map": "^0.7.3",
|
"ts-node": "^8.8.2",
|
||||||
"ts-node": "^10.2.0",
|
|
||||||
"tslint": "^6.1.1",
|
"tslint": "^6.1.1",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"tslint-plugin-prettier": "^2.3.0",
|
"tslint-plugin-prettier": "^2.3.0",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^3.9.7"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"source-map": "~0.6.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import * as _ from 'lodash';
|
import { selectSource } from './common';
|
||||||
import { harvestEnergyFromTombstone, selectSource } from './common';
|
|
||||||
|
|
||||||
interface BuilderMemory extends CreepMemory {
|
interface BuilderMemory extends CreepMemory {
|
||||||
building: boolean | undefined;
|
building: boolean | undefined;
|
||||||
|
sourceId: Id<Source> | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runAsBuilder(creep: Creep) {
|
export function runAsBuilder(creep: Creep) {
|
||||||
@ -10,19 +10,16 @@ export function runAsBuilder(creep: Creep) {
|
|||||||
|
|
||||||
if (memory.building && creep.store[RESOURCE_ENERGY] === 0) {
|
if (memory.building && creep.store[RESOURCE_ENERGY] === 0) {
|
||||||
memory.building = false;
|
memory.building = false;
|
||||||
memory.repairTargetId = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memory.building && creep.store.getFreeCapacity() === 0) {
|
if (!memory.building && creep.store.getFreeCapacity() === 0) {
|
||||||
memory.building = true;
|
memory.building = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memory.sourceId) {
|
if (memory.sourceId === undefined) {
|
||||||
memory.sourceId = selectSource(creep.room);
|
memory.sourceId = selectSource(creep);
|
||||||
}
|
}
|
||||||
|
|
||||||
harvestEnergyFromTombstone(creep);
|
|
||||||
|
|
||||||
if (memory.building) {
|
if (memory.building) {
|
||||||
if (build(creep)) {
|
if (build(creep)) {
|
||||||
return;
|
return;
|
||||||
@ -56,62 +53,14 @@ function build(creep: Creep): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Починить.
|
|
||||||
*
|
|
||||||
* Если цель нужно чинить, то дойти до цели и произвести починку.
|
|
||||||
* Если цель починена, найти ближайшую цель в радиусе починки и чинить ее.
|
|
||||||
*
|
|
||||||
* @param creep
|
|
||||||
*/
|
|
||||||
function repair(creep: Creep) {
|
function repair(creep: Creep) {
|
||||||
const memory = creep.memory as BuilderMemory;
|
const targets = creep.room.find(FIND_STRUCTURES).filter((t) => t.hits < t.hitsMax);
|
||||||
let repairTarget;
|
if (targets.length === 0) {
|
||||||
// Если уже установлена цель, то получаем объект,
|
|
||||||
// иначе ищем подходящую цель для ремонта.
|
|
||||||
if (memory.repairTargetId) {
|
|
||||||
repairTarget = Game.getObjectById(memory.repairTargetId);
|
|
||||||
} else {
|
|
||||||
repairTarget = findBestRepairTarget(creep);
|
|
||||||
memory.repairTargetId = repairTarget ? repairTarget.id : '';
|
|
||||||
}
|
|
||||||
// Если цель ремонта не найдена, то ничего не делаем.
|
|
||||||
if (!repairTarget) {
|
|
||||||
memory.repairTargetId = '';
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (repairTarget.hits < repairTarget.hitsMax) {
|
if (creep.repair(targets[0]) == ERR_NOT_IN_RANGE) {
|
||||||
if (creep.repair(repairTarget) == ERR_NOT_IN_RANGE) {
|
creep.say('🚧 repair');
|
||||||
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;
|
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,39 +1,21 @@
|
|||||||
export function selectSource(room: Room): Id<Source> {
|
import { randomValueFromArray } from '../utils/Random';
|
||||||
console.log('Select source for room', room.name);
|
|
||||||
const creepsInRoom = room.find(FIND_MY_CREEPS);
|
export function selectSource(creep: Creep): Id<Source> {
|
||||||
const sourcesInRoom = room.find(FIND_SOURCES);
|
const sources = creep.room.find(FIND_SOURCES);
|
||||||
const rates: Weights = {};
|
const choices: Array<String> = [];
|
||||||
for (let source of sourcesInRoom) {
|
for (let source of sources) {
|
||||||
const creepCount = creepsInRoom.filter((c) => c.memory.sourceId === source.id).length;
|
|
||||||
const weight = getSourceWeight(source);
|
const weight = getSourceWeight(source);
|
||||||
rates[source.id] = creepCount / weight;
|
for (let i = 0; i < weight; ++i) {
|
||||||
|
choices.push(source.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
console.log('Sources', JSON.stringify(rates));
|
console.log('Sources', choices);
|
||||||
return getKeyWithMinWeight(rates) as Id<Source>;
|
return randomValueFromArray(choices) as Id<Source>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceWeight(source: Source): number {
|
function getSourceWeight(source: Source) {
|
||||||
const pos = source.pos;
|
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 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');
|
const walked = terrains.filter((t) => t.terrain !== 'wall');
|
||||||
return Math.max(walked.length - 1, 1);
|
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,21 +1,25 @@
|
|||||||
import { harvestEnergyFromTombstone, selectSource } from './common';
|
import { selectSource } from './common';
|
||||||
|
|
||||||
const TARGET_TYPES = [STRUCTURE_EXTENSION, STRUCTURE_SPAWN, STRUCTURE_TOWER];
|
const TARGET_TYPES = [STRUCTURE_EXTENSION, STRUCTURE_SPAWN, STRUCTURE_TOWER];
|
||||||
|
|
||||||
|
interface HarvesterMemory extends CreepMemory {
|
||||||
|
sourceId: Id<Source> | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
Harvest = 'harvest',
|
Harvest = 'harvest',
|
||||||
Charge = 'charge'
|
Charge = 'charge'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runAsHarvester(creep: Creep) {
|
export function runAsHarvester(creep: Creep) {
|
||||||
const memory = creep.memory as CreepMemory;
|
const memory = creep.memory as HarvesterMemory;
|
||||||
|
|
||||||
if (memory.action === undefined) {
|
if (memory.action === undefined) {
|
||||||
memory.action = Action.Harvest;
|
memory.action = Action.Harvest;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memory.sourceId) {
|
if (memory.sourceId === undefined) {
|
||||||
memory.sourceId = selectSource(creep.room);
|
memory.sourceId = selectSource(creep);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (memory.action) {
|
switch (memory.action) {
|
||||||
@ -31,14 +35,12 @@ export function runAsHarvester(creep: Creep) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
harvestEnergyFromTombstone(creep);
|
|
||||||
|
|
||||||
switch (memory.action) {
|
switch (memory.action) {
|
||||||
case Action.Harvest:
|
case Action.Harvest:
|
||||||
const source = Game.getObjectById(memory.sourceId);
|
const source = Game.getObjectById(memory.sourceId);
|
||||||
if (source && creep.harvest(source) === ERR_NOT_IN_RANGE) {
|
if (source && creep.harvest(source) === ERR_NOT_IN_RANGE) {
|
||||||
creep.say('🔄 harvest');
|
creep.say('🔄 harvest');
|
||||||
creep.moveTo(source, { reusePath: 2, visualizePathStyle: { stroke: '#ffaa00' } });
|
creep.moveTo(source, { visualizePathStyle: { stroke: '#ffaa00' } });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Action.Charge:
|
case Action.Charge:
|
||||||
@ -54,13 +56,13 @@ export function runAsHarvester(creep: Creep) {
|
|||||||
|
|
||||||
function findTarget(creep: Creep): Structure | undefined {
|
function findTarget(creep: Creep): Structure | undefined {
|
||||||
for (let type of TARGET_TYPES) {
|
for (let type of TARGET_TYPES) {
|
||||||
const target = creep.pos.findClosestByPath(FIND_STRUCTURES, {
|
const targets = creep.room.find(FIND_STRUCTURES, {
|
||||||
filter: (structure) => {
|
filter: (structure) => {
|
||||||
return structure.structureType === type && structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0;
|
return structure.structureType === type && structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (target) {
|
if (targets.length) {
|
||||||
return target;
|
return targets[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -69,7 +71,7 @@ function findTarget(creep: Creep): Structure | undefined {
|
|||||||
function moveEnergyToTarget(creep: Creep, target: Structure) {
|
function moveEnergyToTarget(creep: Creep, target: Structure) {
|
||||||
if (creep.transfer(target, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) {
|
if (creep.transfer(target, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) {
|
||||||
creep.say('🚛 transfer');
|
creep.say('🚛 transfer');
|
||||||
creep.moveTo(target, { reusePath: 2, visualizePathStyle: { stroke: '#ffffff' } });
|
creep.moveTo(target, { visualizePathStyle: { stroke: '#ffffff' } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { harvestEnergyFromTombstone, selectSource } from './common';
|
import { selectSource } from './common';
|
||||||
|
|
||||||
interface UpgraderMemory extends CreepMemory {
|
interface UpgraderMemory extends CreepMemory {
|
||||||
upgrading: boolean | undefined;
|
upgrading: boolean | undefined;
|
||||||
|
sourceId: Id<Source> | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runAsUpgrader(creep: Creep) {
|
export function runAsUpgrader(creep: Creep) {
|
||||||
@ -15,12 +16,10 @@ export function runAsUpgrader(creep: Creep) {
|
|||||||
memory.upgrading = true;
|
memory.upgrading = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memory.sourceId) {
|
if (memory.sourceId === undefined) {
|
||||||
memory.sourceId = selectSource(creep.room);
|
memory.sourceId = selectSource(creep);
|
||||||
}
|
}
|
||||||
|
|
||||||
harvestEnergyFromTombstone(creep);
|
|
||||||
|
|
||||||
if (memory.upgrading) {
|
if (memory.upgrading) {
|
||||||
if (creep.room.controller && creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
|
if (creep.room.controller && creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
|
||||||
creep.say('⚡ upgrade');
|
creep.say('⚡ upgrade');
|
||||||
|
137
src/main.ts
137
src/main.ts
@ -3,18 +3,51 @@ import { runAsHarvester } from './creep/harvester';
|
|||||||
import { runAsBuilder } from './creep/builder';
|
import { runAsBuilder } from './creep/builder';
|
||||||
import { runAsUpgrader } from './creep/upgrader';
|
import { runAsUpgrader } from './creep/upgrader';
|
||||||
import { uniqId } from './utils/Identity';
|
import { uniqId } from './utils/Identity';
|
||||||
import { runTower } from './tower';
|
|
||||||
import * as _ from 'lodash';
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When compiling TS to JS and bundling with rollup, the line numbers and file names in error messages change
|
// 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
|
// This utility uses source maps to get the line numbers and file names of the original, TS source code
|
||||||
export const loop = ErrorMapper.wrapLoop(() => {
|
export const loop = ErrorMapper.wrapLoop(() => {
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(`Current game tick is ${Game.time}, ${Game.cpu.bucket}`);
|
console.log(`Current game tick is ${Game.time}`);
|
||||||
|
|
||||||
for (let room of Object.values(Game.rooms)) {
|
|
||||||
writeRoomInfo(room);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatically delete memory of missing creeps
|
// Automatically delete memory of missing creeps
|
||||||
for (const name in Memory.creeps) {
|
for (const name in Memory.creeps) {
|
||||||
@ -23,7 +56,7 @@ export const loop = ErrorMapper.wrapLoop(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callHarvestersFromOthers(4);
|
callHarvestersFromOthers(2);
|
||||||
|
|
||||||
makeCreep(CreepRole.HARVESTER, 4);
|
makeCreep(CreepRole.HARVESTER, 4);
|
||||||
makeCreep(CreepRole.UPGRADER, 4);
|
makeCreep(CreepRole.UPGRADER, 4);
|
||||||
@ -42,92 +75,4 @@ export const loop = ErrorMapper.wrapLoop(() => {
|
|||||||
runAsBuilder(creep);
|
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
57
src/tower.ts
@ -1,57 +0,0 @@
|
|||||||
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,14 +11,6 @@ interface CreepMemory {
|
|||||||
* Действие, которое крип будет выполнять.
|
* Действие, которое крип будет выполнять.
|
||||||
*/
|
*/
|
||||||
action: string | undefined;
|
action: string | undefined;
|
||||||
/**
|
|
||||||
* Идентификатор источника энергии для крипа.
|
|
||||||
*/
|
|
||||||
sourceId: Id<Source> | '';
|
|
||||||
/**
|
|
||||||
* Цель для починки.
|
|
||||||
*/
|
|
||||||
repairTargetId: Id<Structure> | '';
|
|
||||||
room: string;
|
room: string;
|
||||||
working: boolean;
|
working: boolean;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// tslint:disable:no-conditional-assignment
|
// tslint:disable:no-conditional-assignment
|
||||||
import { SourceMapConsumer } from 'source-map';
|
import { SourceMapConsumer } from 'source-map';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
export class ErrorMapper {
|
export class ErrorMapper {
|
||||||
// Cache consumer
|
// Cache consumer
|
||||||
private static _consumer?: SourceMapConsumer;
|
private static _consumer?: SourceMapConsumer;
|
||||||
|
|
||||||
public static async consumer(): Promise<SourceMapConsumer> {
|
public static get consumer(): SourceMapConsumer {
|
||||||
if (this._consumer === undefined) {
|
if (this._consumer == null) {
|
||||||
this._consumer = await new SourceMapConsumer(require('main.js.map'));
|
this._consumer = new SourceMapConsumer(require('main.js.map'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._consumer;
|
return this._consumer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export class ErrorMapper {
|
|||||||
* @param {Error | string} error The error or original stack trace
|
* @param {Error | string} error The error or original stack trace
|
||||||
* @returns {string} The source-mapped stack trace
|
* @returns {string} The source-mapped stack trace
|
||||||
*/
|
*/
|
||||||
public static async sourceMappedStackTrace(error: Error | string): Promise<string> {
|
public static sourceMappedStackTrace(error: Error | string): string {
|
||||||
const stack: string = error instanceof Error ? (error.stack as string) : error;
|
const stack: string = error instanceof Error ? (error.stack as string) : error;
|
||||||
if (this.cache.hasOwnProperty(stack)) {
|
if (this.cache.hasOwnProperty(stack)) {
|
||||||
return this.cache[stack];
|
return this.cache[stack];
|
||||||
@ -37,8 +37,7 @@ export class ErrorMapper {
|
|||||||
|
|
||||||
while ((match = re.exec(stack))) {
|
while ((match = re.exec(stack))) {
|
||||||
if (match[2] === 'main') {
|
if (match[2] === 'main') {
|
||||||
const consumer = await this.consumer();
|
const pos = this.consumer.originalPositionFor({
|
||||||
const pos = consumer.originalPositionFor({
|
|
||||||
column: parseInt(match[4], 10),
|
column: parseInt(match[4], 10),
|
||||||
line: parseInt(match[3], 10)
|
line: parseInt(match[3], 10)
|
||||||
});
|
});
|
||||||
@ -79,9 +78,7 @@ export class ErrorMapper {
|
|||||||
const message = `Source maps don't work in the simulator - displaying original error`;
|
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>`);
|
console.log(`<span style='color:red'>${message}<br>${_.escape(e.stack)}</span>`);
|
||||||
} else {
|
} else {
|
||||||
this.sourceMappedStackTrace(e).then((s) => {
|
console.log(`<span style='color:red'>${_.escape(this.sourceMappedStackTrace(e))}</span>`);
|
||||||
console.log(`<span style='color:red'>${_.escape(s)}</span>`);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// can't handle it
|
// can't handle it
|
||||||
|
Loading…
Reference in New Issue
Block a user