Count instances of a word and Note that

Posted by Solara on Thu 27 Feb 2025 12:51 AM — 7 posts, 8,812 views.

USA #0
Game text: "A room with a grinning revenant, a fragile-looking revenant, two large revenants and a dried-out revenant."

I'd like to be able to get a count of the number of revenants and have it replace that long sentence with one simple text "5 revenants".

The game can have one, two, three, up to 6 of a mob with different descriptions.

I've been reading the boards and it seems Lua gmatch is the way to go, but I can't work it out with my very basic understanding of triggers.

I guess I would set a trigger to capture the text 'A room with', but not sure what else needs to be done.

Thanks!

Edit: okay I have this so far. Triggers on "^A room with (.+)"

Revsroom = "%1"

local count = 0
for word in string.gmatch (Revsroom, "revenant") do
count = count +1
end

Note (count.." revenants") 


I just need to figure out how to add to the count when it has the text 2 revenants or 3 revenants.

Adding this:
for word in string.gmatch (Revsroom, "two revenants") do
count = count +2
end 
doesn't seem to work. It seems capturing 'revenant' in 'revenants' counts as 1

Changed it to this and it works now:

for word in string.gmatch (Revsroom, "two") do
count = count +1
end 
Amended on Sat 01 Mar 2025 04:01 AM by Solara
Australia Forum Administrator #1
Hmm, this doesn't allow for "three" does it?

Can you post more examples of the sort of text you receive? (eg. other mobs, different numbers of them).
USA #2
Oh I have more entries for 3 and 4, I just didn't include it.

I figured it out reading the forum and looking at other examples. I figured I'd keep this post up for others if they had similar situations.'

The revenants have one adjective descriptions, so it'd be "a bleach-boned revenant", or "two stinking revenants".

The lizardman have 3 adjective descriptions, so it'd be "a smirking green-skinned male lizardman" or "two smirking green-skinned male lizardmans".

RoomLook = "%1"
local revcount = 0
local lizcount = 0

for word in string.gmatch (RoomLook, "revenant") do
revcount = revcount +1
end

for word in string.gmatch (RoomLook, "two .* revenants") do
revcount = revcount +1
end 

for word in string.gmatch (RoomLook, "three .* revenants") do
revcount = revcount +2
end

for word in string.gmatch (RoomLook, "four .* revenants") do
revcount = revcount +3
end

if revcount > 1 then
ColourNote ("yellow", "black", "-"..revcount.."- Revs")
elseif revcount == 1 then
ColourNote ("yellow", "black", "-"..revcount.."- Rev")
end

for word in string.gmatch (RoomLook, "lizardman") do
lizcount = lizcount +1
end

for word in string.gmatch (RoomLook, "two .* .* .* lizardmans") do
lizcount = lizcount +1
end 

for word in string.gmatch (RoomLook, "three .* .* .* lizardmans") do
lizcount = lizcount +2
end

for word in string.gmatch (RoomLook, "four .* .* .* lizardmans") do
lizcount = lizcount +3
end

if lizcount > 1 then
ColourNote ("yellow", "black", "-"..lizcount.."- Lizards")
elseif lizcount == 1 then
ColourNote ("yellow", "black", "-"..lizcount.."- Lizard")
end


There's probably a more elegant and concise way to do this?
Just curious how would I gmatch for "revenant" without also capturing "revenants" like it's currently doing. I'm compensating for that by reducing the count by 1 for the multiple two/three mobs as shown.
Amended on Sat 01 Mar 2025 06:37 AM by Solara
Australia Forum Administrator #3
You can make a table of words (eg. one, two, three) and use that for matching each one, like this:


numbers = {
  "one",
  "two",
  "three",
  "four",
  "five",    -- etc.
  }

for word in string.gmatch (RoomLook, "revenant%f[%A]") do
  revcount = revcount + 1
end

for num, word in ipairs (numbers) do
  for word in string.gmatch (RoomLook, word .. " .* revenants") do
    revcount = revcount + num
  end 
end -- for

Note (count .. " revenants") 


The use of "revenant%f[%A]" is a "frontier pattern" that matches the end of a word, so it matches revenant and not revenants.

Then if you are matching on other things (eg. lizardman) then the whole thing could go in a function and you pass in the word you are looking for (eg. revanant, lizardman). Or, have a table of them, and make an outer loop that gives you each word to look for, and the inner one would be the code above.
USA #4
Wow thanks Nick. So elegant
USA #5
Nick,

Using your more concise code with the table of number words, it didn't seem to work for this text from the game:

"A room with a dried-out revenant facing backwards, two fragile-looking revenants facing backwards, two menacing revenants facing backwards and a red-eyed revenant facing backwards."

It only returned 4 revenants. From what I can tell, the coding should work.
Australia Forum Administrator #6
Yes, there should be 6, not 4.

You need to amend the pattern and change .* to .- which makes the capture not greedy:


for num, word in ipairs (numbers) do
  for word in string.gmatch (RoomLook, word .. " .- revenants") do
    revcount = revcount + num
    print (word)
  end 
end -- for


Also, in my code I didn't zero out revcount, nor did I print revcount. This works and returns 6:


function countMobs (name, look)

  local count = 0

  local numbers = {
    "one",
    "two",
    "three",
    "four",
    "five",
    "six",
    "seven",
    "eight",
    "nine",
    -- etc.
    }

  for word in string.gmatch (look, name .. "%f[%A]") do
    count = count + 1
  end

  for num, word in ipairs (numbers) do
    for word in string.gmatch (look, word .. " .- " .. name .. "s") do
      count = count + num
    end 
  end -- for

  Note (count, " ", name, "(s)") 
end -- countMobs

RoomLook = "A room with a dried-out revenant facing backwards, two fragile-looking revenants facing backwards, two menacing revenants facing backwards and a red-eyed revenant facing backwards."

countMobs ("revenant", RoomLook)



This version moves the calculation into a function, then you pass it the thing you are looking for (revenant) and the room look.