Zist text manipulation

From RoDpedia
Revision as of 04:14, 6 July 2023 by Blithe (talk | contribs)
Jump to navigation Jump to search

Text Manipulation

Text Manipulation : The changing of a text string to reformat it, remove unwanted text, or add desired text.

If you ever want to do very useful things with zMud, in as few commands as possible, then the creative manipulation of text is definitely something you want to master. So what are some useful things that the manipulation of text can bring you, that using the regular parameters (i.e. %1, %2, %3, etc) cannot do for you? To attempt to answer that simply, and completely, what follows are two examples. In the first example there will be a simple text manipulation, handy for when you run multiple characters that are grouped. The second example will be a bit more complicated version of the same manipulation, but will begin to show the power of text manipulation and how it can be used in conjunction with zMud list routines to build stacks and queues.

Text Manipulation Example 1: Simple reporting of "Affected" stats:

In this example, we take as input the data that the MUD gives us when we type "AF" and get our "Affected" list. This list shows what spell affects the character has on it, and this example shows how a trigger can manipulate the output line of text into a "say" command that reports this information to a public channel for everyone in the room to see. An example line that would set off the trigger would look like this:

Affected: ( 911) kindred strength

This line will then be converted by the trigger, using text manipulation techniques, into the following command which is then sent back to the mud:

say 911-kindredstrength

Here is the "logic flow" of how we can accomplish this task:

1. First, trigger off of the text pattern "^Affected:" so that every line that starts with Affected: causes the trigger to fire. The carrot (^), when placed at the beginning of a trigger pattern, causes that trigger to look for the pattern at the beginning of a line instead of the middle. Since every "Affected:" line starts with that particular string, coding a pattern of "^Affected:" makes this trigger's pattern matching more efficient.

2. Second, we need to take the text of the line that triggered the trigger, and manipulate it. To get the text of the line that triggered the trigger, we use the "%trigger" variable, a built-in zMud variable that automatically contains the text of the line that triggered the trigger. The first manipulation, working from left to right, is to make a copy of %trigger into a new variable while removing the text "Affected:" from it. We create a new variable named @temp, and put the changed text into it like this :

"#VAR temp %remove("Affected:",%trigger)". 

This uses the built-in function "%remove" which removes the text "Affected:" from the string held in "%trigger", and then provides the modified string to the #VAR function so that it can assign that changed text sting to @temp. If we looked in @temp right now, it would contain " ( 911) kindred strength", the same string that triggered it from above, but without the "Affected:" portion. Note that the value of "%trigger" is read-only and is not modified by this command.

3. Third, we need to remove all spaces from @temp, so that the text is all smushed together. To do this, we use a different, but similar function. It is %replace and is used as follows:

"#VAR temp %replace(@temp," ","")". 

This command tells the %replace function to look in the variable @temp, find every occurrence of " " (a space), and replace it with "" (nothing). In effect, this deletes every space from @temp. In the end, the %replace function hands the modified string over to the #VAR command, who reassigns the @temp variable with the new value of "(911)kindredstrength".

4. Fourth, we need to remove the left parenthesis, "(" from the string in @temp. To do this, we use %replace again like this :

"#VAR temp %replace(@temp,"(","")". 

Similar to the step above, %replace changes every occurrence in @temp of "(" with "", effectively deleting every left parenthesis it finds. The variable @temp now contains the string "911)kindredstrength", since it has been modified to no longer contain any left parenthesis. Now, you may be wondering why I used %replace instead of %remove. Well, %remove only removes the first string it finds that matches what you ask it to remove, whereas %replace will replace every single occurrence of whatever you tell it. Since I reasoned that it may be possible that there is more than one left parenthesis (due to a mud coding change or whatever), I used %replace to make this trigger more versatile and robust.

5. Fifth, the right parenthesis, ")", needs to be changed into a dash, "-". Again, we use the %replace and #VAR to do the job:

"#VAR temp %replace(@temp,")","-")". 

As before, @temp is searched for any occurrence of ")" and each is replace with "-". At this point, @temp contains the string "911-kindredstrength". This is the string we specified above, so we are almost done with this trigger.

6. Sixth, and lastly, we want the character to "say" this string. The easiest way to code this is to use the command

"say @temp". 

This will send the text

"say 911-kindredstrength" 

to the mud, as desired.


So, as a recap, here are the four commands in our trigger command-list:

