The Endless Engine

Creating Your Own Endless Engine Data

Basic Data

The randomizer uses a basic JavaScript object structure. For example, if you wanted to generate a random name for a fantasy RPG character, you could use:

const myData = {
    name: ["Adelbert", "Beatrice", "Celestine", "Duncan"]
}

var results = new Randomizer(myData, ["name"]).randomize();

If an array of values is provided for a field, a random element of that array will be returned (and evaluated recursively in one of the many ways described below).

Template Strings

Admittedly, this isn't very interesting, either as a randomization engine or as a name. Suppose, then, you wanted to randomize both the first name and the last name separately, and combine the results. In that case, you can have the name field return a template that references other fields:

const myData = {
    name: "{firstName} {lastName}",
    firstName: ["Adelbert", "Beatrice", "Celestine", "Duncan"],
    lastName: ["Beetleglen", "Candleroot", "Duskdale", "Foxhollow"],
}

After evaluating the name field, the randomizer finds template calls for firstName and lastName, so it then randomizes those and inserts them into the name template.

Conditional Randomization

Randomization by Other Result

Suppose you had a random character profession, as provided below:

const myData = {
    profession: ["hunter", "knight", "mage", "priest"],
}

Obviously, you would want to equip your characters appropriately for their profession. A hunter would hardly be able to sneak up on prey in plate armor, and a knight would probably be a poor choice to give a wizard's staff. Here, then, you would want to use conditional probabilities. Instead of returning a string as a result for our "equipment" field, you would instead return an object with the single key "by__profession", which itself contains another object with keys for each of the possible professions. For example:

const myData = {
    profession: ["hunter", "knight", "mage", "priest"],
    weapon: {
        by__profession: {
            hunter: ["bow", "crossbow", "dagger"],
            knight: ["sword", "flail", "axe"],
            mage: ["staff", "wand"],
            priest: ["staff", "wand"],
        }
    }
}

What's more, since the mage and priest both have the same options in this example, you could simplify them with a default field in place of both:

const myData = {
    profession: ["hunter", "knight", "mage", "priest"],
    weapon: {
        by__profession: {
            hunter: ["bow", "crossbow", "dagger"],
            knight: ["sword", "flail", "axe"],
            default: ["staff", "wand"],
        }
    }
}

Randomization by Numeric Range

Suppose next you wanted to add another profession, the thief, who can wield the same weapons as the hunter. Since you cannot have the key default twice, there is yet another way to handle grouping the field names. If you put two underscores between the names of fields, you can list as many as you want:

const myData = {
    profession: ["hunter", "knight", "mage", "priest", "thief"],
    weapon: {
        by__profession: {
            hunter__thief: ["bow", "crossbow", "dagger"],
            knight: ["sword", "flail", "axe"],
            mage__priest: ["staff", "wand"],
        }
    }
}

