Discussion N-Dimensional Arrays, Useful?

Jesus4Lyf

Good Idea™
Jesus, this worked for me
It works about as well as
JASS:
globals
    private integer StoredValue
endglobals
function StoreInHashtable takes integer key, integer value returns nothing
    set StoredValue=value
endfunction
function ReadFromHashtable takes integer key returns integer
    return StoredValue
endfunction

Try something like this:
JASS:
function test takes nothing returns nothing
        set MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][5][-200][90000]=2
        set MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][6][-200][90000]=3
        call BJDebugMsg(I2S(MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][5][-200][90000]))
        call BJDebugMsg(I2S(MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][6][-200][90000]))
    endfunction

Haven't tried it. I expect it would fail. :)
 

Sevion

The DIY Ninja
The 90000 fails because this has a 8192 limit :-/

Though, if I change the 90000 to 8191, it works fine <3

Though, I've been playing around with it trying to get it to work with data retrieval and storage in any of the dimensions... Until I got sidetracked into playing some Halo on XBL ;3
 

Jesus4Lyf

Good Idea™
The 90000 fails because this has a 8192 limit :-/

Though, if I change the 90000 to 8191, it works fine <3
JASS:
library MDA    
    //! textmacro NEWARRAY takes DIMENSION, NEXTDIMENSION, END
        struct array$DIMENSION$ extends array
            integer data
            static if $END$ then
                method operator [] takes integer index returns thistype
                    return thistype(index).data
                endmethod
            else
                method operator [] takes integer index returns array$NEXTDIMENSION$
                    return index
                endmethod
            endif
            method operator []= takes integer index, integer data returns nothing
                set thistype(index).data = data
            endmethod
        endstruct
    //! endtextmacro
    
    //! runtextmacro NEWARRAY(&quot;4&quot;, &quot;0&quot;, &quot;true&quot;)
    //! runtextmacro NEWARRAY(&quot;3&quot;, &quot;4&quot;, &quot;false&quot;)
    //! runtextmacro NEWARRAY(&quot;2&quot;, &quot;3&quot;, &quot;false&quot;)
    
    struct MDA_array extends array
        integer data
        static method operator [] takes integer index returns array2
            return index
        endmethod
        method operator []= takes integer index, integer data returns nothing
            set thistype(index).data = data
        endmethod
    endstruct
endlibrary

scope Test initializer test
    function test takes nothing returns nothing
        set MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][5][-200][8191]=2
        set MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][6][-200][8191]=3
        call BJDebugMsg(I2S(MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][5][-200][8191]))
        call BJDebugMsg(I2S(MDA_array[GetHandleId(CreateTimer())][GetHandleId(GetTriggerUnit())][6][-200][8191]))
    endfunction
endscope

No, Sevion. No, it does not work fine. As I said, it does not even come close to working at all.

It displays 3 twice, instead of two. And indeed, as long as the last index is the same, it will overwrite, making every previous index redundant. It is actually really blatant in your code:
JASS:
method operator [] takes integer index returns array$NEXTDIMENSION$
                    return index
                endmethod

That method makes no use of "this" whatsoever, which means what's returned is irrelevant to the current array you are on.

In fact, even your own test shouldn't work if your implementation is correct:
JASS:
// 'i' will change with each index you write. Obviously.
        set i = GetHandleId(CreateTimer())
        set MDA_array<i>[5][-200][8191]=2
        set i = GetHandleId(CreateTimer())
        set MDA_array<i>[5][-199][1531]=3
        set i = GetHandleId(CreateTimer())
        set MDA_array<i>[5][-198][8116]=4
        set i = GetHandleId(CreateTimer())
        set MDA_array<i>[5][-197][3457]=5
        set i = GetHandleId(CreateTimer())
        set MDA_array<i>[5][-200][14]=6
        // yet here you read from the 'i' for the last one every time.
        call BJDebugMsg(&quot;Value: &quot; + I2S(MDA_array<i>[5][-200][8191]))
        call BJDebugMsg(&quot;Value: &quot; + I2S(MDA_array<i>[5][-199][1531]))
        call BJDebugMsg(&quot;Value: &quot; + I2S(MDA_array<i>[5][-198][8116]))
        call BJDebugMsg(&quot;Value: &quot; + I2S(MDA_array<i>[5][-197][3457]))
        call BJDebugMsg(&quot;Value: &quot; + I2S(MDA_array<i>[5][-200][14]))
        // therefore, obviously at least the first index is irrelevant.</i></i></i></i></i></i></i></i></i></i>

