why is _.filter not "working" in this situation???



  •  this has driven me insane for the last couple of hours.

    I was trying to get my towers to repair damaged structures with the following code

    var towers = _.filter(Game.rooms.sim.find(FIND_STRUCTURES), (tower) => tower.structureType == STRUCTURE_TOWER);
    for (var idx in towers){

    if(towers[idx]) {
    var closestDamagedStructure = _.filter(towers[idx].pos.findClosestByRange(FIND_STRUCTURES), (structure) => structure.hits < structure.hitsMax);
    if(closestDamagedStructure) {
    towers[idx].repair(closestDamagedStructure);
    }

    var closestHostile = towers[idx].pos.findClosestByRange(FIND_HOSTILE_CREEPS);
    if(closestHostile) {
    towers[idx].attack(closestHostile);
    }
    }

    but nothing happens (as in will not heal damaged structures), but when i change the filter from "_.filter" to "filter:" it works. I can not understand why "_.filter" does not work while "filter:" does?

    WORKING: var closestDamagedStructure = towers[idx].pos.findClosestByRange(FIND_STRUCTURES, {filter: (structure) => structure.hits < structure.hitsMax});

    NOT WORKING: var closestDamagedStructure = _.filter(towers[idx].pos.findClosestByRange(FIND_STRUCTURES), (structure) => structure.hits < structure.hitsMax);

     



  • The _.filter function uses an array to look through,.you're sending it a single object by looking at the closest FIND_STRUCTURES. If you find all the structures and choose the structures that is the most hurt it will work.

    In essence you're finding the closest structure, if it isn't damaged then it returns nothing, otherwise it will return that object. The first one looks through all your structures and returns those with less then full hp.

    For a CPU boost I'd suggest the following:

    var closestDamagedStructure = _.filter(Game.structures, function(structure) {
    return(structure.hits < structure.hitsMax);
    });


  • towers[idx].pos.findClosestByRange(FIND_STRUCTURES) will return the 1 closest structure (which will have structure.hits = structure.hitsMax eventually). Then you are doing a _.filter on this 1 structure to only return those with structure.hits < structure.hitsMax . That may correctly return nothing...



  • Thanks for the quick response Rask Vann, but it still is not working. I am not getting any console errors, it just does not seem to have any effect. i cant work out why using "filter:" works but "_.filter" doesn't. what is the difference between them? i thought they were supposed to be pretty much equivalent?



  • @cyberblast, thanks, that makes a lot of sense but then what exactly is the "filter:" doing different from the lodash "_.filter" version? is the "filter:" testing the structure hits before finding the closest structure or in parallel? 

    Stupid question, but what is the "filter:" called and is there any documentation on it so i can understand it better. Before screeps i had come across the lodash version but not the other.

    could my statement/expression be altered for the lodash filter to fulfil the wanted function?



  • Hi Bob. findClosestByRange and other find functions are functions, implemented on objects of the game api. 

    Those find functions accept additional arguments to apply a custom filter. 

    So when you use 

    towers[idx].pos.findClosestByRange(FIND_STRUCTURES, {filter: (structure) => structure.hits < structure.hitsMax}); 

    , a function, developed by the screeps team will look for the nearest structure with your filter to be true.

    This filter object has nothing to do with _.filter. It is a custom specification.

    http://support.screeps.com/hc/en-us/articles/203079201-RoomPosition#findClosestByRange 

    Lodash will be used internally as well, but as you correctly assumed, first filtering the objects using your argument, then returning the nearest of it. 

    I would advice to keep: 

    towers[idx].pos.findClosestByRange(FIND_STRUCTURES, {filter: (structure) => structure.hits < structure.hitsMax}); 

    There is nothing wrong with it. There is not THAT big "CPU boost" you are missing, when you use it only where you have to. Much later you may eventually want to optimize again to get every flop of performance you can get.

    You can very easily use lodash, when you are not interested in "the closest". Rask posted an example above.



  • Btw, this is the code of RoomPosition.findClosestByRange :)

     

    function (type, opts) {
    var room = register.rooms[this.roomName];

    if (!room) {
    throw new Error(`Could not access room ${ this.roomName }`);
    }

    opts = _.clone(opts || {});

    var objects = [],
    result = [];

    if (_.isNumber(type)) {
    objects = room.find(type, opts);
    }
    if (_.isArray(type)) {
    objects = opts.filter ? _.filter(type, opts.filter) : type;
    }

    var closest = null,
    minRange = Infinity;

    objects.forEach(i => {
    var range = this.getRangeTo(i);
    if (range < minRange) {
    minRange = range;
    closest = i;
    }
    });

    return closest;
    }


  • Thank you 🙂 ... you have been very helpful. Can't believe i have spent most of my day trying to understand why it was not working the way i was expecting.  



  • you are welcome 🙂