Help me debug a function?

by Simimi

Back to Mechanic's Corner.

Simimi2007-07-19 01:13:37
2 steps forward, 3 steps back. I have everything working now except for my function to pass afflictions about the script. I have been working with it all day, and I thought I had it fixed once, but I just had a workaround that did nothing productive, and not a fix. This is the function it it's entirety.

CODE
--*Addaff
function addaff(aff,val)
  -- If it's not a valid affliction, exit function
  if not aff then
    return
  end

  -- Default val to true, if I forget to set it in a trigger
  val = val or true

--This is a snippet from Zarquan, use it or no?
  -- You can pass in a table of afflictions to save calls,
  -- or you can omit this as it's really just extra fun
  --if type(aff) == "table" then
    --for _,v in pairs(aff) do
      --addaff(v,val)
    --end
    --return
  --end

  -- This puts affs into the table affs = {}
  affs = val

  -- Consider adding queue checks/runs here!
end


I have the following alias which prints what is in the affs table: I think the if v == true thing is a bit rendundent since if it is true it is in the table, if it is not true it is nil, and not in the table, though I am hoping I can pass something like "curing" to the val, so that I can have a curing check to try and foil stupidity and such.
CODE
affs
SEND:
for k,v in pairs(affs) do
  if v == true then
    print(k)
  end
end


I have tried everything I can think of to get the trigger line for paralysis to pass into the function. I know I can add it into the table by doing:
CODE
affs.paralysis = true
affs = true

Notice the second one is what the function is supposed to do...

I want to pass the affliction to the function, and let the function add it to the table. The logic being I can just edit the function later when I think of something cool I would like (like a check for something...) to do, instead of having to edit each little trigger line.

For some reason, as of yet unknown to me, things like:
CODE
addaff(paralysis,true)
addaff(paralysis, true)
etc etc ad infinitum

do not work... I have tried the function as "addaff(aff, val)" and ass addaff(aff,val)" and nothing.

According to the function instructions found on the Lua users wiki, this looks syntactically correct, and it passed the compile, and does not return any errors or script errors in Lusternia...
Theomar2007-07-19 02:07:03
QUOTE(Simimi @ Jul 18 2007, 09:13 PM) 426845
2 steps forward, 3 steps back. I have everything working now except for my function to pass afflictions about the script. I have been working with it all day, and I thought I had it fixed once, but I just had a workaround that did nothing productive, and not a fix. This is the function it it's entirety.

CODE
--*Addaff
function addaff(aff,val)
  -- If it's not a valid affliction, exit function
  if not aff then
    return
  end

  -- Default val to true, if I forget to set it in a trigger
  val = val or true

--This is a snippet from Zarquan, use it or no?
  -- You can pass in a table of afflictions to save calls,
  -- or you can omit this as it's really just extra fun
  --if type(aff) == "table" then
    --for _,v in pairs(aff) do
      --addaff(v,val)
    --end
    --return
  --end

  -- This puts affs into the table affs = {}
  affs = val

  -- Consider adding queue checks/runs here!
end


In place of "affs = val" should be:
CODE
affs=val

The previous method was making a table entry with "aff" as the key, no matter what you were passing it.

I have the following alias which prints what is in the affs table: I think the if v == true thing is a bit rendundent since if it is true it is in the table, if it is not true it is nil, and not in the table, though I am hoping I can pass something like "curing" to the val, so that I can have a curing check to try and foil stupidity and such.
QUOTE

CODE
affs
SEND:
for k,v in pairs(affs) do
  if v == true then
    print(k)
  end
end

v == true is redundant and slows the processor, I believe.
All you need is:
CODE
if (v) then

That is a faster way of asking "If v is equal to true."
Similarly:
CODE
if (not v) then

means "if v is not equal to true."

QUOTE

CODE
I have tried everything I can think of to get the trigger line for paralysis to pass into the function. I know I can add it into the table by doing:
affs.paralysis = true
affs = true

Notice the second one is what the function is supposed to do...

