Use RRR for name generation.
authorJoe Wreschnig <joe.wreschnig@gmail.com>
Fri, 10 Oct 2014 11:53:24 +0000 (13:53 +0200)
committerJoe Wreschnig <joe.wreschnig@gmail.com>
Fri, 10 Oct 2014 11:53:24 +0000 (13:53 +0200)
ogre.html
ogre.js
rrr.js [new file with mode: 0644]
units.js

index bcdb0f5..f8d5df4 100644 (file)
--- a/ogre.html
+++ b/ogre.html
@@ -27,6 +27,7 @@
     <link rel=apple-touch-icon href=favicon_192.png>
     <link rel=stylesheet href=ogre.css>
     <script src=fastclick.js></script>
+    <script src=rrr.js></script>
     <script src=units.js></script>
     <script src=ogre.js></script>
     <!-- <script src=brain.js></script> -->
diff --git a/ogre.js b/ogre.js
index c670e93..9bc3211 100644 (file)
--- a/ogre.js
+++ b/ogre.js
@@ -57,64 +57,8 @@ var CHECK = "☒";
 var FIRST_EMPTY = /☐/;
 var LAST_TICKED = /☒([^☒]*)$/;
 
-function choice (a) {
-    return a[(Math.random() * a.length) | 0];
-}
-
-function cap (s) {
-    return s && s[0].toUpperCase() + s.slice(1);
-}
-
-function letters (n) {
-    var r = "";
-    n = n || 1;
-    while (n-- > 0)
-        r += choice("abcdefghijklmnopqrstuvwxyz");
-    return r;
-}
-
-function range (a, b) {
-    return a + (Math.random() * (b - a)) | 0;
-}
-
-function numerals (n) {
-    var r = "";
-    n = n || 1;
-    while (n-- > 0)
-        r += choice("0123456789");
-    return r;
-}
-
-function pid () {
-    return letters(range(1, 4)).toUpperCase()
-        + choice(["‑", "", ".", "/"])
-        + numerals(range(1, 4));
-}
-
-function roman () {
-    return choice(["I", "II", "III", "IV", "V", "VI",
-                   "VII", "VIII", "IX", "X", "XI", "XII",
-                   "XIV"]);
-}
-
-function oid () {
-    return choice([letters(1).toUpperCase() + choice("-./") + numerals(2)]);
-}
-
 function randomName (scheme) {
-    switch (scheme) {
-    case "id":
-        return choice([pid() + " " + cap(choice(NOUNS))],
-                      [cap(choice(NOUNS)) + " " + pid()]);
-    case "air":
-        return choice([
-            oid() + " " + cap(choice(ADJECTIVES)) + " " + cap(choice(BIRDS)),
-            cap(choice(ADJECTIVES)) + " " + cap(choice(BIRDS)) + " " + roman()]);
-    default:
-        return choice([
-            oid() + " " + cap(choice(ADJECTIVES)) + " " + cap(choice(NOUNS)),
-            cap(choice(ADJECTIVES)) + " " + cap(choice(NOUNS)) + " " + roman()]);
-    }
+    return G.expand(scheme);
 }
 
 var STATS = ["attack", "range", "defense", "aside"];
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 = {});
index 984b6fd..30f0841 100644 (file)
--- a/units.js
+++ b/units.js
@@ -9,79 +9,96 @@
    See https://creativecommons.org/publicdomain/zero/1.0/ for details.
 */
 
