Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Lua ➜ Event driven state machine

Event driven state machine

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by FxF   (5 posts)  Bio
Date Mon 25 May 2009 09:55 AM (UTC)
Message
Hello,
I have found out that very often I want particular trigger to work only in certain context (depending on previous lines). As a solution to this I track the state by a number of flags (as in_combat=1, bashed=0, skill_lag=1 ... etc.) but the whole script soon starts to be quite complex and hard to understand.

Maybe better approach would be finite state machine paradigm where the script would track current "state". Each state would have it's own triggers, aliases, timers and actions. Depending on mud input (trigger), user output (alias) or time elapsed (timer), the state might change to another state with different sets of triggers, aliases etc.

The state change should include:

  1. Removal (or disabling - I'm not sure which is better) of old triggers, timers, aliases
  2. Definition (or enabling) of new triggers, timers and aliases
  3. Execution of predefined actions


This seems to be extremely powerful approach which would enable to easily create context sensitive triggers, e.g. empty line can be triggered only in combat to separate the combat rounds, mobs in room can be parsed only after the description of room arrived or after recent look or movement input from player.

I'm not experienced with Lua programming and any suggestion how to implement such a state machine in Lua would help. I guess it would involve using the tables but I can't figure out how to do it elegantly.
Top

Posted by Nick Gammon   Australia  (23,173 posts)  Bio   Forum Administrator
Date Reply #1 on Mon 25 May 2009 08:52 PM (UTC)
Message
One approach that might help is to use the EnableGroup function to enable and disable whole groups of triggers, aliases and timers.

http://www.gammon.com.au/scripts/doc.php?function=EnableGroup

Effectively each group could be your state in your state machine.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by FxF   (5 posts)  Bio
Date Reply #2 on Tue 26 May 2009 03:56 PM (UTC)
Message
I have created first draft of the state machine. For the comparison, the example from the beginning of http://www.gammon.com.au/forum/?id=4957 is used as the test case, perhaps it might be useful for scripting languages without coroutines.

The code is still not complete because it lacks an implementation of functions create_all, delete_all and enable_all, disable_all. It also doesn't solve the problem of passing of the parameters among different states - for example the target of heal from the first alias should be know to the trigger which casts heal.

I could again keep all created triggers, aliases as well as all potential arguments in tables, but I feel that maybe Lua can offer some kind of more object oriented approach which would be easier to work with. Also note that the first part of code contains simple description of particular state machine and everything in the lower part is general to all prospective state machines (there can be more than one machine running). I would like to have this part in separate file which would be required by all state machines, but I'm not sure how to pass "states" variable.

Please let me know if you have any idea how to create some 'object' which would track triggers, aliases and their actual arguments. Is passing the arguments by global table really the best solution?


state_basic = 
{
	aliases = {{"^heal ", jump(state_prepare_heal)}}
}

state_prepare_heal = 
{
	actions = {send("prepare heal")}
	triggers = 
	{
		{"You are ready", jump(state_cast_heal)},
		{"You lose your concentrations", jump(state_prepare_heal)}
	}
}

state_cast_heal =
{
	actions = {send("cast heal")}
	triggers = 
	{
		{"You heal", jump(state_done)},
		{"The spell fizzles out", jump(state_cast_heal)},
	}

state_done = 
{
	actions = {Note("heal done!"), jump(state_basic)}
}


states = {state_basic, state_prepare_heal, state_cast_heal, state_done}

-----------------------------------------------------------------------------
----------------------- state machine description is above ------------------
----------------------- what follows is general for all possible plugins ----
-----------------------------------------------------------------------------

function jump(new_state)
	disable_all(current_state) -- disable old aliases, triggers and timers
	current_state=new_state
	enable_all(current_state)  -- enable new aliases, triggers and timers
	if current_state.actions then
		for k, v in ipairs(current_state.actions) do
			v
		end
	end
end

-- some initialization stuff

current_state=nil

function OnPluginEnable()
	current_state=state_basic
	for k,v in pairs(states) do
		create_all(v) -- this should create all aliases, triggers and timers and keep them in some sort of table
	end
end

function OnPluginDisable()
	for k, v in pairs(states) do
		delete_all(v) -- this will delete all aliases, triggers and timers
	end
end
Top

Posted by Nick Gammon   Australia  (23,173 posts)  Bio   Forum Administrator
Date Reply #3 on Wed 27 May 2009 09:02 PM (UTC)
Message
Your idea looks ingenious, but I can't help thinking it will bog down in implementation detail. For one thing, state machines often consider more than one condition. As an example, you might transition from fighting to healing oneself if a number of conditions are true:


  • You are fighting
  • You need a heal
  • You have sufficient mana to heal yourself
  • Someone else in your party doesn't need healing more urgently
  • Healing is possible right now (eg. heal spell not on cooldown, you are not stunned, etc.)


Trying to put all that into a state table might be tricky.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by FxF   (5 posts)  Bio
Date Reply #4 on Sat 30 May 2009 10:00 PM (UTC)
Message
I think that things might get complicated simply because they are hard to automate. Healing in combat can be quite complex thing, replacing human decision by a script might not be the best idea.

But in principle it is possible to create script that would decide whether should I heal myself, heal tank, cast lightning bolt or flee. And such script can contain lots of things including random number generator, mood tracker or check whether the tank is my spouse :). And I also believe that the state machine approach would work and the resulting script, while still complex, could be less complex than anything else.

Important feature of my suggested approach is that in the state machine table, the functions called by triggers and aliases don't have to be only 'jump' functions but any functions are allowed. This includes functions that would execute conditional jumps based on other conditions. Yes, it means comeback of various flags and variables tracking all aspects of world (like current spellpoints, wait states, hitpoints, etc.), but I hope that result will be more organized and easier to code and improve.

I'm learning Lua features that would allow using more object oriented approach and I will try to post improved version soon.
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


21,128 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.