Register to post in forums, or Log in to your existing account
 

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » CMUD General Discussion
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Jan 06, 2008 8:58 am   

Hash Set Function
 
I love the new Lua ability to use hash tables in variables. What is a hash table? Let me explain.

Most of us know about using database variables to store a key value pair like so.

#addkey variable key value

Then we can retrieve it using one of the following.

@variable.key or %db(@variable,key)

And we can set them using the following.variable.key=value or by just using #addkey again to recreate the value

#addkey variable key newvalue.
Wonderful, but what if you wanted to store more than a single value in a single key?
It could be done but it was usually messy and it normally consisted of using %word or %item to separate out a string.

An example of this is the following from the Generic queue thread.

Skill Name=Priority Number, Balance Requirements [string List], Uses these balances [and thus removes them] [string list], Gives these balances [string list], Keep this item as a priority until it is confirmed [0 or 1] <-<< This last one is not fully implemented and I suggest that you leave it as 0

Example
Vitality=P-90,R-A|e|G|N|S|V|x,U-e|V,G-,C-0

Skill Name: Vitality
Priority Number: 90
Balance Requirements: A|e|G|N|S|V|x
Uses these balances: e|V
Gives these Balances: None
Keep on list:0

Checking and changing all of these values is a real pain. Not to mention understanding them when you look at the code.

Fang suggested using hash tables instead.

Code:

PQueueData.vitality = { priority    = 90 ,
                        requires    =   {   equilibrium     = true,
                                            balance         = true,
                                            health          = true,
                                            mana            = true,
                                            awake           = true,
                                            standing        = true,
                                            vita_balance    = true,
                                        },
                        takes       =   {   equilibrium     = true,
                                            vita_balance    = true
                                        },
                        gives       =   {},
                        command = "vitality"
                      }


This is wonderful, but it is in lua. How do we get it into a zscript variable?

The following alias does this.
Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<cmud>
  <alias name="forFang" language="Lua">
    <value>vitality = {
                               priority = 122 ,
                               requires = {
                                                 equilibrium = true,
                                                 balance = true,
                                                 health = true,
                                                 mana = true,
                                                 awake = true,
                                                 standing = true,
                                                 vita_balance = true,
                                              },
                               takes = {
                                              equilibrium = true,
                                             vita_balance = true
                                           },
                               gives = {},
                               command = 'vitality'
                             }
zs.cmd.addkey("PQueueData","vitality",vitality)</value>
  </alias>
</cmud>


Now that the PQueueData variable contains the hash table we can look at its values very easily using the dot notation.

#show @PQueueData.vitality.command
displays
vitality

#show @PQueueData.vitality.priority
displays
122

Great I say but what if I want to change the value of priority to 90? Note that Fang changed it to 122 on us.
Well, sadly the dot notation does not work to change hash tables. Nor does #addkey. Try it and see.

So on that note I decided to learn a bit more Lua and make my own function for doing just that.
Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<cmud>
  <func name="HashSet" language="Lua">
    <value>local nparams=zs.numparam
if nparams &lt; 3 then
  print("HashSet requires at least three arguments. \n  Arg1:=varName Agr2:=key Agr3:=value")
  do return end
end
var=zs.param("1")
tvar=zs.var[var]
if zs.func.vartype(var) == -1 then
  zs.cmd.var(var,{})
  zs.func.vartype(var,5)
  tvar=zs.var[var]
elseif not (vart == 5) then
  zs.func.vartype(var,5)
  tvar=zs.var[var]
end
local  key, val, tstring, vtype
local list = {}
for i=2,nparams do
  if i==nparams then
    val=zs.param(i)
  elseif (i==(nparams-1)) then
    key=zs.param(i)
  else
    list[(i-1)]=zs.param(i)
  end
end
tstring="tvar"
for i,v in ipairs(list) do
  tstring= tstring .. "['" .. v .. "']"
  local func = assert(loadstring("return " .. "type(" .. tstring .. ")"))
  vtype=func()
  if vtype == 'table' then
      -- tstring leads to a table.
  else
      -- tstring does Not lead to a table, one must be crerated.
      local func1 = assert(loadstring( tstring .. "={}"))
      func1()
      -- For a hash table to remain it must contain at least two values.
      -- Creating table key for second value.
      local func2 = assert(loadstring( tstring .. "['table']=1"))
      func2()
  end
