-
-
Save jrandom/c9eae51f58876c897e1b929390f1c900 to your computer and use it in GitHub Desktop.
Argh...
This file contains 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
// | |
// Gestalt.h | |
// Tools/AI/Collections | |
// | |
// Copyright © 2017 Bradley Wilson. All rights reserved. | |
// E-Mail: the.mind.walrus@gmail.com | |
// | |
#ifndef Tools_AI_Collections_Gestalt_h | |
#define Tools_AI_Collections_Gestalt_h | |
// ================================================================================ Tools Includes | |
// Tools Includes | |
// -------------------------------------------------------------------------------- | |
#include "Tools/Core/Local_Allocator.h" | |
#include "Tools/Core/Local_Collections.h" | |
#include "Tools/AI/Fragments/Gestalt_Tag.h" | |
// ================================================================================ Standard Includes | |
// Standard Includes | |
// -------------------------------------------------------------------------------- | |
#include <algorithm> | |
#include <set> | |
#include <utility> | |
namespace AI | |
{ | |
template < typename gestalt_tag_type_id_t, | |
typename Foundry_t > | |
class Gestalt | |
{ | |
public: | |
// -------------------------------------------------------------------- Types | |
using SharedPtr_t = std::shared_ptr< Gestalt >; | |
using Tag_t = Gestalt_Tag< gestalt_tag_type_id_t, Foundry_t >; | |
using Tag_SPtr_t = typename Tag_t::SharedPtr_t; | |
using Tag_Allowed_Type_IDs_t = typename Tag_t::Type_ID_Init_List_t ; | |
using Tag_Set_t = Core::Local::Set< Tag_SPtr_t, | |
decltype( &Tag_t::from_sptr_is_Less_Than ) >; | |
enum class Mutation_Action { Drop, New_Random, Clone_Mutated }; | |
// -------------------------------------------------------------------- Settings_for_Random_t | |
struct Settings_for_Random | |
{ | |
float probability_of_nonempty; | |
typename Tag_Set_t::size_type max_tag_count; | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids; | |
}; | |
private: | |
// -------------------------------------------------------------------- State | |
Tag_Set_t _gestalt_tags; | |
public: | |
// ==================================================================== Constructors | |
// Constructors | |
// -------------------------------------------------------------------- construct( empty ) | |
Gestalt() : _gestalt_tags( Empty_Gestalt_Tag_Set() ) {} | |
// -------------------------------------------------------------------- construct( from sptr ) | |
Gestalt( const SharedPtr_t & source_ptr ) | |
: _gestalt_tags | |
( | |
( source_ptr ) | |
? source_ptr->_gestalt_tags | |
: Empty_Gestalt_Tag_Set() | |
) | |
{} | |
// -------------------------------------------------------------------- construct( copy / move from set ) | |
Gestalt( const Tag_Set_t & gestalt_tags ) : _gestalt_tags( gestalt_tags ) {} | |
Gestalt( Tag_Set_t && gestalt_tags ) : _gestalt_tags( std::move( gestalt_tags ) ) {} | |
// -------------------------------------------------------------------- construct( copy / move ) | |
Gestalt( const Gestalt & source ) = default; | |
Gestalt( Gestalt && source ) = default; | |
// -------------------------------------------------------------------- construct( random ) | |
Gestalt( Random::Generator & rng, | |
const Settings_for_Random & rng_settings, | |
const Foundry_t & foundry ) | |
: _gestalt_tags( Empty_Gestalt_Tag_Set() ) | |
{ | |
if( rng.Chance( rng_settings.probability_of_nonempty ) ) | |
{ | |
std::generate_n | |
( | |
std::inserter( _gestalt_tags, _gestalt_tags.begin() ), | |
rng.Size_T( 1, rng_settings.max_tag_count ), | |
[ &rng, &foundry, &rng_settings ]() | |
{ | |
return Tag_t::Get_Random | |
( | |
rng, | |
rng_settings.allowed_tag_type_ids, | |
foundry | |
); | |
} | |
); | |
} | |
} | |
// -------------------------------------------------------------------- construct( copy / mutate one ) | |
Gestalt( const Gestalt & source, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
const Foundry_t & foundry ) | |
: _gestalt_tags | |
( | |
Tags_Mutated_from( source._gestalt_tags, | |
rng, | |
allowed_tag_type_ids, | |
foundry ) | |
) | |
{} | |
// -------------------------------------------------------------------- construct( from sptr mutate one ) | |
Gestalt( const SharedPtr_t & source_sptr, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
const Foundry_t & foundry ) | |
: _gestalt_tags | |
( | |
Tags_Mutated_from | |
( | |
(source_sptr) | |
? source_sptr->_gestalt_tags | |
: Empty_Gestalt_Tag_Set(), | |
rng, | |
allowed_tag_type_ids, | |
foundry | |
) | |
) | |
{} | |
// -------------------------------------------------------------------- construct( copy / mutate all ) | |
Gestalt( const Gestalt & source, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
float mutation_rate, | |
const Foundry_t & foundry ) | |
: _gestalt_tags | |
( | |
Tags_Mutated_from( source._gestalt_tags, | |
rng, | |
allowed_tag_type_ids, | |
mutation_rate, | |
foundry ) | |
) | |
{} | |
// -------------------------------------------------------------------- construct( from sptr mutate all ) | |
Gestalt( const SharedPtr_t & source_sptr, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
float mutation_rate, | |
const Foundry_t & foundry ) | |
: _gestalt_tags | |
( | |
Tags_Mutated_from | |
( | |
(source_sptr) | |
? source_sptr->_gestalt_tags | |
: Empty_Gestalt_Tag_Set(), | |
rng, | |
allowed_tag_type_ids, | |
mutation_rate, | |
foundry | |
) | |
) | |
{} | |
public: | |
// ==================================================================== Public API | |
// Public API | |
// -------------------------------------------------------------------- Iterators / Accessors | |
decltype( _gestalt_tags.cbegin() ) cbegin() const { return _gestalt_tags.cbegin(); } | |
decltype( _gestalt_tags.cend () ) cend () const { return _gestalt_tags.cend(); } | |
decltype( _gestalt_tags.size () ) Count () const { return _gestalt_tags.size(); } | |
// -------------------------------------------------------------------- Clone_Mutated( mutate one ) | |
SharedPtr_t Clone_Mutated ( Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
const Foundry_t & foundry ) const | |
{ | |
return Core::Allocate_Shared< Gestalt >( *this, | |
rng, | |
allowed_tag_type_ids, | |
foundry ); | |
} | |
// -------------------------------------------------------------------- Clone_Mutated( mutate all ) | |
SharedPtr_t Clone_Mutated ( Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
float mutation_rate, | |
const Foundry_t & foundry ) const | |
{ | |
return Core::Allocate_Shared< Gestalt >( *this, | |
rng, | |
allowed_tag_type_ids, | |
mutation_rate, | |
foundry ); | |
} | |
// -------------------------------------------------------------------- Contains( gestalt tag ) | |
bool Contains( const Tag_SPtr_t & gestalt_tag ) const | |
{ | |
return _gestalt_tags.find( gestalt_tag ) != _gestalt_tags.end(); | |
} | |
// -------------------------------------------------------------------- Contains( other gestalt ) | |
bool Contains( const Gestalt & other_gestalt ) const | |
{ | |
// Handle us being empty | |
if( _gestalt_tags.size() == 0 ) | |
return other_gestalt._gestalt_tags.size() == 0; | |
// Compare sets | |
for( const auto & other_gestalt_tag: other_gestalt._gestalt_tags ) | |
if( _gestalt_tags.find( other_gestalt_tag ) == _gestalt_tags.end() ) | |
return false; | |
return true; | |
} | |
// -------------------------------------------------------------------- Percent_Contains() | |
float Percent_Contains( const Gestalt & other_gestalt ) const | |
{ | |
// Handle empty sets | |
if( other_gestalt._gestalt_tags.size() == 0 ) | |
{ | |
return _gestalt_tags.size() == 0 | |
? 1.0f | |
: 0.0f; | |
} | |
// Calculate percent contains | |
return float( other_gestalt.Count_Matches_in( _gestalt_tags ) ) | |
/ float( other_gestalt._gestalt_tags.size() ); | |
} | |
// -------------------------------------------------------------------- Percent_Exact_Match() | |
float Percent_Exact_Match( const Gestalt & other_gestalt ) const | |
{ | |
// Handle empty sets | |
if( other_gestalt._gestalt_tags.size() == 0 ) | |
{ | |
return _gestalt_tags.size() == 0 | |
? 1.0f | |
: 0.0f; | |
} | |
// Calculate exact match | |
return float( Count_Matches_in( other_gestalt ) ) | |
/ float( std::max( _gestalt_tags.size(), | |
other_gestalt._gestalt_tags.size() ) ); | |
} | |
// -------------------------------------------------------------------- Percent_Contains_and_Percent_Exact_Match() | |
std::pair< float, float > | |
Percent_Contains_and_Percent_Exact_Match( const Gestalt & other_gestalt ) const | |
{ | |
// Handle empty sets | |
if( other_gestalt._gestalt_tags.size() == 0 ) | |
{ | |
return _gestalt_tags.size() == 0 | |
? std::make_pair( 1.0f, 1.0f ) | |
: std::make_pair( 0.0f, 1.0f / float( _gestalt_tags.size() ) ); | |
} | |
// Compare sets | |
int matches = Count_Matches_in( other_gestalt ); | |
const float percent_contains = float( matches ) | |
/ float( other_gestalt._gestalt_tags.size() ); | |
const float percent_exact_match = float( matches ) | |
/ float( std::max( _gestalt_tags.size(), | |
other_gestalt._gestalt_tags.size() ) ); | |
return { percent_contains, percent_exact_match }; | |
}; | |
// -------------------------------------------------------------------- is_Less_Than() | |
bool is_Less_Than( const Gestalt & rhs ) const | |
{ | |
// Treat gestalt tags like digits in a number. If the lengths | |
// of the sets differ, the shorter one is less than. | |
if( _gestalt_tags.size() < rhs._gestalt_tags.size() ) return true; | |
if( rhs._gestalt_tags.size() < _gestalt_tags.size() ) return false; | |
// Lengths are equal, so compare "digit" by "digit" | |
auto lhs_gestalt_tag_sptr_iter = _gestalt_tags.cbegin(); | |
auto rhs_gestalt_tag_sptr_iter = rhs._gestalt_tags.cbegin(); | |
while( lhs_gestalt_tag_sptr_iter != _gestalt_tags.cend() ) | |
{ | |
if | |
( | |
Tag_t::from_sptr_is_Less_Than( (*lhs_gestalt_tag_sptr_iter), | |
(*rhs_gestalt_tag_sptr_iter) ) | |
) return true; | |
lhs_gestalt_tag_sptr_iter++; | |
rhs_gestalt_tag_sptr_iter++; | |
} | |
// Sets are exactly equal, so we're not less than. | |
return false; | |
} | |
private: | |
// ==================================================================== Prvate API | |
// Prvate API | |
// -------------------------------------------------------------------- Count_Matches_in() | |
int Count_Matches_in( const Gestalt & other_gestalt ) const | |
{ | |
int matches = 0; | |
for( const auto & other_gestalt_tag: other_gestalt._gestalt_tags ) | |
if( _gestalt_tags.find( other_gestalt_tag ) != _gestalt_tags.end() ) | |
matches++; | |
return matches; | |
} | |
public: | |
// ==================================================================== Public Static API - Comparison / Query | |
// Public Static API - Comparison | |
// -------------------------------------------------------------------- from_sptr_is_Less_Than() | |
static bool from_sptr_is_Less_Than( const SharedPtr_t & lhs, const SharedPtr_t & rhs ) | |
{ | |
if( !lhs ) return (bool) rhs; // If lhs is null, it might be less than. | |
if( !rhs ) return false; // Otherwise, if rhs is null then lhs is not less than. | |
// Neither are null, so compare contents | |
return lhs->is_Less_Than( *rhs ); | |
} | |
// -------------------------------------------------------------------- from_sptr_Contains() | |
static bool from_sptr_Contains( const SharedPtr_t & lhs, const SharedPtr_t & rhs ) | |
{ | |
if( !lhs ) return !rhs; // If lhs is null, it only contains rhs if rhs is null | |
if( !rhs ) return true; // Otherwise, if rhs is null we technically contain it regardless | |
// Neither side is null, so check for containment of elements | |
return lhs->Contains( *rhs ); | |
} | |
public: | |
// ==================================================================== Public Static API - Utility | |
// Public Static API - Utility | |
// -------------------------------------------------------------------- Empty_Gestalt_Tag_Set() | |
static Tag_Set_t Empty_Gestalt_Tag_Set() | |
{ | |
return Tag_Set_t( &Tag_t::from_sptr_is_Less_Than ); | |
} | |
// -------------------------------------------------------------------- Clone_Mutated( from sptr, mutate one ) | |
static SharedPtr_t Clone_Mutated( const SharedPtr_t & source, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
const Foundry_t foundry ) | |
{ | |
return Core::Allocate_Shared< Gestalt >( source, | |
rng, | |
allowed_tag_type_ids, | |
foundry ); | |
} | |
// -------------------------------------------------------------------- Clone_Mutated( from sptr, mutate all ) | |
static SharedPtr_t Clone_Mutated( const SharedPtr_t & source, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
float mutation_rate, | |
const Foundry_t & foundry ) | |
{ | |
return Core::Allocate_Shared< Gestalt >( source, | |
rng, | |
allowed_tag_type_ids, | |
mutation_rate, | |
foundry ); | |
} | |
private: | |
// ==================================================================== Private Static API - Tags | |
// Private Static API - Tags | |
// -------------------------------------------------------------------- Random_Mutation_Action() | |
static Mutation_Action Random_Mutation_Action( Random::Generator & rng ) | |
{ | |
return Mutation_Action | |
( | |
rng.Number( Enum::as_Index( Mutation_Action::Drop ), | |
Enum::as_Index( Mutation_Action::Clone_Mutated ) ) | |
); | |
} | |
// -------------------------------------------------------------------- Tags_Mutated_from( gestalt tag set / mutate one ) | |
static Tag_Set_t Tags_Mutated_from( const Tag_Set_t & source_gestalt_tags, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
const Foundry_t & foundry ) | |
{ | |
Tag_Set_t mutated_tags = Empty_Gestalt_Tag_Set(); | |
// Handle empty source | |
if ( source_gestalt_tags.empty() ) | |
{ | |
mutated_tags.insert( | |
Tag_t::Get_Random( rng, | |
allowed_tag_type_ids, | |
foundry ) ); | |
return mutated_tags; | |
} | |
// Otherwise, perform a single mutation within the tags, starting | |
// by selecting which element is getting mutated | |
auto selected_tag_iter = | |
std::next( source_gestalt_tags.cbegin(), | |
rng.Size_T(0, source_gestalt_tags.size() - 1) ); | |
auto after_selected_tag_iter = std::next( selected_tag_iter ); | |
// Copy over any tags that occur before and after the selected tag | |
mutated_tags.insert( source_gestalt_tags.cbegin(), selected_tag_iter ); | |
mutated_tags.insert( after_selected_tag_iter, source_gestalt_tags.cend() ); | |
// Perform mutation action | |
switch ( Random_Mutation_Action( rng ) ) | |
{ | |
case Mutation_Action::Drop : break; | |
case Mutation_Action::New_Random : mutated_tags.insert( Tag_t::Get_Random( rng, allowed_tag_type_ids, foundry ) ); break; | |
case Mutation_Action::Clone_Mutated: mutated_tags.insert( (*selected_tag_iter)->Clone_Mutated(rng, foundry) ); break; | |
} | |
return mutated_tags; | |
} | |
// -------------------------------------------------------------------- Tags_Mutated_from( gestalt tag set / mutate all ) | |
static Tag_Set_t Tags_Mutated_from( const Tag_Set_t & source_gestalt_tags, | |
Random::Generator & rng, | |
Tag_Allowed_Type_IDs_t allowed_tag_type_ids, | |
float mutation_rate, | |
const Foundry_t & foundry ) | |
{ | |
Tag_Set_t mutated_gestalt_tags = Empty_Gestalt_Tag_Set(); | |
// Handle empty source | |
if( source_gestalt_tags.empty() | |
&& rng.Chance( mutation_rate ) ) | |
{ | |
mutated_gestalt_tags.insert( | |
Tag_t::Get_Random( rng, | |
allowed_tag_type_ids, | |
foundry ) ); | |
return mutated_gestalt_tags; | |
} | |
// Otherwise, copy / mutate tags | |
for ( auto const & gestalt_tag_sptr: source_gestalt_tags ) | |
{ | |
if( rng.Chance( mutation_rate ) ) | |
{ | |
switch ( Random_Mutation_Action( rng ) ) | |
{ | |
case Mutation_Action::Drop: | |
continue; | |
case Mutation_Action::New_Random: | |
mutated_gestalt_tags.insert( | |
Tag_t::Get_Random( rng, | |
allowed_tag_type_ids, | |
foundry)); | |
break; | |
case Mutation_Action::Clone_Mutated: | |
mutated_gestalt_tags.insert( | |
gestalt_tag_sptr->Clone_Mutated( rng, | |
mutation_rate, | |
foundry) ); | |
break; | |
} | |
} | |
else // No mutation | |
mutated_gestalt_tags.insert( gestalt_tag_sptr ); | |
} | |
return mutated_gestalt_tags; | |
} | |
}; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment