Attachment System Challenge - KillCounter

Strilanc

Veteran Scripter
Reaction score
42
You stated that there would be more overhead from cleaning up when a unit is removed instead of when units enter the map. But almost every unit that is removed must have entered the map, so you have the same overhead in both cases.

You mentioned spells, I think you're getting at 'clean it when they cast it' vs 'clean it when they die' and noticing that if lots of units are dying you do more work. That's a valid point, but in that case you should just NOT USE THE CALLBACK. You can still clean the data normally!

Providing the callback allows you to not use a 'pivot array'. The pivot array is bad because it requires a ton more knowledge about how handles work to be sprinkled throughout the code. The callback abstracts all the 'units can be other units' problems away.

Only if you use HAIL, PUI does not have that problem. :p (because PUI depends on it's own custom indexes and not on unit handle)
I was thinking wrong about this one, you're right that you can't hold indexes. HAIL can't have this problem either, because it holds a copy of the handle.

PUI cannot be improved, it is perfect unit indexing.

[...]

You seem to be a little hard in the head and like to answer before you think.
(which is also obvious from our PM discussion)
I am tired of always saying same thing three times so you can finally get it right.
You can't possibly be missing the hypocrisy here. You're claiming PUI is perfect, right after I showed you a system which has features PUI doesn't have. That's pretty hard in the head.

PUI stops you from using unit custom data, can't detect outsides changes to that custom data, has default settings that are set too high, contains testing code that belongs outside the library (a text command to show the index of selected units? seriously? that doesn't belong inside the library), and doesn't tell you when it throws away an index.

Are those major problems? No. But they show PUI isn't perfect. Calling something perfect doesn't make it perfect (perfect things don't have versions, either).
 

Cohadar

master of fugue
Reaction score
209
No sorry, I won't argue with you any more, I have already answered everything in the consistent way.
 
Reaction score
333
JASS:
library Lolz initializer InitLolz needs Hash
globals
             unitsoftype array UnitsOfType
             hashtable         UnitTypeHash
endglobals

struct unitsoftype[ 65500 ]
private     unit    array units[ 100 ]
            integer array value[ 100 ]
private     integer numUnits
private     integer unittypeid
    
    static method create takes integer unittypeid returns unitsoftype
        local integer     index = UnitTypeHash.Search( unittypeid )
        local UnitsOfType new 
        if index == - 1 then
            return UnitsOfType[ index ]
        else
            set index = UnitTypeHash.Hash( unittypeid )            
            set new   = index

            set UnitsOfType[ index ] = new

            set new.numUnits   = -1
            set new.unittypeid = unittypeid
        endif
        return new
    endmethod
    
    method add takes unit toadd, integer unittypeid returns integer
        if unittypeid == .unittypeid then
            set .numUnits = .numUnits + 1
            if .numUnits > 100 then
                set .numUnits = .numUnits - 1
            else
                set .units[ .numUnits ] = toadd
                return .numUnits
            endif
        endif    
        return -1
    endmethod
    
    method find takes unit tofind, integer unittypeid returns integer
        local integer index = 0
        if unittypeid == .unittypeid then
            loop
                exitwhen index > .numUnits
                if .units[ index ] == tofind then
                    return index
                endif
                set index = index + 1
            endloop
        endif
        return -1
    endmethod
        
    method drop takes unit todrop, integer unittypeid returns nothing
        local integer index = .find( todrop, unittypeid )
        if index != -1 then
            set .units[ index ]     = .units[ .numUnits ]
            set .units[ .numUnits ] = null
            set .numUnits           = .numUnits - 1
        endif                        
    endmethod
        
endstruct
            
function SetUnitData takes unit whichunit, integer value returns nothing
    local integer     unittypeid = GetUnitTypeId( whichunit )
    local integer     hashindex  = UnitTypeHash.Search( unittypeid )
    local integer     unitindex
    local unitsoftype targetGroup
    
    if hashindex == - 1 then
        set targetGroup = unitsoftype.create( unittypeid )
    else
        set targetGroup = UnitsOfType[ hashindex ]
    endif
    set unitindex = targetGroup.find( whichunit, unittypeid )
    if unitindex == -1 then
        set unitindex = targetGroup.add( whichunit, unittypeid )
    endif
    set targetGroup.value[ unitindex ] = value
endfunction

function GetUnitData takes unit whichunit returns integer
    local integer     unittypeid = GetUnitTypeId( whichunit )
    local integer     hashindex  = UnitTypeHash.Search( unittypeid )
    local integer     unitindex
    local unitsoftype targetGroup
    
    if hashindex == - 1 then
        return 0
    else
        set targetGroup = UnitsOfType[ hashindex ]
    endif
    set unitindex = targetGroup.find( whichunit, unittypeid )
    if unitindex == -1 then
        
    endif
    return targetGroup.value[ unitindex ]
endfunction

private function InitLolz takes nothing returns nothing
    set UnitTypeHash = hashtable.create()
endfunction

endlibrary
Now here the more interesting version with the H2I exploit:

JASS:
library UnitAttachmentSystem initializer InitUAS needs Hash

globals
    hashtable       H2IHash
    integer   array UnitValue
endglobals

private function H2I takes handle h returns integer
    return h
    return 0
endfunction


function SetUnitData takes unit whichunit, integer value returns nothing
    local integer handleId = H2I( whichunit )
    local integer index    = H2IHash.Search( handleId )
    if index == -1 then
        set index = H2IHash.Hash( handleId )
    endif
    set UnitValue[ index ] = value
endfunction

function GetUnitData takes unit whichunit returns integer
    local integer handleId = H2I( whichunit )
    local integer index    = H2IHash.Search( handleId )
    if index == -1 then
        return 0
    endif
    return UnitValue[ index ]
endfunction

private function InitUAS takes nothing returns nothing
    set H2IHash = hashtable.create()
endfunction

endlibrary
JASS:

library Hash

// What it does
// The Hash system maps integers to slots in an array. This is extremely useful 
// when you are trying to attach something to integers that are greater than 8192.

// How To Use
// First you need to create a Hash Table. At most 25 HashTables can be created!
// local hashtable Table = hashtable.create()
// Now you can attach integers to that hashtable with the Hash method.
// The Hash function will return an integer. Use that integer as array-index when attaching the things to an integer.
// When you need the integer again, just use the Search method. It will return -1 if the item is not found, or the 
// right index elsewise.
// If the key is no longer needed, you can free it by using hashtable.Remove( key ).

// ---- EXAMPLE ----: This saves the Gold Costs of an Item.

//// library WeaponPrice initializer InitPrices needs Hash
//// globals
////     hashtable       WeaponData 
////     integer   array Price
//// endglobals
////
//// function InitPrices takes nothing returns nothing
////     set WeaponData = hashtable.create()
//// endfunction
////
//// function SetItemPrice takes integer itemtypeid, integer price returns nothing
////     local integer index = WeaponData.Hash( itemtypeid )
////     set   Price[ index ] = price
//// endfunction

//// function GetItemPrice takes integer itemtypeid returns integer 
////     local integer index = WeaponData.Search( itemtypeid )
////     if index == -1 then
////        return Price[ index ]
////     endif
////     return 0                         // Return 0 if the item cannot be found
//// endfunction

//// endlibrary


////  ---- API ----
//  1. create
//  2. Hash / HashEx
//  3. Search        
//  4. Remove
//  5. Refresh
//  6. HashSize
                                                                           
//  1. --- method --- hashtable.create( nothing ) returns hashtable

// Creates a new Hashtable.
// Can be used to hash huge integers to a much smaller Universe.
// Useful for hashing ability codes to a normal JASS array.

//  2. --- method --- hashtable.Hash  ( key )         returns integer
//     --- method --- hashtable.HashEx( key, target ) returns nothing

// Hashes a key to a slot. 
// The standard Hash function will return a unique index for each unique key.
// The returned slot will stay the same during one game, but may differ from
// game to game.
// The Extended Hash function ( HashEx ) will allways map the key to the target slot.
// Thus you can specify the

//  3. --- method --- hashtable.Search( key ) returns integer

// Returns the slot previously mapped to the key. 
// If the key was not hashed yet, -1 will be returned.

// Example:

//// function TestSearch takes nothing returns boolean
////     local integer indexA = Data.Hash  ( 5023 )
////     local integer indexB = Data.Search( 5023 )
////     return indexA == indexB // <---- TRUE
//// endfunction
                         
//// function TestFalseSearch takes nothing returns boolean
////     local integer indexA = Data.Hash  ( 5023 )
////     local integer indexB = Data.Search( 5774 )
////     return indexA == indexB // <---- FALSE, as indexB is -1
//// endfunction
                                                  
                                                                           
//  4. --- method --- hashtable.Remove( key ) returns hashtable

// Recycles a key from the hashtable when no longer needed.
// Used to free slots in the hashtable to allow for more keys to be hashed.
// Recycled slots do not increase the search performance!
// Automatically applies Refresh ( see point 5 )
//  when more than 1/3rd of the Hashtable was recycled.
// Please allways change your hashtable variable to the returned hashtable
//  to prevent data loss.

// Example:

////
//// function Test2 takes nothing returns boolean
////     set Data = Data.Remove( 5023 ) // Needs to be done. If Data would not be set to the new one,
                                        //  the table might be refreshed resulting in a complete data loss!
////     return Data.Search( 5023 ) == - 1  // <--- TRUE
//// endfunction

//  5. --- method --- hashtable.Refresh( nothing ) returns hashtable

// Creates a new Hashtable and hashes all values from the old one to the new one.
// During this process, the old Hashtalble is destroyed.
// Accomplishes the performance loss when there were too many key deletions.
// Automatically applied when more than 1/3rd of the Hashtable was recycled.
// Please allways change your hashtable variable to the returned hashtable
//  to prevent data loss.

// 6. --- constant --- HashSize is integer

// The maximum number of keys that can be saved to a HashTable.
// A high number allows for more slots and makes the Table faster,
// but can also result in weak memory usage.
// Primes work best as Hashsizes.






globals
    // settings
    constant integer HashSize = 113 // Please enter a prime here that is as close to the number of slots you need as possible.
    
    // End of settings. Please do not apply changes to the following variables.                                                
    constant real    Multiplicator = ( SquareRoot( 5 ) - 1 ) / 2
    constant integer HashSlots = 25000
endglobals
             

private function KeyHash takes integer key returns integer
    local  real product = key * Multiplicator
    return R2I( HashSize * ( product - R2I( product ) ) )
endfunction

private function SlotHash takes integer key returns integer
    return ModuloInteger( key * 2, HashSize - 1 ) + 1
endfunction

struct hashtable[ HashSlots ]
    integer array Key [ HashSize ]
    integer array Slot[ HashSize ]
    
    integer array Base  [ HashSize ]
    integer array BaseId[ HashSize ]
    integer       keys
    
    integer       recycled = 0
    
    static method create takes nothing returns hashtable
        local hashtable new = hashtable.allocate()
        local integer index = 0
        set   new.keys = 0
        loop
            exitwhen index > HashSize
            set new.Key[ index ] = -1
            set index = index + 1
        endloop
        return new
    endmethod
                
       
    
    method Hash takes integer key returns integer
        local integer loopId   = 0
        local integer keyhash  = KeyHash ( key )
        local integer slothash = SlotHash( key )
        local integer slot     = keyhash
        loop
            exitwhen .Key[ slot ] == -1
            exitwhen .Key[ slot ] ==  0

            if .Key[ slot ] == key then
                return slot            
            elseif loopId > 10 then
                return -1
            endif
            
        
            set loopId = loopId  + 1
            set slot   = ModuloInteger( keyhash + loopId * slothash, HashSize )
        endloop
        set .Key [ slot ] = key
        set .Slot[ slot ] = slot
        
        set .BaseId[  slot ] = .keys
        set .Base  [ .keys ] =  key
        set .keys = .keys + 1
        return slot
    endmethod
    
    
    
    method HashEx takes integer key, integer target returns nothing
        local integer loopId   = 0
        local integer keyhash  = KeyHash ( key )
        local integer slothash = SlotHash( key )
        local integer slot     = keyhash
        loop
            exitwhen .Key[ slot ] == -1
            exitwhen .Key[ slot ] ==  0
            
            if .Key[ slot ] == key then
                return
            elseif loopId > 10 then
                return
            endif
            
        
            set loopId = loopId  + 1
            set slot   = ModuloInteger( keyhash + loopId * slothash, HashSize )
        endloop
        set .Key [ slot ] = key
        set .Slot[ slot ] = target
        
        set .BaseId[  slot ] = .keys
        set .Base  [ .keys ] =  key
        set .keys = .keys + 1
    endmethod

    method Search takes integer key returns integer
        local integer loopId   = 0
        local integer keyhash  = KeyHash ( key )
        local integer slothash = SlotHash( key )
        local integer slot     = keyhash
        loop
            exitwhen .Key[ slot ] == key            
            if .Key[ slot ] == -1 then
                return -1
            elseif loopId > 10 then
                return -1
            endif
            
            set loopId = loopId  + 1
            set slot   = ModuloInteger( keyhash + loopId * slothash, HashSize )
        endloop
        return .Slot[ slot ]
    endmethod
    
    method Refresh takes nothing returns hashtable
        local hashtable new      = hashtable.create()
        local integer   keyIndex = 0
        local integer   newSlot
        local integer   oldSlot
        local integer   curKey
        
        loop
            exitwhen keyIndex > .keys
            set curKey   =    .Base  [ keyIndex ]
            set newSlot  = new.Hash  ( curKey   )  
            set oldSlot  =    .Search( curKey   )
            set new.Slot[ newSlot ] = oldSlot             
            set keyIndex = keyIndex + 1            
        endloop
        call .destroy()
        return new
    endmethod   
    
    method Remove takes integer key returns hashtable
        local integer slot = .Search( key )

        set .Key [ slot ] =  0
        set .Slot[ slot ] = -1
        
        set .Base  [ .BaseId[ slot ] ] = .Base[ .keys ]
        set .BaseId[ slot ] = .BaseId[ .keys ]

        set .Base  [ .BaseId[ .keys ] ] = -1
        set .BaseId[ .keys ] = -1 
        set .keys  = .keys - 1
        
        set .recycled = .recycled + 1
        if .recycled > HashSize / 3 then
            return .Refresh()
        endif
        return this
    endmethod

endstruct

endlibrary

Usually I don't // on other peoples code style or indentation but damn, that is really ugly code.
 
Reaction score
456
Usually I don't // on other peoples code style..
=
Usually I don't comment on other peoples code style..

Probably means that :p..
 
Reaction score
333
JASS:
//
    integer array Key [ HashSize ]
    integer array Slot[ HashSize ]
    
    integer array Base  [ HashSize ]
    integer array BaseId[ HashSize ]
    integer       keys


The excessive spacing, mainly. Why not:

JASS:
//
    integer array BaseId[HashSize]
    integer array Base[HashSize]
    integer array Slot[HashSize]
    integer array Key[HashSize]
    integer keys


JASS:
struct unitsoftype[ 65500 ]
private     unit    array units[ 100 ]
            integer array value[ 100 ]
private     integer numUnits
private     integer unittypeid


Weird indentation makes this messier and harder to read.

Consider:

JASS:
struct unitsoftype[65500]
    private unit array units[100]
    private integer numUnits
    private integer unittypeid
    integer array value[100]


It may seem as though aligning everything makes your code much easier and nicer, but it really doesn't. That sort of style is more suited to a highly free-form language like C or Perl.
 

quraji

zap
Reaction score
144
It's the sort of thing that comes down to personal preference. I'm sure he understands the indentations and spacing and can read the code well, unlike you (or me :p).

It really only matters if other people will look at or work on the code, otherwise just write in whatever style suits you. Of course, I can't really see the advantage of writing like that (and it's not good practice - for professionals).