I would refer you back to my implementation of this.
JASS:
library NArray
    globals
        private hashtable Store=InitHashtable()
    endglobals
    struct NArray
        method operator[] takes integer index returns thistype
            if not HaveSavedInteger(Store,this,index) then
                call SaveInteger(Store,this,index,thistype.create())
            endif
            return thistype(LoadInteger(Store,this,index))
        endmethod
        method operator []= takes integer index, integer value returns nothing
            call SaveInteger(Store,this,index,value)
        endmethod
    endstruct
endlibrary

Here is the correct implementation. The only limitation is you must only read where you know you have written a value, and do not write to where you intend to read as an array.

This does not include flushing, and hence leaks on destroy.

Anyone want to admire how useless it is? It's basically table on crack...

Edit: Here is what seems to be the perfect implementation.
JASS:
library NArray // Jesus4Lyf's NArray demonstration.
    struct NArray
        private static hashtable Store=InitHashtable()
        
        private thistype next
        private thistype prev
        
        private static thistype new
        method operator[] takes integer index returns thistype
            if not HaveSavedInteger(Store,this,index) then
                set thistype.new=thistype.allocate()
                set this.prev.next=thistype.new
                set thistype.new.prev=this.prev
                set thistype.new.next=this
                set this.prev=thistype.new
                call SaveInteger(thistype.Store,this,index,thistype.new)
                return thistype.new
            endif
            return thistype(LoadInteger(thistype.Store,this,index))
        endmethod
        method operator []= takes integer index, integer value returns nothing
            call SaveInteger(thistype.Store,this,index,value)
        endmethod
        static method create takes nothing returns thistype
            local thistype this=thistype.allocate()
            set this.next=this
            set this.prev=this
            return this
        endmethod
        method destroy takes nothing returns nothing
            local thistype node=this.next
            loop
                exitwhen node==this
                call node.destroy()
                set node=node.next
            endloop
            call FlushChildHashtable(thistype.Store,this)
            set this.next.prev=this.prev
            set this.prev.next=this.next
            call this.deallocate()
        endmethod
    endstruct
endlibrary

scope Test initializer test
    function test takes nothing returns nothing
        local timer t=CreateTimer()
        
        local NArray arr=NArray.create()
        set arr[90000][-90000][GetHandleId(t)]=577
        set arr[90000][-90000][2][9][GetHandleId(t)][GetUnitTypeId(GetTriggerUnit())][8][7][6][5][4][3][2][1][19029532890]=209348
        call BJDebugMsg(I2S(arr[90000][-90000][GetHandleId(t)]))
        call BJDebugMsg(I2S(arr[90000][-90000][2][9][GetHandleId(t)][GetUnitTypeId(GetTriggerUnit())][8][7][6][5][4][3][2][1][19029532890]))
        call arr.destroy()
        
        call DestroyTimer(t)
        set t=null
    endfunction
endscope

The NArray. May no one ever find a valid use for it... :p
It's actually efficient, solid data storage, of infinite dimension (but can only store globally 8191 integers, totally).
It will automatically clean itself, when you destroy.
 

Jesus4Lyf

Good Idea™
The only limitation is you must only read where you know you have written a value, and do not write to where you intend to read as an array.

...

