Hierarchical finite state machine

Started by
3 comments, last by FrozenSnake 9 years, 4 months ago

Hello! I am currently working on a FSM for a project at my university. But I have problem with figuring out if I meet the criteria for this program.

I need to have a state that have sub-states (Hierarchical states). This code is about an AI I know, but the topic itself I feel is more related to general programming.

For example Combat leads to Shooting OR TakingCover. And no other state except for Combat should be able to reach these two states directly they need to go through Combat. This is how I have interpreted Hierarchical Finite State Machine anyway, so I would really like to have some feedback on this. It is not the most pretty piece of code out there so I apologize in advance. I also made a bitbucket repository if someone feel they need to dig deeper to help me, I do not wanna bloat this post with all the code. This is the code I think is the most important one and I only included the rest for people that feel they wanna study the code more.

TLDR; Is the code below hierarchical or have I misinterpreted something?

Here is my PonyStates.h file


#include "State.h"
class Pony;

class AgentIsHungry : public State
{
public:
	static	AgentIsHungry*	Instance		();
	virtual void			EnterState		(Pony* p);
	virtual void			ExitState		(Pony* p);
	virtual void			ExecuteState	(Pony* p);
private:				
	// Copy construtor and assignment.
	AgentIsHungry()							{}
							AgentIsHungry	(const AgentIsHungry&);
	AgentIsHungry&			operator=		(const AgentIsHungry&);
};

class AgentIsWorking : public State
{
public:
	static AgentIsWorking* Instance();
	virtual void   EnterState(Pony* p);
	virtual void   ExitState(Pony* p);
	virtual void   ExecuteState(Pony* p);
private:
	AgentIsWorking()       { }
	// Copy construtor and assignment.
	AgentIsWorking(const AgentIsWorking&);
	AgentIsWorking&   operator=  (const AgentIsWorking&);
};