-var ADJECTIVES = [
-    "angry",
-    "cold",
-    "deadly",
-    "easy",
-    "faithful",
-    "fatal",
-    "fiery",
-    "harsh",
-    "lost",
-    "mean",
-    "mighty",
-    "noisy",
-    "old",
-    "proud",
-    "pure",
-    "quiet",
-    "sharp",
-    "slow",
-    "strong",
-    "true",
-    "",
-    "",
-    "",
-];
+var G = new rrr.Grammar({
 
-var BIRDS = [
-    "angel",
-    "arrow",
-    "cloud",
-    "eagle",
-    "falcon",
-    "owl",
-    "raptor",
-    "storm",
-    "swan",
-    "swarm",
-];
+    Model: ["<Oid> <Adjective> <noun>", "<Adjective> <noun> <roman>"],
+    Flying: ["<Oid> <Adjective> <bird>", "<Adjective> <bird> <roman>"],
+    Id: ["<Pid> <noun>", "<noun> <Pid>"],
 
-var NOUNS = [
-    "axe",
-    "boar",
-    "brute",
-    "claw",
-    "cobra",
-    "dagger",
-    "demon",
-    "fox",
-    "hyena",
-    "knife",
-    "lion",
-    "lynx",
-    "saber",
-    "scout",
-    "snake",
-    "spear",
-    "spire",
-    "stone",
-    "stream",
-    "sword",
-    "talon",
-    "thorn",
-    "tide",
-    "tooth",
-    "tower",
-    "tusk",
-    "venom",
-    "viper",
-    "wall",
-    "wave",
-    "wolf",
-    "worker",
-];
+    Adjective: { "<adjective>": 0.9, "": 0.1 },
+    Separator: { "<separator>": 1, "": 0.25 },
+    Oid: ["<letter><separator><digit>{2}"],
+    Pid: ["<letter>{1,4}<Separator><digit>{1,4}"],
+
+    adjective: [
+        "Angry",
+        "Cold",
+        "Deadly",
+        "Easy",
+        "Faithful",
+        "Fatal",
+        "Fiery",
+        "Harsh",
+        "Lost",
+        "Mean",
+        "Mighty",
+        "Noisy",
+        "Old",
+        "Proud",
+        "Pure",
+        "Quiet",
+        "Sharp",
+        "Slow",
+        "Strong",
+        "True",
+    ],
+
+    bird: [
+        "Angel",
+        "Arrow",
+        "Cloud",
+        "Eagle",
+        "Falcon",
+        "Owl",
+        "Raptor",
+        "Storm",
+        "Swan",
+        "Swarm",
+    ],
+
+    noun: [
+        "Axe",
+        "Boar",
+        "Brute",
+        "Claw",
+        "Cobra",
+        "Dagger",
+        "Demon",
+        "Fox",
+        "Hyena",
+        "Knife",
+        "Lion",
+        "Lynx",
+        "Saber",
+        "Scout",
+        "Snake",
+        "Spear",
+        "Spire",
+        "Stone",
+        "Stream",
+        "Sword",
+        "Talon",
+        "Thorn",
+        "Tide",
+        "Tooth",
+        "Tower",
+        "Tusk",
+        "Venom",
+        "Viper",
+        "Wall",
+        "Wave",
+        "Wolf",
+        "Worker",
+    ],
+
+    roman: ["I", "II", "III", "IV", "V", "VI",
+            "VII", "VIII", "IX", "X", "XI", "XII",
+            "XIV"],
+
+    letter: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+    digit: "0123456789",
+    separator: "-./",
+}, "<Model>");
 
 // Most game data in this file based on
 // http://www.sjgames.com/ogre/kickstarter/ogre-rec-sheets.pdf
@@ -115,7 +132,7 @@ var WEAPONS = {
 var UNITS = {
     "Superheavy Tank": {
         weapons: ["3 Main Guns", "3 Antipersonnel"],
-        nameScheme: "id",
+        nameScheme: "<Id>",
         tread: 18,
         size: 5,
         au: 3
@@ -184,7 +201,6 @@ var UNITS = {
         au: 25
     },
     "Ogre Mk. VI": {
-        nameScheme: "grand",
         weapons: [
             "3 Main Battery",
             "6 Secondary Battery",
@@ -224,7 +240,6 @@ var UNITS = {
         au: "25+"
     },
     "Doppelsoldner": {
-        nameScheme: "grand",
         weapons: [
             "2 Main Battery",
             "8 Secondary Battery",
@@ -266,7 +281,7 @@ var UNITS = {
     },
     // http://www.sjgames.com/ogre/articles/csa.html
     "CSA-10 (Magi)": {
-        nameScheme: "air",
+        nameScheme: "<Flying>",
         weapons: [
             "2 Bombloads",
             "1 Air-to-Air Missile",
@@ -278,7 +293,7 @@ var UNITS = {
         propulsion: 48,
     },
     "CSA-15 (Magi)": {
-        nameScheme: "air",
+        nameScheme: "<Flying>",
         weapons: [
             "4 Bombloads",
             "3 Air-to-Air Missiles",