[vJASS] SequenceT

Code
JASS:
library Sequence /* v1.2 -- 12.11.2017 -- By IcemanBo -- https://www.hiveworkshop.com/threads/sequencet.274244/#post2772894

*/ requires /*

        */ VectorT        /* hiveworkshop.com/forums/submissions-414/containers-vector-t-248942/
        */ Alloc          /* any alloc.
     
*/
//    Read the readme before config.

// ================ Config =================== //
//! novjass

// To define sequence types we have to run following textmacro

    textmacro SEQUENCE takes SEQUENCE, SEQUENCE_TYPE, VECTOR_TYPE, TYPE
   
        SEQUENCE
            // The struct name of the sequence
        SEQUENCE_TYPE
            // The struct name of the sequence type
        VECTOR_TYPE
            // The vector struct type name that defines the sequence type
        TYPE
            // The type that a sequence stores
//! endnovjass

    //! runtextmacro SEQUENCE("IntegerSequence", "IntegerSequenceType", "IntegerVector", "integer")
        // For integer sequences
       
    //! runtextmacro SEQUENCE("StringSequence", "StringSequenceType", "StringVector", "string")
        // For string sequences
   
   
// Read the readme for the system's API
 
// ================ End Config =================== //
   
    //! textmacro SEQUENCE takes SEQUENCE SEQUENCE_TYPE VECTOR_TYPE, TYPE
    struct $SEQUENCE_TYPE$ extends array
        implement Alloc
        readonly $VECTOR_TYPE$ vector
        readonly integer size
     
        static method create takes $VECTOR_TYPE$ vec returns thistype
            local thistype this = allocate()
            set this.vector = $VECTOR_TYPE$[vec]
            set this.size = vec.size()
            return this
        endmethod
     
        method destroy takes nothing returns nothing
            call this.vector.destroy()
            call this.deallocate()
        endmethod
    endstruct
   
    struct $SEQUENCE$ extends array
        implement Alloc
        readonly $SEQUENCE_TYPE$ sequence
       
        readonly boolean isSolved
        readonly boolean isInputCorrect
        readonly integer currentPosition
     
        method onSequenceEvent takes $TYPE$ input returns nothing
            set this.isInputCorrect = (input == this.sequence.vector[this.currentPosition])
            if (this.isInputCorrect) then
                set this.currentPosition = this.currentPosition + 1
                set this.isSolved = this.currentPosition >= this.sequence.size
            else
                set this.isSolved = false
            endif
        endmethod

        method setPos takes integer pos returns boolean
            if pos > 0 and pos <= this.sequence.size then
                set this.currentPosition = pos
                return true
            endif
            return false
        endmethod
     
        method destroy takes nothing returns nothing
            call deallocate()
        endmethod
     
        static method create takes $SEQUENCE_TYPE$ sequ, integer startPos returns thistype
            local thistype this = allocate()
            set this.currentPosition = startPos
            set this.sequence = sequ
            return this
        endmethod
    endstruct
    //! endtextmacro
endlibrary

Readme
JASS:
/*
    Sequence Readme v1.2 -- 12.11.2017

===== Introduction =====

    A sequence is a defined order of data of one type. If all data is correctly
    given through the input for a sequence, the sequence can be seen as solved.
 
    Note:
    Sequences work with vectors from VectorT library. If you want create
    a sequence of a new type, you also have to create the VECTOR_TYPE respectivly.
 
 
===== Technical info =====

    Sequences of type string and integer are supported by default.
    To add more types you need to run the textmacro in Sequence config.


===== API ===== */

/*  You now need to create a sequence type that can be used:    */
    
    struct $SEQUENCE_TYPE$
 
            static method create takes $VECTOR_TYPE$ vec returns thistype
                // constructor
            
            method destroy takes nothing returns nothing
                // destructor
            
            readonly $VECTOR_TYPE$ vector
            readonly integer size
                // readonly members
    
      
