Having trouble printing a table

Posted by Shinth on Fri 10 Aug 2007 06:57 AM — 16 posts, 60,910 views.

#0
Could someone tell me what is wrong with this function?

function change_priority(name,line,wildcards)
if (wildcards [1] == "health") then 
   healpriority = {}	
   healpriority = {health = "drink health", mana = "drink mana", ego = "drink bromide"}
end
if (wildcards [1] == "mana") then
   healpriority = {}
   Note ("mana")
   healpriority = {mana = "drink mana", health = "drink health", ego = "drink bromide"}
   for k, v in ipairs (healpriority) do 
   Tell (tostring(k),"=")
   Note (v)
   end -- for
end
if (wildcards [1] == "ego") then
   healpriority = {}
   healpriority = {ego = "drink bromide", health = "drink health", mana = "drink mana"}
end
ColourTell ("aqua","","Healing priority: ") ColourNote ("lime","", string.upper(wildcards [1]))
tprint (healpriority)
end

If I call the function with the wildcard "mana" (from an alias) and don't have tprint, the "for k,v in ipairs (healpriority)" loop doesn't do a thing, but it enters the if statement (that's why I put "Note" there, to check).
If I use tprint, no matter what wildcard I use, either health, mana or ego, it prints the same table

ego=drink bromide
mana=drink mana
health=drink health

I've been trying to see what is wrong with it for a couple of hours now, any suggestions?
Amended on Fri 10 Aug 2007 06:58 AM by Shinth
Australia Forum Administrator #1
ipairs is designed to only iterate over numeric keys. You want pairs, not ipairs.

Your keys are the strings "mana", "health" and "ego", which are not numbers.
Australia Forum Administrator #2
Quote:

healpriority = {}
healpriority = {health = "drink health", mana = "drink mana", ego = "drink bromide"}


The first line there is redundant. You are recreating the table in the second line, and thus you create an empty table that has to be garbage-collected eventually. It is like writing this:


a = 2
a = 3


Not much point in the first line.
USA #3
I'm not really entirely sure what the end result of this script would be, but here's how I would do what you have so far:

healqueues = { health = { "drink health", "drink mana", "drink bromide" },
               mana   = { "drink mana", "drink bromide", "drink health" },
	       ego    = { "drink bromide", "drink health", "drink mana" } }
function change_priority(name,line,wildcards)
  healpriority = healqueues[ wildcards[1] ]
  ColourNote ("aqua","","Healing priority: ",
              "lime","", string.upper(wildcards [1]) )
  for k,v in ipairs( healpriority ) do
    Note( k.."="..v )
  end
end

This will let you access the highest priority with healpriority[1], just like a normal list. Some sanity checking will need to be done for making sure that healqueues[wildcards[1]] exists and isn't nil unless you're absolutely positive that sane values will be sent into the script.

oh, and here's the output of a quick test:

Healing priority: MANA
1=drink mana
2=drink bromide
3=drink health
Healing priority: HEALTH
1=drink health
2=drink mana
3=drink bromide
Amended on Fri 10 Aug 2007 07:20 AM by Shaun Biggs
USA #4
Odd thought on sanity checking... no change to the healpriorty variable if there isn't a bad value is passed. This is assuming that you declared healpriority somewhere globally...

healqueues = { health = { "drink health", "drink mana", "drink bromide" },
               mana   = { "drink mana", "drink bromide", "drink health" },
	       ego    = { "drink bromide", "drink health", "drink mana" } }
healpriority = healqueues.health --make health the default

function change_priority(name,line,wildcards)
  healpriority = healqueues[ wildcards[1] ] or healpriority
  ColourNote ("aqua","","Healing priority: ",
              "lime","", string.upper(wildcards [1]) )
  for k,v in ipairs( healpriority ) do
    Note( k.."="..v )
  end
end

and the test data:

Healing priority: MANA
1=drink mana
2=drink bromide
3=drink health
Healing priority: WAFFLES
1=drink mana
2=drink bromide
3=drink health
#5
Thank you all for the help, I have decided to use an array instead of a table because I need it compared to the key of another table, but in a specific order. Another question, I've declared a table like this:

drinkqueue = {health=0;mana=0;ego=0}

So when I check the conditions to drink health:

drinkqueue.health="drink health"

I want to check if a key is 0, but for some reason v != 0 gives me the error:

Compile error
World: Lusternia
Immediate execution
[string "Script file"]:86: ')' expected near '!'
Error context in script:
  82 : tprint(drinkqueue)
  83 : if (todrink == 1) then
  84 : for i=1,3 do
  85 :   for k,v in pairs (drinkqueue) do
  86*:     if (healpriority[i] == k) and (v != 0) then

Any ideas what is wrong with it? And also, a break in the second for loop will stop the first loop too?
Amended on Fri 10 Aug 2007 09:18 PM by Nick Gammon
USA #6
Quote:
I have decided to use an array instead of a table because I need it compared to the key of another table, but in a specific order.
In Lua, there is no difference between an array and a table. An "array" is merely a table with a sequential set of keys. Being a table, there is no practical difference at all between {health = "drink health", mana = "drink mana", ego = "drink bromide"} and {mana = "drink mana", health = "drink health", ego = "drink bromide"}. They both have the same keys and the same values, and order does not matter terribly much, since there isn't a numerical set dictating any order.

Quote:
I want to check if a key is 0, but for some reason v != 0 gives me the error:

!= doesn't mean anything to a Lua interpreter. A nonequivalency is declared as ~= for this language.

If you would like to get a bit more familiar with Lua, this site is where I learned from: http://lua-users.org/wiki/TutorialDirectory I still use it quite frequently for reference.
Amended on Fri 10 Aug 2007 10:44 AM by Shaun Biggs
USA #7
Would you mind posting the whole function? I'm getting confused with how the line drinkqueue.health="drink health" relates to drinkqueue = {health=0;mana=0;ego=0}

Break will just break the most recent block of code, so it won't exit all of your loops, just the last one. Keep in mind that the counter in a for loop is destroyed once the for loop is finished, so if you plan on keeping the value, use a while loop instead.
#8
Thank you for the link, I will try the tutorials there as soon as I have time, they seem interesting.
And here is the function:

function system_drink ()
todrink=0
if (tonumber(GetVariable("health"))<math.floor(tonumber(GetVariable("MaxHealth"))*2/3)) then 
  drinkqueue.health="drink health"
  todrink=1
end -- if
if (tonumber(GetVariable("mana"))<math.floor(tonumber(GetVariable("MaxMana"))*1/4)) then 
  drinkqueue.mana="drink mana"
  todrink=1
end -- if
if (tonumber(GetVariable("ego"))<math.floor(tonumber(GetVariable("MaxEgo"))*1/3)) then 
  drinkqueue.ego="drink bromide"
  todrink=1
end -- if
i = 1
while (todrink == 1) and (i <= 3) do
  for k,v in pairs (drinkqueue) do
    if (healpriority[i] == k) and (v ~= 0) then
      Send (drinkqueue.v)
      SetVariable("elixbal","0.5")
      drinkqueue = {health = 0, mana = 0, ego = 0}
      todrink=0
      break
    end -- if
  end -- for
  i = i+1
end -- while
end -- function

What I am trying to do is a priority based autosipper, when both my mana and health are low, for example, what will drink first will be set by my healpriority table. Basically it checks if the first item, then the next, and so on, in the healpriority table is below a certain level by checking if there is a value, not equal to 0, assigned to that attribute in the drink queue. If so, it drinks, sets my balance flag to 0.5, that means that I am waiting to actually drink from a vial and exits. It also resets the drinkqueue, just to be sure it will always cure based on my priorities. There might be some flaws here, I haven't test it yet.
Edit: it keeps spelling italic, I don't know why.
Amended on Fri 10 Aug 2007 09:18 PM by Nick Gammon
USA #9

i = 1
while (todrink == 1) and (i <= 3) do
  for k,v in pairs (drinkqueue) do
    if (healpriority == k) and (v ~= 0) then
      Send (drinkqueue.v)
      SetVariable("elixbal","0.5")
      drinkqueue = {health = 0, mana = 0, ego = 0}
      todrink=0
      break
    end -- if
  end -- for
  i = i+1
end -- while

II'm not quite sure I understand the outer loop in this part. i is never used within the loop, so it will either do the loop once if you send a drink, or it will repeat three times if you don't send a drink (which is impossible due to you needing a drink in the first place.

I'm also still not clear on some of those variables. If the heal priority is the same as the function I posted above, then you should go through that array using ipairs to ensure the proper order, and match drinkqueue up with that.

The following block of code is how I would do the whole thing (both functions and supporting tables). Obviously, it's not the only way to do it, but this way you just have one loop to deal with testing and sending data. This will require changing the max health/mana/ego variables to lowercase, but if that would complicate things too much for other scripts, just change the keys/values for the various tables here. I tested this out with some dummy data, and it seemed to work. I've also taken advantage of quite a few scripting tricks to make the loop easier to deal with, such as concatenating the "max"..healpriority[i] have only one loop take care of all the data comparisons.

healstart = { health = 2/3, mana = 0.25, ego = 1/3 }
healaction = { health = "drink health", mana = "drink mana", ego = "drink bromide" }
healqueues = { health = { "health", "mana", "ego" },
               mana   = { "mana", "ego", "health" },
	       ego    = { "ego", "health", "mana" } }
healpriority = healqueues.health --make health the default

function change_priority(name,line,wildcards)
  healpriority = healqueues[ wildcards[1] ] or healpriority
  ColourNote ("aqua","","Healing priority: ",
              "lime","", string.upper(wildcards [1]) )
  for k,v in ipairs( healpriority ) do
    Note( k.."="..v.."  action = "..healaction[v] )
  end
end

function system_drink()
  i = 1
  repeat
    if tonumber( GetVariable( healpriority[i] ) ) <
      tonumber( GetVariable( "max"..healpriority[i] ) ) * healstart[healpriority[i]] then
      Send( healaction[healpriority[i]] )
      SetVariable( "elixbal", "0.5")
      i = 3
    end
    i = i + 1
  until i > 3
end

edit: Once again foiled by the forum eating my table brackets.
Amended on Fri 10 Aug 2007 09:26 PM by Nick Gammon
USA #10
if (tonumber(GetVariable("health"))<math.floor(tonumber(GetVariable("MaxHealth"))*2/3)) then

Two quick notes about these lines. First, you don't need to put the tonumber(GetVariable...) in parenthesis. Lua will recognize that fine as it is. Second, and the actual important one, is that there isn't really a point to using math.floor here unless you can somehow get 12.7 health or something like that. The test for 25 <= 25 is just the same as 25 < 25.12 and 26 <= 25 is just the same as 26 < 25.12.
Amended on Fri 10 Aug 2007 01:00 PM by Shaun Biggs
#11
Very nice scripting, it's faster and you can also configure when to drink (like at 3/4*health, instead of 2/3). One thing though, this will run every prompt line, and could slow me down. The todrink variable was to do into the loop only if you needed to drink. But the again, when you needed to heal, you execute more lines, so I guess I will use yours, it looks better. And thanks for the advice about math.floor too!

Edit: It works great. Thank you!
Amended on Fri 10 Aug 2007 03:25 PM by Shinth
USA #12
Quote:
One thing though, this will run every prompt line, and could slow me down. The todrink variable was to do into the loop only if you needed to drink.

The way you originally had it set up worked the same way as mine. You had todrink set with 3 if statements, where I have it set with a loop of one if statement up to three times. I figured you would have the "elixbal" variable checked to see if you drank a potion already, surrounding the whole thing with an if GetVariable( "elixbal" ) ~= "0.5" bit. Or you could turn off the trigger with EnableTrigger( "triggername", false ) and turn it back on with the trigger that catches you drinking the potion. The latter option is what I tend to do, but I go crazy and solve things by turning triggers on and off like strobe lights. For simple things that's fine, but it can get complicated very quickly.
#13
I check to see if I have balance in the prompt trigger. And at the drink message I check to see if I had actually sent the drink command by seeing if the elixbal is 0.5. If it is, then I enable a failsafe timer in case I cannot sip because of an affliction. Then when the actual effect message of the potion is sent I check to see if I drank, then make my balance 0. When the message that I can drink another potion is sent, I check to see if I was off balance and assign elixbal=1. Complicated, but it is a good protection against illusion.
Quote:

Or you could turn off the trigger with EnableTrigger( "triggername", false ) and turn it back on with the trigger that catches you drinking the potion. The latter option is what I tend to do, but I go crazy and solve things by turning triggers on and off like strobe lights.

I don't like having too much triggers active too, it slows you down and makes you vulnerable to illusions. Disabling the prompt trigger where I have set this function is a bad idea, because when my system will be finished I will have to check for about 10 types of balances and other afflictions, and turning it off and on isn't going to work.
Australia Forum Administrator #14
Quote:

Edit: it keeps spelling italic, I don't know why.


You are using [i] which means "make the text italic" to the forum. If you are posting text with square brackets, or backslashes in it, you should "escape" them by putting a backslash in front of them, like this: \[i\]

There is a function in MUSHclient to do that for you, which I always use when posting code here. This is what I do:

  • Copy the code I want to paste (Ctrl+C)
  • Select Edit menu -> Convert Clipboard Forum Codes (Shift+Ctrl+Alt+Q) - a small dialog box pops up showing how many were converted
  • Switch to the forum and paste (Ctrl+V)


This reliably fixes this problem with only a small amount of effort.

I am fixing up your current posts for you.
Amended on Fri 10 Aug 2007 11:04 PM by Nick Gammon
USA #15
Wow, that Convert Clipboard to Forum Codes option is nice. I didn't notice it until you pointed it out here. That will cut back a lot on my editing of posts due to dealing with brackets.