end
if val == 'nil' then
-- Pass the value of nil to delete a key from the hash
val="=nil"
else
val="='" .. val .. "'"
end
local func3 = assert(loadstring(tstring.."['".. key .. "']" .. val))
func3()
zs.var[var]=tvar</value>
  </func>
</cmud>

This is a Lua function but you do not have to know any Lua to use it.

The syntax is as follows.
#call @HashSet("varname","key","key","key","value")
You can use from one to as many keys as you like. If you do not use at least one key it prints an error to the screen.
Quote:
HashSet requires at least three arguments.
Arg1:=varName Agr2:=key Agr3:=value

Now if I want to alter the value of the priority key above I would use the following.
#call @HashSet(PQueueData,vitality,priority,90)

#show @PQueueData.vitality.priority
displays
90

If I wanted to get rid of the priority key altogether I would send a nil value at the end.

#call @HashSet(PQueueData,vitality,priority,nil)
This is roughly equivilant to the way you would expect #delkey to work. Which it doesn't on hash tables. Of course if you are using a standard Data record #delkey still works just fine.

HashSet will create a variable if one by the given name does not already exist.
HashSet will create a key if one does not already exist.

Example, the variable child does not exist.
#call @HashSet(child,girl,hair,blonde)
Creates a variable containing the following has table.
Code:

child = {
             girl = {
                        hair=blonde
                        table=1
                      }
           }

That you can read in the following manner.
#show @child.girl.hair
displays
blonde

#show @child.girl.table
displays
1

In order for a hash to remain a hash each sub layer must contain two values. So each time @HashSet creates a new sub layer that last is automatically given the value of table=1.

Now let's give the girl a dress.

#call @HashSet(child,girl,wears,dress)
Code:

child = {
             girl = {
                        hair=blonde
                        table=1
                        wears=dress
                      }
           }


For a boy with brown hair that wears blue jeans and a shirt we could do this.

#call @HashSet(child,boy,hair,brown)
#call @HashSet(child,boy,wears,legs,"blue jeans")
#call @HashSet(child,boy,wears,torso,shirt)
Code:

child = {
             girl = {
                        hair=blonde
                        table=1
                        wears=dress
                      }
            boy = {
                       hair=brown
                       table=1
                       wears=  {
                                      table=1
                                      torso=shirt
                                      legs="blue jeans"
                                    }
                      }
           }

Anyway I hope everyone enjoys the new toy. Twisted Evil
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Jan 06, 2008 7:58 pm   
 
Please note that the &lt; is converted to a < when placed within code tags. And that makes copying and pasting the xml not work. If you want to try out this function I added it to the package library.

Edit: Taz fixed the above code block. But I have updated the code several times since the above post. For the latest, [and faster] code download the package from the library.


Last edited by Arminas on Mon Jan 07, 2008 10:30 am; edited 1 time in total
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Mon Jan 07, 2008 3:26 am   
 
Some things that struck me, large and small, while reading your code:

Line 4, you use "do return end" which is only needed if the return doesn't immediately preceed an "end". Since it does in this case, all you need is a return. Line 12, you reference a variable vart that you haven't defined. Your structure from lines 18-26 could probably be simplified because by this point you definitely have at least a key and a value. I was thinking something like:

Code:
val = zs.param(nparam)
key = zs.param(nparam-1)
if nparam > 3 then
    for i=2,nparams-2 do
        list[(i-1)]=zs.param(i)
    end
end


Lines 29-31, I'm not sure why you're bothering to check the value of tvar since it was made into a data record variable in lines 8-15. Also, I'm sure there's a better way to do that than with loadstring (which is obviously very slow), but I can't think of it now. Since you're going to be calling your loadstringed functions immediately and then never calling them again, you might find it simpler to replace lines 30-32 with if assert(loadstring("return " .. "type(" .. tstring .. ")"))() == "table" then and similarly on lines 36-37, 40-41 and 50-51. I'm not sure which I prefer since all the brackets jammed together can be a bit confusing.

