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.
  • Monovertex Monovertex:
    How are you all? :D
    +1
  • Ghan Ghan:
    Howdy
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though

      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