HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

[script] Base

02-06-2011, 02:37 AM#1
Nestharus
Very simple base script that handles the creation of and conversion between bases.

Collapse JASS:
library Base /* v1.0.0.2
*************************************************************************************
*
*   A script used for base conversion where integers are represented as strings in
*   another base.
*
************************************************************************************
*
*   struct Base extends array
*
*       static method operator [] takes string base returns Base
*
*       method convertToString takes integer i returns string
*       method convertToInteger takes string i returns integer
*
*       method ord takes string c returns integer
*       method char takes integer i returns string
*
*************************************************************************************/

/*************************************************************************************
*
*   Code
*
*************************************************************************************/
    globals
        private hashtable t = InitHashtable()
        private integer c = 0
        private integer array s
    endglobals
    
    struct Base extends array
        static method operator [] takes string base returns thistype
            local integer value = StringHash(base)
            local thistype this = LoadInteger(t, 0, value)
            local string char
            
            if (this == 0) then
                set c = c + 1
                set this = c
                
                call SaveInteger(t, 0, value, this)
                set value = StringLength(base)
                set s[this] = value
                
                loop
                    set value = value - 1
                    set char = SubString(base, value, value+1)
                    if (char == StringCase(char, true)) then
                        call SaveInteger(t, this, StringHash(char), value)
                    else
                        if (char == "/") then
                            call SaveInteger(t, this, StringHash(char), value)
                        else
                            call SaveInteger(t, this, -StringHash(char), value)
                        endif
                    endif
                    call SaveStr(t, -this, value, char)
                    exitwhen value == 0
                endloop
            endif
            
            return this
        endmethod
        
        method convertToString takes integer i returns string
            local integer k = s[this]
            local string n = ""
            loop
                exitwhen i < k
                set n = LoadStr(t, -this, i-i/k*k) + n
                set i = i/k
            endloop
            return LoadStr(t, -this, i) + n
        endmethod
        
        method convertToInteger takes string i returns integer
            local integer n = 0
            local integer p = StringLength(i)
            local integer l = 0
            local integer k = s[this]
            local string char
            loop
                exitwhen p == 0
                set p = p - 1
                set l = l + 1
                set char = SubString(i, l-1, l)
                if (char == StringCase(char, true)) then
                    set n = n+LoadInteger(t, this, StringHash(char))*R2I(Pow(k, p))
                else
                    if (char == "/") then
                        set n = n+LoadInteger(t, this, StringHash(char))*R2I(Pow(k, p))
                    else
                        set n = n+LoadInteger(t, this, -StringHash(char))*R2I(Pow(k, p))
                    endif
                endif
            endloop
            
            return n
        endmethod
        
        method ord takes string c returns integer
            if (c == StringCase(c, true)) then
                return LoadInteger(t, this, StringHash(c))
            endif
            if (c == "/") then
                return LoadInteger(t, this, StringHash(c))
            endif
            return LoadInteger(t, this, -StringHash(c))
        endmethod
        method char takes integer i returns string
            return LoadStr(t, -this, i)
        endmethod
        method operator size takes nothing returns integer
            return s[this]
        endmethod
    endstruct
endlibrary
02-18-2011, 03:39 AM#2
Nestharus
Updated this as apparently StringHash("d") == StringHash("D") ... will now work, but had to add this bs if (c == StringCase(c, true)) then.
02-18-2011, 08:53 AM#3
Deaod
heres another one: StringHash("/")==StringHash("\\")
02-21-2011, 08:02 PM#4
Nestharus
Fixed ;|.

wc3c is finally back up, lol.
02-27-2011, 06:22 PM#5
Anitarf
  • The ord and char methods need more descriptive names. Are they even needed? They seem to do the same thing as ConvertToInteger and convertToString, only for single character strings.
  • The library should detect and report user errors, at least in debug mode, for example if a user uses a character that is not present in the given base or if a base contains the same character more than once.
  • Given the hashtable limit, you should use table instead of a native hashtable, since you don't really need the full 2D array functionality, you just need one static Table and two Tables per base instance. Never mind, forgot that Table can only store integers.
03-05-2011, 09:11 PM#6
Nestharus
->The ord and char methods need more descriptive names
ord and char is the way it's done. Those are the names if you program in the majority of languages. When you say ord, you are talking about the -ordinal value of a character. When you say char, you are talking about the symbolic representation of that value.
->Are they even needed? They seem to do the same thing as ConvertToInteger and convertToString, only for single character strings.
ord/char faster as they perform less operations
->The library should detect and report user errors, at least in debug mode, for example if a user uses a character that is not present in the given base or if a base contains the same character more than once.
new one does that to a degree
->Never mind, forgot that Table can only store integers.
New one uses a Table script written by Bribe and this will never use the Table script written by Vexorian as it doesn't have the necessary features.
The updates also use an Ascii library, which will probably never end up getting sent here and probably would never be approved anyways since the Ascii library is only used for Char and Ord functionality in wc3, which has extremely limited uses.