I'm a bit perplexed as well why the hashes only work if they have two or more keys. Makes me wonder if something untoward is happening behind the scenes.

All in all, though, an excellent tool for those who can't, won't, or simply haven't learnt Lua. All hail nestable hashes.
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Dharkael
Enchanter


Joined: 05 Mar 2003
Posts: 593
Location: Canada

PostPosted: Mon Jan 07, 2008 7:29 am   
 
The reason why the hashes need 2 or more keys is this:
If in a record-variable you have the key-value pair
Key:girl
Value:hair=blond

The algorithm that is used to translate this to a lua value has no way of knowing if the value represents a string literal or an embeded record-variable which should be translated as a table
with the key-value pair
Key:girl
Value:hair=blond|wears=dress

It assumes that this is an embedded record-variable and translates this to a hash
This would be a problem if you wanted the value of the last example to be a string literal.
_________________
-Dharkael-
"No matter how subtle the wizard, a knife between the shoulder blades will seriously cramp his style."
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Mon Jan 07, 2008 10:23 am   
 
Thanks Fang, ok so I went over the issues you mentioned.

1. do return end line 4.
--Cmud throws a Lua error if I remove the end.

2. Undeclared variable check at line 12.
--Declared it. You are correct it was an oversight on my part. This should speed up the function.

3. Lines 18-26
--Made the change. Thanks. This should speed up the function.

4. Lines 29-31
--I am checking to see if the current Level of the hash is a lua table.
--Before I was setting the Zscript variable to a database type if it was not one. And creating the variable if it does not exist.
--If it is already a table and I set it as one I will delete any of the nested contents.
--If it does not exist or it is some other type than a table Cmud throws a Lua error.

5. Loadstring and functions.
--Had a heck of a time with the functions but now I only have two functions that I use repeatedly instead of building and trashing new ones.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
cazador
Apprentice


Joined: 08 Dec 2005
Posts: 108

PostPosted: Wed Jan 30, 2008 11:49 pm   
 
Arminas wrote:
Please note that the < is converted to a < when placed within code tags. And that makes copying and pasting the xml not work. If you want to try out this function I added it to the package library.

Edit: Taz fixed the above code block. But I have updated the code several times since the above post. For the latest, [and faster] code download the package from the library.


I tried to download from the library, and looked under your name. I didn't find it. Is it still in the library? And what is the name of the package?
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Thu Jan 31, 2008 3:04 am   
 
Actually... I just looked and it is NOT still in the library Exclamation Confused

Unfortunately I don't have a clean copy of it that I can upload tonight. I've been monkeying with it and I don't have time to fix it. I have an old copy and one that doesn't work. I had the latest working copy online Or thought I did. Shocked

The one that is posted above does work so I guess use it until I get the new one back into the library.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Fri Feb 01, 2008 4:14 pm   
 
The latest version is now in the Library for download. Sorry for the trouble.

MAN is the library a pain to administer!

So for a nine year old boy name John with brown hair who is wearing size 10 blue trousers, a size small red shirt, and size six black boots you could do the following.

#call @HashSet(people,John,sex,male)
#call @HashSet(people,John,age,9)
#call @HashSet(people,John,hair,brown)

#show @people.John.sex
#show @people.John.age
#show @people.John.hair

#call @HashSet(people,John,clothing,torso,item,shirt)
#call @HashSet(people,John,clothing,torso,color,red)
#call @HashSet(people,John,clothing,torso,size,small)

#show @people.John.clothing.torso.item
#show @people.John.clothing.torso.color
#show @people.John.clothing.torso.size

#call @HashSet(people,John,clothing,legs,item,trousers)
#call @HashSet(people,John,clothing,legs,color,blue)
#call @HashSet(people,John,clothing,legs,size,10)

#show @people.John.clothing.legs.item
#show @people.John.clothing.legs.color
#show @people.John.clothing.legs.size

#call @HashSet(people,John,clothing,feet,item,boots)
#call @HashSet(people,John,clothing,feet,color,black)
#call @HashSet(people,John,clothing,feet,size,6)

