// A search utility for arbitrary (new) tileUpdates. // Provides `add` functionality, which auto-checks adjacency with other blocks. Subsequently, it searches them // Provides `del` functionality, which should be used with auto-deletion const bs = require('binary-search'); const Space = require('../space'); const comb = require('../utils/comb') const vec = require('../utils/vec'); const raster = require('../utils/raster'); function getComp(index){ return (element,needle) => (element[index] - needle[index] || element[1-index] - needle[1-index]) } function Search(){ // searchBlock should be a Space object. this.tiles = {}; // Object which stores Spaces. this.sort = [[],[]]; // Vertically/horizontally sorted list of tiles for fast addition, deletion, and searching this.spaces = []; this.calls = []; this.add = function(loc, space, send){ // loc should be [tileY,tileX] and space Space. this.tiles[loc] = space; let inds = Array(2); // Records horizontal and vertical indices for insertion. Then actually inserts the item. [0,1].forEach( ind => { // ind chooses y-or-x inds[ind] = -bs(this.sort[ind], loc, getComp(ind))-1; if (inds[ind] < 0) throw "loc should not be a part of the list"; this.sort[ind].splice(inds[ind], 0, loc); }); let block = this.block(loc, inds); let searchspace = new Space(); block.forEach( (tile) => { searchspace.comb( this.tiles[tile], comb.add ); }); for (let i=0; i { // inds[ind] is address, so check neighbors on sort[ind] (up, then down) // until sort[ind][inds[ind]][ind] != sort[ind][otherind][ind]. // on each, if sort[ind][otherind][1-ind] == sort[ind][inds[ind]][1-ind], // add to adjacent and search it with exclude=adjacent. Add that to adjacent let sort = this.sort[ind]; let chkind = inds[ind]; [-1, 1].forEach( sgn => { let curind = chkind + sgn; while (sort[curind] && sort[chkind][ind] == sort[curind][ind]){ if (! exclude.has(sort[curind])){ adjacent.add(...this.block(sort[curind], [curind, bs(this.sort[1-ind], sort[curind], getComp(1-ind))], exclude)); } curind += sgn; } }); }); return adjacent; } this.del = function(loc){ delete this.tiles[loc]; [0, 1].forEach( ind => { let theind = bs(this.sort[ind], loc, getComp(ind)); if (theind < 0) throw "loc must already be a part of the list"; this.sort[ind].splice(bs(this.sort[ind], loc, getComp(ind)), 1); }) } this.has = function(loc){ if (bs(this.sort[0], loc, getComp(0)) < 0) return false; // Could use ind = 1 just as well, but doesn't really matter // bs returns negative if not found. else return true; } this.update = function(space){ // Must be a space w/ valid loc; allows overlay of some arbitrary text (in the space) mostly for recording updates from the server without directly cataloguing them let tiles = raster(space); // Returns a this.tiles-style binding: {'2,-3':Space object} for (let tile in tiles){ if (this.tiles[tile]) this.tiles[tile].comb(tiles[tile], comb.flip(comb.add)); else this.add(tile, tiles[tile]); } } this.match = function(space, call){ this.spaces.push(space); this.calls.push(call); } this.unmatch = function(space){ let ind = this.spaces.indexOf(space); this.spaces.splice(ind, 1); this.calls.splice(ind, 1); } } module.exports = Search;