aboutsummaryrefslogtreecommitdiff
path: root/src/enzymatic.js
blob: 6cd99bdf10b53053cea055a28ef9f0a10ca0a5e2 (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
/**
 * An implementation of an 'enzymatic programming' paradigm.
 */

DEBUG = true;
DEBUG = false;

Substrate = function(name_) {
  this.name_ = name_;
  this.items = [];
  this.zymes = [];
  this.currUid = 1;
};

Substrate.prototype = {
  addItem: function(item) {
    if (!item.__uid__) {
      item.__uid__ = this.currUid;
      this.currUid ++;
    }
    this.items.push(item);
  },

  addZyme: function(zyme) {
    this.zymes.push(zyme);
    if (!zyme.select) zyme.select = Zyme.prototype.select;
    if (!zyme.process) zyme.process = Zyme.prototype.process;
  },

  solve: function() {
    print("// Solving " + this._name + "...");

    var startTime = Date.now();
    var midTime = startTime;
    var that = this;
    function midComment() {
      var curr = Date.now();
      if (curr - midTime > 1000) {
        print('// Working on ' + that.name_ + ', so far ' + ((curr-startTime)/1000).toString().substr(0,10) + ' seconds. Have ' + that.items.length + ' items.');
        midTime = curr;
      }
    }
    function finalComment() {
      print('// Completed ' + that.name_ + ' in ' + ((Date.now() - startTime)/1000).toString().substr(0,10) + ' seconds.');
    }

    // Naive solver - sheer brute force.
    // Assumes list of Zymes is non-changing.
    var results = [];
    while (true) {
      if (DEBUG) print("Cycle start, " + this.items.length + " items.");
      var hadProcessing = false;
      for (var z = 0; z < this.zymes.length; z++) {
        var zyme = this.zymes[z];
        var selected = zyme.select(this.items);
        if (selected.length > 0) {
          if (DEBUG) print("Calling: " + (zyme.processItem ? zyme.processItem : zyme.process));
          if (DEBUG) {
            try {
              print("Inputs: \n---\n\n" + outputs.map(JSON.stringify).join('\n\n') + '\n\n---');
            } catch(e) {
              print("Inputs: \n---\n\n" + outputs + '\n\n---');
            }
          }
          hadProcessing = true;
          this.items = this.items.filter(function(item) { return selected.indexOf(item) == -1 });
          var outputs;
          try {
            outputs = zyme.process(selected);
          } catch (e) {
            print("Exception, current selected are: " + selected.map(dump).join('\n\n').substr(0,100));
            print("Stack: " + new Error().stack);
            throw e;
          }
          if (DEBUG) {
            try {
              print("Outputs: \n---\n\n" + outputs.map(JSON.stringify).join('\n\n') + '\n\n---');
            } catch(e) {
              print("Outputs: \n---\n\n" + outputs + '\n\n---');
            }
          }
          if (outputs.length === 1 && outputs[0].__finalResult__) {
            if (DEBUG) print("Solving complete: __finalResult__");
            delete outputs[0].__finalResult__; // Might recycle this
            delete outputs[0].__uid__;
            finalComment();
            return outputs[0];
          }
          results = results.concat(outputs.filter(function(output) { return !!output.__result__; }))
          outputs.filter(function(output) { return !output.__result__; }).forEach(this.addItem, this);
          results.forEach(function(output) {
            delete output.__result__; // Might recycle these
            delete output.__uid__;
          });
        }
      }
      if (this.items.length === 0) {
        if (DEBUG) print("Solving complete: no remaining items");
        finalComment();
        return results;
      }
      if (!hadProcessing) {
        print("Reached a dead end.");
        this.items.forEach(function(item) {
          print("remaining item:" + dump(item));
        });
        throw "failure";
      }
      midComment();
      this.items = this.items.filter(function(item) { return item !== null; });
    }
  },
};

Zyme = function() { };
Zyme.prototype = {
  select: function(items) {
    return items.filter(this.selectItem, this);
  },
  process: function(items) {
    var ret = [];
    items.forEach(function(item) { ret = ret.concat(this.processItem(item)) }, this);
    return ret;
  },
};