Queues

by Unknown

Back to Mechanic's Corner.

Unknown2007-01-10 12:32:40
Loops are okay when you optimize them to have as few iterations as possible, which is exactly what I have done. Plus, I put them inside #PRIORITY blocks everywhere in my code (which I didn't do in my illustration because people would ask what that was).

The code you've posted, Daruin, won't work, unfortunately. I count at least 5-6 errors in that snippet.
Unknown2007-01-10 16:02:38
QUOTE(Zarquan @ Jan 10 2007, 04:32 AM) 371448


The code you've posted, Daruin, won't work, unfortunately. I count at least 5-6 errors in that snippet.


As I said, I didn't have my zmud box in front of me, so I was kinda going off memory and the general idea I wanted to accomplish. As far as my loop goes, the only time it will iterate at all is if there's at least one affliction listed with %numitems and all the state tracking vars are good. Is there a more effective way to optimize that? Also, I'm not sure of the right syntax to name an alarm.

Here is what my herbcure really looks like now that I have zmud in front of me:
CODE

#if {%numitems(@herbqueue)> 0 and @herbbalance=1 and @anorexia=0 and @asleep=0 and @stunned=0 and @aeoned=0} {#loopdb {@herbqueue} {%va;#alarm +2}}
Unknown2007-01-10 18:34:08
Sorry that I missed the part of your post where you said you were pseudo-coding it off the top of you head. My mistake.

Your #alarm +2 is still incomplete syntax, however, and doesn't do anything. You might want to lookup the #alarm command in the help file. smile.gif
Unknown2007-01-10 21:18:39
The helpfile on #alarm confuses the hell out of me

Edit (2 minutes later): I think I got it:
#alarm herbalarm +2 {}
Unknown2007-01-10 21:26:30
For a recurring alarm that fires every five seconds and executes "who":
CODE
#ALARM *5 {who}

The * means "keep firing and don't stop."

Replacing the * with a + changes the meaning to "fire once and then disappear."

If you add a name to the alarm, just after the #ALARM command, you can control and inspect the alarm:
CODE
#ALARM "mytimer" *5 {who}

To check the who list every five seconds, you enable it with #T+ mytimer. To stop checking, you disable it with #T- mytimer. I find this to work much better than repeatedly making temporary timers, even for one shot things that you execute semi-infrequently.

You can check the time left on the alarm, or even modify the time left, with the %alarm function:
CODE
#SHOW %alarm(mytimer)
#NOOP %alarm(mytimer, 10)

Sometimes useful, though not usually necessary.

Another benefit of giving a timer a name is that you won't end up with dozens or hundreds of timers when a script goes awry and fires too often. Each time you create an alarm with that same name, you just overwrite the previous alarm. This is true of temporary or permanent timers. Also, simply enabling a permanent timer will reset the countdown on its timer, even if it's already enabled and counting down.

Hope that helps clarify some of the ins and outs of timers for you!
Unknown2007-01-11 00:03:38
So in my case:

CODE

#var herbcure {#if {%numitems(@herbqueue)> 0 and @herbbalance=1 and @anorexia=0 and @asleep=0 and @stunned=0 and @aeoned=0} {#loopdb {@herbqueue} {%va;#alarm herbcure +2{} }}}


...will the alarm keep the loop from trying to cure everything before I set @herbbalance to 0 with a trigger?
Unknown2007-01-11 03:20:57
No. Your alarm does absolutely nothing. You have to put code inside the {} to be executed. You also need a space between the +2 and the {}. What you really need is to only execute one thing in the herbqueue, and for that the best thing is not a data record variable.

You'd be better off just having a string list that you could just grab the first item, pop it off, and execute that. I'm not advocating the string list as a good idea, however. A data record variable like the one in my post would be better, but you'd have to use it the way I do.
Unknown2007-01-11 04:06:57
Bah, back to the drawing board, then.
Kharaen2007-01-11 18:28:06
QUOTE(Zarquan @ Jan 9 2007, 07:26 AM) 370930

My system is designed to make it fairly simple to add new afflictions, mostly because that makes it easy for me to maintain. First, I have a data record variable for each balance type that holds the relevant cure information:
CODE
#VAR herb_cures {}
#ADDKEY herb_cures blindness myrtle
#ADDKEY herb_cures lost_ear marjoram
#ADDKEY herb_cures stupidity pennyroyal

... and so on.

Then, I have a priority string list for each balance type, and each affliction's position on the string list is automatically it's priority value. If it's not on one of these lists, the system completely doesn't try to cure it, which means it's dynamically adjustable through aliases, triggers, etc.
CODE
#VAR herb_affs {stupidity|blindness|lost_ear}


Now, you need triggers to track afflictions being added or removed. First, let me explain the aliases to add/remove afflictions. My i_aff alias not only adds a single affliction to the data record variable, it can add a list of afflictions in a single call. I have the option of passing in a last argument as the value for the affliction, where the default is 1 (meaning True), but I could use 2 to represent the number of broken arms, for example. Also, it checks the priority lists to see which queue should be checked on the prompt for the curing of the affliction(s). Finally, it tracks the afflictions in a special "keys" string list in the data record variable itself, making it easier for me to display my current afflictions without having to iterate through the list of keys in a loop. (For purposes of this post, I'll simplify it to just tracking a single affliction and leave the rest up to those that want to figure it out.)
CODE
#ALIAS i_aff {#ADDKEY afflictions %1 1;#IF (%ismember(%1, @herb_affs)) {#ADDKEY scan herb 1}}
#ALIAS i_cure {#DELKEY afflictions %1}


The triggers are fairly simple.
CODE
#TRIGGER {^Someone gave you stupidity!$} {i_aff stupidity}
#TRIGGER {^You cured stupidity!$} {i_cure stupidity}


The queue is executed on the prompt, which is also fairly simple.
CODE
#TRIGGER {^%dh, %dm} {#IF (%iskey(@scan, herb)) {i_scan_herb}} "" {nocr|prompt}


The tricky part is in the i_scan_herb alias, where it determines the highest priority affliction and eats the appropriate cure. (This will be simplified a little for the purposes of illustration, of course.)
CODE
#ALIAS i_herb {outr %1;eat %1}

#ALIAS i_herbcure {
  herb_priority = 100
  #LOOPDB @afflictions {
    herb_pos = %ismember(%key, @herb_affs)
    #IF (@herb_pos > 0 and @herb_pos < @herb_priority) {
      herb_priority = @herb_pos
    }
  }
  #IF (@herb_priority < 100) {
    i_herb %db(@herb_cures, %item(@herb_affs, @herb_priority))
  }
}
#ALIAS i_scan_herb {
  #IF (@able_scan) {
    #DELKEY scan herb
    #IF (!%iskey(@lost_bals, herb)) {i_herbcure}
  }
}


The i_herbcure is the real trick, and I'm quite proud of that particular alias. Basically, it loops over your current list of afflictions, which is hopefully a shorter list than the possible list of all herb-cured afflictions (meaning it's a tighter loop that executes faster). It compares the priority of each affliction found in the herb priority list and only remembers the affliction with the highest priority. At the end of the loop, either you have no herb-cured afflictions to report or you have the name of the highest priority one that can now be cured.

(Note: I cut out a LOT of the code I use in these aliases. This isn't a complete herb curing solution that you can just copy, paste, and use. You have to fill in the blanks when building your own stuff. There aren't any balance triggers, timer failsafes, or other miscellaneous goodies in this example.)


Have you always been using this code? I have one of your old systems, and I may want to go through it to compare.
Unknown2007-01-12 15:36:47
No, I haven't always been using it.