Word/Name Highlighting triggers

User avatar
Rakon
Posts: 350
Joined: Tue Feb 16, 2010 7:41 pm
Contact:

Word/Name Highlighting triggers

Post by Rakon »

Greetings again.

I've been making progress on creating a system for Mudlet, with the help of you forum goers and the development team.

As was brought up in another thread, I make liberal use of highlighting triggers and I would like to be able to create/use functional, correctly highlighting triggers with Mudlet. Currently I've been trying to create an 'enemies name' highlighter. The key requirements of this for me are:
  • Must be quick. IE, no taking over a minute to highlight names
  • Highlight multiple names, occurrences of one name, on a line
  • ONLY Highlight the name, not the name if it's part of another name/word
  • Must be able to add and remove names/highlighting from the list easily
To do this, I decided on creating a table of enemy names, and then using table.save() and table.load() for persistence to save and load the table when needed.

This is the code for the alias to load the table, and create the tempTriggers for the names highlighting.
Code: [show] | [select all] lua
local enemy_file= getMudletHomeDir() .. "/scripts/enemies.tbl"
table.load(enemy_file,enemies)

for i,v in pairs(enemies) do
 tempTrigger(capitalize(tostring(v)), [[deselect() selectString("]] .. capitalize(tostring(v)) .. [[", 1) fg("red") deselect()]])
end 
I originally was using tempRegexTrigger() for the creation of the names, as I could put in a word boundary into the regex to be sure it matched on ONLY that name. IE (\benemyname\b). However, matching with those 1800 rexex triggers was.... slow. Very slow. So, upon advice from Vadi and the manual, I changed to using tempTrigger(). However, now some names are not highlighting on a line in one instance, but ARE highlighting in another. Also, by using this tempTrigger() instead of a RegEx, the word is highlighted even if its contained in ANOTHER word (not, what I want to happen).

For the first instance: Xan is an enemy. There is a temporary trigger that is suppose to highlight the name. On one line, (After 'Enemies of the City of Mhaldor') his name is NOT highlighted. However, on the line after the next prompt, when I pull up information on the character, his name IS highlighted.
Image
What would cause this to occur, and how can I get the highlighting to be consistent?

On the second instance: Phae is an enemy. I want her NAME highlighted, IE only if it's exactly 'Phae' not anything else. On the last line before the the prompts, you'll see that there is a line 'Penned by my hand on the 4th of Phaestian, in the year 533 AF.' where the 'Phae' in 'Phaestian' is highlighted. I would like for this to NOT occur.
Image
I know what's causing this, the trigger matching for a 'substring' is highlighting any occurrence of the word without respect to a word boundary. Short of using regular expressions for these triggers, is there anything I can do to force an 'exact match' or a matching only with word boundaries for tempTrigger?

Thank you.

naftali
Posts: 138
Joined: Wed Jan 20, 2010 8:42 pm

Re: Word/Name Highlighting triggers

Post by naftali »

for substrings, I dunno. In regexp \b is a word boundary

User avatar
Rakon
Posts: 350
Joined: Tue Feb 16, 2010 7:41 pm
Contact:

Re: Word/Name Highlighting triggers

Post by Rakon »

Naftali:
I understand that \b in a regex is a boundary. However, the implementation of 1800 + temp regex triggers substantially slows down the processing. I was asking if there was a way to avoid using regex triggers for the second instance scenario.

User avatar
Heiko
Site Admin
Posts: 1548
Joined: Wed Mar 11, 2009 6:26 pm

Re: Word/Name Highlighting triggers

Post by Heiko »

Here's a fast enemy highlighter demo I made a long time ago http://forums.mudlet.org/viewtopic.php?f=6&t=158

I've told you before, haven't I :)

User avatar
Rakon
Posts: 350
Joined: Tue Feb 16, 2010 7:41 pm
Contact:

Re: Word/Name Highlighting triggers

Post by Rakon »

That does nothing to answer any of my questions whatsoever.

I know that you , AND vadi both have packages with an enemy highlighter functionality. However, your script package appears to have the same issue I'm having in instance two. It highlights the word/enemy regardless of respecting a word boundary.

User avatar
Vadi
Posts: 5035
Joined: Sat Mar 14, 2009 3:13 pm

Re: Word/Name Highlighting triggers

Post by Vadi »

It respects the word boundary:
Image

User avatar
Rakon
Posts: 350
Joined: Tue Feb 16, 2010 7:41 pm
Contact:

Re: Word/Name Highlighting triggers

Post by Rakon »

Wonderful. Glad that is cleared up.
That does nothing to answer any of my questions whatsoever.
I'm still not understanding what makes Heiko package work the way as I'd expect, and not the way I'm doing it.
Also, still have the issue with the first instance occurring that no one has been able to answer yet.

Could one of you, that have written such a highlighter, go about being able to explain HOW it works, and how one would go about recreating its ability??

User avatar
tsuujin
Posts: 695
Joined: Fri Feb 26, 2010 12:59 am
Location: California
Contact:

Re: Word/Name Highlighting triggers

Post by tsuujin »

