- Joined
- Sep 26, 2009
- Messages
- 9,534
nice, thank you for testing it.
Approved now that I feel confident that its bugs are cleared.
I wasn't aware there were any moderators in the JASS section these days, especially if you look at the submission queue.
static method onLoop takes nothing returns nothing
local thistype this = l.first
local unit u
local integer i
local thistype node
local thistype n
local integer ui
while (this != l.sentinel)
if (.active) then
set .time = .time - period
if (.time <= 0.0) then
call GroupEnumUnitsInRange(g, .cenX, .cenY, CAMP_CHECK_AOE, function AliveHeroes)
if ((FirstOfGroup(g) == null or not .checkAoe) and (.cc == 0 or not .checkCount)) then
set node = .ulist.first
while (node != .ulist.sentinel)
call node.uPool.print()
set i = node.max
// LOOK HERE
// So basically I'm looping through a list containing multiple IPools
// I made it so IPool.print method prints out Object Names instead of the id's
// I'm just using the ui integer because I suspect the IPool.getItem() method
// returning the wrong values
while (i > 0 and .cc < .mc)
set ui = node.uPool.getItem()
call Print(GetObjectName(ui), 5)
set u = CreateUnit(Player(12), ui, GetRandomReal(.minX, .maxX), GetRandomReal(.minY, .maxY), 0.)
set n = .units.enqueue()
set n.u = u
set ht[0][GetHandleId(u)] = this
set ht[1][GetHandleId(u)] = n
set .cc = .cc + 1
set i = i - 1
set u = null
endwhile
set node = node.next
endwhile
endif
set .time = .ogt
endif
endif
set this = .next
endwhile
set updateTime = updateTime - period
if (updateTime <= 0.0) then
// Periodically update IPool units...
if (weakCamp.contains(MURGUL_SLAVE_ID)) then
call weakCamp.remove(MURGUL_SLAVE_ID)
call weakCamp.shiftWeight(MURGUL_CASTER_ID, 1)
call weakCamp.add(MAKURA_TIDEBRINGER_ID, 1)
call strongCamp.remove(MAKURA_TIDEBRINGER_ID)
call strongCamp.add(MURGUL_SEEKER_ID, 1)
elseif (weakCamp.contains(MURGUL_CASTER_ID)) then
call weakCamp.remove(MURGUL_CASTER_ID)
call weakCamp.shiftWeight(MURGUL_HUNTER_ID, 1)
call weakCamp.add(MAKURA_DWELLER_ID, 1)
call strongCamp.remove(MAKURA_DWELLER_ID)
call strongCamp.add(MURGUL_TIDERUNNER_ID, 1)
elseif (weakCamp.contains(MURGUL_HUNTER_ID)) then
call weakCamp.remove(MURGUL_HUNTER_ID)
call weakCamp.shiftWeight(MAKURA_TIDEBRINGER_ID, 1)
call weakCamp.add(MURGUL_SEEKER_ID, 1)
call strongCamp.remove(MURGUL_SEEKER_ID)
call strongCamp.add(MURGUL_FLESHEATER_ID, 1)
elseif (weakCamp.contains(MAKURA_TIDEBRINGER_ID)) then
call weakCamp.remove(MAKURA_TIDEBRINGER_ID)
call weakCamp.add(MURGUL_TIDERUNNER_ID, 1)
call weakCamp.shiftWeight(MAKURA_DWELLER_ID, 1)
call strongCamp.remove(MURGUL_TIDERUNNER_ID)
call strongCamp.add(SEA_TURTLE_ID, 1)
elseif (weakCamp.contains(MAKURA_DWELLER_ID)) then
call weakCamp.remove(MAKURA_DWELLER_ID)
call weakCamp.shiftWeight(MURGUL_SEEKER_ID, 1)
call weakCamp.add(MURGUL_FLESHEATER_ID, 1)
call strongCamp.remove(MURGUL_FLESHEATER_ID)
call strongCamp.add(MAKURA_SNAPPER_ID, 1)
elseif (weakCamp.contains(MURGUL_SEEKER_ID)) then
call weakCamp.remove(MURGUL_SEEKER_ID)
call weakCamp.shiftWeight(MURGUL_TIDERUNNER_ID, 1)
call weakCamp.add(SEA_TURTLE_ID, 1)
call strongCamp.remove(SEA_TURTLE_ID)
call strongCamp.add(GARGANTUAN_TURTOISE_ID, 1)
endif
set updateTime = PHASE_TIME
endif
set u = null
endmethod
static method onInit takes nothing returns nothing
set weakCamp = IPool.create()
call weakCamp.add(MURGUL_SLAVE_ID, 2)
call weakCamp.add(MURGUL_CASTER_ID, 1)
call weakCamp.add(MURGUL_HUNTER_ID, 1)
set strongCamp = IPool.create()
call strongCamp.add(MAKURA_TIDEBRINGER_ID, 1)
call strongCamp.add(MAKURA_DWELLER_ID, 1)
endmethod
scope Test
globals
IPool camp1
IPool camp2
endglobals
private struct xe extends array
implement SharedList
rect r
thistype ulist
IPool uPool
integer max
static integer ic = 0
static trigger t = CreateTrigger()
static thistype l
static group g = CreateGroup()
static method killAll takes nothing returns boolean
call RemoveUnit(GetFilterUnit())
return false
endmethod
static method onChat takes nothing returns boolean
local string str = GetEventPlayerChatString()
local thistype this
local thistype node
local integer id
local integer i = GetRandomInt(0, ic-1)
if (str == "-upg") then
if (camp1.contains('hpea')) then
call camp1.remove('hpea')
call camp1.add('hmtm', 1)
call camp2.remove('hmtm')
call camp2.add('hgry', 1)
elseif (camp1.contains('hfoo')) then
call camp1.remove('hfoo')
call camp1.add('hgyr', 1)
call camp2.remove('hgyr')
call camp2.add('hmpr', 1)
elseif (camp1.contains('hkni')) then
call camp1.remove('hkni')
call camp1.add('hgry', 1)
call camp2.remove('hgry')
call camp2.add('hsor', 1)
elseif (camp1.contains('hmtm')) then
call camp1.remove('hmtm')
call camp1.add('hmpr', 1)
call camp2.remove('hmpr')
call camp2.add('hmtt', 1)
elseif (camp1.contains('hgyr')) then
call camp1.remove('hgyr')
call camp1.add('hsor', 1)
call camp2.remove('hsor')
call camp2.add('hspt', 1)
elseif (camp1.contains('hgry')) then
call camp1.remove('hgry')
call camp1.add('hmtt', 1)
call camp2.remove('hmtt')
call camp2.add('hdhw',1)
endif
elseif (str == "-make") then
set this = l.first
while (this != l.sentinel and i > 0)
set this = .next
set i = i - 1
endwhile
set node = .ulist.first
while(node != .ulist.sentinel)
set i = node.max
call node.uPool.print()
while (i > 0)
set id = node.uPool.getItem()
call BJDebugMsg(GetObjectName(id))
call CreateUnit(Player(0), id, GetRectCenterX(.r), GetRectCenterY(.r), 0.)
set i = i - 1
endwhile
set node = node.next
endwhile
else
call GroupEnumUnitsOfPlayer(g, Player(0), function thistype.killAll)
endif
return false
endmethod
static method new takes rect r returns thistype
local thistype this = l.enqueue()
set .r = r
set .ulist = create()
set ic = ic + 1
return this
endmethod
method add takes IPool uPool, integer max returns nothing
local thistype node = .ulist.enqueue()
set node.uPool = uPool
set node.max = max
endmethod
static method onInit takes nothing returns nothing
local thistype this
local thistype node
set l = create()
set camp1 = IPool.create()
call camp1.add('hpea', 1)
call camp1.add('hfoo', 1)
call camp1.add('hkni', 1)
set camp2 = IPool.create()
call camp2.add('hmtm', 1)
call camp2.add('hgyr', 1)
set this = new(gg_rct_1)
call .add(camp1, 3)
call .add(camp2, 2)
set this = new(gg_rct_2)
call .add(camp1, 2)
set this = new(gg_rct_3)
call .add(camp1, 2)
call .add(camp2, 1)
set this = new(gg_rct_4)
call .add(camp1, 3)
set this = new(gg_rct_5)
call .add(camp1, 3)
call .add(camp2, 2)
call TriggerRegisterPlayerChatEvent(t, Player(0), "-upg", true)
call TriggerRegisterPlayerChatEvent(t, Player(0), "-make", true)
call TriggerRegisterPlayerChatEvent(t, Player(0), "-clear", true)
call TriggerAddCondition(t, Filter(function thistype.onChat))
endmethod
endstruct
endscope
itempool
does these native types work? How to use them?library IPool requires Table, Alloc
private module Init
private static method onInit takes nothing returns nothing
set .tar = TableArray[8192]
endmethod
endmodule
private struct data extends array
static TableArray tar
integer int
integer locks
integer weight
implement Alloc
implement Init
endstruct
private function Create takes nothing returns data
local data this = data.allocate()
set this.locks = 0
return this
endfunction
private function Destroy takes data this returns boolean
if this.locks == -1 or this == 0 then
debug call BJDebugMsg("IPool Error: Attempt to double-free instance!")
return false
endif
set this.locks = -1
call this.deallocate()
return true
endfunction
private function Lock takes data i returns nothing
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to lock a destroyed instance!")
return
endif
set i.locks = i.locks + 1
endfunction
private function Unlock takes data i returns boolean
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to unlock destroyed instance!")
return false
endif
set i.locks = i.locks - 1
return i.locks == 0
endfunction
struct IPool extends array
method operator weight takes nothing returns integer
return data(this).weight
endmethod
private method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator table takes nothing returns Table
return data(this).int
endmethod
private method operator table= takes Table t returns nothing
set data(this).int = t
endmethod
static method create takes nothing returns thistype
local thistype this = Create()
set this.table = Table.create()
return this
endmethod
method flush takes nothing returns nothing
call this.table.flush()
call data.tar[this].flush()
set this.weight = 0
endmethod
method destroy takes nothing returns nothing
if Destroy(this) then
call this.table.destroy()
call data.tar[this].flush()
set this.weight = 0
endif
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
//One-liner method to get a random item from the pool based on weight
method getItem takes nothing returns integer
return this.table[GetRandomInt(0, this.weight -1)]
endmethod
method weightOf takes integer value returns integer
return data.tar[this][value]
endmethod
method chanceOf takes integer value returns real //returns between 0. and 1.
return this.weightOf(value) / (this.weight + 0.) //don't divide by 0 here or else!
endmethod
method contains takes integer value returns boolean
return data.tar[this].has(value)
endmethod
method add takes integer value, integer lbs returns nothing
local Table tb = this.table
local integer i = this.weight
if lbs < 1 then
debug call BJDebugMsg("IPool Error: Tried to add value with invalid weight!")
return
endif
set data.tar[this][value] = data.tar[this][value] + lbs
set lbs = i + lbs
loop
exitwhen i == lbs
set tb[i] = value //treat this.table as an array
set i = i + 1
endloop
endmethod
method remove takes integer value returns nothing
local Table tb = this.table
local Table new
local integer i = this.weight
local integer n = 0
local integer val
if not this.contains(value) then
debug call BJDebugMsg("IPool Error: Attempt to remove un-added instance!")
return
endif
set new = Table.create()
set this.table = new
loop
set i = i - 1
set val = tb[i]
if val != value then
set new[n] = val //write to the new Table without gaps
set n = n + 1
endif
exitwhen i == 0
endloop
set this.weight = n //lower pool weight
call tb.destroy() //abandon old Table instance
call data.tar[this].remove(value) //clear the value's weight now that it's gone
endmethod
method copy takes nothing returns thistype
local thistype new = .create()
local integer i = this.weight
local Table tt = this.table
local Table nt = new.table
local Table dt = data.tar[new]
local integer val
if this.weight == 0 then
debug call BJDebugMsg("IPool Error: Attempt to copy invalid instance!")
call new.destroy()
return 0
endif
set new.weight = i
loop
set i = i - 1
exitwhen i == 0
set val = tt[i]
set nt[i] = val
set dt[val] = dt[val] + 1
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing //print the array of the pool
local string s = "IPool: |cffffcc33"
local integer i = this.weight
local Table t = this.table
loop
set i = i - 1
exitwhen i <= 0
set s = s + "[" + I2S(t[i]) + "]"
endloop
call BJDebugMsg(s + "|r")
endmethod
endif
endstruct
//New struct to handle deliberately-rare chances
struct SubPool extends array
private IPool iPool //for association if you want it
private thistype nest //you can nest IPoolMinis for poolception
//you can change a value's weight via subpoolinstance[value].weight = blah
//you can also change the entire pool's weight via subpoolinstance.weight = blah.
method operator weight takes nothing returns integer
return data(this).weight
endmethod
method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator value takes nothing returns integer
return data(this).int
endmethod
private method operator value= takes integer val returns nothing
set data(this).int = val
endmethod
private thistype next
private thistype prev
method operator pool takes nothing returns IPool
return this.iPool
endmethod
method operator pool= takes IPool ip returns nothing
if ip != 0 then
call ip.lock()
endif
if this.iPool != 0 then
call this.iPool.unlock()
endif
set this.iPool = ip
endmethod
static method create takes integer totalWeight returns thistype
local thistype this = Create()
set this.next = this
set this.prev = this //I'm my own best friend
set this.weight = totalWeight
return this
endmethod
method destroy takes nothing returns nothing
local thistype curr = this
if this.next == -1 then
debug call BJDebugMsg("SubPool Error: Attempt to double-free!")
return
endif
loop
set curr = curr.next
call Destroy(curr) //destroy all the things
exitwhen curr == this
endloop
set this.next = -1
set this.pool = 0
if this.nest != 0 then
if data(this.nest).locks == 1 then
call this.nest.destroy()
else
call Unlock(this.nest)
endif
set this.nest = 0
endif
call data.tar[this].flush()
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
method operator subPool takes nothing returns IPool
return this.nest
endmethod
method operator subPool= takes thistype ip returns nothing
if this == ip then
debug call BJDebugMsg("SubPool Error: Don't set a subPool within itself. Use the .copy() method, instead.")
return
endif
if ip != 0 then
call ip.lock()
endif
if this.nest != 0 then
call this.nest.unlock()
endif
set this.nest = ip
endmethod
method add takes integer val, integer lbs returns nothing
local thistype new
if lbs <= 0 then
debug call BJDebugMsg("SubPool Error: Don't add a value without weight")
return
endif
if data.tar[this].has(val) then
set new = data.tar[this][val]
set new.weight = new.weight + lbs
return
endif
set new = Create()
set new.prev = this.prev
set this.prev.next = new
set this.prev = new
set new.next = this
set new.value = val
set new.weight = lbs
set data.tar[this][val] = new
endmethod
method contains takes integer val returns boolean
return data.tar[this].has(val)
endmethod
method operator [] takes integer val returns thistype
return data.tar[this][val]
endmethod
method operator []= takes integer val, integer newWeight returns nothing
set this[val].weight = newWeight
endmethod
method remove takes integer val returns nothing
local thistype node = this[val]
if Destroy(node) then
set node.prev.next = node.next
set node.next.prev = node.prev
call data.tar[this].remove(val)
else
debug call BJDebugMsg("SubPool Error: Attempt to remove non-added value")
endif
endmethod
method getItem takes nothing returns integer
local thistype curr = this
local integer i = GetRandomInt(1, this.weight)
loop
set curr = curr.next
set i = i - curr.weight
exitwhen i <= 0
endloop
if curr == this then
if this.nest != 0 then
set i = this.nest.getItem()
else
set i = 0
endif
if i == 0 and this.pool != 0 then //if no low-probability item could be found...
set i = this.pool.getItem() //pick a random int from main pool
endif
else
set i = curr.value
endif
return i
endmethod
method copy takes nothing returns thistype
local thistype new = .create(this.weight)
local thistype curr = this
set new.pool = this.iPool
set new.subPool = this.nest
loop
set curr = curr.next
exitwhen curr == this
call new.add(curr.value, curr.weight)
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing
local thistype curr = this
local string s = "SubPool: |cffffcc33"
loop
set curr = curr.next
exitwhen curr == this
set s = s + "[" + I2S(curr.value) + "]<" + I2S(curr.weight) + ">"
endloop
call BJDebugMsg(s + "|r")
if this.nest != 0 then
call this.nest.print()
endif
if this.iPool != 0 then
call this.iPool.print()
endif
endmethod
endif
endstruct
endlibrary
local IPoolMini i = IPoolMini.create()
call i.add('hkni',1,100) //has a 1/100 chance to be picked
call i.add('Hpal',2,53) //has a 2/53 chance to be picked
set i.pool = IPool.create() //FYI multiple IPoolMinis could share the same IPool
call i.pool.add('hpea',6)
call i.pool.add('hfoo',2)
call i.getItem() //if lucky, an item from IPoolMini will be returned. Otherwise,
//the IPool will return one of its items for you. Similarly, 0 will be returned
//if no IPool was preset.
call i.pool.destroy()
call i.destroy() //does not destroy the "pool" member which is why I destroyed it manually
iPoolInstance.add(0, iPoolInstance.weight*2)
to give a 1/3 chance for anything in the pool to be picked.ipm = IPoolMini.create(1000) //each entry has an x/1000 chance
ipm.add('A001', 10) //has a 1/100 chance to be picked
ipm.add('A007', 100) //has a 1/10 chance to be picked
ipm.add('A036', 1) //will have a 1/1000 chance to be picked
ipm.getItem() //has an 11.1% chance of picking something
ipm.weight = 500 //doubles the chance something will be picked
ipm['A007'] = 200 //Now has a 40% chance to be picked
ipm.pool = myGlobalIPool //still supported of course
Say for example you have an IPool that is used by several IPoolMinis and maybe even standalone. You have to manually create it - so manually destroying it comes with the territory.
local IPool pool = IPool.create()
call pool.add('unt1', 0, 0.05) // Group 0, probability 5%
call pool.add('unt2', 0, 0.07) // Group 0, probability 7%
call pool.add('unt3', 0, 0.10) // Group 0, probability 10%
call pool.add('unt4', 1, 0.50) // Group 1, probability 50%
call pool.add('unt5', 1, 0.30) // Group 1, probability 30%
call pool.getItem() // 22% for group 0 and 80% for group 1, rest is null
call pool.destroy() // Just one destroy
local IPoolMini pool = IPoolMini.create(100)
call pool.add('unt1', 5) // probability 5%
call pool.add('unt2', 7) // probability 7%
call pool.add('unt3', 10) // probability 10%
set pool.pool = IPool.create()
call pool.pool.add('unt4', 5) // probability 50%
call pool.pool.add('unt5', 3) // probability 30%
call pool.pool.add(0, 2)
call pool.getItem() // 22% for IPoolMini and 80% for IPool, rest is null
call pool.destroy() // Just one destroy
local SubPool pool = SubPool.create(100)
call pool.add('unt1', 5) // probability 5%
call pool.add('unt2', 7) // probability 7%
call pool.add('unt3', 10) // probability 10%
set pool.subPool = SubPool.create(10)
call pool.subPool.add('unt4', 5) // probability 50%
call pool.subPool.add('unt5', 3) // probability 30%
call pool.getItem() // 22% for first SubPool and 80% for nested SubPool(s).
call pool.destroy() // Just one destroy
library IPool requires Table, Alloc
private module Init
private static method onInit takes nothing returns nothing
set .tar = TableArray[8192]
endmethod
endmodule
private struct data extends array
static TableArray tar
integer int
integer locks
integer weight
implement Alloc
implement Init
endstruct
private function Create takes nothing returns data
local data this = data.allocate()
set this.locks = 0
return this
endfunction
private function Destroy takes data this returns boolean
if this.locks == -1 or this == 0 then
debug call BJDebugMsg("IPool Error: Attempt to double-free instance!")
return false
endif
set this.locks = -1
call this.deallocate()
return true
endfunction
private function Lock takes data i returns nothing
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to lock a destroyed instance!")
return
endif
set i.locks = i.locks + 1
endfunction
private function Unlock takes data i returns boolean
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to unlock destroyed instance!")
return false
endif
set i.locks = i.locks - 1
return i.locks == 0
endfunction
struct IPool extends array
method operator weight takes nothing returns integer
return data(this).weight
endmethod
private method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator table takes nothing returns Table
return data(this).int
endmethod
private method operator table= takes Table t returns nothing
set data(this).int = t
endmethod
static method create takes nothing returns thistype
local thistype this = Create()
set this.table = Table.create()
return this
endmethod
method flush takes nothing returns nothing
call this.table.flush()
call data.tar[this].flush()
set this.weight = 0
endmethod
method destroy takes nothing returns nothing
if Destroy(this) then
call this.table.destroy()
call data.tar[this].flush()
set this.weight = 0
endif
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
//One-liner method to get a random item from the pool based on weight
method getItem takes nothing returns integer
return this.table[GetRandomInt(0, this.weight -1)]
endmethod
method weightOf takes integer value returns integer
return data.tar[this][value]
endmethod
method chanceOf takes integer value returns real //returns between 0. and 1.
return this.weightOf(value) / (this.weight + 0.) //don't divide by 0 here or else!
endmethod
method contains takes integer value returns boolean
return data.tar[this].has(value)
endmethod
method add takes integer value, integer lbs returns nothing
local Table tb = this.table
local integer i = this.weight
if lbs < 1 then
debug call BJDebugMsg("IPool Error: Tried to add value with invalid weight!")
return
endif
set data.tar[this][value] = data.tar[this][value] + lbs
set lbs = i + lbs
loop
exitwhen i == lbs
set tb[i] = value //treat this.table as an array
set i = i + 1
endloop
endmethod
method remove takes integer value returns nothing
local Table tb = this.table
local Table new
local integer i = this.weight
local integer n = 0
local integer val
if not this.contains(value) then
debug call BJDebugMsg("IPool Error: Attempt to remove un-added instance!")
return
endif
set new = Table.create()
set this.table = new
loop
set i = i - 1
set val = tb[i]
if val != value then
set new[n] = val //write to the new Table without gaps
set n = n + 1
endif
exitwhen i == 0
endloop
set this.weight = n //lower pool weight
call tb.destroy() //abandon old Table instance
call data.tar[this].remove(value) //clear the value's weight now that it's gone
endmethod
method copy takes nothing returns thistype
local thistype new = .create()
local integer i = this.weight
local Table tt = this.table
local Table nt = new.table
local Table dt = data.tar[new]
local integer val
if this.weight == 0 then
debug call BJDebugMsg("IPool Error: Attempt to copy invalid instance!")
call new.destroy()
return 0
endif
set new.weight = i
loop
set i = i - 1
exitwhen i == 0
set val = tt[i]
set nt[i] = val
set dt[val] = dt[val] + 1
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing //print the array of the pool
local string s = "IPool: |cffffcc33"
local integer i = this.weight
local Table t = this.table
loop
set i = i - 1
exitwhen i <= 0
set s = s + "[" + I2S(t[i]) + "]"
endloop
call BJDebugMsg(s + "|r")
endmethod
endif
endstruct
//New struct to handle deliberately-rare chances
struct SubPool extends array
private IPool iPool //for association if you want it
private thistype nest //you can nest IPoolMinis for poolception
//you can change a value's weight via subpoolinstance[value].weight = blah
//you can also change the entire pool's weight via subpoolinstance.weight = blah.
method operator weight takes nothing returns integer
return data(this).weight
endmethod
method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator value takes nothing returns integer
return data(this).int
endmethod
private method operator value= takes integer val returns nothing
set data(this).int = val
endmethod
private thistype next
private thistype prev
method operator pool takes nothing returns IPool
return this.iPool
endmethod
method operator pool= takes IPool ip returns nothing
if ip != 0 then
call ip.lock()
endif
if this.iPool != 0 then
call this.iPool.unlock()
endif
set this.iPool = ip
endmethod
static method create takes integer totalWeight returns thistype
local thistype this = Create()
set this.next = this
set this.prev = this //I'm my own best friend
set this.weight = totalWeight
return this
endmethod
method destroy takes nothing returns nothing
local thistype curr = this
if this.next == -1 then
debug call BJDebugMsg("SubPool Error: Attempt to double-free!")
return
endif
loop
set curr = curr.next
call Destroy(curr) //destroy all the things
exitwhen curr == this
endloop
set this.next = -1
set this.pool = 0
if this.nest != 0 then
if data(this.nest).locks == 1 then
call this.nest.destroy()
else
call Unlock(this.nest)
endif
set this.nest = 0
endif
call data.tar[this].flush()
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
method operator subPool takes nothing returns IPool
return this.nest
endmethod
method operator subPool= takes thistype ip returns nothing
if this == ip then
debug call BJDebugMsg("SubPool Error: Don't set a subPool within itself. Use the .copy() method, instead.")
return
endif
if ip != 0 then
call ip.lock()
endif
if this.nest != 0 then
call this.nest.unlock()
endif
set this.nest = ip
endmethod
method add takes integer val, integer lbs returns nothing
local thistype new
if lbs <= 0 then
debug call BJDebugMsg("SubPool Error: Don't add a value without weight")
return
endif
if data.tar[this].has(val) then
set new = data.tar[this][val]
set new.weight = new.weight + lbs
return
endif
set new = Create()
set new.prev = this.prev
set this.prev.next = new
set this.prev = new
set new.next = this
set new.value = val
set new.weight = lbs
set data.tar[this][val] = new
endmethod
method contains takes integer val returns boolean
return data.tar[this].has(val)
endmethod
method operator [] takes integer val returns thistype
return data.tar[this][val]
endmethod
method operator []= takes integer val, integer newWeight returns nothing
set this[val].weight = newWeight
endmethod
method remove takes integer val returns nothing
local thistype node = this[val]
if Destroy(node) then
set node.prev.next = node.next
set node.next.prev = node.prev
call data.tar[this].remove(val)
else
debug call BJDebugMsg("SubPool Error: Attempt to remove non-added value")
endif
endmethod
method getItem takes nothing returns integer
local thistype curr = this
local integer i = GetRandomInt(1, this.weight)
loop
set curr = curr.next
set i = i - curr.weight
exitwhen i <= 0
endloop
if curr == this then
if this.nest != 0 then
set i = this.nest.getItem()
else
set i = 0
endif
if i == 0 and this.pool != 0 then //if no low-probability item could be found...
set i = this.pool.getItem() //pick a random int from main pool
endif
else
set i = curr.value
endif
return i
endmethod
method copy takes nothing returns thistype
local thistype new = .create(this.weight)
local thistype curr = this
set new.pool = this.iPool
set new.subPool = this.nest
loop
set curr = curr.next
exitwhen curr == this
call new.add(curr.value, curr.weight)
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing
local thistype curr = this
local string s = "SubPool: |cffffcc33"
loop
set curr = curr.next
exitwhen curr == this
set s = s + "[" + I2S(curr.value) + "]<" + I2S(curr.weight) + ">"
endloop
call BJDebugMsg(s + "|r")
if this.nest != 0 then
call this.nest.print()
endif
if this.iPool != 0 then
call this.iPool.print()
endif
endmethod
endif
endstruct
endlibrary
library Pool
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script gives the user access to general integer pools. A pool is a data
//* structure that allows you to give entries to it a weight. This weight
//* essentially scales how likely certain random accesses to the pool will be
//* returned by the internal .getRandomInt method. Pools can be useful in any
//* number of ways, such as item drops, randomizing enemy encounters, randomly
//* selecting rects weighted by area, and so forth.
//*
//******************************************************************************
//*
//* Example usage:
//* local intpool ip = intpool.create()
//* call ip.addInt(1, 1.0)
//* call ip.addInt(2, 0.5)
//* call ip.getRInt()
//* call ip.getChance(2)
//* call ip.getWeight(2)
//* call ip.removeInt(1)
//* //or
//8 globals
/// intpool firstip
/// intpool secondip
/// endglobals
// //later on:
/// set firstip=intpool.create()
//* call firstip.addInt(1, 1.0)
//* call firstip.addInt(2, 0.5)
//* call firstip.getRInt()
//* call firstip.getChance(2)
//* call firstip.getWeight(2)
//* call firstip.removeInt(1)
//* //etc...
/// //global intpool are usually easier to use because you don't have to keep
/// //creating them over and over again and unless they need to change with
/// //events or time or whatever you only need 1 line to use them.
/// //that is, call nameofintpool.getRInt()
///
//* You will first need to create an intpool as shown above. Once you've done
//* that, you may use the .addInt method to add an entry to the pool with a
//* specific weight. The example above adds 2 integers, one twice as likely to
//* be randomly selected as the other. That means for the above example, the
//* .getRInt method will 66% of the time return 1 and 33% of the time
//* return 2. If you want to remove an entry from an intpool, the .removeInt
//* method is what you will want to use. If you would like to update an entry's
//* weight after already adding it, simply use .addInt again with the new
//* weight.
//*
//* The .getChance and .getWeight methods are there for convenience. If you are
//* interested in the exact chance of the intpool returning a specific entry,
//* then you should use .getChance to obtain the decimal chance out of 1. If you
//* want to know the weight input for a specific entry, .getWeight will return
//* that for you.
//*
//* When adding an entry to the intpool with .addInt, the actual magnitude of
//* the weight doesn't matter. What matters is its magnitude relative to the
//* magnitudes of all other entries in the intpool. This means that it is ok to
//* use very large or very small weights and is done at the user's discretion.
//*
//* It is worth noting that if you use .getRInt on an intpool with no
//* entries, the function will return INTPOOL_NO_ENTRIES, which is about as
//* random an integer as possible so as to avoid people accidentally using it.
//*
globals
//These constants can be changed//i think he meant to say can change the value
private constant integer MAX_INSTANCES = 2500//8191//current is about 2?
//i don't understand how the numbers work...
private constant integer MAX_ENTRIES = 150//256//current is around 43?
constant integer INTPOOL_NO_ENTRIES = 0x672819
//what is the point? why not return 0? what is that integer!?
//Don't change the following global declaration//i changed it lol(the name)
private hashtable poolht = InitHashtable()
endglobals
struct intpool[MAX_INSTANCES]
private integer Cnt = 0
private real WeightTotal = 0.
private integer array Entries[MAX_ENTRIES]
private real array Weights[MAX_ENTRIES]
method getWeight takes integer entry returns real
return Weights[LoadInteger(poolht, integer(this), entry)]
endmethod
method getChance takes integer entry returns real
if WeightTotal > 0. then
return Weights[LoadInteger(poolht, integer(this), entry)]/WeightTotal
endif
return 0.
endmethod
method addInt takes integer entry, real weight returns nothing
local integer in = 0
if .Cnt == MAX_ENTRIES then
//Can't hold any more entries
debug call BJDebugMsg(SCOPE_PREFIX+"Error: .addEntry has reached MAX_ENTRIES")
return
elseif weight <= 0. then
//Zero or negative weights make no sense
debug call BJDebugMsg(SCOPE_PREFIX+"Error: .addEntry can't take zero or negative weights")
return
endif
set in = LoadInteger(poolht, integer(this), entry)
if in > 0 then
//Update old entry
set .WeightTotal = .WeightTotal - .Weights[in] + weight
set .Weights[in] = weight
else
//Make a new entry
set .Cnt = .Cnt + 1
call SaveInteger(poolht, integer(this), entry, .Cnt)
set .Entries[.Cnt] = entry
set .Weights[.Cnt] = weight
set .WeightTotal = .WeightTotal + weight
endif
endmethod
method removeInt takes integer entry returns nothing
local integer in = LoadInteger(poolht, integer(this), entry)
if in > 0 then
call RemoveSavedInteger(poolht, integer(this), entry)
//Remove its entry in the arrays
set .WeightTotal = .WeightTotal - .Weights[in]
set .Entries[in] = .Entries[.Cnt]
set .Weights[in] = .Weights[.Cnt]
set .Cnt = .Cnt - 1
debug else
debug call BJDebugMsg(SCOPE_PREFIX+"Error: .removeEntry entry doesn't exist")
endif
endmethod
method getRInt takes nothing returns integer
local real r = GetRandomReal(0, .WeightTotal)
local integer c = 0
if .WeightTotal <= 0. then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: intpool has no entries")
return INTPOOL_NO_ENTRIES
endif
loop
set r = r - .Weights[c]
exitwhen r <= 0
set c = c + 1
endloop
return .Entries[c]
endmethod
endstruct
endlibrary
A big departure from RD's Pool is that his can only have MAX_INSTANCES/MAX_ENTRIES Pools, whereas IPool can have up to 8190 IPools+subPools+subPool items. Pool will waste a lot of potential storage space by using predefined max_entries whereas subPools are fully-dynamic.
local player y=Player(PLAYER_NEUTRAL_PASSIVE)
local unit x=CreateUnit( y, 'nwgt', 4864.0, -5760.0, 270.000)//not really relevant what this unit was
local intpool ipgold = intpool.create()
call ipgold.addInt(550000, 0.1)
call ipgold.addInt(250000, 0.6)
call ipgold.addInt(75000, 0.2)
call ipgold.addInt(50000, 2.0)
call ipgold.addInt(150000, 1.0)
call ipgold.addInt(500000000, 0.3)
set x = CreateUnit( y, 'ngol', gx, gy, 270.000 )//exact coordinates were determined by manual placement and documented
call SetResourceAmount(x,ipgold.getRInt())//giving it the number of resources equal to one of the above additions to the pool
well I had no idea, still the system with original values supports only 31 instances of intpool.
This can be used for generating drops from monsters for instance
library IPool requires Table, Alloc
private module Init
private static method onInit takes nothing returns nothing
set .tar = TableArray[8192]
endmethod
endmodule
private struct data extends array
static TableArray tar
integer int
integer locks
integer weight
implement Alloc
implement Init
endstruct
private function Create takes nothing returns data
local data this = data.allocate()
set this.locks = 0
return this
endfunction
private function Destroy takes data this returns boolean
if this.locks == -1 or this == 0 then
debug call BJDebugMsg("IPool Error: Attempt to double-free instance!")
return false
endif
set this.locks = -1
call this.deallocate()
return true
endfunction
private function Lock takes data i returns nothing
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to lock a destroyed instance!")
return
endif
set i.locks = i.locks + 1
endfunction
private function Unlock takes data i returns boolean
if i.locks == -1 then
debug call BJDebugMsg("IPool Error: Attempt to unlock destroyed instance!")
return false
endif
set i.locks = i.locks - 1
return i.locks == 0
endfunction
struct IPool extends array
method operator weight takes nothing returns integer
return data(this).weight
endmethod
private method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator table takes nothing returns Table
return data(this).int
endmethod
private method operator table= takes Table t returns nothing
set data(this).int = t
endmethod
static method create takes nothing returns thistype
local thistype this = Create()
set this.table = Table.create()
return this
endmethod
method flush takes nothing returns nothing
call this.table.flush()
call data.tar[this].flush()
set this.weight = 0
endmethod
method destroy takes nothing returns nothing
if Destroy(this) then
call this.table.destroy()
call data.tar[this].flush()
set this.weight = 0
endif
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
//One-liner method to get a random item from the pool based on weight
method getItem takes nothing returns integer
return this.table[GetRandomInt(0, this.weight -1)]
endmethod
method weightOf takes integer value returns integer
return data.tar[this][value]
endmethod
method chanceOf takes integer value returns real //returns between 0. and 1.
return this.weightOf(value) / (this.weight + 0.) //don't divide by 0 here or else!
endmethod
method contains takes integer value returns boolean
return data.tar[this].has(value)
endmethod
method add takes integer value, integer lbs returns nothing
local Table tb = this.table
local integer i = this.weight
if lbs < 1 then
debug call BJDebugMsg("IPool Error: Tried to add value with invalid weight!")
return
endif
set data.tar[this][value] = data.tar[this][value] + lbs
set lbs = i + lbs
set this.weight = lbs //Important
loop
exitwhen i == lbs
set tb[i] = value //treat this.table as an array
set i = i + 1
endloop
endmethod
method remove takes integer value returns nothing
local Table tb = this.table
local Table new
local integer i = this.weight
local integer n = 0
local integer val
if not this.contains(value) then
debug call BJDebugMsg("IPool Error: Attempt to remove un-added instance!")
return
endif
set new = Table.create()
set this.table = new
loop
set i = i - 1
set val = tb[i]
if val != value then
set new[n] = val //write to the new Table without gaps
set n = n + 1
endif
exitwhen i == 0
endloop
set this.weight = n //lower pool weight
call tb.destroy() //abandon old Table instance
call data.tar[this].remove(value) //clear the value's weight now that it's gone
endmethod
method copy takes nothing returns thistype
local thistype new = .create()
local integer i = this.weight
local Table tt = this.table
local Table nt = new.table
local Table dt = data.tar[new]
local integer val
if i == 0 then
debug call BJDebugMsg("IPool Error: Attempt to copy invalid instance!")
call new.destroy()
return 0
endif
set new.weight = i
loop
set i = i - 1
exitwhen i == 0
set val = tt[i]
set nt[i] = val
set dt[val] = dt[val] + 1
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing //print the array of the pool
local string s = "IPool: |cffffcc33Weight: "
local integer i = this.weight
local Table t = this.table
set s = s + I2S(i) + "; Indices: "
loop
set i = i - 1
exitwhen i <= 0
set s = s + "[" + I2S(t[i]) + "]"
endloop
call BJDebugMsg(s + "|r")
endmethod
endif
endstruct
//New struct to handle deliberately-rare chances
struct SubPool extends array
private IPool iPool //for association if you want it
private thistype nest //you can nest IPoolMinis for poolception
//you can change a value's weight via subpoolinstance[value].weight = blah
//you can also change the entire pool's weight via subpoolinstance.weight = blah.
method operator weight takes nothing returns integer
return data(this).weight
endmethod
method operator weight= takes integer lbs returns nothing
set data(this).weight = lbs
endmethod
private method operator value takes nothing returns integer
return data(this).int
endmethod
private method operator value= takes integer val returns nothing
set data(this).int = val
endmethod
private thistype next
private thistype prev
method operator pool takes nothing returns IPool
return this.iPool
endmethod
method operator pool= takes IPool ip returns nothing
if ip != 0 then
call ip.lock()
endif
if this.iPool != 0 then
call this.iPool.unlock()
endif
set this.iPool = ip
endmethod
static method create takes integer totalWeight returns thistype
local thistype this = Create()
set this.next = this
set this.prev = this //I'm my own best friend
set this.weight = totalWeight
return this
endmethod
method destroy takes nothing returns nothing
local thistype curr = this
if this.next == -1 then
debug call BJDebugMsg("SubPool Error: Attempt to double-free!")
return
endif
loop
set curr = curr.next
call Destroy(curr) //destroy all the things
exitwhen curr == this
endloop
set this.next = -1
set this.pool = 0
if this.nest != 0 then
if data(this.nest).locks == 1 then
call this.nest.destroy()
else
call Unlock(this.nest)
endif
set this.nest = 0
endif
call data.tar[this].flush()
endmethod
method lock takes nothing returns nothing
call Lock(this)
endmethod
method unlock takes nothing returns nothing
if Unlock(this) then
call this.destroy()
endif
endmethod
method operator subPool takes nothing returns thistype //need to return thistype and not IPool :P
return this.nest
endmethod
method operator subPool= takes thistype ip returns nothing
if this == ip then
debug call BJDebugMsg("SubPool Error: Don't set a subPool within itself. Use the .copy() method, instead.")
return
endif
if ip != 0 then
call ip.lock()
endif
if this.nest != 0 then
call this.nest.unlock()
endif
set this.nest = ip
endmethod
method add takes integer val, integer lbs returns nothing
local thistype new
if lbs <= 0 then
debug call BJDebugMsg("SubPool Error: Don't add a value without weight")
return
endif
if data.tar[this].has(val) then
set new = data.tar[this][val]
set new.weight = new.weight + lbs
return
endif
set new = Create()
set new.prev = this.prev
set this.prev.next = new
set this.prev = new
set new.next = this
set new.value = val
set new.weight = lbs
set data.tar[this][val] = new
endmethod
method contains takes integer val returns boolean
return data.tar[this].has(val)
endmethod
method operator [] takes integer val returns thistype
return data.tar[this][val]
endmethod
method operator []= takes integer val, integer newWeight returns nothing
set this[val].weight = newWeight
endmethod
method remove takes integer val returns nothing
local thistype node = this[val]
if Destroy(node) then
set node.prev.next = node.next
set node.next.prev = node.prev
call data.tar[this].remove(val)
else
debug call BJDebugMsg("SubPool Error: Attempt to remove non-added value")
endif
endmethod
method getItem takes nothing returns integer
local thistype curr = this
local integer i = GetRandomInt(1, this.weight)
loop
set curr = curr.next
set i = i - curr.weight
exitwhen i <= 0
endloop
if curr == this then
if this.nest != 0 then
set i = this.nest.getItem()
else
set i = 0
endif
if i == 0 and this.pool != 0 then //if no low-probability item could be found...
set i = this.pool.getItem() //pick a random int from main pool
endif
else
set i = curr.value
endif
return i
endmethod
method copy takes nothing returns thistype
local thistype new = .create(this.weight)
local thistype curr = this
set new.pool = this.iPool
set new.subPool = this.nest
loop
set curr = curr.next
exitwhen curr == this
call new.add(curr.value, curr.weight)
endloop
return new
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing
local thistype curr = this
local string s = "SubPool: |cffffcc33Instance: " + I2S(this) + ", TotalWeight: " + I2S(this.weight) + ", Indices: "
if curr.next == this then
call BJDebugMsg("SubPool is empty!")
//return
endif
loop
set curr = curr.next
exitwhen curr == this
set s = s + "[" + I2S(curr.value) + "]<" + I2S(curr.weight) + ">"
endloop
call BJDebugMsg(s + "|r")
if this.nest != 0 then
call this.nest.print()
endif
if this.iPool != 0 then
call this.iPool.print()
endif
endmethod
endif
endstruct
endlibrary
function TestingIPool takes nothing returns nothing
local SubPool i = SubPool.create(1000)
local integer j
call i.add('Hpal', 1)
call i.add('hkni', 10)
call i.add('hpea', 100)
call i.add('hfoo', 50)
debug call BJDebugMsg("SubPool GetItem: " + I2S(i.getItem()))
debug call i.print()
call i.remove('hpea')
call i.add('ewsp', 100)
debug call i.print()
set i.subPool = SubPool.create(100)
call i.subPool.add('Amrf', 10)
call i.subPool.add('Aloc', 10)
debug call BJDebugMsg("Nested SubPool GetItem: " + I2S(i.subPool.getItem()))
set i.pool = IPool.create()
call i.pool.add(9001, 3)
call i.pool.add(4001, 7)
call i.pool.add('poop', 10)
debug call BJDebugMsg("Nested IPool GetItem: " + I2S(i.pool.getItem()))
debug call i.print()
endfunction
function InitTrig_Pool takes nothing returns nothing
call TimerStart(CreateTimer(), 0, false, function TestingIPool)
endfunction
// on init
set IndexPool = IPool.create()
call IndexPool.add(0, 1)
call IndexPool.add(1, 1)
call IndexPool.add(2, 1)
call IndexPool.add(3, 1)
set pickCount = 0
// on fire coconut event
local integer i = IndexPool.getItem()
set pickCount = pickCount + 1
call FireCannon(i) // order cannon unit with index i to fire a coconut
// then I call this so that the same unit can't fire the coconut twice
call IndexPool.remove(i)
// then after the pool is empty, I re-add all the indexes
if pickCount == 4 then
call IndexPool.flush() // I also tried to remove this line but no luck
call IndexPool.add(0, 1)
call IndexPool.add(1, 1)
call IndexPool.add(2, 1)
call IndexPool.add(3, 1)
set pickCount = 0
endif
// on init
set MasterPool = IPool.create()
call MasterPool.add(0, 1)
call MasterPool.add(1, 1)
call MasterPool.add(2, 1)
call MasterPool.add(3, 1)
set IndexPool = MasterPool.copy()
set pickCount = 0
// on fire coconut event
local integer i = IndexPool.getItem()
set pickCount = pickCount + 1
call FireCannon(i)
// then after the pool is empty, I re-add all the indexes
if pickCount == 4 then
call IndexPool.destroy()
set IndexPool = MasterPool.copy()
set pickCount = 0
else
call IndexPool.remove(i)
endif
This approach will be easier to manage, since you only have to set the pool contents once during Init:
JASS:// on init set MasterPool = IPool.create() call MasterPool.add(0, 1) call MasterPool.add(1, 1) call MasterPool.add(2, 1) call MasterPool.add(3, 1) set IndexPool = MasterPool.copy() set pickCount = 0 // on fire coconut event local integer i = IndexPool.getItem() set pickCount = pickCount + 1 call FireCannon(i) // then after the pool is empty, I re-add all the indexes if pickCount == 4 then call IndexPool.destroy() set IndexPool = MasterPool.copy() set pickCount = 0 else call IndexPool.remove(i) endif
An old version had an issue with adding and/or removing the value of 0 to an IPool. I couldn't see if this one would have the same problem, but I don't think so. Is your version of IPool up to date from the top post?
Well, I updated this in August, so I assume you would have copied it 3 months ago from this thread?