Pool Of Randomness
For my current Unity project I had to convert Kipp Ashford’s Weighted Array script into something I could use in my C# environment. I had a similar solution already working in ISIS but Ashford’s script was much more elegant since it includes a number of useful functions my bare-boned version was lacking.
A clever (or so I hope) use of a hashtable allowed me to trim some of the variables the orginial script had used to keep it as slim as possible.
// WeightedRandomPool.cs
// 2013-10-09 by Thomas Touzimsky
// http://www.simplypointless.com/
//
// Adds, deletes and returns a random item from a pool of objects based on each item's associated weight
//
// A conversion of the original JS script by Kipp Ashford
// http://blog.teamthinklabs.com/index.php/2011/08/23/grabbing-items-from-an-array-based-on-probability/
using UnityEngine;
using System.Collections;
public class WeightedRandomPool
{
// Our pool is stored in a hashtable where
// key = (Object)item
// value = (int)weight
public Hashtable htItems = new Hashtable();
///
/// Adds a new item to the hashtable or changes its weight if has been added before
///
/// The object you want to add to the pool
/// The object's weight within the pool
public void Add(Object item, int weight)
{
if (!htItems.ContainsKey(item))
{
htItems.Add(item, weight);
}
else
{
Debug.Log("WeightedRandomPool::Add() --- WARNING: Object is already added. Changing weight of object instead.");
htItems[item] = weight;
}
}
///
/// Changes the weight of an object within the pool
///
/// The object whose weight you want to change
/// The object's new weight within the pool
public void ChangeWeight(Object item, int nWeight)
{
if (htItems.ContainsKey(item))
{
htItems[item] = nWeight;
}
else
{
Debug.Log("WeightedRandomPool::ChangeWeight() --- WARNING: Object '" + item + "' is not a member of this pool.");
}
}
///
/// Removes an object from the pool
///
/// The object you want to remove
public void Remove(Object item)
{
if (htItems.ContainsKey(item))
{
htItems.Remove(item);
}
else
{
Debug.Log("WeightedRandomPool::ChangeWeight() --- WARNING: Object '" + item + "' is not a member of this pool.");
}
}
///
/// Returns a randomly chosen object from the pool depending on all members' weight
///
///
public Object Get()
{
int nSumOfWeights = 0;
foreach (DictionaryEntry item in htItems)
{
nSumOfWeights += (int)item.Value;
}
int k = Random.Range(0, nSumOfWeights + 1);
// +1 to make sure we can actually find the last item if it has a weight of 1
// since Random.Range never hits the maximum value
foreach (DictionaryEntry item in htItems)
{
// walk the pool one item at a time and see whether its weight is lower than k
// if yes, return the current item ; if no, subtract the item's weight from k
// if, for example, the pool includes three items with a weight of 40/40/60 and
// k = 70, the first loop's result will be negative and k gets reduced by 40;
// the next loop's result is positive since k (now 30) is lower than the second
// item's weight
if (k > (int)item.Value)
{
k -= (int)item.Value;
}
else
{
return (Object)item.Key;
}
}
return null;
}
///
/// Clears the pool
///
public void Clear()
{
htItems.Clear();
}
}
The class itself is used just like Ashford’s original.
WeightedRandomPool myPool = new WeightedRandomPool();
myPool.Add(myFirstItem, 15);
myPool.Add(mySecondItem, 5);
myPool.Add(myThirdItem, 2);
UnityEngine.Object myRandomItem = myPool.Get();
Feel free to suggest any ideas on how to improve the script – after all, I am still a novice coder. 🙂
Category: