Skip to content
Tobias Widlund edited this page Nov 3, 2015 · 7 revisions

RandomSelector

View the code in the repository

Purpose

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.

Usage

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();
Clone this wiki locally