aboutsummaryrefslogtreecommitdiff
path: root/space.js
blob: ae0ee5000938d6d6da65430fb071289e57953cfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// space.js, a full (not sparse data storage object) with some utilities.
// This includes from/to fetch, write, files, and ad-hoc strings. [DONE] [DONE] [DONE] [DONE] [DONE]
// It provides combination between Spaces. [DONE]
// It also gives a search utility and a utility to grab an arbitrary section [DONE] [DONE]
const fs = require('fs')

function chop(string, n){ // chops a string into n-sized chunks. Assumed to be perfect multiple
  let arr = [];
  for (let sec = 0; sec < string.length; sec++){
    arr.push(string.slice(sec*n,(sec+1)*n));
  }
  return arr;
}
function replace(text, old, repl){ //replaces, in an array `text`, the instances of `old` with `repl`
  for (let i=0; i<text.length; i++){
    if (text[i] == old) text[i] = repl;
  }
  return text;
}

function Space(){ // CLASS
  this.data = []; //
  self = this;
  this.fromfetch = function(tiles, dimension, conform=true){ //tiles is straight from fetch/tileUpdate function, dimension is a quadruplet; conform is false for tileUpdate because the cell_props don't actually mean anything; all data is still included
    for (let y=dimension[0]; y<=dimension[2]; y++){
      for (let line=0; line<8; line++)
        this.data.push([]); // Adds lines
      for (let x=dimension[1]; x<=dimension[3]; x++){
        if (! tiles[[y,x]]) tilein({"content":' '.repeat(16*8),"properties":{"cell_props":{}}}); // Insert a null tile
        else tilein(tiles[[y,x]],y-dimension[0]);
      }
    }

    function tilein(tile, tilerow){ //tile is one of the tiles from `tiles`, and tilerow is y-dimension[0]; helper function
      let incl = Object.keys(tile.properties.cell_props).map(linenum => parseInt(linenum)) // list of included lines in the content
      let cont = chop(tile.content,16);
      let read = 0; //line of cont to read
      for (let line=0; line<8; line++){
        curline = line+8*tilerow;
        if (conform && incl.includes(line)){
          for (let i=0; i<16; i++) self.data[curline].push(' ');
        } else {
          Array.prototype.push.apply(self.data[curline],cont[read].split(''));
          read++;
        }
      }
    }
  }
  this.towrite = function(charoffset){ // Does no splitting or anything like that. Just returns a list of triplets for the write function
    let writes = [];
    for (let line = 0; line < this.data.length; line++) for (let chr = 0; chr< this.data[line].length; chr++){
      if (this.data[line][chr] == '') continue;
      writes.push([[charoffset[0]+line,charoffset[1]+chr],this.data[line][chr]]);
    }
    return writes;
  }
  this.tofile = function(filename){
    fs.writeFileSync(filename, this.print());
  };
  this.fromfile = function(filename){ //Reads an external file into internal data
    this.adhoc(fs.readFileSync(filename,'utf8'));
  }
  this.adhoc = function(text){
    text = text.split('\n')
    text = text.map(row => row.split(''));
    this.data = text.map(row => {
      for (let i = 0; i<row.length; i++){
        if (row[i] == '\\') row.splice(i,1);
        else if (row[i] == '&') row[i] = '';
      }
      return row;
    });
  }
  this.print = function(){
    return this.data.map(row => replace(replace(replace(row,'&','\\&'),'\\','\\\\'),'','&').join('')).join('\n');
  }
  this.comb = function(other, func, offset){
    // Convert negative offsets of either sort into zero offsets with significant previous whitespace (translation)
    for (let i = 0; i < -offset[0]; i++) this.data.unshift([]);
    if (offset[0] < 0) offset[0] = 0;
    for (let row = 0; row < offset[0]+other.data.length; row++){ // offset[1] < 0 fix
      for (let i = 0; i < -offset[1]; i++) this.data[row].unshift('');
    }
    if (offset[1] < 0) offset[1] = 0;

    // Iterate over other.data and add to this.data
    while (this.data.length < offset[0]+other.data.length) this.data.push([]); // Row padding
    for (let row = 0; row < other.data.length; row++){
      let thisrow  = this.data[row+offset[0]];
      while (thisrow.length < offset[1]+other.data[row].length) thisrow.push(''); // Character-wise padding
      for (let chr = 0; chr < other.data[row].length; chr++){
        let thechr = chr+offset[1];
        thisrow[thechr] = func(thisrow[thechr], other.data[row][chr]);
      }
    }
  }
  this.search = function(other){ //Returns first instance of a subspace (prioritized vertically then horizontally)
    let loc = [];
    for (let line=0; line<=this.data.length-other.data.length; line++){ for (let chr=0; chr<=this.data[line].length-other.data[0].length; chr++){
      var match = true;
      for (let y=0; y<other.data.length; y++) for (let x=0; x<other.data[y].length; x++){
        if (this.data[line+y][chr+x] != other.data[y][x]){
          match = false;
          break;
        }
      }
      if (match){
        loc = [line,chr];
        break;
      }
    } if (match) break;}
    return loc;
  }
  this.subsection = function(range){ // range is a standard quadruplet
    newspace = new Space();
    for (let line=0; line<=range[2]-range[0]; line++){
      newspace.data.push([]);
      for (let chr=0; chr<=range[3]-range[1]; chr++){
        newspace.data[line].push(
          this.data[line+range[0]][chr+range[1]] || ''
        );
      }
    }
    return newspace;
  }
}
    
module.exports = Space