Program Listing for File Host.h

Return to documentation for file (source/default_mode/Host.h)

#ifndef HOST_H
#define HOST_H

#include "../../Empirical/include/emp/math/Random.hpp"
#include "../../Empirical/include/emp/tools/string_utils.hpp"
#include <iomanip> // setprecision
#include <sstream> // stringstream
#include <string>
#include "../Organism.h"
#include "SymWorld.h"


class Host: public Organism {


protected:

  double interaction_val = 0;

  int age = 0;

  emp::vector<emp::Ptr<Organism>> syms = {};

  emp::vector<emp::Ptr<Organism>> repro_syms = {};

  double points = 0;

  double res_in_process = 0;

  emp::Ptr<emp::Random> random = NULL;

  emp::Ptr<SymWorld> my_world = NULL;

  emp::Ptr<SymConfigBase> my_config = NULL;

  bool dead = false;

public:

  Host(emp::Ptr<emp::Random> _random, emp::Ptr<SymWorld> _world, emp::Ptr<SymConfigBase> _config,
  double _intval =0.0, emp::vector<emp::Ptr<Organism>> _syms = {},
  emp::vector<emp::Ptr<Organism>> _repro_syms = {},
  double _points = 0.0) : interaction_val(_intval), syms(_syms), repro_syms(_repro_syms), points(_points), random(_random), my_world(_world), my_config(_config) {
    if (_intval == -2) {
      interaction_val = random->GetDouble(-1, 1);
    }
    if (interaction_val > 1 || interaction_val < -1) {
       throw "Invalid interaction value. Must be between -1 and 1";  // Exception for invalid interaction value
     };
   }

  ~Host(){
    for(size_t i=0; i<syms.size(); i++){
      syms[i].Delete();
    }
    for(size_t j=0; j<repro_syms.size(); j++){
      repro_syms[j].Delete();
    }
  }


  Host(const Host &) = default;


  Host(Host &&) = default;


  Host() = default;


  Host & operator=(const Host &) = default;


  Host & operator=(Host &&) = default;


  bool operator==(const Host &other) const { return (this == &other);}


  bool operator!=(const Host &other) const {return !(*this == other);}


  std::string const GetName() {
    return  "Host";
  }

  double GetIntVal() const { return interaction_val;}


  emp::vector<emp::Ptr<Organism>>& GetSymbionts() {return syms;}


  emp::vector<emp::Ptr<Organism>>& GetReproSymbionts() {return repro_syms;}


  double GetPoints() { return points;}


  double GetResInProcess() { return res_in_process;}

 bool IsHost() { return true; }


  void SetIntVal(double _in) {
    if ( _in > 1 || _in < -1) {
       throw "Invalid interaction value. Must be between -1 and 1";  // Exception for invalid interaction value
     }
     else {
       interaction_val = _in;
     }
  }


  void SetSymbionts(emp::vector<emp::Ptr<Organism>> _in) {
    ClearSyms();
    for(size_t i = 0; i < _in.size(); i++){
      AddSymbiont(_in[i]);
    }
  }


  void SetPoints(double _in) {points = _in;}


  void ClearSyms() {syms.resize(0);}


  void ClearReproSyms() {repro_syms.resize(0);}


  void SetDead() { dead = true;}


  void SetResInProcess(double _in) { res_in_process = _in;}

  bool GetDead() {return dead;}

  int GetAge() {return age;}

  void SetAge(int _in) {age = _in;}

  void GrowOlder(){
    age = age + 1;
    if(age > my_config->HOST_AGE_MAX() && my_config->HOST_AGE_MAX() > 0){
      SetDead();
    }
  }

  double StealResources(double _intval){
    double hostIntVal = GetIntVal();
    double res_in_process = GetResInProcess();
    //calculate how many resources another organism can steal from this host
    if (hostIntVal>0){ //cooperative hosts shouldn't be over punished by StealResources
      hostIntVal = 0;
    }
    if (_intval < hostIntVal){
      //organism trying to steal can overcome host's defense
      double stolen = (hostIntVal - _intval) * res_in_process;
      double remainingResources = res_in_process - stolen;
      SetResInProcess(remainingResources);
      return stolen;
    } else {
      //defense cannot be overcome, no resources are stolen
      return 0;
    }
  }


  void AddPoints(double _in) {points += _in;}


  int AddSymbiont(emp::Ptr<Organism> _in) {
    if((int)syms.size() < my_config->SYM_LIMIT() && SymAllowedIn()){
      syms.push_back(_in);
      _in->SetHost(this);
      _in->UponInjection();
      return syms.size();
    } else {
      _in.Delete();
      return 0;
    }
  }


  bool SymAllowedIn(){
    bool do_phage_exclusion = my_config->PHAGE_EXCLUDE();
    if(!do_phage_exclusion){
     return true;
    }
    else{
     int num_syms = syms.size();
     //essentially imitaties a 1/ 2^n chance, with n = number of symbionts
     int enter_chance = random->GetUInt((int) pow(2.0, num_syms));
     if(enter_chance == 0) { return true; }
     return false;
    }
  }


  void AddReproSym(emp::Ptr<Organism> _in) {repro_syms.push_back(_in);}


  bool HasSym() {
    return syms.size() != 0;
  }

  emp::Ptr<Organism> MakeNew(){
    emp::Ptr<Host> new_host = emp::NewPtr<Host>(random, my_world, my_config, GetIntVal());
    return new_host;
  }

