Created
October 4, 2021 16:17
-
-
Save nikibobi/eeb7333e7204521a99c2d97cb56b16c1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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