A complicated regex Trigger

Posted by Nexes on Wed 01 Mar 2006 11:10 PM — 34 posts, 123,989 views.

#0
Ok, say there's a line like this: Yuna Concordia, The Archsummoner is here. She wields an elemental staff in her right hand.

Now, my problem is that that line can have a line break where ever there's a space (I am not sure if it replaces the space or it's put right after it but that's not too important). For example it could look like any of these:
Yuna
Concordia, The Archsummoner is here. She wields an elemental staff in her right hand.

Yuna Concordia,
The Archsummoner is here. She wields an elemental staff in her right hand.

Yuna Concordia, The Archsummoner is here.
She wields an elemental staff in her right hand.

Yuna Concordia, The Archsummoner is here. She wields an elemental staff in her
right hand.

And so on.
I tried to make a regex pattern for it and failed miserably. I had two triggers, one for the one line version, and one for the two line version (because Mushclient doesn't like it when I make a two line trigger that's caught in one). But I can't figure out how to make the two line version work properly.

What I have so far is something like ^She( |\n)wields( |\n)(.*\n?)+( |\n)in( |\n)her( |\n)right|left hand\.$

But it still catches the one line version unfortunantly and that makes Mushclient cry. So I was wondering if anyone new some clever way of catching something like this.
#1
I guess this is impossible. Meh.
Australia Forum Administrator #2
Quote:

...because Mushclient doesn't like it when I make a two line trigger that's caught in one ...


Why is that?




This seemed to work for me:


<triggers>
<trigger
enabled="y"
lines_to_match="10"
match="(?s)Yuna( |\n)Concordia,( |\n)The( |\n)Archsummoner( |\n)is( |\n)here\.( |\n)She( |\n)wields( |\n)(a|an)( |\n)(.*)( |\n)in( |\n)her( |\n)(left|right)( |\n)hand\.\Z"
multi_line="y"
regexp="y"
send_to="2"
sequence="100"
>
<send>Matched!</send>
</trigger>
</triggers>


Basically I made each space into ( |\n) which then matches on the space *or* the newline. This works for me provided I don't have a space before the newline. If so, you would have to modify it a bit.

I also used the "dotall" feature ( which is (?s) at ths start) which lets the dot match on newlines, in case the line break comes inside the name of the weapon.
Amended on Fri 03 Mar 2006 06:14 AM by Nick Gammon
#3
Ooer, I'll try that. Also, I say Mushclient doesn't like it because if it is on one line but it's a two line trigger, well, Mushclient fires the same trigger twice on the same line. It's really annoying.
#4
If \Z is supposed to be an endline I can't use that :(

