Skip to content

Instantly share code, notes, and snippets.

@jrandom
Last active January 15, 2017 17:31
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 jrandom/c9eae51f58876c897e1b929390f1c900 to your computer and use it in GitHub Desktop.
Save jrandom/c9eae51f58876c897e1b929390f1c900 to your computer and use it in GitHub Desktop.
Argh...
//
// 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