Fixing the State Machine: Aid Game Repair

Fixing a State Design Pattern
In a recent post to illustrate the use of composition and delegation, I created a simple game using a State design Pattern—the Aid Game. A lot of people had lots of questions and suggestions for making it better and being one who thrives on improvement and change, I promised to have a post where we’d review it as a State design pattern and not a quick and dirty example of composition.
In this post I’d like to go over some key elements of a State design and to see if we can improve on the original Aid Game by better design of state classes. Also, I’m going to leave the final repairs up to you—I’ll provide some guidelines for improved states, and you can implement them. Also, I’ll provide a little helper class that is an example of continuous movement based on one from our book that Chandima used to illustrate MVC. (I simplified it from the original; so if you look at the MVC chapter—Chapter 12—, you’ll only see pieces of it.)
Why a State Design and What are the States?
To get started, we have to ask the same key question that we should ask for every design we do. Namely,
What varies?
In moving the helicopter around in the Aid Game, the main thing that varies is the state of flight. (Each of the different directions and a stopped state represents a variation in state.) Ergo, I selected the State Design Pattern.
In our simple State design, I used five different states:
- Stop
- Move Right
- Move Left
- Move Up
- Move Down
Next, I’ll add some rules that were not in the first version:
- Before reversing direction, you must first go to the Stop state.
- If a current state is called, it cannot invoke itself.
- Movement is continuous
Let’s look at these rules applied to the Move Right state:
- Stop—call Stop state
- Left —call Stop state (Cannot reverse direction—must stop first)
- Right—already moving right–null operation
- Up—call Up state
- Down—call Down state
In the initial version, (The Aid Game) states called themselves, but that’s not allowed now. So, you can change the states to reflect these new rules.
The final rule is really one that better reflects state—continuous movement. To reflect this new game characteristic I put together a simple helper class, Mover. Using this class, the helicopter keeps a constant state moving left, right, up, down or stop.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | package { import flash.utils.Timer; import flash.events.TimerEvent; import flash.display.Sprite; public class Mover extends Sprite { private var timeOn:Timer=new Timer(1000/200); private var sp:Sprite; //Go to the right public function goRight(sp:Sprite):void { timeOn.stop(); this.sp=sp; timeOn.addEventListener("timer",doRight); timeOn.start(); } private function doRight(e:TimerEvent):void { sp.x++; } //Go to the left public function goLeft(sp:Sprite):void { timeOn.stop(); this.sp=sp; timeOn.addEventListener("timer",doLeft); timeOn.start(); } private function doLeft(e:TimerEvent):void { sp.x--; } //Go up public function goUp(sp:Sprite):void { timeOn.stop(); this.sp=sp; timeOn.addEventListener("timer",doUp); timeOn.start(); } private function doUp(e:TimerEvent):void { sp.y--; } //Go down public function goDown(sp:Sprite):void { timeOn.stop(); this.sp=sp; timeOn.addEventListener("timer",doDown); timeOn.start(); } private function doDown(e:TimerEvent):void { sp.y++; } //Stop everything public function goStop(sp:Sprite):void { timeOn.stop(); this.sp=sp; sp.x=sp.x; sp.y=sp.y; } } } |
You don’t have to use that class, and if you want you can put the operations in the individual states. However, to see how it works, test it out with the helicopter in the Aid Game. Then you can decide whether you want to use it or not. Generally, I’d assume that you’d have some way of invoking movement through the Context by a call in the State child classes.
Let’s See What You’ve Made
Okay, now’s a good chance to implement those algorithms. Put them into the State structure and let’s see how you’ve repaired the state machine. We’re not looking for a better algorithm per se, but rather how it can be used with the State design pattern. It doesn’t have to be ready for prime it, but it must be invoked within the context of the State design. (By the way, did you notice that there are no conditional statements in the State design?)
Related posts:

Bill Sanders
Hi Bill,
I’ve tried your Mover class with the first version of AidGame. I may have done something wrong but it doesn’t work as I expected.
Shouldn’t we remove the event listeners in the Mover functions before adding new ones? I’ve seen that the timeOn timer can have multiple and contradictory event listeners.
Hope I am not saying silly things again.
Thanks for sharing.
Curro
Hi Curro,
The Mover class is just something to demonstrate continuous movement. You can set it up any way you want. In fact you can remove it altogether and create another movement solution either as a separate class or composition. Removing the Timer event listeners after each change of state sounds reasonable for using the Mover class provided. Because a lot of you like to change algorithms, the Mover class affords you something to improve. Once you work it into the State design pattern (as revised with the set of rules provided), you should be able to understand the State pattern better.
Kindest regards,
Bill
To get started, we have to ask the same key question that we should ask for every design we do. Namely,
What varies?
In my opinion, for states, just remember about separating behavior and mode (also known as state).
The author should add that, every state has the same behavior, while there can be variety of states.
What the example shown here is mixing both behavior and mode/state.
Also , when using state pattern, every new state involves creating a new class instead of placing all together in 1 class, else if you create a lengthy algorithm on how MoveLeft would do, that class would be unreadable. Else don’t implement state pattern at all because state pattern would be overkill for such a simple example above.
The term open for extension, close for modification is good idea, this can be done by adding new classes without modifying the previous created class.
State pattern adheres to this concept, which is adding new states (this one that varies) but behavior/methods are same for all classes. As we know, with great power comes with great responsibility, all patterns that adheres to this principle/concept/rule will generate macaroni code (as in creating many classes instead of having all in 1 class (spaghetti)) thus increasing the complexity of the whole project), unless those classes are very well managed in folders where it helps navigating through which classes is related.
Hi Ayu,
Sort of all over the map with those comments. Take a look at the follow-up post to this which might help clarify a lot.
Kindest regards,
Bill