Mark Gilbert's Blog

Science and technology, served light and fluffy.

Algebra Game – Configuring Complexity

From the beginning, CJ and I wanted the algebra game to have a series of levels.  The first level would show the player the simplest technique – combining like terms.  For example:

x = 3 + 4

Combining the “3” and the “4” into 7 would be enough to solve this equation.  The second and subsequent levels would teach the player additional techniques that he or she may need to solve more complicated equations, and then, of course, generate a series of problems that required that technique.

What this meant for the app itself was that it would need to be able to not only generate random equations, but equations that met certain, configurable parameters.  I created a class called “EquationConfig”:

using System;

[Serializable]
public class EquationConfig
{
public EquationConfig()
{
this.AreMultiplicationAndDivisionNeeded = true;
this.AreVariableCoefficientsOf1Allowed = true;
this.VariableLetterPool = VariableLetters.X;
}

    public class VariableLetters
{
public const string X = “x”;
public const string AnyValidLetterExceptX = “abcdfghjkmnpqrstuvwyz”;
public const string AnyValidLetter = “abcdfghjkmnpqrstuvwxyz”;
}

    public int NumVarsLeft;
public int NumVarsRight;
public int MaxAbsCoefficient;
public int NumConstsLeft;
public int NumConstsRight;
public int MaxAbsConstant;

    public bool AreMultiplicationAndDivisionNeeded;
public string VariableLetterPool;
public bool AreVariableCoefficientsOf1Allowed;
}

An EquationConfig object gets passed into the “Equation” class (which does the work of actually generating the new equation, based on the configuration passed in; I’ll go through this class in depth in my next post).

The core of EquationConfig are these properties:

  • NumVarsLeft – the number of variables to generate on the left side
  • NumVarsRight – the number of variables to generate on the right side
  • MaxAbsCoefficient – the maximum absolute value of the coefficients on each variable
  • NumConstsLeft – the number of constants to generate on the left side
  • NumConstsRight – the number of constants to generate on the right side
  • MaxAbsConstant – the maximum absolute value of each constant

Each equation will consist of some number of variables and constants on the left side of the equal side, and some number of each on the right side.  Each variable will have a randomly generated coefficient in the range [-MaxAbsCoefficient, MaxAbsCoefficient], except for 0.  Each constant will have a randomly generated value in the range [-MaxAbsConstant, MaxAbsConstant].  Constants of 0 are sometimes allowed (as we’ll see in the next post).

In addition to these, there are three more specialized properties of an EquationConfig:

  • AreMultiplicationAndDivisionNeeded: If this is true, then non-1 variable coefficients are allowed and operations involving multiplication and division are included in the tray; otherwise they aren’t.  These are needed to generate the lower levels where we want players to focus on learning to use addition and subtraction operations to solve equations.  Multiplication and division will come later.
  • VariableLetterPool: A string that determines what will be allowed for the variable – just “x”, most letters of the alphabet, or most letters of the alphabet except for “x”.  I say “most” letters of the alphabet because we initially left out the letters “o” and “i” – those looked too much like the numbers “0” and “1”.  Once we started playing, though, we quickly realized that the letters “e” and “i” should be left out as well, since those have special meaning in math (Euler’s number, and the square root of -1, respectively).  This is needed with the level that involves solving equations that have something other than “x” for the variable.
  • AreVariableCoefficientsOf1Allowed: If this is false, then all variables are required to have a non-1 coefficient.  This is used on the level that requires the player to use multiplication and division to simplify the variable.  Having something like “x = 2” right out of the gate would defeat that purpose.

There are combinations of settings in EquationConfig that are not valid, so Equation needs to vet the config passed in before using it.  For example, if multiplication/division are not needed, allowing more than one variable term on the left could lead to situations like this:

x + x = 2
2x = 2

Without a “divide by 2” operation, the player couldn’t solve this.

Here is the core validation logic, currently contained in the Equation constructor:

