About Finitie State Machine class implementation method

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Warren
Posts: 60
Joined: Fri Jul 07, 2006 11:41 pm
Location: Santiago De Compostela, Spain

About Finitie State Machine class implementation method

Post by Warren »

Hi,
I need to implement a Finite State Machine for a graphic aplication, so looking for info on documents and so on... i found a few ways to implement it. The easiest, a switch sentence to select a case for each state
, but it is a dirty solution and very hard to mantain. Reading some documents i found with the book Programming Game by a Example / Mat Buckland. Here the author describes a tecnique called Embedded Rules, wich define a class State who contains the logic about transition states. I paste the book´s section to understand this interesting method


Embedded Rules

An alternative approach is to embed the rules for the state transitions
within the states themselves. Applying this concept to Robo-Kitty, the state
transition chip can be dispensed with and the rules moved directly into the
cartridges. For instance, the cartridge for “play with string” can monitor
the kitty’s level of hunger and instruct it to switch cartridges for the “eat
fish” cartridge when it senses hunger rising. In turn the “eat fish” cartridge
can monitor the kitten’s bowel and instruct it to switch to the “poo on carpet”
cartridge when it senses poo levels are running dangerously high.
Although each cartridge may be aware of the existence of any of the
other cartridges, each is a self-contained unit and not reliant on any external
logic to decide whether or not it should allow itself to be swapped for
an alternative. As a consequence, it’s a straightforward matter to add states
or even to swap the whole set of cartridges for a completely new set
(maybe ones that make little Kitty behave like a raptor). There’s no need to
take a screwdriver to the kitten’s head, only to a few of the cartridges
themselves.
Let’s take a look at how this approach is implemented within the context
of a video game. Just like Kitty’s cartridges, states are encapsulated as
objects and contain the logic required to facilitate state transitions. In addition,
all state objects share a common interface: a pure virtual class named
State. Here’s a version that provides a simple interface:

Code: Select all

class State
{
   public:

   virtual void Execute (Troll* troll) = 0;
};
Now imagine a Troll class that has member variables for attributes such as
health, anger, stamina, etc., and an interface allowing a client to query and
adjust those values. A Troll can be given the functionality of a finite state
machine by adding a pointer to an instance of a derived object of the State
class, and a method permitting a client to change the instance the pointer is
pointing to.

Code: Select all

class Troll
{
      /* ATTRIBUTES OMITTED */
      State* m_pCurrentState;

      public:
      /* INTERFACE TO ATTRIBUTES OMITTED */
     void Update()
     {
          m_pCurrentState->Execute(this);
      }
     
     void ChangeState(const State* pNewState)
     {
       delete m_pCurrentState;
       m_pCurrentState = pNewState;
     }
};
When the Update method of a Troll is called, it in turn calls the Execute
method of the current state type with the this pointer. The current state
may then use the Troll interface to query its owner, to adjust its owner’s
attributes, or to effect a state transition. In other words, how a Troll
behaves when updated can be made completely dependent on the logic in
its current state. This is best illustrated with an example, so let’s create a
couple of states to enable a troll to run away from enemies when it feels
threatened and to sleep when it feels safe.

Code: Select all

//----------------------------------State_RunAway
class State_RunAway : public State
{
     public:
     void Execute(Troll* troll)
     {
         if (troll->isSafe())
         {
               troll->ChangeState(new State_Sleep());
         }
         else
        {
               troll->MoveAwayFromEnemy();
        }
     }
};



//----------------------------------State_Sleep
class State_Sleep : public State
{
   public:
   void Execute(Troll* troll)
   {
       if (troll->isThreatened())
      {
          troll->ChangeState(new State_RunAway())
      }
      else
      {
        troll->Snore();
      }
   }
};

As you can see, when updated, a troll will behave differently depending on
which of the states m_pCurrentState points to. Both states are encapsulated
as objects and both provide the rules effecting state transition. All very neat
and tidy.
This architecture is known as the state design pattern and provides an
elegant way of implementing state-driven behavior. Although this is a
departure from the mathematical formalization of an FSM, it is intuitive,
simple to code, and easily extensible. It also makes it extremely easy to add
enter and exit actions to each state; all you have to do is create Enter and
Exit methods and adjust the agent’s ChangeState method accordingly.
You’ll see the code that does exactly this very shortly.


ok, the method is undrstood perfectly, but at the moment to get it to implement, i have a dependence fail. Becose, if i declare the class State and then declare the class Troll, it do not reconize the Troll class at virtual void Execute (Troll* troll) = 0. And if i declare the class Troll and then the
class State, it do not reconize the State* m_pCurrentState member.

How do i solve this dependence problem without modify the own nature of the solution???

Thx in advance
and sorry for a too long post
------------------------------------------------
Irrlicht Ussers Map
Join now!!
http://www.frappr.com/irrlichtusers
cuatro
Posts: 11
Joined: Tue May 08, 2007 2:29 am

Post by cuatro »

you can do a forward declaration of class Troll in your State.h file and that error should go away (just put "class Troll;" without quotes somewhere) . Although if you have that book then its actually ALOT easier if you use templates in the State class that he describes later on in the chapter.
Warren
Posts: 60
Joined: Fri Jul 07, 2006 11:41 pm
Location: Santiago De Compostela, Spain

Post by Warren »

oh, thx a lot.
it was easier than i thoght!!
and i´ll take in mind the templates option ;) tnx for the advice

but now i have a another problem, at Change_State(const State* pNewState) member function, first of all i dont know why the State pointer parameter is const and it makes an invalid comversion of types, so i erase it.
But then the sentence m_pCurrentState = pNewState; , retursme a linker error:

