-
Notifications
You must be signed in to change notification settings - Fork 2
RandomSelector
View the code in the repository
Picking outcomes at random is not an uncommon task in coding. Often this is done using rand() and modulo even though this method has flaws. It also often involves explicit control structures such as if-else chains which in some cases become nested too. These are a source of bugs. This class provides a declarative way of doing the same thing. The outcomes can be weighted as well, to further reduce the complexity.
Note that this class by default uses seeding from std::random_device and uses std::mt19937 as the random engine. Both of these can be customised if needed.
The class is templated and works on a single type at a time. Let's start by making an alias to use it with integers.
//As seen, a custom random engine is optionally provided
using IntSelector = th::RandomSelector<int32_t/*, CustomRandomEngine*/>;
Now we can create instances and use them to get a random integer from a selector. The following returns the values uniformly distributed with a randomly picked seed.
IntSelector intSelector({1, 4, 7});
auto number = intSelector.get(); //might produce 1, 4 or 7;
number = IntSelector({7, 2, -1}).get(); //inline usage can be handy
This usage can be further configured as follows.
//uses custom seed 45 and will always give the same value
auto seededNumber = IntSelector({3, 6, 8}, 45);
//these weights are relative to each other,
//so in this line 3 is four times as likely to be choosen over 1
auto weightedNumber = IntSelector({{3, 4.0f}, {6, 2.0f}, {1, 1.0f}});
//seeds and weights can of course be both provided simultaneously
auto probably100 = IntSelector({{1, 0.01f}, {100, 2000.0f}}, 42);
It is possible to alter the selector object
IntSelector numberPool({5, 7, 10, 11});
//add more entries...
numberPool.add(5/*, optionalWeight*/);
//...combine with another pool..
numberPool = numberPool.combineWith(IntSelector({1, 100}));
//...and fetch a random item, removing it from the selector in the process
auto fetched = numberPool.pop();
//Sometimes looping through all items is handy
while(!numberPool.empty())
auto selected = numberPool.pop();
Usage inspiration:
enum Loot { SWORD, BOOMERANG, DAGGER };
using LootSelector = th::RandomSelector<Loot>;
Loot droppedLoot = LootSelector({{SWORD, 1.0f}, {BOOMERANG, 0.5f}, {DAGGER, 2.0f}}).get();
using BoolSelector = th::RandomSelector<bool>;
bool makeDecision = BoolSelector({true, false}).get();