Use RRR for name generation.
[ogre.git] / rrr.js
diff --git a/rrr.js b/rrr.js
new file mode 100644 (file)
index 0000000..4561421
--- /dev/null
+++ b/rrr.js
@@ -0,0 +1,104 @@
+/* rrr - recursive random rewrites
+   Copyright 2014 Joe Wreschnig
+   Licensed under the terms of the GNU GPL v2 or later
+   @license https://www.gnu.org/licenses/gpl-2.0.html
+   @source: https://yukkurigames.com/rrr/
+*//*
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   As additional permission, you may distribute the program or works
+   based on it without the copy of the GNU GPL normally required,
+   provided you include this license notice and a URL through which
+   recipients can access the corresponding source code.
+*/
+
+(function (exports) {
+    "use strict";
+
+    function randfloat (hi) { return Math.random() * hi; }
+    function randint (hi) { return randfloat(hi) | 0; }
+    function randchoice (seq) { return seq[randint(seq.length)]; }
+    function randrange (lo, hi) { return lo + randint(hi - lo); }
+
+    function Productions (dfn) {
+        this.__total = 0;
+        if (Array.isArray(dfn) || dfn.toString() === dfn) {
+            this.__rules = Array.prototype.slice.call(dfn);
+        } else if (typeof dfn === "function") {
+            this.__rules = dfn;
+        } else {
+            this.__rules = {};
+            Object.keys(dfn).forEach(function (key) {
+                var value = dfn[key];
+                this.__total += value;
+                this.__rules[key] = value;
+            }, this);
+        }
+    }
+
+    Productions.prototype.randproduction = function () {
+        if (Array.isArray(this.__rules))
+            return randchoice(this.__rules);
+
+        else if (typeof this.__rules === "function")
+            return this.__rules();
+
+        var x = randfloat(this.__total);
+        for (var k in this.__rules) {
+            x -= this.__rules[k];
+            if (x < 0)
+                return k;
+        }
+        return null;
+    };
+
+    function Grammar (productions, start) {
+        this.start = start || "";
+        this.productions = {};
+        for (var k in productions)
+            this.productions[k] = new Productions(productions[k]);
+    }
+
+    function randcount (lo, hi, rep) {
+        var n = hi ? randrange(+lo, +hi + 1)
+            : lo ? +lo
+            : 1;
+
+        var m = 1;
+        switch (rep) {
+        case '*': m = 0; while (Math.random() < 0.5) ++m; break;
+        case '+': while (Math.random() < 0.5) ++m; break;
+        case '?': m = +(Math.random() < 0.5); break;
+        }
+        return n * m;
+    }
+
+    function repeat (n, f) {
+        var r = [];
+        while (n-- > 0)
+            r.push(f());
+        return r;
+    }
+
+    var R = /<([^> ]+)( ?)>(?:{([0-9]+)(?:,([0-9]+))?}|(\*|\+|\?))?/g;
+    Grammar.prototype.expand = function (start) {
+        var g = this;
+        start = start || this.start;
+        return start.replace(R, function _ (match, p, w, lo, hi, rep) {
+            var prod = g.productions[p];
+            return repeat(randcount(lo, hi, rep), function () {
+                return prod.randproduction().replace(R, _);
+            }).join(w);
+        });
+    };
+
+    exports.Grammar = Grammar;
+})(typeof module !== "undefined" ? module.exports : this.rrr = {});