Pushdown Automata for handling entity states: is the lazy way the right way?

Started by
2 comments, last by LorenzoGatti 8 years, 12 months ago
I found my way to this forum through reddit, and I posted this question there with no results. Though FSM is often employed in AI, I think this is a more general question. If I'm wrong let me know and I'll post to the other forum. This looks like a very active community and I look forward to sharing and getting insights!

I'm quite early in development for a concept I've been working on, and I've decided to use Pushdown Automata (PDA) to handle input and states for both the player character and for more complex NPCs with AI.

My question does require some set up, so sorry for the wall of text:

I don't, at this moment, have a pushdown automata implementation for my platform of choice, which is JavaScript. What I do have is the wonderful machina.js[1] . I'm currently trying to decide how best to extend Machina to serve my needs, and I'm a little stuck on the solution.

The simplest implementation I could think of would be to simply extend the base machina.FSM to include a stack, which for JS can simply be an array, and include pushing and popping functions that operate at the FSM level.

This shoves a lot of the implementation details over to the state transition definitions and functions, since the internals of machina.js don't recognize the stack and don't use it to determine state transitions. I'm okay with this because that means I won't be breaking the middleware trying to get it to work, but I still want to "wrap" my implementation up nicely so all the PDA-related stuff isn't handled in places it shouldn't be.

Thing is, I don't need the stack to operate independently of state. I'd like for my stack to simply be the list of states that we came from (I assume this is common for games).

My intended "model" for this is to have states either explicitly transition into a new one through a push, or to have a state "expire" or otherwise become invalid, at which point the PDA starts popping states off the stack until it finds something that the old state is allowed to transition into.


Eg. My player character is idle. I then start shooting, which will cause "idle" to get pushed onto the stack and the current state to be "shooting". While I am shooting, however, I get hit by an attack that causes "stunned". "Shooting" gets pushed onto the stack, and "stunned" is the current state. "Stunned" is on a timer, of course, and once it is over, the automaton looks at the top symbol of the stack. There is no transition from "stunned" to "shooting", so "shooting" gets discarded and the next pop gives us "idle", which is a valid state to transition into from "stunned", so the game takes me from "stunned" directly to "idle" and we can continue on.


I'm concerned about the integrity of my solution. Instead of transitions causing stack manipulation as a matter of bookkeeping, I want to use stack manipulation as the determining factor for transitions. This way, I don't need the transitions to become aware of what's going on with the stack. It becomes the responsibility of the logic around the stack to find the right transitions, and the transitions don't need to know about it at all. I still need to define all the valid transitions and whatnot, but my feeling is that this "winding/unwinding" model will let me be more expressive and spend less time defining new states or state transitions when they come up.

Everything about pushdown automata that I've seen does it the other way, with transitions being stack-aware and explicitly pushing and popping... but a lot of that content is strongly academic, and what matters isn't that it's theoretically correct, but that it works.

I realize that this restriction may make my implementation strictly less powerful than a real PDA. Is there a very good reason why I shouldn't do it this way? I'd hate to do a bunch of work and then find out that I screwed up and there is no way to describe an operation I want to do in terms of the psuedo-Pushdown Automaton that I've created.

Alternatively, if you know of a robust PDA implementation in JS that is FOSS or at least useable for a project that may or may not be commercial, or a different forum I should post to, please let me know!

Advertisement
State machines and automata are concepts. Implement them however best makes sense for your usage.



If a flat array of data tables coupled with some index makes the most sense for your needs, do it that way.

If a bunch of individual objects that is individually visited makes the most sense for your needs, do it that way.

If a switch statement makes the most sense for your needs, do it that way.

If a jump table of function pointers makes the most sense for your needs, do it that way.

If some other implementation of the concept makes the most sense for your needs, do it that way.


Personally I prefer nested state machines rather than a PDA, since very often the memory is not usable when traveling back down the stack.

Sure, absolutely. I just wouldn't actually be implementing a PDA in the strictest terms: it would be some other data structure that resembles one. The danger there is that we know, theoretically, what a PDA is capable of. This other structure I want to use? Who knows.

After running through a few examples and getting feedback I've realized that this shortcut will probably end up burning me in the end, as most shortcuts do. I just need to implement a "real" PDA and stick to known practices.

I hadn't thought much about FSM hierarchies, but maybe that would work better for my needs. Machina has native support for that, so I'd already be good to get started so long as it works with my design. Thanks for your reply!


I'm concerned about the integrity of my solution. Instead of transitions causing stack manipulation as a matter of bookkeeping, I want to use stack manipulation as the determining factor for transitions. This way, I don't need the transitions to become aware of what's going on with the stack. It becomes the responsibility of the logic around the stack to find the right transitions, and the transitions don't need to know about it at all.

Your example doesn't suggest any bookkeeping or stack "manipulation", only the simple and general rule that states on the stack which cannot be transitioned into from the currently ending state will be popped rather than resumed. States or lists of allowed transitions have nothing to be aware of beyond "the stack would like to resume state X".

You only need a default state ("I don't know what to do, let's run AI to choose an appropriate state") to ensure integrity: the automaton never underflows the stack, never performs nonsensical actions and state transitions, and runs AI routines when needed.

Omae Wa Mou Shindeiru

This topic is closed to new replies.

Advertisement