[Linker Error] undefined reference to `vtable for State'

i dont know what it means, please could you help me again?

Thx in advance
------------------------------------------------
Irrlicht Ussers Map
Join now!!
http://www.frappr.com/irrlichtusers
Dorth
Posts: 931
Joined: Sat May 26, 2007 11:03 pm

Post by Dorth »

also, if either one of you could put the template method used by the author here, I'd be ever so thankful ^^
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

The const is ok, because this method will probably copy the nextstate on the current state. Hence, the method itself is non-const.
The vtable linker error means that you did not overwrite all pure virtual methods from the interface. Probably because you removed the const.
Warren
Posts: 60
Joined: Fri Jul 07, 2006 11:41 pm
Location: Santiago De Compostela, Spain

Post by Warren »

hybrid wrote:The const is ok, because this method will probably copy the nextstate on the current state. Hence, the method itself is non-const.
The vtable linker error means that you did not overwrite all pure virtual methods from the interface. Probably because you removed the const.
well, i keep the const sentence but i need to make a trunk to avoid the incompatibility, like this way:

Code: Select all

m_pCurrentState =  (State*) pNewState;
then, i dont understand the causes of the linker error 'cos i overwrite the only one method on State class ( Execute() ). anyway, this error only appears when i make the dynamic assignment, like that:

Code: Select all

State* st;
st= new State(); // or st= new State_RunAway()
Thx again!!
------------------------------------------------
Irrlicht Ussers Map
Join now!!
http://www.frappr.com/irrlichtusers
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

You cannot assign a non-const pointer/reference from a const pointer/reference. To do so would cause the const to be removed, which would make it possible for the const object to be modified through a non-const reference.

Code: Select all

struct Foo
{
  Foo(int fv= 0) : f(fv) { }
  int f;
};

// this is a pointer to a constant Foo
const Foo* const_f = new Foo;

// it would be illegal to attempt to modify const_f->f because the
// object pointed to is constant. the compiler will disallow mutating
// operations on const_f. i.e. this won't compile...
const_f->f = 10;

// the compiler disallows this. assignment of a non-const pointer or
// reference from a const pointer or reference... this is exactly what
// your code is attempting to do. this line won't compile either...
Foo* non_const_f = const_f;

// if the above assignment did work, then you would be able
// to modify const_f->f through non_const_f->f, which would totally
// subvert the system.
non_const_f->f = 10;
You can explicitly bypass the type system by doing a cast or a const_cast, but it is usually a sign that something hasn't been thought through all the way. In this case it just looks like the author made a typographical error. I think that the pointer parameter should be made non-const.

BTW, There was a small discussion on state machines in this thread a while back. I posted links to a state machine implementation that I wrote some time last year. I've since killed the links, but I can update them when I get home tonight if you're interested in seeing the source.

Travis
Last edited by vitek on Wed May 30, 2007 10:24 pm, edited 1 time in total.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

But you can use a const State* member, because this means that you cannot change the State object, but you can change the pointer (otherwise it would be a const State* const member).
Warren
Posts: 60
Joined: Fri Jul 07, 2006 11:41 pm
Location: Santiago De Compostela, Spain

Post by Warren »

Thx Vitek ( actually both xDD) ,
any idea about the linker error??? :x
------------------------------------------------
Irrlicht Ussers Map
Join now!!
http://www.frappr.com/irrlichtusers
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

otherwise it would be a const State* const member
Yeah, I didn't want to get into that bag of worms. Not many applications use constant pointers, and it confuses most people. :)
any idea about the linker error???
I'd start by defining an empty virtual destructor in your state class. You should be defining one anyways. If you still get the linker error, let me know.

Travis
Warren
Posts: 60
Joined: Fri Jul 07, 2006 11:41 pm
Location: Santiago De Compostela, Spain

Post by Warren »

Well, actually my State class is like follows:

Code: Select all

class State
  {
   public: 
   
      virtual void Evalue(Glad* glad);
      virtual ~State(){};
};
It´s the same but the entity is the Glad class, and now i´d add the empty virtual destructor and now the linker returns 2 same errors, but i dont understand:

...../include/IEventReceiver.h: undefined reference to `vtable for State'
...../include/IEventReceiver.h: undefined reference to `vtable for State'

i think that it dont have sense ´cos before it remarks other libs like the source of the errors

:?: :?: :?:
------------------------------------------------
Irrlicht Ussers Map
Join now!!
http://www.frappr.com/irrlichtusers
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

First the method is named Evalue not Evaluate and second is the signature different even if it would have the same name. You have to define exactly the same method, including parameter types and constness. So with your new type the method is a completely different one. Your class still lacks the troll method. I guess the original author intended something different when going for different classes...
Warren
Posts: 60
Joined: Fri Jul 07, 2006 11:41 pm
Location: Santiago De Compostela, Spain

Post by Warren »

i defined the same method, call it Evalue or Eval or whatever...

Code: Select all

--------------------------------Main State Class
class State
{
   public:
     virtual void Evalue(Glad* glad)
     virtual ~State(){};
}


-------------------------------------State_Stand
class State_Stand : public State
{
  public:
    void Evalue(Glad* glad)
    {
       //whatever to do
     }

};

As you can see, i defined the exactly the same method at State class as derived State_Stand class.

Thx
------------------------------------------------
Irrlicht Ussers Map
Join now!!
http://www.frappr.com/irrlichtusers
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I think the best thing you can do to get this solved is to post a link to a simplified version of your code that compiles and gives the linker error that you are complaining about.

Travis
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Oh, it was Execute then, not Evaluate. Well, how many State classes do you have - I mean base classes?!
Post Reply