Word count in string/string manipulation question

Posted by Chaosmstr on Fri 05 Sep 2014 06:53 PM — 9 posts, 32,970 views.

#0
Mushclient 4.94 with LUA

So, I'm an old time Wintin user and it's time to move to something new, and Mushclient seems to offer quite a lot.

Having some growing pains trying to convert the programming language (Wintin is very Basic-like and easy)

I'm trying to make a trigger that will grab specific items from a corpse.

I'm on an old DIKU based game (mud.finalchallenge.net 4000 if your interested) and I get lists like this:

The corpse contains:
(Moderate magic) a sickle
(Moderate magic) a bright white potion with purple swirls
(Potent magic) a pair of hardened leather breeches
an axe
a mushroom
80 coins

I want the magic items and the gold (the gold has already been figured out, that's an easy one) while leaving the rest.

The problem comes in the form of the item names.
They can be 2-12 words long and the last word in the name isn't always a usable keyword.
For instance, typing 'get swirls' for the potion wont work, but bright, white, purple and potion are valid. Hardened won't work, but leather and breeches will.

In Wintin, my thought was to try and count the # of words in the name (if 6 words, 'get %5 corpse', if 2 words 'get %2 corpse') but I've hit programming limits with that.

Seems that
(Moderate magic) %1 %2 (no space after %2)
will catch the sickle ok, but on the breeches will turn %2 into the rest of the string ('pair of hardened leather breeches') which I cant use in the 'get %2 corpse' command (more than a single keyword).

But if I put a space after %2 then it won't recognize the sickle, as there is an EOL char that I cannot read/process.

So far all I have is:
<triggers>
<trigger
enabled="y"
match="(Moderate magic) *"
send_to="9"
sequence="1"
variable="modtxt"
>
<send>%1</send>
</trigger>
</triggers>

So, I have a string of varying length that I need to be able to process in some fashion to pull out a word or two that could be used as a keyword, ignoring obvious non-keyword words (a, the, pair, etc).

And the REALLY tricky part is that I could also find:
(Moderate magic) a pair of hardened leather pants
in the same corpse, meaning there is a HUGE list of keywords for these items.

So I know WHAT I want to do, I don't know to finagle the programs to do what I want.

Anyone have suggestions for a Mushclient newbie?


CM
Australia Forum Administrator #1
Fortunately you have Lua now, and you should love Lua. String manipulation is one of its strengths.

Your problem is pretty simple. Let's take your trigger and modify it a bit:


<triggers>
  <trigger
   enabled="y"
   match="(Moderate magic) *"
   send_to="12"
   sequence="1"
   variable="modtxt"
  >
  <send>
itemDescription = "%1"

-- get rid of multiple spaces
itemDescription = string.gsub (itemDescription, "%%s+", " ")

-- break up on spaces
itemNames = utils.split (itemDescription, " ")

require "tprint"

tprint (itemNames)
</send>
  </trigger>
</triggers>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.


I am scripting rather than sending to a variable.

The output of this first attempt is:


(Moderate magic) a sickle
1="a"
2="sickle"
(Moderate magic) a bright white potion with purple swirls
1="a"
2="bright"
3="white"
4="potion"
5="with"
6="purple"
7="swirls"


So you can see straight away that we have the individual words, as well as knowing how many there are.

A bit of work now with that table of words, and you can probably work out what to take. For example, discard "a" and "the". Then if you have more than one word, take the next one following a colour (eg. potion).

The number of items in the table is #tableName, in this case:


count = #itemNames



A slightly more complex example:


<triggers>
  <trigger
   enabled="y"
   match="(Moderate magic) *"
   send_to="12"
   sequence="1"
   variable="modtxt"
  >
  <send>

itemDescription = "%1"

-- get rid of multiple spaces
itemDescription = string.gsub (itemDescription, "%%s+", " ")

-- get rid of "a xxxx"
itemDescription = string.gsub (itemDescription, "^an? ", "")

-- get rid of "the xxxx"
itemDescription = string.gsub (itemDescription, "^the ", "")

-- break up on spaces
itemNames = utils.split (itemDescription, " ")

require "tprint"

-- debugging
tprint (itemNames)

print ("Number of words = ", #itemNames)

</send>
  </trigger>
</triggers>



Output:


(Moderate magic) a sickle
1="sickle"
Number of words =  1
(Moderate magic) a bright white potion with purple swirls
1="bright"
2="white"
3="potion"
4="with"
5="purple"
6="swirls"
Number of words =  6
Amended on Sat 06 Sep 2014 12:09 AM by Nick Gammon
Australia Forum Administrator #2
Modifying a bit more:


<triggers>
  <trigger
   enabled="y"
   match="(Moderate magic) *"
   send_to="12"
   sequence="1"
   variable="modtxt"
  >
  <send>

itemDescription = "%1"

-- get rid of multiple spaces
itemDescription = string.gsub (itemDescription, "%%s+", " ")

-- get rid of "a xxxx" or "an xxxx"
itemDescription = string.gsub (itemDescription, "^an? ", "")

-- get rid of "the xxxx"
itemDescription = string.gsub (itemDescription, "^the ", "")

-- get rid of "with purple swirls" or similar
itemDescription = string.gsub (itemDescription, " with .*$", "")

-- break up on spaces
itemNames = utils.split (itemDescription, " ")

-- debugging
require "tprint"
tprint (itemNames)
print ("Number of words = ", #itemNames)

-- pick up the last word (if there is one)
if #itemNames &gt; 0 then
  Send ("get " .. itemNames [#itemNames] )
end -- if

 </send>
  </trigger>
</triggers>



Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.


This discards " with xxx" from the description, and then assumes that the last word is the thing to pick up.

eg.


(Moderate magic) a sickle
1="sickle"
Number of words =  1
get sickle

(Moderate magic) a bright white potion with purple swirls
1="bright"
2="white"
3="potion"
Number of words =  3
get potion
Amended on Sat 06 Sep 2014 12:15 AM by Nick Gammon
Australia Forum Administrator #3
One more attempt. This time I put the noise words into a table, so you can just add others easily.


<triggers>
  <trigger
   enabled="y"
   match="(* magic) *"
   send_to="12"
   sequence="1"
   variable="modtxt"
  >
  <send>

itemDescription = "%2"

-- get rid of multiple spaces
itemDescription = string.gsub (itemDescription, "%%s+", " ")

-- get rid of "with purple swirls" or similar
itemDescription = string.gsub (itemDescription, " with .*$", "")

-- break up on spaces
itemNames = utils.split (itemDescription, " ")

-- make table of noise words (keyed by word)
noiseWords = { }
for k, v in ipairs { "a", "an", "the", "of", "pair" } do
  noiseWords [v] = true
end -- for

-- go through words, removing noise words
i = 1  -- first one
while i &lt;= #itemNames do
  -- if in noise words table, delete it
  if noiseWords [itemNames [i]] then
     table.remove (itemNames, i)
  else
    i = i + 1
  end -- if
end -- for

-- pick up the last word (if there is one)
if #itemNames &gt; 0 then
  Send ("get " .. itemNames [#itemNames] )
end -- if
 </send>
  </trigger>
</triggers>


This also has the type of magic as a wildcard (so now the item description is wildcard %2).

Output:


The corpse contains:
(Moderate magic) a sickle
get sickle
(Moderate magic) a bright white potion with purple swirls
get potion
(Potent magic) a pair of hardened leather breeches
get breeches
an axe
a mushroom
80 coins
#4
Nick, yer totally awesome.
I saw the first post and had some additional questions, but by the time I got done with my commute home, you had already answered them.
*laugh*

Thank you Sir!

CM
#5
I'm from the same realm as Chaosmstr. I tried this, but it's not pulling from the corpse only from the room/floor. Command for getting with in a corpse is, get item corpse. I had failed in trying to input 'corpse' into the line, so that it would pull from corpse.

V
#6
Hey Vic.. I had to change the get line as well.

if #itemNames &gt; 0 then
Send ("get " .. itemNames [#itemNames] .. " corpse")

I'm also working on adding in turning this on and off, so that I don't try to get stuff from a corpse that doesn't exist whenever I look into a bag.

Still experimenting with it, difficult to do when I have all the time in the world to program at work, but no connection to test any of it... and while at home, people want my attention instead of being lost in programming land.

BUT, that change above will at least change the get location.

CM
#7
Thanks CM, i was missing the ..
I just now started to use Mush, so getting used to it.

V
Australia Forum Administrator #8
You can test with the Game -> Test Trigger feature (Shift+Ctrl+F12).

Make sure you put a newline at each side of the text (Ctrl+Enter).

I just copied your examples and used that for my testing. Then you can test without even needing to connect (unless you want the response to work, of course).

To stop accidentally trying to get stuff from your inventory, try making the trigger disabled initially, and then match on:


The corpse contains:


When you get that line enable the trigger.

Template:function=EnableTrigger
EnableTrigger

The documentation for the EnableTrigger script function is available online. It is also in the MUSHclient help file.



Then disable it when you get something that is not from the corpse (eg. a prompt line).