Tuesday, August 25, 2020

v1.1 update

Summary

The update brings a conservative stream reading feature, a few minor library usage changes, and some performance improvements.

Conservative stream reading

When exposing formulas for modding, you are probably going to have them as a part of a larger structured text file, like XML, JSON, INI, CSV, or your own DSL. In most cases you could use the syntax of the "outer" document to find out where each formula begins and ends but with this update, that is not entirely necessary.

The Formula Parser can work with two kinds of inputs: a string and a text stream. With a string, the parser assumes the whole input should be one valid formula while with a stream the parser tries to be conservative. This is a new feature where it assumes a formula starts at the current position in the stream and that there can be more data after the formula ends. Consider this input:

2+x a:4

Here the parser would read "2+x", look for another operator symbol, notice there is none (read whitespace and peek "a") and leave "a:4" in the stream. With this feature, you can make a domain-specific language with very light syntax where formulas are not enclosed by any particular symbols and can span multiple lines


Friday, July 24, 2020

Formula Parser

About

I'm proud to announce that Formula Parser has been published on the Unity Asset Store. It's a scripting tool for converting mathematical expressions from raw strings to an object that can be used to calculate the value of the expression. For example, expressions like this:

2 + 2
x * y
10 ^ -(x / 2)

Simple syntax

There are similar assets on the store so what makes this one stand out? First of all the expression syntax is designed for describing game math, meaning most expressions can be described simply in value-operator-value form, without functions and complex constructs. Examples above speak for themselves :), so let's take a look at something complex. Let's say there is a strategy game where factories produce 10 industry points initially and output can be increased with technologies, +2 with one, and +3 with the other. One could write factory output formula using if function:


10 + if(tech1 , 2, 0) + if(tech2, 3, 0)

 
But there is a better way, summation and multiplication can have conditions for when an operand applies to the total result. Question mark notation may look strange at first but on the second look it's much easier to read and comprehend:


10 + 2 ? tech1 + 3 ? tech2


There is also a "lookup" syntax when a piece or whole formula can be boiled down to reading a column in a table:


x [2, 3, 5, 7, 10]


If x is 0 result is 2, if x is 4 result is 10. No need to devise smart formulas that would produce those values through algebra. There is more lookup syntax can do, namely interpolation and extrapolation but that a topic for more in-depth discussion.

Variable management

Formula Parser doesn't limit you in how many variables an expression can have, or force you to use a naming convention, and it doesn't force you to supply them all at once when evaluating an expression. Instead, variable values are accessed through a variable provider that servers expression only with values it needs, from whichever runtime object you want and can be written to do it in a performant way.


Great pitch! Well, you have to roll your own variable provider implementation. Fear not, it's very simple:

class VarProviderExample : IVariableProvider<VarProviderExample>
{
    public PlayerData Player { get; set; }

    public Func<VarProviderExample, double> Get(string name)
    {
        swithc (name)
        {
            case "EXP":
                return v => v.Player.Experience;
            case "HP":
                return v => v.Player.HitPoints;
            case "LVL":
                return v => v.Player.Level;
        }

        throw new ArgumentException("Unsupported variable name", nameof(name));
    }
}


Embarrassingly simple. Hope you find it useful in making your game moddable.

v1.1 update

Summary The update brings a conservative stream reading feature, a few minor library usage changes, and some performance improvements. Conse...