this._AreMultiplicationAndDivisionNeeded = CurrentConfig.AreMultiplicationAndDivisionNeeded;
if (!this._AreMultiplicationAndDivisionNeeded && CurrentConfig.NumVarsLeft > 1)
{
throw new EquationConfigurationException(EquationConfigurationException.InvalidStates.TooManyVariableTermsRequestedOnLeft);
}
if (!this._AreMultiplicationAndDivisionNeeded && CurrentConfig.NumVarsRight > 1)
{
throw new EquationConfigurationException(EquationConfigurationException.InvalidStates.TooManyVariableTermsRequestedOnRight);
}
if (!this._AreMultiplicationAndDivisionNeeded && CurrentConfig.NumVarsLeft > 0 && CurrentConfig.NumVarsRight > 0)
{
throw new EquationConfigurationException(EquationConfigurationException.InvalidStates.TooManyVariableTermsRequested);
}

this._VariableLetter = this.PickALetter(CurrentConfig.VariableLetterPool);

// Step 2. Build the equation here (covered in next blog post)

this.EnsureTheVariablesAreNotBalanced();

The invalid states are all around variables.  If multiplication/division are not needed:

  • make sure there is at most 1 variable on the left.
  • make sure there is at most 1 variable on the right.
  • make sure there is only 1 variable in the entire equation (don’t allow variables on both sides of the equal sign).

In most ways, the last check covers the first two.  However, having all three allows me to record a more specific error.

Next, we need to pick a letter to use as the variable in the new equation.

Next, build the left and right sides of the equation, based on the newly vetted configuration.  (I’ll cover this later.)

Finally, make sure the variables in the equation are NOT balanced.  One of the earlier iterations of the game generated an equation that looked like this:

x = x – 9

There’s no valid answer for the value of “x”.  The final step of the validation, then, is to ensure that the sum of the variable coefficients on the left does NOT equal the sum on the right.  If Equation random generates an equation where left and right are equal, it flips the sign of the last variable in the equation, so the above would become:

x = -x – 9

Which does have a solution.

In retrospect, this validation logic probably makes more sense as a method on EquationConfig (VerifyThyself, or something like it) than in the Equation constructor – a refactoring for the future.

These configuration options allowed CJ and I to define the initial five levels of the game as follows:

  • Level 1: Combining constants on the same side of the equal sign.  Shows the user that they can drag constants to combine them.  Requires a single variable with a coefficient of 1 on one side, and two constants on the other.
  • Level 2: Moving all of the constants to one side of the equal sign.  Introduces addition and subtraction operations, and teaches the player to apply those operations to both sides equally.  Requires a single variable with a coefficient of 1 on one side, and constants on both sides.
  • Level 3: Reducing the variable’s coefficient to 1.  Introduces multiplication and division operations, and teaches the player to apply those operations to every term on both sides.  Requires a variable on one side with a coefficient of something other than 1, and a constant on the other side.
  • Level 4: Reducing the number of constants, and reducing the variable’s coefficient.  Combines the previous lessons, and teaches the player that they sometimes need to perform multiple steps to solve the equation.
  • Level 5: Solving for variables other than “x”.  Similar to Level 4, except that now the variable in use is something other than “x”.

These level-configs are currently stored in a JSON file that gets loaded with the game, but the groundwork is laid for future versions to call a web service to pull them down.

In my next post, I’ll go through the logic that uses the configuration to actually generate an equation, including the edge cases it handles.

Advertisements

October 16, 2017 Posted by | Game - Algebra, Unity | Leave a comment

Giving the game a voice – TextBoxDribble

One day on a car trip, CJ and I were brainstorming about the algebra game, and my two daughters got into the act.  The three girls started working out a storyline for the game, and how there should be a guide of some sort that leads you through the levels.  The girls did some sketches, and I quickly threw one in to see what it would look like.

10

A lot of the game is (we think) intuitive, or is explained to you in the tutorials.  However, there are a handful of messages that can appear during gameplay, mostly around telling you when you tried to do something invalid (like applying an operation to only one side of the equation).  I thought it would be cool to have this wizardly-looking mentor fellow “speak” those feedback messages to the user.  To make that look a little more convincing, I wanted the message to “dribble” in, one word at a time, instead of having the full message appear over the image, to mimic someone speaking them.

TextBoxDribble