class AgentIsTierd : public State
{
public:
	static AgentIsTierd*	Instance	();
	virtual void			EnterState	(Pony* p);
	virtual void			ExitState	(Pony* p);
	virtual void			ExecuteState(Pony* p);
	AgentIsTierd()			{ }
private:
	// Copy construtor and assignment.
							AgentIsTierd(const AgentIsTierd&);
	AgentIsTierd&			operator=	(const AgentIsTierd&);
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
class AgentIsHavingANightmare : public AgentIsTierd
{
public:
	static AgentIsHavingANightmare* Instance();
	virtual void			EnterState(Pony* p);
	virtual void			ExitState(Pony* p);
	virtual void			ExecuteState(Pony* p);
private:
	// Copy construtor and assignment.
	AgentIsHavingANightmare() {}
	AgentIsHavingANightmare(const AgentIsHavingANightmare&);
	AgentIsHavingANightmare& operator= (const AgentIsHavingANightmare&);
};

class AgentIsHavingADream : public AgentIsTierd
{
public:
	static AgentIsHavingADream* Instance();
	virtual void			EnterState(Pony* p);
	virtual void			ExitState(Pony* p);
	virtual void			ExecuteState(Pony* p);
private:
	// Copy construtor and assignment.
	AgentIsHavingADream() {}
	AgentIsHavingADream(const AgentIsHavingADream&);
	AgentIsHavingADream& operator= (const AgentIsHavingADream&);
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Here is my PonyStates.cpp file


////////////////////////////////
///////// AgentIsTierd /////////
////////////////////////////////
AgentIsTierd* AgentIsTierd::Instance()
{
	static AgentIsTierd instance;
	return &instance;
}

void AgentIsTierd::EnterState(Pony* p)
{
	if (p->WhereAmI() != Bedroom)
	{
		std::cout << GetName(p->GetID()) << " is walking to the bedroom; it is time to sleep.\n";
		p->ChangeRoom(Bedroom);
	}
}

void AgentIsTierd::ExitState(Pony* p)
{
	std::cout << GetName(p->GetID()) << " is now leaving the bedroom.\n";
}

void AgentIsTierd::ExecuteState(Pony *p)
{
	if (p->IsTierd())
	{
		std::cout << GetName(p->GetID()) << " wishes to sleep. " << GetName(p->GetID()) << " lay down in bed to sleep! Zzz...\n";
		if ((rand() % 100) % 2)
			p->ChangeState(AgentIsHavingADream::Instance());
		else
			p->ChangeState(AgentIsHavingANightmare::Instance());
	}
	else
	{
		std::cout << GetName(p->GetID()) << " wakes up refreshed! Time to go to work!\n";
		p->ChangeState(AgentIsWorking::Instance());
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

// Good dream
AgentIsHavingADream* AgentIsHavingADream::Instance()
{
	static AgentIsHavingADream instance;
	return &instance;
}
void AgentIsHavingADream::EnterState(Pony* p)
{
	// Already in bedrom.
}
void AgentIsHavingADream::ExitState(Pony* p)
{
	std::cout << "\t" << GetName(p->GetID()) << " had a nice dream and is feeling refreshed.\n";
	//p->ChangeState(AgentIsHungry::Instance());
}
void AgentIsHavingADream::ExecuteState(Pony* p)
{
	p->Sleep();
	p->ChangeState(AgentIsHungry::Instance());
}
// Nightmare
AgentIsHavingANightmare* AgentIsHavingANightmare::Instance()
{
	static AgentIsHavingANightmare instance;
	return &instance;
}
void AgentIsHavingANightmare::EnterState(Pony* p)
{
	// Already in bedrom.
}
void AgentIsHavingANightmare::ExitState(Pony* p)
{
	std::cout << "\t" << GetName(p->GetID()) << " had a nightmare and didn't sleep very well.\n";
	
}
void AgentIsHavingANightmare::ExecuteState(Pony* p)
{
	p->BadSleep();
	p->ChangeState(AgentIsHungry::Instance());
}
Advertisement
In an HFSM implementation, the "hierarchy" refers to the way the states themselves can be entered or exited; it has nothing to do with inheritance in the code itself. So it seems to me like you have an unnecessarily complicated class design for what you need to do.

My HFSM models typically look like this:

- Interface/abstract base class for "State"
- Any number of derived classes implementing specific states
- A special State derivative that points to a child StateMachine (the "hierarchical" bit)
- A StateMachine implementation which simply pumps states from one to the next

The State usually has three operations: Enter, Update, and Exit. Update returns some kind of signal when it wants to change to a new state. Enter and Exit are called when the state begins executing and when it's done, respectively.

In this way, you can implement any HFSM with a minimum of 3 or 4 classes. It's also trivial to make them data-driven so the state transitions are not hard coded.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Thanks for the reply ApochPiQ, do you know any good articles I can read to some more understanding?

The article I checked out was this. I had a hard time finding any text about this topic.


Thanks for the reply ApochPiQ, do you know any good articles I can read to some more understanding?
The article I checked out was this. I had a hard time finding any text about this topic.

You are doing this as an assignment, right? Your class has no text about it?


Thanks for the reply ApochPiQ, do you know any good articles I can read to some more understanding?
The article I checked out was this. I had a hard time finding any text about this topic.

You are doing this as an assignment, right? Your class has no text about it?

The text we have about the hierarchical is:

Implementation

  • The machine represent a tree structure
  • Transition
    • Exit is run on states up to the parent
    • Enter is run on states down from the parent.

UG7bW5H.png

After this he talks about a global state, which according to him didn't meet the criteria because all states are substates to the global one. So I decided to place Two other states inside AgentIsTierd but I do not know is I did the thing he asks for or something else entirely.

So we have 4 lines of text, but it doesn't really show anything that is helpful for me to understand how to implement this. This is why I looked elsewhere to find some more information but I am still unsure about if it meet the criteria, and I would prefer to get some more information about HFSM and how it can be implemented.

This topic is closed to new replies.

Advertisement