[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Lua
. . -> [Subject]  ArrayList preserve order

ArrayList preserve order

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Posted by Morat   (18 posts)  [Biography] bio
Date Mon 14 Dec 2009 12:01 AM (UTC)

Amended on Sat 19 Dec 2009 08:01 PM (UTC) by Morat

Message
Is there a way to preserve the order of an ArrayList using Lua?

I do not want the ArrayList sorted.

I need to be able to display the ArrayList in the same order as they were added.

Here is the working script I am using:

* sample output (using FUSS SMAUG server)

 Average Xp   Total Xp   #   Death List, R.I.P. 
------------------------------------------------
          3          5   2   The gnoll
          4          7   2   The kobold
          3          5   2   The naga
          3          5   2   The minotaur
------------------------------------------------
         11         22       Death Count: 8


* trigger and alias

<triggers>
  <trigger
   enabled="y"
   group="death"
   match="^You receive (\d*) experience points\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>local mob, cnt, tot
if not ArrayExists("arrcnt") then ArrayCreate("arrcnt") end
if not ArrayExists("arrtot") then ArrayCreate("arrtot") end
mob = string.match(GetRecentLines(2), "(.*) is DEAD!!.*")
cnt = ArrayGet("arrcnt", mob)
tot = ArrayGet("arrtot", mob)
if cnt == nil then ArraySet("arrcnt", mob,  1) else ArraySet("arrcnt", mob, cnt +  1) end
if tot == nil then ArraySet("arrtot", mob, %1) else ArraySet("arrtot", mob, tot + %1) end</send>
  </trigger>
</triggers>


<aliases>
  <alias
   match="rip"
   enabled="y"
   group="death"
   send_to="12"
   sequence="100"
  >
  <send>local n = string.char(10)
local cnt, tot, avg
local cntsum = 0
local totsum = 0
local avgsum = 0
if ArrayExists("arrcnt") and ArrayExists("arrtot") then
  AnsiNote(n .. ANSI(33) .. " Average Xp   Total Xp   #   Death List, R.I.P. ")
  AnsiNote(     ANSI(37) .. "------------------------------------------------")
  for k, v in pairs(ArrayList("arrcnt")) do
    cnt = ArrayGet("arrcnt", k)
    tot = ArrayGet("arrtot", k)
    avg = tot / cnt
    cntsum = cnt + cntsum
    totsum = tot + totsum
    avgsum = avg + avgsum
    AnsiNote(string.format(ANSI(32) .. " %%10.0f %%10.0f %%3.0f   %s",
      avg, tot, cnt, k))
  end
  AnsiNote(ANSI(37) .. "----------------------")
  AnsiNote(string.format(ANSI(32) .. " %%10.0f %%10.0f      " .. ANSI(33) .. " Death Count: %d",
    avgsum, totsum, cntsum))
else
  ColourNote("tan", "", n .. "Error: death list is empty")
end</send>
  </alias>
</aliases>


I read the following in a Lua reference manual:

Quote:
There is no guarantee as to the order in which keys will be stored in a table when using dictionaries so the order of retrieval of keys using pairs() is not guaranteed.


Is there a better way to do this?

Will the ArrayList order still be random if I use another scripting language like VBScript?
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #1 on Mon 14 Dec 2009 12:09 AM (UTC)
Message
Why can't you use native Lua tables instead of MUSHclient's arrays? MUSH arrays have some useful benefits, but I don't see any particular point in this case.

(I'm going to look over what you posted, and hopefully post back with a converted version, but let me know if you have a specific reason!)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #2 on Mon 14 Dec 2009 12:26 AM (UTC)

Amended on Mon 14 Dec 2009 12:52 AM (UTC) by Twisol

Message
Here is a literal translation, ignoring the problem of keeping the list in order.


<triggers>
  <trigger
   enabled="y"
   group="death"
   match="^You receive (\d*) experience points\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>if not arrcnt then arrcnt = {} end
if not arrtot then arrtot = {} end

local mob = string.match(GetRecentLines(2), "(.*) is DEAD!!")

arrcnt[mob] = (arrcnt[mob] or 0) + 1
arrtot[mob] = (arrtot[mob] or 0) + %1</send>
  </trigger>
</triggers>



<aliases>
  <alias
   match="rip"
   enabled="y"
   group="death"
   send_to="12"
   sequence="100"
  >
  <send>local n = string.char(10)

if not arrcnt or not arrtot then
  ColourNote("tan", "", n .. "Error: death list is empty")
  return
end

AnsiNote(n .. ANSI(33) .. " Average Xp   Total Xp   #   Death List, R.I.P. ")
AnsiNote(     ANSI(37) .. "------------------------------------------------")

local cntsum, totsum, avgsum = 0, 0, 0
local cnt, tot, avg

for k, v in pairs(arrcnt) do
  cnt = v
  tot = arrtot[k]
  avg = tot / cnt

  cntsum = cnt + cntsum
  totsum = tot + totsum
  avgsum = avg + avgsum

  AnsiNote(string.format(ANSI(32) .. " %%10.0f %%10.0f %%3.0f   %s",
    avg, tot, cnt, k))
end

AnsiNote(ANSI(37) .. "----------------------")
AnsiNote(string.format(ANSI(32) .. " %%10.0f %%10.0f      " .. ANSI(33) .. " Death Count: %d",
  avgsum, totsum, cntsum))</send>
  </alias>
</aliases>


Next I'll change it to keep the table in order. If you could test this version to make sure it still works properly, that would probably help... I can't really ensure that it works on my end.

EDIT: Also, what are 'cnt' and 'tot' supposed to stand for? I assume 'count' and 'total'?

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #3 on Mon 14 Dec 2009 12:51 AM (UTC)
Message
Okay, here's an order-preserved version. I also cleaned the code up a little, such as merging the two arrays into one. The 'deathlist' table consists of subtables containing the mob details. Every time a NEW mob is added, it's (a) added with a numeric index, to maintain order, and (b) added by its name, to make it easy to access and update specific records. The ipairs() loop in the alias doesn't touch the string-indexed members, so it only loops over the ordered list of mobs.


Just to let you know, I changed the \d* to a \d+ because I'm a perfectionist :P.

<triggers>
  <trigger
   enabled="y"
   group="death"
   match="^You receive (\d+) experience points\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>if not deathlist then deathlist = {} end

local mobname = string.match(GetRecentLines(2), "(.*) is DEAD!!")

local record = deathlist[mobname]
if not record then
  record = {name = mobname, kills = 0, exp = 0}

  deathlist[mobname] = record
  table.insert(deathlist, record)
end

-- update the record
record.kills = record.kills + 1
record.exp = record.exp + %1</send>
  </trigger>
</triggers>



<aliases>
  <alias
   match="rip"
   enabled="y"
   group="death"
   send_to="12"
   sequence="100"
  >
  <send>local n = string.char(10)

if not deathlist then
  ColourNote("tan", "", n .. "Error: death list is empty")
  return
end

AnsiNote(n .. ANSI(33) .. " Average Xp   Total Xp   #   Death List, R.I.P. ")
AnsiNote(     ANSI(37) .. "------------------------------------------------")

local killsum, expsum, avgsum = 0, 0, 0

for _, mob in ipairs(deathlist) do
  avg = mob.exp / mob.kills

  killsum = mob.kills + killsum
  expsum = mob.exp + expsum
  avgsum = avg + avgsum

  AnsiNote(string.format(ANSI(32) .. " %%10.0f %%10.0f %%3.0f   %s",
    avg, mob.exp, mob.kills, mob.name))
end

AnsiNote(ANSI(37) .. "----------------------")
AnsiNote(string.format(ANSI(32) .. " %%10.0f %%10.0f      " .. ANSI(33) .. " Death Count: %d",
  avgsum, expsum, killsum))</send>
  </alias>
</aliases>

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Mon 14 Dec 2009 01:35 AM (UTC)
Message
Morat said:

Is there a way to preserve the order of an ArrayList using Lua?

I do not want the ArrayList sorted.

...

Is there a better way to do this?

Will the ArrayList order still be random if I use another scripting language like VBScript?


The Array functions were added before Lua was added to MUSHclient. You are strongly advised to use Lua instead, as it is much more flexible (eg. you can easily have nested tables). The Array functions store items keyed alphabetically, this cannot be changed (regardless of which language you use).

However using Lua tables, you can preserve the order by using table.insert to add items to a table, in which case they will be entered in sequence (in fact, in this case the keys are 1, 2, 3 ...).

In Lua you can also use table.sort to sort a numerically-keyed table into a different order (effectively, this reassigns the keys to change the order).

You can also make a copy of a table, so you can keep the original in one order, and have a copy in another order.

There is a forum post about copying tables:

Template:post=8042 Please see the forum thread: http://gammon.com.au/forum/?id=8042.


Twisol's post shows a combination of ideas, both using numeric keys to preserve order, and alpha keys so you can quickly find a particular item. You could split that idea into using two tables, but what he has shown keeps everything together.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Morat   (18 posts)  [Biography] bio
Date Reply #5 on Mon 14 Dec 2009 02:18 AM (UTC)
Message
Thanks Twisol! It works perfect.
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #6 on Mon 14 Dec 2009 02:19 AM (UTC)
Message
Woot! Glad to help.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Morat   (18 posts)  [Biography] bio
Date Reply #7 on Sat 19 Dec 2009 07:30 PM (UTC)

Amended on Sat 19 Dec 2009 07:52 PM (UTC) by Morat

Message
Here is my finished script for the forum :)~

* commands

rip ~ death list in order of kill
rip m ~ death list sorted by mob
rip c ~ death list sorted by count
rip t ~ death list sorted by total
rip a ~ death list sorted by average
ripremove ~ remove the last mob in order of kill
ripremove the giant lizard ~ remove the giant lizard
ripreset ~ reset the death list

* sample input

the dwarven worker is DEAD!!
You receive 90 experience points.

* sample output

 Average Xp   Total Xp   #   Death List, R.I.P. 
------------------------------------------------
         89      2,307  26   the dwarven worker
        407     10,588  26   the dwarven guard
        344      4,127  12   the giant lizard
        259      3,629  14   the barracks guard
         13        188  14   the dwarven miner
        416      3,331   8   the dwarven mine leader
----------------------
      1,529     24,170       Death Count: 100


* script (1 trigger, 3 aliases)

note: copy to clipboard, then file > import > clipboard

<triggers>
  <trigger
   enabled="y"
   group="death"
   match="^You receive (\d+) experience points\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>if not death then death = {} end
local m = string.match(GetRecentLines(2), "(.*) is DEAD!!")
local record = death[m]
if not record then
  record = {mob = m, count = 0, total = 0}
  death[m] = record
  table.insert(death, record)
end
record.count = record.count + 1
record.total = record.total + %1</send>
  </trigger>
</triggers>
<aliases>
  <alias
   match="^rip(?: (.*)){0,1}$"
   enabled="y"
   group="death"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>if not death then
  AnsiNote("\\n" .. ANSI(33) .. "Error: death list is empty")
else
  AnsiNote("\\n" .. ANSI(33) ..
    " Average Xp   Total Xp   #   Death List, R.I.P. ")
  AnsiNote(ANSI(37) .. string.rep("-", 48))
  local t = {}
  for k, v in pairs(death) do t[k] = v end
  if "%1" == "m" then
    table.sort(t, function(a, b) return (a.mob &lt; b.mob) end)
  elseif "%1" == "c" then
    table.sort(t, function(a, b) return (a.count &lt; b.count) end)
  elseif "%1" == "t" then
    table.sort(t, function(a, b) return (a.total &lt; b.total) end)
  elseif "%1" == "a" then
    table.sort(t, function(a, b)
      return (a.total / a.count &lt; b.total / b.count) end)
  end
  local sumcount, sumtotal, sumaverage = 0, 0, 0
  local function round(n) return math.floor(n + 0.5) end
  local function comma(n)
    local a = n
    local b
    while true do  
      a, b = string.gsub(a, "^(%d+)(%d%d%d)", "%%1,%%2")
      if b == 0 then break end
    end
    return a
  end
  for k, v in ipairs(t) do
    sumcount = sumcount + v.count
    sumtotal = sumtotal + v.total
    sumaverage = sumaverage + v.total / v.count
    AnsiNote(string.format(ANSI(32) .. " %%10s %%10s %%3d   %s",
      comma(round(v.total / v.count)), comma(v.total), v.count, v.mob))
  end
  AnsiNote(ANSI(37) .. string.rep("-", 22))
  AnsiNote(string.format(ANSI(32) .. " %%10s %%10s       " .. ANSI(33) ..
    "Death Count: %d", comma(round(sumaverage)), comma(sumtotal), sumcount))  
end</send>
  </alias>
  <alias
   match="^ripremove(?: (.*)){0,1}$"
   enabled="y"
   group="death"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>if not death then
  AnsiNote("\\n" .. ANSI(33) .. "Error: death list is empty")
else
  if "%1" == "" then
    death[death[#death].mob] = nil
    table.remove(death, #death)
  else
    death["%1"] = nil
    for k, v in ipairs(death) do
      if v.mob == "%1" then table.remove(death, k) end
    end
  end
  if #death == 0 then death = nil end
  Execute("rip")
end</send>
  </alias>
  <alias
   match="ripreset"
   enabled="y"
   group="death"
   send_to="12"
   sequence="100"
  >
  <send>death = nil
AnsiNote("\\n" .. ANSI(33) .. "Death List Reset")</send>
  </alias>
</aliases>


* test input

/death = {}
death["the dwarven worker"] = {mob = "the dwarven worker", count = 26, total = 2307}
death["the dwarven guard"] = {mob = "the dwarven guard", count = 26, total = 10588}
death["the giant lizard"] = {mob = "the giant lizard", count = 12, total = 4127}
death["the barracks guard"] = {mob = "the barracks guard", count = 14, total = 3629}
death["the dwarven miner"] = {mob = "the dwarven miner", count = 14, total = 188}
death["the dwarven mine leader"] = {mob = "the dwarven mine leader", count = 8, total = 3331}
table.insert(death, death["the dwarven worker"])
table.insert(death, death["the dwarven guard"])
table.insert(death, death["the giant lizard"])
table.insert(death, death["the barracks guard"])
table.insert(death, death["the dwarven miner"])
table.insert(death, death["the dwarven mine leader"])


Here is the same script in zMUD that I posted back in 2005:

http://forums.zuggsoft.com/forums/viewtopic.php?t=21358
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


22,385 views.

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]