I'll link the two required libraries as well as the updated script for your convenience so that you can review whether it is worth it or not. I'll also link some of the dependent libraries that use this.

Bribe's Table
http://www.hiveworkshop.com/forums/j...-table-188084/
Ascii
http://www.hiveworkshop.com/forums/1858865-post11.html

Updated Base (quite a bit better)
http://www.hiveworkshop.com/forums/j...t-base-188814/

BigInt
http://www.hiveworkshop.com/forums/j...bigint-188973/
Scrambler
http://www.hiveworkshop.com/forums/j...ambler-189766/
Knuth Checksum
http://www.hiveworkshop.com/forums/1846246-post343.html

Encoder
http://www.hiveworkshop.com/forums/s...-0-0-a-189883/


And Encoder is superior to the save/load system here by a small amount. Encoder 2.0.0.0 will be vastly superior to it though.


So yea, up to you guys. Bribe's table does have an add on that makes it backwards compatible with vex's table and it's already being widely accepted (especially with the backwards compatibility add on).


The reason Encoder 1.0.0.0 is slightly superior to PipeDream's-
1. Takes ranges rather than maximums (shifts values around to minimize their sizes)
2. Can save negative values
3. If the checksum is greater than the first value on the code, it will add checksum rather than the first value. The first value in a code does not have to be multiplied in, so you want the biggest value first.
4. More customizable and better security options with as strong a security as you want (to prevent code modification). Stronger security means a bigger code, but the point is that it puts the user in control.
5. Not coupled with a big int library


The reason Encoder 2.0.0.0 will be vastly superior
1. Links ranges together for saving objects*. This solves the problem of saving something like item charges, which can be different for each item. Rather than having something like a max of 300 for all items, each item's specific max charge can be saved with that item. It also makes the API a bit more intuitive =).

Quote:
You know how most save/load systems just take like all of your values or w/e and they are all static and in order? What if you had something like item charges? Items have different charges on them no? : O.

I thought, wouldn't it be cool if I could just store the max charge for specific items ^)^. So I came up with this linking idea.

First, you make all of your ranges (the min and maximum values of what can be in the game).

Collapse JASS:
local CodeRange resource = CodeRange.create(0,1000000)
local CodeRange hero = CodeRange.create(1,HeroCount)
local CodeRange perc = CodeRange.create(0,100)
local CodeRange lvl = CodeRange.create(1,10)
local CodeRange xp = CodeRange.create(0,99)
local CodeRange stat = CodeRange.create(1,256)
local CodeRange item = CodeRange.create(1,ItemCount)
local CodeRange charge = CodeRange.create(0,25)
local CodeRange pet = CodeRange.create(1,PetCount)

Next, you link! : D