To do that, I wrote a script called TextBoxDribble.  As the name implies, this script will make the message you assign to a textbox appear to dribble in.  Here is the script, in its entirety:

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Text))]
public class TextBoxDribble : MonoBehaviour
{

[Tooltip(“Must be greater than 0; defaults to 1”)]
public int FramesBetweenEachWord = 1;

private string _PreviousValue;
private string _NewValue;
private string[] _Words;
private bool _ShouldWatchForChanges;
private bool _ShouldDribbleTheWordsIn;
private int _WordIndex;

private void Start()
{

this._ShouldWatchForChanges = true;
this._ShouldDribbleTheWordsIn = false;
this._WordIndex = 0;
this._PreviousValue = this.GetComponent<Text>().text;

        // Make sure FramesBetweenEachWord is always something valid;
if (FramesBetweenEachWord <= 0) { FramesBetweenEachWord = 1; }

}

void Update ()
{

if(this._ShouldWatchForChanges && !this._PreviousValue.Equals(this.GetComponent<Text>().text))
{

this._ShouldWatchForChanges = false;
this._NewValue = this.GetComponent<Text>().text;
this.GetComponent<Text>().text = “”;
this._Words = this._NewValue.Split(‘ ‘);
this._WordIndex = 0;
this._ShouldDribbleTheWordsIn = true;

}

if(this._ShouldDribbleTheWordsIn)
{

if (this._WordIndex < this._Words.Length)
{

if(Time.frameCount % FramesBetweenEachWord == 0)
{

this.GetComponent<Text>().text = string.Format(“{0} {1}”, this.GetComponent<Text>().text, this._Words[this._WordIndex]);
this._WordIndex++;

}

}
else
{

this._PreviousValue = this.GetComponent<Text>().text;
this._NewValue = “”;
this._ShouldDribbleTheWordsIn = false;
this._ShouldWatchForChanges = true;

}

}

}

}

}

The script’s Update() method has two main pieces.  The first conditional looks for changes to the textbox .text property.  When the textbox is assigned a new value, the script pulls it out, breaks it into an array of words, clears the textbox, and sets _ShouldDribbleTheWordsIn to true.

That signals the second block to go into action.  This block begins to reconstruct the new message, one word at a time.  Once the entire original value has been assigned, it goes back to watching for new changes.

The user can control how quickly the words appear through the public FramesBetweenEachWord property, which gets exposed in the Unity editor.  The name, hopefully, explains how it will work – the higher the value, the more time passes between each word getting appended, and the slower the overall phrase will come in.

October 9, 2017 Posted by | Game - Algebra, Unity | Leave a comment

Drag-and-drop, Event-ually

I mentioned in my previous Unity post that the logic powering the dragging and dropping on the equation screen was not straightforward to develop.  I had multiple false starts looking for a pattern that could support everything I needed it to:

* Dragging operations out of the OperationsTray and dropping them onto a domino to place it
* Dragging operations out of the OperationsTray or a domino, and dropping it onto the screen to delete it
* Dragging operations out of a domino to place it into another domino
* Dragging dominos to merge them with another
* Etc.

What I finally landed on was evaluating the Input.GetMouseButtonDown, Input.GetMouseButton, and Input.GetMouseButtonUp methods in the EquationScreenController.Update event handler.  (“Mouse button” here covers both clicking a mouse button as well as touching the screen with your finger.)  These methods return true when the user first clicks the mouse button to grab something, holds the mouse button down to move it, and then releases the button to drop it, respectively.

Once the user grabbed something, I needed logic to determine what they grabbed and were now dragging.  As they were dragging it, I had to refresh the UI so that the thing being dragged would actually track with their finger.  Once they dropped it, I had to evaluate if they dropped it on a valid spot or not, and do something in both cases.

My first working iteration of this required pages and pages of conditional logic – very ugly and hard to debug.  I needed a better way.  Luckily, history repeats itself – or is it “recurses”?

Years ago, when my colleagues and I were playing with the Microsoft Kinect, we started a project where the user would dress a paper doll on screen.  The user would “pick up” some article of clothing, move it over the doll, and then “drop it”.  “Pick up” in this game translated into “hold your hand over the article of clothing on the screen for a second or two until you convinced the software that you wanted THAT one”.  At that point, it would pop it off of the “wardrobe” and being to track with your hand.  “Drop it” translated into the user pushing their hand toward the Kinect, as if he or she were pushing a piece of paper onto a corkboard.