/*  Now you can create new instances of the sequence type    */
 
    struct SEQUENCE
    
        static method create takes SEQUENCE_TYPE sequ, integer startPos returns thistype
            // constructor
            // startPos is the position where you want to start. Usually it should be 0.
        
         method destroy takes nothing returns nothing
            // destructor
        
/*  To give input to the sequence interact with using this method    */

        method onSequenceEvent takes $TYPE$ input returns nothing

        readonly boolean isSolved
        readonly boolean isInputCorrect
        readonly integer currentPosition
            // readonly members
            // it makes sense to read this members after onSequenceEvent was called

In the default settings integer and string vectors are supported.
So it requires your VectorT library support vectors of type string to let it run!
Add this line in your VectorT config:

//! runtextmacro DEFINE_VECTOR("", "StringVector", "string")

Examples:

 
Last edited:
I found a little but critical error which lead to bug of sequence type integrity.
But now it should be fixed.

I also tried to work a bit on readability and the easyness to understand, but the macros are making it a bit ugly. :(

I thought about just to remove the macros and only support integer sequences, because actually we almost
always can use handleids or so... but finaly I decided against it, because it can be neat at places to use sequences of abstract data types.
Also strings are supported this way - and some say that using StringHash() to get the id might be not very safe always in terms of collision.
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Definitely useful. I think this can be simplified by only using 1 struct (The instances of both structs have 1 to 1 relation anyway). Then the create method only takes a vector and a starting position.

I see that the elements of the sequence is designed to be static (only set at creation) thus, it might be dangerous if for some reason the vector is tampered by the user (since it is public) and the vector size will no longer match with the 'size' member of the sequence, leading to a bug. For this, you can use the size member of the vector directly instead. Also, i think it would be less error prone if you do not let a class destroy a foreign object that was not created by it (in this case, the vector) and rather just let the user destroy it manually.

Maybe it would also be nice to add method to reset the current position in the sequence like for example in cases where if the input is wrong, you need to start over. And maybe a method to move the position backward by 1.

Also, for portability, you can add a static constant boolean flag per struct
JASS:
struct $NAME$

    static constant boolean DEFINED = true

endstruct
so that any library can happily declare its own needed sequence type without worrying for collisions by using static ifs = ). Because without one, the users will have to do some work removing duplicate //!runtextmacros from different libraries.
 
Last edited:
Relation is: 1 to N , where 1 Sequence_Type can be used by N Sequences.

dangerous if for some reason the vector is tampered by the user (since it is public)
It is readonly I believe.

less error prone if you do not let a class destroy a foreign object that was not created by it (in this case, the vector) and rather just let the user destroy it manually.
Sounds quite reasonable, hm. User should not be able to change it anyways, so I better completly encapsulate it, with using the copy constructor. [edit] changed

Maybe it would also be nice to add method to reset the current position in the sequence like for example in cases where if the input is wrong, you need to start over. And maybe a method to move the position backward by 1.
Hm, yes. Hi might now, but only indirectly, with restarting the sequence, and then he might choose a new start position. But making a new function for only changing pos might be good. Will do. [edit] changed

Also, for portability, you can add a static constant boolean flag per struct
What if not all parameters are identical? Maybe throwing error is not so bad, too. Hm.
 
Last edited:

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Relation is: 1 to N , where 1 Sequence_Type can be used by N Sequences.
I see. But I think it the two struct can still be merged so that only one textmacro definition is needed (Only SEQUENCE macro will remain). That macro then just have to take arguments NAME, VECTOR_TYPE, and TYPE.

You could eliminate Alloc by using the vector instance as the Sequence instance on create method local thistype this = $VECTOR_TYPE$[vec].

What if not all parameters are identical?
If you could use one struct, you could add this additional static check I think.