I want to pass the affliction to the function, and let the function add it to the table. The logic being I can just edit the function later when I think of something cool I would like (like a check for something...) to do, instead of having to edit each little trigger line.

For some reason, as of yet unknown to me, things like:
CODE
addaff(paralysis,true)
addaff(paralysis, true)
etc etc ad infinitum

do not work... I have tried the function as "addaff(aff, val)" and ass addaff(aff,val)" and nothing.

According to the function instructions found on the Lua users wiki, this looks syntactically correct, and it passed the compile, and does not return any errors or script errors in Lusternia...


Also, in your example, "addaff(paralysis, true)" is trying to pass the contents of a variable called "paralysis."
CODE
addaff("paralysis", true)


Hppe this helps.
Simimi2007-07-19 04:47:52
That was immense help! I was able to fix it but now I can not make them nil with addaff. I suppose I should make a removeaff function for that?
CODE
addaff("paralysis",nil)

Is a no.
Unknown2007-07-19 08:15:14
QUOTE(Simimi @ Jul 19 2007, 06:47 AM) 426887
That was immense help! I was able to fix it but now I can not make them nil with addaff. I suppose I should make a removeaff function for that?
CODE
addaff("paralysis",nil)

Is a no.


Remember this piece of code in the addaff function:
CODE
val = val or true

Basically, you are setting val to true when val is either false or nil. By removing that code you should be able to pass nil, and it should remove the entry from the table.
On the other hand, a removeaff function will probably make your code more easily to read, when you use it. So you should probably create such a function wink.gif


In response to Theomar. (With whom I agree on all other accounts. )
CODE
if v == true then

is not the same as
CODE
if v then

This is basically due to how Lua deals with booleans. Nil and false are both considered false, all the other values are considered true.
So in the first case, the code in the if block would only be executed when v has the exact value true.
In the second case, the code in the if block would be executed when v has the value true, or contains a table or contains a string.

And really you should not be worried about any difference in speed between the two cases. Even if there is a speed difference, I wouldn't know which would be faster. Any way, it is more important to have code you can easily read and understand, then any tiny, not noticeable processor use difference.

In this specific case, it does not matter which of the two pieces of code is used, since the table will not contain anything except true. Either one of them will work.
Simimi2007-07-19 12:36:36
Ahh thank you so much!

Will I be able to (in the future) pass say... curing, as a value.
CODE
addaff("paralysis",curing)

The logic being something like; I want to be able to set a third value for certain afflictions, or maybe all of them, such that any one aff can be:
CODE
True: I have it
Nil I do not have it
Curing: The cure was sent, but the restore message has not triggered yet, if the restore message does not come in like .8 (or whatever) seconds, reset the value to True

The problem, in my mind, for the above, would be having to trigger every line to do more than one thing, which I seem to not be able to do. (like set the focus balance recovery line to do
CODE
addaff("paralysis",nil) and bals.focusbody = true
. Also having each trigger do more than one thing like this might be incredibly system-slowing in the long run.

That is my idea for handling things like stupidity. If I send a cure and it goes in but no restore comes soon enough, it goes back to true, assuming stupidity or a message caused it to fail. I am not sure if this is doable in practice, or as of yet how I would do it, or even if it is practical from a coding standpoint. But it sounds good on paper.
Unknown2007-07-19 14:10:02
You could certainly use "curing" as a value for the affliction, but again be sure that you put the quotes around it and treat it as a string or else you're telling Lua to use the value of the curing variable (which may or may not exist).

Doing multiple things in a trigger is very simple, so I'm not sure why you'd have problems with it. Just put multiple lines in your trigger code. If it's something you do in many places, you can generalize it with a function and keep the repetitiveness down to a minimum.
Simimi2007-07-19 16:30:13
CODE
val = val or true or nil

does not seem to work, but if I comment it out, I can just pass nil or true as needed with the same function, though I may add a removeaff function if I it would be more speed/resource effecient?


QUOTE(Zarquan)
You could certainly use "curing" as a value for the affliction, but again be sure that you put the quotes around it and treat it as a string or else you're telling Lua to use the value of the curing variable (which may or may not exist).

So I would be passing this as something like:
CODE
addaff("Paralysis","curing")

QUOTE(Zarquan)
Doing multiple things in a trigger is very simple, so I'm not sure why you'd have problems with it.

I figured it out, improper syntax and an incorrect use of the "and" operator. XD

Might as well keep this going and not waste another thread. I have 3 things left to do before I can start adding in lines and sparring with a purpose. Feel free to contribute to any of these if you want to/feel you can... In no order of importance.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1)Queues.
I am considering having queues to scan through my table of affs that I have, compare them to a static phpTable of affs based on their cure (herb_affs, salve_affs, purgative_affs etc etc) to determine priority, and heal as needed. My current ideal looks something like this:
CODE
function herb_curing()
  if not bals.herb then <-if I do not have herb balance, stop
    return
  end

  if affs.aeon or affs.sap then <-If I have Aeon or Sap, stop
    return
  end                      

  for aff,cure in herb_affs:pairs() do
    if affs then <-if I have an aff in my aff table
      if (string.find(cure, "eat") and able_eat()) or <-if the cure is eat, and I can eat
         (string.find(cure, "apply") and able_apply()) or <-if the cure is apply
         (string.find(cure, "smoke") and able_smoke()) then <-if the cure is smoke
        Execute(cure) <-do the cure
        break
      end
    end
  end