What that app taught me was the value in custom events.  The Kinect reports the three-dimensional position of 20 different joints for the user, 30 times a second.  I ended up writing a “manager” class that would translate the raw data coming off the Kinect into a series of events being raised – specifically things like hover, pick up a piece of clothing, drop a piece of clothing, etc.  That translation layer greatly simplified writing, and especially debugging, the rest of the app.

I decided to go the same route here.  Here is a slightly simplified version of the new EquationScreenController.Update() logic:

public void Update()
{

if (Input.GetMouseButtonDown(0))
{

this._DragDropManager.EvaluateInputDown(Input.mousePosition);

}
else if (Input.GetMouseButton(0))
{

this._DragDropManager.EvaluateInputMove(Input.mousePosition);

}
else if (Input.GetMouseButtonUp(0))
{

this._DragDropManager.EvaluateInputUp(Input.mousePosition);

}

}

The DragDropManager.Evaluate… methods would translate the raw UI input into a series of events to be raised:

  • OperationPickedUp
  • DominoPickedUp
  • OperationMoved
  • DominoMoved
  • DroppedOnNothing
  • DroppedOnDomino

My EquationScreenController, then, would handle those events and do all of the necessary UI steps.

This structure allowed me to not only abstract the messy details of the translation away, but it broke up the monolithic Update() method I had going into smaller, and much easier to debug, methods.

As a bonus, the refactored structure made it possible to unit-test the three DragDropManager methods.  I could set up various scenarios, and verify that the proper events were being raised given that input.

October 2, 2017 Posted by | Game - Algebra, Unity | Leave a comment

Staying on top of things

One of the first things I tackled for the algebra game was the drag-n-drop logic for the equation screen.  The path to that logic was not straightforward at all, and I’ll go into more detail about that in my next post.  One of the first oddities I encountered testing this was that the gameobject I was dragging didn’t always appear on top of everything else.  Sometimes it looked like my operation, for example, was sliding behind its siblings.

On the equation screen, there are two types of things that are draggable – operations:

10

And the terms of the equation, which appear in a UI control CJ dubbed “dominos”:

20

Initially, I found that the operation/domino being dragged would appear behind the operations/dominos to its right, along with several other controls rendered on the page.  After a bit, I realized what was happening.  Unity determines the z-order (depth) of the gameobjects on the screen in the order they appear in the Hierarchy tab.

30

Things that appear higher up in the hierarchy will be rendered visually behind things further down in the hierarchy.  When I populate the list of operations, I add them one after another to the OperationsPanel, which meant that the first one would appear visually behind the second, the second behind the third, and so on.

To solve this, as soon as I start dragging an operation or a domino, I re-parent it to the top-level canvas object, and then I make it the last sibling of the canvas:

this._ParentCanvas = transform.root.gameObject;
this._OperationBeingDragged.transform.SetParent(this._ParentCanvas.transform);
this._OperationBeingDragged.transform.SetAsLastSibling();

Since I can only drag one gameobject at a time, these two steps guarantee that the item being dragged will appear below every other gameobject in the hierachy, which means it will appear visually on top of everything else.  When I drop it onto a new container, I re-parent it to that container, which effectively resets it to the “proper” place in the hierarchy.

September 25, 2017 Posted by | Game - Algebra, Unity | 1 Comment

3D-Printed Desk Fan Base

I’ve had a desk fan at home for years, but I was never happy with the base.  It was just too deep:

10

That large ring pushed the fan too far out from the wall, so it was taking up way more room than I thought it should.  The fan’s existing base attached with two screws, which were easily removed:

20

A little while ago, my brother got a 3D printer, and offered to print stuff for us.  All we needed to do was send him the file.  I decided it was time to try my hand at this, and see if I could come up with a better base.  The printer could handle things up to 5.5” x 5.5” x 5.5”.  That was more than enough to accommodate the height of what I had in mind, but to make the new base stable, I figured it would need to be close to the width of the fan, which was more like 8 inches.  Even if the printer could print something that large, I didn’t want to use that much filament printing a solid block of plastic.  I dug around my junk box, and found these:

22