Edit: Here is what seems to be the perfect implementation.
JASS:
library NArray // Jesus4Lyf's NArray demonstration.
    struct NArray
        private static hashtable Store=InitHashtable()
        
        private thistype next
        private thistype prev
        
        private static thistype new
        method operator[] takes integer index returns thistype
            if not HaveSavedInteger(Store,this,index) then
                set thistype.new=thistype.allocate()
                set this.prev.next=thistype.new
                set thistype.new.prev=this.prev
                set thistype.new.next=this
                set this.prev=thistype.new
                call SaveInteger(thistype.Store,this,index,thistype.new)
                return thistype.new
            endif
            return thistype(LoadInteger(thistype.Store,this,index))
        endmethod
        method operator []= takes integer index, integer value returns nothing
            call SaveInteger(thistype.Store,this,index,value)
        endmethod
        static method create takes nothing returns thistype
            local thistype this=thistype.allocate()
            set this.next=this
            set this.prev=this
            return this
        endmethod
        method destroy takes nothing returns nothing
            local thistype node=this.next
            loop
                exitwhen node==this
                call node.destroy()
                set node=node.next
            endloop
            call FlushChildHashtable(thistype.Store,this)
            set this.next.prev=this.prev
            set this.prev.next=this.next
            call this.deallocate()
        endmethod
    endstruct
endlibrary

scope Test initializer test
    function test takes nothing returns nothing
        local timer t=CreateTimer()
        
        local NArray arr=NArray.create()
        set arr[90000][-90000][GetHandleId(t)]=577
        set arr[90000][-90000][2][9][GetHandleId(t)][GetUnitTypeId(GetTriggerUnit())][8][7][6][5][4][3][2][1][19029532890]=209348
        call BJDebugMsg(I2S(arr[90000][-90000][GetHandleId(t)]))
        call BJDebugMsg(I2S(arr[90000][-90000][2][9][GetHandleId(t)][GetUnitTypeId(GetTriggerUnit())][8][7][6][5][4][3][2][1][19029532890]))
        call arr.destroy()
        
        call DestroyTimer(t)
        set t=null
    endfunction
endscope

The NArray. May no one ever find a valid use for it... :p
It's actually efficient, solid data storage, of infinite dimension (but can only store globally 8191 integers, totally).
It will automatically clean itself, when you destroy.
Bump for comment.
 

the Immortal

I know, I know...
Really cool as a concept (and realization) but really can't seem to find a use for it anywhere.