Not to be insulting to any of our dev team, because they're doing great things with mudlet, but I feel your pain here. I asked a while back how I would write a temp trigger to highlight each instance of a name on a line, not just the first, and was redirected to a script that was -way- over complicated for what I wanted to do (Sorry vadi, but if you're going to point us to scripts, PLEASE document them better!)

It took me a while, but I eventually answered my own question, and the answer was a lot simpler than I was making it out to be. Basically just a difference in coding styles that I'm used to.

So, moving on, here's what I can help you with:
a substring trigger has no way to distinguish that you -don't- want it to stop at the end of your pattern. If you use a substring, it'll match no matter where in the line it catches. It's the nature of the beast. The only way that I know of to tell a pattern that you explicitly want it to only match on a whole word is to use regex and specify "\b". I know it's slower, but it's a necessary evil.

To compensate, you have to learn to be a bit more conservative about when your triggers match. If you went through your whole system, you'd very likely find that a great deal of them are only important in certain situations. Identify those situations and turn on or off groups of triggers only to be used when they're useful. You end up with plenty of regex's at the end, but make your matching much more efficient to make the speed hit lighter.


Next, why the highlighters work. I can't explain exactly how Vadi's works (because I gave up myself on that one) but I can show you the general premise that I use for matching.

Temp triggers are awesome for this. Instead of matching every line, every time, you're going to create several individual triggers that trigger on a given word-- that is, the word you're looking to highlight. I know this -seems- inefficient, but in all honesty it's faster than trying to use a "match-all" trigger for parsing words.

the code to be executed for each trigger is exactly the same. You're going to look for the word, select it, color it, and move on. selectString has a very useful return value of -1 when it doesn't find a match, which makes this possible.

Code: Select all

local c = 1
while selectString(target,c) > -1 do
	setFgColor(255,0,0)
	setItalics(true)
	c = c + 1
end
resetFormat()
Fairly simple here: start a counter at 1. Next, enter a loop ("while") that looks for a match ("target") from the current line. The second argument to selectString ("c") is which -instance- of the word to display. If you're looking for the word "the", and that word appears 15 times in the line, you use that argument to specify which of those instances you want to manipulate.
If the instance exists, you set the foreground color (and I make it italic), and increase the counter. The loop goes through again, until it finally can't find another instance of that word, which causes selectString to return -1.
Then you're done, so reset the format.

I -believe- what Vadi did was simply include a similar block of code in a function, and call the function rather than the code itself. Probably more efficient, but I didn't bother.

For reference, here's the entire script from my setTarget alias:

Code: Select all

target = matches[2]
echo("Target is now: "..target.."\n")
if hlTarget then killTrigger(hlTarget) end
hlTarget = tempRegexTrigger("\\b"..target.."\\b", 
	[[
		local c = 1
		while selectString(target,c) > -1 do
			setFgColor(255,0,0)
			setItalics(true)
			c = c + 1
		end
		resetFormat()
	]]
)

Hope I could be of some help. If I explained something poorly (it is, after all, like 1:30am) just ask me and I'll try and clarify.

User avatar
Heiko
Site Admin
Posts: 1548
Joined: Wed Mar 11, 2009 6:26 pm

Re: Word/Name Highlighting triggers

Post by Heiko »

Rakon wrote:That does nothing to answer any of my questions whatsoever.

I know that you , AND vadi both have packages with an enemy highlighter functionality. However, your script package appears to have the same issue I'm having in instance two. It highlights the word/enemy regardless of respecting a word boundary.
Rakon, do you honestly believe that I'll bother to answer any further questions of yours in the future if you behave like this? It's more than obvious that you've never ever tried out the package, let alone bothered to look into the script to find out how things are done yourself.

User avatar
Heiko
Site Admin
Posts: 1548
Joined: Wed Mar 11, 2009 6:26 pm

Re: Word/Name Highlighting triggers

Post by Heiko »

tsuujin wrote:Not to be insulting to any of our dev team, because they're doing great things with mudlet, but I feel your pain here. I asked a while back how I would write a temp trigger to highlight each instance of a name on a line, not just the first, and was redirected to a script that was -way- over complicated for what I wanted to do
Vadi's highlighter package is based on my tech demo script that I've pointed out above. His package is intended to be used by actual players while mine is just a demo.

How does it work? The demo shows how to use Mudlet's fundamental gating paradigm in the context of dynamically created triggers i. e. using computationally fast pattern matching (=string comparison based matching) instead of computationally expensive pattern matching (=regex) to shield your expensive regex patterns until they are actually needed.

Each enemy name becomes a substring trigger (tempTrigger). You'll end up getting thousands of such patterns that need to be matched against every incoming line from the MUD. In case any of these pattern matches, you'll have to apply further more expensive computation to find out if the match is of the nature that you expect. In the case of my demo I simply run a Lua script to check if the substring match is a real name in which case the script will highlight it. Consequently, in this script the highlighting code is slow, but the overall lag is very good.

This is the one area where a selectRegex() function would make things easier. There has been a significant amount of discussion about such a function in the early days of Mudlet developmet and it was decided against making one for various reasons. It is something that could be brought up again for further discussion in the development forum, however.

Another option to make things easier (and also the actual highlighting much faster also) would be a selectWord() function. That's something I'll add in future releases.

Post Reply