Register to post in forums, or Log in to your existing account
 

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » CMUD General Discussion
geniusclown
Magician


Joined: 23 Apr 2003
Posts: 358
Location: USA

PostPosted: Thu Sep 15, 2011 2:01 pm   

Cutting LoopLines short
 
Some shops in Dragonreams work something like this:
Quote:
> shop
The following items contain goods for sale:

a round metal tin
a simple cedar box
a wire-wound teak coffer

[Type SHOP [ITEM] to see what's on it.]
> shop tin
In the metal tin, you see:

a box of chocolate cherries for 62 copper Kronars
a box of jellied fruit for 37 copper Kronars
a box of chocolate caramels for 37 copper Kronars
a bag of caramel bullseyes for 25 copper Kronars
a bag of rock candy for 18 copper Kronars

[Type SHOP [GOOD] to see some details about it.]
> shop box
In the cedar box, you see:

some cubes of dried papaya for 18 copper Kronars
some sliced dried bananas for 31 copper Kronars
a jar of dried blueberries for 25 copper Kronars
a jar of dried raspberries for 31 copper Kronars
a jar of dried cranberries for 25 copper Kronars

[Type SHOP [GOOD] to see some details about it.]


What I'm trying to do is automatically shop each container listed. Here's the code I'm using:
Code:
  <var name="bundle" id="285">1</var>
  <trigger name="Shopping" priority="6780" regex="true" enabled="false" id="678">
    <pattern>^The following items contain goods for sale:$\n</pattern>
    <value>ShoppingContainers=""</value>
    <trigger type="Loop Lines" param="10">
      <pattern>^({ *|})$</pattern>
      <value>#IF (%1) {
  #ADDITEM ShoppingContainers @lastword(%1)
  } {
  #FORALL @ShoppingContainers {qn shop %i}
  #STATE 0
  }</value>
    </trigger>
  </trigger>

This properly triggers the first pattern along with the blank like immediately after it, then the extra state captures the last word of each line and loads it into a list, but if the line is blank (hence the list of things to shop is done), then it queues up the list to shop each one, correctly. The problem is that it queues the shop commands twice - the first time it gets the blank line, it should reset the trigger to the zero state (waiting for "The following items contain goods for sale") and no longer trigger off blank lines - but it does! Even weirder, if I put #ECHO anything on the first line of the state 1 trigger, it works properly, albeit with extra scroll.

Any suggestions?
_________________
.geniusclown
Reply with quote
MattLofton
GURU


Joined: 23 Dec 2000
Posts: 4834
Location: USA

PostPosted: Sun Sep 18, 2011 9:32 pm   
 
State 0 (pattern): in the whatever you see:
State 1 (pattern): blank
State 2 (within lines, param=1): ({An|Some|A} *) for (%d) (%w) ({Kronar|Dokora|Lirum})

Since State 2 will now only match on lines that are actually part of the list, there's no need to test for anything. The qn stuff can be handled in the below trigger on the [Type SHOP line.

There's a limitation with the withinlines type wherein if it expires without firing it always resets the trigger back to state 0 even when there are further states defined, but in your case you can just make a second trigger on the [Type SHOP line that gags this and the preceding blank if you desired.
_________________
EDIT: I didn't like my old signature
Reply with quote
geniusclown
Magician


Joined: 23 Apr 2003
Posts: 358
Location: USA

PostPosted: Mon Sep 19, 2011 2:36 am   
 
I think you missed the point. I'm not trying to modify the output in any way, and the first blank line is matching perfectly via regex \n.

