function px(p) { return !is_null(p.x) ? p.x : p[0] } function py(p) { return !is_null(p.y) ? p.y : p[1] } function grid(w, h) { var newgrid = meme(grid_prototype) newgrid.width = w; newgrid.height = h; // create a height*width array of empty lists newgrid.cells = array(h); var y = 0; var x = 0; for (y = 0; y < h; y++) { newgrid.cells[y] = array(w); for (x = 0; x < w; x++) { newgrid.cells[y][x] = []; // each cell holds its own list } } return newgrid } var grid_prototype = { // return the array at (x,y) cell(x, y) { return this.cells[y][x]; }, // alias for cell at(pos) { return this.cell(px(pos), py(pos)); }, // add an entity into a cell add(entity, pos) { var cx = px(pos), cy = py(pos); this.cell(cx, cy)[] = entity; entity.coord = [cx, cy]; }, // remove an entity from a cell remove(entity, pos) { var cx = px(pos), cy = py(pos); this.cells[cy][cx] = filter(this.cells[cy][cx], x => x != entity) }, // bounds check inBounds(pos) { var cx = px(pos), cy = py(pos); return ( cx >= 0 && cx < this.width && cy >= 0 && cy < this.height ); }, // call fn(entity, coord) for every entity in every cell each(fn) { var list = null; var y = 0; var x = 0; for (y = 0; y < this.height; y++) { for (x = 0; x < this.width; x++) { list = this.cells[y][x] arrfor(list, function(entity) { fn(entity); }) } } }, // printable representation toString() { var out = `grid [${this.width}x${this.height}]\n`; var y = 0; var x = 0; for (y = 0; y < this.height; y++) { for (x = 0; x < this.width; x++) { out += text(length(this.cells[y][x])); } if (y != this.height - 1) out += "\n"; } return out; } } return grid