Also, at its current state .destroy() doesn't work - stops the thread. Though I'm not quite sure if I understand your recycling mechanism, I suppose it comes from the .allocate() instead of .create() when making the child structs (you need to init the linked list for each of them, too, in order to free the indexes, don't you?); simply replacing it doesn't seem to work but gonna try to debug it (if you don't do it before me, that is).

Also the integer limit can be fixed by saving in the hashtable the .next and .prev (that would mean excessive calling to Save/LoadInteger, though). Hmm, but that means allocators will have to be rewritten as well, and may (depending on impelementation / restrictions) need a hashtable abuse, as well.

Apart from that, I find it (completely) useless. I haven't seen much cases (and none in war3) where an N array would be needed. Generally you always know whether you need a 1, 2, 3 or.. even 4-dimensional one. And then a simple 1-line hashtable wrapper could do the same work.

EDIT: Wrong understanding of recycling. Whited out

EDIT 2: in the destroy method should replace [ljass]call node.destroy()[/ljass] with [ljass]call node.deallocate()[/ljass] to recycle properly.
 

Sevion

The DIY Ninja
Well, I suppose you are correct Jesus. I can't think of any way using my method to completely recreate what you've done. Oh, well.
 

Jesus4Lyf

Good Idea™
Also, at its current state .destroy() doesn't work - stops the thread.
...
EDIT 2: in the destroy method should replace [ljass]call node.destroy()[/ljass] with [ljass]call node.deallocate()[/ljass] to recycle properly.
That's not recursive - it will leak. I can't see why, at a glance, it would thread terminate, but perhaps I can look into it..

I don't "recycle" at all.

... Ah. I see now. I need to break up my linked list into two. :)
Requires a significant rework of my node tracking to fix.

Edit: I lie. Here's the fix (freehanded):
JASS:
library NArray // Jesus4Lyf's NArray demonstration.
    struct NArray
        private static hashtable Store=InitHashtable()
        
        private thistype next
        private thistype prev
        
        private static thistype new
        method operator[] takes integer index returns thistype
            if not HaveSavedInteger(Store,this,index) then
                set thistype.new=thistype.allocate()
                set this.prev.next=thistype.new
                set thistype.new.prev=this.prev
                set thistype.new.next=this
                set this.prev=thistype.new
                call SaveInteger(thistype.Store,this,index,thistype.new)
                return thistype.new
            endif
            return thistype(LoadInteger(thistype.Store,this,index))
        endmethod
        method operator []= takes integer index, integer value returns nothing
            call SaveInteger(thistype.Store,this,index,value)
        endmethod
        static method create takes nothing returns thistype
            local thistype this=thistype.allocate()
            set this.next=this
            set this.prev=this
            return this
        endmethod
        method destroy takes nothing returns nothing
            local thistype node=this.next
            loop
                call FlushChildHashtable(thistype.Store,node)
                call node.deallocate()
                exitwhen node==this
                set node=node.next
            endloop
        endmethod
    endstruct
endlibrary

scope Test initializer test
    function test takes nothing returns nothing
        local timer t=CreateTimer()
        
        local NArray arr=NArray.create()
        set arr[90000][-90000][GetHandleId(t)]=577
        set arr[90000][-90000][2][9][GetHandleId(t)][GetUnitTypeId(GetTriggerUnit())][8][7][6][5][4][3][2][1][19029532890]=209348
        call BJDebugMsg(I2S(arr[90000][-90000][GetHandleId(t)]))
        call BJDebugMsg(I2S(arr[90000][-90000][2][9][GetHandleId(t)][GetUnitTypeId(GetTriggerUnit())][8][7][6][5][4][3][2][1][19029532890]))
        call arr.destroy()
        
        call DestroyTimer(t)
        set t=null
    endfunction
endscope

Immortal was right, but I needed to also flush the child hashtable for the node. I accidentally wrote something better than I intended to, and mistook it for what I intended it to be instead. XD
 

the Immortal

I know, I know...
Wait, if I got it properly, you have the main object, with a linked list on it; then every child node of that object gets added to the list.
So if you write arr[1][2][3] then the 1 is attached to the main list, 2 is attached as 'next' of 1 i.e. in the same list and so on.

Then you save into hashtable using (this, index) meaning in destroy (where you have to simply release (or "recycle" as I said) all indexes and flush the table) you should just loop through all objects in the linked list, [ljass].deallocate()[/ljass] them, and flush the hashtable from all members with their key. Which, I think, should clean it completely.

In other words shouldn't the destructor be something like this:
JASS:
        method destroy takes nothing returns nothing
            local thistype node=this.next
            loop
                exitwhen node==this
                call node.deallocate()
                call FlushChildHashtable(thistype.Store,node)
                set node=node.next
            endloop
            call this.deallocate()
        endmethod

Isn't this how it works? With the change I suggested there's no recursion -and- it releases all indexes from the testmap and cleans the hashtable. Haven't done any further tests but I don't see any logical problems with it.


Also, as I'm not really good with these things, I would love understanding what's wrong with it / how it should work.. if you have the time to explain, so..

PS. At first I thought each new 'level' keeps a new linked list (i.e. when creating 'child' members in [] operator use .create() and then build up a new linked list for their childs, recursively using .destroy() when deallocating main object) but compared to this, it is generally an unneeded complication.

PPS. BLAAAARGH, Gotta refresh pages more often. So it works! Whee o_O
 

Jesus4Lyf

Good Idea™
PS. At first I thought each new 'level' keeps a new linked list (i.e. when creating 'child' members in [] operator use .create() and then build up a new linked list for their childs, recursively using .destroy() when deallocating main object) but compared to this, it is generally an unneeded complication.
That's what I'd thought I'd done with the linked lists. Then I realised the way I'd written it, I must have arbitrarily overwritten next/prev pointers, because each node would need to be part of a parent linked list and and child linked list, but I used the same set of pointers for both. Then I realised the way I'd written it, it would actually construct one giant linked list. Hence I realised you were actually right. I accidentally wrote a better/simpler solution than I meant to.. :)

But yea, I agree I can't think of a good use for this. Although, I don't see why you'd need to make use of the "n" dimensions factor - I'd use this even if it was 5d or something. But I can't see myself needing 5d data storage in a hurry. It's just cool, that it's possible.. :p
 

Jesus4Lyf

Good Idea™
any reason we can't just use a hashtable with the known and appreciatied x*k other than multiplication being so slow?
x*k == k*x and therefore fails.
It would mean myArr[x][k] stores to the same slot as myArr[k][x]. Else I'd have used it in my implementation. :)

>With the N Dimensional Array, I could even detect Stacks(instances).
Do tell?

Actually, if people have a use for it, I can submit...
Just because it does actually work. Lol.
 

SerraAvenger

Cuz I can
x*k == k*x and therefore fails.
It doesn't.

JASS:
myArray [k1][k2][k3]int

puts myArray[x1][x2][x3]

&lt;=&gt;

myArray[k1*k2*k3]
puts myArray[ x1 + k1*x2 + k1*k2*x3]

If it doesn't work for you, you did something wrong. And it is actually pretty easy to compile such that k1*k2 is a constant, and if you don't want to precompile it it's still fast _enough_.

EDIT:
btw, the sole purpose for an n-dimensional array I have found so far is multilayered board games. Eg n instances of chess runing at the same time in the same environment, or SET.
 

Sevion

The DIY Ninja
What he means is:

JASS:
set myArr[10][11] = 1
set myArr[11][10] = 2


Using the x*k method, myArr[10][11] == myArr[11][10]

Because, myArr[10][11] is really myArr[10*11]

Thus, myArr[11][10] is myArr[11*10]

myArr[110]==myArr[110]
 

SerraAvenger

Cuz I can
JASS:
myArray [k1][k2][k3]int
puts myArray[x1][x2][x3]

&lt;=&gt;

myArray[k1*k2*k3]
puts myArray[ x1 + k1*x2 + k1*k2*x3]
Try it out.

JASS:
myArray [10][10]int
puts myArray[2][3]
puts myArray[3][2]

&lt;=&gt;

myArray[10*10] // 100
puts myArray[ 2 + 10*3] // 32
puts myArray[ 3 + 10*2] // 23

and no, 32 != 23, just in case you were wondering.

EDIT: What kinky states is nonsensical and hard to instanciate.
IIRC, all members are zero value initialized.

what you would want to do is something like this

JASS:
package array
type ThreeDArray interface {
    Get( x1, x2, x3 int ) interface{}
    Set( x1, x2, x3 int, val interface{} )
}


An example struct that implements the interface and a main package to test it
JASS:
package samplearray

import &quot;array&quot;

type sampleThreeDArray struct {
    k_2, k_3 int // no k_1 because it is always 1
    table [8192]interface{}
}

func New(k1, k2, k3 int) array.ThreeDArray {
    return sampleThreeDArray{
        k1,
        k1 * k2,
        make([]interface{}, k1 * k2 * k3 ),
    }    
}

func (a sampleThreeDArray) Get(x1, x2, x3 int) interface{} {
    return a.table[ x1 + x2 * a.k_2 + x3 * a.k_3 ]
}
func (a sampleThreeDArray) Set( x1, x2, x3 int, val interface{} ) {
    a.table[ x1 + x2 * a.k_2 + x3 * a.k_3 ] = val
}

// ...

package main

import &quot;samplearray&quot;

func main () {
  var := samplearray.New( 20, 25, 15 )
  var.Set( 19, 24, 14, &quot;hi!&quot; )  
}


I haven't compiled/gofmted those though. And yes they have no bounds checks. And yes golang comes with arrays of arrays already so this is not needed.

EDIT2:
Yes this has constant lookup and constant write. Yes this has the maximum array size of wc3 jass. Yes this supports any number of 3d arrays.
 

Jesus4Lyf

Good Idea™
Serra is talking about the way vJass does multi-dimensional arrays. Not the multiplication thing mentioned earlier.

If it is possible to use it, by all means, do. But it requires restrictions around what values you can use in an index.

Indeed, it's the approach I try to take in all my systems. :)

Good luck using a handle id as an index, for example.
So the answer to this thread be Yes. >=D
Not really... I still agree with Serra instead. They're just theoretically useful. :p
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    I read in the PC Gamer review that it was not much of an update to the game like just a re-release
  • Ghan Ghan:
    It's a good remaster. They did not want to change the game much because there are still many die-hard players out there that wouldn't like it to be a new game.
  • Ghan Ghan:
    They've said they are open to adding new content in the future, but wanted to get what's there correct first. As it is, for what you get the $40 asking price is a bit steep.
  • The Helper The Helper:
    Yeah that is a high price think I am going to wait a minute and see if it goes down some.
  • The Helper The Helper:
    Looking forward to doing some dungeon grinding again though.
  • The Helper The Helper:
    still 101 bots on the site constantly
  • midnight8 midnight8:
    my bots got out of their cage? oh no
  • The Helper The Helper:
    these bots are from russia, china and asia they are less than they were but more than they need to be I have no idea why they hover here
  • tom_mai78101 tom_mai78101:
    Probably because of my Headline News
  • The Helper The Helper:
    Facebook is getting fucked right now lol
  • The Helper The Helper:
    I learned one thing from the Facebook outage - BGP - I am sure Ghan is an expert in it but I did not know what the name of it was I knew there was something like that there though
  • The Helper The Helper:
    What is up Tom?
  • jonas jonas:
    We once considered doing a project on BGP looking for problems in the implementations. But we didn't get sufficient interest and Tony Li was mean to us so we said whatever
  • Varine Varine:
    hello, peeps. I got most of my computer back up to Idaho so I have passwords again after that nightmare of like 4 months
  • Varine Varine:
    We're all trying to get time off now, but holy shit. I did not expect it to be that busy. Every day was a historical record, and every week was a total sales record for like all of summer. My pay is going to almost double next summer, which makes cooking finally a viable career, I might actually afford to change industries soon!
  • Varine Varine:
    We had people ask if there was anything we could do about the fucking sun. There's like 30 inside tables and every outside table has a sunbrella thing.
  • Varine Varine:
    No, I'm sorry, I cannot move the sun and the umbrella doesn't go 90 degrees sideways. They unfortunately had to deal with a sunset across mountains and a lake, we can only imagine the horror they had to undergo by visiting our restaurant
  • Varine Varine:
    Well, they do, but they're big and blow away and a 10 foot runaway umbrella is a much bigger problem than some cunt with the sun sorta in her eyes
  • The Helper The Helper:
    What is up Varine? Good to see you buddy! Too bad you are not in Houston I have a friend that needs Cooking staff like desperately remember if you ever want to get a different landscape, not sunsets over mountains and a lake Houston has opportunity!
  • tom_mai78101 tom_mai78101:
    Nice to see Varine back in action.
  • The Helper The Helper:
    Welcome back Varine!
  • The Helper The Helper:
    can someone answer a question in the World Editor Help forum?
  • jonas jonas:
    ok
  • The Helper The Helper:
    thank you jonas!
  • Varine Varine:
    I'll probably be here through next summer at least, I get paid decent at the moment and I expect it to go up significantly next year. Plus I'm on contract through the end of the year
    +1

    Staff online

    • Ghan
      Administrator - Servers are fun

    Members online

    Affiliates

    Hive Workshop NUON Dome
    Top