Program Listing for File SpawnSendingService.hpp

Return to documentation for file (include/dish2/services/SpawnSendingService.hpp)

#pragma once
#ifndef DISH2_SERVICES_SPAWNSENDINGSERVICE_HPP_INCLUDE
#define DISH2_SERVICES_SPAWNSENDINGSERVICE_HPP_INCLUDE

#include <algorithm>
#include <numeric>
#include <set>
#include <utility>

#include "../../../third-party/conduit/include/uitsl/algorithm/copy_if.hpp"
#include "../../../third-party/conduit/include/uitsl/math/shift_mod.hpp"
#include "../../../third-party/Empirical/include/emp/base/vector.hpp"
#include "../../../third-party/signalgp-lite/include/sgpl/utility/ThreadLocalRandom.hpp"

#include "../cell/cardinal_iterators/GenomeNodeOutputWrapper.hpp"
#include "../cell/cardinal_iterators/ResourceStockpileWrapper.hpp"
#include "../cell/cardinal_iterators/SpawnArrestWrapper.hpp"
#include "../cell/cardinal_iterators/SpawnCountWrapper.hpp"
#include "../cell/cardinal_iterators/SpawnRequestWrapper.hpp"
#include "../config/cfg.hpp"
#include "../debug/LogScope.hpp"
#include "../runninglog/SpawnEvent.hpp"

namespace dish2 {

struct SpawnSendingService {

  static bool ShouldRun( const size_t update, const bool alive ) {
    const size_t freq = dish2::cfg.SPAWN_SENDING_SERVICE_FREQUENCY();
    return
      alive
      && freq > 0
      && uitsl::shift_mod( update, freq ) == 0;
  }

  template<typename Cell>
  static void DoService( Cell& cell ) {

    const dish2::LogScope guard{ "spawn sending service", "TODO", 3 };

    using spec_t = typename Cell::spec_t;

    // check resource stockpile consistency
    emp_assert((
      std::set< typename dish2::ResourceStockpileWrapper<spec_t>::value_type >(
        cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>(),
        cell.template end<dish2::ResourceStockpileWrapper<spec_t>>()
      ).size() == 1
    ));

    // check spawn count consistency
    emp_assert((
      std::set< typename dish2::SpawnCountWrapper<spec_t>::value_type >(
        cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
        cell.template end<dish2::SpawnCountWrapper<spec_t>>()
      ).size() == 1
    ));

    float available_resource{
      *cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>()
    };

    if ( available_resource < 1 ) return;

    thread_local emp::vector< std::reference_wrapper<
      typename dish2::GenomeNodeOutputWrapper<spec_t>::value_type
    > > requested_outputs;
    requested_outputs.clear();

    uitsl::copy_if(
      cell.template begin<dish2::GenomeNodeOutputWrapper<spec_t>>(),
      cell.template end<dish2::GenomeNodeOutputWrapper<spec_t>>(),
      cell.template begin<dish2::SpawnArrestWrapper<spec_t>>(),
      cell.template begin<dish2::SpawnRequestWrapper<spec_t>>(),
      std::back_inserter( requested_outputs ),
      []( const auto arrest, const auto request ){ return request && !arrest; }
    );

    while ( available_resource >= 1 && requested_outputs.size() ) {

      // pick a random request
      const size_t idx = sgpl::tlrand.Get().GetUInt(
        requested_outputs.size()
      );

      // do the spawn send
      requested_outputs[ idx ].get().Put( *cell.genome );
      available_resource -= 1;

      // record spawn send in spawn count
      std::transform(
        cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
        cell.template end<dish2::SpawnCountWrapper<spec_t>>(),
        cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
        []( const auto& cur ){ emp_assert( cur + 1 > cur ); return cur + 1; }
      );

      // record spawn event in running log
      const auto& peripheral = cell.cardinals[idx].peripheral;
      const size_t replev = peripheral.readable_state.template Get<
        dish2::RepLevRequest<spec_t>
      >().GetRepLev();
      const size_t kin_id_commonality_parent_daughter = spec_t::NLEV - replev;
      const size_t kin_id_commonality_parent_eliminated =
        peripheral.readable_state.template Get<
          dish2::KinGroupIDView<spec_t>
        >().CountCommonality(
          std::as_const( peripheral.state_node_input ).Get().template Get<
            dish2::KinGroupIDView<spec_t>
          >()
        );
      const size_t kin_id_commonality_daughter_eliminated = std::min(
        kin_id_commonality_parent_daughter,
        kin_id_commonality_parent_eliminated
      );

      cell.running_logs.Record( dish2::SpawnEvent<spec_t>{
        kin_id_commonality_daughter_eliminated,
        kin_id_commonality_parent_daughter,
        kin_id_commonality_parent_eliminated,
        cell.cardinals.size(),
        cell.GetPeripherality(),
        replev
      } );

      // swap 'n pop request
      requested_outputs[ idx ] = requested_outputs.back();
      requested_outputs.pop_back();

    }

    std::fill(
      cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>(),
      cell.template end<dish2::ResourceStockpileWrapper<spec_t>>(),
      available_resource
    );

    // check resource stockpile consistency
    emp_assert((
      std::set< typename dish2::ResourceStockpileWrapper<spec_t>::value_type >(
        cell.template begin<dish2::ResourceStockpileWrapper<spec_t>>(),
        cell.template end<dish2::ResourceStockpileWrapper<spec_t>>()
      ).size() == 1
    ));

    // check spawn count consistency
    emp_assert((
      std::set< typename dish2::SpawnCountWrapper<spec_t>::value_type >(
        cell.template begin<dish2::SpawnCountWrapper<spec_t>>(),
        cell.template end<dish2::SpawnCountWrapper<spec_t>>()
      ).size() == 1
    ));

  }

};

} // namespace dish2

#endif // #ifndef DISH2_SERVICES_SPAWNSENDINGSERVICE_HPP_INCLUDE