You can also condition your response based on a numerical range with the object key that looks like by__myField__range. The number range, then, uses an object key such as _1__3 to denote the range from 1 through 3. (The leading underscore is only there because JavaScript doesn't like starting any non-numerical key with a number and could be any single, non-numerical character, such as r1__3. If you would rather it start with a numerical character, you can put the key in quotes to make sure JavaScript knows it's a string, e.g. "1__3".

Suppose, then, you wanted to scale the equipment based on the character's level, which is an integer range from 1 to 10. If, then, you wanted characters to have basic equipment from levels 1 through 5 and magic equipment from levels 6 through 10, then you would add a by__level__range, as follows:

const myData = {
    profession: ["hunter", "knight", "mage", "priest", "thief"],
    weapon: {
        by__profession: {
            hunter__thief: {
                by__level__range: {
                    _1__5: ["bow", "crossbow", "dagger"],
                    _6__10: [
                        "bow of {magic}",
                        "crossbow of {magic}",
                        "dagger of {magic}"
                    ]
                }
            },
            knight: {
                by__level__range: {
                    _1__5: ["sword", "flail", "axe"],
                    _6__10: [
                        "sword of {magic}",
                        "flail of {magic}",
                        "axe of {magic}"
                    ]
                }
            },
            mage__priest: {
                by__level__range: {
                    _1__5: ["staff", "wand"],
                    _6__10: [
                        "staff of {magic}",
                        "wand of {magic}"
                    ]
                }
            }
        }
    },
    magic: ["the Storm-wood", "the Silver-moon", "Whisp-fire"]
}

In this example of the weapon data, we demonstrate several capabilities of the Endless Engine combined: Conditional randomization based on string value and numeric value as well as nested randomization templates. The many randomization methods of the Endless Engine are made specifically that they can be applied in any combination so that rich results with texture and depth can be created from your data.

Weighted Randomization

Suppose, next, you decide that magic is difficult to master, and so mages and priests are less common than hunters and knights. This can easily be accomplished with weighted probability values by splitting each result object into a value which contains the desired return value and the probability of that value, given as p:

const myData = {
    profession: [
        {
            value: "hunter",
            p: 3
        },
        {
            value: "knight",
            p: 2
        },
        {
            value: "mage",
            p: 1
         },
         {
             value: "priest",
             p: 1
          },
          {
              value: "thief"
              p: 3
          }
    ],
}

Here, the hunter is three times as likely to be returned as the mage. The knight is two times as likely as the mage. Moreover, since the probabilities add to 10, the mage has a 30% chance of being returned here. The numbers can sum to anything you want, though — you can make them add to 100 so that they read as percentages, make them small enough that they all sum to 1, or just use whatever numbers you want so they sum to an arbitrary value of 42. The Endless Engine will scale the numbers so that the results are the same so long as the proportions of each number of the total are the same.

Integer Randomization: Linear, Normal, and Exponential

Above, we gave an example of conditioning a character's equipment based on the character's level. How, then, would you randomize the level? There are three ways you can randomize an integer with the Endless Engine:

Linear Distribution

Linear distribution gives an even chance of any number in the range from the provided min through the max. The min and max must be provided in that order in an array with the key range, for instance:

level: {
    range: [1, 10]
}

Normal Distribution

The normal distribution biases the result by making values closer to the middle of the range more likely than values at the extremes, producing a bell curve. For instance, if you used:

level: {
    normal_range: [1, 10]
}

...you would see values of 5 and 6 much more often than values of 1 or 10. Although this seems an odd choice for character level, it could be useful for deciding basic character statistics (like Strength, Dexterity, etc. in D&D and related games) that tend toward an average but could, with less likelihood, fall above or below that value.

Exponential Distribution

The exponential distribution will give a ditribution where the probability (and thus the frequency of results) is one of exponential growth or decay. In fact, the exponential distribution is the only one where the order the numbers appear in the array matters, for the first number given will always be the more likely end of the result range. For instance, if you put:

level: {
    exp_range: [1, 10, 4]
}

There will be a much higher probability of level returning 1 than 10, wherease if you put:

level: {
    exp_range: [10, 1, 4]
}

There will instead be a much higher probability of returning a 10 than a 1.

The third number provided here is a weighting variable, where higher values will create a more drastic exponential distribution with much more common results of the first number and rarer results of the second, while a lower weight will make the exponential distribution more even.

The format Property

The format property is provided as a tool for expanded formatting options. Currently, the only formatting option the Endless Engine uses is allowing multiple elements to be returned for a given Randomizer field. For instance, if you wanted to not only return the name of a character, but also a short summary below it, you could use:

const myData = {
    name: {
        format: ["{firstName} {lastName}", "{summary}"]
    },
    firstName: ["Adelbert", "Beatrice", "Celestine", "Duncan"],
    lastName: ["Beetleglen", "Candleroot", "Duskdale", "Foxhollow"],
    summary: [
        "Captain of the Watch",
        "Champion of the Downtrodden",
        "Keeper of Secrets",
        "Wanted for Many Crimes",
    ]
}

What's Next?

Using the Endless Engine from Your Own Code
Learn how to call the Randomizer class to incorporate the Endless Engine however you want!

Try the Endless Engine Sample!
See the Endless Engine in action!