#include <iostream>
#include <random>
#include <numeric>
#include <iterator>

// seed the generator
std::mt19937 generator((std::random_device())());

struct DieInterface
{
    virtual ~DieInterface() = default;
    virtual unsigned operator()() const = 0 ;
    virtual unsigned sides() const = 0;
};

struct Die : public DieInterface
{
    Die(unsigned nSides) : _dist(1, nSides) {}

    unsigned operator()() const { return _dist(generator); }
    unsigned sides() const { return _dist.b(); }

private:
    mutable std::uniform_int_distribution<unsigned> _dist;
};

struct LoadedDie : public DieInterface
{
    using distribution = std::uniform_int_distribution<unsigned>;

    LoadedDie(unsigned nSides, unsigned loadedFace, unsigned weight_out_of_100)
        : _dist(1, nSides - 1), _face(loadedFace), _weight(weight_out_of_100) {}

    unsigned operator()() const
    {
        using param_type = distribution::param_type;

        if (_dist(generator, param_type{ 1, 100 }) >= _weight)
            return _face;
        else
        {
            unsigned face = _dist(generator);

            if (face >= _face)
                ++face;

            return face;
        }
    }

    unsigned sides() const { return _dist.b() + 1; }

private:
    mutable distribution _dist;

    unsigned _face;
    unsigned _weight;
};


// Take two dice objects, roll them, and return the sum

template <typename iter_type>
unsigned roll(iter_type beg, iter_type end)
{
    using die_type = const DieInterface&;
    return std::accumulate(beg, end, 0u, [](unsigned x, die_type d) { return x + d(); });
}

int main()
{
    Die dice[] = { Die(6), Die(10) };

    std::cout << "\nRolling two normal dice:\n";
    for (int i = 0; i < 10; ++i)
        std::cout << roll(std::begin(dice), std::end(dice)) << '\n';

    {
        const unsigned faces = 6;
        const unsigned loaded_face = 6;
        const unsigned weight = 50;

        LoadedDie die(faces, loaded_face, weight);

        std::cout << "\n\nRolling the loaded die with a 50 percent chance to roll max val:\n";
        for (int i = 0; i < 10; ++i)
            std::cout << die() << '\n';
    }

    {
        const unsigned faces = 20;
        const unsigned loaded_face = 5;
        const unsigned weight = 50;

        LoadedDie die(faces, loaded_face, weight);

        std::cout << "\n\nRolling a 20-sided loaded die: (" << loaded_face << "is loaded)\n";
        for (int i = 0; i < 10; ++i)
            std::cout << die() << '\n';
    }
}