Yours is still firing multiple times on one line :(
Australia Forum Administrator #5
Quote:

If \Z is supposed to be an endline I can't use that


Why can't you use it?

From the regular expression documentation:


\Z matches at end of subject or before newline at end
\z matches at end of subject


Using \z at the end guarantees a "tail match". This stops it matching on the same line twice. Without it, if you matched over (say) 10 lines, and the line appeared twice in the 10 lines, you would get 2 matches.

Quote:

Yours is still firing multiple times on one line


It isn't firing multiple times for me. Did you change it? Using mine, it fires once per matching line. What are you testing it with? In my case I sent:


Yuna Concordia, The Archsummoner is here. She wields an elemental staff in her right hand.


I don't see how it can match multiple times, unless you have put the trigger in twice.
#6
The thing is, the ilne doesn't end, it's part of a big paragraph. So I removed the /Z and it matches multiple times.
Australia Forum Administrator #7
Well, it will do that. You expect it. If you have a regexp that might match one line, and tell it to match over 10 lines, and don't "anchor" it to the start or end of those 10 lines, then potentially you will get 10 matches, as the matching line "ripples" back through the batch of 10.

You need to find a way of anchoring it, either to the start or end of the multi-line expression.
#8
*sigh* There's no way to anchor it. Oh well, I guess I'll just have to live with it.

I was hoping for some way to specify in the regex at at least one new line should be caught or something. It'd be nice if Mushclient itself did that so a 2 line trigger must be on two lines. But, I guess I can live with it. :p

Thanks for your help!
Australia Forum Administrator #9
I'm not quite sure what you mean by that. If you specify a multi-line trigger as matching over 2 lines, and have a newline in the regexp, then it will indeed match exactly 2 lines.
#10
Like in your trigger example, every \n is in parentheses and a space can replace it. Therefore, if I set it as two line, I wanted it to only match if there was at least one \n in the actual matching text, not just if the regex matched.

Like say I have this trigger: Hide\n?you\n?thief
And I put it in a multiline trigger and set it to catch on two lines.

Well, it'd be nice if it did catch on two lines only.
Ie:
Hide
youthief

Hideyou
thief

But NOT:
Hideyouthief
USA #11
(hideyou\nthief)|(hide\nyouthief)

Why doesn't that work? OK, it's easy for the simple case and would be very long for the longer case, but something similar probably would work for you.
#12
See, the line I actually want to make this for is anything but a simple case. That's why.
USA #13
OK. Let's go back to the beginning. Are you trying to extract what the summoner is wielding?
#14
I am trying to extract the name of every person in the room (which comes with the title sadly), and what they're wielding.

Then there are also items that are in the room that show up in the description and I want to get all those too.

Then there are NPCs that show up in the room description.

Basically, I want to grab everything useful from a big block of text.

See, when you look in Achaea it gives you something like this:
Postal Office of Hashan.
A runic totem is planted solidly in the ground. A mottled Mhojave desert falcon glides effortlessly to and fro. A sigil in the shape of a small, rectangular monolith is on the ground.
You see a single exit leading northwest.

Except when there's people in there it's more complicated, like:
The Crossroads.
Occasional drops of rain fall to the ground from a sky grey with pregnant clouds. A runic totem is planted solidly in the ground. There are 10 fierce viragos here. This shrine is crafted into the form of three tall women standing back to
back in a tight circle, hands clasped together at their sides. A sewer grate looms darkly beneath your feet. An emaciated and skeletal dracolich lurks in the shadows. Shimmering an opalescent, milky white, this iridescent pearl bears the
words "Suggestion Box" painted in flowing silver script upon its smooth surface. There are 7 mounted tsalmaveths here. A sigil in the shape of a small, rectangular monolith is on the ground. There are 4 hooded nocturnis here. A ludicrously
bright green blanket has been placed here to clash with everything. Flapping its massive wings, a dire bat flies overhead. A hefty glass mug has been left here. A flame-charred chariot pulled by demonic goats stands here. Spreading its
majestic golden wings, a giant eagle searches the ground with piercing eyes. A mottled Mhojave desert falcon glides effortlessly to and fro. A blue marble bench with a covering of dark blue satin is here. Standing on a pedestal of silver,
an Orb of Confinement has been carefully constructed here. Flaze is here, sprawled on the floor. Brother of the Red Lotus, Jasato Kuriskagi is here. He wields a steel Theran broadsword in each hand.
You see exits leading north, east, southeast, south, southwest, and northwest.
#15
As you can see, Achaea preparses the description into a big paragraph, that's why I can't assume there's an end line at the end of a sentence, and I must assume any space can be replaced with a newline.
USA #16
Although I haven't 100% checked this, I think to be accurate that Achaea does not replace spaces with newlines, but inserts newlines after spaces when its wrapping.
Australia Forum Administrator #17
It seems safe to assume that Achaea is trying fairly hard to make it difficult to parse their output with simple triggers.

The only suggestion I can make is this:

  • Somehow detect the ends of the description (start and finish)
  • As it arrives, build it up into a big block of text
  • Once you have the whole description, replace newlines with a space
  • Convert multiple spaces to one space
  • Break up the resulting block of text into sentences (ie. ending with periods)
  • Apply a regexp to each sentence to see if it matches what you want.

#18
Yeah, Im going to use UDP to send it to a C++ program and have it parse it into sentences. Then start splitting the sentences to pieces one at a time. Im worried about the speed penalties of doing this though.
USA #19
I wouldn't worry about the speed consequences of this. All you're doing is moving some bytes to a running program, which will split into sentences. All of this is quite cheap, computationally.

I'm curious though why you're sending this to an external program, and why you chose UDP. Why not just have a running Lua script that takes care of this for you?
#20
C++ classes, OOP design for my combat system. Also, GUI components that I can't create inside of MUSHClient. And it's faster for processing complex tasks. I chose UDP because I know nothing about COM objects and UDP was the only other method that people have mentioned to send data to a third (second?) program. I also could have written DLLs for Lua in C but frankly I felt this was also unnecessary considering I could just use a full blown executable with gauges, buttons, and the works.

Also, less logical, but I know C++ is fast and I feel more comfortable writing complex and computationally expensive functions in it than I would in any scripting language.
USA #21
Oh, I didn't know you were doing all that other stuff. In that case it makes sense to have C++, especially if you've already written all this other code for it.

I was surprised to hear you say UDP, and not just plain old normal TCP sockets, examples of which abound for Winsock. UDP seems to be a slightly more unusual route to me.

FWIW, Lua is an extremely fast scripting language. It is more than sufficient for any kind of processing people would be doing for a MUD. People can render 640x480 images at hundreds of frames per second with Lua doing the grunt-work; surely that is sufficiently fast for a measly little text parser? :-)

