Discussion N-Dimensional Arrays, Useful?

Jesus4Lyf

Good Idea™
Reaction score
397
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
Reaction score
413
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™
Reaction score
397
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:
// &#039;i&#039; 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 &#039;i&#039; 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&#039;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™
Reaction score
397
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&#039;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...
Reaction score
51
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
Reaction score
413
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™
Reaction score
397
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&#039;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...
Reaction score
51
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™
Reaction score
397
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
 

SerraAvenger

Cuz I can
Reaction score
234
any reason we can't just use a hashtable with the known and appreciatied x*k other than multiplication being so slow?
 

Jesus4Lyf

Good Idea™
Reaction score
397
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
Reaction score
234
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
Reaction score
413
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]
 

Bribe

vJass errors are legion
Reaction score
67
Not if you are extending the arrays.

JASS:
type Array1 extends integer array [80]
type Array2 extends Array1 array [80]
type Array3 extends Array2 array [80]


As KingPin stated.
 

SerraAvenger

Cuz I can
Reaction score
234
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™
Reaction score
397
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.
  • Varine Varine:
    I ordered like five blocks for 15 dollars. They're just little aluminum blocks with holes drilled into them
  • Varine Varine:
    They are pretty much disposable. I have shitty nozzles though, and I don't think these were designed for how hot I've run them
  • Varine Varine:
    I tried to extract it but the thing is pretty stuck. Idk what else I can use this for
  • Varine Varine:
    I'll throw it into my scrap stuff box, I'm sure can be used for something
  • Varine Varine:
    I have spare parts for like, everything BUT that block lol. Oh well, I'll print this shit next week I guess. Hopefully it fits
  • Varine Varine:
    I see that, despite your insistence to the contrary, we are becoming a recipe website
  • Varine Varine:
    Which is unique I guess.
  • The Helper The Helper:
    Actually I was just playing with having some kind of mention of the food forum and recipes on the main page to test and see if it would engage some of those people to post something. It is just weird to get so much traffic and no engagement
  • The Helper The Helper:
    So what it really is me trying to implement some kind of better site navigation not change the whole theme of the site
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top