These were bendable metal straps that came with a swingset anchor kit, but they were too small to use with our new swingset.  If I turned them up on their edges, though, they could become the legs of the new base.  With that idea in hand, I fired up SketchUp to generate the 3D drawing.  Fast forward 3 hours, and I arrived at this:

25

I exported this as an STL file, and emailed it to my brother.  Fast forward several hours again, and we get this:

30

The top is solid.  The bottom has two channels that I hoped would allow me to insert the metal straps:

40

I started by drilling out two holes that would hopefully match the fan’s base.

60

They are intentionally offset because I wanted the fan to lean back a little.  Next, I cut a piece of 12-gauge solid copper wire, and bent it into a U-shape:

70

The holes were just large enough to accommodate the wire, so friction would do most of the work of holding it in place:

80

90

Next it was time to insert the new legs.  I bent the metal straps to match the V-shaped bends in the base, and worked them in gently:

120

I was most worried about pieces of the plastic base snapping off if I pushed it too hard, but it remained solid. Here’s the assembled base:

130

I re-attached the base to the fan, and bent the wire over to make doubly-sure it wouldn’t pull out accidentally:

135

The new base didn’t look too bad:

140

And it was significantly less deep than the original:

150

I did notice the fan felt like it would fall backward a little too easily now, but another bend to the rear legs solved that:

160

All in all, a resounding success.  3D printing got a new fan today.

Ahem.

July 4, 2017 Posted by | Maker Life | Comments Off on 3D-Printed Desk Fan Base

Unit-Testing in Unity

CJ and I are collaborating to build an app that teaches you how to solve single-variable algebraic equations like this:

2x – 4 = 16

Our target audiences are kids and teachers, so it was obvious to us that this would be a mobile app.  For me, the choice of development platform was also a no-brainer – Unity:

  • Katherine and I had tinkered with Unity a while ago, so I was already familiar with it
  • It can generate binaries for every platform we’re considering, and then some (XBox or PS4 anyone?)
  • One of the primary programming languages supported was C#
  • Unity integrates with Visual Studio (you can also use MonoDevelop, but if you know and love VS, why?)
  • Unity has a flourishing user community
  • I could get started for free

One of the very first things I figure out with any new development language / platform / IDE is how to build unit-tests with it.  As it turns out, Unity has a built-in test-runner based on NUnit (yet another point of familiarity for me).  The runner is available under the Window menu, but normally I leave it docked on the right side of the IDE.

10

Now the next step was figure out WHERE my tests should actually live.  After a little digging I found an article on the Unity Test Runner.  The test runner has two modes – Play and Edit.  I started with Edit, and (as the article shows), there was a button for “Created EditMode Test”.  I clicked it, and it created a folder called Assets/UnitTests/Editor.  I tried renaming and moving the folders in that string, and kept on eye on the test runner – would it find my test if I put it in a folder called this?.  As it turns out, the “Editor” folder has special meaning for Unity.  I settled on a folder structure of Assets/Editor/UnitTests (inverting what Unity created by default), so I could add other Editor scripts later if I needed.

20

Now that I had the basic structure, it was time to get down to business, and start writing the logic for the game.  Fast-forward several weeks and 450+ unit tests later, and I have a few observations about unit testing in Unity.

The integration between the Unity IDE and Visual Studio is fairly seamless.  I can add a new test in the latter, switch back to the former (where it kicks off a compilation in the background automatically), and see it appear a second or two later.  In Studio, there is a preset button for attaching to Unity, allowing me to debug through my test and business logic easily.  The one minor quirk is how Unity reports compilation errors – subtly, at the very bottom of the Unity UI:

30

This stung me a couple of times – I would make some typographical error, miss this clue that my new code wasn’t actually compiling, and re-ran my unit tests expecting a change, only to find that the same test was failing because my new code wasn’t actually being used yet.  To avoid this, I now do an explicit compilation in Studio, and let Studio throw up the errors and warnings I’m used to seeing.

As a whole, the test runner does its job reliably.  One thing I’d change, though, is how it reports the current state of the tests.  If you’ve already run your test suite once, and want to run it again, Unity doesn’t clear all of the green (and red) flags for the already-run tests.  Unless something switches from red to green or vice versa, you really don’t have any indication which test is being run at the moment, how far it is through the suite, etc.  There IS a progress bar that appears while the tests are being executed:

