Skip to content

Latest commit

 

History

History
138 lines (101 loc) · 3.29 KB

File metadata and controls

138 lines (101 loc) · 3.29 KB

nicerand - C++

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.

Features

  • 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
  • noexcept hot paths - predictable performance

Installation

Copy include/nicerand/nicerand.hpp to your project:

#include <nicerand/nicerand.hpp>

Usage

Basic Usage

#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 (Full Rotation)

// 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);

Custom Cooldown

// Cooldown of 2: an item won't repeat for 2 picks
nicerand::NiceRandom<std::string> picker({"a", "b", "c", "d"}, 2);

Weighted Items

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();

With Integers (e.g., enum-backed IDs)

enum class LootType { GOLD, POTION, SWORD, ARMOR };

nicerand::NiceRandom<LootType> loot({
    LootType::GOLD,
    LootType::POTION,
    LootType::SWORD,
    LootType::ARMOR
}, 2);

LootType drop = loot.pick();

Seeded Random (for reproducibility)

// Third parameter is the seed (0 = random device)
nicerand::NiceRandom<int> picker({1, 2, 3, 4}, 2, 42);

API Reference

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;
};

Building Examples and Tests

cmake -B build
cmake --build build
./build/basic_usage      # Run examples
./build/test_nicerand    # Run tests

Requirements

  • C++17 or later
  • CMake 3.14+ (for building examples/tests)