#VAR temp %remove("Affected:",%trigger)
#VAR temp %replace(@temp," ","")
#VAR temp %replace(@temp,"(","")
#VAR temp %replace(@temp,")","-")
say @temp

Here is the full command to enter to create this text manipulating trigger (with a class name of (affshort) added to the end):

(click here for copy/paste-able versions of all examples on this page:)
#TRIGGER {^Affected:} {#VAR temp %remove("Affected:",%trigger);#VAR temp %replace(@temp," ","");#VAR temp %replace(@temp,"(","");#VAR temp %replace(@temp,")","-");say @temp} {affshort}

Now, believe it or not, there is a shorter and more efficient way to code the above five commands so that they are all in ONE command line and do EXACTLY the same thing! I will cover this next, before we get into example 2, so that you can see how efficient (and ugly, and unreadable) zMud scripting code can be! Here is how we combine the four above commands into one, building block by building block...

1. First, the first two commands can be combined from:

(old command #1) - #VAR temp %remove("Affected:",%trigger)
(old command #2) - >#VAR temp %replace(@temp," ","")
into : - #VAR temp %replace(%remove("Affected:",%trigger)," ","")

See how the first command with the %remove is "contained" in the second command when rewritten into the new command line? The innermost function, %remove, is executed first, and after it has removed the "Affected:" from the %trigger value, it passes that value into the next outer function named %replace, which replaces all spaces with nulls (i.e. deletes all spaces)! That is called "nested function calls" because the %remove function is now "nested" inside the %replace, and it "hatches" it's value directly to the function it's nesting in! Ok, here's the next combination...

2. Second, the next command is added around the new combination:

(command from last step) - #VAR temp %replace(%remove("Affected:",%trigger)," ","")
(old command #3)---------- #VAR temp %replace(@temp,"(","")
(new command) - #VAR temp %replace(%replace(%remove("Affected:",%trigger)," ",""),"(","")

Is this getting ugly or what? The next %replace command is now containing the previously nested command, creating a third layer of nesting! Now the %replace that removes all left-parenthesis takes the accumulated output from the other two functions, and then does it's remove! But wait, the fun's not over yet!

3. Third, the newly created triple-nested command gets the fourth command, a %replace command, added on to it in the same way:

(command from last step) - #VAR temp %replace(%replace(%remove("Affected:",%trigger)," ",""),"(","")
(old command #4) --------- #VAR temp %replace(@temp,")","-")
(new command) - #VAR temp %replace(%replace(%replace(%remove("Affected:",%trigger)," ",""),"(",""),")","-")

And there it is, a fourth layer of nesting! Same as before, the next command to be executed it placed around the previous commands. The command added is the one that changes the right parenthesis into dashes. Just one more thing to do to finish this nesting example!

4. Fourth and lastly, the "say" command can be added to this new command so that a variable is completely unnecessary! Here is how it works:

(command from last step) - #VAR temp %replace(%replace(%replace(%remove("Affected:",%trigger)," ",""),"(",""),")","-")
(old command #5) --------- say @temp
(new command) - say %replace(%replace(%replace(%remove("Affected:",%trigger)," ",""),"(",""),")","-")

And as simple as that, all five commands have been combined into one line. The variable has been removed because it is not needed. You only need a variable if you are storing a value to use again in a different command or a different trigger/alias. Since all of the values we need are created in one nested string of functions where each nested function's output value is given as input to the next outward layer's function's input, there is no permanent storage needed. This ONE command line replaces all FIVE previously shown, and does EXACTLY the same thing (and does it quicker!). It's faster for zMud to interpret this long and ugly line than it is to execute five separate command lines. If you do a lot of text manipulation then this is the way to do it, combining wherever you can. It saves you CPU resources in the long run, and can drastically cut down on zMud lockups if you are using an unsupported version that still has a few interpreter bugs in it (like v5 and below).

Here, then, is what the fully-combined trigger looks like, with no variables and a new class name of (affshorter):

(click here for copy/paste-able versions of all examples on this page:)
#TRIGGER {^Affected:} {say %replace(%replace(%replace(%remove("Affected:",%trigger)," ",""),"(",""),")","-") } {affshorter}

Text Manipulation Example 2: Reporting of "Affected" stats w/list:

This example is a bit more advanced, and includes the first useful example of a zMud list. It also includes another useful example of automatically enabling and disabling triggers using their class names (and the commands #T+ and #T-). In addition, a couple of advanced programming techniques are discussed, including conditional decision making (using #IF) and looping (using #LOOP).

Specifically, this example consists of two triggers that do the same work as the single-line trigger above. Why write it again, to do the same thing, in TWO triggers? Because now that you understand what is going on with the "Affected" trigger above, adding bells and whistles to it and building on it will make it easier for you to see how these advanced features work. Realistically, these advanced features are not needed for something as simple as reporting "Affected" lines, but these techniques can be added to, expanded upon, and make much bigger jobs much simpler (just as they make this simple job slightly more complicated).

Let's start with the two triggers and what they do.

TRIGGER 1 : This trigger is VERY similar to the trigger written above, except that instead of using the "say" command, it uses the "#VAR aff %additem(...,@aff)" command and adds an extra command of "#T+ aft". In short, these two additions cause this trigger to add the text "911-kindredstrength" (or whatever Affected: line is found) to the list in variable @aff, and causes the class named "aft" to be enabled using the #T+ command.

So, TRIGGER 1 adds the compressed and reformatted text into a list called @aff, and then turns on a class named "aft". The importance of this is explained below in TRIGGER 2.

(click here for copy/paste-able versions of all examples on this page:)

#TRIGGER {^Affected:} {#VAR aff %additem(%replace(%replace(%replace(%remove("Affected:",%trigger)," ",""),"(",""),")","-"),@aff);#T+ aft} {aff}

TRIGGER 2 : This trigger patterns off of "*", which causes it to trigger off of EVERY SINGLE LINE that comes across the mud, provided the trigger is enabled. Since TRIGGER 1 above enables the class of this trigger, then it should be enabled only when "Affected:" lines are found. Then this trigger checks to see if the current line is a line that contains the text "Affected:". If it IS a line that contains "Affected:" then it does NOTHING. If the line does NOT contain the text "Affected:" then this trigger first disables itself so that it won't fire anymore. Then it counts the number of words (entries) in the list named @aff, then it reads each word (entry) from the list in turn and uses the "say" command to send the word (entry) to the MUD. Once all the words (entries) have been sent to the MUD with "say" in front of them, it erases the @aff variable.

So, TRIGGER 2 "says" to the MUD all the "Affected" lines collected up by the trigger above, and then empties the list of "Affected" lines.

(click here for copy/paste-able versions of all examples on this page:)

#TRIGGER {*} {#IF (%pos("Affected:",%trigger) < 1) {#T- aft;#VAR affnum %numwords(@aff,|);#LOO 1,@affnum {#VAR affitem %word(@aff,%i,|);say @affitem};#VAR aff ""}} {aft}

At this point, you may understand completely how these two triggers work, and how the interact with one another to collect up all the "Affected" lines, and then report them all at once. If you have, then you can stop reading here. If you are, however, wondering something like, "How the heck does it do all that?" then the following break down explanation is for you. What follows here is first an explanation of TRIGGER 1, command for command, and then a break down of TRIGGER 2, command for command, and then a walk-through of how it works.

First things first, TRIGGER 1:

The only things new in TRIGGER 1 are the %additem command and the #T+ command. The rest of it comes from the first trigger discussed previous in Example 1, and you should reference it if you have any misunderstanding about that portion of it.

The addition of "#VAR aff %additem(...,@aff)" where "..." is code from the trigger in Example 1, causes the variable @aff to be defined with the output of the %additem function. The %additem takes whatever text you put in it's first parameter (... in this case), and adds it on to the end (right hand side) of the list variable in it's second parameter (@aff in this case). When it adds the text on to the end, it puts a "pipe" (|) symbol between the text that is already in @aff and the text that is specified in the first parameter. To demonstrate, here is an example that uses the %additem command to build a list in variable @temp that in the end contains "test1|test2|test3":

#VAR temp {"test1"}

...at this point the variable @temp contains the text "test1"

#VAR temp %additem("test2",@temp)

...at this point the variable @temp contains the text "test1|test2"

#VAR temp %additem("test3",@temp)

...now @temp contains the text "test1|test2|test3".

The %additem function added the "pipe" (|) between each entry so that you could find them individually if you needed to. This regular text variable, @temp, has now been transformed into a zMud list variable! It's really that simple! Now don't get confused with this example using @temp and our trigger that uses @aff, the two aren't related at all, this is just an example to show how the %additem command works it's magic.

The second and last command in this trigger, "#T+ aft" enables class "aft". All triggers, aliases, macros, etc that are associated with the class name of "aft" are now eligible for use. This enables TRIGGER 2, allowing it to fire.

Second things second, TRIGGER 2:

This trigger is almost all new material to this discussion, and deserves a more detailed explanation. Here are the commands in it, and what they do (refer to the zMud manual for complete explanations of each command and what it can do):

(The complete trigger:) #TRIGGER {*} {#IF (%pos("Affected:",%trigger) < 1) {#T- aft;#VAR affnum %numwords(@aff,|);#LOO 1,@affnum {#VAR affitem %word(@aff,%i,|);say @affitem};#VAR aff ""}} {aft}
(Trigger Pattern) {*}

...as discussed earlier, a pattern consisting of just an asterisk (*) causes this trigger to fire on EVERY SINGLE line that comes across the mud to you, as long as the trigger is class-enabled.

(Command #1) #IF (%pos("Affected:",%trigger) < 1)

...the #IF command evaluates the expression in the parenthesis for a true or false value. The expression, (%pos("Affected:",%trigger) < 1), calls the %pos function which then looks in the %trigger variable for the text string "Affected:" and returns the number position of the first character of that string. Put simply, if the text "Affected:" exists in %trigger then the %pos function will return a value of 1 or higher. If the text does not exist in %trigger then the %pos function will return a zero. Therefore, the #IF is really just looking at the number returned by %pos and examining it to see if it is "less than 1" (same as "< 1"). By default, 0 is false, and 1 is true, so if %pos returns 0 then the #IF expression IS less than 1, so it is TRUE. If the %pos returns 1 or higher, then the expression "less than 1" is FALSE.

...in summary, this #IF simply tests to see if the line that triggered it (%trigger) contains "Affected:". The #IF expression (%pos("Affected:",%trigger) < 1) is TRUE if %trigger does not contain "Affected:", and FALSE if %trigger contains "Affected:".

(Command #1 - separator) {

...the separator that opens the "true leg" of the #IF command is an open-curly-brace or a left-curly-brace. The significance of this is VERY important, because between the first left-curly-brace "{" and the right-curly-brace that matches it "}" is contained ALL the commands that will be executed if the #IF statement evaluates to TRUE. Think of it like this : #IF (expression) {do if true} {do if false}. The "do if true" commands contained in the first set of matching curly-braces is executed if the "expression" is TRUE or equal to zero. If the "expression" is FALSE or not equal to zero then the commands contained in the second set of matching curly-braces is executed. The FALSE commands are optional, and if the curly-braces for it are left off then the FALSE side of the #IF is skipped.

(Command #2) #T- aft

...if the #IF is true, then the first command in the "true leg" of the #IF statement is this "#T- aft" command, which functions to turn off the class named "aft". Since this trigger is part of the "aft" class, it will disable this trigger from firing again until it is enabled. This command prevents the spam-reporting of the "Affected:" lines over and over and over - it limits reporting to ONE output report per "AF" command you enter.

(Command #3) #VAR affnum %numwords(@aff,|)

...this command assigns variable @affnum with the count of the number of words that exist within @aff that are separated by a "|". This gives a quick count of the number of "Affected:" lines that have been collected in the "list variable" named @aff. Remember how when we used the %additem function above it added the text separated by a "|" pipe character? Well, this is the first step as to how we can re-separate that list into it's individual components. If we are to separate a list, or pull portions of it out, it would be good to know how long the list is (i.e. how many items are in it, separated by "|" pipes).

(Command #4) #LOO 1,@affnum {...}

...yes, I know there is more than "..." in the curly braces, but let's take this one step at a time. The #LOO is short for #LOOP, and it loops from 1 to the value of @affnum. This means that it is going to start at one and keep adding 1 to it's internal count until that count reaches the value of @affnum. Each time that it adds 1 to it's internal count, the #LOOP command executes EVERY COMMAND contained in it's command-list between the curly-braces. Therefore, the commands shown above by "..." will be executed the number of times specified by the number in @affnum. For example, if @affnum equaled "3" then the commands in the curly braces after the #LOOP would be executed three times before the #LOOP command ended and passed control on to the next command.

(Command #4a) #VAR affitem %word(@aff,%i,|)

...this command, which is contained in the command-list of the #LOOP, sets variable @affitem to a specific word located in the list variable @aff. The word-number it extracts from @aff is controlled by a variable called "%i". The "%i" variable is the internal count of the #LOOP command, and increases by one each time the #LOOP executes it's command-list. Additionally, the %word function has been instructed to use the "|" pipe character to tell the difference between one word and the next. Therefore, what this does is finds word "%i" in list @aff as separated by "|" pipes, and places that word into the variable @affitem. Since %i starts at 1 and counts up, the first time this command runs it will put the first word into @affitem (the first word, is of course, the first "Affected:" line). The second time it is called in the loop, it will put the second word into @affitem, and so on until it has processed every word in the list.

(Command #4b) say @affitem

...this is the second and last command in the #LOOP command-list, and it does the same as the "say" MUD command in the first trigger at the top of this webpage -- it sends the modified "Affected:" line to the mud! Geez, all this work just to get to the "say", eh? Trust me, when you read my "killbuffer" tracking trigger and see what it takes to REALLY use the "list" variables, you will be glad you read through all of this!

(Command #5) #VAR aff ""

...this sets the "list" variable @aff to a null string (no text in it). This clears the list so that the next time an "Affected:" list comes from the mud the list will be empty and not have any "leftover" lines from an older run of the triggers.

(Command #1 - separator) }

...this is the last part of Command #1, the #IF statement. This right-curly-brace or close-curly-brace ends the "true leg" of the #IF statement. Since no open-curly-brace or left-curly-brace follows this one, there is no "false leg" of the #IF defined.

The last part of it : "} {aft}" closes the #TRIGGER command and sets the class to "aft" for this trigger when you enter it from the command line.

Text Manipulation Example 2: AN EXAMPLE OF THE EXAMPLE!

Here is an example, step by step, to show you how the list-triggers accomplish their work:

On the high level, when the following example text (used throughout this example) comes across the MUD:

Affected: ( 911) kindred strength 

Affected: ( 375) armor

Affected: ( 873) bless

The following text is sent back to the MUD after ALL THREE of these lines have been read, AND the next line after them (a blank line, or whatever) has been read by the Example 2 triggers:

say 911-kindredstrength
say 375-armor
say 873-bless


Here is the Play-by-Play (comments in parenthesis):

(1. before first "Affected:" trigger hits, variable @aff is empty)
(2. first "Affected:" line appears:) Affected: ( 911) kindred strength
(3. first trigger hits, with the following effects:)
@aff = "911-kindredstrength"
class "aft" is enabled by "#T+ aft"
(4. second "Affected:" line appears:) Affected: ( 375) armor
(5. first trigger hits again, with the following effects:)
@aff = "911-kindredstrength|375-armor"
class "aft" is enabled again by "#T+ aft"
(6. second trigger, enabled by first hit of "Affected:", hits with:)
#IF statement is FALSE, %pos returns a value of "1", no action taken
(7. third "Affected:" line appears:) Affected: ( 873) bless
(8. first trigger hits again, with the following effects:)
@aff = "911-kindredstrength|375-armor|873-bless"
class "aft" is enabled again by "#T+ aft"
(9. second trigger, still enabled from first hit of "Affected:", hits with:)
#IF statement is FALSE, %pos returns a value of "1", no action taken
(10. next line from MUD appears, does NOT contain "Affected:" in it)
(11. first trigger does NOT hit since line does not start with "Affected:")
(12. second trigger hits, still enabled from first hit of "Affected:", with:)
#IF statement is TRUE, %pos returns a value of "0", actions taken:
class "aft" disabled by command "#T- aft"
@affnum = 3 based on "#VAR affnum %numwords(@aff,|)"
#LOO 1,3 enabled, %i (index) = 1
@affitem = "911-kindredstrength" based on "#VAR affitem %word(@aff,%1,|)
text "say 911-kindredstrength" sent to MUD from "say @affitem"
loop iterates, %i = 2
@affitem = "375-armor" based on "#VAR affitem %word(@aff,%1,|)
text "say 375-armor" sent to MUD from "say @affitem"
loop iterates, %i = 3
@affitem = "873-bless" based on "#VAR affitem %word(@aff,%1,|)
text "say 873-bless" sent to MUD from "say @affitem"
loop iterates, %i = 4, loop exits
@aff = "" based on "#VAR aff "" " command
(13. neither trigger fires again until a new set of "Affected:" lines comes from the MUD)

Well, there you have it, the long and short way to write an Affected: trigger, both using lists and not. Remember, this is a SIMPLE list example, they get more involved from here, so if you need to refer back please do so often. And if the syntax is bugging you, read the zMud Manual posted on this site, it is how I learned to do ALL of this stuff!

:-)


Source: Zistrosk's zMud Page – Information from way back when...