  emp::Ptr<Organism> Reproduce(){
    emp::Ptr<Organism> host_baby = MakeNew();
    host_baby->Mutate();
    SetPoints(0);
    return host_baby;
  }

  void Mutate(){
    double mutation_size = my_config->HOST_MUTATION_SIZE();
    if (mutation_size == -1) mutation_size = my_config->MUTATION_SIZE();
    double mutation_rate = my_config->HOST_MUTATION_RATE();
    if (mutation_rate == -1) mutation_rate = my_config->MUTATION_RATE();

    if(random->GetDouble(0.0, 1.0) <= mutation_rate){
      interaction_val += random->GetRandNormal(0.0, mutation_size);
      if(interaction_val < -1) interaction_val = -1;
      else if (interaction_val > 1) interaction_val = 1;
    }
  }


  void DistribResources(double resources) {
    double hostIntVal = interaction_val; //using private variable because we can
    //do ectosymbiosis if the config setting is on, there is a parallel sym

    //In the event that the host has no symbionts, the host gets all resources not allocated to defense or
    // given to absent partner.
    if(syms.empty()) {
      if(hostIntVal >= 0){
        double spent = resources * hostIntVal;
        this->AddPoints(resources - spent);
      }
      else {
        double hostDefense = -1.0 * hostIntVal * resources;
        this->AddPoints(resources - hostDefense);
      }
      return; //This concludes resource distribution for a host without symbionts
    }

    size_t num_sym = syms.size();
    double sym_piece = (double) resources / num_sym;

    for(size_t i=0; i < syms.size(); i++){
      DistribResToSym(syms[i], sym_piece);
    }
  } //end DistribResources

  double HandleEctosymbiosis(double resources, size_t location){
    double leftover_resources = resources;
    if(GetDoEctosymbiosis(location)){
      double sym_piece = leftover_resources / (syms.size() + 1); //if there are no endo syms, the ecto sym will handle all the resources
      DistribResToSym(my_world->GetSymAt(location), sym_piece);
      leftover_resources = leftover_resources - sym_piece; //leave the leftover resources to be split by other syms
    }
    return leftover_resources;
  }

  bool GetDoEctosymbiosis(size_t location){
    //a host is immune to ectosymbiosis if immunity is on and it has a sym.
    if (!my_config->ECTOSYMBIOSIS()) return false; //if the config setting is off, we immediately know that ectosymbiosis won't happen
    else{
      bool is_immune = my_config->ECTOSYMBIOTIC_IMMUNITY() && HasSym();
      bool valid_sym = my_world->GetSymAt(location) != nullptr && !my_world->GetSymAt(location)->GetDead();
      return (valid_sym == true) && (is_immune == false);
    }
  }

  void DistribResToSym(emp::Ptr<Organism> sym, double sym_piece){
    double hostIntVal = interaction_val;
    double hostDonation = 0;
    if(hostIntVal < 0){
      double hostDefense = hostIntVal * sym_piece * -1.0;
      hostDonation = 0;
      SetResInProcess(sym_piece - hostDefense);
    }
    else if(hostIntVal >= 0){
      hostDonation = hostIntVal * sym_piece;
      SetResInProcess(sym_piece - hostDonation);
    }
    double sym_return = sym->ProcessResources(hostDonation, this);
    this->AddPoints(sym_return + GetResInProcess());
    SetResInProcess(0);
  }


  void Process(emp::WorldPosition pos) {
    size_t location = pos.GetIndex();
    //Currently just wrapping to use the existing function
    double desired_resources = my_config->RES_DISTRIBUTE();
    double world_resources = my_world->PullResources(desired_resources); //recieve resources from the world
    double resources = HandleEctosymbiosis(world_resources, location);
    if(resources > 0) DistribResources(resources); //if there are enough resources left, distribute them.

    // Check reproduction
    if (GetPoints() >= my_config->HOST_REPRO_RES() && repro_syms.size() == 0) {  // if host has more points than required for repro
        // will replicate & mutate a random offset from parent values
        // while resetting resource points for host and symbiont to zero
       emp::Ptr<Organism> host_baby = Reproduce();

        //Now check if symbionts get to vertically transmit
        for(size_t j = 0; j< (GetSymbionts()).size(); j++){
          emp::Ptr<Organism> parent = GetSymbionts()[j];
          parent->VerticalTransmission(host_baby);
        }
        my_world->DoBirth(host_baby, location); //Automatically deals with grid
      }
    if (GetDead()){
        return; //If host is dead, return
      }
    if (HasSym()) { //let each sym do whatever they need to do
        emp::vector<emp::Ptr<Organism>>& syms = GetSymbionts();
        for(size_t j = 0; j < syms.size(); j++){
          emp::Ptr<Organism> curSym = syms[j];
          if (GetDead()){
            return; //If previous symbiont killed host, we're done
          }
          //sym position should have host index as id and
          //position in syms list + 1 as index (0 as fls index)
          emp::WorldPosition sym_pos = emp::WorldPosition(j+1, location);
          if(!curSym->GetDead()){
            curSym->Process(sym_pos);
          }
          if(curSym->GetDead()){
            syms.erase(syms.begin() + j); //if the symbiont dies during their process, remove from syms list
            curSym.Delete();
          }
        } //for each sym in syms
      } //if org has syms
    GrowOlder();
  }
};//Host
#endif