Managing complexity when implementing complex conditionals

Programming is in large part about managing complexity and imposing structure onto a problem domain in order to make it simple (simple enough for a machine to be able to do the solution, as well as simple enough for human programmers to be able to build it).

Something struck me about a technique that I used when I was developing my game last weekend.

The code that handles the setup of the simulated atari 2600 console is full of conditionals like this:

if PowerPlug.is_plugged_in && (console.switch_power.position == POWER_ON) 
 && (TV.switch_power.position == POWER_ON) && Cartridge.is_plugged_in && 
 Joystick.is_plugged_in 
{
 //simulate the game 
}

So one thing that I had to deal with as I was building this was all the preliminary stuff’ you know, to create all the objects that needed to be checked in the conditional statement above. Rather than do that, all at once, and have a lot of conditions that could have bugs in them, any one of which could make the thing not work, and be hard to figure out since multiple bugs could end up canceling each other out, or mask one another in some way, I opted to simplify things at first:

if true
{
 //simulate the game
}

The advantage here is obvious. It’s simple, and you KNOW that conditional is always going to evaluate to true! So you can get right into working out the code inside the branch, without worrying for now about the conditional logic that would cause it to execute.

Once that was working, I added in stuff:

if PowerPlug.is_plugged_in
{
 //simulate the game
}
else
{
 if PowerPlug.was_plugged_in_previously
 {
 kill_game();
 }
}

Then from there it became easier to elaborate on the conditional:

if PowerPlug.is_plugged_in
{
 if Console.switch_power.position == POWER_ON
 {
 //simulate the game
 }
}
else
{
 if PowerPlug.was_plugged_in_previously
 {
 kill_game();
 }
}
 
//etc.

(Note, the above isn’t the exact code from my project, which is a bit messy at the moment as I have some refactoring to do, but it illustrates the point.)

I found that by coding it in this way, I could iterate and test each little piece of the logic easily, and make sure it was working before trying the next piece.

I found that doing it this way made it much, much easier to handle the complex nested if business, and as well I found it easier to know which branches of the complex conditional tree the next bit of code needed to belong in.

Now that I did it this way, it seems obvious, nothing of great insight… but 6 years ago I would have tried to code the whole thing out, and then pulled my hair out swearing at myself when it didn’t work as expected, and I had to untangle all the logic to figure out wtf it wasn’t working.

Back then I would have thought that a really superior version of me who I aspired to be would be able to code out all the conditions in one go without making a mistake.

Today a really superior version of me knows better. Start simple, add elaboration through iteration.

This is a good lesson to hang onto. And to pass on.

Of course, there’s also often ways to avoid having to code conditionals at all, but that’s a topic for another post.

4 Comments

Add a Comment
  1. Another way to keep conditionals simple is to use helper methods:

    all_systems_go() 
    { 
       return PowerPlug.is_plugged_in && (console.switch_power.position == POWER_ON) && 
              (TV.switch_power.position == POWER_ON) && Cartridge.is_plugged_in && Joystick.is_plugged_in
    }

    This way, the code would read:

    if all_systems_go()
    {
       //simulate the game
    }

    Which is a lot easier to understand the intent of than all that complicated boolean math that it replaces and abstracts away from the reader.

      

  2. Interesting post.

    “Of course, there’s also often ways to avoid having to code conditionals at all, but that’s a topic for another post.” – would be good to hear about that sometime.

      

    1. I’ve touched on this in the past… My article on using instance_change() rather than complex if or switch statements in a State Machine pattern is a good example of what I’m talking about.

      The lesson is, rather than having objects with complex state that is tricky to figure out and keep track of, and handling its behavior in a complex conditional, have a different object representing each state, and since the state is known based on which object it is, you don’t have to have all the branching logic and the attendant conditionals which handle it, resulting in very simple objects that do only one thing and are thus easier to debug and less likely to have problems in the first place.

        

      1. Ahh! Nice. Yes makes sense. Can’t believe I didn’t know about that function until now. Thanks again!

          

Leave a Reply