Finite State Machine

By on 11/11/2008

Thanks to the wonderful waybackmachine, I was able to dig up an old article of mine from the old blog software that I hadn't seen for a long time.  Figured I'd post it up for posterity ... plus, you never know if a service like archive.org is going to be around forever:

http://web.archive.org/web/20041204125618/http://www.codecube.net/item.asp?cc_ItemID=247

One of these days, I'll get around to updating this sample to XNA as I can think of a number of ways that I could factor the code a bit better:

----------------------

After reading a great article over at ai-junkie.� The article describes the tenets of "state driven agent design" much better than I ever could, but just for reference, here's a quick rundown.
It is often regarded by many in the ai field that one of the most basic forms of artificial intelligence is using a state based architecture. In the end, it breaks down to being a bunch of if/then/else branches. They can easily be represented as flowcharts and are very easy to understand by most which makes it easy for non-technical people to help with the design of the ai.
I decided I'd like to make an implementation of the sample code he provides in C# (the article presents the code in c++). The sample code is a little miner dude ... he gets up in the morning, heads down to the mine to gather gold nuggets. When he gets thirsty, he heads out to the bar and buys a beer. When his pockets are full, he heads to the bank to deposit his gold. When he gets tired, he goes home and sleeps. And eventually, once he deems he has enough gold, he gits outta dodge!
On to the code ...

Download Sample Project Files

IState interface

The IState interface is how you implement the actual "intelligence". The idea is that an entity can only be in one state at any given time, so you can use this interface to dictate what happens when that state is entered, when to leave that state, and where to go.

public interface IState
{
	void Enter(IEntity entity);
	void Execute(IEntity entity);
	void Exit(IEntity entity);
}

Entities

Every entity that uses the state machine must implement the IEntity interface. This simply dictates that the entity must keep track of the current state, the previous state, and provide a mechanism for changing state. For the sake of reusing common code, I also implemented an EntityBase class which is a simple IEntity implementation. You can create your own, or subclass it if you need additional functionality.

public interface IEntity
{
	IState CurrentState {get;set;}
	IState PreviousState {get;set;}
	void ChangeState(IState newState);
}   public class EntityBase : IEntity
{
	private IState mCurrentState;
	private IState mPreviousState;   #region IEntity Members   public IState CurrentState
	{
		get
		{return mCurrentState;}
		set
		{mCurrentState = value;}
	}   public IState PreviousState
	{
		get
		{return mPreviousState;}
		set
		{mPreviousState = value;}
	}   public void ChangeState(IState newState)
	{
		if (CurrentState != null)
			CurrentState.Exit(this);   PreviousState = CurrentState;
		CurrentState = newState;   newState.Enter(this);
	}   #endregion
}

Now, if you go back and read the original article, you'll notice that his implementation didn't store the previous state. I added that property because I think it might make the state machine a bit more versatile if your code is able to know what state it was previously in, thus having the ability to change its behavior. Others say that's bad practice because they think that each state should have predictable behavior, and if you want different behavior, that should be another state. I'm still undecided about whether I agree with that ...

Miner

The miner class is going to be the concrete implementation of the IEntity interface. Notice how we define additional properties such as: Fatigue, Thirs, and GoldNuggets. As the state machine puts it through its paces, those properties will change and dictate the when states transition to another state.

public class Miner : EntityBase
{
	public Miner()
	{}   public int Fatigue = 0;
	public int Thirst = 0;
	public int GoldNuggets = 0;
	public int Savings;
}

Concrete State Implementations

For the sake of saving space, I will only print one of the State classes, the first one. download the sample project to see the rest.

	class EnterMineAndDigForNugget : IState
	{
		private EnterMineAndDigForNugget()
		{}

private static EnterMineAndDigForNugget _instance; public static EnterMineAndDigForNugget Instance { get { if (_instance == null) { _instance = new EnterMineAndDigForNugget(); } return _instance; } }

#region IState Members

public void Enter(IEntity entity) { Console.WriteLine("Entering the mine"); }

public void Execute(IEntity entity) { Miner m = entity as Miner; Console.WriteLine("Picked Up a Nugget"); m.GoldNuggets++; m.Thirst++; m.Fatigue++;

if (m.GoldNuggets >= 8) { m.ChangeState(VisitBankAndDepositGold.Instance); return; }

if (m.Fatigue >= 10) { m.ChangeState(GoHomeAndSleepTilRested.Instance); return; } if (m.Thirst >= 6) m.ChangeState(QuenchThirst.Instance);

}

public void Exit(IEntity entity) { Console.WriteLine("Leaving the mine"); }

#endregion }

What will happen is that the app will create a loop and continue to execute the entity's current state (whatever that state may be). It is up to the state itself to decide when it is time to move to another state. That's what makes this such a powerful AI technique because you can easily change the state transition patterns and modify the entity's behavior.

You will also want to notice that the state class is implemented as a singleton. I did this because every time you want to switch states, you need an instance of the state class. The original implementation had me instantiating a new class every time ... I can imagine that this would be a performance hit in an actual game ... so by using the Singleton pattern, I ensure that there's only one state class at any given time.

TypeSafe Enum Pattern

I found this pattern about a year ago, and I love it. Here we see it implemented and described by Andy Smith. In retrospect, I could have used this pattern to have a States class with instances of all the individual states. I think this would have looked a little cleaner ... but oh well.

Output

This is the generated output from the sample project:

C:\Documents and Settings\jdmartinez\My Documents\testProjects\MinerGame\bin\Deb
ug>MinerGame.exe
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Buyin' a beer (-1 gold)! Gulp Gulp Gulp Gulp Gulp Gulp  - Ahhh
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Depositing 8 Gold ... I now have 8 gold saved up
Home Sweet Home
ZZZZZZZZ'nother day, 'nother doller
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Buyin' a beer (-1 gold)! Gulp Gulp Gulp Gulp Gulp Gulp  - Ahhh
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Depositing 8 Gold ... I now have 16 gold saved up
Home Sweet Home
ZZZZZZZZ'nother day, 'nother doller
Entering the mine
Picked Up a Nugget
Leaving the mine
Buyin' a beer (-1 gold)! Gulp Gulp Gulp Gulp Gulp Gulp Gulp  - Ahhh
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Buyin' a beer (-1 gold)! Gulp Gulp Gulp Gulp Gulp Gulp  - Ahhh
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Depositing 8 Gold ... I now have 24 gold saved up
Home Sweet Home
ZZZZZZZZ'nother day, 'nother doller
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Buyin' a beer (-1 gold)! Gulp Gulp Gulp Gulp Gulp Gulp  - Ahhh
Entering the mine
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Picked Up a Nugget
Leaving the mine
Depositing 8 Gold ... I now have 32 gold saved up
YEEEEEHAAWWWWWWW ... headin' back east with 32 gold nuggets

See more in the archives