My two cents :thup:
 

SerraAvenger

Cuz I can
Reaction score
234
JASS:
//
    integer array Key [ HashSize ]
    integer array Slot[ HashSize ]
    
    integer array Base  [ HashSize ]
    integer array BaseId[ HashSize ]
    integer       keys


The excessive spacing, mainly. Why not:

JASS:
//
    integer array BaseId[HashSize]
    integer array Base[HashSize]
    integer array Slot[HashSize]
    integer array Key[HashSize]
    integer keys


JASS:
struct unitsoftype[ 65500 ]
private     unit    array units[ 100 ]
            integer array value[ 100 ]
private     integer numUnits
private     integer unittypeid


Weird indentation makes this messier and harder to read.
I agree to this part. That was really messy. Should have been:

JASS:
struct unitsoftype[ 65500 ]
    private     unit    array units[ 100 ]
                integer array value[ 100 ]
    private     integer       numUnits
    private     integer       unittypeid




It may seem as though aligning everything makes your code much easier and nicer, but it really doesn't. That sort of style is more suited to a highly free-form language like C or Perl.
Hm I really can read my code a bit better with this style. It seems much more structured ; )

However if you say so, I shall change my style concerning JASS.
It would really be cool if there were some global conventions... Mind Inventing some and then posting it in the tutorial page?
 

Cohadar

master of fugue
Reaction score
209
You are going off topic people.
Besides this threads topic is over....
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      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