TooAngel/screeps

View on GitHub
src/prototype_roomPosition.js

Summary

Maintainability
C
1 day
Test Coverage
'use strict';

RoomPosition.prototype.findClosestStructureWithMissingEnergyByRange = function(filter) {
  const structure = this.findClosestByRange(FIND_MY_STRUCTURES, {
    filter: (object) => (object.store && object.store.getFreeCapacity(RESOURCE_ENERGY) > 0 && (!filter || filter(object))),
  });
  return structure;
};

RoomPosition.prototype.clearPosition = function(target) {
  const structures = this.lookFor('structure');
  for (const structureId of Object.keys(structures)) {
    const structure = structures[structureId];
    if (structure.structureType === STRUCTURE_SPAWN) {
      const spawns = this.getRoom().findSpawn();
      if (spawns.length <= 1) {
        target.remove();
        return true;
      }
    }
    if (structure.structureType === target.structureType) {
      console.log(`Trying to clear Position for constructionSite, while target already correct structure: ${structure.structureType} ${structure.pos}`);
      continue;
    }
    console.log(`Destroying: ${structure.structureType} ${structure.pos}`);
    structure.destroy();
  }
};

RoomPosition.prototype.getClosestSource = function(filter) {
  let source = this.findClosestByPath(FIND_SOURCES_ACTIVE, {
    filter,
  });
  if (source === null) {
    source = this.findClosestByRange(FIND_SOURCES_ACTIVE);
  }
  if (source === null) {
    source = this.findClosestByRange(FIND_SOURCES);
  }
  return source;
};

RoomPosition.prototype.findInRangeStructures = function(objects, range, structureTypes) {
  // TODO this method should be deprecated
  return this.findInRange(objects, range, {filter: (object) => structureTypes.includes(object.structureType)});
};

RoomPosition.prototype.findHostileStructuresInRangedAttackRange = function() {
  return this.findInRange(FIND_HOSTILE_STRUCTURES, 3);
};

RoomPosition.prototype.findClosestStructure = function(structures, structureType) {
  return this.findClosestByPath(structures, {filter: (object) => object.structureType === structureType});
};

/**
 * Get the position adjacent to this position in a specific direction
 *
 * @param {Number} direction (or 0)
 * @return {boolean|void|RoomPosition} adjacent position, or this position for direction==0
 */
RoomPosition.prototype.getAdjacentPosition = function(direction) {
  const adjacentPos = [
    [0, 0],
    [0, -1],
    [1, -1],
    [1, 0],
    [1, 1],
    [0, 1],
    [-1, 1],
    [-1, 0],
    [-1, -1],
  ];
  // no clean way to handle negative directions here because 0 is a special case instead of equivalent to 8
  if (direction > 8) {
    direction = RoomPosition.fixDirection(direction);
  }
  const x = this.x + adjacentPos[direction][0];
  const y = this.y + adjacentPos[direction][1];

  if (x < 0 || y < 0) {
    return false;
  }
  if (x > 49 || y > 49) {
    return false;
  }

  try {
    return new RoomPosition(x, y, this.roomName);
  } catch (e) {
    // TODO do we need to catch it?
    this.log(`RoomPosition.getAdjacentPosition Exception: ${e} for direction ${direction} x: ${x} y: ${y} roomName: ${this.roomName} stack: ${e.stack}`);
    // throw e;
    return;
  }
};

RoomPosition.prototype.getAllAdjacentPositions = function* () {
  for (let direction = 1; direction <= 8; direction++) {
    const position = this.getAdjacentPosition(direction);
    if (position) {
      yield position;
    }
  }
};

RoomPosition.prototype.getAllPositionsInRange = function* (range) {
  for (let x = -range; x <= range; ++x) {
    for (let y = -range; y <= range; ++y) {
      if (this.x + x >= 0 && this.y + y >= 0 && this.x + x < 50 && this.y + y < 50) {
        yield new RoomPosition(this.x + x, this.y + y, this.roomName);
      }
    }
  }
};

RoomPosition.prototype.hasNonObstacleAdjacentPosition = function() {
  for (const pos of this.getAllPositionsInRange(1)) {
    if (!pos.checkForWall() && !pos.checkForObstacleStructure() && !pos.checkForCreep()) {
      return true;
    }
  }
  return false;
};

RoomPosition.prototype.checkForCreep = function() {
  return this.lookFor(LOOK_CREEPS).length > 0;
};

RoomPosition.prototype.checkForWall = function() {
  return this.lookFor(LOOK_TERRAIN)[0] === 'wall';
};

RoomPosition.prototype.checkForObstacleStructure = function() {
  return this.lookFor(LOOK_STRUCTURES).some((s) => OBSTACLE_OBJECT_TYPES.includes(s.structureType));
};