(Don't forget that text parsing is really a very inexpensive task in general. If you don't believe me, write some perl scripts that chunk over megabytes of data and do something (un)interesting. You might be surprised by the speed results.)
#22
I use UDP because of the MUSHClient supplied functions for sending and receiving UDP packets. Otherwise, I have no idea how I would establish a connection between MUSHClient and my program. :(

And I distrust string operations because of my experience in Java. It was...incredibly slow with the strings. Concatenating, and everything else really ate up my memory and while I found the string buffers in Lua to be exceedingly simple to use I have since distrusted string operations.
Amended on Tue 14 Mar 2006 09:44 PM by Nexes
USA #23
Do you mean Java or Javascript? I've not had problems you describe using strings in Java. There are some gotchas, especially if you're building very large strings a few characters at a time, in which case you actually want to use a StringBuffer (or StringBuilder in 1.5 I think it's called). That way, you allocate a chunk of memory once, and then fill it up. But, this also applies to C++ strings.

But again, I'd be quite surprised if you had problems with strings in Java. I wrote a program for work that takes several hundred session logs, reads them all in, does black magic on them involving very many string operations per session, and the blocking part is disk access, not string manipulation.
#24
I meant Java. I dunno, maybe my virtual machine sucked :(
Australia Forum Administrator #25
Quote:

Yeah, Im going to use UDP to send it to a C++ program and have it parse it into sentences


Personally I wouldn't use UDP in this application. A UDP packet is limited in size (something around 512 bytes) so a long description would need to be split between packets, reassembled at the receiving end, and so on.

If you look at this forum post:

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=6034

... I did 200,000 string.find operations (that is, the Lua regexp) in one second, so I don't think speed will be an issue. Also, I recently wrote an application in Lua that read in all my old email messages, parsed the headers, and added them to an SQL database. The whole thing ran really fast, processing something like 5,000 emails in a minute or so. There was no problem with string management.
USA #26
Might I suggest a way to make this easier? (wouldn't want to take away from your project though Nexes, hehe)

CONFIG SCREENWIDTH 250

and set MushClient to do your wrapping for you... It won't be perfect, since descriptions are often longer than 250 characters, but it might make things a bit easier.
#27
My screen width is already set at 240.

Hmm, I guess I would have to figure out the max length of a UDP packet, and figure out how many charachters that translates into. If it's above 240 then it shouldn't be a problem, otherwise maybe your method would be better Nick.
USA #28
Well now. Achaea now allows you to CONFIG SCREENWIDTH 0, so try that if you'd like. However I'd think it'd be much simpler to target a person IE 'set summoner Yuna/<person>', and have a trigger match on the line that the name is in. In Cyan or whatever you use for a 'player' color. Thus, when the trigger matched you could make the REGEX '(She|he) is wielding (a|an) (.*?) in (his|her) (right|left) hand
.' I don't know how you'd account for a newline in the description however, maybe use the REGEX to do a few ( |\n) ??
Good Luck

--Rakon
#29
I read about the screenwidth 0 thing the moment the put it in and I love it.

Actually that brings Nick's point even more relevant since I definitely will run into that limit on packet size now, at least if I send it all in one packet.

Anyways, the reason I don't want to do it your way, Rakon, is because that's not quite what I wanted to do. The trigger pattern you mentioned is exactly what I wanted to avoid, since it fires multiple times if it comes in less lines than I set the trigger to capture. Also I don't want to limit the capture to only my target.
#30
Not sure if it was said yet, but if you are talking about Achaea, try CONFIG SCREENWIDTH 0.
USA #31
Quote:
The trigger pattern you mentioned is exactly what I wanted to avoid, since it fires multiple times if it comes in less lines than I set the trigger to capture. Also I don't want to limit the capture to only my target.


The reason for setting a target is so that it only matches the line the targets name is in. Thus, it should not fire multiple times unless you QL more then once. Really, without defining a target for the system to look for, won't you in essence, overwrite the variables everytime it makes a match???

Or am I utterly confused as to what you are trying to do??
#32
Meh, it's not really important any more since the screenwidth 0 option is in, but before, if you had a regex pattern set to multiple lines, say 3, and it was caught on one line, it would fire 3 times anyways for that single line. The reason I posted here was to figure out if there was a way around this and there isn't so I decided to simply start parsing the text in a big block.
Australia Forum Administrator #33
Well it won't if you anchor the regexp to the start or the end of the 3 lines. If you don't, well it is working as intended.