/
moshroom_edu
/
evolution
Обзор
Документация
Войти
/
moshroom_edu
/
evolution
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
src/bact_engine/population_controller.cpp
899 строк
27 KB
mushroom
bkp
26 июн 2026, 08:34
26 июн 2026, 08:34
84517f4
Код
Авторство
О чём код?
#include "bact_engine/population_controller.h" #include "bact_engine/engine.h" #include "defines.h" #include <algorithm> #include <fstream> #include <math.h> namespace b_engine { void popul_controller::save_command( const std::string& comm ) { std::vector< std::string > out; slice( out, comm, ' ' ); if( out.size() != 2 ) { logger::error( "Wrong comand syntax!" ); return; } pop_save( out[ 1 ] ); } void popul_controller::load_command( const std::string& comm ) { std::vector< std::string > out; slice( out, comm, ' ' ); if( out.size() != 2 ) { logger::error( "Wrong comand syntax!" ); return; } pop_load( out[ 1 ] ); } void popul_controller::make_commands() { m_commands = std::make_shared< multithreading::queue< comm_message::ptr > >(); terminal::add_command( command( "save_p", static_cast< int >( commands::save_population ), "save_p <filename> - save population to file", m_commands ) ); terminal::add_command( command( "load_p", static_cast< int >( commands::load_population ), "load_p <filename> - load population from file", m_commands ) ); } void popul_controller::remove_commands() { terminal::remove_command( "save_p" ); terminal::remove_command( "load_p" ); } void popul_controller::process_commands() { comm_message::ptr command; if( !m_commands->try_pop( command ) ) { return; } switch( static_cast< commands >( command->key ) ) { case commands::load_population: { load_command( command->command_line ); return; } case commands::save_population: { save_command( command->command_line ); return; } } } ull popul_controller::current_life_time() { ull ticks = engine::instance().ticks_couner(); ull res = ticks - m_last_pop_tick_count; return res; } bool popul_controller::find_palce( const n_bact_ptr& _bact, const std::vector< object_ptr >& objs ) { int trys_count = 10; int result = true; while( trys_count ) { result = true; for( const auto& o : objs ) { if( !o ) { continue; } if( _bact.get() != o.get() && _bact->check_collision( o ) ) { result = false; break; } } if( result ) { return true; } _bact->set_pos( rand() % static_cast< int >( g_world_width ), rand() % static_cast< int >( g_world_height ) ); trys_count--; } return result; } popul_param popul_controller::brain_param() const { return m_brain_param; } void popul_controller::set_brain_param( const popul_param& brain_param ) { m_brain_param = brain_param; } void popul_controller::serialize_population( std::string& population ) { m_brain_param.serialize( population ); population += "\n"; population += std::to_string( m_last_life_time ) + '\n'; population += std::to_string( m_last_pop_tick_count ) + '\n'; population += std::to_string( m_generation_counter ) + '\n'; for( auto& an : m_valhalla ) { an.creature->brain()->serialize( population ); population += "/"; } } void popul_controller::deserialize_population( const std::string& population ) { //Run only in the engine's thread std::vector< std::string > out; slice( out, population, '\n' ); m_brain_param.deserialize( out[ 0 ] ); clear(); m_last_life_time = std::stoull( out[ 1 ] ); //m_last_pop_tick_count = std::stoull(out[2]); m_generation_counter = std::stoull( out[ 3 ] ); std::vector< std::string > anns; slice( anns, out[ 4 ], '/' ); anns.pop_back(); for( const auto& ann : anns ) { auto bact = make_bact(); bact->brain()->deserialize( ann ); } assert( m_alive_bacts.size() == m_brain_param.population_size ); } void popul_controller::pop_save( const std::string& file_name ) { std::fstream f( file_name, std::ios_base::out ); if( !f.is_open() ) { logger::error( "Can't open file for write: " + file_name ); return; } std::string population; serialize_population( population ); f << population; f.close(); logger::info( "Population Saved" ); } void popul_controller::pop_load( const std::string& file_name ) { std::fstream f( file_name, std::ios_base::in ); if( !f.is_open() ) { logger::error( "Can't open file: " + file_name ); return; } std::string population( ( std::istreambuf_iterator< char >( f ) ), std::istreambuf_iterator< char >() ); deserialize_population( population ); logger::info( std::string( "Population loaded!\n" ) + "Generation nuber:" + std::to_string( m_generation_counter ) + "\nLast live time: " + std::to_string( m_last_life_time ) ); } std::shared_ptr< n_engine::ann > popul_controller::create_brain() { return std::make_shared< n_engine::ann >( m_brain_param.brain_struct, m_brain_param.reccurent ); } void popul_controller::send_to_valhala( const n_bact_ptr& bact ) { stat_frame sf( { { "death_reason", static_cast< double >( bact->get_death_reason() ) }, { "score", bact->score() } } ); m_stat_holder.push( sf ); leader lead; lead.creature = bact; lead.score = bact->score(); m_valhalla.push_back( lead ); if( m_valhalla.size() > m_brain_param.population_size ) { sort_leaders(); m_valhalla.pop_back(); } } void popul_controller::sort_leaders() { std::sort( m_valhalla.begin(), m_valhalla.end(), []( const leader& a, const leader& b ) { return a.score > b.score; } ); } void popul_controller::clear() { m_alive_bacts.clear(); m_valhalla.clear(); } void popul_controller::auto_save() { if( m_generation_counter % 250 ) return; std::string fname = "../autosaves/population_" + std::to_string( m_generation_counter ) + ".pop"; logger::info( "Auto save: " + fname ); pop_save( fname ); } ull popul_controller::get_life_time() { ull ticks = engine::instance().ticks_couner(); ull res = ticks - m_last_pop_tick_count; m_last_pop_tick_count = ticks; return res; } void popul_controller::correct_mutauion_delta_by_life_time( size_t mean_life_time ) { if( ( mean_life_time > m_last_life_time ) && ( mean_life_time > static_cast< ull >( m_brain_param.next_target ) ) ) { logger::warn( "Mean life time grows over target " + std::to_string( m_brain_param.next_target ) + " frames! Correcting mutation delta!" ); m_brain_param.new_gen_mutation_weights_delta *= 0.9; m_brain_param.next_target = mean_life_time; m_last_life_time = mean_life_time; } } void popul_controller::correct_mutation_delta_by_mean_score( lli mean_score ) { if( mean_score > m_brain_param.next_target ) { double corrector = 0.85; /*(0.0 + m_brain_param.next_target) / mean_score*/ ; logger::warn( "Mean score in population grows over target " + std::to_string( m_brain_param.next_target ) + " points! Correcting mutation delta " + std::to_string( corrector ) ); m_brain_param.new_gen_mutation_weights_delta *= corrector; m_brain_param.next_target = mean_score /*m_brain_param.target_multiplier*/; } } lli popul_controller::get_mean_score() { lli score = 0; for( const auto& l : m_valhalla ) { score += l.score; } double temp = std::round( static_cast< double >( score ) / m_valhalla.size() ); score = static_cast< lli >( temp ); return score; } bool popul_controller::respawn_bact( const n_bact_ptr& bact ) { bact->set_mass( g_bact_born_mass ); bact->set_energy( g_bact_born_energy ); bact->set_pos( rand() % static_cast< int >( g_world_width ), rand() % static_cast< int >( g_world_height ) ); if( !find_palce( bact, engine::instance().objects() ) ) { logger::warn( "Can't find place for neuro_bact" ); return false; } bact->respawn(); bact->set_speed( 0.f, 0.f ); bact->set_score( 0 ); bact->set_population_index( m_alive_bacts.size() ); m_alive_bacts.push_back( bact ); engine::instance().add_object( bact ); return true; } void popul_controller::make_copy_and_mutation( const brain_ptr& parent, const brain_ptr& child, std::size_t mutation_percent, double mutation_delta ) { child->copy( *parent ); child->mutate_weights( mutation_percent, mutation_delta ); } void popul_controller::make_fuck_and_mutation( const brain_ptr& parent_one, const brain_ptr& parent_two, const brain_ptr& child, std::size_t mutation_percent, double mutation_delta ) { child->brain_fuck( *parent_one, *parent_two ); child->mutate_weights( mutation_percent, mutation_delta ); } void popul_controller::to_helheim( size_t mean_score ) { m_helheim.push_back( std::make_shared< population >() ); auto& pop = m_helheim.back(); pop->m_serial.reserve( 10e6 ); pop->m_score = mean_score; serialize_population( pop->m_serial ); } void popul_controller::from_helheim() { logger::warn( "\033[38;5;95m___EXTRACTION FROM HELHEIM___\033[0m" ); std::sort( m_helheim.begin(), m_helheim.end(), []( const std::shared_ptr< population >& a, const std::shared_ptr< population >& b ) { return a->m_score > b->m_score; } ); size_t temp = m_generation_counter; std::string params; m_brain_param.serialize( params ); deserialize_population( m_helheim.front()->m_serial ); logger::warn( "\033[38;5;95m___EXTRACTION FROM HELHEIM DONE___\033[0m" ); logger::warn( "___FROM:" + to_std_str( m_generation_counter ) + " BEST: " + to_std_str( m_helheim.front()->m_score ) + " MIN: " + to_std_str( m_helheim.back()->m_score ) + "___\n" ); m_generation_counter = temp; m_brain_param.deserialize( params ); m_helheim.clear(); } bool popul_controller::reborn_warrior( const n_bact_ptr& warrior ) { assert( m_valhalla.size() ); auto bact = m_valhalla.back().creature; if( !respawn_bact( bact ) ) { logger::error( "Can't create generation!" ); return false; } auto brains = bact->brain(); make_copy_and_mutation( warrior->brain(), brains, m_brain_param.new_gen_mutation_weights_percent, m_brain_param.new_gen_mutation_weights_delta ); auto color = warrior->get_color(); // mutate_color(color, m_brain_param.new_gen_mutation_weights_delta); bact->set_color( color ); m_valhalla.pop_back(); return true; } bool popul_controller::warriors_love( const n_bact_ptr& warrior1, const n_bact_ptr& warrior2 ) { assert( m_valhalla.size() ); auto bact = m_valhalla.back().creature; if( !respawn_bact( bact ) ) { logger::error( "Can't create generation!" ); return false; } auto brains = bact->brain(); make_fuck_and_mutation( warrior1->brain(), warrior2->brain(), brains, m_brain_param.new_gen_mutation_weights_percent, m_brain_param.new_gen_mutation_weights_delta ); m_valhalla.pop_back(); return true; } bool popul_controller::make_child( const n_bact_ptr& mommy ) { if( m_alive_bacts.size() == m_brain_param.population_size ) return false; if( mommy->energy() < g_bact_division_energy ) { mommy->destroy(); //mommy->delta_score( mommy->mass() - temp); return false; } //sort_leaders(); auto bact = make_bact(); //m_valhalla.back().creature; // if(!respawn_bact(bact)) { // logger::error("Can't respawn bact!"); // return false; // } auto brains = bact->brain(); make_copy_and_mutation( mommy->brain(), brains, m_brain_param.new_gen_mutation_weights_percent, m_brain_param.new_gen_mutation_weights_delta / 2. ); // m_valhalla.pop_back(); // float x = mommy->x() + ((mommy->speed_x() > 0 ? -1 : 1) * // (mommy->radius() + bact->radius() + 10)); // float y = mommy->y() + ((mommy->speed_y() > 0 ? -1 : 1) * // (mommy->radius() + bact->radius() + 10)); // bact->set_pos(x,y); mommy->delta_energy( -g_bact_division_energy ); mommy->delta_score( +int( g_bact_division_energy ) ); return true; } void popul_controller::make_popualtion() { ull temp = m_brain_param.population_size; ull per_leader = m_brain_param.population_size / 5 - 1; ull tail = m_brain_param.population_size % 5; temp -= tail; auto leader_it = m_valhalla.begin(); while( temp ) { for( ull i = 0; i < per_leader; ++i ) { reborn_warrior( leader_it->creature ); --temp; } respawn_bact( leader_it->creature ); --temp; ++leader_it; } logger::info( "tail:" + std::to_string( tail ) ); leader_it = m_valhalla.begin(); auto fin = leader_it + 5; while( tail ) { reborn_warrior( leader_it->creature ); ++leader_it; if( leader_it == fin ) { leader_it = m_valhalla.begin(); } --tail; } } void popul_controller::make_crossingover() { ull temp = m_brain_param.population_size; ull per_leader = m_brain_param.population_size / 5; ull tail = m_brain_param.population_size % 5; temp -= tail; auto leader_it = m_valhalla.begin(); while( temp ) { for( ull i = 0; i < per_leader; ++i ) { n_bact_ptr warrior2 = m_valhalla[ i ].creature; if( warrior2.get() == leader_it->creature.get() ) { respawn_bact( leader_it->creature ); } else { if( !( i % 2 ) ) //Chance for losers { auto selector = [ this ]() { assert( m_valhalla.size() ); auto index = m_valhalla.size() > 1 ? rand() % ( m_valhalla.size() - 1 ) : 0; return m_valhalla[ index ].creature; }; warrior2 = selector(); while( warrior2.get() == leader_it->creature.get() ) { warrior2 = selector(); } } warriors_love( leader_it->creature, warrior2 ); } --temp; } ++leader_it; } logger::info( "tail:" + std::to_string( tail ) ); leader_it = m_valhalla.begin(); auto fin = leader_it + 5; while( tail ) { reborn_warrior( leader_it->creature ); ++leader_it; if( leader_it == fin ) { leader_it = m_valhalla.begin(); } --tail; } } bool popul_controller::generate_next_population() { if( m_generation_counter % g_helheim_seleection_freq == 0 ) { if( m_generation_counter == g_helheim_seleection_freq ) { logger::warn( "Selecting grand population" ); } from_helheim(); return true; } if( m_generation_counter < g_helheim_seleection_freq ) { logger::warn( "Generate random population" ); clear(); generate(); return true; } bool radiation = false; double temp_delta = m_brain_param.new_gen_mutation_weights_delta; size_t is_radiation = m_generation_counter % g_radiation_freq; if( is_radiation == 0 ) { m_brain_param.new_gen_mutation_weights_delta *= g_radiation_value; logger::warn( "RADIATION!!! Mutation delta: " + std::to_string( m_brain_param.new_gen_mutation_weights_delta ) ); radiation = true; } else { logger::warn( "Radiation come in " + std::to_string( g_radiation_freq - is_radiation ) ); } make_crossingover(); m_valhalla.clear(); logger::info( "Generation " + std::to_string( m_generation_counter ) + " created Size: " + std::to_string( m_alive_bacts.size() ) + "(\033[33mcrossingover\033[0m)" ); if( radiation ) { m_brain_param.new_gen_mutation_weights_delta = temp_delta; radiation = false; } return true; } popul_controller::popul_controller() : object( object_type::object ) , m_stat_holder( "../statistics/statistics_at_" + get_time() + ".stat" ) { make_commands(); } n_bact_ptr popul_controller::make_bact() { auto bact = std::make_shared< neuro_bact >(); bact->set_pos( rand() % static_cast< int >( g_world_width ), rand() % static_cast< int >( g_world_height ) ); if( !find_palce( bact, engine::instance().objects() ) ) { logger::warn( "Can't find place for neuro_bact" ); return nullptr; } auto brains = create_brain(); bact->set_brain( brains ); bact->set_population_index( m_alive_bacts.size() ); m_alive_bacts.push_back( bact ); engine::instance().add_object( bact ); return bact; } void popul_controller::draw( sf::RenderWindow& window ) { sf::Text text( " T:" + std::to_string( current_life_time() ) + " G:" + std::to_string( m_generation_counter ) + " L:" + std::to_string( m_alive_bacts.size() ) + " V:" + std::to_string( m_valhalla.size() ), g_font, 12 ); text.setPosition( 0, 0 ); text.setFillColor( sf::Color( 255, 255, 255, 127 ) ); window.draw( text ); } popul_controller::popul_controller( const popul_param& ann_param ) : object( object_type::object ) , m_brain_param( ann_param ) , m_stat_holder( "../statistics/statistics_at_" + get_time() + ".stat" ) { make_commands(); } popul_controller::~popul_controller() {} void popul_controller::generate() { for( ull i = 0; i < m_brain_param.population_size; ++i ) { if( !make_bact() ) { logger::error( "Can't create zero generation! Can't create bactery" ); return; } } logger::info( "Info: Zero generation created!" ); } bool popul_controller::next_gen() { ull life_time = get_life_time(); lli mean_score = 0l; stat_frame stat; // ---------------------- // Calculating scores size_t mean_life_time = static_cast< size_t >( std::round( ( m_stat_holder.mean( "life_time" ) ) ) ); sort_leaders(); mean_score = get_mean_score(); auto_save(); //correct_mutauion_delta_by_life_time(mean_life_time); correct_mutation_delta_by_mean_score( mean_score ); //----------------------- // Write logs logger::info("Generation " + to_std_str(m_generation_counter) + " is death! " "Live time: " + std::to_string(life_time)); logger::info( "Mean of life_time is:" + std::to_string( mean_life_time ) ); logger::info( "Mean population score is: " + std::to_string( mean_score ) ); logger::info( "Target :" + std::to_string( m_brain_param.next_target ) ); logger::info( "Mutation delta is: " + std::to_string( m_brain_param.new_gen_mutation_weights_delta ) ); logger::info( "Mutation percentage is: " + std::to_string( m_brain_param.new_gen_mutation_weights_percent ) ); logger::info( "Hi score:" + std::to_string( m_valhalla.front().score ) + " low:" + std::to_string( m_valhalla.back().score ) + "\n" ); logger::info( "Create Generation " + std::to_string( ++m_generation_counter ) + " size: " + std::to_string( m_brain_param.population_size ) ); //----------------------- // Write statistics stat.add_param( "generation_num", m_generation_counter ); stat.add_param( "life_time", life_time ); stat.add_param( "mean_life_time", mean_life_time ); stat.add_param( "mean_pop_score", mean_score ); stat.add_param( "next_target", m_brain_param.next_target ); stat.add_param( "mutation_delta", m_brain_param.new_gen_mutation_weights_delta ); stat.add_param( "hi_score", m_valhalla.front().score ); stat.add_param( "low_score", m_valhalla.back().score ); //----------------------- // Serialize population to storage to_helheim( mean_score ); //----------------------- // Generating nex population bool res = generate_next_population(); m_stat_holder.push( stat ); return res; } void popul_controller::update() { process_commands(); //static int i = 0; n_bact_ptr bact; bool need_stat = false; for( auto b_it = m_alive_bacts.begin(); b_it != m_alive_bacts.end(); ) { bact = *b_it; if( !bact->is_destroy() ) { ++b_it; if( bact->wants_division() ) { make_child( bact ); } continue; } send_to_valhala( bact ); b_it = m_alive_bacts.erase( b_it ); need_stat = true; } if( need_stat ) { m_stat_holder.push( stat_frame( { { "alive_creatures", m_alive_bacts.size() } } ) ); // logger::info("Alive creatures: " // + std::to_string(m_alive_creatures)); } if( m_alive_bacts.size() == 0 ) { next_gen(); //i = 0; return; } //_________________TIME LIMT______________ // if(i > 10000) // { // ull size = m_alive_bacts.size(); // n_bact_ptr bact; // for (ull i = 0; i < size; ++i) { // n_bact_ptr & bact = m_alive_bacts[i]; // bact->destroy(); // } // i = 0; // return; // } // ++i; } void popul_param::log() { logger::info( "Population size:" + std::to_string( population_size ) ); logger::info( "Percentage of weights mutation:" + std::to_string( new_gen_mutation_weights_percent ) ); logger::info( "Delta of mutation:" + std::to_string( new_gen_mutation_weights_delta ) ); logger::info( "Next target:" + std::to_string( next_target ) ); logger::info( "Target multiplier:" + std::to_string( target_multiplier ) ); } void popul_param::serialize( std::string& state ) { state += std::to_string( brain_struct.size() ) + "|"; for( const auto& lp : brain_struct ) { state += std::to_string( lp.size ) + "~" + std::to_string( static_cast< int >( lp.type ) ) + "~"; } state += "|" + std::to_string( new_gen_mutation_weights_percent ) + "|"; state += std::to_string( new_gen_mutation_weights_delta ) + "|"; state += std::to_string( new_gen_mutation_struct_percent ) + "|"; state += std::to_string( new_gen_mutation_struct_opt_param ) + "|"; state += std::to_string( children_mutation_weights_percent ) + "|"; state += std::to_string( children_mutation_weights_delta ) + "|"; state += std::to_string( children_mutation_struct_percent ) + "|"; state += std::to_string( children_mutation_struct_opt_param ) + "|"; state += std::to_string( population_size ) + "|"; state += std::to_string( next_target ) + "|"; state += std::to_string( target_multiplier ) + "|"; } void popul_param::deserialize( const std::string& state ) { std::vector< std::string > out; slice( out, state, '|' ); out.pop_back(); ulli count = std::stoull( out.front() ); std::vector< std::string > b_str; slice( b_str, out[ 1 ], '~' ); brain_struct.clear(); auto it = b_str.begin(); for( ulli i = 0; i < count; ++i ) { ulli s = std::stoull( *( it++ ) ); activation_type t = static_cast< activation_type >( std::stoi( *( it++ ) ) ); n_engine::layer_param p( s, t ); brain_struct.push_back( p ); } new_gen_mutation_weights_percent = std::stoull( out[ 2 ] ); new_gen_mutation_weights_delta = std::stod( out[ 3 ] ); new_gen_mutation_struct_percent = std::stoull( out[ 4 ] ); new_gen_mutation_struct_opt_param = std::stod( out[ 5 ] ); children_mutation_weights_percent = std::stoull( out[ 6 ] ); children_mutation_weights_delta = std::stod( out[ 7 ] ); children_mutation_struct_percent = std::stoull( out[ 8 ] ); children_mutation_struct_opt_param = std::stod( out[ 9 ] ); population_size = std::stoull( out[ 10 ] ); if( out.size() != 13 ) { logger::warn( "Old format of params" ); return; } next_target = std::stoull( out[ 11 ] ); target_multiplier = std::stod( out[ 12 ] ); log(); } } //namespace b_engine