end

That was a snippit shown to me by Zarquan, and I modified it to fit my system variables and such, this is my current idea for an able_eat function:
CODE
function able_eat()
  if affs or <-if you have these affs you can not eat duh
  if affs or
  if affs or
  if affs or
  if not bals <-if you do not have herb balance, as in the balance table
    return nil <-return nil since you can't eat anyway
  else
    return <- or return.. I suppose it should return something, like true?
  end
end

I have not tested any of this yet (since I am still looking up when I want to fire the curing aliases (I'm thinking firing them all on each prompt, or putting all the healing functions into one superfunction called Cure() that runs the other curing checks on a prompt... or some such). Basically the function able_eat is supposed to return that I can not eat if the affs or no balance conditions are met, and return that I can eat if the opposite is true. I have a similar queue and check function for smoking/eating/applying and I will be making one if I find something else, like maybe one for trueheal or something.

These are the format of the static tables I mentioned above:
CODE
herb_affs = "smoke coltsfoot"
salve_affs = "apply melancholic to chest"

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

2) Autosipper
My autosipper worked just fine when it was inside of a trigger box, when I moved it to the script and set it to trigger on an alias (when testing it) it gives errors about trying to compare a number to a string. I see where and why this is a problem, but it was not a problem before since the variable contained a value. It looks like this, currently.
CODE
function autosipper ()
if bals.potion == true then
    end
    if var.health <= (.50 * var.maxhealth) then
        Send ("drink health")
    --elseif --really bad deepwounds stuffs here, no idea what the lines and such for those are...or how to handle them, or what deepwounds --are even...
        --Send ("apply health to --wounded spot")
    elseif var.mana <= (.50 * var.maxmana) then
        Send ("drink mana")
    elseif var.ego <= (.50 * var.maxego) then
        Send ("drink bromide")
    --elseif --more deepwounds stuff once I learn about it
        --Send ("deepwounds cure stuffs")
    elseif var.health <= (.65 * var.maxhealth) then
        Send ("drink health")
    elseif var.mana <= (.65 * var.maxmana) then
        Send ("drink mana")
    elseif var.ego <= (.65 * var.maxego) then
        Send ("drink bromide")
    --elseif .. --less bad deepwounds stuffs here
        --Send ("lessbad deepwounds stuffs")
    elseif var.health <= (.80 * "var.maxhealth") then
        Send ("drink health")
    elseif var.mana <= (.80 * var.maxmana) then
        Send ("drink mana")
    elseif var.ego <= (.80 * var.maxego) then
        Send ("drink bromide")
    end
        
end;

I see why it is incorrect but not how I should go about remedying it. I do not understand, because it worked before when it was just the first 3 ifs.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

3)Random Programming Questions
These are just random questions I have that I have not found conclusive answers for when googling.