And my last suggestion is if you could make the 3 globals private and give the users readonly access instead by providing wrapper functions =) since they shouldn't be able to set it anyways.
 
  • Combined in one macro. Though 2 structs are still used.
  • Alloc is also still used (it's anyways imported due to VectorT), because I find it a bit clearer. But I need to update then the ArrowKeySequence soon anyways (maybe not too soon^^), and will think to change it then.
  • The 3 globals are now just made to non-static readonly members of a sequence instance.
  • Name change SequenceT -> Sequence.
  • Docu updated.
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Everything seems good to me except for just 1 thing that I think can still be better. The struct $SEQUENCE_TYPE$ seems to be only a wrapper for the vector object, it only has 2 data namely vector, and size - which the $SEQUENCE$ struct can already directly access without the said struct. You can even remove the additional instance member size and replace it with a method operator size that returns vector.size() directly, in which case, the only member in use is actually the vector - which you can simply move to the $SEQUENCE$ struct. It would also shorten the codes the behind the scenes - it will remove the allocate/deallocate of the one struct.
 
The SequenceType is like only a static vector of a certain type, yes, which is optimally never destroyed actually. I can have dozens of active running Sequences which all refer to the one SequenceType. So when starting a sequence we don't need to create a new prototype of the wanted sequence, but just use a already existing seqeunce type. If the exra struct wouldn't exist, I'm not sure how to create a new "type" for a sequence, or we would need to define a new vector for wanted vector type on each run when create a new sequence.
The size seems really not needed maybe.^^ Will remove soon.
 
In the arrow key map, I create sequence types onInit like this:

JASS:
    // Data of all default sequences (added by user)
    struct ArrowKeySequence extends array
        implement Alloc
        readonly trigger handler
        readonly IntegerSequenceType sequence
       
        static method create takes IntegerVector vec, boolexpr bx returns thistype
            local thistype this = allocate()
            set this.sequence = IntegerSequenceType.create(vec)
            set this.handler = CreateTrigger()
            call TriggerAddCondition(handler, bx)
            return this
        endmethod
       
        method destroy takes nothing returns nothing
            call this.sequence.destroy()
            call DestroyTrigger(this.handler)
            set this.handler = null
            call this.deallocate()
           
        endmethod
    endstruct

^Those created sequences will function as default sequence types I wanna solve in my map. They act pretty much static, they're like prototype reference for all future running sequences.

So when a new sequence is started, we define the sequence type which is the goal (ArrowKeySequence that) :
JASS:
        // Will start a new sequence from vectorPosition[startPos]
        method start takes ArrowKeySequence that, integer startPos returns nothing
            call this.arrowKeySequence.push(that)
            call this.sequence.push(IntegerSequence.create(that, startPos))
            set this.sequenceAmount = this.sequenceAmount + 1
        endmethod

^So, there are the prototypes used. Instead of doing so I easily could also ask for a same VectorT from respective type (integer vector in this example), that's true, if you mean this. But having a seperate container for the static types of a sequence instead of dealing with "raw" vector types in running time was probably my preference. And in the container, it's then also already ensured that the vector will never ever change from outside, like the size, because it's copy constructed, and can't be touched anymore. But maybe both can be done, idk.

But honestly, hm.. not because of your posts, but I maybe believe now the use of the resource is maybe very minimal, I'm not sure if it's needed to be in approved section. And for the minimal use it's pretty ugly to read. Maybe if you think alike we could think about just GY it, I wouldn't be sad at all. (though suggestion would still be welcome of course)
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Instead of doing so I easily could also ask for a same VectorT from respective type (integer vector in this example), that's true, if you mean this. But having a seperate container for the static types of a sequence instead of dealing with "raw" vector types in running time was probably my preference. And in the container, it's then also already ensured that the vector will never ever change from outside, like the size, because it's copy constructed, and can't be touched anymore.
It makes sense now

I'm gonna go ahead and approve this since I think it has its usefulness and can simplify one's codes in some cases. Approved.
 
Top