#show @people.John.clothing.feet.item
#show @people.John.clothing.feet.color
#show @people.John.clothing.feet.size
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Fri Feb 01, 2008 7:18 pm   
 
Arminas wrote:
MAN is the library a pain to administer!

New thread, methinks?
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
alluran
Adept


Joined: 14 Sep 2005
Posts: 223
Location: Sydney, Australia

PostPosted: Sat Feb 02, 2008 2:52 am   
 
Calling This From Within An Event With A Named Param Breaks It. LUA reports The Parent Events Params As The Functions Params
_________________
The Drake Forestseer
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Feb 03, 2008 5:21 pm   
 
Yes there is a way to do this indirectly but I wanted to keep it as simple as possible.

If you create a string list in your event and then alter this function to accept your string list instead of using params and param(1) you can use it with events.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
oldguy2
Wizard


Joined: 17 Jun 2006
Posts: 1201

PostPosted: Sat Mar 01, 2008 4:03 am   
 
I love this.

However, I am not sure how to get the right values to display for what I want to do.

I am using this to store all my guild stocks. I just use a regular database for totals, but the problem is you might have multiple commodity stores with different quantities. So I want to display STOCKLOCATIONS and have it loop through the StockLocation database and display the commodity, location, and quantity in that location. The value changes though so I am not sure how to display it correctly.

Here is the trigger to start recording:

Code:
^Guild stock of (\w+)\:$


Value:

Code:
stock=%1
#T+ stock_locations


stock_locations trigger:

Code:
^(\d+) at (.*)\.$


Value:

Code:

$quantity=%1
$location=%2
#call @HashSet(StockLocations, @stock, location, $location, $quantity)


It stores just what I need. My problem is how do I get it to display in the right format?

I am trying to display it off of an alias like this:

Code:
#say {<color royalblue>GUILD STOCK LOCATIONS</color>}
$list="Wheat|Sugar|Cotton|Iron|Yarriol|Copper|Adamant|Gold|Silver|Fish|Straw|Meat|Clay|Tin|Animals|Leather|Marble|Balsa|Spices|Furs|Wood|Honey|Papyrus|Eggs|Yeast|Gems|Flint|Essences|Rope|Glass|Milk|Oil|Dyes|Salt|Potatoes|Wax|Wool|Silk|Hemp|Bronze|Soy|Dung|Rose"
#show {<color silver>================</color>}
#wait 1
#forall $list {#show {<color saddlebrown>%i = @StockLocations.%i</color>}}
#show {<color silver>================</color>}


I then see this on the screen:

GUILD STOCK LOCATIONS
==================
Inside a hay barn=49|table=Copper|Inside the lumber store=1
==================

Copper is the only one I have stored since I am just doing testing.

The mud output looks like this:

Guild stock of Copper:
49 at Inside a hay barn.
1 at Inside the lumber store.
Total of 50 Copper commodity.
123h, 123m e-

So how do I get it to display what I need when I run the display alias?

I want it to show:

==============
Guild Stock Locations
==============
Copper: 49 Inside the hay bar, 1 Inside the lumber store

I'm probably just confusing myself. I can't very well do @StockLocations.%i.location.inside the lumber store and so on. I don't know what the locations are anyway when I loop through.
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Sun Mar 02, 2008 6:21 am   
 
Because you know the structure of the table you can create the alias to display it based on that structure.

#loopdb @StockLocations.copper.location {#show %val %key}

Gives you most of the information that you need and should point you in the right direction.

It is late and I'm running on batteries so if you need more help post again and I might have time later to write up the whole alias.
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
oldguy2
Wizard


Joined: 17 Jun 2006
Posts: 1201

PostPosted: Mon Mar 03, 2008 5:37 am   
 
This sort of worked except it displayed it like this...

location table
49 Inside a room A
2 Inside a room B
location table
10 Inside a room A
location table
20 Inside a room A
5 Inside a room B

I just gagged the "location table" and displayed %i first then looped the db with your code above and got what I wanted.

Now it shows:

Copper
10 Inside a room A
5 Inside a room B
Tin
12 Inside a room A
1 Inside a room B

Thanks.
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » CMUD General Discussion All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

© 2009 Zugg Software. Hosted by Wolfpaw.net