#include <map>
#include <sstream>
#include <vector>
#include <random>
#include <iostream>
#include <iomanip>
#include <numeric>

struct Die
{
    Die(unsigned faces = 6) : _faces(faces), _die(std::random_device()()) {}
    Die(const Die& d) : _faces(d._faces), _die(std::random_device()()) {}

    unsigned operator()()
    {
        return std::uniform_int_distribution<unsigned>(1, _faces)(_die);
    }

private:
    unsigned _faces;
    std::mt19937 _die;
};

int main()
{
    const unsigned trials = 100000;
    const unsigned faces = 6;
    const unsigned num_dice = 2;

    std::vector<Die> dice(num_dice, Die(faces));

    using seq_map = std::map<std::vector<unsigned>, unsigned>;
    seq_map sequence_frequency ;

    // generate and store roll sequences.
    for (unsigned i = 0; i < trials; ++i)
    {
        std::vector<unsigned> rolls;
        for (auto& die : dice)
            rolls.push_back(die());

        ++sequence_frequency[rolls];
    }

    // collate roll sequences which have the same total.
    std::map<unsigned, std::vector<seq_map::const_iterator>> by_total ;
    for (auto it = sequence_frequency.cbegin(); it != sequence_frequency.cend(); ++it)
    {
        unsigned total = std::accumulate(it->first.cbegin(), it->first.cend(), 0u);
        by_total[total].push_back(it);
    }

    // display:
    for (const auto& seq : by_total)
    {
        std::cout << std::setw(3) << seq.first << ": ";
        
        std::ostringstream rolls_string;
        
        unsigned freq = 0;
        for (const auto& roll : seq.second)
        {
            for (const auto& value : roll->first)
                rolls_string << '[' << value << ']';
            rolls_string << " (" << roll->second << ")  ";
            freq += roll->second;
        }

        std::cout << std::setw(6) << freq << " - ";
        std::cout << std::setprecision(2) << std::fixed << std::setw(5);
        std::cout << (freq / (double) trials*100.0) << "%\n";
        
        std::cout << '\t' << rolls_string.str() << '\n';
    }
}
 