A "nice" random library for games - random selection with memory to prevent frustrating repeats.
Header-only C++17 library. Game-dev friendly: minimal STL, float weights, no allocations in hot paths, noexcept where possible.
- Single header - just include and use
- Float weights - standard for game dev (not double)
- No allocations in pick() - pre-allocated buffers
- Linear search for history - cache-friendly for small pools (typical in games)
- Simple sentinel values - no
std::optional noexcepthot paths - predictable performance
Copy include/nicerand/nicerand.hpp to your project:
#include <nicerand/nicerand.hpp>#include <nicerand/nicerand.hpp>
// Default cooldown is 1 (no immediate repeats)
nicerand::NiceRandom<std::string> playlist({
"Song A", "Song B", "Song C", "Song D"
});
// Pick one item - won't get the same song twice in a row
const std::string& song = playlist.pick();
// Pick multiple items
std::vector<std::string> songs = playlist.pick_n(5);// Shuffle bag: all items appear before any can repeat
std::vector<std::string> items = {"A", "B", "C", "D", "E"};
nicerand::NiceRandom<std::string> bag(items, items.size());
// Every 5 picks will contain all 5 items exactly once
auto round = bag.pick_n(5);// Cooldown of 2: an item won't repeat for 2 picks
nicerand::NiceRandom<std::string> picker({"a", "b", "c", "d"}, 2);using W = nicerand::Weighted<std::string>;
nicerand::NiceRandom<std::string> loot({
W{"Gold Coins", 10.0f}, // Very common
W{"Health Potion", 5.0f}, // Common
W{"Rare Sword", 1.0f}, // Rare
}, 2);
const std::string& drop = loot.pick();enum class LootType { GOLD, POTION, SWORD, ARMOR };
nicerand::NiceRandom<LootType> loot({
LootType::GOLD,
LootType::POTION,
LootType::SWORD,
LootType::ARMOR
}, 2);
LootType drop = loot.pick();// Third parameter is the seed (0 = random device)
nicerand::NiceRandom<int> picker({1, 2, 3, 4}, 2, 42);template <typename T>
class NiceRandom {
// Constructors
NiceRandom(std::initializer_list<T> items, size_t cooldown = AUTO, uint32_t seed = 0);
NiceRandom(std::vector<T> items, size_t cooldown = AUTO, uint32_t seed = 0);
NiceRandom(std::initializer_list<Weighted<T>> items, size_t cooldown = AUTO, uint32_t seed = 0);
// Core operations
const T& pick() noexcept; // Pick one item (returns reference, no copy)
std::vector<T> pick_n(size_t n); // Pick n items
void reset() noexcept; // Clear history
// Accessors
const std::vector<T>& items() const noexcept;
size_t cooldown() const noexcept;
size_t size() const noexcept;
std::vector<T> history() const;
};
template <typename T>
struct Weighted {
T item;
float weight;
};cmake -B build
cmake --build build
./build/basic_usage # Run examples
./build/test_nicerand # Run tests- C++17 or later
- CMake 3.14+ (for building examples/tests)