RoomPosition.prototype.inPath = function() {
  const room = this.getRoom();
  for (const pathName of Object.keys(room.getMemoryPaths())) {
    const path = room.getMemoryPath(pathName);
    for (const pos of path) {
      if (this.x === pos.x && this.y === pos.y) {
        return true;
      }
    }
  }
  return false;
};

RoomPosition.prototype.inPositions = function() {
  const room = this.getRoom();
  if (!room.data.positions) {
    return false;
  }

  for (const creepId of Object.keys(room.data.positions.creep)) {
    if (!room.data.positions.creep[creepId]) {
      // TODO when does this happen?
      continue;
    }
    try {
      for (const pos of room.data.positions.creep[creepId]) {
        if (!pos) {
          continue;
        }
        if (this.isEqualTo(pos.x, pos.y)) {
          return true;
        }
      }
    } catch (e) {
      this.log(`inPositions ${creepId} ${room.data.positions.creep[creepId]} ${e}`);
    }
  }

  for (const structureId of Object.keys((room.data.positions.structure || {}))) {
    for (const pos of room.data.positions.structure[structureId]) {
      if (this.isEqualTo(pos.x, pos.y)) {
        return true;
      }
    }
  }

  return false;
};

RoomPosition.prototype.isBorder = function(offset) {
  offset = offset || 0;
  if (this.x <= 1 + offset || this.x >= 48 - offset || this.y <= 1 + offset || this.y >= 48 - offset) {
    return true;
  }
  return false;
};

RoomPosition.prototype.isValid = function() {
  if (this.x < 0 || this.y < 0) {
    return false;
  }
  if (this.x > 49 || this.y > 49) {
    return false;
  }
  return true;
};

RoomPosition.prototype.validPosition = function(opts = {}) {
  if (!opts.ignoreBorder && this.isBorder()) {
    if (opts.debug) {
      this.log('is border');
    }
    return false;
  }
  if (!opts.ignoreWall && this.checkForWall()) {
    if (opts.debug) {
      this.log('is wall');
    }
    return false;
  }
  if (!opts.ignorePositions && this.inPositions(opts)) {
    if (opts.debug) {
      this.log('is positions');
    }
    return false;
  }
  if (!opts.ignorePath && this.inPath()) {
    if (opts.debug) {
      this.log('is path');
    }
    return false;
  }
  return this.isValid();
};

RoomPosition.prototype.getFirstNearPosition = function(...args) {
  return this.findNearPosition(...args).next().value;
};

RoomPosition.prototype.getLastNearPosition = function(...args) {
  // TODO not sure how this works
  const arr = this.findNearPosition(...args);
  arr.next();
  arr.next();
  return arr.next().value;
};

RoomPosition.prototype.getBestNearPosition = function(...args) {
  return _.max(Array.from(this.findNearPosition(...args)), (pos) => Array.from(pos.findNearPosition(...args)).length);
};

RoomPosition.prototype.getWorseNearPosition = function(...args) {
  return _.max(Array.from(this.findNearPosition(...args)), (pos) => -1 * Array.from(pos.findNearPosition(...args)).length);
};

RoomPosition.prototype.findNearPosition = function* (...args) {
  for (const posNew of this.getAllAdjacentPositions()) {
    if (args.debug) {
      console.log(posNew);
    }
    if (!posNew.validPosition(...args)) {
      if (args.debug) {
        console.log(posNew + ' - invalid');
      }
      continue;
    }
    // Single position or array
    // Array?, because path and structures are arrays?
    yield posNew;
  }
};

RoomPosition.prototype.getRoom = function() {
  const room = Game.rooms[this.roomName];
  if (!room) {
    throw new Error(`Could not access room ${this.roomName}`);
  }
  return room;
};

/**
 * Restore RoomPosition object after JSON serialisation.
 *
 * @param {object} json JSON object
 * @param {number} json.x X coordinate
 * @param {number} json.y Y coordinate
 * @param {string} json.roomName Name of the room
 * @return {RoomPosition} RoomPosition object
 */
RoomPosition.fromJSON = function(json) {
  return new RoomPosition(json.x, json.y, json.roomName);
};

/**
 * Given a direction-like number, wrap it to fit in 1-8
 *
 * @param {Number} direction
 * @return {Number} fixed direction
 */
RoomPosition.fixDirection = function(direction) {
  return (((direction - 1) % 8) + 8) % 8 + 1;
};

/**
 * Given a direction, 1-8, increment/decrement it by some value
 *
 * @param {Number} direction
 * @param {Number} change
 * @return {Number}
 */
RoomPosition.changeDirection = function(direction, change) {
  return RoomPosition.fixDirection(direction + change);
};

/**
 * Given a direction, 1-8, return the opposite direction
 *
 * @param {Number} direction
 * @return {Number}
 */
RoomPosition.oppositeDirection = function(direction) {
  return RoomPosition.fixDirection(direction + 4);
};