• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Snippet] Static List

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
StaticList v3.0.0.0

Combining Jesus4Lyf's linked-list method with Vexorian's struct-index technique brings this convention for use with structs. Implement StaticListStruct at the top of an array struct or StaticListModule at the top of any struct to acquire some very useful methods.

Code:
API in both modules:
        
    thistype.head -> thistype; returns the first node in the list (0)
    this.prev -> thistype
    this.next -> thistype
    
API in StaticListStruct only:
    
    thistype.allocate() -> thistype
    this.deallocate()
    
API in StaticListModule only:
    
    this.linkNode()
    this.unlinkNode()

Often struct instances share some common ground, such as a projectile-movement system. That common ground often involves somehow linking all instances together using a Stack. Stacks are useful because they allow you to loop through all struct instances and do something for them. This library is designed to make looping through a Stack as quick and painless as possible, using a linked-list approach.

You can loop through a stack starting at the "head" struct, which is 0, then meandering through all structs until you reach the end (0 again). This is fast because it involves a quick "set this = this.next" instead of incrementing through a single array of structs with an "i" variable and a "thistype" array, and is very intuitive and user-friendly to work with.

So, when starting the loop, you start at the head (0), get its next instance, the instance of its next instance, the instance of its next instance's next instance, and so forth, until you reach 0. So a simple exitwhen this == 0 should be placed at the top of the loop, and the loop actions below that statement, and the set this = this.next statement should be found in there somewhere.

Instead of using the standard allocate/deallocate hidden methods in a struct, make your struct into an array struct struct Name extends array and implement StaticListStruct. It imports a new set of allocate/deallocate methods that do everything the old ones do, but automatically adds each instance to the linked-list, making your life much easier.

In case you already have a list of indexes, such as if you are using UnitIndexer or just a regular struct, you can still get the benefits of this system by implementing StaticListModule, which adds/removes to the linked list via this.linkNode() and this.unlinkNode(), respectively.

JASS:
library StaticList
    
// Generates indexes and automatically adds them to the list.
module StaticListStruct
    
    private static integer s_ze = 0
    private static integer l_st = 0
    private thistype pr_v
    private thistype n_xt
    
    static method operator head takes nothing returns thistype
        return 0
    endmethod
    
    method operator next takes nothing returns thistype
        return this.n_xt
    endmethod
    
    method operator prev takes nothing returns thistype
        return this.pr_v
    endmethod
    
    static method allocate takes nothing returns thistype
        local thistype this
        if (l_st == 0) then
            set this = s_ze + 1
            debug if (integer(this) > 8190) then
                debug call BJDebugMsg("StaticList Error: Cannot allocate past 8190!")
                debug return 0
            debug endif
            set s_ze = this
        else
            set this = l_st
            set l_st = this.pr_v
        endif
        set head.n_xt.pr_v = this
        set this.n_xt = head.n_xt
        set head.n_xt = this
        set this.pr_v = head
        return this
    endmethod
    
    method deallocate takes nothing returns nothing
        static if (DEBUG_MODE) then
            if (integer(this) < 1 or integer(this) > 8190) then
                call BJDebugMsg("StaticList Error: Attempt to destroy an invalid instance: " + I2S(this))
                return
            elseif (this.n_xt == head and head.pr_v != this) then
                call BJDebugMsg("StaticList Error: Attempt to destroy an alread-destroyed instance: " + I2S(this))
                return
            endif
            set this.n_xt = 0
        endif
        set this.pr_v.n_xt = this.n_xt
        set this.n_xt.pr_v = this.pr_v
        set this.pr_v = l_st
        set l_st = this
    endmethod
    
endmodule
    
// When you already have the indexes (eg. with a Unit Indexer)
module StaticListModule
    
    private thistype pr_v
    private thistype n_xt
    
    static method operator head takes nothing returns thistype
        return 0
    endmethod
    
    method operator next takes nothing returns thistype
        return this.n_xt
    endmethod
    
    method operator prev takes nothing returns thistype
        return this.pr_v
    endmethod
    
    method linkNode takes nothing returns nothing
        static if (DEBUG_MODE) then
            if (integer(this) < 1 or integer(this) > 8190) then
                call BJDebugMsg("StaticList Error: Attempt to link an invalid instance: " + I2S(this))
                return
            elseif (this.n_xt != head or head.pr_v == this) then
                call BJDebugMsg("StaticList Error: Attempt to link an already-linked instance: " + I2S(this))
                return
            endif
        endif
        set head.n_xt.pr_v = this
        set this.n_xt = head.n_xt
        set head.n_xt = this
        set this.pr_v = head
    endmethod
    
    method unlinkNode takes nothing returns nothing
        static if (DEBUG_MODE) then
            if (integer(this) < 1 or integer(this) > 8190) then
                call BJDebugMsg("StaticList Error: Attempt to unlink an invalid instance: " + I2S(this))
                return
            elseif (this.n_xt == head and head.pr_v != this) then
                call BJDebugMsg("StaticList Error: Attempt to unlink an already-unlinked instance: " + I2S(this))
                return
            endif
            set this.n_xt = 0
        endif
        set this.pr_v.n_xt = this.n_xt
        set this.n_xt.pr_v = this.pr_v
    endmethod
    