a) The more triggers trigger functions in the script to handle their data, the over all performance of the system should be smoother/faster, correct? Thus the more I let functions handle things internally, the more speed/resource efficient the script would be?

cool.gif I have many things that are simply passed to a storage table, like defenses. Over time, as I add defenses for other classes, would it be more speed/resource efficient to just let the same one adddef function handle every line, as with addaff, or would it better in terms of efficiency to just let each trigger pass the value right to the table?

c) Addaff can both add and remove afflictions from the table; Is it more speed/resource efficient to let the one function handle both, or to have 2 functions to handle the adding and then the removing individually?
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Thanks for everything, it is much appreciated and will be re payed in time.
Unknown2007-07-19 17:49:44
QUOTE(Simimi @ Jul 19 2007, 06:30 PM) 426975
So I would be passing this as something like:
CODE
addaff("Paralysis","curing")


I think this already works. But you should change you alias, so that is shows these kinds of values.
So the affs alias should probably look like:
CODE
affs
SEND:
for k,v in pairs(affs) do
    print(k,v)
end


Well, how I would do able_eat:
CODE
function able_eat()
  if affs or -- if you have these affs you can not eat duh
     affs or
     affs or
     affs or
     (not bals )
  then
    return nil -- ( nil can be removed, since just "return" is the same as "return nil"
  else
    return true -- I think you should return true   ;)
  end
end


QUOTE

a) The more triggers trigger functions in the script to handle their data, the over all performance of the system should be smoother/faster, correct? Thus the more I let functions handle things internally, the more speed/resource efficient the script would be?
I am not sure what you are asking here. Putting multiple lines in the trigger declaration or in the methods that are called by the trigger does not really change anything. (As far as I know. No knowledge of mushclient, disclaimer)

QUOTE

I have many things that are simply passed to a storage table, like defenses. Over time, as I add defenses for other classes, would it be more speed/resource efficient to just let the same one adddef function handle every line, as with addaff, or would it better in terms of efficiency to just let each trigger pass the value right to the table?

I don't think it will matter, in speed. The main advantage in the use of an addaff method, instead of changing a table directly is that you can easily add additional checking or extra functionality later on.
Let's say, that at some point later on you want to check whether you actually entered the command to get the defense, or else assume it is an illusion. If you used adddef in every trigger then it would be a simple change to adddef. However if you changed the table in every trigger manually, then you would have to change every trigger and add the check there.

QUOTE
c) Addaff can both add and remove afflictions from the table; Is it more speed/resource efficient to let the one function handle both, or to have 2 functions to handle the adding and then the removing individually?

It doesn't matter for efficiency. However it will have an impact on readability.
When you use addaff("paralysis", nil) to remove the affliction, then addaff("paralysis") will also remove the affliction.
However, when such code is somewhere hidden between a lot of lines of code, it will not be easy to spot, that actually the affliction is removed:
CODE
addaff("paralysis")

Using an afflictions.remove("paralysis") is in my opinion far more readable, and it will make your software more reliable.
(Or at least change the name addaff to setaff wink.gif )


PS. When I say, it doesn't matter for efficiency, I always mean that it will not be noticeable. Readability and understandability and reliability are far more important.
Simimi2007-07-19 21:26:43
I totally replied to this once already...
QUOTE(Kpdath)
I am not sure what you are asking here. Putting multiple lines in the trigger declaration or in the methods that are called by the trigger does not really change anything. (As far as I know. No knowledge of mushclient, disclaimer)

I was saying like... The more I have triggers catch and do functions inside the script, the better off the system will be in the long run, right? The more that is handled with scriptside functions, the better?
Theomar2007-07-19 21:47:27
QUOTE(Kodath @ Jul 19 2007, 04:15 AM) 426901
In response to Theomar. (With whom I agree on all other accounts. )
CODE
if v == true then

is not the same as
CODE
if v then

This is basically due to how Lua deals with booleans. Nil and false are both considered false, all the other values are considered true.
So in the first case, the code in the if block would only be executed when v has the exact value true.
In the second case, the code in the if block would be executed when v has the value true, or contains a table or contains a string.

