Mark Gilbert's Blog

Science and technology, served light and fluffy.

Building an Equation

In my previous post, I covered how the complexity of each of the game’s levels are configured.  This post will walk through how that configuration gets turned into an actual equation.

After the Equation constructor verifies that the configuration is valid, it generates the list of terms for the new equation, in three steps:

this._Terms.AddRange(this.BuildTerms(CurrentConfig.NumVarsLeft, CurrentConfig.NumConstsLeft, CurrentConfig.MaxAbsCoefficient, CurrentConfig.MaxAbsConstant, CurrentConfig.AreVariableCoefficientsOf1Allowed));

this._Terms.Add(new EqualSign());

this._Terms.AddRange(this.BuildTerms(CurrentConfig.NumVarsRight, CurrentConfig.NumConstsRight, CurrentConfig.MaxAbsCoefficient, CurrentConfig.MaxAbsConstant, CurrentConfig.AreVariableCoefficientsOf1Allowed));

The first step is to generate the terms on the left side, then it adds an EqualSign, and finally generates the terms on the right side.  The _Terms private member is defined as a List<IAmATerm>, so it can hold constants, variables, and the equal sign, alike.  The BuildTerms() method is as follows:

private List<IAmATerm> BuildTerms(int NumVars, int NumConsts, int MaxAbsCoefficient, int MaxAbsConstant, bool AreVariableCoefficientsOf1Allowed)
{
List<IAmATerm> ListOfTerms;
int CurrentVariableNumerator, CurrentConstantNumerator;

    ListOfTerms = new List<IAmATerm>();

    if(NumVars == 0 && NumConsts == 0)
{
ListOfTerms.Add(new Constant(0, 1));
}
else
{
for (int i = 0; i < NumVars; i++)
{
if(this._AreMultiplicationAndDivisionNeeded)
{
CurrentVariableNumerator = Random.Range(-1 * MaxAbsCoefficient, MaxAbsCoefficient);
if (CurrentVariableNumerator == 0) { CurrentVariableNumerator++; }
}
else
{
CurrentVariableNumerator = 1;
}

            if (!AreVariableCoefficientsOf1Allowed && CurrentVariableNumerator == 1) { CurrentVariableNumerator++; }

            ListOfTerms.Add(new Variable(CurrentVariableNumerator, 1, this._VariableLetter));
}

        for (int i = 0; i < NumConsts; i++)
{
CurrentConstantNumerator = Random.Range(-1 * MaxAbsConstant, MaxAbsConstant);
if((NumConsts + NumVars) > 1 && CurrentConstantNumerator == 0) { CurrentConstantNumerator++; }
ListOfTerms.Add(new Constant(CurrentConstantNumerator, 1));
}
}

    // TODO: Randomize the terms in the list

    if(ListOfTerms.Count > 0) { ListOfTerms[0].SetAsFirst(); }

    return ListOfTerms;
}

First, please note the use of “numerator” throughout this logic.  The code maintains the coefficients and constants in fractional form, rather than decimal.  I’ll discuss this at length in a later blog post.  Currently, BuildTerms will only generate whole-number coefficients and constants – that is, numbers with a denominator of “1”.

First, it checks to see if the number of variables and constants it is supposed to add to this side are both zero.  If so, it adds a single constant of “0” to the equation and skips to the end.  The main conditional branch generates the requested number of variables first:

  • For variables, it checks to see if multiplication/division are needed.  If not, the coefficient for the new variable is “1”.  If those operations are needed, it generates a number in the range [-MaxAbsCoefficient, MaxAbsCoefficient].  If it generates a “0”, it increments it to force it to be “1” instead (variables with a coefficient of “0” are never allowed).
  • Next, it checks to see if coefficients of “1” are even allowed.  If they are not, it will increment any “1” coefficients, making them “2”.
  • Finally, it adds a new Variable object to the list using the generated coefficient and the chosen variable letter (which was selected in the constructor).

For constants:

  • It generates the new numerator in the range [-MaxAbsConstant, MaxAbsConstant].
  • Constants of “0” are allowed if there is only 1 constant (and 0 variables) allowed on this side of the equation.  If there are supposed to be more than that, the new numerator is incremented to make it non-0.
  • Lastly, it generates a new Constant from the new numerator, and adds it to the list.

After all of the terms have been generated, the logic marks the first term in the list explicitly as “first”, allowing the UI to render the number correctly (something else that I will cover in a later post).

You’ll see by the TODO that I originally wanted to have the method shuffle the terms randomly, so that variables are not always listed before constants.  However, I encountered problems getting the randomized list to actually display in the UI randomized.  I fought with it for a bit, and then just moved on.  Another issue for the future.

You may also notice that I’m using a mix of values passed in as parameters to the method, and also accessing a private member of the class directly.  I need to pass in “NumVars” and “NumConsts” explicitly because I’m calling this method twice – once for the left side of the equation and once for the right.  The other values apply equally to both sides of the equation (no pun intended), so I had a choice of how to make them available to the method.  The hybrid approach is something else on my list of things to clean up.

In my next post, I’ll cover generating the operations that the player can apply to the equation.

Advertisements

October 23, 2017 Posted by | Game - Algebra, Unity | Comments Off on Building an Equation