Skip to content

Instantly share code, notes, and snippets.

@nikibobi
Created October 4, 2021 16:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nikibobi/eeb7333e7204521a99c2d97cb56b16c1 to your computer and use it in GitHub Desktop.
Save nikibobi/eeb7333e7204521a99c2d97cb56b16c1 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <iomanip>
#include <random>
#include <functional>
#include <stdexcept>
#include <vector>
#include <map>
#include <string>
#include <boost/math/special_functions/binomial.hpp>
namespace dice {
using std::size_t;
using roll_type = size_t;
using sides_type = size_t;
using multiplier_type = unsigned long long int;
using offset_type = unsigned long long int;
using probability_type = double;
inline probability_type n_k(unsigned n, unsigned k) {
return boost::math::binomial_coefficient<probability_type>(n, k);
};
struct RollResults {
friend class DiceExpr;
using value_type = roll_type;
using container = std::vector<roll_type>;
using iterator = container::const_iterator;
using const_iterator = container::const_iterator;
RollResults(size_t n) : values(n + 1) { }
operator roll_type() const {
return values[0];
}
roll_type operator[](size_t i) const {
return values[i];
}
iterator begin() const {
return values.cbegin();
}
iterator end() const {
return values.cend();
}
private:
roll_type& operator[](size_t i) {
return values[i];
}
container values;
};
struct DiceExpr {
constexpr DiceExpr(sides_type sides)
: sides{sides} { }
constexpr DiceExpr(multiplier_type multiplier, sides_type sides)
: multiplier{multiplier}, sides{sides} { }
constexpr DiceExpr(multiplier_type multiplier, sides_type sides, offset_type offset)
: multiplier{multiplier}, sides{sides}, offset{offset} { }
constexpr DiceExpr(const DiceExpr&) = default;
constexpr DiceExpr& operator=(const DiceExpr&) = default;
constexpr size_t min() const {
return multiplier;
}
constexpr size_t max() const {
return multiplier * sides;
}
[[nodiscard]] RollResults operator()() const {
RollResults result{multiplier};
result[0] = offset;
for (size_t i = 0; i < multiplier; i++) {
result[i + 1] = roll();
result[0] += result[i + 1];
}
return result;
}
[[nodiscard]] roll_type roll() const {
static std::random_device device{};
static std::mt19937 generator{device()};
static std::uniform_int_distribution<roll_type> distribution{1, sides};
return distribution(generator);
}
[[nodiscard]] constexpr probability_type p(roll_type roll) const {
sides_type s = sides;
multiplier_type n = multiplier;
roll_type k = roll;
if (k < min() || k > max()) {
throw std::out_of_range{"roll"};
}
if (n == 1) {
return 1.0 / s;
}
probability_type sum = 0.0;
size_t max = (k - n) / s;
for (size_t i = 0; i <= max; i++) {
sum += (i % 2 == 0 ? 1 : -1) * n_k(n, i) * n_k(k - s * i - 1, n - 1);
}
probability_type p = 1.0 / std::pow(s, n);
p *= sum;
return p;
}
explicit operator RollResults() const {
return (*this)();
}
explicit operator roll_type() const {
return roll_type{(*this)()};
}
friend constexpr DiceExpr operator +(const DiceExpr& expr, offset_type offset);
private:
sides_type sides;
multiplier_type multiplier{1};
offset_type offset{0};
};
[[nodiscard]] probability_type p(const DiceExpr& e, std::function<bool(roll_type)> predicate) {
probability_type probability = 0.0;
for (size_t i = e.min(); i <= e.max(); i++) {
if (predicate(i)) {
probability += e.p(i);
}
}
return probability;
}
constexpr DiceExpr operator +(const DiceExpr& expr, offset_type offset) {
return DiceExpr(expr.multiplier, expr.sides, offset + expr.offset);
}
constexpr DiceExpr d4{4};
constexpr DiceExpr d6{6};
constexpr DiceExpr d8{8};
constexpr DiceExpr d10{10};
constexpr DiceExpr d12{12};
constexpr DiceExpr d20{20};
constexpr DiceExpr d100{100};
namespace literals {
inline constexpr DiceExpr operator ""_d4(multiplier_type multiplier) {
return DiceExpr{multiplier, 4};
}
inline constexpr DiceExpr operator ""_d6(multiplier_type multiplier) {
return DiceExpr{multiplier, 6};
}
inline constexpr DiceExpr operator ""_d8(multiplier_type multiplier) {
return DiceExpr{multiplier, 8};
}
inline constexpr DiceExpr operator ""_d10(multiplier_type multiplier) {
return DiceExpr{multiplier, 10};
}
inline constexpr DiceExpr operator ""_d12(multiplier_type multiplier) {
return DiceExpr{multiplier, 12};
}
inline constexpr DiceExpr operator ""_d20(multiplier_type multiplier) {
return DiceExpr{multiplier, 20};
}
inline constexpr DiceExpr operator ""_d100(multiplier_type multiplier) {
return DiceExpr{multiplier, 100};
}
};
template<typename Map = std::map<roll_type, int>>
Map sample(const DiceExpr& expr, size_t sample_size = 10'000) {
Map hist;
for (size_t i = 0; i < sample_size; i++) {
roll_type roll = expr();
hist[roll]++;
}
return hist;
}
template<typename Map = std::map<roll_type, int>>
void print_histogram(const Map& hist) {
for (const auto& [roll, count] : hist) {
std::cout << std::setfill(' ') << std::setw(2) << roll << std::string(count / 10, '*') << std::endl;
}
}
};
#include "dice.hpp"
using namespace dice;
using namespace dice::literals;
int main()
{
roll_type roll{4_d4 + 2};
constexpr DiceExpr expr{3_d6};
auto hist = sample(expr);
print_histogram(hist);
constexpr DiceExpr dd6{2_d6};
std::cout << dd6.p(6) << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment