From 854ad5a1a38e697e8ef2d05375ae13d8f410e154 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 13 Apr 2018 23:59:20 -0700 Subject: [PATCH] First commit - Seems to pass "all 1's" evolution test --- Breeder.cpp | 152 +++++++++++++++++ Breeder.h | 41 +++++ Chromosome.cpp | 184 ++++++++++++++++++++ Chromosome.h | 71 ++++++++ Defines.h | 13 ++ Enums.h | 9 + ForwardDeclarations.h | 14 ++ Includes.h | 13 ++ Makefile | 122 +++++++++++++ Population.cpp | 389 ++++++++++++++++++++++++++++++++++++++++++ Population.h | 96 +++++++++++ Random.cpp | 86 ++++++++++ Random.h | 41 +++++ 13 files changed, 1231 insertions(+) create mode 100644 Breeder.cpp create mode 100644 Breeder.h create mode 100644 Chromosome.cpp create mode 100644 Chromosome.h create mode 100644 Defines.h create mode 100644 Enums.h create mode 100644 ForwardDeclarations.h create mode 100644 Includes.h create mode 100644 Population.cpp create mode 100644 Population.h create mode 100644 Random.cpp create mode 100644 Random.h diff --git a/Breeder.cpp b/Breeder.cpp new file mode 100644 index 0000000..f4d8fa0 --- /dev/null +++ b/Breeder.cpp @@ -0,0 +1,152 @@ + + +// +#include "BitEvolver/Random.h" +#include "BitEvolver/Chromosome.h" +#include "BitEvolver/Breeder.h" + + +// +#include + + +// +namespace BitEvolver +{ + // + using std::cout; + using std::endl; + + // + Breeder::Breeder(std::shared_ptr _random) + { + // + this->random = _random; + } + + // + std::shared_ptr Breeder::Breed( + std::shared_ptr mama, + std::shared_ptr papa, + double crossover_rate, + double mutation_rate + ) + { + // + std::shared_ptr kiddo; + int bit_length; + + // For now, don't crossover unless the bit lengths are identical + bit_length = mama->GetBitCount(); + if ( papa->GetBitCount() != bit_length ) { + throw std::runtime_error("Breeder::Breed() - Incompatible parents"); + } + + // Directly copy the mama + kiddo = std::shared_ptr(new Chromosome(this->random, bit_length)); + *kiddo = *mama; + + // Apply crossover + this->ApplyCrossover(kiddo, papa, crossover_rate); + + // Apply mutation + this->Mutate(kiddo, mutation_rate); + + return kiddo; + } + + // + void Breeder::Mutate(std::shared_ptr chromosome, double mutation_rate) + { + // + int i, size; + + // + #warning "For efficiency, let's instead roll the number of bits to flip in advance, and loop that many times (faster than rolling for every bit)" + + // + size = chromosome->GetBitCount(); + for ( i=0; irandom->RollBool(mutation_rate) ) { + chromosome->FlipBit(i); + } + } + + // + chromosome->ResetFitness(); + } + + // + int Breeder::PickRandomCrossoverPoint(std::shared_ptr chromosome, double crossover_rate) + { + // + double crossover_point_double; + int crossover_point; + int bit_count; + + // + bit_count = chromosome->GetBitCount(); + + /** + Choose a double between [0.0,1.0] for the crossover point. + Use normal distribution, with the mean to the default point, + and 1.0 as the std. + That way, there is still randomness to the crossover point, + but it still generally sticks near the default + */ + crossover_point_double = this->random->GetNormal(crossover_rate, 1.0); + + // Apply to the actual int length + crossover_point = crossover_point_double * bit_count; + + // Loop around to keep in bounds + while ( crossover_point < 0 ) + { + crossover_point += bit_count; + } + while ( crossover_point >= bit_count) + { + crossover_point -= bit_count; + } + + return crossover_point; + } + + // + void Breeder::ApplyCrossover(std::shared_ptr kiddo, std::shared_ptr parent, double crossover_rate) + { + // + int + bits_count, + crossover_point, + i + ; + + // + bits_count = kiddo->GetBitCount(); + if ( parent->GetBitCount() != bits_count ) { + throw std::runtime_error("Breeder::ApplyCrossover() - Parent incompatible with Kiddo (bit lengths don't match)"); + } + + // Pick random crossover point + crossover_point = this->PickRandomCrossoverPoint(kiddo, crossover_rate); + + // Begin copying the parent at the crossover point and beyond + // (not before) + for ( i=crossover_point; iSetBit( i, parent->GetBit(i) ); + } + + // + kiddo->ResetFitness(); + } +}; + + + + + + + diff --git a/Breeder.h b/Breeder.h new file mode 100644 index 0000000..7decf6e --- /dev/null +++ b/Breeder.h @@ -0,0 +1,41 @@ + + +// +#pragma once + + +// +namespace BitEvolver +{ + // + class Breeder + { + // + public: + + // + Breeder(std::shared_ptr _random); + + // + std::shared_ptr Breed( + std::shared_ptr mama, + std::shared_ptr papa, + double crossover_rate, + double mutation_rate + ); + + // + void Mutate(std::shared_ptr chromosome, double mutation_rate); + + // + private: + + // + std::shared_ptr random; + + // + int PickRandomCrossoverPoint(std::shared_ptr chromosome, double crossover_rate); + void ApplyCrossover(std::shared_ptr kiddo, std::shared_ptr parent, double crossover_rate); + }; +}; + diff --git a/Chromosome.cpp b/Chromosome.cpp new file mode 100644 index 0000000..0fde499 --- /dev/null +++ b/Chromosome.cpp @@ -0,0 +1,184 @@ + + +// +#include "BitEvolver/Chromosome.h" +#include "BitEvolver/Random.h" + + +// +#include +#include +#include +#include + + +// +namespace BitEvolver +{ + // + using std::string; + using std::to_string; + using std::stringstream; + + // + Chromosome::Chromosome(std::shared_ptr _random, int _bits) + { + // + this->random = _random; + this->SetBitCount(_bits); + + // + this->Reset(); + } + + // + void Chromosome::Reset() + { + // + this->Randomize(); + } + + // + void Chromosome::Randomize() + { + // + int i; + + // + this->bits.clear(); + + // + for ( i=0; ibits_count_desired; i++ ) { + this->bits.push_back(this->random->GetInt(0, 1)); + } + } + + // + void Chromosome::SetBitCount(int count) + { + // + this->bits_count_desired = count; + this->Randomize(); + } + + // + int Chromosome::GetBitCount() + { + return (int)this->bits.size(); + } + + // + void Chromosome::FlipBit(int index) + { + // + if ( index >= (int)this->bits.size() ) { + throw std::runtime_error("Chromosome::FlipBit() - Tried to flip out of range bit index: " + to_string(index)); + } + + // + if ( this->bits[index] ) { + this->bits[index] = false; + } + else{ + this->bits[index] = true; + } + } + + // + bool Chromosome::GetBit(int index) + { + // + if ( index >= (int)this->bits.size() ) { + throw std::runtime_error("Chromosome::GetBit() - Tried to access out of bounds bit"); + } + + // + return this->bits[index]; + } + + // + void Chromosome::SetBit(int index, bool b) + { + // + if ( index >= (int)this->bits.size() ) { + throw std::runtime_error("Chromosome::GetBit() - Tried to access out of bounds bit"); + } + + // + this->bits[index] = b; + } + + // + void Chromosome::ResetFitness() + { + // + this->SetFitness(0.0); + } + + // + void Chromosome::SetFitness(double d) + { + // + this->fitness = d; + } + + // + void Chromosome::AdjustFitness(double d) + { + // + this->fitness += d; + } + + // + double Chromosome::GetFitness() + { + // + return this->fitness; + } + + // + string Chromosome::ToString() + { + // + stringstream s; + + // + for ( bool b : this->bits ) { + + // + if ( b ) { + s << "1"; + } + else{ + s << "0"; + } + } + + // + return s.str(); + } + + // + const Chromosome& Chromosome::operator=(const Chromosome& other) + { + // + int i; + + // + this->random = other.random; + this->bits = other.bits; + this->bits_count_desired = other.bits_count_desired; + this->fitness = other.fitness; + + // Copy the bits! + this->bits.clear(); + for ( i=0; i<(int)other.bits.size(); i++ ) { + this->bits.push_back(other.bits[i]); + } + + return *this; + } +}; + + + diff --git a/Chromosome.h b/Chromosome.h new file mode 100644 index 0000000..a403b68 --- /dev/null +++ b/Chromosome.h @@ -0,0 +1,71 @@ + + +// +#pragma once + + +// +#include "BitEvolver/Includes.h" + + +// +#include +#include +#include +#include + + +// +namespace BitEvolver +{ + // + class Chromosome + { + // + public: + + // + Chromosome(std::shared_ptr _random, int _bits); + + // + void Reset(); + void Randomize(); + + // + void SetBitCount(int count); + int GetBitCount(); + + // + void FlipBit(int index); + + // + bool GetBit(int index); + void SetBit(int index, bool b); + + // + void ResetFitness(); + void SetFitness(double d); + void AdjustFitness(double d); + double GetFitness(); + + // + std::string ToString(); + + // + const Chromosome& operator=(const Chromosome& other); + + // + private: + + // Random number generator + std::shared_ptr random; + + // + std::vector bits; + int bits_count_desired; + + // Fitness + double fitness; + }; +}; + diff --git a/Defines.h b/Defines.h new file mode 100644 index 0000000..61a3ba4 --- /dev/null +++ b/Defines.h @@ -0,0 +1,13 @@ +#ifndef BITEVOLVER_DEFINES_H +#define BITEVOLVER_DEFINES_H + + + +// +#define BIT_EVOLVER_POPULATION_DEFAULT_POPULATION_SIZE 100 +#define BIT_EVOLVER_POPULATION_DEFAULT_MUTATE_RATE 0.01 +#define BIT_EVOLVER_POPULATION_DEFAULT_CROSSOVER 0.7 + + + +#endif \ No newline at end of file diff --git a/Enums.h b/Enums.h new file mode 100644 index 0000000..b108c2c --- /dev/null +++ b/Enums.h @@ -0,0 +1,9 @@ +#ifndef BITEVOLVER_ENUMS_H +#define BITEVOLVER_ENUMS_H + + +// + + + +#endif \ No newline at end of file diff --git a/ForwardDeclarations.h b/ForwardDeclarations.h new file mode 100644 index 0000000..25f710e --- /dev/null +++ b/ForwardDeclarations.h @@ -0,0 +1,14 @@ +#ifndef BITEVOLVER_FORWARD_DECLARATIONS_H +#define BITEVOLVER_FORWARD_DECLARATIONS_H + +// +namespace BitEvolver +{ + // + class Population; + class Breeder; + class Chromosome; +}; + + +#endif \ No newline at end of file diff --git a/Includes.h b/Includes.h new file mode 100644 index 0000000..da3cbb8 --- /dev/null +++ b/Includes.h @@ -0,0 +1,13 @@ +#ifndef BITEVOLVER_INCLUDES_H +#define BITEVOLVER_INCLUDES_H + + +// +#include "BitEvolver/Defines.h" +#include "BitEvolver/Enums.h" + + + + + +#endif \ No newline at end of file diff --git a/Makefile b/Makefile index 792d600..5b71764 100644 --- a/Makefile +++ b/Makefile @@ -1 +1,123 @@ + +# Custom functions +define say + $(info [BitEvolver] $1) +endef +define error + $(error [BitEvolver] $1) +endef +define die + $(call error,$1) + $(exit 1) +endef + + +# Demand BUILD_DIR and BIN_DIR +ifeq ($(BUILD_DIR),) +$(call die,Please provide BUILD_DIR) +endif +ifeq ($(BIN_DIR),) +$(call die,Please provide BIN_DIR) +endif + + # +CC=g++ +CFLAGS= -c -std=c++11 -Wall -I.. + + +# +OBJECT_PREFIX=BitEvolver_ + + +# +default: default-say +default: release +default: + $(call say,Default target finished) +default-say: + $(call say,Using default target: release) + + +# +release: release-say +release: CFLAGS+= -O2 +release: build-objects +release: + $(call say,Done building RELEASE) +release-say: + $(call say,Building RELEASE) + +# +debug: debug-say +debug: CFLAGS+= -g -g3 +debug: build-objects +debug: + $(call say,Done building DEBUG) +debug-say: + $(call say,Building DEBUG) + + +# +build-objects: \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Random.o \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Population.o \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Breeder.o \ + $(BUILD_DIR)/$(OBJECT_PREFIX)Chromosome.o + $(call say,Done building objects) + + +# Population.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Population.o: \ + Population.h \ + Population.cpp \ + Defines.h Enums.h Includes.h \ + Random.h \ + Chromosome.h + $(CC) -o $@ \ + Population.cpp \ + $(CFLAGS) + $(call say,Built $@) + + +# Breeder.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Breeder.o: \ + Breeder.h \ + Breeder.cpp \ + Defines.h Enums.h Includes.h \ + Random.h \ + Chromosome.h + $(CC) -o $@ \ + Breeder.cpp \ + $(CFLAGS) + $(call say,Built $@) + + +# Chromosome.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Chromosome.o: \ + Chromosome.h \ + Chromosome.cpp \ + Defines.h Enums.h Includes.h \ + Random.h + $(CC) -o $@ \ + Chromosome.cpp \ + $(CFLAGS) + $(call say,Built $@) + + +# Random.o +$(BUILD_DIR)/$(OBJECT_PREFIX)Random.o: \ + Random.h \ + Random.cpp \ + Defines.h Enums.h Includes.h + $(CC) -o $@ \ + Random.cpp \ + $(CFLAGS) + $(call say,Built $@) + + + + + + + diff --git a/Population.cpp b/Population.cpp new file mode 100644 index 0000000..1458be2 --- /dev/null +++ b/Population.cpp @@ -0,0 +1,389 @@ + + +// +#include "BitEvolver/Includes.h" +#include "BitEvolver/Random.h" +#include "BitEvolver/Population.h" +#include "BitEvolver/Breeder.h" +#include "BitEvolver/Chromosome.h" + + +// +#include +#include +#include +#include +#include +#include + + +// +namespace BitEvolver +{ + // + using std::cout; + using std::endl; + + // + Population::Population() + { + // + this->InitRandomGenerator(); + this->InitBreeder(); + + // + this->Reset(); + } + + // + void Population::Reset() + { + // + this->population_size = BIT_EVOLVER_POPULATION_DEFAULT_POPULATION_SIZE; + this->SetMutationRate(BIT_EVOLVER_POPULATION_DEFAULT_MUTATE_RATE); + this->evolution_number = 0; + + // + this->RandomizePopulation(this->population_size); + } + + // + void Population::ClearPopulation() + { + // + this->chromosomes.clear(); + this->evolution_number = 0; + } + + // + void Population::InitRandomPopulation(int _population_size, int _bit_length) + { + // + this->population_size = _population_size; + + // + this->RandomizePopulation(_bit_length); + } + + // + void Population::RandomizePopulation(int _bit_length) + { + // + std::shared_ptr chromosome; + int i; + + // + this->ClearPopulation(); + for ( i=0; ipopulation_size; i++ ) { + + // + chromosome = std::shared_ptr( + new Chromosome( this->random, _bit_length ) + ); + this->chromosomes.push_back(chromosome); + } + } + + // + void Population::PopulationChanged() + { + // + this->population_needs_sorting = true; + } + + // + std::vector> Population::GetChromosomes() + { + return this->chromosomes; + } + + // + void Population::GetChromosomes(std::shared_ptr>> _chromosomes) + { + // + _chromosomes->clear(); + for ( std::shared_ptr chromosome : this->chromosomes) { + _chromosomes->push_back(chromosome); + } + } + + // + std::shared_ptr Population::GetChampion() + { + // + this->EnsureSortedPopulation(); + + // + if ( this->chromosomes.size() > 0 ) { + return this->chromosomes[0]; + } + + return nullptr; + } + + // + double Population::GetAverageFitness() + { + return this->GetAverageFitness(this->chromosomes); + } + + // + double Population::GetAverageFitness(std::vector> _chromosomes) + { + // + double fitness_sum; + double fitness_average; + + // + fitness_sum = 0; + for ( std::shared_ptr chromosome : _chromosomes ) { + fitness_sum += chromosome->GetFitness(); + } + + // + fitness_average = 0; + if ( _chromosomes.size() > 0 ) { + fitness_average = fitness_sum / _chromosomes.size(); + } + + return fitness_average; + } + + // + void Population::SetMutationRate(double r) + { + // + this->mutation_rate = r; + } + + // + void Population::Evolve() + { + // + std::shared_ptr > > population_new; + + // + if ( this->chromosomes.size() == 0 ) { + return; + } + + // + this->EnsureSortedPopulation(); + + // + population_new = std::shared_ptr< + std::vector< + std::shared_ptr + > + >( + new std::vector>() + ); + + // Start the new population off with our champion, + // so the best score always carries over (elitism = 1 unit) + #warning "Elitism is only 1 right now" + population_new->push_back(this->chromosomes[0]); + + // Breed the new population + this->BreedNewPopulation(population_new, (int)this->chromosomes.size()); + + // Replace old population with the new + this->chromosomes = *population_new; + this->evolution_number++; + + // + this->PopulationChanged(); + } + + // + int Population::GetEvolutionNumber() + { + return this->evolution_number; + } + + // + void Population::PrintPopulation() + { + // + this->EnsureSortedPopulation(); + + this->PrintPopulation(this->chromosomes); + } + + // + void Population::PrintPopulation(std::vector> _chromosomes) + { + // + for ( std::shared_ptr chromosome : chromosomes ) { + cout << chromosome->ToString() << endl; + } + cout << "Average Fitness --> " << this->GetAverageFitness(_chromosomes) << endl; + } + + // + void Population::InitRandomGenerator() + { + // + this->random = std::shared_ptr( + new Random() + ); + } + + // + void Population::InitBreeder() + { + // + if ( !this->random ) { + throw std::runtime_error("Population::InitBreeder() - Should come after InitRandomGenerator()"); + } + + // + this->breeder = std::shared_ptr( + new Breeder( this->random ) + ); + } + + // + void Population::EnsureSortedPopulation() + { + // + if ( !this->population_needs_sorting ) { + return; + } + + // Yay std::sort + std::sort( + this->chromosomes.begin(), + this->chromosomes.end(), + []( std::shared_ptr& left, std::shared_ptr& right ) -> bool + { + // + if ( left->GetFitness() > right->GetFitness() ) { + return true; + } + + return false; + } + ); + + // + this->population_needs_sorting = false; + } + + // + void Population::BreedNewPopulation(std::shared_ptr>> population_new, int size) + { + // + std::vector> threads; + std::shared_ptr thread; + int + thread_count, + i + ; + + // + thread_count = std::thread::hardware_concurrency(); + + // + for ( i=0; i( + new std::thread(&Population::BreedNewPopulation_Thread, this, population_new, size) + ); + threads.push_back(thread); + } + + // + for ( i=0; i<(int)threads.size(); i++) { + threads[i]->join(); + } + } + + // + void Population::BreedNewPopulation_Thread(std::shared_ptr>> population_new, int size) + { + // + std::shared_ptr kiddo; + + // + while ( (int)population_new->size() < size ) + { + // + kiddo = this->BreedChild(); + + // Mutexed + this->breed_mutex.lock(); + if ( (int)population_new->size() < size ) { + population_new->push_back(kiddo); + } + this->breed_mutex.unlock(); + } + } + + // + std::shared_ptr Population::BreedChild() + { + // + std::shared_ptr + mama, papa, kiddo + ; + + // Pick two parents + mama = this->PickChromosomeForBreeding(); + papa = this->PickChromosomeForBreeding(); + + // + kiddo = this->breeder->Breed(mama, papa, BIT_EVOLVER_POPULATION_DEFAULT_CROSSOVER, this->mutation_rate); + + return kiddo; + } + + /** + (1) We want to pick the best chromosomes to repopulate + (2) [not in lecture] We want to add a bit of randomness + to the selection process, such that the worst chromosomes + can still possibly be picked, and the best can still + possibly be not-picked; Their fitness only increases + the probability they're picked. + */ + std::shared_ptr Population::PickChromosomeForBreeding() + { + // + double normal; + int chromosome_index; + + // + this->EnsureSortedPopulation(); + + /** + Grab normal with 0 at the mean, and the standard deviation equal + to 1/2 of the population size. Then make that an absolute value. + This will make top/best chromosomes more likely to be picked, + with the far/low end being much less likely + */ + #warning "Need to upgrade this to Roulette Wheel" + // Repeat as needed, since the normal generator might actually + // give us an out-of-bounds result sometimes + while ( true ) + { + // + normal = this->random->GetNormal(0, 0.5); + chromosome_index = abs( normal * this->chromosomes.size() + ); + if ( chromosome_index >= 0 && chromosome_index < (int)this->chromosomes.size() ) { + break; + } + } + + // + return this->chromosomes[chromosome_index]; + } +}; + + + + + + + + + diff --git a/Population.h b/Population.h new file mode 100644 index 0000000..2e0c703 --- /dev/null +++ b/Population.h @@ -0,0 +1,96 @@ + + +// +#pragma once + + +// +#include "BitEvolver/Includes.h" + + +// +#include +#include +#include + + +// +namespace BitEvolver +{ + // + class Population + { + // + public: + + // + Population(); + + // + void Reset(); + + // + void ClearPopulation(); + void InitRandomPopulation(int _population_size, int _bit_length); + void RandomizePopulation(int _bit_length); + + // + void PopulationChanged(); + + // + std::vector> GetChromosomes(); + void GetChromosomes(std::shared_ptr>> _chromosomes); + std::shared_ptr GetChampion(); + + // + double GetAverageFitness(); + double GetAverageFitness(std::vector> _chromosomes); + + // + void SetMutationRate(double r); + + // + void Evolve(); + int GetEvolutionNumber(); + + // + void PrintPopulation(); + void PrintPopulation(std::vector> _chromosomes); + + // + private: + + // + std::shared_ptr random; + std::shared_ptr breeder; + + // + std::vector> chromosomes; + int population_size; + bool population_needs_sorting; + double mutation_rate; + int evolution_number; + + // + std::mutex breed_mutex; + + // + void InitRandomGenerator(); + void InitBreeder(); + + // + void EnsureSortedPopulation(); + + // + void BreedNewPopulation(std::shared_ptr>> population_new, int size); + void BreedNewPopulation_Thread(std::shared_ptr>> population_new, int size); + + // + std::shared_ptr BreedChild(); + std::shared_ptr PickChromosomeForBreeding(); + + }; +}; + + + diff --git a/Random.cpp b/Random.cpp new file mode 100644 index 0000000..87726a5 --- /dev/null +++ b/Random.cpp @@ -0,0 +1,86 @@ + + +// +#include "BitEvolver/Random.h" + + +// +#include +#include + + +// +namespace BitEvolver +{ + // + Random::Random() + { + // + this->InitializeGenerators(); + } + + // + int Random::GetInt(int min, int max) + { + // + std::uniform_int_distribution distribution(min, max); + + // + int the_int = distribution(this->generator_mersenne_twister); + + return the_int; + } + + // + double Random::GetDouble(double min, double max) + { + // + std::uniform_real_distribution distribution(min, max); + + // + double the_double = distribution(this->generator_mersenne_twister); + + return the_double; + } + + // + double Random::GetNormal(double mean, double standard_deviation) + { + // + std::normal_distribution distribution(mean, standard_deviation); + + double d = distribution(this->generator_mersenne_twister); + + return d; + } + + // + bool Random::RollBool(double chance) + { + // + double d = this->GetDouble(0, 1); + if ( d <= chance ) { + return true; + } + + return false; + } + + // + void Random::InitializeGenerators() + { + // Mostly taken from + // http://www.cplusplus.com/reference/random/mersenne_twister_engine/seed/ + typedef std::chrono::high_resolution_clock myclock; + + // + myclock::time_point beginning = myclock::now(); + myclock::duration d = myclock::now() - beginning; + unsigned seed = d.count(); + + // Seed our internal generator + this->generator_mersenne_twister.seed(seed); + } +}; + + diff --git a/Random.h b/Random.h new file mode 100644 index 0000000..9d9d8d4 --- /dev/null +++ b/Random.h @@ -0,0 +1,41 @@ + + +// +#pragma once + + +// +#include + + +// +namespace BitEvolver +{ + // + class Random + { + // + public: + + // + Random(); + + // + int GetInt(int min, int max); + double GetDouble(double min, double max); + double GetNormal(double mean, double standard_deviation); + + // + bool RollBool(double chance); + + // + private: + + // + std::mt19937_64 generator_mersenne_twister; + + // + void InitializeGenerators(); + + }; +};