├── DieCast.h ├── Example.cpp ├── LICENSE ├── README.md └── UserLiteralExample.cpp /DieCast.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | Author: TheMaverickProgrammer 7 | Date: 11/30/2018 8 | Description: 9 | 10 | die:: namespace has a function `cast(int rolls, int sides, int modifier)` 11 | that returns an iterable die_cast object that can chain multiple `cast()` calls. 12 | The object can be directly used in a for-each loop. See accompanying README for examples. 13 | 14 | die::literal:: namespace exports a user-defined literal "# of rolls"_D(int sides) 15 | and has an object that exploits basic addition and concatenation operators to be human readable. 16 | */ 17 | 18 | namespace die { 19 | namespace literal { 20 | class die_cast_facade; /* forward decl */ 21 | } 22 | 23 | struct die_cast_info { 24 | int result; 25 | int sides; 26 | int roll; 27 | int modifier; 28 | 29 | // dissolves into result for ease of use 30 | operator int() { return result; } 31 | }; 32 | 33 | struct die_cast { 34 | friend class literal::die_cast_facade; 35 | 36 | public: 37 | 38 | /* constructor needed to construct random device and generator */ 39 | die_cast() : generator(rand_dev()) { ; } 40 | 41 | /* custom copy needed because random devices are non copyable */ 42 | die_cast(const die_cast& rhs) { 43 | rand_dev(); 44 | generator = rhs.generator; 45 | list = rhs.list; 46 | } 47 | 48 | /* short hand iterable keywords */ 49 | typedef die_cast_info* iterator; 50 | typedef const die_cast_info* const_iterator; 51 | 52 | /* 53 | iterable template syntax requirements 54 | */ 55 | iterator begin() { return &list[0]; } 56 | const_iterator begin() const { return &list[0]; } 57 | iterator end() { return &list[list.size()]; } 58 | const_iterator end() const { return &list[list.size()]; } 59 | die_cast_info operator*() { return *iter; } 60 | void operator++() { ++iter; } 61 | bool operator!=(die_cast rhs) { return iter != rhs.iter; } 62 | 63 | private: 64 | /* 65 | internals for iteration 66 | */ 67 | std::vector list; 68 | iterator iter; 69 | 70 | /* 71 | internals for rand 72 | */ 73 | std::random_device rand_dev; 74 | std::mt19937 generator; 75 | 76 | public: 77 | 78 | /* Chain function is same signature as static calls */ 79 | die_cast& cast(int rolls, int sides, int modifier=0) { 80 | 81 | if(rolls < 1) { 82 | throw std::invalid_argument("die::die_cast number of rolls must be at least 1"); 83 | } 84 | 85 | if(sides < 1) { 86 | throw std::invalid_argument("die::die_cast number of sides must be at least 1"); 87 | } 88 | 89 | /* 90 | API allows for any type of die to be rolled in sequence 91 | and must create new distribution each time 92 | */ 93 | 94 | std::uniform_int_distribution distr(1, sides); 95 | 96 | for(int i = 0; i < rolls; i++) { 97 | list.push_back(die_cast_info({distr(generator)+modifier, sides, i+1, modifier})); 98 | } 99 | 100 | return *this; 101 | } 102 | 103 | // dissolves into only result set 104 | operator std::vector() { 105 | std::vector result_set; 106 | 107 | for(auto& i : *this) { 108 | result_set.push_back(i.result); 109 | } 110 | 111 | return result_set; 112 | } 113 | }; 114 | 115 | // primary call 116 | static auto cast(int rolls, int sides, int modifier=0) { 117 | die_cast res; 118 | return res.cast(rolls, sides, modifier); 119 | } 120 | 121 | namespace literal { 122 | /* Goal: achieve the following syntax 123 | die_cast res = 3_D(10)+4 << 3_D10 << ... ; 124 | */ 125 | 126 | class die_cast_facade { 127 | die_cast internal; 128 | 129 | public: 130 | /* assignment to die_cast */ 131 | die_cast_facade(const die_cast& rhs) { 132 | internal.list = rhs.list; 133 | } 134 | 135 | /* dissolve into original object */ 136 | operator die_cast() { return internal; } 137 | 138 | /* add modifier */ 139 | die_cast_facade& operator+(int modifier) { 140 | for(auto& i : internal) { 141 | i.modifier = modifier; 142 | i.result += modifier; 143 | } 144 | 145 | return *this; 146 | } 147 | 148 | /* sub modifier */ 149 | die_cast_facade& operator-(int modifier) { 150 | for(auto& i : internal) { 151 | i.modifier = -modifier; 152 | i.result -= modifier; 153 | } 154 | 155 | return *this; 156 | } 157 | 158 | /* allow concatenation */ 159 | die_cast_facade& operator<<(const die_cast_facade& rhs) { 160 | internal.list.insert(internal.list.end(), rhs.internal.list.begin(), rhs.internal.list.end()); 161 | return *this; 162 | } 163 | }; 164 | 165 | /* user-defined string literal for intuitive experience */ 166 | auto operator "" _D(unsigned long long rolls) { 167 | return [rolls](int sides) -> auto 168 | { 169 | die_cast_facade proxy = cast((int)rolls, (int)sides); 170 | return proxy; 171 | }; 172 | } 173 | } 174 | 175 | }; 176 | -------------------------------------------------------------------------------- /Example.cpp: -------------------------------------------------------------------------------- 1 | #include "DieCast.h" 2 | #include 3 | 4 | // die:: namespace has one function: cast() 5 | // returns a chainable object that is also an iterable result set 6 | int main() { 7 | // Roll a 6 sided die 3 times 8 | // Roll a 8 sided die 2 times and then add 10 to the result 9 | // e.g. roll 3D6 2D8+10 10 | auto res = die::cast(3,6).cast(2,8,+10); 11 | 12 | // iterable die_cast_info object can tell you 13 | // the following properties about a roll: 14 | // count of sides on die, roll #, modifier, and the result 15 | for(auto& i : res) { 16 | std::cout << "Rolled a " << i.sides << " sided die "; 17 | std::cout << i.roll << ((i.roll == 1)? " time " : " times"); 18 | 19 | if(i.modifier != 0) { 20 | std::cout << " with a modifier of " << i.modifier << "!"; 21 | } else { 22 | std::cout << "!"; 23 | } 24 | 25 | std::cout << " => " << i.result << std::endl; 26 | } 27 | 28 | std::cout << "\nResults only:" << std::endl; 29 | 30 | // Dissolve into integer result set only 31 | // if you just want the final value 32 | for(auto& i : res) { 33 | std::cout << "Final value: " << i << std::endl; 34 | } 35 | } 36 | 37 | /* 38 | Output 39 | ---------------------------------------------------------- 40 | 41 | $g++ -o main Example.cpp 42 | $main 43 | 44 | Rolled a 6 sided die 1 time ! => 5 45 | Rolled a 6 sided die 2 times! => 3 46 | Rolled a 6 sided die 3 times! => 1 47 | Rolled a 8 sided die 1 time with a modifier of 10! => 17 48 | Rolled a 8 sided die 2 times with a modifier of 10! => 14 49 | 50 | Results only: 51 | Final value: 5 52 | Final value: 3 53 | Final value: 1 54 | Final value: 17 55 | Final value: 14 56 | */ 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The zlib/libpng License 2 | 3 | Copyright (c) 2018 Maverick Peppers 4 | 5 | Permission is granted to anyone to use this software for any purpose, 6 | including commercial applications, and to alter it and redistribute it freely, 7 | subject to the following restrictions: 8 | 9 | The origin of this software must not be misrepresented; you must not claim 10 | that you wrote the original software. If you use this software in a product, 11 | an acknowledgment in the product documentation would be appreciated but 12 | is not required. Altered source versions must be plainly marked as such, 13 | and must not be misrepresented as being the original software. 14 | This notice may not be removed or altered from any source distribution. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 12/1/2018 2 | 3 | Due to feedback about compile-time limitations, the api has been changed since release. 4 | 5 | The api now supports user-defined literals which makes the coding experience even more intuitive. 6 | 7 | # UniformDieCast 8 | Provides chainable and iterable object for uniformly distributed die casts. 9 | Useful for statistics or table top RPG simulations. 10 | 11 | # Signature 12 | 13 | ## Literal Notation 14 | (humand readable) 15 | 16 | ```cpp 17 | using namespace die::literal; 18 | die::die_cast result = #N_D(int M); 19 | ``` 20 | 21 | ## Regular Notation 22 | (applicable for run-time) 23 | See [Example.cpp](https://github.com/TheMaverickProgrammer/UniformDieCast/blob/master/Example.cpp) 24 | 25 | ```cpp 26 | die::die_cast result = die::cast(int N, int M, int modifier=0); 27 | ``` 28 | 29 | Rolls an _M_ sided die _N_ number of times 30 | 31 | The signature comes from the common convention "3D8" meaning "roll an 8 sided dice 3 times". 32 | 33 | # Throws 34 | 35 | Both _N_ and _M_ must be greater than or equal to 1 otherwise an instance of `std::invalid_argument` is thrown. 36 | 37 | # Modifiers 38 | 39 | What about something like "2D20-4" or "1D8+10"? 40 | 41 | To add modifiers to a roll, do so like this: 42 | 43 | ```cpp 44 | auto result = 2_D(20)-4 << 1_D(8)+10; 45 | ``` 46 | 47 | Rolls a 20 sided die 2 times and subtracts 4 from each result in the group. 48 | 49 | Then it rolls an 8 sided die once and adds 10 to the result. 50 | 51 | # Iterable 52 | You can keep chaining the cast function for complex rolls or invoke them independantly 53 | on the `die_cast` object. The object stores each roll in an internal list. 54 | 55 | ```cpp 56 | die::die_cast chain = 10_D(6) << 1_D(20) << 3_D(4) << 1_D(8); 57 | ``` 58 | 59 | Here's how to fetch the result set: 60 | 61 | ```cpp 62 | for(auto& i : chain) { 63 | std::cout << "Final value: " << i << std::endl; 64 | } 65 | ``` 66 | 67 | You can copy the result sets for dynamic lookups like so: 68 | 69 | ```cpp 70 | // copy from the info list. only the final result set dissolves into a vector. 71 | std::vector final_values = chain; 72 | 73 | ... 74 | 75 | // direct assignment 76 | std::vector final_values = 3_D(5)+1; 77 | ``` 78 | 79 | # More info 80 | You can get more information about the roll after the fact. 81 | Properties like the roll number, how many sides the die had, and any modifiers. 82 | See below for a full example. 83 | 84 | ```cpp 85 | #include "DieCast.h" 86 | #include 87 | 88 | // die:: namespace has one function: cast() 89 | // returns a chainable object that is also an iterable result set 90 | int main() { 91 | // Roll a 6 sided die 3 times 92 | // Roll a 8 sided die 2 times and then add 10 to the result 93 | // e.g. roll 3D6 2D8+10 94 | auto res = 3_D(6) << 2_D(8)+10; 95 | 96 | // iterable die_cast_info object can tell you 97 | // the following properties about a roll: 98 | // count of sides on die, roll #, modifier, and the result 99 | for(auto& i : res) { 100 | std::cout << "Rolled a " << i.sides << " sided die "; 101 | std::cout << i.roll << ((i.roll == 1)? " time " : " times"); 102 | 103 | if(i.modifier != 0) { 104 | std::cout << " with a modifier of " << i.modifier << "!"; 105 | } else { 106 | std::cout << "!"; 107 | } 108 | 109 | std::cout << " => " << i.result << std::endl; 110 | } 111 | } 112 | 113 | /* 114 | Output 115 | ---------------------------------------------------------- 116 | 117 | $g++ -o main UserLiteralExample.cpp 118 | $main 119 | 120 | Rolled a 6 sided die 1 time ! => 5 121 | Rolled a 6 sided die 2 times! => 3 122 | Rolled a 6 sided die 3 times! => 1 123 | Rolled a 8 sided die 1 time with a modifier of 10! => 17 124 | Rolled a 8 sided die 2 times with a modifier of 10! => 14 125 | */ 126 | ``` 127 | -------------------------------------------------------------------------------- /UserLiteralExample.cpp: -------------------------------------------------------------------------------- 1 | #include "DieCast.h" 2 | #include 3 | 4 | // die::literal namespace exports a literal "# of rolls"_D(int sides) 5 | // returns a facade to the die_cast object that can be concatenated. 6 | int main() { 7 | 8 | using namespace die::literal; 9 | die::die_cast res = 4_D(6)+5 << 3_D(8) << 10_D(2)-30 << 1_D(6); 10 | 11 | for(auto& i : res) { 12 | std::cout << "Rolled a " << i.sides << " sided die "; 13 | std::cout << i.roll << ((i.roll == 1)? " time " : " times"); 14 | 15 | if(i.modifier != 0) { 16 | std::cout << " with a modifier of " << i.modifier << "!"; 17 | } else { 18 | std::cout << "!"; 19 | } 20 | 21 | std::cout << " => " << i.result << std::endl; 22 | } 23 | 24 | std::cout << "\nResults only:" << std::endl; 25 | 26 | // Dissolve into integer result set only 27 | // if you just want the final value 28 | for(auto& i : res) { 29 | std::cout << "Final value: " << i << std::endl; 30 | } 31 | } 32 | --------------------------------------------------------------------------------