Mersenne Twister Cpp Dice

Project Description

Develop a set of abstractions that can model real world dice, to be used in D&D simulations.
Should be based on the Mersenne Twister Random Number Engine.
Language: C++17

The first function will model a single die of a given size. Output requires a uniform distribution within the proper range. The second function will model a given number of dice of a given size and return the sum. The smallest die roll needed is 1d1, the biggest is 1000d1000, anything in-between is also fair game, even if it would be geometrically impossible in three dimensions – like a five sided die.

Project Specification

Function: die(int sides) -> int

@guard sides must be 1000 or less.
@guard sides must be greater than zero else early return zero.
@param sides: Dice Size: represents the number of dice sides or faces.
@return: Rolled Value: a number within the range 1 to sides, inclusive with a flat distribution.

Function: dice(int rolls, int sides) -> int

@guard rolls must be 1000 or less.
@guard rolls must be greater than zero else early return zero.
@param rolls: Number of iterations of d(sides): represents the number of dice rolls.
@param sides: Dice Size: represents the number of dice sides or faces.
@return: Sum Total of the dice rolled. Matches the probability distribution of real dice over a sufficiently large data set.

Mersenne Twister C++ Dice: First Attempt

// File: Dice.hpp 
#pragma once #include <random> int d(const int& sides) { static std::random_device hardware_seed{}; static std::mt19937 engine(hardware_seed()); std::uniform_int_distribution distribution(1, sides);
return distribution(engine); } int dice(const int& rolls, const int& sides) { int total {0}; for (int i {0}; i < rolls; ++i)
total += die(sides); return total; }

The above code covers the core design requirements but leaves some edge cases unanswered. If you call d(0) you will not get what you expect. It should either raise an error or return zero.

Mersenne Twister C++ Dice: Second Attempt

Lets say, after many weeks of intense meetings, the upper management decides that a zero or any negatively sided dice should always return 0 rather than fail or raise an exception. In addition, it may be handy to have a more generic random number generator that understands the entire domain of int. It should take any two integers and return a random integer in that range. The output range should include both inputs and produce a uniform distribution.

Function: random_int(int lo, int hi) -> int

@guard if hi equals lo, early return hi
@guard if hi is less than lo, reverse the order
@param lo: lower limit
@param hi: upper limit
@return: Random Value: an integer within the range lo to hi, inclusive with a uniform probability distribution.

// File: Dice.hpp 
#pragma once #include <random> int random_int(const int& lo, const int& hi) { static std::random_device hardware_seed{}; static std::mt19937 engine(hardware_seed()); std::uniform_int_distribution distribution(lo, hi); return distribution(engine); } int die(const int& sides) { if (sides < 1) return 0; return random_int(1, sides); } int dice(const int& rolls, const int& sides) { if (rolls < 1) return 0; int total {0}; for (int i {0}; i < rolls; ++i)
total += die(sides); return total; }

Almost there! What if the parameters for random_int(lo, hi) are given in the wrong order? Also, what happens when lo equals hi? Both die(N) and dice(X, Y) need an upper input limit: 1000.

Mersenne Twister C++ Dice: Third Attempt

// File: Dice.hpp
#pragma once
#include <random>

int random_int(const int& lo, const int& hi) {
    if (hi == lo) return hi;
    if (hi < lo) return random_int(hi, lo);
    static std::random_device hardware_seed{};
    static std::mt19937 engine(hardware_seed());
    std::uniform_int_distribution distribution(lo, hi);
    return distribution(engine);
}

int die(const int& sides) {
    assert(sides <= 1000);
    if (sides < 1) return 0;
    return random_int(1, sides);
}

int dice(const int& rolls, const int& sides) {
    assert(rolls <= 1000);
    if (rolls < 1) return 0;
    int total {0};
    for (int i {0}; i < rolls; ++i)
total += die(sides); return total; }