40

And once that disappears you know it’s done, but I’d prefer the test statuses to be cleared before each run so I can watch it work through the suite again, fresh.

I’ve also noticed that the tests run much faster if you keep the focus on Unity the whole time.  More than once I would kick off a series of tests, and switch to some other window while they ran, and found my tests were still running long beyond when they should have (I’ve seen suites take 2-7 times longer to complete this way).  Again, a relatively easy issue to avoid – don’t switch away from Unity while the tests are running – but an odd one.

Overall, I’m very happy with my choice of Unity.

June 18, 2017 Posted by | Agile, Game - Algebra, Unity, Visual Studio/.NET | Comments Off on Unit-Testing in Unity

Moving back from the Cloud

Back in August I mentioned moving my critical files to the cloud so I’d have them available from both home and at work.  At the time I selected OneDrive to be my cloud storage provider, and mapped a network drive on both machines so all of my shortcuts would work.

While it solved the problem of losing a very tiny thumbdrive, it introduced some serious lag to Tasks, my custom-written task management app.  Every time I needed to create a new task, update an existing one, or reprioritize the things on my todo list, there was at least 2-3 seconds (and sometimes a lot more) delay in committing that change to OneDrive.  Very soon after I began using OneDrive like this, I started to think through how I could hide more of this lag in the background of Tasks so it would be less noticeable.

Then last week I started getting permission errors from OneDrive.  After some tinkering, I found that I could no longer even drag files into the drive letter I had mapped, or directly modify files through the drive letter, without it complaining about permission issues.  If I did everything through the browser, it was fine, but not through my drive letter.

I spent a night troubleshooting it, and then I pulled out my old thumbdrive as an interim solution.  I went a week like that, hoping that Microsoft would sort out whatever it was they had changed.  This weekend I retested it, and found it was still misbehaving.  I took another look at using Google Drive, but apparently you need some third-party software to map a drive letter to it, so I abandoned that idea.

That’s when I took a hard look at what I had in the cloud, and found that I either didn’t really need access to those files at work, or came up with alternatives (like the occasional email to myself).

Today was my first day 1) not carrying a thumbdrive, and 2) not relying on the cloud for anything relating to my todo list.  To my surprise, I barely noticed the difference.  I felt just as productive, and no longer experienced any of the lag I was seeing with saving things to OneDrive.  I still use Tasks at home, though, but the files are now stored on my local server, rather than in the cloud, so again – no thumbdrive and no lag.

Am I annoyed with OneDrive for ceasing to work?  A little, but being forced to give it up allowed me to ditch the lag at the same time, so I won’t be annoyed for long.

January 30, 2017 Posted by | Tools and Toys | Comments Off on Moving back from the Cloud

Dropping a mic – and picking up another

In the middle of our most recent podcast we had a major technical failure.

10

The microphone Katherine, Lucy, and I have used for the first 32 podcasts finally gave up the ghost.  To be fair, it had broken a couple of times before, and I had apply liberal amounts of glue to piece it back together.  This time, though, half of the audio tracks Lucy and I recorded had too much static to be usable.

So, we said goodbye to Computer Associates, and hello to Tonor.

20

This is a Tonor 3.5mm Cardoid Condensor Microphone.  We recorded all of the tracks for Episode 33 with the Tonor, and we think it sounds at least as good.  Let us know what you think!

January 11, 2017 Posted by | Podcast, Tools and Toys | Comments Off on Dropping a mic – and picking up another

Science & Technology Podcast, Episode 33: String Shooter

Mark and Lucy build a machine that shoots string, and lets them play with waves:

https://markofquality.wordpress.com/2017/01/10/science-technology-podcast-episode-33-string-shooter/

January 10, 2017 Posted by | Podcast, Science | Comments Off on Science & Technology Podcast, Episode 33: String Shooter

Science & Technology Podcast, Episode 32: Summoning Thor’s Hammer

Where we build a second prototype of a flying Thor’s Hammer that you can summon:

https://markofquality.wordpress.com/2016/09/23/science-technology-podcast-episode-32-summoning-thors-hammer/

September 23, 2016 Posted by | Podcast | Comments Off on Science & Technology Podcast, Episode 32: Summoning Thor’s Hammer