endmodule
    
endlibrary

JASS:
struct Example extends array // Uses StaticList
    
    implement StaticListStruct
    
    static method create takes nothing returns thistype
        return thistype.allocate()
    endmethod
    
    method destroy takes nothing returns nothing
        call this.deallocate()
    endmethod
    
    static method loopAll takes nothing returns nothing
        local thistype this = head.next
        loop
            exitwhen (this == head)
            call BJDebugMsg(I2S(this))
            set this = this.next
        endloop
    endmethod
    
endstruct

struct OtherExample extends array // Uses UnitIndexer and StaticList
    
    implement StaticListModule
    
    method index takes nothing returns nothing
        call this.linkNode()
    endmethod
    
    method deindex takes nothing returns nothing
        call this.unlinkNode()
    endmethod
    
    implement UnitIndexStruct
    
    static method loopAll takes nothing returns nothing
        local thistype this = head.next
        loop
            exitwhen (this == head)
            call BJDebugMsg(GetUnitName(this.unit))
            set this = this.next
        endloop
    endmethod
    
endstruct
 
Last edited:
Level 15
Joined
Jul 6, 2009
Messages
889
At least put some effort in adding some sort of thread title, and description. I count the post as 0 characters.... o_O

JASS:
/*        USE
        
        implement IndexFactory at the top of any struct to gain its features.*/
By the way, implement should be capitalised.

Edit: I just realized that implement wasn't part of the sentence, it was code.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
queue isn't accurate... a queue is last in first out..

It is a dequeue with an enqueue method and a remove method.

A dequeue is a double ended queue, meaning first/last in, first/last out. Because it is double ended, it is also a doubly linked list, meaning any node in it can be removed (your next/previous pointers).

The code also looks strange... you don't need size, I don't know what's up with safety. A single instanced dequeue is very simple and the operations on it are also very simple.

Also don't make ur methods static ;p.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Array structs are used to discard the normal struct indexing, and throws syntax errors when someone calls this.destroy if there is no destroy method defined by the array struct, which is useful for instances that need to be destroyed internally only, such as for unit-indexing libraries.

You can't hook a non-BJ/native using vJass, last I checked.

This module doesn't import the create/destroy methods; it imports the allocate/deallocate methods. If the user wants to make those methods private to the struct, he would just declare allocate and deallocate as private keywords in the scope/library containing the struct that implements the module.

JASS:
library guy

    globals
        private keyword allocate
        private keyword deallocate
    endglobals
    
    struct something extends array
  
        implement DequeueStruct
  
    endstruct

endlibrary
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I suppose I could store them in a chain linked by a_prev[0], since that is currently a free spot on the chain. That would indeed get rid of an array space.

Edit: done. .getLength() has been removed in order to remove an array variable, but that doesn't matter because its only purpose (to check if the struct is empty to pause the struct's timer) can be formatted like this:

Previously:

if (.getLength() == 0) then
...PauseTimer

Now:

if (thistype(0).next() == 0) then
...PauseTimer
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
To save on a method and provide a similar API to the norm, this should be changed from

JASS:
        method next takes nothing returns thistype
            return a_next[this]
        endmethod

to just readonly thistype next

There is also no reason not to make prev public as well (readonly thistype previous) in case someone wants to loop backwards ^_^.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Readonly variables bug in modules, I know it's stupid but it's just another fail on a long list of fails. A readonly variable in a module can still be set outside the module, provided it's still within the struct. The only way to achieve readonly through a module is by operators, so that is what I've done. Prev is kind of useless since it has no starting point (starting at 0 would return a destroyed struct).
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I'm wondering if I should shorten the name to StaticDeque because that seems to be the most popular spelling of it.

Also, deque does not seem to represent its function as the only real definition I could find is: http://en.wikipedia.org/wiki/Double-ended_queue and google dictionary procures: A queue that can have elements added and removed at both ends. A double-ended queue.

This essentially states that it's a double-ended queue, and that it can only add/remove to the front/back of the queue. Your definition of it in your tutorial (can be removed from any point in the queue) conflicts with the established definition of it.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Dequeues can actually be either a singly linked list with a first/last pointer or a doubly linked list. Doubly linked implementation is typically a circular list as well.

Most people, when thinking of a dequeue, think of a doubly linked list ;P. I know I do ;D.

I suppose you could call this a StaticDoublyLinkedList then? ;)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Originally, I brought this very thing up with Pyritie:

...since I have resources of my own, what are the steps to take to get them approved?...
[In the submissions forum], we have a lot of things pending which is why an active JASS moderator is needed. That's what I'm unsure of because I have some things which I feel should be submitted but someone else would have to give approval (autocracy is no thx).

As you can see, I share the same opinion as you guys.

If you feel this thing does not merit an approval, please state why and I'll give it an honest looking-over.

Here's a quote from someone who was already using this:

Edit: I am just using Bribe's DequeueStruct for linked-list-ish behavior. I removed some excess function calls and am able to get ~30 fps with 601 projectiles moving without collision or rotation. I am hoping to get 300 moving at at least 30 fps after new collision detection and rotation.
 
Top