When I type just "shop", it shows a list of containers - no telling how many (likely no more than 10 - I've never seen more than six in one shop)! I then want the script to automatically look in/on each container by sending "shop (container)". My problem is matching an unknown number of lines with the same {^(*)$} pattern, and stop matching when it receives a blank line. In my code above, I attempt to use the LoopLines parameter to match whether there's something there or not, then test the match to know whether to stop the looping and reset the multistate trigger to 0. It doesn't quite work, and I'm not sure why.
_________________
.geniusclown
Reply with quote
Daern
Sorcerer


Joined: 15 Apr 2011
Posts: 809

PostPosted: Mon Sep 19, 2011 5:05 am   
 
Try changing your looplines trigger to a manual trigger. Manual triggers never change the state on their own, it must be done manually in the code, thus the name. You shouldn't even need to change your code at all, and it would be more reliable - it would match ANY number of lines between the two blanks, no matter how rare the chance of a shop having more than 10 containers is.
Reply with quote
geniusclown
Magician


Joined: 23 Apr 2003
Posts: 358
Location: USA

PostPosted: Tue Sep 20, 2011 12:33 pm   
 
What? Spell "bolor" with a "k"? "Kolor"! Oh, there we go! What a silly bunt!

Thanks, this solution was too obvious.
_________________
.geniusclown
Reply with quote
Arde
Enchanter


Joined: 09 Sep 2007
Posts: 605

PostPosted: Wed Sep 21, 2011 9:14 pm   
 
Why you can't have just 2 triggers - one simple text parsing trigger for containers and one for a blank line that will disable the first one and do things with data collected? After that second trigger can disable itself. There is no need for "loop lines" or other hard-to-debug options.
Make OnInput trigger for the "shop" command which will turn on both mentioned above triggers.
_________________
My personal bug|wish list:
-Wrong Priority when copy-paste setting
-1 prompt trigger for Mapper, Session and General Options, not 3 different!
-#SECTION can terminate threads
-Buttons can't start threads
Reply with quote
geniusclown
Magician


Joined: 23 Apr 2003
Posts: 358
Location: USA

PostPosted: Wed Sep 21, 2011 11:34 pm   
 
Using the Manual parameter was the perfect solution, a single trigger to handle all of my shopping needs. I had never attempted to use that parameter except as the primary trigger, I'm glad to discover how to use it in later states.
_________________
.geniusclown
Reply with quote
Daern
Sorcerer


Joined: 15 Apr 2011
Posts: 809

PostPosted: Wed Sep 21, 2011 11:37 pm   
 
Yeah, manual triggers are great for capturing everything between two lines, I use it for things like room descs.
Reply with quote
Arde
Enchanter


Joined: 09 Sep 2007
Posts: 605

PostPosted: Thu Sep 22, 2011 1:23 pm   
 
Very nice solution, Daern. I wonder how you handle a situation when there is no blank line after a list?
_________________
My personal bug|wish list:
-Wrong Priority when copy-paste setting
-1 prompt trigger for Mapper, Session and General Options, not 3 different!
-#SECTION can terminate threads
-Buttons can't start threads
Reply with quote
geniusclown
Magician


Joined: 23 Apr 2003
Posts: 358
Location: USA

PostPosted: Thu Sep 22, 2011 1:28 pm   
 
Assuming you can match whatever ends the list, just change the state's trigger from ^({*|})$ to ^({*|End of list!})$.
_________________
.geniusclown
Reply with quote
Arde
Enchanter


Joined: 09 Sep 2007
Posts: 605

PostPosted: Thu Sep 22, 2011 2:10 pm   
 
You can't, because * will match everything. It doesn't match an empty string, but will match any other "end of list" string. And, while you can do ^({End of list!|*})$ instead, it adds 1 additional string match check per each trigger execution. Is it will be more effective (in terms of execution speed) than 2 separate triggers?

Can you see, in my MUD there is no blank lines after a list, and I have different approach to list parsing. In my system I have custom OnPrompt event that fires each prompt. I use it for many tasks and handle it whenever I need to. This way I can catch any "shop"-like command, turn list-parsing trigger on and then disable it with the OnPrompt event handler. Of course, I'm always looking for more speed-effective solution. If I'll find it, I'll definitely switch to it.
_________________
My personal bug|wish list:
-Wrong Priority when copy-paste setting
-1 prompt trigger for Mapper, Session and General Options, not 3 different!
-#SECTION can terminate threads
-Buttons can't start threads
Reply with quote
Daern
Sorcerer


Joined: 15 Apr 2011
Posts: 809

PostPosted: Thu Sep 22, 2011 3:32 pm   
 
Make the pattern of the trigger ^(*)$, then check in the code if it's the end of list string with %match. If it is, change state, if not, process it however is required. And FYI, * does match an empty string (including blank lines).
Reply with quote
Arde
Enchanter


Joined: 09 Sep 2007
Posts: 605

PostPosted: Thu Sep 22, 2011 4:09 pm   
 
Daern wrote:
Make the pattern of the trigger ^(*)$, then check in the code if it's the end of list string with %match. If it is, change state, if not, process it however is required. And FYI, * does match an empty string (including blank lines).

1) ^(*)$ is the slowest trigger one can ever create.
2) Having %match within a trigger is equal to having 2nd trigger - that's why I've asked about speed of such solution
3) If * match an empty string, why have a pattern like ^({*|})$ ?

I did a check. CMUD translates (*) into (.*) regex, where dot match any single character that is not line break. Not sure about matching an empty string in this case.
_________________
My personal bug|wish list:
-Wrong Priority when copy-paste setting
-1 prompt trigger for Mapper, Session and General Options, not 3 different!
-#SECTION can terminate threads
-Buttons can't start threads
Reply with quote
Daern
Sorcerer


Joined: 15 Apr 2011
Posts: 809

PostPosted: Thu Sep 22, 2011 4:28 pm   
 
First of all, ^(*)$ may be slow, but if it's only enabled when you're trying to capture every line anyway, what else are you going to do? Remember that when you have a multistate trigger, only the currently active state is considered enabled, and a disabled trigger doesn't have any speed impact at all. You don't need a pattern like ^({*|})$, just ^(*)$ will suffice. And it's true that dot matches any single character, but you didn't take into account the * - it means match 0 or more of the previous term. So .* will match anything, including nothing.

As for the speed of using %match in the trigger... if you have an event being raised on your end of list string anyway, I agree that it would be more efficient to reset the state in that event, and leave out the check for if the line is your end of list string in the manual trigger. As you said, %match is equivalent to having another trigger, and if you already have a trigger with that pattern to raise the OnPrompt event, you're just duplicating it.
Reply with quote
geniusclown
Magician


Joined: 23 Apr 2003
Posts: 358
Location: USA

PostPosted: Thu Sep 22, 2011 6:36 pm   
 
Quote:
You don't need a pattern like ^({*|})$, just ^(*)$ will suffice.


Good point, but there's something subtle in my original script that got lost in the quoting and reworking, and could change the conversation a bit... the pattern I'm matching is not
Code:
^({*|})$
but rather
Code:
^({ *|})$
...see the difference?

I agree that if you're doing something similar very frequently and don't have the leading space, another method such as Arde's changing state/disabling via onPrompt event would be generally superior.
_________________
.geniusclown
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » CMUD General Discussion All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

© 2009 Zugg Software. Hosted by Wolfpaw.net