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:
- Removal (or disabling - I'm not sure which is better) of old triggers, timers, aliases
- Definition (or enabling) of new triggers, timers and aliases
- 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
| |
| 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
top