And really you should not be worried about any difference in speed between the two cases. Even if there is a speed difference, I wouldn't know which would be faster. Any way, it is more important to have code you can easily read and understand, then any tiny, not noticeable processor use difference.

In this specific case, it does not matter which of the two pieces of code is used, since the table will not contain anything except true. Either one of them will work.


Yeah, the only reason I said that they were the same was due to the context.

@Simimi:
Really, to help speed up the processing of your computer (it doesn't matter as much with faster computers), any script function should be contained in a script file. The only real script calling triggers should be doing is to pass necessary arguments to functions. It speeds up the process because when everything is contained in a script file, it is compiled once. But, each time the trigger fires, it has to recompile.

Anyways, that's how I do it. I save as much processor speed as I can by doing everything in a file. The only real programming I have in triggers is mainly exceptions, such as how to handle salve balance when apply regeneration.
Simimi2007-07-20 03:02:24
QUOTE(Kodath)
CODE
function able_eat()
  if affs or -- if you have these affs you can not eat duh
     affs or
     affs or
     affs or
     (not bals )
  then
    return nil -- ( nil can be removed, since just "return" is the same as "return nil"
  else
    return true -- I think you should return true  ;)
  end
end


I get an error:
CODE
Compile error
World: Lusternia
Immediate Execution
:210: unexpected symbol near 'if'

When I use the above... do I need a special symbol at the end of the first if line to tell Lua the code continues to the next line?
Unknown2007-07-20 19:57:58
QUOTE(Simimi @ Jul 20 2007, 05:02 AM) 427161
I get an error:
CODE
Compile error
World: Lusternia
Immediate Execution
:210: unexpected symbol near 'if'

When I use the above... do I need a special symbol at the end of the first if line to tell Lua the code continues to the next line?


I have tried the code as shown, and it works for me (at least when I also created the bals and affs tables).
I am not sure why it doesn't work for you.
I don't need any special symbols. However, you might want try to remove all the comments or just putting all the conditions on one line.

Are you sure the problem is caused by this code, and not by the place where it is, in the script file. Maybe there is something wrong just before line 210...
Simimi2007-07-20 20:23:46
I got an error, then I recompiled the script and loaded it in MUSH and it was fine... so it works wonderfully, I just need to test the actual cureing loops and such once I get those finished to see if it will work.
Simimi2007-07-22 19:13:01
Why is it the autosipper is not wanting to work? It worked as a trigger SEND: but not as a script? It says I am trying to compare a number to a string, but the value of said string is a number...
EDIT: Well, I added quotes to the variables so now it looks like this;
CODE
function autosipper ()
if bals.potion == true then
    end
    if var.health <= (.50 * "var.maxhealth") then
        Send ("drink health")
    --elseif --really bad deepwounds stuffs here, no idea what the lines and such for those are...or how to handle them, or what deepwounds --are even...
        --Send ("apply health to --wounded spot")
    elseif var.mana <= (.50 * "var.maxmana") then
        Send ("drink mana")
    elseif var.ego <= (.50 * "var.maxego") then
        Send ("drink bromide")
    --elseif --more deepwounds stuff once I learn about it
        --Send ("deepwounds cure stuffs")
    elseif var.health <= (.65 * "var.maxhealth") then
        Send ("drink health")
    elseif var.mana <= (.65 * "var.maxmana") then
        Send ("drink mana")
    elseif var.ego <= (.65 * "var.maxego") then
        Send ("drink bromide")
    --elseif .. --less bad deepwounds stuffs here
        --Send ("lessbad deepwounds stuffs")
    elseif var.health <= (.80 * "var.maxhealth") then
        Send ("drink health")
    elseif var.mana <= (.80 * "var.maxmana") then
        Send ("drink mana")
    elseif var.ego <= (.80 * "var.maxego") then
        Send ("drink bromide")
    end
        
end;


And now I am returned this error from MUSH when I try to run it via an alias;
CODE
:128: attempt to perform arithmetic on a string value
stack traceback:
    :128: in function 'autosipper'
    :1: in main chunk

But the string value is a number... Back to the Drawing Board!
Arundor2007-07-22 19:38:09
Try this:

CODE
tonumber(var.whatever)
Simimi2007-07-22 19:45:14
So you mean like; ?
CODE
if var.health <= (.50 * tonumber (var.maxhealth)) then
Arundor2007-07-22 19:48:37
Yes, that should do it.

EDIT: Just tested myself, so turns out it should actually be like this:

CODE
if tonumber(var.health) <= (.50 * tonumber (var.maxhealth)) then
Simimi2007-07-22 20:32:33
QUOTE(Arundor @ Jul 22 2007, 02:48 PM) 427704
Yes, that should do it.

EDIT: Just tested myself, so turns out it should actually be like this:

CODE
if tonumber(var.health) <= (.50 * tonumber (var.maxhealth)) then


Well... I got it to work, except that it CHUGS vials... I chugged through 23 vials before I noticed it was doing it (was coding the system in anothr widow >_<)

I tried to stop it with;
CODE
function autosipper ()
if bals.potion == false then
  return
end
if bals.potion == true then
  end
    if tonumber(var.health) <= (.50 * tonumber (var.maxhealth)) then


EDIT1:But no... it still chugs.

EDIT2: and now it has stopped drinking all together... hrm.
Unknown2007-07-23 01:49:27
You need to set bals.potion to false (or some intermediate value) as soon as you send the command to do the drinking. That's how you keep it from looping. If you want for the trigger to set the flag, you could loop a few times and oversip.
Simimi2007-07-23 12:58:47
QUOTE(Zarquan @ Jul 22 2007, 08:49 PM) 427793
You need to set bals.potion to false (or some intermediate value) as soon as you send the command to do the drinking. That's how you keep it from looping. If you want for the trigger to set the flag, you could loop a few times and oversip.


Well the drink lines trigger it to false, but now it won't sip at all...
CODE
--*AUTOSIPPER
function autosipper ()
if bals.potion == false then
  return
end
if bals.potion == true then
  end
    if tonumber(var.health) <= (.50 * tonumber (var.maxhealth)) then
        Send ("drink health")
    --elseif --really bad deepwounds stuffs here, no idea what the lines and such for those are...or how to handle them, or what deepwounds --are even...
        --Send ("apply health to --wounded spot")
    elseif tonumber (var.mana) <= (.50 * tonumber (var.maxmana)) then
        Send ("drink mana")
    elseif tonumber (var.ego) <= (.50 * tonumber (var.maxego)) then
        Send ("drink bromide")
    --elseif --more deepwounds stuff once I learn about it
        --Send ("deepwounds cure stuffs")
    elseif tonumber (var.health) <= (.65 * tonumber (var.maxhealth)) then
        Send ("drink health")
    elseif tonumber (var.mana) <= (.65 * tonumber (var.maxmana)) then
        Send ("drink mana")
    elseif tonumber (var.ego) <= (.65 * tonumber (var.maxego)) then
        Send ("drink bromide")
    --elseif .. --less bad deepwounds stuffs here
        --Send ("lessbad deepwounds stuffs")
    elseif tonumber (var.health) <= (.80 * tonumber (var.maxhealth)) then
        Send ("drink health")
    elseif tonumber (var.mana) <= (.80 * tonumber (var.maxmana)) then
        Send ("drink mana")
    elseif tonumber (var.ego) <= (.80 *  tonumber (var.maxego)) then
        Send ("drink bromide")
    elseif tonumber (var.health) <= (.90 * tonumber (var.maxhealth)) then
        Send ("drink health")
    elseif tonumber (var.mana) <= (.90 * tonumber (var.maxmana)) then
        Send ("drink mana")
    elseif tonumber (var.ego) <= (.90 * tonumber (var.maxego)) then
        Send ("drink bromide")    
    end
        
end;


I tried it with a nil value, and if equals false. I also tied having the if true line be an elseif and nothing. Before it would sip, now it will not... and I changed nothing.