class RandomItemSelector
def initialize(items, weightFunc)
@weightFunc = weightFunc
@items = items
@totalWeight = nil
end
def next
target = rand(totalWeight)
min = 0
@items.each { |item|
max = min + @weightFunc.call(item)
if min <= target && target < max
return item;
end
min = max
}
raise "Internal error: can't select random item"
end
def itemsChanged
@totalWeight = nil
end
def totalWeight
if @totalWeight.nil?
@totalWeight = 0
@items.each { |item| @totalWeight += @weightFunc.call(item) }
end
return @totalWeight
end
end
Usage:
items = []
for i in 1..100
items.push case rand(3)
when 0
'Red'
when 1
'Green'
when 2
'Blue'
end
end
selector = RandomItemSelector.new(items, proc { |item|
case item
when 'Red'
10
when 'Green'
5
when 'Blue'
1
end
})
for i in 1..20
print selector.next
print " "
end