In my demo map (untrue), all of the items can have up to 25 charges (def not true, I'm too lazy to go through a ton of items and look up their charges).

call item.link(item.lowBound, item.highBound, charge) //charge
Woo. That'll make all items have 25 charges max in the code ;D.

What about a hero? Well, a hero has coordinates, facing, level, xp, stats, life, mana, and 6 items, so let's link! : D
Collapse JASS:
call hero.link(hero.lowBound, hero.highBound, perc)     //x
call hero.link(hero.lowBound, hero.highBound, perc)     //y
call hero.link(hero.lowBound, hero.highBound, perc)     //facing
call hero.link(hero.lowBound, hero.highBound, lxl)      //lvl
call hero.link(hero.lowBound, hero.highBound, xp)       //xp
call hero.link(hero.lowBound, hero.highBound, stat)     //str
call hero.link(hero.lowBound, hero.highBound, stat)     //agi
call hero.link(hero.lowBound, hero.highBound, stat)     //int
call hero.link(hero.lowBound, hero.highBound, perc)     //life
call hero.link(hero.lowBound, hero.highBound, perc)     //mana
                
call hero.link(hero.lowBound, hero.highBound, item)     //item1
call hero.link(hero.lowBound, hero.highBound, item)     //item2
call hero.link(hero.lowBound, hero.highBound, item)     //item3
call hero.link(hero.lowBound, hero.highBound, item)     //item4
call hero.link(hero.lowBound, hero.highBound, item)     //item5
call hero.link(hero.lowBound, hero.highBound, item)     //item6

Notice I've yet to add anything to the actual code >.>.

In my demo map, heroes also have 4 pets, so let's link them too.

Collapse JASS:
call hero.link(hero.lowBound, hero.highBound, pet)      //pet1
call hero.link(hero.lowBound, hero.highBound, pet)      //pet2
call hero.link(hero.lowBound, hero.highBound, pet)      //pet3
call hero.link(hero.lowBound, hero.highBound, pet)      //pet4

And pets have x,y,facing,life, and mana.

Collapse JASS:
call pet.link(pet.lowBound, pet.highBound, perc)        //x
call pet.link(pet.lowBound, pet.highBound, perc)        //y
call pet.link(pet.lowBound, pet.highBound, perc)        //facing
call pet.link(pet.lowBound, pet.highBound, perc)        //life
call pet.link(pet.lowBound, pet.highBound, perc)        //mana

So.. now on to actually adding to the encoder things... well, first let's add the resources cuz that's the biggest.

Collapse JASS:
call encoder.add(resource)      //gold
call encoder.add(resource)      //lumber

And now... the hero.
call encoder.add(hero) //hero
But wait, what about all of that other crap? It's all linked through the hero ;D.

Code:
hero
    x
    y
    facing
    lvl
    xp
    str
    agi
    int
    life
    mana
    item1
    item2
    item3
    item4
    item5
    item6
    pet1
        x
        y
        facing
        life
        mana
    pet2
        x
        y
        facing
        life
        mana
    pet3
        x
        y
        facing
        life
        mana
    pet4
        x
        y
        facing
        life
        mana

And it should read out in that fashion. Essentially, you read the value and then you determine what to expect from the value. So all of those values would read in the order above. You read gold, you read lumber, you read hero which then points to all of the crap inside of it. When you get down to the pets, you go inside of each pet. All of the reads would just be DataBuffer.read() though, it does all of the crazy work for you :P.


Base is the first script in a large series of scripts that make up Encoder, so it's your call whether it is worth it or not. I don't know about you, but I like smaller save/load codes =P, and there is certainly no comparison between Encoder 2.0.0.0 and any other save/load system ever created for wc3.



It's your call. I know that wc3c operates differently from TH and THW. It's just that wc3c is starting to kind of fall behind in the latest in vJASS resources, for example, the lua suite at THW and TH.
03-06-2011, 06:25 PM#7
Anitarf
As you know, a library may only require already approved resources in order to qualify for approval itself. This is not necessarily a problem in this case as the code that is currently posted in the first post without the extra requirements seems quite acceptable. The only thing that I request before approving it is that you add the warning debug messages for faulty inputs in debug mode:
  • Negative integers being passed to convertToString and char methods.
  • A too large integer being passed to the char method.
  • Invalid characters being passed to the convertToInteger and ord methods.
  • More than one character being passed to the ord method.
  • A string containing multiple identical characters being passed to the [] static operator.
03-06-2011, 07:58 PM#8
Nestharus
Hm... the debug messages on this version are a pain compared to the one using Ascii, mind if I get Bribe to submit his Ascii first? Actually, the thing was a collaboration between us 2 (he didn't change it much from the one at th), so I'll just submit it here and say by Bribe and nes or something, give me a bit. We can then look back at this script after we look at the Ascii one.

tx.

edit
Got the Base update ready to go with tons of debug stuff, so I can update this to that when Ascii and Bibe's Table are approved. Had to fix a few mistakes by Bribe on Ascii >.>, spent an hour trying to figure out why the initialization order was wrong and realized Bribe made the onInit public... >.>

Contacting Bribe about submitting Table here. It has backwards compatibility to the other one, so it should be ok =).

Here it is
Collapse JASS:
library Base /* v1.0.2.0
*************************************************************************************
*
*   A script used for base conversion where integers are represented as strings in
*   another base.
*
*************************************************************************************
*   */uses/*
*
*       */ Ascii /*         wc3c.net/showthread.php?t=110153
*       */ Table /*         hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
************************************************************************************
*
*   struct Base extends array
*
*       static method operator [] takes string base returns Base
*
*       method convertToString takes integer i returns string
*       method convertToInteger takes string i returns integer
*
*       method ord takes string c returns integer
*       method char takes integer i returns string
*
*************************************************************************************/

/*************************************************************************************
*
*   Code
*
*************************************************************************************/
    globals
        private Table gt = 0        //stacks of strings with same hashes
        
        private integer array n     //next node pointer for gt stack
        private string array b      //base of string
        private Table array t       //base character table
        private integer c = 0       //base instance count
        private integer array s     //base size
    endglobals
    
    private module Init
        private static method onInit takes nothing returns nothing
            set gt = Table.create()
        endmethod
    endmodule
    
    struct Base extends array
        debug private static boolean array a        //is allocated
        
        static method operator [] takes string base returns thistype
            local integer value     //string hash value
            local string char       //iterated character
            local integer i = 0     //this
            local integer v         //stack of hashes
            local integer dv        //copy of v
            
            debug if (StringLength(base) > 1) then
                set value = StringHash(base)    //first get the hash
                set i = gt[value]               //get first node of hash table
                set v = i                       //copy
                if (i != 0) then                //if stack exists, then loop through
                    loop
                        exitwhen i == 0 or b[i] == base
                        set i = n[i]
                    endloop
                endif
                
                //if this still doesn't exist, create it
                if (i == 0) then
                    //allocate
                    set c = c + 1
                    set i = c
                    set dv = v
                    debug set a[i] = true
                    
                    set t[i] = Table.create()   //character table
                    set b[i] = base             //base string
                    
                    //value is now used for iterating through the base string
                    set value = StringLength(base)
                    set s[i] = value    
                    loop
                        set value = value - 1
                        set char = SubString(base, value, value+1)
                        set v = Char2Ascii(char)
                        //if the character already exists, stop
                        //and deallocate (invalid base)
                        debug if (t[i].has(v)) then
                            debug call t[i].destroy()   //destroy character table
                            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CREATION ERROR: " + char + " MULTIPLY DEFINED")
                            debug set c = c - 1
                            debug set a[i] = false
                            debug return 0
                        //character doesn't exist
                        debug else
                            set t[i][v] = value
                            set t[i].string[-value] = char
                        debug endif
                        
                        exitwhen value == 0
                    endloop
                    
                    //if dv is 0, then allocate dv
                    if (dv == 0) then
                        set gt[value] = i
                    //otherwise add i to hash stack
                    else
                        set n[i] = n[dv]
                        set n[dv] = i
                    endif
                endif
                
                return i
            debug endif
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CREATION ERROR: " + base + " IS INVALID")
            debug return 0
        endmethod
        
        method convertToString takes integer i returns string
            local integer k = s[this]
            local string n = ""
            debug if (a[this]) then
                debug if (i >= 0) then
                    loop
                        exitwhen i < k
                        set n = t[this].string[-(i-i/k*k)] + n
                        set i = i/k
                    endloop
                    return t[this].string[-i] + n
                debug endif
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CONVERSION ERROR: " + I2S(i) + " IS OUT OF BOUNDS")
                debug return null
            debug endif
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CONVERSION ERROR: " + I2S(this) + " IS NOT ALLOCATED")
            debug return null
        endmethod
        
        method convertToInteger takes string i returns integer
            local integer n = 0
            local integer p = StringLength(i)
            local integer l = 0
            local integer k = s[this]
            local string char
            debug if (a[this]) then
                loop
                    exitwhen p == 0
                    set p = p - 1
                    set l = l + 1
                    set char = SubString(i, l-1, l)
                    debug if (t[this].has(Char2Ascii(char))) then
                        set n = n + t[this][Char2Ascii(char)]*R2I(Pow(k,p))
                    debug else
                        debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CONVERSION ERROR: " + char + " IS OUT OF BOUNDS")
                        debug return 0
                    debug endif
                endloop
                
                return n
            debug endif
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CONVERSION ERROR: " + I2S(this) + " IS NOT ALLOCATED")
            debug return 0
        endmethod
        
        method ord takes string c returns integer
            debug if (a[this]) then
                debug if (StringLength(c) > 1 or c == "" or c == null or not (t[this].has(Char2Ascii(c)))) then
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE ORD ERROR: " + c + " IS OUT OF BOUNDS")
                    debug return 0
                debug endif
                return t[this][Char2Ascii(c)]
            debug endif
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE ORD ERROR: " + I2S(this) + " IS NOT ALLOCATED")
            debug return 0
        endmethod
        method char takes integer i returns string
            debug if (a[this]) then
                debug if (i < s[this] and i >= 0) then
                    return t[this].string[-i]
                debug endif
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CHAR ERROR: " + I2S(i) + " IS OUT OF BOUNDS")
                debug return null
            debug endif
            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "BASE CHAR ERROR: " + I2S(this) + " IS NOT ALLOCATED")
            debug return null
        endmethod
        method operator size takes nothing returns integer
            return s[this]
        endmethod
        
        implement Init
    endstruct
endlibrary