From ea9ebcc27d6d0a7490e04a0b3752277bfa182c30 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Mon, 18 May 2026 09:44:05 -0400 Subject: [PATCH 01/42] adding event handling files --- source/sgp_mode/ChangingEventsHandler.h | 72 +++++++++++++++++++++++++ source/sgp_mode/EventObjects.h | 60 +++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 source/sgp_mode/ChangingEventsHandler.h create mode 100644 source/sgp_mode/EventObjects.h diff --git a/source/sgp_mode/ChangingEventsHandler.h b/source/sgp_mode/ChangingEventsHandler.h new file mode 100644 index 00000000..6854982f --- /dev/null +++ b/source/sgp_mode/ChangingEventsHandler.h @@ -0,0 +1,72 @@ +#include "emp/base/vector.hpp" +#include "emp/bits/Bits.hpp" +#include "emp/tools/string_utils.hpp" +#include "emp/datastructs/set_utils.hpp" +#include "emp/math/math.hpp" +#include "../../json/json.hpp" + +#include +#include +#include +#include +#include +#include + +#include "EventObjects.h" + +namespace sgpmode::eventHandler { + +class ChangingEventsHandler { + public: + // library for manipulating json files + using json_t = nlohmann::json; + // event object class alias + using event_objects_t = EventObjects; + + + // type for event type functions (i.e., replace, add, mul) + using event_func_t = std::function; + + protected: + std::unordered_map all_events; // size_t is event id, event_func_t is the function that does each event + std::vector current_events; + + // from LogicTaskEnvironment.h get json field values + template + RET_TYPE GetVal( + json_t& json, + const std::string& field, + RET_TYPE default_val + ) { + return (json.contains(field)) ? + static_cast(json[field]) : + default_val; + } + + + // check if event type is valid + void IsValidEvent(){} + + // create instance of event object using EventObject class + void CreateEventObjects(){} + + // load in and process events.json file (includes creating events and checking if they are valid) + void LoadEvents(const std::string& event_filepath){} + + // return vector of all current events + void GetCurrentEvents(){} + + // return map of all events + void GetAllEvents(){} + + // delete finished events + void ClearEvents(){} + + // call event_func_t from current_events if possible based on update_indices + void ProcessEvent(){} +} + +} \ No newline at end of file diff --git a/source/sgp_mode/EventObjects.h b/source/sgp_mode/EventObjects.h new file mode 100644 index 00000000..eedc2d0c --- /dev/null +++ b/source/sgp_mode/EventObjects.h @@ -0,0 +1,60 @@ +#include "emp/base/vector.hpp" +#include "emp/bits/Bits.hpp" +#include "emp/tools/string_utils.hpp" +#include "emp/datastructs/set_utils.hpp" +#include "emp/math/math.hpp" + +#include +#include +#include +#include +#include +#include + +namespace sgpmode::eventHandler { + +class EventObjects { + protected: + + size_t event id; + std::string event_type; + std::string task_name; + double task_value; + int start_update; + int end_update; + int update_step; + std::string org_mode; + std::string reward_mode; + bool is_done = false; + + public: + + void SetIsDone(){ + if(is_done == false){ + is_done = true; + } + } + + std::string GetEventType(){ return event_type; } + + bool GetIsDone(){ return is_done; } + + double GetTaskValue(){ return task_value; } + + std::string GetTaskName(){ return task_name; } + + std::string GetOrgMode(){ return org_mode; } + + std::string GetRewardMode(){ return reward_mode; } + + size_t GetEventId() { return event_id; } + + int GetStartUpdate() { return start_update; } + + int GetEndUpdate() { return end_update; } + + int GetUpdateStep() { return update_step; } + +} + +} \ No newline at end of file From 77083bb527af8cc90a0511d7b7a8f181c3c45dd4 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Mon, 25 May 2026 10:00:25 -0400 Subject: [PATCH 02/42] ChangingEventsHandler functions --- source/sgp_mode/ChangingEventsHandler.h | 76 ++++++++++++++++++++----- source/sgp_mode/EventObjects.h | 5 +- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/source/sgp_mode/ChangingEventsHandler.h b/source/sgp_mode/ChangingEventsHandler.h index 6854982f..4ccca006 100644 --- a/source/sgp_mode/ChangingEventsHandler.h +++ b/source/sgp_mode/ChangingEventsHandler.h @@ -16,23 +16,24 @@ namespace sgpmode::eventHandler { +template class ChangingEventsHandler { public: // library for manipulating json files using json_t = nlohmann::json; // event object class alias using event_objects_t = EventObjects; - - + using world_t = WORLD_T; + // type for event type functions (i.e., replace, add, mul) using event_func_t = std::function; protected: - std::unordered_map all_events; // size_t is event id, event_func_t is the function that does each event - std::vector current_events; + std::unordered_map all_event_functions; // size_t is event id, event_func_t is the function that does each event + std::vector current_event_info; // from LogicTaskEnvironment.h get json field values template @@ -47,26 +48,71 @@ class ChangingEventsHandler { } - // check if event type is valid - void IsValidEvent(){} + // check if event type is valid (aka there is a function in all_event_functions that cna perform the event) + bool IsValidEvent(){} + + // create instance of event object using EventObject class, return event object + event_objects_t CreateEventObjects(size_t event_id, std::string event_name, std::string task_name, std:string update_indices, std::vector parameters){ + //ToDo check IsValidEvent before creating event + + + event_objects_t event; + event.event_id = event_id; + event.event_name = event_name; + event.task_name = task_name; + event.parameters = parameters; + + // slice and convert update indices to individula integers + std::vector indices_vect; + emp::slice(update_indices, indices_vect, ":"); + int start_index = static_cast(indices_vect[0]); + int end_index = static_cast(indices_vect[1]); + int step_index = static_cast(indices_vect[2]); + event.start_update = start_index; + event.end_update = end_index; + event.update_step = step_index; - // create instance of event object using EventObject class - void CreateEventObjects(){} + return event; + } // load in and process events.json file (includes creating events and checking if they are valid) - void LoadEvents(const std::string& event_filepath){} + void LoadEvents(const std::string& event_filepath){ + // from LogicTaskEnvironment.h + std::cout << "Loading tasks from environment file." << std::endl; + ClearEvents(); + // === Parse environment file === + // Check if given environment file exists. Exit if not. + const bool env_file_exists = std::filesystem::exists(event_filepath); + if (!env_file_exists) { + std::cout << "Envent file does not exist: " << env_filepath << std::endl; + std::exit(EXIT_FAILURE); + } + } // return vector of all current events - void GetCurrentEvents(){} + void GetCurrentEventInfo(int index){ + return current_event_info[index]; + } // return map of all events - void GetAllEvents(){} + void GetEventFunctions(size_t event_id){ + return all_event_functions[event_id]; + } - // delete finished events - void ClearEvents(){} + // delete current events info and functions + void ClearEvents(){ + current_event_info.clear(); + } + + // delete finished events + void SortEvents(){} // call event_func_t from current_events if possible based on update_indices - void ProcessEvent(){} + void ProcessEvent(world_t& world){ + world.GetUpdate(); + // std::vector> vec1; + // std::vector<> vec2; + } } } \ No newline at end of file diff --git a/source/sgp_mode/EventObjects.h b/source/sgp_mode/EventObjects.h index eedc2d0c..30e6d04c 100644 --- a/source/sgp_mode/EventObjects.h +++ b/source/sgp_mode/EventObjects.h @@ -23,8 +23,9 @@ class EventObjects { int start_update; int end_update; int update_step; - std::string org_mode; - std::string reward_mode; + // std::string org_mode; + // std::string reward_mode; + std::vector parameters; bool is_done = false; public: From 804ccd957aa2f08bfa974e596b405dd0fcffcf7b Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Sun, 31 May 2026 14:17:36 -0400 Subject: [PATCH 03/42] stash old files --- source/sgp_mode/ChangingEventsHandler.h | 30 +++++++++++++++++++------ source/sgp_mode/EventObjects.h | 5 +++-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/source/sgp_mode/ChangingEventsHandler.h b/source/sgp_mode/ChangingEventsHandler.h index 6854982f..e65afcda 100644 --- a/source/sgp_mode/ChangingEventsHandler.h +++ b/source/sgp_mode/ChangingEventsHandler.h @@ -16,17 +16,18 @@ namespace sgpmode::eventHandler { +template class ChangingEventsHandler { public: // library for manipulating json files using json_t = nlohmann::json; // event object class alias using event_objects_t = EventObjects; - - + using world_t = WORLD_T; + // type for event type functions (i.e., replace, add, mul) using event_func_t = std::function; @@ -51,22 +52,37 @@ class ChangingEventsHandler { void IsValidEvent(){} // create instance of event object using EventObject class - void CreateEventObjects(){} + void CreateEventObjects(size_t event_id, std::string event_name, std::string task_name, std:string update_indices, std::string parameters){ + event_objects_t event; + event.event_id = event_id; + event.event_name = event_name; + event.task_name = task_name; + event.parameters = parameters; + + } // load in and process events.json file (includes creating events and checking if they are valid) void LoadEvents(const std::string& event_filepath){} // return vector of all current events - void GetCurrentEvents(){} + void GetCurrentEvent(int index){ + return current_events[index]; + } // return map of all events - void GetAllEvents(){} + void GetAllEvents(){ + return all_events; + } // delete finished events void ClearEvents(){} // call event_func_t from current_events if possible based on update_indices - void ProcessEvent(){} + void ProcessEvent(world_t& world){ + world.GetUpdate(); + std::vector> vec1; + std::vector<> vec2; + } } } \ No newline at end of file diff --git a/source/sgp_mode/EventObjects.h b/source/sgp_mode/EventObjects.h index eedc2d0c..01f545c0 100644 --- a/source/sgp_mode/EventObjects.h +++ b/source/sgp_mode/EventObjects.h @@ -23,8 +23,9 @@ class EventObjects { int start_update; int end_update; int update_step; - std::string org_mode; - std::string reward_mode; + // std::string org_mode; + // std::string reward_mode; + std::string parameters; bool is_done = false; public: From 2dd517ddd41b108087283852b587360c552f410e Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Sun, 31 May 2026 14:24:17 -0400 Subject: [PATCH 04/42] stash old files --- source/sgp_mode/EventObjects.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/sgp_mode/EventObjects.h b/source/sgp_mode/EventObjects.h index 01f545c0..6f8406c0 100644 --- a/source/sgp_mode/EventObjects.h +++ b/source/sgp_mode/EventObjects.h @@ -26,6 +26,7 @@ class EventObjects { // std::string org_mode; // std::string reward_mode; std::string parameters; + bool reoccuring_event = false; bool is_done = false; public: From 37d651b8c989a316439bdef69236d1173f230ca1 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Sun, 31 May 2026 15:46:35 -0400 Subject: [PATCH 05/42] new changes up to date --- source/sgp_mode/ChangingEventsHandler.h | 84 +++++++++++++++---------- source/sgp_mode/EventObject.h | 75 ++++++++++++++++++++++ 2 files changed, 127 insertions(+), 32 deletions(-) create mode 100644 source/sgp_mode/EventObject.h diff --git a/source/sgp_mode/ChangingEventsHandler.h b/source/sgp_mode/ChangingEventsHandler.h index 4ccca006..df6296dc 100644 --- a/source/sgp_mode/ChangingEventsHandler.h +++ b/source/sgp_mode/ChangingEventsHandler.h @@ -32,8 +32,9 @@ class ChangingEventsHandler { )>; protected: - std::unordered_map all_event_functions; // size_t is event id, event_func_t is the function that does each event - std::vector current_event_info; + std::unordered_map predefined_event_functs; // std::string event_type/name, event_func_t is the function that does each event + emp::vector single_event_info; // vector of one time events (memebers of Event Object) + emp::vector reoccur_event_info; // vector of reoccuring events // from LogicTaskEnvironment.h get json field values template @@ -49,29 +50,30 @@ class ChangingEventsHandler { // check if event type is valid (aka there is a function in all_event_functions that cna perform the event) - bool IsValidEvent(){} + bool IsValidEvent(const std::string & event_type){ + return emp::Has(predefined_event_functs, event_type); + } // create instance of event object using EventObject class, return event object - event_objects_t CreateEventObjects(size_t event_id, std::string event_name, std::string task_name, std:string update_indices, std::vector parameters){ - //ToDo check IsValidEvent before creating event - - - event_objects_t event; - event.event_id = event_id; - event.event_name = event_name; - event.task_name = task_name; - event.parameters = parameters; - + event_objects_t CreateEventObjects(const size_t event_id, const std::string & event_name, const std::string & task_name, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ + + if(reoccur){ // slice and convert update indices to individula integers - std::vector indices_vect; - emp::slice(update_indices, indices_vect, ":"); - int start_index = static_cast(indices_vect[0]); - int end_index = static_cast(indices_vect[1]); - int step_index = static_cast(indices_vect[2]); - event.start_update = start_index; - event.end_update = end_index; - event.update_step = step_index; - + std::vector indices_vect; + emp::slice(update_indices, indices_vect, ":"); + int start_index = static_cast(indices_vect[0]); + int end_index = static_cast(indices_vect[1]); + int step_index = static_cast(indices_vect[2]); + event.start_update = start_index; + event.end_update = end_index; + event.update_step = step_index; + + event_objects_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); + } + else { + int start_index = static_cast(update_indices); + event_objects_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); + } return event; } @@ -82,29 +84,47 @@ class ChangingEventsHandler { ClearEvents(); // === Parse environment file === // Check if given environment file exists. Exit if not. - const bool env_file_exists = std::filesystem::exists(event_filepath); - if (!env_file_exists) { - std::cout << "Envent file does not exist: " << env_filepath << std::endl; + const bool event_file_exists = std::filesystem::exists(event_filepath); + if (!event_file_exists) { + std::cout << "Event file does not exist: " << event_filepath << std::endl; std::exit(EXIT_FAILURE); } + + // read event.json file + std::ifstream event_ifstream(event_filepath); + nlohmann::json eve_json; + event_ifstream >> eve_json; + + // check for correct json format + emp::vector fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; + + emp_assert(eve.json.contains("events")); + + + } + + // return single event info at a specific index + void GetSingleEventInfo(int index){ + return single_event_info[index]; } - // return vector of all current events - void GetCurrentEventInfo(int index){ - return current_event_info[index]; + // return reoccuring event info at a specific index + void GetReoccurEventInfo(int index){ + return reoccur_event_info[index]; } - // return map of all events + // void GetEventFunctions(size_t event_id){ - return all_event_functions[event_id]; + return predefined_event_functs[event_id]; } // delete current events info and functions void ClearEvents(){ - current_event_info.clear(); + single_event_info.clear(); + reoccur_event_info.clear(); } - // delete finished events + // delete finished events sort events based on when should occur void SortEvents(){} // call event_func_t from current_events if possible based on update_indices diff --git a/source/sgp_mode/EventObject.h b/source/sgp_mode/EventObject.h new file mode 100644 index 00000000..540affd4 --- /dev/null +++ b/source/sgp_mode/EventObject.h @@ -0,0 +1,75 @@ +#include "emp/base/vector.hpp" +#include "emp/bits/Bits.hpp" +#include "emp/tools/string_utils.hpp" +#include "emp/datastructs/set_utils.hpp" +#include "emp/math/math.hpp" + +#include +#include +#include +#include +#include +#include + +namespace sgpmode::eventHandler { + +class EventObject { + protected: + + size_t event id = 0; + std::string event_type{"NULL"}; + std::string task_name{"NULL"}; + double task_value = 0.0; + int start_update = 0; + int end_update = 0; + int update_step = 0; + + emp::vector parameters{}; + bool reoccuring_event = false; + bool is_done = false; + + public: + // default + EventObject()=default; + // reoccuring events + EventObject(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, + const double & arg_task_value, const int & arg_start_update, const int & arg_end_update, const int & arg_update_step, + const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : + event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), + start_update(arg_start_update), end_update(arg_end_update), update_step(arg_end_update), + parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) + {} + // one time events + EventObject(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, + const double & arg_task_value, const int & arg_start_update, + const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : + event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), + start_update(arg_start_update), + parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) + {} + + void SetIsDone(){ is_done = true; } + + std::string GetEventType(){ return event_type; } + + bool GetIsDone(){ return is_done; } + + double GetTaskValue(){ return task_value; } + + std::string GetTaskName(){ return task_name; } + + std::string GetOrgMode(){ return org_mode; } + + std::string GetRewardMode(){ return reward_mode; } + + size_t GetEventId() { return event_id; } + + int GetStartUpdate() { return start_update; } + + int GetEndUpdate() { return end_update; } + + int GetUpdateStep() { return update_step; } + +} + +} \ No newline at end of file From a11f577b7263c2285853e73c03672a8bf2278a36 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Mon, 1 Jun 2026 09:57:01 -0400 Subject: [PATCH 06/42] LoadEvents, DeleteEvents --- source/sgp_mode/ChangingEventsHandler.h | 59 ++++++++++++++++++----- source/sgp_mode/EventObjects.h | 63 ------------------------- 2 files changed, 48 insertions(+), 74 deletions(-) delete mode 100644 source/sgp_mode/EventObjects.h diff --git a/source/sgp_mode/ChangingEventsHandler.h b/source/sgp_mode/ChangingEventsHandler.h index df6296dc..117334d2 100644 --- a/source/sgp_mode/ChangingEventsHandler.h +++ b/source/sgp_mode/ChangingEventsHandler.h @@ -12,7 +12,7 @@ #include #include -#include "EventObjects.h" +#include "EventObject.h" namespace sgpmode::eventHandler { @@ -22,7 +22,7 @@ class ChangingEventsHandler { // library for manipulating json files using json_t = nlohmann::json; // event object class alias - using event_objects_t = EventObjects; + using event_object_t = EventObject; using world_t = WORLD_T; // type for event type functions (i.e., replace, add, mul) @@ -33,8 +33,8 @@ class ChangingEventsHandler { protected: std::unordered_map predefined_event_functs; // std::string event_type/name, event_func_t is the function that does each event - emp::vector single_event_info; // vector of one time events (memebers of Event Object) - emp::vector reoccur_event_info; // vector of reoccuring events + emp::vector single_event_info; // vector of one time events (memebers of Event Object) + emp::vector reoccur_event_info; // vector of reoccuring events // from LogicTaskEnvironment.h get json field values template @@ -55,7 +55,7 @@ class ChangingEventsHandler { } // create instance of event object using EventObject class, return event object - event_objects_t CreateEventObjects(const size_t event_id, const std::string & event_name, const std::string & task_name, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ + event_object_t CreateEventObject(const size_t event_id, const std::string & event_name, const std::string & task_name, const double & task_value, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ if(reoccur){ // slice and convert update indices to individula integers @@ -68,11 +68,11 @@ class ChangingEventsHandler { event.end_update = end_index; event.update_step = step_index; - event_objects_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); + event_object_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); } else { int start_index = static_cast(update_indices); - event_objects_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); + event_object_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); } return event; } @@ -96,10 +96,28 @@ class ChangingEventsHandler { event_ifstream >> eve_json; // check for correct json format - emp::vector fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; + emp::vector event_fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; + + emp_assert(eve_json.contains("events")); + for(auto& line; eve_json["events"]){ + // check all fields are in file + for(std::string name: event_fields){ + emp_assert(line.contains(name)); + } + + IsValidEvent(line["event_type"]); + if(line["reoccuring_event"]){ + int event_id = reoccur_event_info.size(); + event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); + reoccur_event_info.emplace_back(event_obj); + } + if(!line["reoccuring_event"]){ + int event_id = single_event_info.size(); + event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); + single_event_info.emplace_back(event_obj); + } + } - emp_assert(eve.json.contains("events")); - } @@ -118,12 +136,18 @@ class ChangingEventsHandler { return predefined_event_functs[event_id]; } - // delete current events info and functions + // delete all current events info void ClearEvents(){ single_event_info.clear(); reoccur_event_info.clear(); } + // delete an event from event + void DeleteEvent(const bool reoccur, const int index){ + (reoccur) ? reoccur_event_info.erase(index) : + single_event_info.erase(index); + } + // delete finished events sort events based on when should occur void SortEvents(){} @@ -133,6 +157,19 @@ class ChangingEventsHandler { // std::vector> vec1; // std::vector<> vec2; } + + const std::unordered_map < std::string, event_func_t > + predefined_event_functs = { + { + "task_value_replace", + }, + { + "task_value_add", + }, + { + "task_value_mul", + } + } } } \ No newline at end of file diff --git a/source/sgp_mode/EventObjects.h b/source/sgp_mode/EventObjects.h deleted file mode 100644 index 8d71f38a..00000000 --- a/source/sgp_mode/EventObjects.h +++ /dev/null @@ -1,63 +0,0 @@ -#include "emp/base/vector.hpp" -#include "emp/bits/Bits.hpp" -#include "emp/tools/string_utils.hpp" -#include "emp/datastructs/set_utils.hpp" -#include "emp/math/math.hpp" - -#include -#include -#include -#include -#include -#include - -namespace sgpmode::eventHandler { - -class EventObjects { - protected: - - size_t event id; - std::string event_type; - std::string task_name; - double task_value; - int start_update; - int end_update; - int update_step; - // std::string org_mode; - // std::string reward_mode; - std::string parameters; - bool reoccuring_event = false; - std::vector parameters; - bool is_done = false; - - public: - - void SetIsDone(){ - if(is_done == false){ - is_done = true; - } - } - - std::string GetEventType(){ return event_type; } - - bool GetIsDone(){ return is_done; } - - double GetTaskValue(){ return task_value; } - - std::string GetTaskName(){ return task_name; } - - std::string GetOrgMode(){ return org_mode; } - - std::string GetRewardMode(){ return reward_mode; } - - size_t GetEventId() { return event_id; } - - int GetStartUpdate() { return start_update; } - - int GetEndUpdate() { return end_update; } - - int GetUpdateStep() { return update_step; } - -} - -} \ No newline at end of file From 0bf3438c39aeffed23c61b1970b7f77e99541713 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Mon, 8 Jun 2026 08:55:38 -0400 Subject: [PATCH 07/42] ProcessEvents, DeleteEvents, SortEvents --- source/sgp_mode/ChangingEventsHandler.h | 219 ++++++++++++++++++------ source/sgp_mode/EventObject.h | 4 + 2 files changed, 175 insertions(+), 48 deletions(-) diff --git a/source/sgp_mode/ChangingEventsHandler.h b/source/sgp_mode/ChangingEventsHandler.h index 117334d2..ddfff47a 100644 --- a/source/sgp_mode/ChangingEventsHandler.h +++ b/source/sgp_mode/ChangingEventsHandler.h @@ -11,10 +11,28 @@ #include #include #include +#include #include "EventObject.h" -namespace sgpmode::eventHandler { +namespace sgpmode::EventHandler { + +struct EventDefinition { + int event_id; + std::string event_name; + event_func_t event_function; + std::string description; + EventDefinition( + int a_event_id, + std::string a_event_name, + event_func_t a_event_func, + std::string a_desc + ) : + event_id(a_event_is), a_event_name(a_event_name), event_function(a_event_func), description(a_desc) + { ; } +}; +// todo: define event types +// EventDefinition template class ChangingEventsHandler { @@ -32,26 +50,28 @@ class ChangingEventsHandler { )>; protected: - std::unordered_map predefined_event_functs; // std::string event_type/name, event_func_t is the function that does each event - emp::vector single_event_info; // vector of one time events (memebers of Event Object) - emp::vector reoccur_event_info; // vector of reoccuring events + // make predefined_event_functions into vector + emp::vector predefined_event_functs; // event_func_t is the function that does each event + std::unordered_map name_to_id; // event type/name mapped to index of event in predefined_event_functs. Index is used as event_id + emp::vector single_event_info; // vector of one time events (Event Object type) + emp::vector reoccur_event_info; // vector of reoccuring events (Event Object type) - // from LogicTaskEnvironment.h get json field values - template - RET_TYPE GetVal( - json_t& json, - const std::string& field, - RET_TYPE default_val - ) { - return (json.contains(field)) ? - static_cast(json[field]) : - default_val; - } + // // from LogicTaskEnvironment.h get json field values + // template + // RET_TYPE GetVal( + // json_t& json, + // const std::string& field, + // RET_TYPE default_val + // ) { + // return (json.contains(field)) ? + // static_cast(json[field]) : + // default_val; + // } // check if event type is valid (aka there is a function in all_event_functions that cna perform the event) bool IsValidEvent(const std::string & event_type){ - return emp::Has(predefined_event_functs, event_type); + return emp::Has(name_to_id, event_type); } // create instance of event object using EventObject class, return event object @@ -77,10 +97,18 @@ class ChangingEventsHandler { return event; } + // should i add emp:: to funct? + bool CheckJsonField(const emp::vector & fields, auto& json_line){ + for(std::string name : fields){ + (emp_assert(json_line.contains(name)))? continue : + return false; + } + } + // load in and process events.json file (includes creating events and checking if they are valid) void LoadEvents(const std::string& event_filepath){ // from LogicTaskEnvironment.h - std::cout << "Loading tasks from environment file." << std::endl; + std::cout << "Loading tasks from event file." << std::endl; ClearEvents(); // === Parse environment file === // Check if given environment file exists. Exit if not. @@ -100,25 +128,30 @@ class ChangingEventsHandler { emp_assert(eve_json.contains("events")); for(auto& line; eve_json["events"]){ - // check all fields are in file - for(std::string name: event_fields){ - emp_assert(line.contains(name)); - } + CheckJsonFields(event_fields, line); IsValidEvent(line["event_type"]); + + // Is an reoccuring event if(line["reoccuring_event"]){ - int event_id = reoccur_event_info.size(); + // set event_id + int event_id = name_to_id[line["event_type"]]; + // create event object event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); reoccur_event_info.emplace_back(event_obj); } - if(!line["reoccuring_event"]){ - int event_id = single_event_info.size(); + // is a single event + else { + // set event_id + int event_id = name_to_id[line["event_type"]]; + // create event object event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); single_event_info.emplace_back(event_obj); } } - - + // sort vectors that hold event objects + SortSingleEvents(); + SortReoccurEvents(); } // return single event info at a specific index @@ -131,7 +164,7 @@ class ChangingEventsHandler { return reoccur_event_info[index]; } - // + // get predefined event function at certain index void GetEventFunctions(size_t event_id){ return predefined_event_functs[event_id]; } @@ -142,33 +175,123 @@ class ChangingEventsHandler { reoccur_event_info.clear(); } - // delete an event from event - void DeleteEvent(const bool reoccur, const int index){ - (reoccur) ? reoccur_event_info.erase(index) : - single_event_info.erase(index); + // // delete an event from event info vector (single or reoccur) + // void DeleteOneEvent(const bool reoccur, const int & index){ + // (reoccur) ? reoccur_event_info.erase(reoccur_event_info.begin()+index) : + // single_event_info.erase(single_event_info.begin()+index); + // } + + // Delete all finished events from an event_info vector + void DeleteEvents(const emp::vector & event_vect){ + // erase remove idiom https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom + event_vect.erase(std::remove_if(event_vect.begin(), event_vect.end(), + [](const event_object_t & eve){ return eve.GetIsDone(); }), + event_vect.end()); } - // delete finished events sort events based on when should occur - void SortEvents(){} + // helper function to SortEvents + int SortPartition(emp::vector & event_vect, int & begin_index, int & end_index){ + // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ + int piv = event_vect[end_index].GetStartUpdate(); + int i = begin_index - 1; + for(int j = begin_index; j <= end_index - 1; j++){ + if(event_vect[j].GetStartUpdate() < piv){ + i++; + event_object_t temp = event_vect[i]; + event_vect[i] = event_vect[j]; + event_vect[j] = temp; + } + } + i++; + event_object_t temp = event_vect[i]; + event_vect[i] = event_vect[end_index]; + event_vect[end_index] = temp; + return i; + } + + // sort events based on start update + void SortEvents(emp::vector & event_vect, int & begin_index, int & end_index){ + // use quick sort method + // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ + + if(begin_index < end_index){ + int pivot_index = SortPartition(event_vect, begin_index, end_index); + SortSingelEvent(event_vect, begin_index, pivot_index - 1); + SortSingleEvent(event_vect, pivot_index + 1, end_index); + } - // call event_func_t from current_events if possible based on update_indices - void ProcessEvent(world_t& world){ - world.GetUpdate(); - // std::vector> vec1; - // std::vector<> vec2; } - const std::unordered_map < std::string, event_func_t > - predefined_event_functs = { - { - "task_value_replace", - }, - { - "task_value_add", - }, - { - "task_value_mul", + // call event_func_t from current_events if possible based on update_indices + void ProcessEvent(const world_t& world){ + // // set update variable to world.GetUpdate() or whaterver gets the world's update + int update = world.GetUpdate(); + + // loop through single time events + for(auto& it = single_event_info.begin(); it != single_event_info.end(); it++){ + if(*it.GetStartUpdate() > update){ + break; + } + if(*it.GetStartUpdate() == update){ + // call event function look at how logic task does it + predefined_event_functs[*it.GetEventId()].event_function; + // set to is_done true + *it.SetIsDone(); + } } + + // loop through reoccur time events + for(auto& it = reoccur_event_info.begin(); it != reoccur_event_info.end(); it++){ + if(*it.GetStartUpdate() > update){ + break; + } + if(*it.GetStartUpdate() == update){ + // call event function look at how logictask does it + predefined_event_functs[*it.GetEventId()].event_function; + // reset start update + int new_start = *it.GetStartUpdate() + *it.GetUpdateStep(); + *it.SetStartUpdate(new_start); + // check if event is done + if (update >= *it.GetEndUpdate() || *it.GetStartUpdate() > *it.GetEndUpdate()){ + *it.SetIsDone(); + } + } + } + + // clean up + DeleteEvents(single_event_info); + DeleteEvents(reoccur_event_info); + // don't need to resort single_event_info at the moment + SortEvents(reoccur_event_info, 0, reoccur_event.size() - 1); + } + + // defined predefined_event_functs + const std::vector < EventDefinition > + ChangingEventHandler::predefined_event_functs = { + ChangingEventsHandler::EventDefinition{ + 0, + "task_value_replace", + [](const world_t & world, const event_object_t & event_info)->{ + // TODO: define function + }, + "replace a specific preexisting task value with another value" + }, + ChangingEventsHandler::EventDefinition{ + 1, + "task_value_add", + [](const world_t & world, const event_object_t & event_info)->{ + // TODO: define function + }, + "add a specific amount to the current value of a preexisting task" + }, + ChangingEventsHandler::EventDefinition{ + 2, + "task_value_mul", + [](const world_t & world, const event_object_t & event_info)->{ + // TODO: define function + }, + "multiply a specific amount to the current value of a preexisting task" + } } } diff --git a/source/sgp_mode/EventObject.h b/source/sgp_mode/EventObject.h index 540affd4..93a4cb12 100644 --- a/source/sgp_mode/EventObject.h +++ b/source/sgp_mode/EventObject.h @@ -50,6 +50,10 @@ class EventObject { void SetIsDone(){ is_done = true; } + void SetStartUpdate(const int & new_start){ + start_update = new_start; + } + std::string GetEventType(){ return event_type; } bool GetIsDone(){ return is_done; } From 6aaff0653e74bbc567811198e94c580032b4e9f7 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Tue, 9 Jun 2026 10:49:06 -0400 Subject: [PATCH 08/42] Remove accidentally duplicated function in Host.h --- source/default_mode/Host.h | 52 ++++++++++++-------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/source/default_mode/Host.h b/source/default_mode/Host.h index d95a9b35..05cbba6f 100644 --- a/source/default_mode/Host.h +++ b/source/default_mode/Host.h @@ -126,17 +126,17 @@ class Host: public Organism { * */ emp::BitSet tag; - + /** - * + * * Purpose: Tracks the taxon of this organism. * */ emp::Ptr my_taxon = NULL; - - /** + + /** * Purpose: To track location in the world - * + * */ emp::WorldPosition location; @@ -377,9 +377,9 @@ class Host: public Organism { /** * Input: None - * + * * Output: The world position of the organism - * + * * Purpose: To get the world position of the organism */ emp::WorldPosition GetLocation() {return location;} @@ -437,12 +437,12 @@ class Host: public Organism { /** * Input: A new world position - * + * * Output: None - * + * * Purpose: To set the organism's world position */ - virtual void SetLocation(emp::WorldPosition _in) {location = _in;} + virtual void SetLocation(emp::WorldPosition _in) {location = _in;} /** @@ -474,7 +474,7 @@ class Host: public Organism { * Purpose: To set a host's tag. */ void SetTag(emp::BitSet & _in) { tag.Import(_in); } - + /** * Input: None * @@ -601,27 +601,6 @@ class Host: public Organism { */ void AddPoints(double _in) {points += _in;} - /** - * Input: The symbiont index position to remove (remember it should be 1-indexed) - * - * Output: The removed symbiont or null if invalid index given - * - * Purpose: To allow removal of a symbiont - */ - emp::Ptr RemoveSymbiont(int index) { - int num_syms = syms.size(); - if(index < 1 || index > num_syms) { - return nullptr; - } else { - emp::Ptr to_remove = syms[index-1]; - syms.erase(syms.begin() + (index-1)); - to_remove->SetHost(nullptr); - to_remove->SetLocation(emp::WorldPosition::invalid_id); - return to_remove; - } - - } - /** * Input: The symbiont index position to remove (remember it should be 1-indexed) * @@ -637,10 +616,11 @@ class Host: public Organism { emp::Ptr to_remove = syms[index-1]; syms.erase(syms.begin() + (index-1)); to_remove->SetHost(nullptr); + to_remove->SetLocation(emp::WorldPosition::invalid_id); return to_remove; } - } + } /** * Input: The pointer to the organism that is to be added to the host's symbionts. @@ -745,7 +725,7 @@ class Host: public Organism { */ emp::Ptr Reproduce(){ emp::Ptr host_baby = MakeNew(); - + host_baby->Mutate(); host_baby->SetReproCount(reproductions + 1); SetPoints(0); @@ -781,12 +761,12 @@ class Host: public Organism { 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->GetNormal(0.0, mutation_size); if(interaction_val < -1) interaction_val = -1; else if (interaction_val > 1) interaction_val = 1; - + } if (my_config->TAG_MATCHING()) { From 197faa18fae77f4301069984c1abbdb1fb908b13 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Tue, 9 Jun 2026 14:51:50 -0400 Subject: [PATCH 09/42] Move event system into its own directory. Separate event type definition management from event manager. Renaming/reorganization in progress. --- source/sgp_mode/ChangingEventsHandler.h | 298 ------------------ .../{EventObject.h => events/Event.h} | 24 +- source/sgp_mode/events/EventManager.h | 281 +++++++++++++++++ source/sgp_mode/events/EventTypeDefinition.h | 52 +++ source/sgp_mode/events/EventTypeLibrary.h | 96 ++++++ source/sgp_mode/events/readme.md | 23 ++ 6 files changed, 465 insertions(+), 309 deletions(-) delete mode 100644 source/sgp_mode/ChangingEventsHandler.h rename source/sgp_mode/{EventObject.h => events/Event.h} (79%) create mode 100644 source/sgp_mode/events/EventManager.h create mode 100644 source/sgp_mode/events/EventTypeDefinition.h create mode 100644 source/sgp_mode/events/EventTypeLibrary.h create mode 100644 source/sgp_mode/events/readme.md diff --git a/source/sgp_mode/ChangingEventsHandler.h b/source/sgp_mode/ChangingEventsHandler.h deleted file mode 100644 index ddfff47a..00000000 --- a/source/sgp_mode/ChangingEventsHandler.h +++ /dev/null @@ -1,298 +0,0 @@ -#include "emp/base/vector.hpp" -#include "emp/bits/Bits.hpp" -#include "emp/tools/string_utils.hpp" -#include "emp/datastructs/set_utils.hpp" -#include "emp/math/math.hpp" -#include "../../json/json.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "EventObject.h" - -namespace sgpmode::EventHandler { - -struct EventDefinition { - int event_id; - std::string event_name; - event_func_t event_function; - std::string description; - EventDefinition( - int a_event_id, - std::string a_event_name, - event_func_t a_event_func, - std::string a_desc - ) : - event_id(a_event_is), a_event_name(a_event_name), event_function(a_event_func), description(a_desc) - { ; } -}; -// todo: define event types -// EventDefinition - -template -class ChangingEventsHandler { - public: - // library for manipulating json files - using json_t = nlohmann::json; - // event object class alias - using event_object_t = EventObject; - using world_t = WORLD_T; - - // type for event type functions (i.e., replace, add, mul) - using event_func_t = std::function; - - protected: - // make predefined_event_functions into vector - emp::vector predefined_event_functs; // event_func_t is the function that does each event - std::unordered_map name_to_id; // event type/name mapped to index of event in predefined_event_functs. Index is used as event_id - emp::vector single_event_info; // vector of one time events (Event Object type) - emp::vector reoccur_event_info; // vector of reoccuring events (Event Object type) - - // // from LogicTaskEnvironment.h get json field values - // template - // RET_TYPE GetVal( - // json_t& json, - // const std::string& field, - // RET_TYPE default_val - // ) { - // return (json.contains(field)) ? - // static_cast(json[field]) : - // default_val; - // } - - - // check if event type is valid (aka there is a function in all_event_functions that cna perform the event) - bool IsValidEvent(const std::string & event_type){ - return emp::Has(name_to_id, event_type); - } - - // create instance of event object using EventObject class, return event object - event_object_t CreateEventObject(const size_t event_id, const std::string & event_name, const std::string & task_name, const double & task_value, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ - - if(reoccur){ - // slice and convert update indices to individula integers - std::vector indices_vect; - emp::slice(update_indices, indices_vect, ":"); - int start_index = static_cast(indices_vect[0]); - int end_index = static_cast(indices_vect[1]); - int step_index = static_cast(indices_vect[2]); - event.start_update = start_index; - event.end_update = end_index; - event.update_step = step_index; - - event_object_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); - } - else { - int start_index = static_cast(update_indices); - event_object_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); - } - return event; - } - - // should i add emp:: to funct? - bool CheckJsonField(const emp::vector & fields, auto& json_line){ - for(std::string name : fields){ - (emp_assert(json_line.contains(name)))? continue : - return false; - } - } - - // load in and process events.json file (includes creating events and checking if they are valid) - void LoadEvents(const std::string& event_filepath){ - // from LogicTaskEnvironment.h - std::cout << "Loading tasks from event file." << std::endl; - ClearEvents(); - // === Parse environment file === - // Check if given environment file exists. Exit if not. - const bool event_file_exists = std::filesystem::exists(event_filepath); - if (!event_file_exists) { - std::cout << "Event file does not exist: " << event_filepath << std::endl; - std::exit(EXIT_FAILURE); - } - - // read event.json file - std::ifstream event_ifstream(event_filepath); - nlohmann::json eve_json; - event_ifstream >> eve_json; - - // check for correct json format - emp::vector event_fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; - - emp_assert(eve_json.contains("events")); - for(auto& line; eve_json["events"]){ - - CheckJsonFields(event_fields, line); - IsValidEvent(line["event_type"]); - - // Is an reoccuring event - if(line["reoccuring_event"]){ - // set event_id - int event_id = name_to_id[line["event_type"]]; - // create event object - event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); - reoccur_event_info.emplace_back(event_obj); - } - // is a single event - else { - // set event_id - int event_id = name_to_id[line["event_type"]]; - // create event object - event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); - single_event_info.emplace_back(event_obj); - } - } - // sort vectors that hold event objects - SortSingleEvents(); - SortReoccurEvents(); - } - - // return single event info at a specific index - void GetSingleEventInfo(int index){ - return single_event_info[index]; - } - - // return reoccuring event info at a specific index - void GetReoccurEventInfo(int index){ - return reoccur_event_info[index]; - } - - // get predefined event function at certain index - void GetEventFunctions(size_t event_id){ - return predefined_event_functs[event_id]; - } - - // delete all current events info - void ClearEvents(){ - single_event_info.clear(); - reoccur_event_info.clear(); - } - - // // delete an event from event info vector (single or reoccur) - // void DeleteOneEvent(const bool reoccur, const int & index){ - // (reoccur) ? reoccur_event_info.erase(reoccur_event_info.begin()+index) : - // single_event_info.erase(single_event_info.begin()+index); - // } - - // Delete all finished events from an event_info vector - void DeleteEvents(const emp::vector & event_vect){ - // erase remove idiom https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom - event_vect.erase(std::remove_if(event_vect.begin(), event_vect.end(), - [](const event_object_t & eve){ return eve.GetIsDone(); }), - event_vect.end()); - } - - // helper function to SortEvents - int SortPartition(emp::vector & event_vect, int & begin_index, int & end_index){ - // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ - int piv = event_vect[end_index].GetStartUpdate(); - int i = begin_index - 1; - for(int j = begin_index; j <= end_index - 1; j++){ - if(event_vect[j].GetStartUpdate() < piv){ - i++; - event_object_t temp = event_vect[i]; - event_vect[i] = event_vect[j]; - event_vect[j] = temp; - } - } - i++; - event_object_t temp = event_vect[i]; - event_vect[i] = event_vect[end_index]; - event_vect[end_index] = temp; - return i; - } - - // sort events based on start update - void SortEvents(emp::vector & event_vect, int & begin_index, int & end_index){ - // use quick sort method - // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ - - if(begin_index < end_index){ - int pivot_index = SortPartition(event_vect, begin_index, end_index); - SortSingelEvent(event_vect, begin_index, pivot_index - 1); - SortSingleEvent(event_vect, pivot_index + 1, end_index); - } - - } - - // call event_func_t from current_events if possible based on update_indices - void ProcessEvent(const world_t& world){ - // // set update variable to world.GetUpdate() or whaterver gets the world's update - int update = world.GetUpdate(); - - // loop through single time events - for(auto& it = single_event_info.begin(); it != single_event_info.end(); it++){ - if(*it.GetStartUpdate() > update){ - break; - } - if(*it.GetStartUpdate() == update){ - // call event function look at how logic task does it - predefined_event_functs[*it.GetEventId()].event_function; - // set to is_done true - *it.SetIsDone(); - } - } - - // loop through reoccur time events - for(auto& it = reoccur_event_info.begin(); it != reoccur_event_info.end(); it++){ - if(*it.GetStartUpdate() > update){ - break; - } - if(*it.GetStartUpdate() == update){ - // call event function look at how logictask does it - predefined_event_functs[*it.GetEventId()].event_function; - // reset start update - int new_start = *it.GetStartUpdate() + *it.GetUpdateStep(); - *it.SetStartUpdate(new_start); - // check if event is done - if (update >= *it.GetEndUpdate() || *it.GetStartUpdate() > *it.GetEndUpdate()){ - *it.SetIsDone(); - } - } - } - - // clean up - DeleteEvents(single_event_info); - DeleteEvents(reoccur_event_info); - // don't need to resort single_event_info at the moment - SortEvents(reoccur_event_info, 0, reoccur_event.size() - 1); - } - - // defined predefined_event_functs - const std::vector < EventDefinition > - ChangingEventHandler::predefined_event_functs = { - ChangingEventsHandler::EventDefinition{ - 0, - "task_value_replace", - [](const world_t & world, const event_object_t & event_info)->{ - // TODO: define function - }, - "replace a specific preexisting task value with another value" - }, - ChangingEventsHandler::EventDefinition{ - 1, - "task_value_add", - [](const world_t & world, const event_object_t & event_info)->{ - // TODO: define function - }, - "add a specific amount to the current value of a preexisting task" - }, - ChangingEventsHandler::EventDefinition{ - 2, - "task_value_mul", - [](const world_t & world, const event_object_t & event_info)->{ - // TODO: define function - }, - "multiply a specific amount to the current value of a preexisting task" - } - } -} - -} \ No newline at end of file diff --git a/source/sgp_mode/EventObject.h b/source/sgp_mode/events/Event.h similarity index 79% rename from source/sgp_mode/EventObject.h rename to source/sgp_mode/events/Event.h index 93a4cb12..3219d70a 100644 --- a/source/sgp_mode/EventObject.h +++ b/source/sgp_mode/events/Event.h @@ -1,3 +1,5 @@ +#pragma once + #include "emp/base/vector.hpp" #include "emp/bits/Bits.hpp" #include "emp/tools/string_utils.hpp" @@ -11,38 +13,38 @@ #include #include -namespace sgpmode::eventHandler { +namespace sgpmode { -class EventObject { - protected: +class Event { +protected: - size_t event id = 0; + size_t event_id = 0; std::string event_type{"NULL"}; std::string task_name{"NULL"}; double task_value = 0.0; int start_update = 0; int end_update = 0; int update_step = 0; - + emp::vector parameters{}; bool reoccuring_event = false; bool is_done = false; public: // default - EventObject()=default; + Event()=default; // reoccuring events - EventObject(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, + Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, const double & arg_task_value, const int & arg_start_update, const int & arg_end_update, const int & arg_update_step, - const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : + const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), start_update(arg_start_update), end_update(arg_end_update), update_step(arg_end_update), parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) {} // one time events - EventObject(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, + Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, const double & arg_task_value, const int & arg_start_update, - const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : + const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), start_update(arg_start_update), parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) @@ -51,7 +53,7 @@ class EventObject { void SetIsDone(){ is_done = true; } void SetStartUpdate(const int & new_start){ - start_update = new_start; + start_update = new_start; } std::string GetEventType(){ return event_type; } diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h new file mode 100644 index 00000000..46255f01 --- /dev/null +++ b/source/sgp_mode/events/EventManager.h @@ -0,0 +1,281 @@ +#pragma once +// @AML review: added header guards +// @AML review: switch indentation to 2 spaces for consistency + +// @AML review: Moved local includes to top for consistency +#include "Event.h" +#include "EventTypeDefinition.h" + +#include "emp/base/vector.hpp" +#include "emp/bits/Bits.hpp" +#include "emp/tools/string_utils.hpp" +#include "emp/datastructs/set_utils.hpp" +#include "emp/math/math.hpp" +#include "../../../json/json.hpp" + +#include +#include +#include +#include +#include +#include +#include + +// @AML review: Don't need to namespace the event handler +// (unless there's a bunch of internal components that should be isolated +// from the rest of the repo) +namespace sgpmode { + +// TODO: Document event types + +template +class EventManager { +public: + using json_t = nlohmann::json; // json file type + using event_t = Event; // event class alias + using world_t = WORLD_T; // world type alias + using event_type_def_t = EvenTypeDefinition; + using event_handler_func_t = typename event_type_def_t::event_handler_func_t; + +protected: + + + + + emp::vector single_event_info; // vector of one time events (Event Object type) + emp::vector reoccur_event_info; // vector of reoccuring events (Event Object type) + + // // from LogicTaskEnvironment.h get json field values + // template + // RET_TYPE GetVal( + // json_t& json, + // const std::string& field, + // RET_TYPE default_val + // ) { + // return (json.contains(field)) ? + // static_cast(json[field]) : + // default_val; + // } + + // create instance of event object using EventObject class, return event object + event_object_t CreateEventObject(const size_t event_id, const std::string & event_name, const std::string & task_name, const double & task_value, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ + + if(reoccur){ + // slice and convert update indices to individula integers + std::vector indices_vect; + emp::slice(update_indices, indices_vect, ":"); + int start_index = static_cast(indices_vect[0]); + int end_index = static_cast(indices_vect[1]); + int step_index = static_cast(indices_vect[2]); + event.start_update = start_index; + event.end_update = end_index; + event.update_step = step_index; + + event_object_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); + } + else { + int start_index = static_cast(update_indices); + event_object_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); + } + return event; + } + + // should i add emp:: to funct? + bool CheckJsonField(const emp::vector & fields, auto& json_line){ + for(std::string name : fields){ + (emp_assert(json_line.contains(name)))? continue : + return false; + } + } + + // load in and process events.json file (includes creating events and checking if they are valid) + void LoadEvents(const std::string& event_filepath){ + // from LogicTaskEnvironment.h + std::cout << "Loading tasks from event file." << std::endl; + ClearEvents(); + // === Parse environment file === + // Check if given environment file exists. Exit if not. + const bool event_file_exists = std::filesystem::exists(event_filepath); + if (!event_file_exists) { + std::cout << "Event file does not exist: " << event_filepath << std::endl; + std::exit(EXIT_FAILURE); + } + + // read event.json file + std::ifstream event_ifstream(event_filepath); + nlohmann::json eve_json; + event_ifstream >> eve_json; + + // check for correct json format + emp::vector event_fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; + + emp_assert(eve_json.contains("events")); + for(auto& line; eve_json["events"]){ + + CheckJsonFields(event_fields, line); + IsValidEvent(line["event_type"]); + + // Is an reoccuring event + if(line["reoccuring_event"]){ + // set event_id + int event_id = name_to_id[line["event_type"]]; + // create event object + event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); + reoccur_event_info.emplace_back(event_obj); + } + // is a single event + else { + // set event_id + int event_id = name_to_id[line["event_type"]]; + // create event object + event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); + single_event_info.emplace_back(event_obj); + } + } + // sort vectors that hold event objects + SortSingleEvents(); + SortReoccurEvents(); + } + + // return single event info at a specific index + void GetSingleEventInfo(int index){ + return single_event_info[index]; + } + + // return reoccuring event info at a specific index + void GetReoccurEventInfo(int index){ + return reoccur_event_info[index]; + } + + // get predefined event function at certain index + void GetEventFunctions(size_t event_id){ + return event_types[event_id]; + } + + // delete all current events info + void ClearEvents(){ + single_event_info.clear(); + reoccur_event_info.clear(); + } + + // // delete an event from event info vector (single or reoccur) + // void DeleteOneEvent(const bool reoccur, const int & index){ + // (reoccur) ? reoccur_event_info.erase(reoccur_event_info.begin()+index) : + // single_event_info.erase(single_event_info.begin()+index); + // } + + // Delete all finished events from an event_info vector + void DeleteEvents(const emp::vector & event_vect){ + // erase remove idiom https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom + event_vect.erase(std::remove_if(event_vect.begin(), event_vect.end(), + [](const event_object_t & eve){ return eve.GetIsDone(); }), + event_vect.end()); + } + + // helper function to SortEvents + int SortPartition(emp::vector & event_vect, int & begin_index, int & end_index){ + // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ + int piv = event_vect[end_index].GetStartUpdate(); + int i = begin_index - 1; + for(int j = begin_index; j <= end_index - 1; j++){ + if(event_vect[j].GetStartUpdate() < piv){ + i++; + event_object_t temp = event_vect[i]; + event_vect[i] = event_vect[j]; + event_vect[j] = temp; + } + } + i++; + event_object_t temp = event_vect[i]; + event_vect[i] = event_vect[end_index]; + event_vect[end_index] = temp; + return i; + } + + // sort events based on start update + void SortEvents(emp::vector & event_vect, int & begin_index, int & end_index){ + // use quick sort method + // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ + + if(begin_index < end_index){ + int pivot_index = SortPartition(event_vect, begin_index, end_index); + SortSingelEvent(event_vect, begin_index, pivot_index - 1); + SortSingleEvent(event_vect, pivot_index + 1, end_index); + } + + } + + // call event_func_t from current_events if possible based on update_indices + void ProcessEvent(const world_t& world){ + // // set update variable to world.GetUpdate() or whaterver gets the world's update + int update = world.GetUpdate(); + + // loop through single time events + for(auto& it = single_event_info.begin(); it != single_event_info.end(); it++){ + if(*it.GetStartUpdate() > update){ + break; + } + if(*it.GetStartUpdate() == update){ + // call event function look at how logic task does it + event_types[*it.GetEventId()].event_function; + // set to is_done true + *it.SetIsDone(); + } + } + + // loop through reoccur time events + for(auto& it = reoccur_event_info.begin(); it != reoccur_event_info.end(); it++){ + if(*it.GetStartUpdate() > update){ + break; + } + if(*it.GetStartUpdate() == update){ + // call event function look at how logictask does it + event_types[*it.GetEventId()].event_function; + // reset start update + int new_start = *it.GetStartUpdate() + *it.GetUpdateStep(); + *it.SetStartUpdate(new_start); + // check if event is done + if (update >= *it.GetEndUpdate() || *it.GetStartUpdate() > *it.GetEndUpdate()){ + *it.SetIsDone(); + } + } + } + + // clean up + DeleteEvents(single_event_info); + DeleteEvents(reoccur_event_info); + // don't need to resort single_event_info at the moment + SortEvents(reoccur_event_info, 0, reoccur_event.size() - 1); + } + + // defined event_types + const std::vector < EventDefinition > + ChangingEventHandler::event_types = { + ChangingEventsHandler::EventDefinition{ + 0, + "task_value_replace", + [](const world_t & world, const event_object_t & event_info)->{ + // TODO: define function + }, + "replace a specific preexisting task value with another value" + }, + ChangingEventsHandler::EventDefinition{ + 1, + "task_value_add", + [](const world_t & world, const event_object_t & event_info)->{ + // TODO: define function + }, + "add a specific amount to the current value of a preexisting task" + }, + ChangingEventsHandler::EventDefinition{ + 2, + "task_value_mul", + [](const world_t & world, const event_object_t & event_info)->{ + // TODO: define function + }, + "multiply a specific amount to the current value of a preexisting task" + } + } +} + +} \ No newline at end of file diff --git a/source/sgp_mode/events/EventTypeDefinition.h b/source/sgp_mode/events/EventTypeDefinition.h new file mode 100644 index 00000000..28c2b04c --- /dev/null +++ b/source/sgp_mode/events/EventTypeDefinition.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Event.h" + +#include "emp/base/Ptr.hpp" + +#include +#include + +namespace sgp_mode { + +// The event type definition contains the information necessary to define a +// type of event: +// - event_id: unique, used to lookup handler function when processing events +// - event_name: unique, human-readable event type name, used to identify event +// in the events file +// - event_function: +template +class EventTypeDefinition { +public: + using event_t = Event; // event class alias + using world_t = WORLD_T; // world type alias + + // Event handler function type + using fun_event_handler_t = std::function /* event being processed */ + )>; + +protected: + // @AML Review: if a variable should never by negative, prefer size_t over int + size_t event_id; // Event definition ID + std::string event_name; // Human-readable event type name + event_handler_func_t event_handler_fun; // Event handler function + std::string description; // Event type description + // TODO: Any other type parameters? + +public: + EventDefinition( + size_t a_event_id, + const std::string& a_event_name, + const fun_event_handler_t& a_event_handler, + const std::string& a_desc + ) : + event_id(a_event_id), + event_name(a_event_name), + event_handler_fun(a_event_handler), + description(a_desc) + { ; } +}; + +} \ No newline at end of file diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h new file mode 100644 index 00000000..4180e8ff --- /dev/null +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -0,0 +1,96 @@ +#pragma once + +#include "EventTypeDefinition.h" + +#include "emp/base/vector.hpp" +#include "emp/datastructs/map_utils.hpp" + +namespace sgp_mode { + +template +class EventDefinitionLibrary { +public: + using world_t = WORLD_T; + using event_type_def_t = EventTypeDefinition; + using fun_event_handler_t = typename event_type_def_t::fun_event_handler_t; +protected: + emp::vector event_definitions; + std::unordered_map event_name_to_id; // event type/name mapped to index of event in event_types. Index is used as event_id + +public: + EventDefinitionLibrary(bool add_default_events=true) { + if (add_default_events) { + AddDefaultEventTypes(); + } + } + + // Clear all event types from the library + void Clear() { + event_definitions.clear(); + } + + // Get event type id using string name + size_t GetEventTypeID(const std::string& event_name) const { + emp_assert(IsValidEventType(event_name)); + return event_name_to_id[event_name]; + } + + // check if event type is valid (aka there is a function in all_event_functions that cna perform the event) + bool IsValidEventType(const std::string& event_name) const { + return emp::Has(event_name_to_id, event_name); + } + + // Add default event types + // NOTE: Called by constructor by default. + // Should not be called a second time without clearing first. + void AddDefaultEventTypes(); + + // Add a new event type to the event library + void AddEventType( + const std::string& event_name, + const fun_event_handler_t& event_handler, + const std::string& event_description = "" + ) { + // Should not be duplicate event type names. + emp_assert(!emp::Has(event_name, event_name_to_id)); + const size_t event_id = event_definitions.size(); + event_definitions.emplace_back( + event_id, + event_name, + event_handler, + event_description + ); + event_name_to_id[event_name] = event_id; + } + +}; + +template +void EventDefinitionLibrary::AddDefaultEventTypes() { + // Add task value replace + AddEventType( + "task_value_replace", + [] (const world_t& world, const Event& event_info) { + // TODO: define function + }, + "replace a specific preexisting task value with another value" + ); + + AddEventType( + "task_value_add", + [] (const world_t& world, const Event& event_info) { + // TODO: define function + }, + "add a specific amount to the current value of a preexisting task" + ); + + AddEventType( + "task_value_mul", + [](const world_t& world, const Event& event_info) { + // TODO: define function + }, + "multiply a specific amount to the current value of a preexisting task" + ); +} + +} \ No newline at end of file diff --git a/source/sgp_mode/events/readme.md b/source/sgp_mode/events/readme.md new file mode 100644 index 00000000..42f1f1d7 --- /dev/null +++ b/source/sgp_mode/events/readme.md @@ -0,0 +1,23 @@ +# Events system notes + +## Organization + +Two layers of organization: + +1. Event types - Classes or types of events (e.g., change task value, add new task, etc). An event type is specified by an instance of the EventTypeDefinition class. +2. Events - Individual instances of a particular type of event. Each of these events corresponds to an event included in the events file that needs to be triggered during the run. + +### EventTypeLibrary + +The EventTypeLibrary manages EventTypeDefinitions. +Every event type is specified by an instance of EventTypeDefinition. +The EventTypeLibrary includes definitions for default event types and allows for runtime specification of additional event types. + +### EventManager + +The EventManager owns an EventTypeLibrary to keep track of valid event types. +The EventManager is responsible for loading a user-defined events file. Each event type in the events file must match a valid event type in the EventTypeLibrary. +For each event in the events file, the EventManager creates an Event object. +The event manager handles triggering events each update. + +The world object owns an event manager, and calls the process events function in the event manager each update. From 48da76e8d78ba8c9e82cdfd497a362759feaf564 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Tue, 9 Jun 2026 16:50:24 -0400 Subject: [PATCH 10/42] Start separating out different event types --- source/json/utils.h | 16 ++ source/sgp_mode/events/Event.h | 141 +++++++++++----- source/sgp_mode/events/EventManager.h | 161 ++++++++++++------- source/sgp_mode/events/EventTypeDefinition.h | 11 +- source/sgp_mode/events/EventTypeLibrary.h | 31 +++- source/sgp_mode/tasks/LogicTaskEnvironment.h | 17 +- 6 files changed, 251 insertions(+), 126 deletions(-) create mode 100644 source/json/utils.h diff --git a/source/json/utils.h b/source/json/utils.h new file mode 100644 index 00000000..6a4514b4 --- /dev/null +++ b/source/json/utils.h @@ -0,0 +1,16 @@ +#pragma once + +namespace sym_json { + +template +RET_TYPE GetVal( + json_t& json, + const std::string& field, + RET_TYPE default_val +) { + return (json.contains(field)) ? + static_cast(json[field]) : + default_val; +} + +} \ No newline at end of file diff --git a/source/sgp_mode/events/Event.h b/source/sgp_mode/events/Event.h index 3219d70a..76a696f0 100644 --- a/source/sgp_mode/events/Event.h +++ b/source/sgp_mode/events/Event.h @@ -15,67 +15,120 @@ namespace sgpmode { +// note: add event timing helper class to manage event timing? + +// helper class to manage event timing +// TODO - write test for event timer helper +class EventTiming { +protected: + size_t start_update; + size_t end_update; + size_t frequency; + size_t next_update; + bool recurring; +public: + + // Constructor for recurring events + EventTiming(size_t start_u, size_t end_u, size_t freq_u) : + start_update(start_u), + end_update(end_u), + frequency(freq_u), + recurring(true) + { + next_update = start_update; + } + + // Constructor for one-time events + EventTiming(size_t start_u) : + start_update(start_u), + end_update((size_t)-1), + frequency((size_t)-1), + recurring(false) + { + next_update = start_update; + } + + size_t GetNextUpdate() const { + return next_update; + } + + void Step() { + next_update += (recurring) ? frequency : 0; + } + +}; + +// Basic event type class Event { protected: - size_t event_id = 0; - std::string event_type{"NULL"}; - std::string task_name{"NULL"}; - double task_value = 0.0; - int start_update = 0; - int end_update = 0; - int update_step = 0; + size_t event_id = 0; + std::string event_type{"NULL"}; + EventTiming timer; + bool is_done = false; + + // emp::vector parameters{}; + // bool reoccuring_event = false; + // bool is_done = false; + + // public: + // // default + // Event()=default; + // // reoccuring events + // Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, + // const double & arg_task_value, const int & arg_start_update, const int & arg_end_update, const int & arg_update_step, + // const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : + // event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), + // start_update(arg_start_update), end_update(arg_end_update), update_step(arg_end_update), + // parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) + // {} + // // one time events + // Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, + // const double & arg_task_value, const int & arg_start_update, + // const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : + // event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), + // start_update(arg_start_update), + // parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) + // {} + + // void SetIsDone(){ is_done = true; } + + // void SetStartUpdate(const int & new_start){ + // start_update = new_start; + // } + + // std::string GetEventType(){ return event_type; } + + // bool GetIsDone(){ return is_done; } - emp::vector parameters{}; - bool reoccuring_event = false; - bool is_done = false; + // double GetTaskValue(){ return task_value; } - public: - // default - Event()=default; - // reoccuring events - Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, - const double & arg_task_value, const int & arg_start_update, const int & arg_end_update, const int & arg_update_step, - const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : - event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), - start_update(arg_start_update), end_update(arg_end_update), update_step(arg_end_update), - parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) - {} - // one time events - Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, - const double & arg_task_value, const int & arg_start_update, - const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : - event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), - start_update(arg_start_update), - parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) - {} + // std::string GetTaskName(){ return task_name; } - void SetIsDone(){ is_done = true; } + // std::string GetOrgMode(){ return org_mode; } - void SetStartUpdate(const int & new_start){ - start_update = new_start; - } + // std::string GetRewardMode(){ return reward_mode; } - std::string GetEventType(){ return event_type; } + // size_t GetID() { return event_id; } - bool GetIsDone(){ return is_done; } + // int GetStartUpdate() { return start_update; } - double GetTaskValue(){ return task_value; } + // int GetEndUpdate() { return end_update; } - std::string GetTaskName(){ return task_name; } + // int GetUpdateStep() { return update_step; } +}; - std::string GetOrgMode(){ return org_mode; } +class ChangeTaskValueEvent : Event { - std::string GetRewardMode(){ return reward_mode; } - size_t GetEventId() { return event_id; } +}; - int GetStartUpdate() { return start_update; } +class ChangeTaskRewardTypeEvent : Event { - int GetEndUpdate() { return end_update; } +}; - int GetUpdateStep() { return update_step; } +class StressEvent : Event { -} +}; } \ No newline at end of file diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 46255f01..38317a91 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -5,21 +5,23 @@ // @AML review: Moved local includes to top for consistency #include "Event.h" #include "EventTypeDefinition.h" +#include "EventTypeLibrary.h" + +#include "../../json/json.hpp" #include "emp/base/vector.hpp" #include "emp/bits/Bits.hpp" #include "emp/tools/string_utils.hpp" #include "emp/datastructs/set_utils.hpp" #include "emp/math/math.hpp" -#include "../../../json/json.hpp" -#include -#include +#include #include #include -#include +#include #include -#include +#include +#include // @AML review: Don't need to namespace the event handler // (unless there's a bunch of internal components that should be isolated @@ -39,62 +41,99 @@ class EventManager { protected: + EventTypeLibrary event_type_library; + emp::vector> one_time_events; // vector of one time events (Event Object type) + emp::vector> recurring_events; // vector of reoccuring events (Event Object type) + + // // create instance of event object using EventObject class, return event object + // event_t CreateEventObject(const size_t event_id, const std::string & event_name, const std::string & task_name, const double & task_value, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ + + // if(reoccur){ + // // slice and convert update indices to individula integers + // std::vector indices_vect; + // emp::slice(update_indices, indices_vect, ":"); + // int start_index = static_cast(indices_vect[0]); + // int end_index = static_cast(indices_vect[1]); + // int step_index = static_cast(indices_vect[2]); + // event.start_update = start_index; + // event.end_update = end_index; + // event.update_step = step_index; + + // event_object_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); + // } + // else { + // int start_index = static_cast(update_indices); + // event_object_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); + // } + // return event; + // } + + // @AML review: renamed, fixed inner loop + bool ValidateFieldsJSON( + const emp::vector& fields, + auto& json_line + ) { + // @AML review: Can use const string reference to avoid copying string here + for (const std::string& name : fields) { + if (!json_line.contains(name)) { + return false; + } + } + return true; + } + emp::Ptr LoadEventFromJSON(nlohmann::json& event_json) { + // Check that event_json has event type + emp_assert(event_json.contains("event_type")); + const std::string event_type(event_json["event_type"]); + // Check if event type is valid (i.e., exists in the event type library) + emp_assert(event_type_library.IsValidEventType(event_type)); + // Delegate event loading based on event type + if (event_type == "task_value_change") { + return LoadChangeTaskValueEventFromJSON(event_json); + } else if (event_type == "task_value_add") { + return LoadChangeTaskValueEventFromJSON(event_json); + } else if (event_type == "task_value_mul") { + return LoadChangeTaskValueEventFromJSON(event_json); + } else { + std::cout << "Unknown event type (" << event_type << ") Exiting." << std::endl; + exit(-1); + } + } + // TODO: make adding new event types as easy / centralized as possible + // i.e., relocate these loaders to centralized location where other + // event type definitions are defined. + emp::Ptr LoadChangeTaskValueEventFromJSON(nlohmann::json& event_json) { + // TODO + } - emp::vector single_event_info; // vector of one time events (Event Object type) - emp::vector reoccur_event_info; // vector of reoccuring events (Event Object type) - // // from LogicTaskEnvironment.h get json field values - // template - // RET_TYPE GetVal( - // json_t& json, - // const std::string& field, - // RET_TYPE default_val - // ) { - // return (json.contains(field)) ? - // static_cast(json[field]) : - // default_val; - // } +// @AML review: missing public designation here +public: - // create instance of event object using EventObject class, return event object - event_object_t CreateEventObject(const size_t event_id, const std::string & event_name, const std::string & task_name, const double & task_value, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ - - if(reoccur){ - // slice and convert update indices to individula integers - std::vector indices_vect; - emp::slice(update_indices, indices_vect, ":"); - int start_index = static_cast(indices_vect[0]); - int end_index = static_cast(indices_vect[1]); - int step_index = static_cast(indices_vect[2]); - event.start_update = start_index; - event.end_update = end_index; - event.update_step = step_index; - - event_object_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); - } - else { - int start_index = static_cast(update_indices); - event_object_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); - } - return event; + ~EventManager() { + ClearEvents(); } - // should i add emp:: to funct? - bool CheckJsonField(const emp::vector & fields, auto& json_line){ - for(std::string name : fields){ - (emp_assert(json_line.contains(name)))? continue : - return false; + // Delete all current events info + void ClearEvents() { + for (emp::Ptr event : one_time_events) { + event.Delete(); + } + one_time_events.clear(); + for (emp::Ptr event : recurring_events) { + event.Delete(); } + recurring_events.clear(); } // load in and process events.json file (includes creating events and checking if they are valid) - void LoadEvents(const std::string& event_filepath){ - // from LogicTaskEnvironment.h - std::cout << "Loading tasks from event file." << std::endl; + void LoadEvents(const std::string& event_filepath) { + std::cout << "Loading events from event file." << std::endl; ClearEvents(); - // === Parse environment file === - // Check if given environment file exists. Exit if not. + // === Parse events file === + // Check if given events file exists. Exit if not. const bool event_file_exists = std::filesystem::exists(event_filepath); if (!event_file_exists) { std::cout << "Event file does not exist: " << event_filepath << std::endl; @@ -103,17 +142,21 @@ class EventManager { // read event.json file std::ifstream event_ifstream(event_filepath); - nlohmann::json eve_json; - event_ifstream >> eve_json; + nlohmann::json events_json; + event_ifstream >> events_json; // check for correct json format - emp::vector event_fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; + // emp::vector event_fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; - emp_assert(eve_json.contains("events")); - for(auto& line; eve_json["events"]){ + emp_assert(events_json.contains("events")); + // For each event line in events + for (auto& event_json; events_json["events"]) { + emp::Ptr new_event_ptr = LoadEventFromJSON(event_json); - CheckJsonFields(event_fields, line); - IsValidEvent(line["event_type"]); + // @AML BOOKMARK + + // CheckJsonFields(event_fields, line); + // IsValidEvent(line["event_type"]); // Is an reoccuring event if(line["reoccuring_event"]){ @@ -152,11 +195,7 @@ class EventManager { return event_types[event_id]; } - // delete all current events info - void ClearEvents(){ - single_event_info.clear(); - reoccur_event_info.clear(); - } + // // delete an event from event info vector (single or reoccur) // void DeleteOneEvent(const bool reoccur, const int & index){ diff --git a/source/sgp_mode/events/EventTypeDefinition.h b/source/sgp_mode/events/EventTypeDefinition.h index 28c2b04c..7ac61b72 100644 --- a/source/sgp_mode/events/EventTypeDefinition.h +++ b/source/sgp_mode/events/EventTypeDefinition.h @@ -15,7 +15,7 @@ namespace sgp_mode { // - event_name: unique, human-readable event type name, used to identify event // in the events file // - event_function: -template +template class EventTypeDefinition { public: using event_t = Event; // event class alias @@ -33,6 +33,7 @@ class EventTypeDefinition { std::string event_name; // Human-readable event type name event_handler_func_t event_handler_fun; // Event handler function std::string description; // Event type description + emp::vector required_fields; // TODO: Any other type parameters? public: @@ -40,13 +41,21 @@ class EventTypeDefinition { size_t a_event_id, const std::string& a_event_name, const fun_event_handler_t& a_event_handler, + const emp::vector& a_required_fields, const std::string& a_desc ) : event_id(a_event_id), event_name(a_event_name), event_handler_fun(a_event_handler), + required_fields(a_required_fields), description(a_desc) { ; } + + // Run event handler function + void Process(world_t& world, emp::Ptr event) { + event_handler_fun(world, event); + } + }; } \ No newline at end of file diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h index 4180e8ff..b16ebcec 100644 --- a/source/sgp_mode/events/EventTypeLibrary.h +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -1,5 +1,6 @@ #pragma once +#include "Event.h" #include "EventTypeDefinition.h" #include "emp/base/vector.hpp" @@ -8,7 +9,7 @@ namespace sgp_mode { template -class EventDefinitionLibrary { +class EventTypeLibrary { public: using world_t = WORLD_T; using event_type_def_t = EventTypeDefinition; @@ -18,7 +19,7 @@ class EventDefinitionLibrary { std::unordered_map event_name_to_id; // event type/name mapped to index of event in event_types. Index is used as event_id public: - EventDefinitionLibrary(bool add_default_events=true) { + EventTypeLibrary(bool add_default_events=true) { if (add_default_events) { AddDefaultEventTypes(); } @@ -40,6 +41,12 @@ class EventDefinitionLibrary { return emp::Has(event_name_to_id, event_name); } + // Run appropriate event handler for given event. + void ProcessEvent(world_t& world, emp::Ptr event_ptr) { + const size_t event_id = event_ptr->GetID(); + event_definitions[event_id].Process(world, event_ptr); + } + // Add default event types // NOTE: Called by constructor by default. // Should not be called a second time without clearing first. @@ -65,30 +72,40 @@ class EventDefinitionLibrary { }; +// TODO - create handler factories? template -void EventDefinitionLibrary::AddDefaultEventTypes() { +void EventTypeLibrary::AddDefaultEventTypes() { // Add task value replace AddEventType( - "task_value_replace", - [] (const world_t& world, const Event& event_info) { + "task_value_change", + [] (world_t& world, emp::Ptr event_ptr) { + // Cast event to correct type + emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); // TODO: define function }, + {"task_name", "value", "task_group", "timing"}, "replace a specific preexisting task value with another value" ); AddEventType( "task_value_add", - [] (const world_t& world, const Event& event_info) { + [] (world_t& world, emp::Ptr event_ptr) { + // Cast event to correct type + emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); // TODO: define function }, + {"task_name", "value", "task_group", "timing"}, "add a specific amount to the current value of a preexisting task" ); AddEventType( "task_value_mul", - [](const world_t& world, const Event& event_info) { + [](world_t& world, emp::Ptr event_ptr) { + // Cast event to correct type + emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); // TODO: define function }, + {"task_name", "value", "task_group", "timing"}, "multiply a specific amount to the current value of a preexisting task" ); } diff --git a/source/sgp_mode/tasks/LogicTaskEnvironment.h b/source/sgp_mode/tasks/LogicTaskEnvironment.h index d5add786..b237a860 100644 --- a/source/sgp_mode/tasks/LogicTaskEnvironment.h +++ b/source/sgp_mode/tasks/LogicTaskEnvironment.h @@ -4,6 +4,7 @@ #include "LogicTaskIOBank.h" #include "../../json/json.hpp" +#include "../../json/utils.h" #include "emp/base/vector.hpp" #include "emp/bits/Bits.hpp" @@ -65,21 +66,11 @@ class LogicTaskEnvironment { // TODO - track task performance? // TODO - move this into util file in json directory - template - RET_TYPE GetVal( - json_t& json, - const std::string& field, - RET_TYPE default_val - ) { - return (json.contains(field)) ? - static_cast(json[field]) : - default_val; - } void SetTaskReqInfo(TaskReqInfo& info, json_t& task_cfg_json) { - info.task_value = GetVal(task_cfg_json, "value", 1); - info.max_repeats = GetVal(task_cfg_json, "max_repeats", std::numeric_limits::max()); - const std::string reward_mode = GetVal(task_cfg_json, "reward_mode", "add"); + info.task_value = sym_json::GetVal(task_cfg_json, "value", 1); + info.max_repeats = sym_json::GetVal(task_cfg_json, "max_repeats", std::numeric_limits::max()); + const std::string reward_mode = sym_json::GetVal(task_cfg_json, "reward_mode", "add"); emp_assert(emp::Has(this_t::predefined_reward_functions, reward_mode)); info.fun_calc_task_val = this_t::predefined_reward_functions.at(reward_mode); } From 1e6d4e09a1d2dce455cbc088b68377ea62b53497 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Wed, 10 Jun 2026 16:20:24 -0400 Subject: [PATCH 11/42] Continue updating event parsing from json --- source/json/utils.h | 22 +- source/sgp_mode/events/Event.h | 214 +++++++++++----- source/sgp_mode/events/EventManager.h | 293 +++++++--------------- source/sgp_mode/events/EventTypeLibrary.h | 79 +++--- 4 files changed, 317 insertions(+), 291 deletions(-) diff --git a/source/json/utils.h b/source/json/utils.h index 6a4514b4..cdf665c8 100644 --- a/source/json/utils.h +++ b/source/json/utils.h @@ -1,10 +1,16 @@ #pragma once +#include "json.hpp" + +#include "emp/base/vector.hpp" + +#include + namespace sym_json { template RET_TYPE GetVal( - json_t& json, + nlohmann::json& json, const std::string& field, RET_TYPE default_val ) { @@ -13,4 +19,18 @@ RET_TYPE GetVal( default_val; } +// @AML review: renamed, fixed inner loop +bool ValidateFieldsJSON( + const nlohmann::json& json_line, + const emp::vector& fields +) { + // @AML review: Can use const string reference to avoid copying string here + for (const std::string& name : fields) { + if (!json_line.contains(name)) { + return false; + } + } + return true; +} + } \ No newline at end of file diff --git a/source/sgp_mode/events/Event.h b/source/sgp_mode/events/Event.h index 76a696f0..c5ba9ba0 100644 --- a/source/sgp_mode/events/Event.h +++ b/source/sgp_mode/events/Event.h @@ -1,17 +1,35 @@ #pragma once +#include "../../json/json.hpp" + +#include "emp/base/Ptr.hpp" #include "emp/base/vector.hpp" #include "emp/bits/Bits.hpp" -#include "emp/tools/string_utils.hpp" #include "emp/datastructs/set_utils.hpp" #include "emp/math/math.hpp" +#include "emp/tools/string_utils.hpp" -#include -#include #include #include -#include +#include #include +#include +#include + +/* + +Event types: +- task_value_change: change the task value of an existing task to a new value + - Parameters: + - task_name: name of task to change + - value: value to change to + - timing: event timing. For one-time event, provide a single number. For a recurring event, provide start:stop:step. + - group: [optional] Which task group to apply change to? shared / symbiont / host +- task_value_add: change the task value of an existing task by adding the given value +- task_value_mul: change the task value of an existing task by multiplying the given value + + +*/ namespace sgpmode { @@ -27,6 +45,7 @@ class EventTiming { size_t next_update; bool recurring; public: + EventTiming() = default; // Constructor for recurring events EventTiming(size_t start_u, size_t end_u, size_t freq_u) : @@ -48,10 +67,29 @@ class EventTiming { next_update = start_update; } - size_t GetNextUpdate() const { - return next_update; + void Reset(size_t start_u) { + start_update = start_u; + end_update = (size_t)-1; + frequency = (size_t)-1; + recurring = false; + next_update = start_update; } + void Reset(size_t start_u, size_t end_u, size_t freq_u) { + emp_assert(start_u <= end_u); + start_update = start_u; + end_update = end_u; + frequency = freq_u; + recurring = true; + next_update = start_update; + } + + size_t GetStartUpdate() const { return start_update; } + size_t GetEndUpdate() const { return end_update; } + size_t GetFrequency() const { return frequency; } + bool IsRecurring() const { return recurring; } + size_t GetNextUpdate() const { return next_update; } + void Step() { next_update += (recurring) ? frequency : 0; } @@ -61,71 +99,133 @@ class EventTiming { // Basic event type class Event { protected: - size_t event_id = 0; std::string event_type{"NULL"}; - EventTiming timer; + EventTiming timing; bool is_done = false; - // emp::vector parameters{}; - // bool reoccuring_event = false; - // bool is_done = false; - - // public: - // // default - // Event()=default; - // // reoccuring events - // Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, - // const double & arg_task_value, const int & arg_start_update, const int & arg_end_update, const int & arg_update_step, - // const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : - // event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), - // start_update(arg_start_update), end_update(arg_end_update), update_step(arg_end_update), - // parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) - // {} - // // one time events - // Event(const size_t arg_event_id, const std::string & arg_event_type, const std::string & arg_task_name, - // const double & arg_task_value, const int & arg_start_update, - // const emp::vector & arg_parameters, const bool & arg_reoccuring_event) : - // event_id(arg_event_id), event_type(arg_event_type), task_name(arg_task_name), task_value(arg_task_value), - // start_update(arg_start_update), - // parameters(arg_parameters), reoccuring_event(arg_reoccuring_event) - // {} - - // void SetIsDone(){ is_done = true; } - - // void SetStartUpdate(const int & new_start){ - // start_update = new_start; - // } - - // std::string GetEventType(){ return event_type; } - - // bool GetIsDone(){ return is_done; } - - // double GetTaskValue(){ return task_value; } - - // std::string GetTaskName(){ return task_name; } - - // std::string GetOrgMode(){ return org_mode; } - - // std::string GetRewardMode(){ return reward_mode; } - - // size_t GetID() { return event_id; } +public: - // int GetStartUpdate() { return start_update; } + size_t GetEventID() const { return event_id; } + void SetEventID(size_t id) { event_id = id; } - // int GetEndUpdate() { return end_update; } + const EventTiming& GetEventTiming() const { return timing; } + bool IsRecurring() const { return timing.IsRecurring(); } + size_t GetStartUpdate() const { return timing.GetStartUpdate(); } + size_t GetEndUpdate() const { return timing.GetEndUpdate(); } + size_t GetNextUpdate() const { return timing.GetNextUpdate(); } - // int GetUpdateStep() { return update_step; } }; -class ChangeTaskValueEvent : Event { +class TaskValueEvent : public Event { +public: + enum class ACTION_TYPE { CHANGE, ADD, MULT }; + enum class TASK_GROUP { SHARED, HOST, SYM }; + static const std::unordered_map valid_action_types{ + {"change", ACTION_TYPE::CHANGE}, + {"add", ACTION_TYPE::ADD}, + {"mult", ACTION_TYPE::MULT} + }; + static const std::unordered_map valid_task_groups{ + {"shared", TASK_GROUP::SHARED}, + {"host", TASK_GROUP::HOST}, + {"symbiont", TASK_GROUP::SYM} + }; + + using json_t = nlohmann::json; + using action_t = ACTION_TYPE; + using task_group_t = TASK_GROUP; + + // TODO: make adding new event types as easy / centralized as possible + // i.e., relocate these loaders to centralized location where other + // event type definitions are defined. + // World is necessary for parsing the task id + template + static emp::Ptr LoadEventFromJSON(json_t& event_json, WORLD_T& world) { + // This should be a task_value event + emp_assert(event_json["event_type"] == "task_value"); + // Task value events must have the following fields: + emp_assert( + sym_json::ValidateFieldsJSON(event_json, {"action", "task_name", "value", "timing"}) + ); + // NOTE: Caller is responsible for event deletion. + emp::Ptr event = emp::NewPtr(); + + // Get event parameters out of json + + // Extract task name(s) + // If multiple tasks are given as an array, accept as vector. + // otherwise, wrap single given task into vector. + const emp::vector task_names = (event_json["task_name"].is_array()) ? event_json["task_name"] : {event_json["task_name"]}; + // Convert task names to task_ids as known by the task set. + const auto& world_task_set = world.GetTaskEnv().GetTaskSet(); + emp::vector task_ids(task_names.size()); + for (size_t task_i = 0; task_i < task_names.size(); ++task_i) { + auto& task_name = task_names[task_i]; + emp_assert(world_task_set.HasTask(task_name)); + task_ids[task_i] = world_task_set.GetID(task_name); + } + + // Set the action + const std::string action_str(event_json["action"]); + emp_assert(emp::Has(valid_action_types, action_str)); + const action_t action = valid_action_types[action_str]; + + // Set the task value + const double task_value = event_json.get("value"); + + // Set the timing + // TODO - move into a function to be shared by other event types + const std::string timing_str(event_json["timing"]); + // EventTiming timing; + // Given as a single (integer) number? + if (emp::is_digits(timing_str)) { + const size_t start_u = emp::from_string(timing_str); + event->timing.Reset(start_u); + } else { + // Timing given as Start:Stop:Step + emp::vector recurring_str = emp::slice(timing_str, ':'); + emp_assert(recurring_str.size() == 3); + const size_t start_u = emp::from_string(recurring_str[0]); + const size_t stop_u = emp::from_string(recurring_str[1]); + const size_t freq = emp::from_string(recurring_str[2]); + event->timing.Reset(start_u, stop_u, freq); + } + + // Is there a group specification? If not, assume shared. + const std::string group_str = (event_json.contains("group")) ? event_json["group"] : "shared"; + emp_assert(emp::Has(valid_task_groups, group_str)); + const task_group_t task_group = valid_task_groups[group_str]; + + // Create new event to pass out of function. + // TODO: incorporate these lines above (i.e., no need to create temp variables) + event->action = action; + event->task_ids = task_ids; + event->task_names = task_names; + event->task_group = task_group; + event->value = task_value; + + return event; + } +protected: + action_t action; // What task value action does this event apply? + emp::vector task_ids; + emp::vector task_names; // Kept mainly for debugging asserts + task_group_t task_group; + double value; + +public: + TaskValueEvent() { + // Set event type in base class. + event_type = "task_value"; + } }; -class ChangeTaskRewardTypeEvent : Event { +// class ChangeTaskRewardTypeEvent : Event { -}; +// }; class StressEvent : Event { diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 38317a91..be7ee29e 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -45,69 +45,28 @@ class EventManager { emp::vector> one_time_events; // vector of one time events (Event Object type) emp::vector> recurring_events; // vector of reoccuring events (Event Object type) - // // create instance of event object using EventObject class, return event object - // event_t CreateEventObject(const size_t event_id, const std::string & event_name, const std::string & task_name, const double & task_value, const std:string & update_indices, const std::vector & parameters, const bool & reocccur){ - - // if(reoccur){ - // // slice and convert update indices to individula integers - // std::vector indices_vect; - // emp::slice(update_indices, indices_vect, ":"); - // int start_index = static_cast(indices_vect[0]); - // int end_index = static_cast(indices_vect[1]); - // int step_index = static_cast(indices_vect[2]); - // event.start_update = start_index; - // event.end_update = end_index; - // event.update_step = step_index; - - // event_object_t event(event_id, event_name, task_name, task_value, start_index, end_index, step_index, parameters, reoccur); - // } - // else { - // int start_index = static_cast(update_indices); - // event_object_t event(event_id, event_name, task_name, task_value, start_index, parameters, reoccur); - // } - // return event; - // } - - // @AML review: renamed, fixed inner loop - bool ValidateFieldsJSON( - const emp::vector& fields, - auto& json_line - ) { - // @AML review: Can use const string reference to avoid copying string here - for (const std::string& name : fields) { - if (!json_line.contains(name)) { - return false; - } - } - return true; - } - emp::Ptr LoadEventFromJSON(nlohmann::json& event_json) { // Check that event_json has event type emp_assert(event_json.contains("event_type")); const std::string event_type(event_json["event_type"]); + emp::Ptr loaded_event; // Check if event type is valid (i.e., exists in the event type library) emp_assert(event_type_library.IsValidEventType(event_type)); // Delegate event loading based on event type - if (event_type == "task_value_change") { - return LoadChangeTaskValueEventFromJSON(event_json); - } else if (event_type == "task_value_add") { - return LoadChangeTaskValueEventFromJSON(event_json); - } else if (event_type == "task_value_mul") { - return LoadChangeTaskValueEventFromJSON(event_json); + if (event_type == "task_value") { + loaded_event = TaskValueEvent::LoadEventFromJSON(event_json); } else { std::cout << "Unknown event type (" << event_type << ") Exiting." << std::endl; exit(-1); } - } - // TODO: make adding new event types as easy / centralized as possible - // i.e., relocate these loaders to centralized location where other - // event type definitions are defined. - emp::Ptr LoadChangeTaskValueEventFromJSON(nlohmann::json& event_json) { - // TODO - } + // Configure loaded event's event_id + emp_assert(event_type == loaded_event->event_type); + const size_t event_id = event_type_library.GetEventTypeID(event_type); + loaded_event->SetEventID(event_id); + return loaded_event; + } // @AML review: missing public designation here public: @@ -116,6 +75,8 @@ class EventManager { ClearEvents(); } + const EventTypeLibrary& GetEventTypeLibrary() const { return event_type_library; } + // Delete all current events info void ClearEvents() { for (emp::Ptr event : one_time_events) { @@ -129,7 +90,7 @@ class EventManager { } // load in and process events.json file (includes creating events and checking if they are valid) - void LoadEvents(const std::string& event_filepath) { + void LoadEventsFromJSON(const std::string& event_filepath) { std::cout << "Loading events from event file." << std::endl; ClearEvents(); // === Parse events file === @@ -139,63 +100,48 @@ class EventManager { std::cout << "Event file does not exist: " << event_filepath << std::endl; std::exit(EXIT_FAILURE); } - // read event.json file std::ifstream event_ifstream(event_filepath); nlohmann::json events_json; event_ifstream >> events_json; - - // check for correct json format - // emp::vector event_fields = {"event_type", "task_name", "task_value", "parameters", "update_indices", "reoccuring_event"}; - emp_assert(events_json.contains("events")); // For each event line in events - for (auto& event_json; events_json["events"]) { + for (auto& event_json : events_json["events"]) { emp::Ptr new_event_ptr = LoadEventFromJSON(event_json); - - // @AML BOOKMARK - - // CheckJsonFields(event_fields, line); - // IsValidEvent(line["event_type"]); - - // Is an reoccuring event - if(line["reoccuring_event"]){ - // set event_id - int event_id = name_to_id[line["event_type"]]; - // create event object - event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); - reoccur_event_info.emplace_back(event_obj); - } - // is a single event - else { - // set event_id - int event_id = name_to_id[line["event_type"]]; - // create event object - event_object_t event_obj = CreateEventObject(event_id, line["event_type"], line["task_name"], line["task_value"], line["update_indices"], line["parameters"], line["reoccuring_event"]); - single_event_info.emplace_back(event_obj); + // Categorize event as recurring or one-time + if (new_event_ptr->IsRecurring()) { + recurring_events.emplace_back(new_event_ptr); + } else { + one_time_events.emplace_back(new_event_ptr); } - } - // sort vectors that hold event objects - SortSingleEvents(); - SortReoccurEvents(); - } - - // return single event info at a specific index - void GetSingleEventInfo(int index){ - return single_event_info[index]; - } - // return reoccuring event info at a specific index - void GetReoccurEventInfo(int index){ - return reoccur_event_info[index]; + // Sort events according to their next update + // TODO - Move into reorder function? + std::sort( + recurring_events.begin(), + recurring_events.end(), + [](emp::Ptr a, emp::Ptr b) { + return a->GetNextUpdate() > b->GetNextUpdate() + } + ); + std::sort( + one_time_events.begin(), + one_time_events.end(), + [](emp::Ptr a, emp::Ptr b) { + return a->GetNextUpdate() > b->GetNextUpdate() + } + ); + } } - // get predefined event function at certain index - void GetEventFunctions(size_t event_id){ - return event_types[event_id]; + void ProcessEvents(world_t& world) { + // Process one-time events + // TODO + // Process recurring events + // TODO } - + // TODO - manual 'AddEvent' // // delete an event from event info vector (single or reoccur) // void DeleteOneEvent(const bool reoccur, const int & index){ @@ -203,118 +149,57 @@ class EventManager { // single_event_info.erase(single_event_info.begin()+index); // } - // Delete all finished events from an event_info vector - void DeleteEvents(const emp::vector & event_vect){ - // erase remove idiom https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom - event_vect.erase(std::remove_if(event_vect.begin(), event_vect.end(), - [](const event_object_t & eve){ return eve.GetIsDone(); }), - event_vect.end()); - } - - // helper function to SortEvents - int SortPartition(emp::vector & event_vect, int & begin_index, int & end_index){ - // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ - int piv = event_vect[end_index].GetStartUpdate(); - int i = begin_index - 1; - for(int j = begin_index; j <= end_index - 1; j++){ - if(event_vect[j].GetStartUpdate() < piv){ - i++; - event_object_t temp = event_vect[i]; - event_vect[i] = event_vect[j]; - event_vect[j] = temp; - } - } - i++; - event_object_t temp = event_vect[i]; - event_vect[i] = event_vect[end_index]; - event_vect[end_index] = temp; - return i; - } - - // sort events based on start update - void SortEvents(emp::vector & event_vect, int & begin_index, int & end_index){ - // use quick sort method - // sources: https://www.youtube.com/watch?v=Vtckgz38QHs, https://www.geeksforgeeks.org/dsa/quick-sort-algorithm/ - - if(begin_index < end_index){ - int pivot_index = SortPartition(event_vect, begin_index, end_index); - SortSingelEvent(event_vect, begin_index, pivot_index - 1); - SortSingleEvent(event_vect, pivot_index + 1, end_index); - } - - } - - // call event_func_t from current_events if possible based on update_indices - void ProcessEvent(const world_t& world){ - // // set update variable to world.GetUpdate() or whaterver gets the world's update - int update = world.GetUpdate(); + // // Delete all finished events from an event_info vector + // void DeleteEvents(const emp::vector & event_vect){ + // // erase remove idiom https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom + // event_vect.erase(std::remove_if(event_vect.begin(), event_vect.end(), + // [](const event_object_t & eve){ return eve.GetIsDone(); }), + // event_vect.end()); + // } - // loop through single time events - for(auto& it = single_event_info.begin(); it != single_event_info.end(); it++){ - if(*it.GetStartUpdate() > update){ - break; - } - if(*it.GetStartUpdate() == update){ - // call event function look at how logic task does it - event_types[*it.GetEventId()].event_function; - // set to is_done true - *it.SetIsDone(); - } - } + // // call event_func_t from current_events if possible based on update_indices + // void ProcessEvent(const world_t& world){ + // // // set update variable to world.GetUpdate() or whaterver gets the world's update + // int update = world.GetUpdate(); + + // // loop through single time events + // for(auto& it = single_event_info.begin(); it != single_event_info.end(); it++){ + // if(*it.GetStartUpdate() > update){ + // break; + // } + // if(*it.GetStartUpdate() == update){ + // // call event function look at how logic task does it + // event_types[*it.GetEventId()].event_function; + // // set to is_done true + // *it.SetIsDone(); + // } + // } - // loop through reoccur time events - for(auto& it = reoccur_event_info.begin(); it != reoccur_event_info.end(); it++){ - if(*it.GetStartUpdate() > update){ - break; - } - if(*it.GetStartUpdate() == update){ - // call event function look at how logictask does it - event_types[*it.GetEventId()].event_function; - // reset start update - int new_start = *it.GetStartUpdate() + *it.GetUpdateStep(); - *it.SetStartUpdate(new_start); - // check if event is done - if (update >= *it.GetEndUpdate() || *it.GetStartUpdate() > *it.GetEndUpdate()){ - *it.SetIsDone(); - } - } - } + // // loop through reoccur time events + // for(auto& it = reoccur_event_info.begin(); it != reoccur_event_info.end(); it++){ + // if(*it.GetStartUpdate() > update){ + // break; + // } + // if(*it.GetStartUpdate() == update){ + // // call event function look at how logictask does it + // event_types[*it.GetEventId()].event_function; + // // reset start update + // int new_start = *it.GetStartUpdate() + *it.GetUpdateStep(); + // *it.SetStartUpdate(new_start); + // // check if event is done + // if (update >= *it.GetEndUpdate() || *it.GetStartUpdate() > *it.GetEndUpdate()){ + // *it.SetIsDone(); + // } + // } + // } - // clean up - DeleteEvents(single_event_info); - DeleteEvents(reoccur_event_info); - // don't need to resort single_event_info at the moment - SortEvents(reoccur_event_info, 0, reoccur_event.size() - 1); - } + // // clean up + // DeleteEvents(single_event_info); + // DeleteEvents(reoccur_event_info); + // // don't need to resort single_event_info at the moment + // SortEvents(reoccur_event_info, 0, reoccur_event.size() - 1); + // } - // defined event_types - const std::vector < EventDefinition > - ChangingEventHandler::event_types = { - ChangingEventsHandler::EventDefinition{ - 0, - "task_value_replace", - [](const world_t & world, const event_object_t & event_info)->{ - // TODO: define function - }, - "replace a specific preexisting task value with another value" - }, - ChangingEventsHandler::EventDefinition{ - 1, - "task_value_add", - [](const world_t & world, const event_object_t & event_info)->{ - // TODO: define function - }, - "add a specific amount to the current value of a preexisting task" - }, - ChangingEventsHandler::EventDefinition{ - 2, - "task_value_mul", - [](const world_t & world, const event_object_t & event_info)->{ - // TODO: define function - }, - "multiply a specific amount to the current value of a preexisting task" - } - } -} +}; } \ No newline at end of file diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h index b16ebcec..1c286a9d 100644 --- a/source/sgp_mode/events/EventTypeLibrary.h +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -70,44 +70,65 @@ class EventTypeLibrary { event_name_to_id[event_name] = event_id; } + template + void AddEventType( + const std::string& event_name, + const std::string& event_description + ) { + AddEventType( + event_name, + [](world_t& world, emp::Ptr event_ptr) { + emp::Ptr event = static_cast(event_ptr.Raw()); + event->Process(world); + }, + event_description + ) + } + }; // TODO - create handler factories? template void EventTypeLibrary::AddDefaultEventTypes() { // Add task value replace - AddEventType( - "task_value_change", - [] (world_t& world, emp::Ptr event_ptr) { - // Cast event to correct type - emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); - // TODO: define function - }, - {"task_name", "value", "task_group", "timing"}, + // TODO - Might be able to compress this into a templated function + // AddEventType( + // "task_value", + // [](world_t& world, emp::Ptr event_ptr) { + // // Cast event to correct type + // emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); + // task_event_ptr->Process(world); + // // TODO: define function + // }, + // "replace a specific preexisting task value with another value" + // ); + + AddEventType( + "task_value", "replace a specific preexisting task value with another value" ); - AddEventType( - "task_value_add", - [] (world_t& world, emp::Ptr event_ptr) { - // Cast event to correct type - emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); - // TODO: define function - }, - {"task_name", "value", "task_group", "timing"}, - "add a specific amount to the current value of a preexisting task" - ); - - AddEventType( - "task_value_mul", - [](world_t& world, emp::Ptr event_ptr) { - // Cast event to correct type - emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); - // TODO: define function - }, - {"task_name", "value", "task_group", "timing"}, - "multiply a specific amount to the current value of a preexisting task" - ); + // AddEventType( + // "task_value_add", + // [] (world_t& world, emp::Ptr event_ptr) { + // // Cast event to correct type + // emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); + // // TODO: define function + // }, + // {"task_name", "value", "task_group", "timing"}, + // "add a specific amount to the current value of a preexisting task" + // ); + + // AddEventType( + // "task_value_mul", + // [](world_t& world, emp::Ptr event_ptr) { + // // Cast event to correct type + // emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); + // // TODO: define function + // }, + // {"task_name", "value", "task_group", "timing"}, + // "multiply a specific amount to the current value of a preexisting task" + // ); } } \ No newline at end of file From 26fd923430cf292e01e5579c2f3b0f167e71be41 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 11 Jun 2026 12:20:10 -0400 Subject: [PATCH 12/42] Fix many compilation errors in current draft of event system --- source/json/{utils.h => json_utils.h} | 11 ++ source/sgp_mode/events/Event.h | 134 +++++++------------ source/sgp_mode/events/EventManager.h | 29 ++-- source/sgp_mode/events/EventTiming.h | 70 ++++++++++ source/sgp_mode/events/EventTypeDefinition.h | 18 ++- source/sgp_mode/events/EventTypeLibrary.h | 55 +++----- source/sgp_mode/events/readme.md | 2 +- source/sgp_mode/tasks/LogicTaskEnvironment.h | 2 +- 8 files changed, 179 insertions(+), 142 deletions(-) rename source/json/{utils.h => json_utils.h} (68%) create mode 100644 source/sgp_mode/events/EventTiming.h diff --git a/source/json/utils.h b/source/json/json_utils.h similarity index 68% rename from source/json/utils.h rename to source/json/json_utils.h index cdf665c8..c7e246cf 100644 --- a/source/json/utils.h +++ b/source/json/json_utils.h @@ -8,6 +8,7 @@ namespace sym_json { +// Get value from json field with default value if field doesn't exist. template RET_TYPE GetVal( nlohmann::json& json, @@ -19,6 +20,16 @@ RET_TYPE GetVal( default_val; } +// Get value from jsonm field. Assumes that given field exists. +template +RET_TYPE GetVal( + nlohmann::json& json, + const std::string& field +) { + emp_assert(json.contains(field)); + return static_cast(json[field]); +} + // @AML review: renamed, fixed inner loop bool ValidateFieldsJSON( const nlohmann::json& json_line, diff --git a/source/sgp_mode/events/Event.h b/source/sgp_mode/events/Event.h index c5ba9ba0..a9183ced 100644 --- a/source/sgp_mode/events/Event.h +++ b/source/sgp_mode/events/Event.h @@ -1,11 +1,15 @@ #pragma once +#include "EventTiming.h" + #include "../../json/json.hpp" +#include "../../json/json_utils.h" #include "emp/base/Ptr.hpp" #include "emp/base/vector.hpp" #include "emp/bits/Bits.hpp" #include "emp/datastructs/set_utils.hpp" +#include "emp/datastructs/map_utils.hpp" #include "emp/math/math.hpp" #include "emp/tools/string_utils.hpp" @@ -33,81 +37,20 @@ Event types: namespace sgpmode { -// note: add event timing helper class to manage event timing? - -// helper class to manage event timing -// TODO - write test for event timer helper -class EventTiming { -protected: - size_t start_update; - size_t end_update; - size_t frequency; - size_t next_update; - bool recurring; -public: - EventTiming() = default; - - // Constructor for recurring events - EventTiming(size_t start_u, size_t end_u, size_t freq_u) : - start_update(start_u), - end_update(end_u), - frequency(freq_u), - recurring(true) - { - next_update = start_update; - } - - // Constructor for one-time events - EventTiming(size_t start_u) : - start_update(start_u), - end_update((size_t)-1), - frequency((size_t)-1), - recurring(false) - { - next_update = start_update; - } - - void Reset(size_t start_u) { - start_update = start_u; - end_update = (size_t)-1; - frequency = (size_t)-1; - recurring = false; - next_update = start_update; - } - - void Reset(size_t start_u, size_t end_u, size_t freq_u) { - emp_assert(start_u <= end_u); - start_update = start_u; - end_update = end_u; - frequency = freq_u; - recurring = true; - next_update = start_update; - } - - size_t GetStartUpdate() const { return start_update; } - size_t GetEndUpdate() const { return end_update; } - size_t GetFrequency() const { return frequency; } - bool IsRecurring() const { return recurring; } - size_t GetNextUpdate() const { return next_update; } - - void Step() { - next_update += (recurring) ? frequency : 0; - } - -}; - // Basic event type class Event { protected: - size_t event_id = 0; + size_t event_type_id = 0; std::string event_type{"NULL"}; EventTiming timing; bool is_done = false; public: - size_t GetEventID() const { return event_id; } - void SetEventID(size_t id) { event_id = id; } + size_t GetEventTypeID() const { return event_type_id; } + void SetEventTypeID(size_t id) { event_type_id = id; } + + const std::string& GetEventType() const { return event_type; } const EventTiming& GetEventTiming() const { return timing; } bool IsRecurring() const { return timing.IsRecurring(); } @@ -121,16 +64,8 @@ class TaskValueEvent : public Event { public: enum class ACTION_TYPE { CHANGE, ADD, MULT }; enum class TASK_GROUP { SHARED, HOST, SYM }; - static const std::unordered_map valid_action_types{ - {"change", ACTION_TYPE::CHANGE}, - {"add", ACTION_TYPE::ADD}, - {"mult", ACTION_TYPE::MULT} - }; - static const std::unordered_map valid_task_groups{ - {"shared", TASK_GROUP::SHARED}, - {"host", TASK_GROUP::HOST}, - {"symbiont", TASK_GROUP::SYM} - }; + static const std::unordered_map valid_action_types; + static const std::unordered_map valid_task_groups; using json_t = nlohmann::json; using action_t = ACTION_TYPE; @@ -144,19 +79,23 @@ class TaskValueEvent : public Event { static emp::Ptr LoadEventFromJSON(json_t& event_json, WORLD_T& world) { // This should be a task_value event emp_assert(event_json["event_type"] == "task_value"); - // Task value events must have the following fields: - emp_assert( - sym_json::ValidateFieldsJSON(event_json, {"action", "task_name", "value", "timing"}) - ); // NOTE: Caller is responsible for event deletion. emp::Ptr event = emp::NewPtr(); // Get event parameters out of json - // Extract task name(s) + // --- Extract task name(s) --- // If multiple tasks are given as an array, accept as vector. // otherwise, wrap single given task into vector. - const emp::vector task_names = (event_json["task_name"].is_array()) ? event_json["task_name"] : {event_json["task_name"]}; + emp::vector task_names; + if (event_json["task_name"].is_array()) { + task_names = event_json["task_name"]; + } else { + const std::string task_name = event_json["task_name"]; + task_names = {task_name}; + } + std::cout << task_names << std::endl; + // Convert task names to task_ids as known by the task set. const auto& world_task_set = world.GetTaskEnv().GetTaskSet(); emp::vector task_ids(task_names.size()); @@ -166,15 +105,16 @@ class TaskValueEvent : public Event { task_ids[task_i] = world_task_set.GetID(task_name); } - // Set the action + // --- Set the action --- const std::string action_str(event_json["action"]); emp_assert(emp::Has(valid_action_types, action_str)); - const action_t action = valid_action_types[action_str]; + const action_t action = valid_action_types.at(action_str); - // Set the task value - const double task_value = event_json.get("value"); + // --- Set the task value --- + const double task_value = sym_json::GetVal(event_json, "value"); + // event_json.get("value"); - // Set the timing + // --- Set the timing --- // TODO - move into a function to be shared by other event types const std::string timing_str(event_json["timing"]); // EventTiming timing; @@ -192,10 +132,11 @@ class TaskValueEvent : public Event { event->timing.Reset(start_u, stop_u, freq); } + // --- Task group --- // Is there a group specification? If not, assume shared. const std::string group_str = (event_json.contains("group")) ? event_json["group"] : "shared"; emp_assert(emp::Has(valid_task_groups, group_str)); - const task_group_t task_group = valid_task_groups[group_str]; + const task_group_t task_group = valid_task_groups.at(group_str); // Create new event to pass out of function. // TODO: incorporate these lines above (i.e., no need to create temp variables) @@ -221,6 +162,23 @@ class TaskValueEvent : public Event { event_type = "task_value"; } + template + void Process(WORLD_T& world) { + // TODO + } +}; + +// TODO - encapsulate all task value event stuff into single file / namespace(?) +const std::unordered_map TaskValueEvent::valid_action_types = { + {"change", ACTION_TYPE::CHANGE}, + {"add", ACTION_TYPE::ADD}, + {"mult", ACTION_TYPE::MULT} + }; + +const std::unordered_map TaskValueEvent::valid_task_groups = { + {"shared", TASK_GROUP::SHARED}, + {"host", TASK_GROUP::HOST}, + {"symbiont", TASK_GROUP::SYM} }; // class ChangeTaskRewardTypeEvent : Event { diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index be7ee29e..9a4cdc55 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -8,6 +8,7 @@ #include "EventTypeLibrary.h" #include "../../json/json.hpp" +#include "../../json/json_utils.h" #include "emp/base/vector.hpp" #include "emp/bits/Bits.hpp" @@ -36,8 +37,8 @@ class EventManager { using json_t = nlohmann::json; // json file type using event_t = Event; // event class alias using world_t = WORLD_T; // world type alias - using event_type_def_t = EvenTypeDefinition; - using event_handler_func_t = typename event_type_def_t::event_handler_func_t; + using event_type_def_t = EventTypeDefinition; + using fun_event_handler_t = typename event_type_def_t::fun_event_handler_t; protected: @@ -45,26 +46,30 @@ class EventManager { emp::vector> one_time_events; // vector of one time events (Event Object type) emp::vector> recurring_events; // vector of reoccuring events (Event Object type) - emp::Ptr LoadEventFromJSON(nlohmann::json& event_json) { + emp::Ptr LoadEventFromJSON(nlohmann::json& event_json, world_t& world) { // Check that event_json has event type emp_assert(event_json.contains("event_type")); const std::string event_type(event_json["event_type"]); emp::Ptr loaded_event; // Check if event type is valid (i.e., exists in the event type library) emp_assert(event_type_library.IsValidEventType(event_type)); + const size_t event_type_id = event_type_library.GetEventTypeID(event_type); + auto& event_type_def = event_type_library.GetEventTypeDefinition(event_type_id); + // Event json must have required fields (as determined by event type definition) + emp_assert( + sym_json::ValidateFieldsJSON(event_json, event_type_def.GetRequiredFields()) + ); // Delegate event loading based on event type if (event_type == "task_value") { - loaded_event = TaskValueEvent::LoadEventFromJSON(event_json); + loaded_event = TaskValueEvent::LoadEventFromJSON(event_json, world); } else { std::cout << "Unknown event type (" << event_type << ") Exiting." << std::endl; exit(-1); } // Configure loaded event's event_id - emp_assert(event_type == loaded_event->event_type); - const size_t event_id = event_type_library.GetEventTypeID(event_type); - loaded_event->SetEventID(event_id); - + emp_assert(event_type == loaded_event->GetEventType()); + loaded_event->SetEventTypeID(event_type_id); return loaded_event; } @@ -90,7 +95,7 @@ class EventManager { } // load in and process events.json file (includes creating events and checking if they are valid) - void LoadEventsFromJSON(const std::string& event_filepath) { + void LoadEventsFromJSON(const std::string& event_filepath, world_t& world) { std::cout << "Loading events from event file." << std::endl; ClearEvents(); // === Parse events file === @@ -107,7 +112,7 @@ class EventManager { emp_assert(events_json.contains("events")); // For each event line in events for (auto& event_json : events_json["events"]) { - emp::Ptr new_event_ptr = LoadEventFromJSON(event_json); + emp::Ptr new_event_ptr = LoadEventFromJSON(event_json, world); // Categorize event as recurring or one-time if (new_event_ptr->IsRecurring()) { recurring_events.emplace_back(new_event_ptr); @@ -121,14 +126,14 @@ class EventManager { recurring_events.begin(), recurring_events.end(), [](emp::Ptr a, emp::Ptr b) { - return a->GetNextUpdate() > b->GetNextUpdate() + return a->GetNextUpdate() > b->GetNextUpdate(); } ); std::sort( one_time_events.begin(), one_time_events.end(), [](emp::Ptr a, emp::Ptr b) { - return a->GetNextUpdate() > b->GetNextUpdate() + return a->GetNextUpdate() > b->GetNextUpdate(); } ); } diff --git a/source/sgp_mode/events/EventTiming.h b/source/sgp_mode/events/EventTiming.h new file mode 100644 index 00000000..3d300a28 --- /dev/null +++ b/source/sgp_mode/events/EventTiming.h @@ -0,0 +1,70 @@ +#pragma once + +#include "emp/base/assert.hpp" + +namespace sgpmode { + +// note: add event timing helper class to manage event timing? + +// Helper class for managing event timing +// TODO - write test for event timer helper +class EventTiming { +protected: + size_t start_update; + size_t end_update; + size_t frequency; + size_t next_update; + bool recurring; +public: + EventTiming() = default; + + // Constructor for recurring events + EventTiming(size_t start_u, size_t end_u, size_t freq_u) : + start_update(start_u), + end_update(end_u), + frequency(freq_u), + recurring(true) + { + next_update = start_update; + } + + // Constructor for one-time events + EventTiming(size_t start_u) : + start_update(start_u), + end_update((size_t)-1), + frequency((size_t)-1), + recurring(false) + { + next_update = start_update; + } + + void Reset(size_t start_u) { + start_update = start_u; + end_update = (size_t)-1; + frequency = (size_t)-1; + recurring = false; + next_update = start_update; + } + + void Reset(size_t start_u, size_t end_u, size_t freq_u) { + // emp_assert(start_u <= end_u); + start_update = start_u; + end_update = end_u; + frequency = freq_u; + recurring = true; + next_update = start_update; + } + + size_t GetStartUpdate() const { return start_update; } + size_t GetEndUpdate() const { return end_update; } + size_t GetFrequency() const { return frequency; } + bool IsRecurring() const { return recurring; } + size_t GetNextUpdate() const { return next_update; } + + void Step() { + next_update += (recurring) ? frequency : 0; + } + +}; + +} \ No newline at end of file diff --git a/source/sgp_mode/events/EventTypeDefinition.h b/source/sgp_mode/events/EventTypeDefinition.h index 7ac61b72..35eac91e 100644 --- a/source/sgp_mode/events/EventTypeDefinition.h +++ b/source/sgp_mode/events/EventTypeDefinition.h @@ -7,7 +7,7 @@ #include #include -namespace sgp_mode { +namespace sgpmode { // The event type definition contains the information necessary to define a // type of event: @@ -31,26 +31,30 @@ class EventTypeDefinition { // @AML Review: if a variable should never by negative, prefer size_t over int size_t event_id; // Event definition ID std::string event_name; // Human-readable event type name - event_handler_func_t event_handler_fun; // Event handler function + fun_event_handler_t event_handler_fun; // Event handler function std::string description; // Event type description emp::vector required_fields; // TODO: Any other type parameters? public: - EventDefinition( + EventTypeDefinition( size_t a_event_id, const std::string& a_event_name, const fun_event_handler_t& a_event_handler, - const emp::vector& a_required_fields, - const std::string& a_desc + const std::string& a_desc, + const emp::vector& a_required_fields ) : event_id(a_event_id), event_name(a_event_name), event_handler_fun(a_event_handler), - required_fields(a_required_fields), - description(a_desc) + description(a_desc), + required_fields(a_required_fields) { ; } + const emp::vector& GetRequiredFields() const { + return required_fields; + } + // Run event handler function void Process(world_t& world, emp::Ptr event) { event_handler_fun(world, event); diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h index 1c286a9d..c0c1bc82 100644 --- a/source/sgp_mode/events/EventTypeLibrary.h +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -6,7 +6,7 @@ #include "emp/base/vector.hpp" #include "emp/datastructs/map_utils.hpp" -namespace sgp_mode { +namespace sgpmode { template class EventTypeLibrary { @@ -15,7 +15,7 @@ class EventTypeLibrary { using event_type_def_t = EventTypeDefinition; using fun_event_handler_t = typename event_type_def_t::fun_event_handler_t; protected: - emp::vector event_definitions; + emp::vector event_definitions; std::unordered_map event_name_to_id; // event type/name mapped to index of event in event_types. Index is used as event_id public: @@ -33,7 +33,12 @@ class EventTypeLibrary { // Get event type id using string name size_t GetEventTypeID(const std::string& event_name) const { emp_assert(IsValidEventType(event_name)); - return event_name_to_id[event_name]; + return event_name_to_id.at(event_name); + } + + const event_type_def_t& GetEventTypeDefinition(size_t event_type_id) const { + emp_assert(event_type_id < event_definitions.size()); + return event_definitions[event_type_id]; } // check if event type is valid (aka there is a function in all_event_functions that cna perform the event) @@ -43,8 +48,8 @@ class EventTypeLibrary { // Run appropriate event handler for given event. void ProcessEvent(world_t& world, emp::Ptr event_ptr) { - const size_t event_id = event_ptr->GetID(); - event_definitions[event_id].Process(world, event_ptr); + const size_t event_type_id = event_ptr->GetEventTypeID(); + event_definitions[event_type_id].Process(world, event_ptr); } // Add default event types @@ -56,16 +61,18 @@ class EventTypeLibrary { void AddEventType( const std::string& event_name, const fun_event_handler_t& event_handler, - const std::string& event_description = "" + const std::string& event_description = "", + const emp::vector& event_required_cfg_fields = {} ) { // Should not be duplicate event type names. - emp_assert(!emp::Has(event_name, event_name_to_id)); + emp_assert(!emp::Has(event_name_to_id, event_name)); const size_t event_id = event_definitions.size(); event_definitions.emplace_back( event_id, event_name, event_handler, - event_description + event_description, + event_required_cfg_fields ); event_name_to_id[event_name] = event_id; } @@ -73,7 +80,8 @@ class EventTypeLibrary { template void AddEventType( const std::string& event_name, - const std::string& event_description + const std::string& event_description = "", + const emp::vector& event_required_cfg_fields = {} ) { AddEventType( event_name, @@ -81,8 +89,9 @@ class EventTypeLibrary { emp::Ptr event = static_cast(event_ptr.Raw()); event->Process(world); }, - event_description - ) + event_description, + event_required_cfg_fields + ); } }; @@ -105,30 +114,10 @@ void EventTypeLibrary::AddDefaultEventTypes() { AddEventType( "task_value", - "replace a specific preexisting task value with another value" + "replace a specific preexisting task value with another value", + {"action", "task_name", "value", "timing"} ); - // AddEventType( - // "task_value_add", - // [] (world_t& world, emp::Ptr event_ptr) { - // // Cast event to correct type - // emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); - // // TODO: define function - // }, - // {"task_name", "value", "task_group", "timing"}, - // "add a specific amount to the current value of a preexisting task" - // ); - - // AddEventType( - // "task_value_mul", - // [](world_t& world, emp::Ptr event_ptr) { - // // Cast event to correct type - // emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); - // // TODO: define function - // }, - // {"task_name", "value", "task_group", "timing"}, - // "multiply a specific amount to the current value of a preexisting task" - // ); } } \ No newline at end of file diff --git a/source/sgp_mode/events/readme.md b/source/sgp_mode/events/readme.md index 42f1f1d7..f0ae8c68 100644 --- a/source/sgp_mode/events/readme.md +++ b/source/sgp_mode/events/readme.md @@ -4,7 +4,7 @@ Two layers of organization: -1. Event types - Classes or types of events (e.g., change task value, add new task, etc). An event type is specified by an instance of the EventTypeDefinition class. +1. Event types - Classes or types of events (e.g., change task value, add new task, volcano, tsunami, acid rain, etc). An event type is specified by an instance of the EventTypeDefinition class. 2. Events - Individual instances of a particular type of event. Each of these events corresponds to an event included in the events file that needs to be triggered during the run. ### EventTypeLibrary diff --git a/source/sgp_mode/tasks/LogicTaskEnvironment.h b/source/sgp_mode/tasks/LogicTaskEnvironment.h index b237a860..3492de17 100644 --- a/source/sgp_mode/tasks/LogicTaskEnvironment.h +++ b/source/sgp_mode/tasks/LogicTaskEnvironment.h @@ -4,7 +4,7 @@ #include "LogicTaskIOBank.h" #include "../../json/json.hpp" -#include "../../json/utils.h" +#include "../../json/json_utils.h" #include "emp/base/vector.hpp" #include "emp/bits/Bits.hpp" From ca6104ea100be6dc0113bdc26bfe78f13654873a Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 11 Jun 2026 13:01:23 -0400 Subject: [PATCH 13/42] Implement one-time event processing function --- source/sgp_mode/events/EventManager.h | 108 +++++++++++++++++++++----- 1 file changed, 87 insertions(+), 21 deletions(-) diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 9a4cdc55..5601d64f 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -73,6 +73,68 @@ class EventManager { return loaded_event; } + void ProcessOneTimeEvents(world_t& world) { + // One-time events are reverse sorted according to next update. + // Sorting should have happened on load (as well as anytime an event was added). + const size_t current_update = world.GetUpdate(); + // Process one-time events + const size_t num_one_time_events = one_time_events.size(); + size_t events_processed = 0; + for (size_t event_i = num_one_time_events - 1; (num_one_time_events > 0) && (event_i >= 0); --event_i) { + emp::Ptr event = one_time_events[event_i]; + const size_t event_update = event->GetNextUpdate(); + // Event's next update should never be less than current update. If so, + // we failed to process it on a previous update. :( + emp_assert(event_update >= current_update); + // If event's next update is bigger than current update, no more events + // to trigger this update. + if (event_update > current_update) { + break; + } + emp_assert(event_update == current_update); + // Process this event + event_type_library.ProcessEvent(world, event); + // One-time event processed. Delete, update # events processed. + event.Delete(); + ++events_processed; + } + // Chop off processed events + one_time_events.resize(num_one_time_events - events_processed); + } + + void ProcessRecurringEvents(world_t& world) { + // TODO + } + + // Re-sort the one-time events. + // One-time events should be reverse-sorted by their next update (i.e., soonest + // next update at end). One-time events must be sorted for processing to be correct. + // i.e., when adding a new event, must resort! + void ReorderOneTimeEvents() { + std::sort( + one_time_events.begin(), + one_time_events.end(), + [](emp::Ptr a, emp::Ptr b) { + return a->GetNextUpdate() > b->GetNextUpdate(); + } + ); + } + + // Reorder recurring events. Should be reverse sorted by each event's next update + // (i.e., soonest next update at end of vector). Recurring events must be sorted + // for processing to be correct. + // I.e., when adding a new event, must sort! And, when re-queueing a recurring + // event, we must make sure it ends up in the appropriate spot by next update. + void ReorderRecurringEvents() { + std::sort( + recurring_events.begin(), + recurring_events.end(), + [](emp::Ptr a, emp::Ptr b) { + return a->GetNextUpdate() > b->GetNextUpdate(); + } + ); + } + // @AML review: missing public designation here public: @@ -119,34 +181,38 @@ class EventManager { } else { one_time_events.emplace_back(new_event_ptr); } - - // Sort events according to their next update - // TODO - Move into reorder function? - std::sort( - recurring_events.begin(), - recurring_events.end(), - [](emp::Ptr a, emp::Ptr b) { - return a->GetNextUpdate() > b->GetNextUpdate(); - } - ); - std::sort( - one_time_events.begin(), - one_time_events.end(), - [](emp::Ptr a, emp::Ptr b) { - return a->GetNextUpdate() > b->GetNextUpdate(); - } - ); } + // Sort events according to their next update + ReorderOneTimeEvents(); + ReorderRecurringEvents(); } void ProcessEvents(world_t& world) { - // Process one-time events - // TODO - // Process recurring events - // TODO + // Get current update in the world. Process all events that should occur on this update. + // Options: + // 1. Maintain unordered list of events. Loop over entire list each update, + // triggering any events where event->NextUpdate() == current update. + // - Pro: no need to maintain sorted order, simple insertion/deletion + // - Con: need to loop over all events no matter what. Could be costly if + // there are many events that occur throughout the run. + // 2. Keep events sorted by their next update to trigger on. Trigger any events + // where event->NextUpdate() == current update. + // - Pro: Very efficient to check if any events need to be triggered. Will + // end up looping over just events that need to be triggered each update. + // - Con: Recurring events need to be resorted if they need to be triggered + // again in the future. + // AML thoughts: Leaning toward option 2. In option 1, we pay the expensive part + // every update. In option 2, we only pay the resorting cost when a recurring + // event triggers (most recurring events will not happen every update). + + // Process all one-time events that need to be triggered this update, then + // process all recurring events that need to be triggered this update. + ProcessOneTimeEvents(world); + ProcessRecurringEvents(world); } // TODO - manual 'AddEvent' + // NOTE - must resort! // // delete an event from event info vector (single or reoccur) // void DeleteOneEvent(const bool reoccur, const int & index){ From 596c718522801b7ec91fa40d972ceeb16b32431e Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 11 Jun 2026 15:40:20 -0400 Subject: [PATCH 14/42] Refactor event processing functions in event manager --- source/sgp_mode/events/Event.h | 9 +- source/sgp_mode/events/EventManager.h | 131 +++++++++++++------------- 2 files changed, 75 insertions(+), 65 deletions(-) diff --git a/source/sgp_mode/events/Event.h b/source/sgp_mode/events/Event.h index a9183ced..56eb5f50 100644 --- a/source/sgp_mode/events/Event.h +++ b/source/sgp_mode/events/Event.h @@ -43,13 +43,15 @@ class Event { size_t event_type_id = 0; std::string event_type{"NULL"}; EventTiming timing; - bool is_done = false; + bool is_done = false; // Can be used by an event to end a recurring event before its end update public: size_t GetEventTypeID() const { return event_type_id; } void SetEventTypeID(size_t id) { event_type_id = id; } + bool IsDone() const { return is_done; } + const std::string& GetEventType() const { return event_type; } const EventTiming& GetEventTiming() const { return timing; } @@ -57,6 +59,11 @@ class Event { size_t GetStartUpdate() const { return timing.GetStartUpdate(); } size_t GetEndUpdate() const { return timing.GetEndUpdate(); } size_t GetNextUpdate() const { return timing.GetNextUpdate(); } + // Advance next update, return new next update. + size_t AdvanceNextUpdate() { + timing.Step(); + return timing.GetNextUpdate(); + } }; diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 5601d64f..8d2a0f2a 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -74,13 +74,16 @@ class EventManager { } void ProcessOneTimeEvents(world_t& world) { + if (one_time_events.empty()) { return; } // One-time events are reverse sorted according to next update. // Sorting should have happened on load (as well as anytime an event was added). const size_t current_update = world.GetUpdate(); // Process one-time events - const size_t num_one_time_events = one_time_events.size(); + const size_t num_events = one_time_events.size(); + // If no events to process, skip. + emp_assert(num_events > 0); size_t events_processed = 0; - for (size_t event_i = num_one_time_events - 1; (num_one_time_events > 0) && (event_i >= 0); --event_i) { + for (size_t event_i = num_events - 1; event_i >= 0; --event_i) { emp::Ptr event = one_time_events[event_i]; const size_t event_update = event->GetNextUpdate(); // Event's next update should never be less than current update. If so, @@ -99,11 +102,69 @@ class EventManager { ++events_processed; } // Chop off processed events - one_time_events.resize(num_one_time_events - events_processed); + emp_assert(events_processed <= num_events); + one_time_events.resize(num_events - events_processed); } void ProcessRecurringEvents(world_t& world) { - // TODO + if (recurring_events.empty()) { return; } + // Recurring events are reverse sorted by the next update they should trigger + // on. + const size_t current_update = world.GetUpdate(); + const size_t num_events = recurring_events.size(); + emp_assert(num_events > 0); + size_t events_deleted = 0; + size_t events_recurred = 0; + for (size_t event_i = num_events - 1; event_i >= 0; --event_i) { + emp::Ptr event = recurring_events[event_i]; + emp_assert(event->IsRecurring()); + const size_t event_update = event->GetNextUpdate(); + // Event's next update should never be less than current update. If so, + // we failed to process it on a previous update. :( + emp_assert(event_update >= current_update); + // If event's next update is bigger than current update, no more events + // to trigger this update. + if (event_update > current_update) { + break; + } + // Otherwise, process this event. + event_type_library.ProcessEvent(world, event); + const size_t next_update = event->AdvanceNextUpdate(); + const size_t end_update = event->GetEndUpdate(); + const bool is_done = event->IsDone(); + if (next_update >= end_update || is_done) { + event.Delete(); + recurring_events[event_i] = nullptr; + ++events_deleted; + // Move deleted events to end of vector. + // - If we haven't recurred any events, either this is the first event + // or all events procssed this update have been deleted. + // - If we have recurred any events, we know that all prior deleted events + // have been swapped to the end of the vector. So, all recurred events + // are in a chunk between this (deleted) event and the rest of the deleted + // events (if any). We want to swap this nullptr with the last recurred + // event in that chunk. + if (events_recurred > 0) { + // There was an event recurred before this, potentially breaking up + // the sequence of deleted events. + // Swap current event with last event in recurred events chunk + emp_assert(recurring_events[event_i + events_recurred] != nullptr); + std::swap( + recurring_events[event_i], + recurring_events[event_i + events_recurred] + ); + } + } else { + // If this event wasn't deleted, it will occur again later. + ++events_recurred; + } + + } + // Resize away the deleted events (that have all been swapped to the end) + recurring_events.resize(num_events - events_deleted); + // Resort! Perfect application for powersort because most events should + // already be sorted... (versus likely std library's introsort) + ReorderRecurringEvents(); } // Re-sort the one-time events. @@ -205,71 +266,13 @@ class EventManager { // every update. In option 2, we only pay the resorting cost when a recurring // event triggers (most recurring events will not happen every update). - // Process all one-time events that need to be triggered this update, then - // process all recurring events that need to be triggered this update. + // Process all one-time events that need to be triggered this update. ProcessOneTimeEvents(world); + // Next, process all recurring events that need to be triggered this update. ProcessRecurringEvents(world); } // TODO - manual 'AddEvent' - // NOTE - must resort! - - // // delete an event from event info vector (single or reoccur) - // void DeleteOneEvent(const bool reoccur, const int & index){ - // (reoccur) ? reoccur_event_info.erase(reoccur_event_info.begin()+index) : - // single_event_info.erase(single_event_info.begin()+index); - // } - - // // Delete all finished events from an event_info vector - // void DeleteEvents(const emp::vector & event_vect){ - // // erase remove idiom https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom - // event_vect.erase(std::remove_if(event_vect.begin(), event_vect.end(), - // [](const event_object_t & eve){ return eve.GetIsDone(); }), - // event_vect.end()); - // } - - // // call event_func_t from current_events if possible based on update_indices - // void ProcessEvent(const world_t& world){ - // // // set update variable to world.GetUpdate() or whaterver gets the world's update - // int update = world.GetUpdate(); - - // // loop through single time events - // for(auto& it = single_event_info.begin(); it != single_event_info.end(); it++){ - // if(*it.GetStartUpdate() > update){ - // break; - // } - // if(*it.GetStartUpdate() == update){ - // // call event function look at how logic task does it - // event_types[*it.GetEventId()].event_function; - // // set to is_done true - // *it.SetIsDone(); - // } - // } - - // // loop through reoccur time events - // for(auto& it = reoccur_event_info.begin(); it != reoccur_event_info.end(); it++){ - // if(*it.GetStartUpdate() > update){ - // break; - // } - // if(*it.GetStartUpdate() == update){ - // // call event function look at how logictask does it - // event_types[*it.GetEventId()].event_function; - // // reset start update - // int new_start = *it.GetStartUpdate() + *it.GetUpdateStep(); - // *it.SetStartUpdate(new_start); - // // check if event is done - // if (update >= *it.GetEndUpdate() || *it.GetStartUpdate() > *it.GetEndUpdate()){ - // *it.SetIsDone(); - // } - // } - // } - - // // clean up - // DeleteEvents(single_event_info); - // DeleteEvents(reoccur_event_info); - // // don't need to resort single_event_info at the moment - // SortEvents(reoccur_event_info, 0, reoccur_event.size() - 1); - // } }; From 7c3f88baff326798f0032ad1a4f74b08e0d0895b Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 11 Jun 2026 16:54:19 -0400 Subject: [PATCH 15/42] Implement process function for TaskValueEvent, make it easier to add new events types to the event library --- source/sgp_mode/events/EventManager.h | 3 +- source/sgp_mode/events/EventTiming.h | 2 +- source/sgp_mode/events/EventTypeDefinition.h | 2 +- source/sgp_mode/events/EventTypeLibrary.h | 41 +- source/sgp_mode/events/event_types/Event.h | 81 ++++ .../events/event_types/ExampleEvent.h | 50 +++ .../{Event.h => event_types/TaskValueEvent.h} | 391 +++++++++--------- 7 files changed, 342 insertions(+), 228 deletions(-) create mode 100644 source/sgp_mode/events/event_types/Event.h create mode 100644 source/sgp_mode/events/event_types/ExampleEvent.h rename source/sgp_mode/events/{Event.h => event_types/TaskValueEvent.h} (60%) diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 8d2a0f2a..089b9d01 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -3,7 +3,7 @@ // @AML review: switch indentation to 2 spaces for consistency // @AML review: Moved local includes to top for consistency -#include "Event.h" +#include "event_types/Event.h" #include "EventTypeDefinition.h" #include "EventTypeLibrary.h" @@ -265,7 +265,6 @@ class EventManager { // AML thoughts: Leaning toward option 2. In option 1, we pay the expensive part // every update. In option 2, we only pay the resorting cost when a recurring // event triggers (most recurring events will not happen every update). - // Process all one-time events that need to be triggered this update. ProcessOneTimeEvents(world); // Next, process all recurring events that need to be triggered this update. diff --git a/source/sgp_mode/events/EventTiming.h b/source/sgp_mode/events/EventTiming.h index 3d300a28..0649b3c7 100644 --- a/source/sgp_mode/events/EventTiming.h +++ b/source/sgp_mode/events/EventTiming.h @@ -47,7 +47,7 @@ class EventTiming { } void Reset(size_t start_u, size_t end_u, size_t freq_u) { - // emp_assert(start_u <= end_u); + emp_assert(start_u <= end_u); start_update = start_u; end_update = end_u; frequency = freq_u; diff --git a/source/sgp_mode/events/EventTypeDefinition.h b/source/sgp_mode/events/EventTypeDefinition.h index 35eac91e..dda3723e 100644 --- a/source/sgp_mode/events/EventTypeDefinition.h +++ b/source/sgp_mode/events/EventTypeDefinition.h @@ -1,6 +1,6 @@ #pragma once -#include "Event.h" +#include "event_types/Event.h" #include "emp/base/Ptr.hpp" diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h index c0c1bc82..639eea71 100644 --- a/source/sgp_mode/events/EventTypeLibrary.h +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -1,6 +1,7 @@ #pragma once -#include "Event.h" +#include "event_types/Event.h" +#include "event_types/TaskValueEvent.h" #include "EventTypeDefinition.h" #include "emp/base/vector.hpp" @@ -55,7 +56,9 @@ class EventTypeLibrary { // Add default event types // NOTE: Called by constructor by default. // Should not be called a second time without clearing first. - void AddDefaultEventTypes(); + void AddDefaultEventTypes() { + AddEventType(); + } // Add a new event type to the event library void AddEventType( @@ -94,30 +97,16 @@ class EventTypeLibrary { ); } -}; + // Add event type assuming EVENT_T has static event spects + a process function + template + void AddEventType() { + AddEventType( + EVENT_T::event_specs.event_type, + EVENT_T::event_specs.event_description, + EVENT_T::event_specs.event_required_fields + ); + } -// TODO - create handler factories? -template -void EventTypeLibrary::AddDefaultEventTypes() { - // Add task value replace - // TODO - Might be able to compress this into a templated function - // AddEventType( - // "task_value", - // [](world_t& world, emp::Ptr event_ptr) { - // // Cast event to correct type - // emp::Ptr task_event_ptr = static_cast(event_ptr.Raw()); - // task_event_ptr->Process(world); - // // TODO: define function - // }, - // "replace a specific preexisting task value with another value" - // ); - - AddEventType( - "task_value", - "replace a specific preexisting task value with another value", - {"action", "task_name", "value", "timing"} - ); - -} +}; } \ No newline at end of file diff --git a/source/sgp_mode/events/event_types/Event.h b/source/sgp_mode/events/event_types/Event.h new file mode 100644 index 00000000..8a6a46c2 --- /dev/null +++ b/source/sgp_mode/events/event_types/Event.h @@ -0,0 +1,81 @@ +#pragma once + +#include "../EventTiming.h" + +#include "../../../json/json.hpp" +#include "../../../json/json_utils.h" + +#include "emp/base/Ptr.hpp" +#include "emp/base/vector.hpp" +#include "emp/bits/Bits.hpp" +#include "emp/datastructs/set_utils.hpp" +#include "emp/datastructs/map_utils.hpp" +#include "emp/math/math.hpp" +#include "emp/tools/string_utils.hpp" + +#include +#include +#include +#include +#include +#include + + +namespace sgpmode { + +// Helper struct used by derived event types. +struct EventTypeDefinitionSpecs { + std::string event_type; + std::string event_description; + emp::vector event_required_fields; + + EventTypeDefinitionSpecs( + const std::string& e_type, + const std::string& e_desc = "", + const emp::vector& e_req_fields = {} + ) : + event_type(e_type), + event_description(e_desc), + event_required_fields(e_req_fields) + { ; } +}; + +// Basic event type +class Event { +protected: + size_t event_type_id = 0; + std::string event_type{"NULL"}; + EventTiming timing; + bool is_done = false; // Can be used by an event to end a recurring event before its end update + +public: + + size_t GetEventTypeID() const { return event_type_id; } + void SetEventTypeID(size_t id) { event_type_id = id; } + + bool IsDone() const { return is_done; } + + const std::string& GetEventType() const { return event_type; } + + const EventTiming& GetEventTiming() const { return timing; } + bool IsRecurring() const { return timing.IsRecurring(); } + size_t GetStartUpdate() const { return timing.GetStartUpdate(); } + size_t GetEndUpdate() const { return timing.GetEndUpdate(); } + size_t GetNextUpdate() const { return timing.GetNextUpdate(); } + // Advance next update, return new next update. + size_t AdvanceNextUpdate() { + timing.Step(); + return timing.GetNextUpdate(); + } + +}; + +// class ChangeTaskRewardTypeEvent : Event { + +// }; + +class StressEvent : Event { + +}; + +} \ No newline at end of file diff --git a/source/sgp_mode/events/event_types/ExampleEvent.h b/source/sgp_mode/events/event_types/ExampleEvent.h new file mode 100644 index 00000000..761a5d58 --- /dev/null +++ b/source/sgp_mode/events/event_types/ExampleEvent.h @@ -0,0 +1,50 @@ +#pragma once + +#include "Event.h" + +namespace sgpmode { + +// Example event class. Used for example purposes only. +class ExampleEvent : public Event { +public: + using json_t = nlohmann::json; + + static const EventTypeDefinitionSpecs event_specs; + + template + static emp::Ptr LoadEventFromJSON( + json_t& event_json, + WORLD_T& world + ) { + // This should be a task_value event + emp_assert(event_json["event_type"] == event_specs.event_type); + // NOTE: Caller is responsible for event deletion. + emp::Ptr event = emp::NewPtr(); + // --- TO FILL IN --- + // Configure new event object based on given event_json + // ------------------ + return event; + } + +protected: +public: + ExampleEvent() { + // Set event type in base class. + event_type = event_specs.event_type; + } + + template + void Process(WORLD_T& world) { + // --- TO FILL IN --- + // Code to process an instance of this event should go here. + // ------------------ + } +}; + +const EventTypeDefinitionSpecs ExampleEvent::event_specs = EventTypeDefinitionSpecs{ + "example_event", + "Example of a minimally defined event type", + {"list", "required", "fields", "for", "this", "event", "here"} +}; + +} \ No newline at end of file diff --git a/source/sgp_mode/events/Event.h b/source/sgp_mode/events/event_types/TaskValueEvent.h similarity index 60% rename from source/sgp_mode/events/Event.h rename to source/sgp_mode/events/event_types/TaskValueEvent.h index 56eb5f50..cfe3b673 100644 --- a/source/sgp_mode/events/Event.h +++ b/source/sgp_mode/events/event_types/TaskValueEvent.h @@ -1,199 +1,194 @@ -#pragma once - -#include "EventTiming.h" - -#include "../../json/json.hpp" -#include "../../json/json_utils.h" - -#include "emp/base/Ptr.hpp" -#include "emp/base/vector.hpp" -#include "emp/bits/Bits.hpp" -#include "emp/datastructs/set_utils.hpp" -#include "emp/datastructs/map_utils.hpp" -#include "emp/math/math.hpp" -#include "emp/tools/string_utils.hpp" - -#include -#include -#include -#include -#include -#include - -/* - -Event types: -- task_value_change: change the task value of an existing task to a new value - - Parameters: - - task_name: name of task to change - - value: value to change to - - timing: event timing. For one-time event, provide a single number. For a recurring event, provide start:stop:step. - - group: [optional] Which task group to apply change to? shared / symbiont / host -- task_value_add: change the task value of an existing task by adding the given value -- task_value_mul: change the task value of an existing task by multiplying the given value - - -*/ - -namespace sgpmode { - -// Basic event type -class Event { -protected: - size_t event_type_id = 0; - std::string event_type{"NULL"}; - EventTiming timing; - bool is_done = false; // Can be used by an event to end a recurring event before its end update - -public: - - size_t GetEventTypeID() const { return event_type_id; } - void SetEventTypeID(size_t id) { event_type_id = id; } - - bool IsDone() const { return is_done; } - - const std::string& GetEventType() const { return event_type; } - - const EventTiming& GetEventTiming() const { return timing; } - bool IsRecurring() const { return timing.IsRecurring(); } - size_t GetStartUpdate() const { return timing.GetStartUpdate(); } - size_t GetEndUpdate() const { return timing.GetEndUpdate(); } - size_t GetNextUpdate() const { return timing.GetNextUpdate(); } - // Advance next update, return new next update. - size_t AdvanceNextUpdate() { - timing.Step(); - return timing.GetNextUpdate(); - } - -}; - -class TaskValueEvent : public Event { -public: - enum class ACTION_TYPE { CHANGE, ADD, MULT }; - enum class TASK_GROUP { SHARED, HOST, SYM }; - static const std::unordered_map valid_action_types; - static const std::unordered_map valid_task_groups; - - using json_t = nlohmann::json; - using action_t = ACTION_TYPE; - using task_group_t = TASK_GROUP; - - // TODO: make adding new event types as easy / centralized as possible - // i.e., relocate these loaders to centralized location where other - // event type definitions are defined. - // World is necessary for parsing the task id - template - static emp::Ptr LoadEventFromJSON(json_t& event_json, WORLD_T& world) { - // This should be a task_value event - emp_assert(event_json["event_type"] == "task_value"); - // NOTE: Caller is responsible for event deletion. - emp::Ptr event = emp::NewPtr(); - - // Get event parameters out of json - - // --- Extract task name(s) --- - // If multiple tasks are given as an array, accept as vector. - // otherwise, wrap single given task into vector. - emp::vector task_names; - if (event_json["task_name"].is_array()) { - task_names = event_json["task_name"]; - } else { - const std::string task_name = event_json["task_name"]; - task_names = {task_name}; - } - std::cout << task_names << std::endl; - - // Convert task names to task_ids as known by the task set. - const auto& world_task_set = world.GetTaskEnv().GetTaskSet(); - emp::vector task_ids(task_names.size()); - for (size_t task_i = 0; task_i < task_names.size(); ++task_i) { - auto& task_name = task_names[task_i]; - emp_assert(world_task_set.HasTask(task_name)); - task_ids[task_i] = world_task_set.GetID(task_name); - } - - // --- Set the action --- - const std::string action_str(event_json["action"]); - emp_assert(emp::Has(valid_action_types, action_str)); - const action_t action = valid_action_types.at(action_str); - - // --- Set the task value --- - const double task_value = sym_json::GetVal(event_json, "value"); - // event_json.get("value"); - - // --- Set the timing --- - // TODO - move into a function to be shared by other event types - const std::string timing_str(event_json["timing"]); - // EventTiming timing; - // Given as a single (integer) number? - if (emp::is_digits(timing_str)) { - const size_t start_u = emp::from_string(timing_str); - event->timing.Reset(start_u); - } else { - // Timing given as Start:Stop:Step - emp::vector recurring_str = emp::slice(timing_str, ':'); - emp_assert(recurring_str.size() == 3); - const size_t start_u = emp::from_string(recurring_str[0]); - const size_t stop_u = emp::from_string(recurring_str[1]); - const size_t freq = emp::from_string(recurring_str[2]); - event->timing.Reset(start_u, stop_u, freq); - } - - // --- Task group --- - // Is there a group specification? If not, assume shared. - const std::string group_str = (event_json.contains("group")) ? event_json["group"] : "shared"; - emp_assert(emp::Has(valid_task_groups, group_str)); - const task_group_t task_group = valid_task_groups.at(group_str); - - // Create new event to pass out of function. - // TODO: incorporate these lines above (i.e., no need to create temp variables) - event->action = action; - event->task_ids = task_ids; - event->task_names = task_names; - event->task_group = task_group; - event->value = task_value; - - return event; - } - -protected: - action_t action; // What task value action does this event apply? - emp::vector task_ids; - emp::vector task_names; // Kept mainly for debugging asserts - task_group_t task_group; - double value; - -public: - TaskValueEvent() { - // Set event type in base class. - event_type = "task_value"; - } - - template - void Process(WORLD_T& world) { - // TODO - } -}; - -// TODO - encapsulate all task value event stuff into single file / namespace(?) -const std::unordered_map TaskValueEvent::valid_action_types = { - {"change", ACTION_TYPE::CHANGE}, - {"add", ACTION_TYPE::ADD}, - {"mult", ACTION_TYPE::MULT} - }; - -const std::unordered_map TaskValueEvent::valid_task_groups = { - {"shared", TASK_GROUP::SHARED}, - {"host", TASK_GROUP::HOST}, - {"symbiont", TASK_GROUP::SYM} -}; - -// class ChangeTaskRewardTypeEvent : Event { - -// }; - -class StressEvent : Event { - -}; - +#pragma once + +#include "Event.h" +#include "../../tasks/LogicTaskEnvironment.h" + +#include "../../../json/json.hpp" +#include "../../../json/json_utils.h" + +#include "emp/base/Ptr.hpp" +#include "emp/base/vector.hpp" +#include "emp/bits/Bits.hpp" +#include "emp/datastructs/set_utils.hpp" +#include "emp/datastructs/map_utils.hpp" +#include "emp/math/math.hpp" +#include "emp/tools/string_utils.hpp" + +#include +#include +#include +#include +#include +#include + +namespace sgpmode { + +/* +- task_value: change the task value of one or more existing tasks to a new value + - Parameters: + - task_name: name of task to change or list of names to change + - value: value to change to + - timing: event timing. For one-time event, provide a single number. For a recurring event, provide start:stop:step. + - group: [optional] Which task group to apply change to? shared / symbiont / host +*/ + +class TaskValueEvent : public Event { +public: + static const EventTypeDefinitionSpecs event_specs; + + enum class ACTION_TYPE { CHANGE, ADD, MULT }; + enum class TASK_GROUP { SHARED, HOST, SYM }; + static const std::unordered_map valid_action_types; + static const std::unordered_map valid_task_groups; + + using json_t = nlohmann::json; + using action_t = ACTION_TYPE; + using task_group_t = TASK_GROUP; + + // World is necessary for parsing the task id + template + static emp::Ptr LoadEventFromJSON(json_t& event_json, WORLD_T& world) { + // This should be a task_value event + emp_assert(event_json["event_type"] == event_specs.event_type); + // NOTE: Caller is responsible for event deletion. + emp::Ptr event = emp::NewPtr(); + + // Get event parameters out of json + + // --- Extract task name(s) --- + // If multiple tasks are given as an array, accept as vector. + // otherwise, wrap single given task into vector. + // emp::vector task_names; + emp::vector& task_names = event->task_names; + if (event_json["task_name"].is_array()) { + task_names = event_json["task_name"]; + } else { + const std::string task_name = event_json["task_name"]; + task_names = {task_name}; + } + + // Convert task names to task_ids as known by the task set. + const auto& world_task_set = world.GetTaskEnv().GetTaskSet(); + // emp::vector task_ids(task_names.size()); + emp::vector& task_ids = event->task_ids; + task_ids.resize(task_names.size()); + for (size_t task_i = 0; task_i < task_names.size(); ++task_i) { + auto& task_name = task_names[task_i]; + emp_assert(world_task_set.HasTask(task_name)); + task_ids[task_i] = world_task_set.GetID(task_name); + } + + // --- Set the action --- + const std::string action_str(event_json["action"]); + emp_assert(emp::Has(valid_action_types, action_str)); + event->action = valid_action_types.at(action_str); + + // --- Set the task value --- + // const double task_value = sym_json::GetVal(event_json, "value"); + event->value = sym_json::GetVal(event_json, "value"); + + // --- Set the timing --- + // TODO - move into a function to be shared by other event types + const std::string timing_str(event_json["timing"]); + // EventTiming timing; + // Given as a single (integer) number? + if (emp::is_digits(timing_str)) { + const size_t start_u = emp::from_string(timing_str); + event->timing.Reset(start_u); + } else { + // Timing given as Start:Stop:Step + emp::vector recurring_str = emp::slice(timing_str, ':'); + emp_assert(recurring_str.size() == 3); + const size_t start_u = emp::from_string(recurring_str[0]); + const size_t stop_u = emp::from_string(recurring_str[1]); + const size_t freq = emp::from_string(recurring_str[2]); + event->timing.Reset(start_u, stop_u, freq); + } + + // --- Task group --- + // Is there a group specification? If not, assume shared. + const std::string group_str = (event_json.contains("group")) ? event_json["group"] : "shared"; + emp_assert(emp::Has(valid_task_groups, group_str)); + // const task_group_t task_group = valid_task_groups.at(group_str); + event->task_group = valid_task_groups.at(group_str); + + // Create new event to pass out of function. + // event->action = action; + // event->task_ids = task_ids; + // event->task_names = task_names; + // event->task_group = task_group; + // event->value = task_value; + + return event; + } + +protected: + action_t action; // What task value action does this event apply? + emp::vector task_ids; + emp::vector task_names; // Kept mainly for debugging asserts + task_group_t task_group; + double value; + + void ApplyAction(tasks::LogicTaskEnvironment::TaskReqInfo& task_req) { + switch (action) { + case action_t::ADD: + task_req.task_value += value; + break; + case action_t::MULT: + task_req.task_value *= value; + break; + case action_t::CHANGE: + task_req.task_value = value; + break; + } + } + +public: + TaskValueEvent() { + // Set event type in base class. + event_type = event_specs.event_type; + } + + template + void Process(WORLD_T& world) { + // For each task_id, apply action + for (size_t task_id : task_ids) { + auto& world_task_env = world.GetTaskEnv(); + switch(task_group) { + case task_group_t::SHARED: + emp_assert(world_task_env.IsHostTask(task_id)); + emp_assert(world_task_env.IsSymTask(task_id)); + ApplyAction(world_task_env.GetHostTaskReq(task_id)); + ApplyAction(world_task_env.GetSymTaskReq(task_id)); + break; + case task_group_t::HOST: + ApplyAction(world_task_env.GetHostTaskReq(task_id)); + break; + case task_group_t::SYM: + ApplyAction(world_task_env.GetSymTaskReq(task_id)); + break; + } + } + } + +}; + +const EventTypeDefinitionSpecs TaskValueEvent::event_specs = EventTypeDefinitionSpecs{ + "task_value", + "Modify the value on a current task", + {"action", "task_name", "value", "timing"} +}; + +const std::unordered_map TaskValueEvent::valid_action_types = { + {"change", ACTION_TYPE::CHANGE}, + {"add", ACTION_TYPE::ADD}, + {"mult", ACTION_TYPE::MULT} +}; + +const std::unordered_map TaskValueEvent::valid_task_groups = { + {"shared", TASK_GROUP::SHARED}, + {"host", TASK_GROUP::HOST}, + {"symbiont", TASK_GROUP::SYM} +}; + } \ No newline at end of file From 62c80b44c11b6b1efeec8f5820a217f80a72a864 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 11 Jun 2026 18:16:17 -0400 Subject: [PATCH 16/42] Integrate events system into world, remove temporary solution to configuring fluctuating environments --- source/sgp_mode/SGPConfigSetup.h | 9 +- source/sgp_mode/SGPWorld.h | 8 ++ source/sgp_mode/SGPWorldSetup.cc | 97 ++----------------- source/sgp_mode/events/EventManager.h | 36 ++++--- source/sgp_mode/events/EventTypeDefinition.h | 15 ++- source/sgp_mode/events/EventTypeLibrary.h | 9 ++ .../events/event_types/TaskValueEvent.h | 2 + 7 files changed, 65 insertions(+), 111 deletions(-) diff --git a/source/sgp_mode/SGPConfigSetup.h b/source/sgp_mode/SGPConfigSetup.h index c447569d..fd4bf2dc 100644 --- a/source/sgp_mode/SGPConfigSetup.h +++ b/source/sgp_mode/SGPConfigSetup.h @@ -68,10 +68,11 @@ EMP_EXTEND_CONFIG(SymConfigSGP, SymConfigBase, VALUE(HOST_ONLY_FIRST_TASK_CREDIT, bool, false, "Only give host credit for one task (whatever they do first)?"), VALUE(SYM_ONLY_FIRST_TASK_CREDIT, bool, false, "Only give sym credit for one task (whatever they do first)?"), - GROUP(TEMP_CHANGING_ENVIRONMENT, "Temporally changing environment settings (task rewards change over time)"), - VALUE(ENABLE_TEMP_CHANGING_ENVIRONMENT, bool, false, "Do task reward values change over time?"), - VALUE(TEMP_CHANGING_ENVIRONMENT_INTERVAL, size_t, 100, "How many updates elapse between task reward value shuffling?"), - VALUE(TEMP_CHANGING_ENVIRONMENT_ORG_TYPE, std::string, "static", "Can organisms sense task reward values? (plastic-both: both symbionts and hosts can sense whether tasks are rewarded; static: neither hosts nor symbiont can sense whether tasks are rewarded)"), + GROUP(EVENTS, "Events settings"), + VALUE(EVENTS_CFG_PATH, std::string, "events.json", "JSON file that provides event configuration"), + + GROUP(INSTRUCTIONS, "Instruction settings"), + VALUE(SENSE_TASK_INSTRUCTION, bool, false, "Should sense task instruction be included in the instruction set?"), GROUP(DATA, "Data settings"), VALUE(PRINT_INTERVAL, size_t, 1, "How often to print run status") diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index 28a59d73..ebf11248 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -14,6 +14,7 @@ #include "hardware/SGPHardwareSpec.h" #include "hardware/GenomeLibrary.h" #include "hardware/SGPHardware.h" +#include "events/EventManager.h" #include "emp/Evolve/World_structure.hpp" #include "emp/data/DataNode.hpp" @@ -49,6 +50,7 @@ class SGPWorld : public SymWorld { using task_io_t = typename task_io_bank_t::TaskIO; using mutator_t = SGPMutator; using sgp_prog_rectifier_t = sgpl::OpCodeRectifier; + using event_manager_t = EventManager; using fun_sym_do_birth_t = std::function, /* symbiont baby ptr */ @@ -397,6 +399,7 @@ class SGPWorld : public SymWorld { ReproductionQueue repro_queue; // Stores which organisms are queued for reproduction ProgramBuilder prog_builder; // Utility for building signalgp programs tasks::LogicTaskEnvironment task_env; // Manages task set, task requirements, and task rewards + event_manager_t event_manager; mutator_t mutator; // Handles mutating sgp programs // TODO - Consider having symbiont rectifier and host rectifier // -> Symbiont-specific instructions wouldn't be in host's instruction set @@ -531,6 +534,7 @@ class SGPWorld : public SymWorld { void SetupHostReproduction(); void SetupHostSymInteractions(); void SetupTaskEnvironment(); + void SetupEvents(); void SetupMutator(); void SetupStressInteractions(); void SetupHealthInteractions(); @@ -646,6 +650,10 @@ class SGPWorld : public SymWorld { */ void Update() override { emp_assert(setup); + // NOTE - When do we want events to occur? Typically, I think we want them + // as the *very* first thing that happens on an update. E.g., changing + // a task value, etc. + event_manager.ProcessEvents(*this); begin_update_sig.Trigger(); // Handle resource inflow // TODO - implement inflow configuration diff --git a/source/sgp_mode/SGPWorldSetup.cc b/source/sgp_mode/SGPWorldSetup.cc index 3281373c..5a3fa311 100644 --- a/source/sgp_mode/SGPWorldSetup.cc +++ b/source/sgp_mode/SGPWorldSetup.cc @@ -47,6 +47,8 @@ void SGPWorld::Setup() { // Configure task environment SetupTaskEnvironment(); + // Configure events + SetupEvents(); // NOTE - Some of this code is repeated from base class. // - Could do some reorganization to copy-paste. E.g., make functions for this, @@ -88,95 +90,6 @@ void SGPWorld::Setup() { ); } - if (sgp_config.ENABLE_TEMP_CHANGING_ENVIRONMENT()) { - // on setup, set NAND, AND-NOT, OR-NOT to be negative (at update zero) - // then during each interval apply *-1 to the changing tasks - - size_t nand_task_id = task_env.GetTaskSet().GetSize(); - if (task_env.GetTaskSet().HasTask("NAND")) { - nand_task_id = task_env.GetTaskSet().GetID("NAND"); - } - else if (task_env.GetTaskSet().HasTask("nand")) { - nand_task_id = task_env.GetTaskSet().GetID("nand"); - } - - size_t andn_task_id = task_env.GetTaskSet().GetSize(); - if (task_env.GetTaskSet().HasTask("AND_NOT")) { - andn_task_id = task_env.GetTaskSet().GetID("AND_NOT"); - } - else if (task_env.GetTaskSet().HasTask("and_not")) { - andn_task_id = task_env.GetTaskSet().GetID("and_not"); - } - - - size_t orn_task_id = task_env.GetTaskSet().GetSize(); - if (task_env.GetTaskSet().HasTask("OR_NOT")) { - orn_task_id = task_env.GetTaskSet().GetID("OR_NOT"); - } - else if (task_env.GetTaskSet().HasTask("or_not")) { - orn_task_id = task_env.GetTaskSet().GetID("or_not"); - } - - - // grab task ids for NOT, AND, OR - size_t not_task_id = task_env.GetTaskSet().GetSize(); - if (task_env.GetTaskSet().HasTask("NOT")) { - not_task_id = task_env.GetTaskSet().GetID("NOT"); - } - else if (task_env.GetTaskSet().HasTask("not")) { - not_task_id = task_env.GetTaskSet().GetID("not"); - } - - size_t and_task_id = task_env.GetTaskSet().GetSize(); - if (task_env.GetTaskSet().HasTask("AND")) { - and_task_id = task_env.GetTaskSet().GetID("AND"); - } - else if (task_env.GetTaskSet().HasTask("and")) { - and_task_id = task_env.GetTaskSet().GetID("and"); - } - - size_t or_task_id = task_env.GetTaskSet().GetSize(); - if (task_env.GetTaskSet().HasTask("OR")) { - or_task_id = task_env.GetTaskSet().GetID("OR"); - } - else if (task_env.GetTaskSet().HasTask("or")) { - or_task_id = task_env.GetTaskSet().GetID("or"); - } - - // update 0 will flip not-and-or to rewarded and nand-andn-orn to punished - GetTaskEnv().GetHostTaskReq(not_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(not_task_id).task_value; - GetTaskEnv().GetSymTaskReq(not_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(not_task_id).task_value; - - GetTaskEnv().GetHostTaskReq(and_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(and_task_id).task_value; - GetTaskEnv().GetSymTaskReq(and_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(and_task_id).task_value; - - GetTaskEnv().GetHostTaskReq(or_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(or_task_id).task_value; - GetTaskEnv().GetSymTaskReq(or_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(or_task_id).task_value; - - begin_update_sig.AddAction( - [this, nand_task_id, andn_task_id, orn_task_id, not_task_id, and_task_id, or_task_id]() { - if (GetUpdate() % sgp_config.TEMP_CHANGING_ENVIRONMENT_INTERVAL() == 0) { - GetTaskEnv().GetHostTaskReq(nand_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(nand_task_id).task_value; - GetTaskEnv().GetHostTaskReq(andn_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(andn_task_id).task_value; - GetTaskEnv().GetHostTaskReq(orn_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(orn_task_id).task_value; - - GetTaskEnv().GetHostTaskReq(not_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(not_task_id).task_value; - GetTaskEnv().GetHostTaskReq(and_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(and_task_id).task_value; - GetTaskEnv().GetHostTaskReq(or_task_id).task_value = -1 * GetTaskEnv().GetHostTaskReq(or_task_id).task_value; - - - GetTaskEnv().GetSymTaskReq(nand_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(nand_task_id).task_value; - GetTaskEnv().GetSymTaskReq(andn_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(andn_task_id).task_value; - GetTaskEnv().GetSymTaskReq(orn_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(orn_task_id).task_value; - - GetTaskEnv().GetSymTaskReq(not_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(not_task_id).task_value; - GetTaskEnv().GetSymTaskReq(and_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(and_task_id).task_value; - GetTaskEnv().GetSymTaskReq(or_task_id).task_value = -1 * GetTaskEnv().GetSymTaskReq(or_task_id).task_value; - } - } - ); - } - SetupHosts(&POP_SIZE); Resize(max_world_size); // TODO - move this back to setup pop structure after fixing setup hosts // NOTE - any way to clean this up a little? Or, add some explanatory comments. @@ -241,7 +154,7 @@ void SGPWorld::SetupOrgMode() { // if temporally changing environment are off, or if organisms aren't allowed to sense their environment, // disable the SenseTask instruction - if (!sgp_config.ENABLE_TEMP_CHANGING_ENVIRONMENT() || sgp_config.TEMP_CHANGING_ENVIRONMENT_ORG_TYPE() == "static") { + if (!sgp_config.SENSE_TASK_INSTRUCTION()) { del_inst( opcode_rectifier.mapper.begin(), opcode_rectifier.mapper.end(), @@ -1293,6 +1206,10 @@ void SGPWorld::SetupTaskEnvironment() { ); } +void SGPWorld::SetupEvents() { + event_manager.LoadEventsFromJSON(sgp_config.EVENTS_CFG_PATH(), *this); +} + void SGPWorld::SetupMutator() { // NOTE - can add more flexibility to mutator mutator.SetPerBitMutationRate(sgp_config.SGP_MUT_PER_BIT_RATE()); diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 089b9d01..98c9a208 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -46,11 +46,11 @@ class EventManager { emp::vector> one_time_events; // vector of one time events (Event Object type) emp::vector> recurring_events; // vector of reoccuring events (Event Object type) - emp::Ptr LoadEventFromJSON(nlohmann::json& event_json, world_t& world) { + emp::Ptr LoadEventFromJSON(json_t& event_json, world_t& world) { // Check that event_json has event type emp_assert(event_json.contains("event_type")); const std::string event_type(event_json["event_type"]); - emp::Ptr loaded_event; + // emp::Ptr loaded_event; // Check if event type is valid (i.e., exists in the event type library) emp_assert(event_type_library.IsValidEventType(event_type)); const size_t event_type_id = event_type_library.GetEventTypeID(event_type); @@ -59,18 +59,20 @@ class EventManager { emp_assert( sym_json::ValidateFieldsJSON(event_json, event_type_def.GetRequiredFields()) ); - // Delegate event loading based on event type - if (event_type == "task_value") { - loaded_event = TaskValueEvent::LoadEventFromJSON(event_json, world); - } else { - std::cout << "Unknown event type (" << event_type << ") Exiting." << std::endl; - exit(-1); - } + return event_type_def.LoadEventFromJSON(event_json, world); + + // // Delegate event loading based on event type + // if (event_type == "task_value") { + // loaded_event = TaskValueEvent::LoadEventFromJSON(event_json, world); + // } else { + // std::cout << "Unknown event type (" << event_type << ") Exiting." << std::endl; + // exit(-1); + // } - // Configure loaded event's event_id - emp_assert(event_type == loaded_event->GetEventType()); - loaded_event->SetEventTypeID(event_type_id); - return loaded_event; + // // Configure loaded event's event_id + // emp_assert(event_type == loaded_event->GetEventType()); + // loaded_event->SetEventTypeID(event_type_id); + // return loaded_event; } void ProcessOneTimeEvents(world_t& world) { @@ -83,7 +85,8 @@ class EventManager { // If no events to process, skip. emp_assert(num_events > 0); size_t events_processed = 0; - for (size_t event_i = num_events - 1; event_i >= 0; --event_i) { + // && event_i < num_events <-- checks for roll over + for (size_t event_i = num_events - 1; event_i >= 0 && event_i < num_events; --event_i) { emp::Ptr event = one_time_events[event_i]; const size_t event_update = event->GetNextUpdate(); // Event's next update should never be less than current update. If so, @@ -115,7 +118,7 @@ class EventManager { emp_assert(num_events > 0); size_t events_deleted = 0; size_t events_recurred = 0; - for (size_t event_i = num_events - 1; event_i >= 0; --event_i) { + for (size_t event_i = num_events - 1; event_i >= 0 && event_i < num_events; --event_i) { emp::Ptr event = recurring_events[event_i]; emp_assert(event->IsRecurring()); const size_t event_update = event->GetNextUpdate(); @@ -127,12 +130,13 @@ class EventManager { if (event_update > current_update) { break; } + std::cout << "Processing a recurring event " << event_i << std::endl; // Otherwise, process this event. event_type_library.ProcessEvent(world, event); const size_t next_update = event->AdvanceNextUpdate(); const size_t end_update = event->GetEndUpdate(); const bool is_done = event->IsDone(); - if (next_update >= end_update || is_done) { + if (next_update > end_update || is_done) { event.Delete(); recurring_events[event_i] = nullptr; ++events_deleted; diff --git a/source/sgp_mode/events/EventTypeDefinition.h b/source/sgp_mode/events/EventTypeDefinition.h index dda3723e..aea514d1 100644 --- a/source/sgp_mode/events/EventTypeDefinition.h +++ b/source/sgp_mode/events/EventTypeDefinition.h @@ -2,6 +2,7 @@ #include "event_types/Event.h" +#include "../../json/json.hpp" #include "emp/base/Ptr.hpp" #include @@ -20,6 +21,7 @@ class EventTypeDefinition { public: using event_t = Event; // event class alias using world_t = WORLD_T; // world type alias + using json_t = nlohmann::json; // json file type // Event handler function type using fun_event_handler_t = std::function /* event being processed */ )>; + using fun_json_loader_t = std::function(json_t&, world_t&)>; + protected: // @AML Review: if a variable should never by negative, prefer size_t over int size_t event_id; // Event definition ID std::string event_name; // Human-readable event type name fun_event_handler_t event_handler_fun; // Event handler function + fun_json_loader_t event_json_loader_fun; std::string description; // Event type description emp::vector required_fields; // TODO: Any other type parameters? @@ -41,12 +46,14 @@ class EventTypeDefinition { size_t a_event_id, const std::string& a_event_name, const fun_event_handler_t& a_event_handler, + const fun_json_loader_t& a_event_json_loader_fun, const std::string& a_desc, const emp::vector& a_required_fields ) : event_id(a_event_id), event_name(a_event_name), event_handler_fun(a_event_handler), + event_json_loader_fun(a_event_json_loader_fun), description(a_desc), required_fields(a_required_fields) { ; } @@ -56,10 +63,16 @@ class EventTypeDefinition { } // Run event handler function - void Process(world_t& world, emp::Ptr event) { + void Process(world_t& world, emp::Ptr event) const { event_handler_fun(world, event); } + emp::Ptr LoadEventFromJSON(json_t& json, world_t& world) const { + emp::Ptr event = event_json_loader_fun(json, world); + event->SetEventTypeID(event_id); + return event; + } + }; } \ No newline at end of file diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h index 639eea71..92a0272e 100644 --- a/source/sgp_mode/events/EventTypeLibrary.h +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -14,7 +14,10 @@ class EventTypeLibrary { public: using world_t = WORLD_T; using event_type_def_t = EventTypeDefinition; + using json_t = nlohmann::json; // json file type using fun_event_handler_t = typename event_type_def_t::fun_event_handler_t; + using fun_json_loader_t = typename event_type_def_t::fun_json_loader_t; + protected: emp::vector event_definitions; std::unordered_map event_name_to_id; // event type/name mapped to index of event in event_types. Index is used as event_id @@ -64,6 +67,7 @@ class EventTypeLibrary { void AddEventType( const std::string& event_name, const fun_event_handler_t& event_handler, + const fun_json_loader_t& event_loader, const std::string& event_description = "", const emp::vector& event_required_cfg_fields = {} ) { @@ -74,6 +78,7 @@ class EventTypeLibrary { event_id, event_name, event_handler, + event_loader, event_description, event_required_cfg_fields ); @@ -92,6 +97,10 @@ class EventTypeLibrary { emp::Ptr event = static_cast(event_ptr.Raw()); event->Process(world); }, + [](json_t& event_json, world_t& world) -> emp::Ptr { + // emp::Ptr event = static_cast(event_ptr.Raw()); + return EVENT_T::LoadEventFromJSON(event_json, world); + }, event_description, event_required_cfg_fields ); diff --git a/source/sgp_mode/events/event_types/TaskValueEvent.h b/source/sgp_mode/events/event_types/TaskValueEvent.h index cfe3b673..d12dba4d 100644 --- a/source/sgp_mode/events/event_types/TaskValueEvent.h +++ b/source/sgp_mode/events/event_types/TaskValueEvent.h @@ -130,6 +130,7 @@ class TaskValueEvent : public Event { double value; void ApplyAction(tasks::LogicTaskEnvironment::TaskReqInfo& task_req) { + std::cout << "Before: " << task_req.task_value; switch (action) { case action_t::ADD: task_req.task_value += value; @@ -141,6 +142,7 @@ class TaskValueEvent : public Event { task_req.task_value = value; break; } + std::cout << " After: " << task_req.task_value << std::endl; } public: From 64128d790c6e8b29429d1cf38b1cf47d6aca0528 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Fri, 12 Jun 2026 09:28:51 -0400 Subject: [PATCH 17/42] Remove debugging print statements --- source/sgp_mode/events/EventManager.h | 2 +- source/sgp_mode/events/event_types/TaskValueEvent.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 98c9a208..93c4a456 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -130,7 +130,7 @@ class EventManager { if (event_update > current_update) { break; } - std::cout << "Processing a recurring event " << event_i << std::endl; + // std::cout << "Processing a recurring event " << event_i << std::endl; // Otherwise, process this event. event_type_library.ProcessEvent(world, event); const size_t next_update = event->AdvanceNextUpdate(); diff --git a/source/sgp_mode/events/event_types/TaskValueEvent.h b/source/sgp_mode/events/event_types/TaskValueEvent.h index d12dba4d..5de04206 100644 --- a/source/sgp_mode/events/event_types/TaskValueEvent.h +++ b/source/sgp_mode/events/event_types/TaskValueEvent.h @@ -130,7 +130,7 @@ class TaskValueEvent : public Event { double value; void ApplyAction(tasks::LogicTaskEnvironment::TaskReqInfo& task_req) { - std::cout << "Before: " << task_req.task_value; + // std::cout << "Before: " << task_req.task_value; switch (action) { case action_t::ADD: task_req.task_value += value; @@ -142,7 +142,7 @@ class TaskValueEvent : public Event { task_req.task_value = value; break; } - std::cout << " After: " << task_req.task_value << std::endl; + // std::cout << " After: " << task_req.task_value << std::endl; } public: From 13ce5f2b1cbffb07ac063743640c659e176fc6fe Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Fri, 12 Jun 2026 09:49:04 -0400 Subject: [PATCH 18/42] Move temp changing environment and sense instruction tests into need updating directory, add stub for TaskValueEvent test file. --- source/catch/main.cc | 5 +-- .../functional_tests/TaskValueEvent.test.cc | 32 +++++++++++++++++++ .../SenseTask_Tasks.test.cc | 0 .../TempChangingEnvironments.test.cc | 0 source/test/sgp_mode_test/test-events.json | 20 ++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc rename source/test/sgp_mode_test/{functional_tests => need_updating}/SenseTask_Tasks.test.cc (100%) rename source/test/sgp_mode_test/{functional_tests => need_updating}/TempChangingEnvironments.test.cc (100%) create mode 100644 source/test/sgp_mode_test/test-events.json diff --git a/source/catch/main.cc b/source/catch/main.cc index 061e1f64..8dc75165 100644 --- a/source/catch/main.cc +++ b/source/catch/main.cc @@ -32,6 +32,7 @@ #include "../test/pgg_mode_test/PGGWorld.test.cc" //SGP mode Old tests (need to update before moving) +#include "../test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc" #include "../test/sgp_mode_test/unit_tests/ProgramBuilder.test.cc" // #include "../test/old_sgp_mode_test/unit_tests/HealthHost.test.cc" // #include "../test/old_sgp_mode_test/unit_tests/SGPSymbiont.test.cc" @@ -72,8 +73,8 @@ #include "../test/sgp_mode_test/unit_tests/utils.test.cc" #include "../test/sgp_mode_test/unit_tests/SGPCureHosts.test.cc" -#include "../test/sgp_mode_test/functional_tests/TempChangingEnvironments.test.cc" -#include "../test/sgp_mode_test/functional_tests/SenseTask_Tasks.test.cc" +// #include "../test/sgp_mode_test/functional_tests/TempChangingEnvironments.test.cc" +// #include "../test/sgp_mode_test/functional_tests/SenseTask_Tasks.test.cc" // Anya's tests #include "../test/sgp_mode_test/unit_tests/SGPWorld.test.cc" diff --git a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc new file mode 100644 index 00000000..e1a0d759 --- /dev/null +++ b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc @@ -0,0 +1,32 @@ +#include "emp/math/Random.hpp" + +#include "../../../sgp_mode/SGPWorld.h" +#include "../../../sgp_mode/SGPWorld.cc" +#include "../../../sgp_mode/SGPWorldSetup.cc" +#include "../../../sgp_mode/SGPWorldData.cc" + +#include "../../../catch/catch.hpp" + +using world_t = sgpmode::SGPWorld; +using cpu_state_t = sgpmode::CPUState; +using hw_spec_t = sgpmode::SGPHardwareSpec; +using hardware_t = sgpmode::SGPHardware; +using program_t = typename world_t::sgp_prog_t; +using sgp_host_t = sgpmode::SGPHost; + +TEST_CASE("TaskValueEvent", "[sgp][events]") { + emp::Random random(2); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/test-events.json"); + // Fill in any generic configs here + + world_t world(random, &config); + world.Setup(); + // world.Resize(2, 2); + + std::cout << "Hello!" << std::endl; + + // TODO + +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/functional_tests/SenseTask_Tasks.test.cc b/source/test/sgp_mode_test/need_updating/SenseTask_Tasks.test.cc similarity index 100% rename from source/test/sgp_mode_test/functional_tests/SenseTask_Tasks.test.cc rename to source/test/sgp_mode_test/need_updating/SenseTask_Tasks.test.cc diff --git a/source/test/sgp_mode_test/functional_tests/TempChangingEnvironments.test.cc b/source/test/sgp_mode_test/need_updating/TempChangingEnvironments.test.cc similarity index 100% rename from source/test/sgp_mode_test/functional_tests/TempChangingEnvironments.test.cc rename to source/test/sgp_mode_test/need_updating/TempChangingEnvironments.test.cc diff --git a/source/test/sgp_mode_test/test-events.json b/source/test/sgp_mode_test/test-events.json new file mode 100644 index 00000000..b0629042 --- /dev/null +++ b/source/test/sgp_mode_test/test-events.json @@ -0,0 +1,20 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "add", + "value": 50, + "timing": "1", + "group": "shared" + }, + { + "event_type": "task_value", + "task_name": ["NAND"], + "action": "mult", + "value": -1, + "timing": "500:10000:1000", + "group": "shared" + } + ] +} \ No newline at end of file From 11b9d83396180dd9a64e20db6cdd1501bfe4a006 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Fri, 12 Jun 2026 09:49:40 -0400 Subject: [PATCH 19/42] Ignore Data/ directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 766aaef2..dfa6741b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ symbulation.test *_output/ output/ local/ +Data/ From 8ed6304fbb4f7c67bb04aed6f0d09b45ad155408 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Fri, 12 Jun 2026 10:45:21 -0400 Subject: [PATCH 20/42] Clean up event system code, implement manual AddEvent functions in EventManager --- source/sgp_mode/events/EventManager.h | 94 ++++++++++++++----- source/sgp_mode/events/EventTiming.h | 1 - source/sgp_mode/events/EventTypeDefinition.h | 4 +- source/sgp_mode/events/event_types/Event.h | 53 +++++++---- .../events/event_types/TaskValueEvent.h | 38 +------- 5 files changed, 113 insertions(+), 77 deletions(-) diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 93c4a456..d7480147 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -29,8 +29,6 @@ // from the rest of the repo) namespace sgpmode { -// TODO: Document event types - template class EventManager { public: @@ -42,11 +40,15 @@ class EventManager { protected: - EventTypeLibrary event_type_library; - emp::vector> one_time_events; // vector of one time events (Event Object type) - emp::vector> recurring_events; // vector of reoccuring events (Event Object type) + EventTypeLibrary event_type_library; // Manages known event type definitions. Must be updated before loading event cfg file. + emp::vector> one_time_events; // Manages all one-off events in reverse sorted order by update to apply. + emp::vector> recurring_events; // Manages all recurring events in reverse sorted order by update to apply. - emp::Ptr LoadEventFromJSON(json_t& event_json, world_t& world) { + // Load a single event from JSON + // Each event type should define it's own loading function (see ExampleEvent), + // so this function grabs the appropriate event type definition and then calls + // that event type's load event function. + emp::Ptr LoadEventFromJSON(json_t& event_json, world_t& world) { // Check that event_json has event type emp_assert(event_json.contains("event_type")); const std::string event_type(event_json["event_type"]); @@ -60,21 +62,12 @@ class EventManager { sym_json::ValidateFieldsJSON(event_json, event_type_def.GetRequiredFields()) ); return event_type_def.LoadEventFromJSON(event_json, world); - - // // Delegate event loading based on event type - // if (event_type == "task_value") { - // loaded_event = TaskValueEvent::LoadEventFromJSON(event_json, world); - // } else { - // std::cout << "Unknown event type (" << event_type << ") Exiting." << std::endl; - // exit(-1); - // } - - // // Configure loaded event's event_id - // emp_assert(event_type == loaded_event->GetEventType()); - // loaded_event->SetEventTypeID(event_type_id); - // return loaded_event; } + // Process all one-type events that should be applied this update. + // After processing, delete event. + // Note: this function assumes that one_time_events is in reverse sorted + // order by next update. void ProcessOneTimeEvents(world_t& world) { if (one_time_events.empty()) { return; } // One-time events are reverse sorted according to next update. @@ -87,7 +80,7 @@ class EventManager { size_t events_processed = 0; // && event_i < num_events <-- checks for roll over for (size_t event_i = num_events - 1; event_i >= 0 && event_i < num_events; --event_i) { - emp::Ptr event = one_time_events[event_i]; + emp::Ptr event = one_time_events[event_i]; const size_t event_update = event->GetNextUpdate(); // Event's next update should never be less than current update. If so, // we failed to process it on a previous update. :( @@ -109,6 +102,11 @@ class EventManager { one_time_events.resize(num_events - events_processed); } + // Process any recurring events that should be applied this update. + // After processing, either delete event (if next update is past end update + // or if event was marked as done by the event's process function). + // Note: this function assumes that recurring_events is in reverse sorted order + // by each event's next update. void ProcessRecurringEvents(world_t& world) { if (recurring_events.empty()) { return; } // Recurring events are reverse sorted by the next update they should trigger @@ -130,7 +128,6 @@ class EventManager { if (event_update > current_update) { break; } - // std::cout << "Processing a recurring event " << event_i << std::endl; // Otherwise, process this event. event_type_library.ProcessEvent(world, event); const size_t next_update = event->AdvanceNextUpdate(); @@ -171,7 +168,7 @@ class EventManager { ReorderRecurringEvents(); } - // Re-sort the one-time events. + // Resort the one-time events. // One-time events should be reverse-sorted by their next update (i.e., soonest // next update at end). One-time events must be sorted for processing to be correct. // i.e., when adding a new event, must resort! @@ -179,7 +176,7 @@ class EventManager { std::sort( one_time_events.begin(), one_time_events.end(), - [](emp::Ptr a, emp::Ptr b) { + [](emp::Ptr a, emp::Ptr b) { return a->GetNextUpdate() > b->GetNextUpdate(); } ); @@ -194,7 +191,7 @@ class EventManager { std::sort( recurring_events.begin(), recurring_events.end(), - [](emp::Ptr a, emp::Ptr b) { + [](emp::Ptr a, emp::Ptr b) { return a->GetNextUpdate() > b->GetNextUpdate(); } ); @@ -275,7 +272,54 @@ class EventManager { ProcessRecurringEvents(world); } - // TODO - manual 'AddEvent' + // Manual add event function. Used when an event that didn't originate from a config + // file needs to be added to the event system. + void AddEvent(emp::Ptr event) { + // Check that this event is who they say it is + const auto& event_type_name = event->GetEventType(); + // Check that incoming event is a known event type + emp_assert(event_type_library.IsValidEventType(event_type_name)); + // Set event's type id + event->SetEventTypeID(event_type_library.GetEventTypeID(event_type_name)); + // Add event to either recurring or one-time event set, then reorder. + if (event->IsRecurring()) { + recurring_events.emplace_back(event); + ReorderRecurringEvents(); + } else { + one_time_events.emplace_back(event); + ReorderOneTimeEvents(); + } + } + + // Add multiple events. Faster to bulk add than add one at a time to save on + // resorting. + void AddEvents(emp::vector> events) { + bool resort_recurring = false; + bool resort_one_time = false; + for (size_t i = 0; i < events.size(); ++i) { + // Check that this event is who they say it is + const auto& event_type_name = events[i]->GetEventType(); + // Check that incoming event is a known event type + emp_assert(event_type_library.IsValidEventType(event_type_name)); + // Set event's type id + events[i]->SetEventTypeID(event_type_library.GetEventTypeID(event_type_name)); + // Add event to either recurring or one-time event set. + if (events[i]->IsRecurring()) { + recurring_events.emplace_back(events[i]); + resort_recurring = true; + } else { + one_time_events.emplace_back(events[i]); + resort_one_time = true; + } + } + // Resort as needed + if (resort_recurring) { + ReorderRecurringEvents(); + } + if (resort_one_time) { + ReorderOneTimeEvents(); + } + } }; diff --git a/source/sgp_mode/events/EventTiming.h b/source/sgp_mode/events/EventTiming.h index 0649b3c7..acc464d4 100644 --- a/source/sgp_mode/events/EventTiming.h +++ b/source/sgp_mode/events/EventTiming.h @@ -7,7 +7,6 @@ namespace sgpmode { // note: add event timing helper class to manage event timing? // Helper class for managing event timing -// TODO - write test for event timer helper class EventTiming { protected: size_t start_update; diff --git a/source/sgp_mode/events/EventTypeDefinition.h b/source/sgp_mode/events/EventTypeDefinition.h index aea514d1..9131754a 100644 --- a/source/sgp_mode/events/EventTypeDefinition.h +++ b/source/sgp_mode/events/EventTypeDefinition.h @@ -15,7 +15,8 @@ namespace sgpmode { // - event_id: unique, used to lookup handler function when processing events // - event_name: unique, human-readable event type name, used to identify event // in the events file -// - event_function: +// - event_handler_fun: Function that handles processing an event of this type +// - event_json_loader_fun: Function that handles loading an event of this type template class EventTypeDefinition { public: @@ -39,7 +40,6 @@ class EventTypeDefinition { fun_json_loader_t event_json_loader_fun; std::string description; // Event type description emp::vector required_fields; - // TODO: Any other type parameters? public: EventTypeDefinition( diff --git a/source/sgp_mode/events/event_types/Event.h b/source/sgp_mode/events/event_types/Event.h index 8a6a46c2..4409bc67 100644 --- a/source/sgp_mode/events/event_types/Event.h +++ b/source/sgp_mode/events/event_types/Event.h @@ -40,28 +40,34 @@ struct EventTypeDefinitionSpecs { { ; } }; -// Basic event type +// Basic event type. Should be used as base class for event types. +// See ExampleEvent.h for a minimal example of how to implement a new event type. class Event { protected: - size_t event_type_id = 0; - std::string event_type{"NULL"}; - EventTiming timing; - bool is_done = false; // Can be used by an event to end a recurring event before its end update + size_t event_type_id = 0; // Event type ID for this event. Filled in when event is loaded from file. + std::string event_type{"NULL"}; // Human-readable event type. + EventTiming timing; // Manages event's timing (one-time vs. recurring start/stop/step) + bool is_done = false; // Can be used by an event to end a recurring event before its end update public: - size_t GetEventTypeID() const { return event_type_id; } void SetEventTypeID(size_t id) { event_type_id = id; } - bool IsDone() const { return is_done; } - const std::string& GetEventType() const { return event_type; } - const EventTiming& GetEventTiming() const { return timing; } bool IsRecurring() const { return timing.IsRecurring(); } size_t GetStartUpdate() const { return timing.GetStartUpdate(); } size_t GetEndUpdate() const { return timing.GetEndUpdate(); } size_t GetNextUpdate() const { return timing.GetNextUpdate(); } + + void ResetTiming(size_t start_update) { + timing.Reset(start_update); + } + + void ResetTiming(size_t start, size_t end, size_t freq) { + timing.Reset(start, end, freq); + } + // Advance next update, return new next update. size_t AdvanceNextUpdate() { timing.Step(); @@ -70,12 +76,27 @@ class Event { }; -// class ChangeTaskRewardTypeEvent : Event { - -// }; - -class StressEvent : Event { - -}; +// Common parsing function used by many event types. +void SetEventTimingFromJSON( + nlohmann::json& event_json, + emp::Ptr event_ptr +) { + emp_assert(event_json.contains("timing")); + const std::string timing_str(event_json["timing"]); + // EventTiming timing; + // Given as a single (integer) number? + if (emp::is_digits(timing_str)) { + const size_t start_u = emp::from_string(timing_str); + event_ptr->ResetTiming(start_u); + } else { + // Timing given as Start:Stop:Step + emp::vector recurring_str = emp::slice(timing_str, ':'); + emp_assert(recurring_str.size() == 3); + const size_t start_u = emp::from_string(recurring_str[0]); + const size_t stop_u = emp::from_string(recurring_str[1]); + const size_t freq = emp::from_string(recurring_str[2]); + event_ptr->ResetTiming(start_u, stop_u, freq); + } +} } \ No newline at end of file diff --git a/source/sgp_mode/events/event_types/TaskValueEvent.h b/source/sgp_mode/events/event_types/TaskValueEvent.h index 5de04206..32d31e6b 100644 --- a/source/sgp_mode/events/event_types/TaskValueEvent.h +++ b/source/sgp_mode/events/event_types/TaskValueEvent.h @@ -58,7 +58,6 @@ class TaskValueEvent : public Event { // --- Extract task name(s) --- // If multiple tasks are given as an array, accept as vector. // otherwise, wrap single given task into vector. - // emp::vector task_names; emp::vector& task_names = event->task_names; if (event_json["task_name"].is_array()) { task_names = event_json["task_name"]; @@ -69,7 +68,6 @@ class TaskValueEvent : public Event { // Convert task names to task_ids as known by the task set. const auto& world_task_set = world.GetTaskEnv().GetTaskSet(); - // emp::vector task_ids(task_names.size()); emp::vector& task_ids = event->task_ids; task_ids.resize(task_names.size()); for (size_t task_i = 0; task_i < task_names.size(); ++task_i) { @@ -84,53 +82,28 @@ class TaskValueEvent : public Event { event->action = valid_action_types.at(action_str); // --- Set the task value --- - // const double task_value = sym_json::GetVal(event_json, "value"); event->value = sym_json::GetVal(event_json, "value"); // --- Set the timing --- - // TODO - move into a function to be shared by other event types - const std::string timing_str(event_json["timing"]); - // EventTiming timing; - // Given as a single (integer) number? - if (emp::is_digits(timing_str)) { - const size_t start_u = emp::from_string(timing_str); - event->timing.Reset(start_u); - } else { - // Timing given as Start:Stop:Step - emp::vector recurring_str = emp::slice(timing_str, ':'); - emp_assert(recurring_str.size() == 3); - const size_t start_u = emp::from_string(recurring_str[0]); - const size_t stop_u = emp::from_string(recurring_str[1]); - const size_t freq = emp::from_string(recurring_str[2]); - event->timing.Reset(start_u, stop_u, freq); - } + SetEventTimingFromJSON(event_json, event); // --- Task group --- // Is there a group specification? If not, assume shared. const std::string group_str = (event_json.contains("group")) ? event_json["group"] : "shared"; emp_assert(emp::Has(valid_task_groups, group_str)); - // const task_group_t task_group = valid_task_groups.at(group_str); event->task_group = valid_task_groups.at(group_str); - // Create new event to pass out of function. - // event->action = action; - // event->task_ids = task_ids; - // event->task_names = task_names; - // event->task_group = task_group; - // event->value = task_value; - return event; } protected: - action_t action; // What task value action does this event apply? - emp::vector task_ids; + action_t action; // What task value action does this event apply? + emp::vector task_ids; // List of task ids that this event applies to emp::vector task_names; // Kept mainly for debugging asserts - task_group_t task_group; - double value; + task_group_t task_group; // Sym / host / shared? + double value; // Event value to be applied to task value void ApplyAction(tasks::LogicTaskEnvironment::TaskReqInfo& task_req) { - // std::cout << "Before: " << task_req.task_value; switch (action) { case action_t::ADD: task_req.task_value += value; @@ -142,7 +115,6 @@ class TaskValueEvent : public Event { task_req.task_value = value; break; } - // std::cout << " After: " << task_req.task_value << std::endl; } public: From 6ad9b16c136c0c507c8172f751fb9c65fe37dd36 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Fri, 12 Jun 2026 12:53:51 -0400 Subject: [PATCH 21/42] Updates during code review --- source/json/json_utils.h | 2 -- source/sgp_mode/events/EventManager.h | 2 ++ source/sgp_mode/events/EventTypeLibrary.h | 1 - source/sgp_mode/events/event_types/TaskValueEvent.h | 8 ++++++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/source/json/json_utils.h b/source/json/json_utils.h index c7e246cf..fcf767f2 100644 --- a/source/json/json_utils.h +++ b/source/json/json_utils.h @@ -30,12 +30,10 @@ RET_TYPE GetVal( return static_cast(json[field]); } -// @AML review: renamed, fixed inner loop bool ValidateFieldsJSON( const nlohmann::json& json_line, const emp::vector& fields ) { - // @AML review: Can use const string reference to avoid copying string here for (const std::string& name : fields) { if (!json_line.contains(name)) { return false; diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index d7480147..f86e9c08 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -78,9 +78,11 @@ class EventManager { // If no events to process, skip. emp_assert(num_events > 0); size_t events_processed = 0; + // && event_i < num_events <-- checks for roll over for (size_t event_i = num_events - 1; event_i >= 0 && event_i < num_events; --event_i) { emp::Ptr event = one_time_events[event_i]; + emp_assert(!event->IsRecurring()); const size_t event_update = event->GetNextUpdate(); // Event's next update should never be less than current update. If so, // we failed to process it on a previous update. :( diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h index 92a0272e..703fe5b7 100644 --- a/source/sgp_mode/events/EventTypeLibrary.h +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -98,7 +98,6 @@ class EventTypeLibrary { event->Process(world); }, [](json_t& event_json, world_t& world) -> emp::Ptr { - // emp::Ptr event = static_cast(event_ptr.Raw()); return EVENT_T::LoadEventFromJSON(event_json, world); }, event_description, diff --git a/source/sgp_mode/events/event_types/TaskValueEvent.h b/source/sgp_mode/events/event_types/TaskValueEvent.h index 32d31e6b..98371099 100644 --- a/source/sgp_mode/events/event_types/TaskValueEvent.h +++ b/source/sgp_mode/events/event_types/TaskValueEvent.h @@ -1,4 +1,6 @@ #pragma once +#ifndef TASK_VALUE_EVENT_H +#define TASK_VALUE_EVENT_H #include "Event.h" #include "../../tasks/LogicTaskEnvironment.h" @@ -126,8 +128,8 @@ class TaskValueEvent : public Event { template void Process(WORLD_T& world) { // For each task_id, apply action + auto& world_task_env = world.GetTaskEnv(); for (size_t task_id : task_ids) { - auto& world_task_env = world.GetTaskEnv(); switch(task_group) { case task_group_t::SHARED: emp_assert(world_task_env.IsHostTask(task_id)); @@ -165,4 +167,6 @@ const std::unordered_map TaskValueEvent {"symbiont", TASK_GROUP::SYM} }; -} \ No newline at end of file +} + +#endif \ No newline at end of file From 91a227690e38d95deb8f3ba925e309548a4b47cd Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Sun, 14 Jun 2026 00:48:08 -0400 Subject: [PATCH 22/42] commiting changes to SGPWorldSetup.cc to pull events files --- source/sgp_mode/SGPWorldSetup.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/sgp_mode/SGPWorldSetup.cc b/source/sgp_mode/SGPWorldSetup.cc index b6f41d3a..0ca87bc0 100644 --- a/source/sgp_mode/SGPWorldSetup.cc +++ b/source/sgp_mode/SGPWorldSetup.cc @@ -6,7 +6,6 @@ #include "utils.h" #include "hardware/SGPHardware.h" #include "sgpl/utility/ThreadLocalRandom.hpp" - #include "emp/datastructs/map_utils.hpp" #include "emp/tools/string_utils.hpp" #include "emp/math/math.hpp" From 3151e528de280eb316e4731d28a0d9ea3a76ad5e Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Thu, 18 Jun 2026 21:56:45 -0400 Subject: [PATCH 23/42] add changes to Makefile --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 9adabebc..c7447f75 100644 --- a/Makefile +++ b/Makefile @@ -158,6 +158,10 @@ test-debug-sgp: $(CXX_nat) $(CFLAGS_nat_debug) $(TEST_DIR)/main.cc -o symbulation.test ./symbulation.test [sgp] || { gdb ./$@.out --ex="catch throw" --ex="set confirm off" --ex="run" --ex="backtrace" --ex="quit"; exit 1; } +test-debug-events: + $(CXX_nat) $(CFLAGS_nat_debug) $(TEST_DIR)/main.cc -o symbulation.test + ./symbulation.test [events] || { gdb ./$@.out --ex="catch throw" --ex="set confirm off" --ex="run" --ex="backtrace" --ex="quit"; exit 1; } + test-executable: $(CXX_nat) $(CFLAGS_nat) $(TEST_DIR)/main.cc -o symbulation.test From 1db449a5baefec20bebadde7b48995ce0f2c8944 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Tue, 23 Jun 2026 09:35:37 -0400 Subject: [PATCH 24/42] TaskValue events tests --- .../functional_tests/TaskValueEvent.test.cc | 411 +++++++++++++++++- .../add-reoccur-events.json | 12 + .../change-reoccur-events.json | 12 + .../hostsym-only-events.json | 20 + .../multiple-tasks-events.json | 12 + .../multiply-reoccur-events.json | 12 + .../task_value_json_files/onetime-events.json | 28 ++ .../task_value_json_files/reoccur-events.json | 20 + .../task_value_json_files/test-events.json | 20 + 9 files changed, 535 insertions(+), 12 deletions(-) create mode 100644 source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json create mode 100644 source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json create mode 100644 source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json create mode 100644 source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json create mode 100644 source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json create mode 100644 source/test/sgp_mode_test/task_value_json_files/onetime-events.json create mode 100644 source/test/sgp_mode_test/task_value_json_files/reoccur-events.json create mode 100644 source/test/sgp_mode_test/task_value_json_files/test-events.json diff --git a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc index e1a0d759..cd349d24 100644 --- a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc +++ b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc @@ -13,20 +13,407 @@ using hw_spec_t = sgpmode::SGPHardwareSpec; using program_t = typename world_t::sgp_prog_t; using sgp_host_t = sgpmode::SGPHost; +using sgp_sym_t = sgpmode::SGPSymbiont; -TEST_CASE("TaskValueEvent", "[sgp][events]") { - emp::Random random(2); - sgpmode::SymConfigSGP config; - config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/test-events.json"); - // Fill in any generic configs here +// write tests hard code for multiple events, one add, one mul, one change in current json file +// check organisms are getting the changes when they perform new task +// write new json to alter multiple tasks +// write new json for host and sym only - world_t world(random, &config); - world.Setup(); - // world.Resize(2, 2); +TEST_CASE("TaskValueEvent One Time Events", "[sgp][events]") { + GIVEN("onetime-events.json"){ + emp::Random random(2); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/onetime-events.json"); - std::cout << "Hello!" << std::endl; + // Fill in any generic configs here + int num_updates = 3; + config.UPDATES(num_updates); + config.SYM_ONLY_FIRST_TASK_CREDIT(1); + config.HOST_ONLY_FIRST_TASK_CREDIT(1); + config.POP_SIZE(1); + config.CYCLES_PER_UPDATE(52); - // TODO + config.HOST_REPRO_RES(10000); + config.SYM_HORIZ_TRANS_RES(10000); + config.SYM_VERT_TRANS_RES(10000); -} \ No newline at end of file + + WHEN("One time add, mul, and change task-value events are loaded"){ + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + + THEN("Events are processed"){ + for(int i = 0; i <= num_updates; i++){ + world.Update(); + switch(i){ + case 0: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + // REQUIRE(symbiont->GetPoints() == 5); + // REQUIRE(host->GetPoints() == 5); + continue; + case 1: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + // CHECK(symbiont->GetPoints() == 20); + // CHECK(host->GetPoints() == 20); + continue; + case 2: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); + // CHECK(symbiont->GetPoints() == 50); + // CHECK(host->GetPoints() == 50); + continue; + case 3: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); + // CHECK(symbiont->GetPoints() == 150); + // CHECK(host->GetPoints() == 150); + continue; + } + } + } + } + } +} + + +TEST_CASE("TaskValueEvent Multiple Reoccuring Events", "[sgp][events]") { + GIVEN("reoccur-events.json"){ + emp::Random random(11); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/reoccur-events.json"); + // Fill in any generic configs here + int num_updates = 6; + config.UPDATES(num_updates); + + WHEN("Reoccuring add and mul task-value events are loaded"){ + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + + THEN("Events are processed"){ + for(int i = 0; i <= num_updates; i++){ + world.Update(); + switch(i){ + case 0: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + continue; + case 1: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); + continue; + case 2: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); + continue; + case 3: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 35); + continue; + case 4: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); + continue; + case 5: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); + continue; + case 6: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); + continue; + } + } + } + } + } +} + +TEST_CASE("TaskValueEvent Reoccuring Multiply Event", "[sgp][events]") { + GIVEN("multiply-reoccur-events.json"){ + emp::Random random(7); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + // Fill in any generic configs here + int num_updates = 5; + config.UPDATES(num_updates); + config.SYM_ONLY_FIRST_TASK_CREDIT(1); + config.HOST_ONLY_FIRST_TASK_CREDIT(1); + config.POP_SIZE(1); + config.CYCLES_PER_UPDATE(52); + + config.HOST_REPRO_RES(10000); + config.SYM_HORIZ_TRANS_RES(10000); + config.SYM_VERT_TRANS_RES(10000); + + WHEN("Reoccuring mul task-value events are loaded"){ + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json"); + + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + + THEN("Events are processed"){ + for(int i = 0; i <= num_updates; i++){ + world.Update(); + switch(i){ + case 0: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + // CHECK(host->GetPoints() == 5); + continue; + case 1: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); + // CHECK(host->GetPoints() == 0); + continue; + case 2: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); + // CHECK(host->GetPoints() == -5); + continue; + case 3: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + // CHECK(host->GetPoints() == 0); + continue; + case 4: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + // CHECK(host->GetPoints() == 5); + continue; + case 5: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + // CHECK(host->GetPoints() == 10); + continue; + } + } + } + } + } +} + +TEST_CASE("TaskValueEvent Reoccuring Add Event", "[sgp][events]") { + GIVEN("add-reoccur-events.json"){ + emp::Random random(44); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json"); + + // Fill in any generic configs here + int num_updates = 3; + config.UPDATES(num_updates); + + WHEN("Reoccuring add task-value events are loaded"){ + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + + THEN("Events are processed"){ + for(int i = 0; i <= num_updates; i++){ + world.Update(); + switch(i){ + case 0: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + continue; + case 1: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); + continue; + case 2: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + continue; + case 3: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + continue; + } + } + } + } + } +} + +TEST_CASE("TaskValueEvent Reoccuring Change Event", "[sgp][events]") { + GIVEN("change-reoccur-events.json"){ + emp::Random random(60); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json"); + + // Fill in any generic configs here + int num_updates = 3; + config.UPDATES(num_updates); + + WHEN("Reoccuring change task-value event is loaded"){ + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + + THEN("Events are processed"){ + for(int i = 0; i <= num_updates; i++){ + world.Update(); + switch(i){ + case 0: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + continue; + case 1: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + continue; + case 2: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + continue; + case 3: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + continue; + } + } + } + } + } +} + +TEST_CASE("TaskValueEvent Host/Sym Only Events", "[sgp][events]") { + GIVEN("hostsym-only-events.json"){ + emp::Random random(19); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json"); + // Fill in any generic configs here + int num_updates = 2; + config.UPDATES(num_updates); + config.SYM_ONLY_FIRST_TASK_CREDIT(1); + config.HOST_ONLY_FIRST_TASK_CREDIT(1); + config.POP_SIZE(1); + config.CYCLES_PER_UPDATE(52); + + config.HOST_REPRO_RES(10000); + config.SYM_HORIZ_TRANS_RES(10000); + config.SYM_VERT_TRANS_RES(10000); + + WHEN("Host only and sym only events are loaded"){ + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + + THEN("Events are processed"){ + for(int i = 0; i <= num_updates; i++){ + world.Update(); + switch(i){ + case 0: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(host->GetPoints() == 5); + REQUIRE(symbiont->GetPoints() == 5); + continue; + case 1: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); + REQUIRE(host->GetPoints() == 55); + REQUIRE(symbiont->GetPoints() == 105); + continue; + case 2: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); + // incorrect output + // CHECK(symbiont->GetPoints() == 205); // 105 + // CHECK(host->GetPoints() == 105); // 55 + continue; + } + } + } + } + } +} + +TEST_CASE("TaskValueEvent Mulitple Task Names Events", "[sgp][events]") { + GIVEN("multiple-tasks-events.json"){ + emp::Random random(1); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json"); + // Fill in any generic configs here + int num_updates = 1; + config.UPDATES(num_updates); + + WHEN("Multiple tasks are in a single event"){ + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + const size_t not_task_id = world.GetTaskEnv().GetTaskSet().GetID("NOT"); + const size_t or_task_id = world.GetTaskEnv().GetTaskSet().GetID("OR"); + const size_t and_task_id = world.GetTaskEnv().GetTaskSet().GetID("AND"); + const size_t xor_task_id = world.GetTaskEnv().GetTaskSet().GetID("XOR"); + const size_t equ_task_id = world.GetTaskEnv().GetTaskSet().GetID("EQU"); + const size_t nor_task_id = world.GetTaskEnv().GetTaskSet().GetID("NOR"); + const size_t andnot_task_id = world.GetTaskEnv().GetTaskSet().GetID("AND_NOT"); + const size_t ornot_task_id = world.GetTaskEnv().GetTaskSet().GetID("OR_NOT"); + + THEN("Events are processed"){ + for(int i = 0; i <= num_updates; i++){ + world.Update(); + switch(i){ + case 0: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 5); + continue; + case 1: + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 100); + continue; + } + } + } + } + } +} diff --git a/source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json b/source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json new file mode 100644 index 00000000..8187df25 --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json @@ -0,0 +1,12 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "add", + "value": 5, + "timing": "1:2:1", + "group": "shared" + } + ] +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json b/source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json new file mode 100644 index 00000000..7ff09afe --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json @@ -0,0 +1,12 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "change", + "value": 25, + "timing": "1:2:1", + "group": "shared" + } + ] +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json b/source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json new file mode 100644 index 00000000..88a25452 --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json @@ -0,0 +1,20 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "change", + "value": 50, + "timing": "1", + "group": "host" + }, + { + "event_type": "task_value", + "task_name": "NAND", + "action": "change", + "value": 100, + "timing": "1", + "group": "symbiont" + } + ] +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json b/source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json new file mode 100644 index 00000000..0a1596be --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json @@ -0,0 +1,12 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": ["NAND", "NOT", "OR", "AND", "XOR", "EQU", "NOR", "AND_NOT", "OR_NOT"], + "action": "change", + "value": 100, + "timing": "1", + "group": "shared" + } + ] +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json b/source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json new file mode 100644 index 00000000..a2a24532 --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json @@ -0,0 +1,12 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "mult", + "value": -1, + "timing": "1:3:2", + "group": "shared" + } + ] +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/task_value_json_files/onetime-events.json b/source/test/sgp_mode_test/task_value_json_files/onetime-events.json new file mode 100644 index 00000000..6c4ba771 --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/onetime-events.json @@ -0,0 +1,28 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "add", + "value": 10, + "timing": "1", + "group": "shared" + }, + { + "event_type": "task_value", + "task_name": "NAND", + "action": "mult", + "value": 2, + "timing": "2", + "group": "shared" + }, + { + "event_type": "task_value", + "task_name": "NAND", + "action": "change", + "value": 100, + "timing": "3", + "group": "shared" + } + ] +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/task_value_json_files/reoccur-events.json b/source/test/sgp_mode_test/task_value_json_files/reoccur-events.json new file mode 100644 index 00000000..a6b38fb8 --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/reoccur-events.json @@ -0,0 +1,20 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "add", + "value": 5, + "timing": "1:3:1", + "group": "shared" + }, + { + "event_type": "task_value", + "task_name": "NAND", + "action": "mult", + "value": 2, + "timing": "2:4:2", + "group": "shared" + } + ] +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/task_value_json_files/test-events.json b/source/test/sgp_mode_test/task_value_json_files/test-events.json new file mode 100644 index 00000000..bdce8810 --- /dev/null +++ b/source/test/sgp_mode_test/task_value_json_files/test-events.json @@ -0,0 +1,20 @@ +{ + "events": [ + { + "event_type": "task_value", + "task_name": "NAND", + "action": "add", + "value": 50, + "timing": "1", + "group": "shared" + }, + { + "event_type": "task_value", + "task_name": ["NAND"], + "action": "mult", + "value": -1, + "timing": "2:5:2", + "group": "shared" + } + ] +} \ No newline at end of file From 7864ddcabfde540be60857b2c5b75dad03916f74 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Tue, 7 Jul 2026 12:43:21 -0400 Subject: [PATCH 25/42] update task-value events tests, ProgramBuilder.h, add event json files --- source/sgp_mode/ProgramBuilder.h | 30 +- .../functional_tests/TaskValueEvent.test.cc | 557 ++++++++++++------ source/test/sgp_mode_test/test-events.json | 20 - 3 files changed, 411 insertions(+), 196 deletions(-) delete mode 100644 source/test/sgp_mode_test/test-events.json diff --git a/source/sgp_mode/ProgramBuilder.h b/source/sgp_mode/ProgramBuilder.h index 2580d87f..1a74e015 100644 --- a/source/sgp_mode/ProgramBuilder.h +++ b/source/sgp_mode/ProgramBuilder.h @@ -364,9 +364,33 @@ class ProgramBuilder { start_tag ); // Add not instruction - AddInst(program, io_op); - AddInst(program, io_op, 1); - AddTask_Nand(program); + AddInst(program, io_op); // reg 0 = input a + AddInst(program, io_op, 1); // reg 1 = input b + AddTask_Nand(program); // reg 0 = a NAND b + + // fixing taskvalue error + AddInst(program, io_op); // reg 0 = c + AddInst(program, nand_op, 0, 1, 1); // reg 1 = c NAND b + + AddInst(program, io_op, 1); // reg 1 = d + AddInst(program, nand_op, 0, 1, 0); // reg 0 = c NAND d + + AddInst(program, io_op); // reg 0 = a + AddInst(program, nand_op, 0, 1, 1); // reg 0 = a NAND d + + + // AddInst(program, io_op); // also output a NAND b, reg 0 = c + + // AddInst(program, nand_op, 0, 1, 1); // reg 1 = b NAND c + + // AddInst(program, io_op, 1); // reg 1 = d + + // AddInst(program, nand_op, 0, 1, 0); // reg 0 = c NAND d + + // // AddInst(program, io_op, 0); // reg 0 = a (reg 1 = d) + + // // AddInst(program, nand_op, 0, 1, 1); // reg 0 = a NAND d + // Nop filler is length minus current size + repro instructions // const size_t nop_filler = length - (program.size() + 1); program.resize(length - 1); diff --git a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc index cd349d24..bbb27c31 100644 --- a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc +++ b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc @@ -15,10 +15,6 @@ using program_t = typename world_t::sgp_prog_t; using sgp_host_t = sgpmode::SGPHost; using sgp_sym_t = sgpmode::SGPSymbiont; -// write tests hard code for multiple events, one add, one mul, one change in current json file -// check organisms are getting the changes when they perform new task -// write new json to alter multiple tasks -// write new json for host and sym only TEST_CASE("TaskValueEvent One Time Events", "[sgp][events]") { GIVEN("onetime-events.json"){ @@ -28,7 +24,7 @@ TEST_CASE("TaskValueEvent One Time Events", "[sgp][events]") { config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/onetime-events.json"); // Fill in any generic configs here - int num_updates = 3; + int num_updates = 5; config.UPDATES(num_updates); config.SYM_ONLY_FIRST_TASK_CREDIT(1); config.HOST_ONLY_FIRST_TASK_CREDIT(1); @@ -45,44 +41,84 @@ TEST_CASE("TaskValueEvent One Time Events", "[sgp][events]") { world.Setup(); auto& builder = world.GetProgramBuilder(); - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); THEN("Events are processed"){ - for(int i = 0; i <= num_updates; i++){ - world.Update(); - switch(i){ - case 0: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - // REQUIRE(symbiont->GetPoints() == 5); - // REQUIRE(host->GetPoints() == 5); - continue; - case 1: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); - // CHECK(symbiont->GetPoints() == 20); - // CHECK(host->GetPoints() == 20); - continue; - case 2: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); - // CHECK(symbiont->GetPoints() == 50); - // CHECK(host->GetPoints() == 50); - continue; - case 3: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); - // CHECK(symbiont->GetPoints() == 150); - // CHECK(host->GetPoints() == 150); - continue; + switch(world.GetUpdate()){ + case 0: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + CHECK(symbiont->GetPoints() == 0); + CHECK(host->GetPoints() == 0); + } + + case 1: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + CHECK(symbiont->GetPoints() == 5); + CHECK(host->GetPoints() == 5); + } + + case 2: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + CHECK(symbiont->GetPoints() == 15); + CHECK(host->GetPoints() == 15); + } + + case 3: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); + CHECK(symbiont->GetPoints() == 30); // 80? + CHECK(host->GetPoints() == 30); // 80? } + + case 4: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); + CHECK(symbiont->GetPoints() == 100); // 180? + CHECK(host->GetPoints() == 100); // 180? + } } } } @@ -97,7 +133,7 @@ TEST_CASE("TaskValueEvent Multiple Reoccuring Events", "[sgp][events]") { config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/reoccur-events.json"); // Fill in any generic configs here - int num_updates = 6; + int num_updates = 7; config.UPDATES(num_updates); WHEN("Reoccuring add and mul task-value events are loaded"){ @@ -109,30 +145,45 @@ TEST_CASE("TaskValueEvent Multiple Reoccuring Events", "[sgp][events]") { const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); THEN("Events are processed"){ - for(int i = 0; i <= num_updates; i++){ - world.Update(); - switch(i){ - case 0: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - continue; - case 1: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); - continue; - case 2: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); - continue; - case 3: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 35); - continue; - case 4: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); - continue; - case 5: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); - continue; - case 6: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); - continue; + switch(world.GetUpdate()){ + case 0: { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + } + case 1: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + } + case 2: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 10); + } + case 3: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 30); + } + case 4: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 35); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 35); + } + case 5: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 70); + } + case 6: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 70); + } + case 7: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 70); } } } @@ -146,7 +197,7 @@ TEST_CASE("TaskValueEvent Reoccuring Multiply Event", "[sgp][events]") { sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); // Fill in any generic configs here - int num_updates = 5; + int num_updates = 6; config.UPDATES(num_updates); config.SYM_ONLY_FIRST_TASK_CREDIT(1); config.HOST_ONLY_FIRST_TASK_CREDIT(1); @@ -166,10 +217,8 @@ TEST_CASE("TaskValueEvent Reoccuring Multiply Event", "[sgp][events]") { // create host with NAND operation emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym world.AddOrgAt(host, 0); host->AddSymbiont(symbiont); @@ -178,33 +227,33 @@ TEST_CASE("TaskValueEvent Reoccuring Multiply Event", "[sgp][events]") { const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); THEN("Events are processed"){ - for(int i = 0; i <= num_updates; i++){ - world.Update(); - switch(i){ - case 0: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - // CHECK(host->GetPoints() == 5); - continue; - case 1: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); - // CHECK(host->GetPoints() == 0); - continue; - case 2: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); - // CHECK(host->GetPoints() == -5); - continue; - case 3: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - // CHECK(host->GetPoints() == 0); - continue; - case 4: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - // CHECK(host->GetPoints() == 5); - continue; - case 5: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - // CHECK(host->GetPoints() == 10); - continue; + switch(world.GetUpdate()){ + case 0: { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + } + case 1: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + } + case 2: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); + } + case 3: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); + } + case 4: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + } + case 5: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + } + case 6: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); } } } @@ -220,7 +269,7 @@ TEST_CASE("TaskValueEvent Reoccuring Add Event", "[sgp][events]") { config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json"); // Fill in any generic configs here - int num_updates = 3; + int num_updates = 4; config.UPDATES(num_updates); WHEN("Reoccuring add task-value events are loaded"){ @@ -232,21 +281,25 @@ TEST_CASE("TaskValueEvent Reoccuring Add Event", "[sgp][events]") { const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); THEN("Events are processed"){ - for(int i = 0; i <= num_updates; i++){ - world.Update(); - switch(i){ - case 0: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - continue; - case 1: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); - continue; - case 2: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); - continue; - case 3: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); - continue; + switch(world.GetUpdate()){ + case 0: { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + } + case 1: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + } + case 2: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); + } + case 3: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + } + case 4: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); } } } @@ -262,7 +315,7 @@ TEST_CASE("TaskValueEvent Reoccuring Change Event", "[sgp][events]") { config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json"); // Fill in any generic configs here - int num_updates = 3; + int num_updates = 4; config.UPDATES(num_updates); WHEN("Reoccuring change task-value event is loaded"){ @@ -274,21 +327,30 @@ TEST_CASE("TaskValueEvent Reoccuring Change Event", "[sgp][events]") { const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); THEN("Events are processed"){ - for(int i = 0; i <= num_updates; i++){ - world.Update(); - switch(i){ - case 0: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - continue; - case 1: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); - continue; - case 2: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); - continue; - case 3: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); - continue; + switch(world.GetUpdate()) { + case 0: { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + } + case 1: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + } + case 2: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); + } + case 3: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); + } + case 4: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); } } } @@ -303,7 +365,7 @@ TEST_CASE("TaskValueEvent Host/Sym Only Events", "[sgp][events]") { config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json"); // Fill in any generic configs here - int num_updates = 2; + int num_updates = 4; config.UPDATES(num_updates); config.SYM_ONLY_FIRST_TASK_CREDIT(1); config.HOST_ONLY_FIRST_TASK_CREDIT(1); @@ -321,10 +383,8 @@ TEST_CASE("TaskValueEvent Host/Sym Only Events", "[sgp][events]") { // create host with NAND operation emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym world.AddOrgAt(host, 0); host->AddSymbiont(symbiont); @@ -333,25 +393,58 @@ TEST_CASE("TaskValueEvent Host/Sym Only Events", "[sgp][events]") { const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); THEN("Events are processed"){ - for(int i = 0; i <= num_updates; i++){ - world.Update(); - switch(i){ - case 0: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(host->GetPoints() == 5); - REQUIRE(symbiont->GetPoints() == 5); - continue; - case 1: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); - REQUIRE(host->GetPoints() == 55); - REQUIRE(symbiont->GetPoints() == 105); - continue; - case 2: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); - // incorrect output - // CHECK(symbiont->GetPoints() == 205); // 105 - // CHECK(host->GetPoints() == 105); // 55 - continue; + switch(world.GetUpdate()){ + case 0:{ + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + REQUIRE(host->GetPoints() == 0); + REQUIRE(symbiont->GetPoints() == 0); + } + case 1: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + world.Update(); + + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + REQUIRE(host->GetPoints() == 5); + REQUIRE(symbiont->GetPoints() == 5); + } + case 2: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + world.Update(); + + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); + REQUIRE(host->GetPoints() == 50); + REQUIRE(symbiont->GetPoints() == 100); + } + case 3: { + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + world.Update(); + + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); + // incorrect output + CHECK(host->GetPoints() == 50); + CHECK(symbiont->GetPoints() == 100); } } } @@ -366,7 +459,7 @@ TEST_CASE("TaskValueEvent Mulitple Task Names Events", "[sgp][events]") { config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json"); // Fill in any generic configs here - int num_updates = 1; + int num_updates = 2; config.UPDATES(num_updates); WHEN("Multiple tasks are in a single event"){ @@ -386,34 +479,152 @@ TEST_CASE("TaskValueEvent Mulitple Task Names Events", "[sgp][events]") { const size_t ornot_task_id = world.GetTaskEnv().GetTaskSet().GetID("OR_NOT"); THEN("Events are processed"){ - for(int i = 0; i <= num_updates; i++){ - world.Update(); - switch(i){ - case 0: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 5); - continue; - case 1: - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 100); - continue; + switch(world.GetUpdate()){ + case 1: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(not_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(and_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(or_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(xor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(equ_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(andnot_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(ornot_task_id).task_value == 5); + } + case 2: { + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 100); + + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(not_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(and_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(or_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(xor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(equ_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(andnot_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(ornot_task_id).task_value == 100); } } } } } } + + + +// ***** CREATE NAND TEST ***** +TEST_CASE("Checking if NAND op is done for more than two updates", "[sgp][events]") { + GIVEN("onetime-events.json"){ + emp::Random random(2); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/onetime-events.json"); + + // Fill in any generic configs here + int num_updates = 5; + config.UPDATES(num_updates); + config.SYM_ONLY_FIRST_TASK_CREDIT(1); + config.HOST_ONLY_FIRST_TASK_CREDIT(1); + config.POP_SIZE(1); + config.CYCLES_PER_UPDATE(52); + + config.HOST_REPRO_RES(10000); + config.SYM_HORIZ_TRANS_RES(10000); + config.SYM_VERT_TRANS_RES(10000); + + + WHEN("One time add, mul, and change task-value events are loaded"){ + world_t world(random, &config); + world.Setup(); + auto& builder = world.GetProgramBuilder(); + + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + // get NAND Task id + const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); + + THEN("Events are processed"){ + switch(world.GetUpdate()){ + case 0: { + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + + CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 0); + CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 0); + + CHECK(symbiont->GetPoints() == 0); + CHECK(host->GetPoints() == 0); + } + + case 1: { + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + + CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 1); + CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 1); + + CHECK(symbiont->GetPoints() == 5); + CHECK(host->GetPoints() == 5); + } + + case 2: { + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + + CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 2); + CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 2); + + CHECK(symbiont->GetPoints() == 20); + CHECK(host->GetPoints() == 20); + } + + case 3: { + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); + + CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 4); + CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 4); + + CHECK(symbiont->GetPoints() == 80); + CHECK(host->GetPoints() == 80); + } + + case 4: { + world.Update(); + CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); + + CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 5); + CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 5); + + CHECK(symbiont->GetPoints() == 180); + CHECK(host->GetPoints() == 180); + } + } + } + } + } +} \ No newline at end of file diff --git a/source/test/sgp_mode_test/test-events.json b/source/test/sgp_mode_test/test-events.json deleted file mode 100644 index b0629042..00000000 --- a/source/test/sgp_mode_test/test-events.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "events": [ - { - "event_type": "task_value", - "task_name": "NAND", - "action": "add", - "value": 50, - "timing": "1", - "group": "shared" - }, - { - "event_type": "task_value", - "task_name": ["NAND"], - "action": "mult", - "value": -1, - "timing": "500:10000:1000", - "group": "shared" - } - ] -} \ No newline at end of file From ff4400a9eb15a1fdbd0e686ccd216c215f1add71 Mon Sep 17 00:00:00 2001 From: Delaney Kelley Date: Wed, 8 Jul 2026 10:40:23 -0400 Subject: [PATCH 26/42] fix typo in comment ProgramBuilder CreateNANDProgram --- source/sgp_mode/ProgramBuilder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/sgp_mode/ProgramBuilder.h b/source/sgp_mode/ProgramBuilder.h index 1a74e015..63f4e5f3 100644 --- a/source/sgp_mode/ProgramBuilder.h +++ b/source/sgp_mode/ProgramBuilder.h @@ -376,7 +376,7 @@ class ProgramBuilder { AddInst(program, nand_op, 0, 1, 0); // reg 0 = c NAND d AddInst(program, io_op); // reg 0 = a - AddInst(program, nand_op, 0, 1, 1); // reg 0 = a NAND d + AddInst(program, nand_op, 0, 1, 1); // reg 1 = a NAND d // AddInst(program, io_op); // also output a NAND b, reg 0 = c From b766f824ebc83d9efd4ca396bf4a85fbc0c4fb3e Mon Sep 17 00:00:00 2001 From: Anya Vostinar Date: Mon, 13 Jul 2026 13:14:05 -0500 Subject: [PATCH 27/42] fixing last merge conflict remnants --- source/sgp_mode/ProgramBuilder.h | 4 ---- source/sgp_mode/SGPWorld.h | 1 - 2 files changed, 5 deletions(-) diff --git a/source/sgp_mode/ProgramBuilder.h b/source/sgp_mode/ProgramBuilder.h index 13b7ef9f..03bf0ce6 100644 --- a/source/sgp_mode/ProgramBuilder.h +++ b/source/sgp_mode/ProgramBuilder.h @@ -230,10 +230,6 @@ class ProgramBuilder { // nand r1, r1, r1 // nand r0, r1, r0 // nand r0, r0, r0 -<<<<<<< HEAD -======= - ->>>>>>> ff4400a9eb15a1fdbd0e686ccd216c215f1add71 AddInst(program, nand_op, 0, 0, 0); AddInst(program, nand_op, 1, 1, 1); AddInst(program, nand_op, 0, 1, 0); diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index 2e6b54c8..6fe383f0 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -384,7 +384,6 @@ class SGPWorld : public SymWorld { ReproductionQueue repro_queue; // Stores which organisms are queued for reproduction tasks::LogicTaskEnvironment task_env; // Manages task set, task requirements, and task rewards event_manager_t event_manager; - mutator_t mutator; // Handles mutating sgp programs // TODO - Consider having symbiont rectifier and host rectifier // -> Symbiont-specific instructions wouldn't be in host's instruction set sgp_prog_rectifier_t opcode_rectifier; // Used to "disable" instructions at runtime based on run configuration From f1bafd17d9c86e8088e6e35f28dba5e1f68a6835 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 09:31:07 -0400 Subject: [PATCH 28/42] Fix unresolved merge conflict from prev merge --- source/native/symbulation_sgp.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/native/symbulation_sgp.cc b/source/native/symbulation_sgp.cc index 6b248ed7..1b8bbe12 100644 --- a/source/native/symbulation_sgp.cc +++ b/source/native/symbulation_sgp.cc @@ -40,11 +40,8 @@ int symbulation_main(int argc, char *argv[]) { sgpmode::SGPWorld world(random, &config); world.Setup(); world.Run(true); -<<<<<<< HEAD world.OutputDominantDataFile(); -======= ->>>>>>> ff4400a9eb15a1fdbd0e686ccd216c215f1add71 return 0; } From 470cea269c0a28f38014405f4e7c459e1690f726 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 09:41:22 -0400 Subject: [PATCH 29/42] Add function docstrings to json utils --- source/json/json_utils.h | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/source/json/json_utils.h b/source/json/json_utils.h index fcf767f2..cd21f0af 100644 --- a/source/json/json_utils.h +++ b/source/json/json_utils.h @@ -8,7 +8,16 @@ namespace sym_json { -// Get value from json field with default value if field doesn't exist. +/** + * Purpose: Convenience function for more easily getting a value from a json field + * with a default return value if the field doesn't exist. + * + * Input: json object to access, field to access in json object, and a default + * value to return if the field doesn't exist in the json object. + * + * Output: Either the accessed field value or the provided default value. + * + */ template RET_TYPE GetVal( nlohmann::json& json, @@ -20,7 +29,15 @@ RET_TYPE GetVal( default_val; } -// Get value from jsonm field. Assumes that given field exists. +/** + * Purpose: Convenience function for more easily getting a value from a json field. + * This version of the GetVal function assumes that the field exists. + * + * Input: json object to access, field to access in json object + * + * Output: The accessed field value + * + */ template RET_TYPE GetVal( nlohmann::json& json, @@ -30,6 +47,17 @@ RET_TYPE GetVal( return static_cast(json[field]); } +/** + * Purpose: Validate that specified strings exist as fields inside of a json object. + * This function is useful for asserting that required/expected fields + * exist. + * + * Input: json object to check, fields to check in json object + * + * Output: Boolean indicating whether all given fields are contained in the json + * object. + * + */ bool ValidateFieldsJSON( const nlohmann::json& json_line, const emp::vector& fields From 136fb65d40ecb69554c2d5aa93873a29eade3083 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 10:06:08 -0400 Subject: [PATCH 30/42] Add doc strings for Event.h --- source/sgp_mode/events/event_types/Event.h | 176 +++++++++++++++++++-- 1 file changed, 167 insertions(+), 9 deletions(-) diff --git a/source/sgp_mode/events/event_types/Event.h b/source/sgp_mode/events/event_types/Event.h index 4409bc67..bb63f907 100644 --- a/source/sgp_mode/events/event_types/Event.h +++ b/source/sgp_mode/events/event_types/Event.h @@ -23,12 +23,37 @@ namespace sgpmode { -// Helper struct used by derived event types. +/** + * Purpose: Helper struct used to manage minimum required specifications for new + * event types. Each event type contains a static const EventTypeDefinitionSpecs + * object, containing definitional information about the event type. + */ struct EventTypeDefinitionSpecs { + /** + * Purpose: String that names this type of event. + */ std::string event_type; + + /** + * Purpose: String that provides a brief description of this event type. + */ std::string event_description; + + /** + * Purpose: Vector of strings that lists the required fields for configuring + * an event of this type. + */ emp::vector event_required_fields; + /** + * Purpose: Constructor. + * + * Input: + * - e_type: String specifying name of this event type. + * - e_desc: String specifying a description of this event type. + * - e_req_fields: Vector of strings specifying required fields for configuring + * an event of this type. + */ EventTypeDefinitionSpecs( const std::string& e_type, const std::string& e_desc = "", @@ -40,35 +65,162 @@ struct EventTypeDefinitionSpecs { { ; } }; -// Basic event type. Should be used as base class for event types. -// See ExampleEvent.h for a minimal example of how to implement a new event type. +/** + * Purpose: Basic event type. Should be used as base class for event types. + * See ExampleEvent.h for a minimal example of how to implement a new + * event type. Each instance of any event will have this as its base class. + */ class Event { protected: - size_t event_type_id = 0; // Event type ID for this event. Filled in when event is loaded from file. - std::string event_type{"NULL"}; // Human-readable event type. - EventTiming timing; // Manages event's timing (one-time vs. recurring start/stop/step) - bool is_done = false; // Can be used by an event to end a recurring event before its end update + /** + * Purpose: Event type ID for this event. Filled in by the event manager when + * event is loaded from file. + */ + size_t event_type_id = 0; + + /** + * Purpose: Human-readable event type name. + */ + std::string event_type{"NULL"}; // + + /** + * Purpose: EventTiming object that specifies/manages this event's timing + * (one-time vs. recurring start/stop/step) + */ + EventTiming timing; + + /** + * Purpose: Boolean indicating whether this event is finished. Can be set by + * an event to end a recurring event before its end update. + */ + bool is_done = false; public: + + /** + * Purpose: Get this event's type id. + * + * Input: None. + * + * Output: This event's event type id. + * + */ size_t GetEventTypeID() const { return event_type_id; } + + /** + * Purpose: Set this event's event type id. + * + * Input: New event type id. + * + * Output: None. + * + */ void SetEventTypeID(size_t id) { event_type_id = id; } + + /** + * Purpose: Check whether this event has been flagged as finished. + * + * Input: None. + * + * Output: Boolean indicating whether this event is finished. + * + */ bool IsDone() const { return is_done; } + + /** + * Purpose: Get this event's type string. + * + * Input: None. + * + * Output: String indicating this event's type. + * + */ const std::string& GetEventType() const { return event_type; } + + /** + * Purpose: Get this event's event timing object. + * + * Input: None. + * + * Output: This event's event timing object. + * + */ const EventTiming& GetEventTiming() const { return timing; } + + /** + * Purpose: Get whether this event is recurring. + * + * Input: None. + * + * Output: Boolean indicating whether this event is recurring. + * + */ bool IsRecurring() const { return timing.IsRecurring(); } + + /** + * Purpose: Get this event's start update. + * + * Input: None. + * + * Output: Unsigned integer indicating this event's start update. + * + */ size_t GetStartUpdate() const { return timing.GetStartUpdate(); } + + /** + * Purpose: Get this event's end update. + * + * Input: None. + * + * Output: Unsigned integer indicating this event's ending update. + * + */ size_t GetEndUpdate() const { return timing.GetEndUpdate(); } + + /** + * Purpose: Get the update that this event will next occur. + * + * Input: None. + * + * Output: Unsigned integer indicating the update that this event will next occur. + * + */ size_t GetNextUpdate() const { return timing.GetNextUpdate(); } + /** + * Purpose: Reset this event's timing as a non-recurring event with the given + * trigger update. + * + * Input: Start update to reset this event to. + * + * Output: None. + * + */ void ResetTiming(size_t start_update) { timing.Reset(start_update); } + /** + * Purpose: Reset this event's timing as a recurring event with specified start + * update, end update, and frequency. + * + * Input: Unsigned integers indicating new start update, end update, and frequency. + * + * Output: None. + * + */ void ResetTiming(size_t start, size_t end, size_t freq) { timing.Reset(start, end, freq); } - // Advance next update, return new next update. + /** + * Purpose: Advance event timing (used when event is processed). + * + * Input: None. + * + * Output: Next event update after advancing. + * + */ size_t AdvanceNextUpdate() { timing.Step(); return timing.GetNextUpdate(); @@ -76,7 +228,13 @@ class Event { }; -// Common parsing function used by many event types. +/** + * Purpose: Basic parsing function for event timing used by many event types. + * + * Input: json object with timing information, pointer to the event to configure + * + * Output: None. + */ void SetEventTimingFromJSON( nlohmann::json& event_json, emp::Ptr event_ptr From 8280e060dadc8fe8308bfc2f826770f7baff88c8 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 10:14:34 -0400 Subject: [PATCH 31/42] Add doc strings in ExampleEvent.h --- .../events/event_types/ExampleEvent.h | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/source/sgp_mode/events/event_types/ExampleEvent.h b/source/sgp_mode/events/event_types/ExampleEvent.h index 761a5d58..968a8abd 100644 --- a/source/sgp_mode/events/event_types/ExampleEvent.h +++ b/source/sgp_mode/events/event_types/ExampleEvent.h @@ -4,13 +4,29 @@ namespace sgpmode { -// Example event class. Used for example purposes only. +/** + * Purpose: Example event class. Used for example purposes only. + */ class ExampleEvent : public Event { public: using json_t = nlohmann::json; + /** + * Purpose: Event specification object shared by all instances of this event + * type. + */ static const EventTypeDefinitionSpecs event_specs; + /** + * Purpose: All event type classes should define a "LoadEventFromJSON" function + * that configures an instance of this event type. + * See TaskValueEvent for another example. + * + * Input: Json object to load event information from, reference to the world. + * + * Output: Pointer to new event instance created and configured by this load + * function. + */ template static emp::Ptr LoadEventFromJSON( json_t& event_json, @@ -27,12 +43,28 @@ class ExampleEvent : public Event { } protected: + // -- TO FILL IN -- + // Put any internal non-public variables used by your event here. + // ---------------- + public: + /** + * Purpose: Constructor. Set the base class's event_type based on this event + * type's event specs. + */ ExampleEvent() { // Set event type in base class. event_type = event_specs.event_type; } + /** + * Purpose: Process this event. Called by the event manager when this event should + * be triggered based on its timing. + * + * Input: Reference to the world (will likely be modified by the event) + * + * Output: None. + */ template void Process(WORLD_T& world) { // --- TO FILL IN --- @@ -41,10 +73,11 @@ class ExampleEvent : public Event { } }; +// Every event type needs to have its event specs defined. const EventTypeDefinitionSpecs ExampleEvent::event_specs = EventTypeDefinitionSpecs{ "example_event", "Example of a minimally defined event type", - {"list", "required", "fields", "for", "this", "event", "here"} + {"list", "required", "fields", "for", "this", "event", "here"} }; } \ No newline at end of file From 2af3c7f59b25434b28c84960ab7aa5193e000c7d Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 10:30:29 -0400 Subject: [PATCH 32/42] Add docstrings for TaskValueEvent --- .../events/event_types/TaskValueEvent.h | 115 +++++++++++++++--- 1 file changed, 98 insertions(+), 17 deletions(-) diff --git a/source/sgp_mode/events/event_types/TaskValueEvent.h b/source/sgp_mode/events/event_types/TaskValueEvent.h index 98371099..1d416a81 100644 --- a/source/sgp_mode/events/event_types/TaskValueEvent.h +++ b/source/sgp_mode/events/event_types/TaskValueEvent.h @@ -25,31 +25,71 @@ namespace sgpmode { -/* -- task_value: change the task value of one or more existing tasks to a new value - - Parameters: - - task_name: name of task to change or list of names to change - - value: value to change to - - timing: event timing. For one-time event, provide a single number. For a recurring event, provide start:stop:step. - - group: [optional] Which task group to apply change to? shared / symbiont / host -*/ - +/** + * Purpose: Definition of a task_value event type. + * A task_value event modifies the value of a task. + * Event configuration options: + * - action: How should this event modify task values? + * - Options: + * - change: set the value of this task to the given value + * - add: add the given value to this task's current value + * - mult: multiply the task's current value by the given value + * - task_name: Which task(s) should be affected? Can be given as + * either a string indicating a single task or a list of + * tasks. All tasks must be valid. + * - value: Value used to update task value according to event action. + * - timing: Event timing. + * - group: Optional. Should this event affect sym, host, or shared tasks? + */ class TaskValueEvent : public Event { public: + + /** + * Purpose: Event specification object shared by all instances of this event + * type. + */ static const EventTypeDefinitionSpecs event_specs; + /** + * Purpose: Defines valid actions for a task value event. + */ enum class ACTION_TYPE { CHANGE, ADD, MULT }; + + /** + * Purpose: Defines valid task groups that can be affected by task value event. + */ enum class TASK_GROUP { SHARED, HOST, SYM }; + + /** + * Purpose: Mapping from string to action type. Shared by all instances of this + * class. + */ static const std::unordered_map valid_action_types; + + /** + * Purpose: Mapping from string to task group. Shared by all instances of this + * class. + */ static const std::unordered_map valid_task_groups; using json_t = nlohmann::json; using action_t = ACTION_TYPE; using task_group_t = TASK_GROUP; - // World is necessary for parsing the task id + /** + * Purpose: Required LoadEventFromJSON function. Configures this event based on + * given json object. + * + * Input: json object to load from, reference to world. + * + * Output: Pointer to a new TaskValueEvent created by this function. + * + */ template - static emp::Ptr LoadEventFromJSON(json_t& event_json, WORLD_T& world) { + static emp::Ptr LoadEventFromJSON( + json_t& event_json, + WORLD_T& world + ) { // This should be a task_value event emp_assert(event_json["event_type"] == event_specs.event_type); // NOTE: Caller is responsible for event deletion. @@ -99,12 +139,41 @@ class TaskValueEvent : public Event { } protected: - action_t action; // What task value action does this event apply? - emp::vector task_ids; // List of task ids that this event applies to - emp::vector task_names; // Kept mainly for debugging asserts - task_group_t task_group; // Sym / host / shared? - double value; // Event value to be applied to task value + /** + * Purpose: What task value action does this event apply? + */ + action_t action; + + /** + * Purpose: List of task ids that this event applies to + */ + emp::vector task_ids; + + /** + * Purpose: List of task names that this event applies to. Corresponds to task_ids. + * Redundant information (with task_ids) that is kept for debugging asserts. + */ + emp::vector task_names; + + /** + * Purpose: What task group does this event apply to? Sym / host / shared? + */ + task_group_t task_group; + + /** + * Purpose: Event value to be applied to task value + */ + double value; + + /** + * Purpose: Internal function that applies the correct action to the given task + * information struct. + * + * Input: Reference to the task information struct to be modified. + * + * Output: None. + */ void ApplyAction(tasks::LogicTaskEnvironment::TaskReqInfo& task_req) { switch (action) { case action_t::ADD: @@ -120,11 +189,23 @@ class TaskValueEvent : public Event { } public: + + /** + * Purpose: TaskValueEvent constructor. Sets the base class's event type + * based on event specs. + */ TaskValueEvent() { // Set event type in base class. event_type = event_specs.event_type; } + /** + * Purpose: Process this TaskValueEvent. + * + * Input: Reference to the world. + * + * Output: None. + */ template void Process(WORLD_T& world) { // For each task_id, apply action @@ -152,7 +233,7 @@ class TaskValueEvent : public Event { const EventTypeDefinitionSpecs TaskValueEvent::event_specs = EventTypeDefinitionSpecs{ "task_value", "Modify the value on a current task", - {"action", "task_name", "value", "timing"} + {"action", "task_name", "value", "timing"} }; const std::unordered_map TaskValueEvent::valid_action_types = { From 8bf8757bac1ed9bdb6a832808de4eda338220cf3 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 10:47:09 -0400 Subject: [PATCH 33/42] Add doc strings to EventManager --- source/sgp_mode/events/EventManager.h | 167 ++++++++++++++++++++------ 1 file changed, 128 insertions(+), 39 deletions(-) diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index f86e9c08..960c0815 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -1,8 +1,5 @@ #pragma once -// @AML review: added header guards -// @AML review: switch indentation to 2 spaces for consistency -// @AML review: Moved local includes to top for consistency #include "event_types/Event.h" #include "EventTypeDefinition.h" #include "EventTypeLibrary.h" @@ -24,11 +21,11 @@ #include #include -// @AML review: Don't need to namespace the event handler -// (unless there's a bunch of internal components that should be isolated -// from the rest of the repo) namespace sgpmode { +/** + * Purpose: Manages set of events that can be loaded in from a configuration file. + */ template class EventManager { public: @@ -40,14 +37,32 @@ class EventManager { protected: - EventTypeLibrary event_type_library; // Manages known event type definitions. Must be updated before loading event cfg file. - emp::vector> one_time_events; // Manages all one-off events in reverse sorted order by update to apply. - emp::vector> recurring_events; // Manages all recurring events in reverse sorted order by update to apply. + /** + * Purpose: Manages known event type definitions. + * Must be updated before loading event cfg file. + */ + EventTypeLibrary event_type_library; - // Load a single event from JSON - // Each event type should define it's own loading function (see ExampleEvent), - // so this function grabs the appropriate event type definition and then calls - // that event type's load event function. + /** + * Purpose: Manages all one-off events in reverse sorted order by update to apply. + */ + emp::vector> one_time_events; + + /** + * Purpose: Manages all recurring events in reverse sorted order by update to apply. + */ + emp::vector> recurring_events; + + /** + * Purpose: Load a single event from JSON + * Each event type should define its own loading function (see ExampleEvent), + * so this function grabs the appropriate event type definition and then + * calls that event type's load event function. + * + * Input: json oject to load an event from, reference to the world + * + * Output: Pointer to a new event object + */ emp::Ptr LoadEventFromJSON(json_t& event_json, world_t& world) { // Check that event_json has event type emp_assert(event_json.contains("event_type")); @@ -64,10 +79,16 @@ class EventManager { return event_type_def.LoadEventFromJSON(event_json, world); } - // Process all one-type events that should be applied this update. - // After processing, delete event. - // Note: this function assumes that one_time_events is in reverse sorted - // order by next update. + /** + * Purpose: Process all one-type events that should be applied this update. + * After processing an event, delete it. + * Note: this function assumes that one_time_events is in reverse sorted + * order by next update. + * + * Input: Reference to the world + * + * Output: None. + */ void ProcessOneTimeEvents(world_t& world) { if (one_time_events.empty()) { return; } // One-time events are reverse sorted according to next update. @@ -104,11 +125,20 @@ class EventManager { one_time_events.resize(num_events - events_processed); } - // Process any recurring events that should be applied this update. - // After processing, either delete event (if next update is past end update - // or if event was marked as done by the event's process function). - // Note: this function assumes that recurring_events is in reverse sorted order - // by each event's next update. + /** + * Purpose: Process any recurring events that should be applied this update. + * After processing an event, delete the event if its next update is + * past its end update or if the event was marked as done by the event's + * process function. Otherwise, advance the event's timing and keep + * for future processing. + * Note: this function assumes that recurring_events is in reverse sorted order + * by each event's next update. This function will resort recurring + * events after processing. + * + * Input: Reference to the world. + * + * Output: None. + */ void ProcessRecurringEvents(world_t& world) { if (recurring_events.empty()) { return; } // Recurring events are reverse sorted by the next update they should trigger @@ -170,10 +200,16 @@ class EventManager { ReorderRecurringEvents(); } - // Resort the one-time events. - // One-time events should be reverse-sorted by their next update (i.e., soonest - // next update at end). One-time events must be sorted for processing to be correct. - // i.e., when adding a new event, must resort! + /** + * Purpose: Internal helper function to resort the one-time events. + * One-time events should be reverse-sorted by their next update + * (i.e., soonest next update at end). One-time events must be sorted + * for processing to be correct. i.e., when adding a new event, must resort! + * + * Input: None. + * + * Output: None. + */ void ReorderOneTimeEvents() { std::sort( one_time_events.begin(), @@ -184,11 +220,20 @@ class EventManager { ); } - // Reorder recurring events. Should be reverse sorted by each event's next update - // (i.e., soonest next update at end of vector). Recurring events must be sorted - // for processing to be correct. - // I.e., when adding a new event, must sort! And, when re-queueing a recurring - // event, we must make sure it ends up in the appropriate spot by next update. + /** + * Purpose: Internal helper function to reorder recurring events. + * Should be reverse sorted by each event's next update (i.e., soonest + * next update at end of vector). Recurring events must be sorted for + * processing to be correct. I.e., when adding a new event, must sort! + * And, when re-queueing a recurring event, we must make sure it ends + * up in the appropriate spot by next update. + * + * Input: None. + * + * Output. None. + * + * + */ void ReorderRecurringEvents() { std::sort( recurring_events.begin(), @@ -199,16 +244,31 @@ class EventManager { ); } -// @AML review: missing public designation here public: - + /** + * Purpose: EventManager destructor. Responsible for cleaning up any event objects. + */ ~EventManager() { ClearEvents(); } + /** + * Purpose: + * + * Input: + * + * Output: + */ const EventTypeLibrary& GetEventTypeLibrary() const { return event_type_library; } - // Delete all current events info + + /** + * Purpose: Delete all current events info (one-time and recurring) + * + * Input: None. + * + * Output: None. + */ void ClearEvents() { for (emp::Ptr event : one_time_events) { event.Delete(); @@ -220,7 +280,14 @@ class EventManager { recurring_events.clear(); } - // load in and process events.json file (includes creating events and checking if they are valid) + /** + * Purpose: load in and process events configuratoin json file (includes + * creating events and checking if they are valid) + * + * Input: String path to event configuration file, reference to the world object + * + * Output: None. + */ void LoadEventsFromJSON(const std::string& event_filepath, world_t& world) { std::cout << "Loading events from event file." << std::endl; ClearEvents(); @@ -251,6 +318,14 @@ class EventManager { ReorderRecurringEvents(); } + /** + * Purpose: Process any events that need to be processed on the current world + * update. + * + * Input: Reference to the world. + * + * Output: None. + */ void ProcessEvents(world_t& world) { // Get current update in the world. Process all events that should occur on this update. // Options: @@ -274,8 +349,14 @@ class EventManager { ProcessRecurringEvents(world); } - // Manual add event function. Used when an event that didn't originate from a config - // file needs to be added to the event system. + /** + * Purpose: Manual add event function. Used when an event that didn't originate + * from a config file needs to be added to the event system. + * + * Input: Pointer to the event to add to the event manager. + * + * Output: None. + */ void AddEvent(emp::Ptr event) { // Check that this event is who they say it is const auto& event_type_name = event->GetEventType(); @@ -293,8 +374,16 @@ class EventManager { } } - // Add multiple events. Faster to bulk add than add one at a time to save on - // resorting. + /** + * Purpose: Manually add multiple events to the event manager. Used for events + * that don't originate from the events configuration file. Faster to + * to bulk add multiple events than adding one at a time to avoid + * repeated resorting. + * + * Input: List of event pointers to be added. + * + * Output: None. + */ void AddEvents(emp::vector> events) { bool resort_recurring = false; bool resort_one_time = false; From f1733cc20b00e5d1a50b22ecbd7f312ff1aa0e21 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 11:21:27 -0400 Subject: [PATCH 34/42] Add doc strings to EventTiming --- source/sgp_mode/events/EventTiming.h | 101 +++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/source/sgp_mode/events/EventTiming.h b/source/sgp_mode/events/EventTiming.h index acc464d4..d8d51e44 100644 --- a/source/sgp_mode/events/EventTiming.h +++ b/source/sgp_mode/events/EventTiming.h @@ -4,20 +4,47 @@ namespace sgpmode { -// note: add event timing helper class to manage event timing? - -// Helper class for managing event timing +/** + * Purpose: Manages timing for events. + */ class EventTiming { protected: + /** + * Purpose: Start update for this event. + */ size_t start_update; + + /** + * Purpose: End update for this event if recurring. + */ size_t end_update; + + /** + * Purpose: Update interval for this event if recurring. + */ size_t frequency; + + /** + * Purpose: Next update that this event should occur if recurring. + */ size_t next_update; + + /** + * Purpose: Boolean indicating whether this is a recurring or one-time event. + */ bool recurring; + public: + /** + * Purpose: Default constructor. + */ EventTiming() = default; - // Constructor for recurring events + /** + * Purpose: Constructor for recurring events. + * + * Inputs: Specify start update, end update, and frequency for recurring event. + */ EventTiming(size_t start_u, size_t end_u, size_t freq_u) : start_update(start_u), end_update(end_u), @@ -27,7 +54,11 @@ class EventTiming { next_update = start_update; } - // Constructor for one-time events + /** + * Purpose: Constructor for one-time events. + * + * Inputs: Specify the start update for this one-time event. + */ EventTiming(size_t start_u) : start_update(start_u), end_update((size_t)-1), @@ -37,6 +68,13 @@ class EventTiming { next_update = start_update; } + /** + * Purpose: Reset this event as a one-time event. + * + * Inputs: Start update. + * + * Outputs: None. + */ void Reset(size_t start_u) { start_update = start_u; end_update = (size_t)-1; @@ -45,6 +83,13 @@ class EventTiming { next_update = start_update; } + /** + * Purpose: Reset this event as a recurring event. + * + * Inputs: Start update, end update, and frequency. + * + * Outputs: None. + */ void Reset(size_t start_u, size_t end_u, size_t freq_u) { emp_assert(start_u <= end_u); start_update = start_u; @@ -54,12 +99,58 @@ class EventTiming { next_update = start_update; } + /** + * Purpose: Get this event's start update. + * + * Inputs: None. + * + * Outputs: This event's start update. + */ size_t GetStartUpdate() const { return start_update; } + + /** + * Purpose: Get this event's end update. + * + * Inputs: None. + * + * Outputs: This event's end update. + */ size_t GetEndUpdate() const { return end_update; } + + /** + * Purpose: Get this event's frequency. + * + * Inputs: None. + * + * Outputs: This event's update interval frequency. + */ size_t GetFrequency() const { return frequency; } + + /** + * Purpose: Check whether this event is recurring. + * + * Inputs: None. + * + * Outputs: Boolean indicating whether this event is recurring. + */ bool IsRecurring() const { return recurring; } + + /** + * Purpose: Get this event's next update. + * + * Inputs: None. + * + * Outputs: The update that this event will trigger next. + */ size_t GetNextUpdate() const { return next_update; } + /** + * Purpose: Step the event timing forward if it is recurring. + * + * Inputs: None. + * + * Outputs: None. + */ void Step() { next_update += (recurring) ? frequency : 0; } From a048db0877bc55bac4b3e6346e6071ec28243783 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 11:27:35 -0400 Subject: [PATCH 35/42] Add doc strings to EventTypeDefinition --- source/sgp_mode/events/EventTypeDefinition.h | 82 ++++++++++++++++---- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/source/sgp_mode/events/EventTypeDefinition.h b/source/sgp_mode/events/EventTypeDefinition.h index 9131754a..b51aedb5 100644 --- a/source/sgp_mode/events/EventTypeDefinition.h +++ b/source/sgp_mode/events/EventTypeDefinition.h @@ -10,13 +10,16 @@ namespace sgpmode { -// The event type definition contains the information necessary to define a -// type of event: -// - event_id: unique, used to lookup handler function when processing events -// - event_name: unique, human-readable event type name, used to identify event -// in the events file -// - event_handler_fun: Function that handles processing an event of this type -// - event_json_loader_fun: Function that handles loading an event of this type +/** + * Purpose: The event type definition contains the information necessary to define a + * type of event. + * Event definition info: + * - event_id: unique, used to lookup handler function when processing events + * - event_name: unique, human-readable event type name, used to identify event + * in the events file + * - event_handler_fun: Function that handles processing an event of this type + * - event_json_loader_fun: Function that handles loading an event of this type + */ template class EventTypeDefinition { public: @@ -26,22 +29,49 @@ class EventTypeDefinition { // Event handler function type using fun_event_handler_t = std::function /* event being processed */ + world_t&, // sgp world to use helper functions and avoid circualr dependency + emp::Ptr // event being processed )>; using fun_json_loader_t = std::function(json_t&, world_t&)>; protected: - // @AML Review: if a variable should never by negative, prefer size_t over int - size_t event_id; // Event definition ID - std::string event_name; // Human-readable event type name - fun_event_handler_t event_handler_fun; // Event handler function + + /** + * Purpose: Event definition ID + */ + size_t event_id; + + /** + * Purpose: Human-readable event type name + */ + std::string event_name; + + /** + * Purpose: Event handler function + */ + fun_event_handler_t event_handler_fun; + + /** + * Purpose: Event's json loader function + */ fun_json_loader_t event_json_loader_fun; - std::string description; // Event type description + + /** + * Purpose: Event type description + */ + std::string description; + + /** + * Purpose: List of required fields for loading event configuration + */ emp::vector required_fields; public: + + /** + * Purpose: EventTypeDefinition constructor. + */ EventTypeDefinition( size_t a_event_id, const std::string& a_event_name, @@ -58,15 +88,37 @@ class EventTypeDefinition { required_fields(a_required_fields) { ; } + + /** + * Purpose: Get this event's required fields. + * + * Input: None. + * + * Output: List of strings indicating this event type's set of required fields + * for configuration. + */ const emp::vector& GetRequiredFields() const { return required_fields; } - // Run event handler function + /** + * Purpose: Runs the given event through its event handler. + * + * Input: Reference to the world and a pointer to the event to be processed. + * + * Output: None. + */ void Process(world_t& world, emp::Ptr event) const { event_handler_fun(world, event); } + /** + * Purpose: Loads event from json using event_json_loader_fun. + * + * Input: Json to load from, reference to the world. + * + * Output: Pointer to a new event object created by the event loader. + */ emp::Ptr LoadEventFromJSON(json_t& json, world_t& world) const { emp::Ptr event = event_json_loader_fun(json, world); event->SetEventTypeID(event_id); From 4b93164defa13a1438bd927657c2e33e3900e4ba Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 11:38:47 -0400 Subject: [PATCH 36/42] Add doc strings for EventTypeLibrary --- source/sgp_mode/events/EventTypeLibrary.h | 104 +++++++++++++++++++--- 1 file changed, 94 insertions(+), 10 deletions(-) diff --git a/source/sgp_mode/events/EventTypeLibrary.h b/source/sgp_mode/events/EventTypeLibrary.h index 703fe5b7..f1545d8c 100644 --- a/source/sgp_mode/events/EventTypeLibrary.h +++ b/source/sgp_mode/events/EventTypeLibrary.h @@ -9,6 +9,9 @@ namespace sgpmode { +/** + * Purpose: Manages a library of event type definitions. + */ template class EventTypeLibrary { public: @@ -19,51 +22,115 @@ class EventTypeLibrary { using fun_json_loader_t = typename event_type_def_t::fun_json_loader_t; protected: + + /** + * Purpose: List of event type definitions + */ emp::vector event_definitions; - std::unordered_map event_name_to_id; // event type/name mapped to index of event in event_types. Index is used as event_id + + /** + * Purpose: Mapping of event type names to their index in the event_definitions + * vector. + */ + std::unordered_map event_name_to_id; public: + /** + * Purpose: EventTypeLibrary constructor. + * + * Inputs: Boolean whether to add all default event types to this library on + * construction. + */ EventTypeLibrary(bool add_default_events=true) { if (add_default_events) { AddDefaultEventTypes(); } } - // Clear all event types from the library + /** + * Purpose: Clear all event types from the library + * + * Input: None. + * + * Output: None. + */ void Clear() { event_definitions.clear(); } - // Get event type id using string name + /** + * Purpose: Get event type id using string name + * + * Input: String event type name. + * + * Output: ID of that event type in the library. + */ size_t GetEventTypeID(const std::string& event_name) const { emp_assert(IsValidEventType(event_name)); return event_name_to_id.at(event_name); } + /** + * Purpose: Get an event type definition. + * + * Input: ID of the event type. + * + * Output: Event type definition associated with given event type id. + */ const event_type_def_t& GetEventTypeDefinition(size_t event_type_id) const { emp_assert(event_type_id < event_definitions.size()); return event_definitions[event_type_id]; } - // check if event type is valid (aka there is a function in all_event_functions that cna perform the event) + /** + * Purpose: Check if event type is contained in this library. + * + * Input: Event type name to check. + * + * Output: Boolean indicating whether event type name is in this library. + */ bool IsValidEventType(const std::string& event_name) const { return emp::Has(event_name_to_id, event_name); } - // Run appropriate event handler for given event. + /** + * Purpose: Run appropriate event handler for given event. + * + * Input: Reference to the world, pointer to the event to process. + * + * Output: None. + */ void ProcessEvent(world_t& world, emp::Ptr event_ptr) { const size_t event_type_id = event_ptr->GetEventTypeID(); event_definitions[event_type_id].Process(world, event_ptr); } - // Add default event types - // NOTE: Called by constructor by default. - // Should not be called a second time without clearing first. + /** + * Purpose: Add all default event types to this library. Called by constructor + * by default. Should not be called a second time without clearing + * the library first to avoid adding duplicate event types. + * + * Input: None. + * + * Output: None. + */ void AddDefaultEventTypes() { AddEventType(); } - // Add a new event type to the event library + /** + * Purpose: Add a new event type to the event library. Can be used to add event + * types that don't conform to expectations (e.g., Event.h / ExampleEvent.h) + * + * Inputs: + * - event_name: String indicating event type name + * - event_handler: Function for handling events of this type + * - event_loader: Function for loading events of this type + * - event_description: Description of this event type + * - event_required_cfg_fields: Required fields for configuring events of this type + * + * Output: None. + */ void AddEventType( const std::string& event_name, const fun_event_handler_t& event_handler, @@ -85,6 +152,17 @@ class EventTypeLibrary { event_name_to_id[event_name] = event_id; } + /** + * Purpose: Add event type to this event type library. + * + * Input: + * - event_name: String event type name + * - event_description: String event type description + * - event_required_cfg_fields: List of required fields for configuring events + * of this type. + * + * Output: None. + */ template void AddEventType( const std::string& event_name, @@ -105,7 +183,13 @@ class EventTypeLibrary { ); } - // Add event type assuming EVENT_T has static event spects + a process function + /** + * Purpose: Add event type assuming EVENT_T has static event specs + a process function + * + * Input: None. + * + * Output: None. + */ template void AddEventType() { AddEventType( From 4a3425ad73271906d95fdc3147f3cc1d0b963611 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 11:40:59 -0400 Subject: [PATCH 37/42] Update variable names for readability in Event.h as per PR review --- source/sgp_mode/events/event_types/Event.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/sgp_mode/events/event_types/Event.h b/source/sgp_mode/events/event_types/Event.h index bb63f907..6fa315af 100644 --- a/source/sgp_mode/events/event_types/Event.h +++ b/source/sgp_mode/events/event_types/Event.h @@ -244,16 +244,16 @@ void SetEventTimingFromJSON( // EventTiming timing; // Given as a single (integer) number? if (emp::is_digits(timing_str)) { - const size_t start_u = emp::from_string(timing_str); - event_ptr->ResetTiming(start_u); + const size_t start_update = emp::from_string(timing_str); + event_ptr->ResetTiming(start_update); } else { // Timing given as Start:Stop:Step emp::vector recurring_str = emp::slice(timing_str, ':'); emp_assert(recurring_str.size() == 3); - const size_t start_u = emp::from_string(recurring_str[0]); - const size_t stop_u = emp::from_string(recurring_str[1]); - const size_t freq = emp::from_string(recurring_str[2]); - event_ptr->ResetTiming(start_u, stop_u, freq); + const size_t start_update = emp::from_string(recurring_str[0]); + const size_t stop_update = emp::from_string(recurring_str[1]); + const size_t frequency = emp::from_string(recurring_str[2]); + event_ptr->ResetTiming(start_update, stop_update, frequency); } } From 5e34461eaf38a641c4015c3ad71c24556d48c9d4 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 15:14:13 -0400 Subject: [PATCH 38/42] Update events tests --- source/sgp_mode/SGPWorld.h | 16 +- source/sgp_mode/SGPWorldSetup.cc | 3 + source/sgp_mode/events/EventManager.h | 109 +-- source/sgp_mode/events/event_types/Event.h | 9 + .../functional_tests/TaskValueEvent.test.cc | 731 +++++++----------- .../add-reoccur-events.json | 0 .../change-reoccur-events.json | 0 .../hostsym-only-events.json | 0 .../multiple-tasks-events.json | 0 .../multiply-reoccur-events.json | 0 .../onetime-events.json | 0 .../reoccur-events.json | 2 +- .../test-events.json | 0 13 files changed, 368 insertions(+), 502 deletions(-) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/add-reoccur-events.json (100%) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/change-reoccur-events.json (100%) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/hostsym-only-events.json (100%) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/multiple-tasks-events.json (100%) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/multiply-reoccur-events.json (100%) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/onetime-events.json (100%) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/reoccur-events.json (88%) rename source/test/sgp_mode_test/{task_value_json_files => task-value-events-cfg}/test-events.json (100%) diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index 6fe383f0..ab2471db 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -663,12 +663,22 @@ class SGPWorld : public SymWorld { size_t GetTaskCount() const { return task_env.GetTaskCount(); } /* Accessor for host task profiles */ - const emp::BitVector& GetHostTaskProfile(const sgp_host_t& host){return fun_get_host_task_profile(host);} + const emp::BitVector& GetHostTaskProfile(const sgp_host_t& host) { return fun_get_host_task_profile(host); } /* Accessor for symbiont task profiles */ - const emp::BitVector& GetSymbiontTaskProfile(const sgp_sym_t& symbiont){return fun_get_sym_task_profile(symbiont);} + const emp::BitVector& GetSymbiontTaskProfile(const sgp_sym_t& symbiont) { return fun_get_sym_task_profile(symbiont); } - /** + /** + * Purpose: Accessor for event manager (const) + */ + const event_manager_t& GetEventManager() const { return event_manager; } + + /** + * Purpose: Accessor for event manager + */ + event_manager_t& GetEventManager() { return event_manager; } + + /** * Input: A host, a symbiont, the value of a task before applying nutrient interaction, and the task id. * Output: The change in the points the host will gain from the task after the nutrient interaction. * Purpose: To calculate the configured nutrient interaction for the given symbiont and task diff --git a/source/sgp_mode/SGPWorldSetup.cc b/source/sgp_mode/SGPWorldSetup.cc index 52d5e882..4d3f33a1 100644 --- a/source/sgp_mode/SGPWorldSetup.cc +++ b/source/sgp_mode/SGPWorldSetup.cc @@ -73,6 +73,9 @@ void SGPWorld::Setup() { // Setup any host-symbiont interactions SetupHostSymInteractions(); + // Load events + SetupEvents(); + // CureHost signal // TODO: move to default? Figure out how to remove duplication if (sgp_config.CURE()) { diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 960c0815..3cce1763 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -200,50 +200,6 @@ class EventManager { ReorderRecurringEvents(); } - /** - * Purpose: Internal helper function to resort the one-time events. - * One-time events should be reverse-sorted by their next update - * (i.e., soonest next update at end). One-time events must be sorted - * for processing to be correct. i.e., when adding a new event, must resort! - * - * Input: None. - * - * Output: None. - */ - void ReorderOneTimeEvents() { - std::sort( - one_time_events.begin(), - one_time_events.end(), - [](emp::Ptr a, emp::Ptr b) { - return a->GetNextUpdate() > b->GetNextUpdate(); - } - ); - } - - /** - * Purpose: Internal helper function to reorder recurring events. - * Should be reverse sorted by each event's next update (i.e., soonest - * next update at end of vector). Recurring events must be sorted for - * processing to be correct. I.e., when adding a new event, must sort! - * And, when re-queueing a recurring event, we must make sure it ends - * up in the appropriate spot by next update. - * - * Input: None. - * - * Output. None. - * - * - */ - void ReorderRecurringEvents() { - std::sort( - recurring_events.begin(), - recurring_events.end(), - [](emp::Ptr a, emp::Ptr b) { - return a->GetNextUpdate() > b->GetNextUpdate(); - } - ); - } - public: /** * Purpose: EventManager destructor. Responsible for cleaning up any event objects. @@ -280,6 +236,26 @@ class EventManager { recurring_events.clear(); } + /** + * Purpose: Accessor for recurring events. + * CAUTION: the event manager makes assumptions about the order of recurring + * events based on their timing. This accessor is used primarily for testing. + * If recurring events are modified via this function, you must resort them (ReorderRecurringEvents) + */ + emp::vector>& GetRecurringEvents() { + return recurring_events; + } + + /** + * Purpose: Accessor for one-time events. + * CAUTION: the event manager makes assumptions about the order of one-time + * events based on their timing. This accessor is used primarily for testing. + * If one-time events are modified via this function, you must resort them (ReorderOneTimeEvents) + */ + emp::vector>& GetOneTimeEvents() { + return one_time_events; + } + /** * Purpose: load in and process events configuratoin json file (includes * creating events and checking if they are valid) @@ -289,7 +265,6 @@ class EventManager { * Output: None. */ void LoadEventsFromJSON(const std::string& event_filepath, world_t& world) { - std::cout << "Loading events from event file." << std::endl; ClearEvents(); // === Parse events file === // Check if given events file exists. Exit if not. @@ -412,6 +387,50 @@ class EventManager { } } + /** + * Purpose: Hlper function to resort the one-time events. + * One-time events should be reverse-sorted by their next update + * (i.e., soonest next update at end). One-time events must be sorted + * for processing to be correct. i.e., when adding a new event, must resort! + * + * Input: None. + * + * Output: None. + */ + void ReorderOneTimeEvents() { + std::sort( + one_time_events.begin(), + one_time_events.end(), + [](emp::Ptr a, emp::Ptr b) { + return a->GetNextUpdate() > b->GetNextUpdate(); + } + ); + } + + /** + * Purpose: Helper function to reorder recurring events. + * Should be reverse sorted by each event's next update (i.e., soonest + * next update at end of vector). Recurring events must be sorted for + * processing to be correct. I.e., when adding a new event, must sort! + * And, when re-queueing a recurring event, we must make sure it ends + * up in the appropriate spot by next update. + * + * Input: None. + * + * Output. None. + * + * + */ + void ReorderRecurringEvents() { + std::sort( + recurring_events.begin(), + recurring_events.end(), + [](emp::Ptr a, emp::Ptr b) { + return a->GetNextUpdate() > b->GetNextUpdate(); + } + ); + } + }; } \ No newline at end of file diff --git a/source/sgp_mode/events/event_types/Event.h b/source/sgp_mode/events/event_types/Event.h index 6fa315af..1ebc313b 100644 --- a/source/sgp_mode/events/event_types/Event.h +++ b/source/sgp_mode/events/event_types/Event.h @@ -177,6 +177,15 @@ class Event { */ size_t GetEndUpdate() const { return timing.GetEndUpdate(); } + /** + * Purpose: Get this event's update frequency + * + * Input: None. + * + * Output: Unsigned integer indicating this event's update frequency. + */ + size_t GetFrequency() const { return timing.GetFrequency(); } + /** * Purpose: Get the update that this event will next occur. * diff --git a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc index bbb27c31..a32e6c0c 100644 --- a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc +++ b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc @@ -1,9 +1,18 @@ #include "emp/math/Random.hpp" +#include "../../test_utils.h" + +#include "../../../default_mode/SymWorld.h" +#include "../../../default_mode/WorldSetup.cc" +#include "../../../default_mode/DataNodes.h" #include "../../../sgp_mode/SGPWorld.h" #include "../../../sgp_mode/SGPWorld.cc" #include "../../../sgp_mode/SGPWorldSetup.cc" #include "../../../sgp_mode/SGPWorldData.cc" +#include "../../../sgp_mode/SGPW_InteractionMechanismSetup.cc" +#include "../../../sgp_mode/SGPW_TaskProfileSetup.cc" +#include "../../../sgp_mode/ProgramBuilder.h" +#include "../../../sgp_mode/events/EventManager.h" #include "../../../catch/catch.hpp" @@ -17,457 +26,378 @@ using sgp_sym_t = sgpmode::SGPSymbiont; TEST_CASE("TaskValueEvent One Time Events", "[sgp][events]") { - GIVEN("onetime-events.json"){ + GIVEN("onetime-events.json") { emp::Random random(2); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/onetime-events.json"); - - // Fill in any generic configs here - int num_updates = 5; - config.UPDATES(num_updates); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task-value-events-cfg/onetime-events.json"); + test_utils::SetWellMixed(config, 10, 0); + config.TASK_IO_BANK_SIZE(10); + config.UPDATES(100); config.SYM_ONLY_FIRST_TASK_CREDIT(1); config.HOST_ONLY_FIRST_TASK_CREDIT(1); - config.POP_SIZE(1); + config.INIT_POP_SIZE(1); config.CYCLES_PER_UPDATE(52); config.HOST_REPRO_RES(10000); config.SYM_HORIZ_TRANS_RES(10000); config.SYM_VERT_TRANS_RES(10000); - - WHEN("One time add, mul, and change task-value events are loaded"){ + WHEN("One time add, mul, and change task-value events are loaded") { world_t world(random, &config); world.Setup(); auto& builder = world.GetProgramBuilder(); + auto& event_manager = world.GetEventManager(); + REQUIRE(event_manager.GetOneTimeEvents().size() == 3); + REQUIRE(event_manager.GetRecurringEvents().size() == 0); + // Events are reverse sorted by update. + auto& one_time_events = event_manager.GetOneTimeEvents(); + REQUIRE(one_time_events[0]->GetEventType() == "task_value"); + REQUIRE(one_time_events[0]->GetStartUpdate() == 3); + REQUIRE(one_time_events[1]->GetEventType() == "task_value"); + REQUIRE(one_time_events[1]->GetStartUpdate() == 2); + REQUIRE(one_time_events[2]->GetEventType() == "task_value"); + REQUIRE(one_time_events[2]->GetStartUpdate() == 1); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); - THEN("Events are processed"){ - switch(world.GetUpdate()){ - case 0: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - CHECK(symbiont->GetPoints() == 0); - CHECK(host->GetPoints() == 0); - } - - case 1: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - CHECK(symbiont->GetPoints() == 5); - CHECK(host->GetPoints() == 5); - } - - case 2: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); - CHECK(symbiont->GetPoints() == 15); - CHECK(host->GetPoints() == 15); - } - - case 3: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); - CHECK(symbiont->GetPoints() == 30); // 80? - CHECK(host->GetPoints() == 30); // 80? - } - - case 4: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); - CHECK(symbiont->GetPoints() == 100); // 180? - CHECK(host->GetPoints() == 100); // 180? - } - } + // create host with NAND operation + emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // create symbiont with NAND operation + emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); + // add host and sym + world.AddOrgAt(host, 0); + host->AddSymbiont(symbiont); + + THEN("Events are processed") { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + world.Update(); // Should trigger any update-0 events. (none) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(event_manager.GetOneTimeEvents().size() == 3); + REQUIRE(event_manager.GetRecurringEvents().size() == 0); + + world.Update(); // Should trigger any update-1 events. (+10) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + REQUIRE(event_manager.GetOneTimeEvents().size() == 2); + REQUIRE(event_manager.GetRecurringEvents().size() == 0); + + world.Update(); // Should trigger any update-2 events. (*2) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); + REQUIRE(event_manager.GetOneTimeEvents().size() == 1); + REQUIRE(event_manager.GetRecurringEvents().size() == 0); + + world.Update(); // Should trigger any update-3 events. (=100) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); + REQUIRE(event_manager.GetOneTimeEvents().size() == 0); + REQUIRE(event_manager.GetRecurringEvents().size() == 0); + + world.Update(); // Should trigger any update-4 events. (none) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); } } } } - TEST_CASE("TaskValueEvent Multiple Reoccuring Events", "[sgp][events]") { - GIVEN("reoccur-events.json"){ + GIVEN("reoccur-events.json") { emp::Random random(11); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/reoccur-events.json"); - // Fill in any generic configs here - int num_updates = 7; - config.UPDATES(num_updates); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task-value-events-cfg/reoccur-events.json"); + config.UPDATES(100); + test_utils::SetWellMixed(config, 10, 0); + config.TASK_IO_BANK_SIZE(10); - WHEN("Reoccuring add and mul task-value events are loaded"){ + WHEN("Reoccuring add and mul task-value events are loaded") { world_t world(random, &config); world.Setup(); + auto& builder = world.GetProgramBuilder(); + auto& event_manager = world.GetEventManager(); + REQUIRE(event_manager.GetOneTimeEvents().size() == 0); + REQUIRE(event_manager.GetRecurringEvents().size() == 2); + // Events are reverse sorted by start update + auto& recurring_events = event_manager.GetRecurringEvents(); + REQUIRE(recurring_events[0]->GetEventType() == "task_value"); + REQUIRE(recurring_events[0]->GetStartUpdate() == 2); + REQUIRE(recurring_events[0]->GetEndUpdate() == 4); + REQUIRE(recurring_events[0]->GetFrequency() == 2); + REQUIRE(recurring_events[1]->GetEventType() == "task_value"); + REQUIRE(recurring_events[1]->GetStartUpdate() == 1); + REQUIRE(recurring_events[1]->GetEndUpdate() == 5); + REQUIRE(recurring_events[1]->GetFrequency() == 2); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); - THEN("Events are processed"){ - switch(world.GetUpdate()){ - case 0: { - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); - } - case 1: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); - } - case 2: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 10); - } - case 3: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 30); - } - case 4: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 35); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 35); - } - case 5: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 70); - } - case 6: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 70); - } - case 7: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 70); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 70); - } - } + THEN("Events are processed") { + + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(event_manager.GetOneTimeEvents().size() == 0); + REQUIRE(event_manager.GetRecurringEvents().size() == 2); + + // +5 event triggers on updates 1, 3, 5 + // x2 event triggers on updates 2, 4 + + world.Update(); // Trigger any update-0 events (none) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(recurring_events.size() == 2); + + world.Update(); // Trigger any update-1 events (+5) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); + REQUIRE(recurring_events.size() == 2); + + world.Update(); // Trigger any update-2 events (x2) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 20); + REQUIRE(recurring_events.size() == 2); + + world.Update(); // Trigger any update-3 events (+5) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + REQUIRE(recurring_events.size() == 2); + + world.Update(); // Trigger any update-4 events (x2) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); + REQUIRE(recurring_events.size() == 1); + + world.Update(); // Trigger any update-5 events (+5) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 55); + REQUIRE(recurring_events.size() == 0); + + world.Update(); // Trigger any update-6 events (none) + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 55); + REQUIRE(recurring_events.size() == 0); } } } } + TEST_CASE("TaskValueEvent Reoccuring Multiply Event", "[sgp][events]") { - GIVEN("multiply-reoccur-events.json"){ + GIVEN("multiply-reoccur-events.json") { emp::Random random(7); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - // Fill in any generic configs here - int num_updates = 6; - config.UPDATES(num_updates); + config.UPDATES(100); + test_utils::SetWellMixed(config, 10, 0); + config.TASK_IO_BANK_SIZE(10); config.SYM_ONLY_FIRST_TASK_CREDIT(1); config.HOST_ONLY_FIRST_TASK_CREDIT(1); - config.POP_SIZE(1); + config.INIT_POP_SIZE(1); config.CYCLES_PER_UPDATE(52); - config.HOST_REPRO_RES(10000); config.SYM_HORIZ_TRANS_RES(10000); config.SYM_VERT_TRANS_RES(10000); - WHEN("Reoccuring mul task-value events are loaded"){ - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json"); - + WHEN("Reoccuring mul task-value events are loaded") { + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task-value-events-cfg/multiply-reoccur-events.json"); + world_t world(random, &config); world.Setup(); auto& builder = world.GetProgramBuilder(); + auto& event_manager = world.GetEventManager(); + auto& recurring_events = event_manager.GetRecurringEvents(); + auto& one_time_events = event_manager.GetOneTimeEvents(); // create host with NAND operation emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); // create symbiont with NAND operation emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym + // add host and sym world.AddOrgAt(host, 0); host->AddSymbiont(symbiont); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); - THEN("Events are processed"){ - switch(world.GetUpdate()){ - case 0: { - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - } - case 1: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - } - case 2: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); - } - case 3: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); - } - case 4: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - } - case 5: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - } - case 6: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - } - } + THEN("Events are processed") { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(recurring_events.size() == 1); + world.Update(); // Update 0 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(recurring_events.size() == 1); + world.Update(); // Update 1 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); + REQUIRE(recurring_events.size() == 1); + world.Update(); // Update 2 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == -5); + REQUIRE(recurring_events.size() == 1); + world.Update(); // Update 3 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(recurring_events.size() == 0); + world.Update(); // Update 4 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + world.Update(); // Update 5 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); } } } } TEST_CASE("TaskValueEvent Reoccuring Add Event", "[sgp][events]") { - GIVEN("add-reoccur-events.json"){ + GIVEN("add-reoccur-events.json") { emp::Random random(44); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json"); - - // Fill in any generic configs here - int num_updates = 4; - config.UPDATES(num_updates); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task-value-events-cfg/add-reoccur-events.json"); + config.UPDATES(100); + test_utils::SetWellMixed(config, 10, 0); + config.TASK_IO_BANK_SIZE(10); - WHEN("Reoccuring add task-value events are loaded"){ + WHEN("Reoccuring add task-value events are loaded") { world_t world(random, &config); world.Setup(); auto& builder = world.GetProgramBuilder(); + auto& event_manager = world.GetEventManager(); + auto& recurring_events = event_manager.GetRecurringEvents(); + auto& one_time_events = event_manager.GetOneTimeEvents(); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); - THEN("Events are processed"){ - switch(world.GetUpdate()){ - case 0: { - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - } - case 1: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - } - case 2: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); - } - case 3: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); - } - case 4: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); - } - } + THEN("Events are processed") { + // 1:2:1: 1, 2 + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(recurring_events.size() == 1); + + world.Update(); // Update 0 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(recurring_events.size() == 1); + + world.Update(); // Update 1 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 10); + REQUIRE(recurring_events.size() == 1); + + world.Update(); // Update 2 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + REQUIRE(recurring_events.size() == 0); + + world.Update(); // Update 3 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); + REQUIRE(recurring_events.size() == 0); } } } } TEST_CASE("TaskValueEvent Reoccuring Change Event", "[sgp][events]") { - GIVEN("change-reoccur-events.json"){ + GIVEN("change-reoccur-events.json") { emp::Random random(60); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json"); - - // Fill in any generic configs here - int num_updates = 4; - config.UPDATES(num_updates); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task-value-events-cfg/change-reoccur-events.json"); + config.UPDATES(100); + test_utils::SetWellMixed(config, 10, 0); + config.TASK_IO_BANK_SIZE(10); - WHEN("Reoccuring change task-value event is loaded"){ + WHEN("Reoccuring change task-value event is loaded") { world_t world(random, &config); world.Setup(); auto& builder = world.GetProgramBuilder(); + auto& event_manager = world.GetEventManager(); + auto& recurring_events = event_manager.GetRecurringEvents(); + auto& one_time_events = event_manager.GetOneTimeEvents(); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); - THEN("Events are processed"){ - switch(world.GetUpdate()) { - case 0: { - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); - } - case 1: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); - } - case 2: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); - } - case 3: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); - } - case 4: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); - } - } + THEN("Events are processed") { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); + world.Update(); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 25); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 25); } } } } TEST_CASE("TaskValueEvent Host/Sym Only Events", "[sgp][events]") { - GIVEN("hostsym-only-events.json"){ + GIVEN("hostsym-only-events.json") { emp::Random random(19); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task-value-events-cfg/hostsym-only-events.json"); // Fill in any generic configs here - int num_updates = 4; - config.UPDATES(num_updates); + config.UPDATES(100); + test_utils::SetWellMixed(config, 10, 0); + config.TASK_IO_BANK_SIZE(10); config.SYM_ONLY_FIRST_TASK_CREDIT(1); config.HOST_ONLY_FIRST_TASK_CREDIT(1); - config.POP_SIZE(1); + config.INIT_POP_SIZE(1); config.CYCLES_PER_UPDATE(52); - config.HOST_REPRO_RES(10000); config.SYM_HORIZ_TRANS_RES(10000); config.SYM_VERT_TRANS_RES(10000); - WHEN("Host only and sym only events are loaded"){ + WHEN("Host only and sym only events are loaded") { world_t world(random, &config); world.Setup(); auto& builder = world.GetProgramBuilder(); + auto& event_manager = world.GetEventManager(); + auto& recurring_events = event_manager.GetRecurringEvents(); + auto& one_time_events = event_manager.GetOneTimeEvents(); // create host with NAND operation emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); // create symbiont with NAND operation emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym + // add host and sym world.AddOrgAt(host, 0); host->AddSymbiont(symbiont); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); - THEN("Events are processed"){ - switch(world.GetUpdate()){ - case 0:{ - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); - REQUIRE(host->GetPoints() == 0); - REQUIRE(symbiont->GetPoints() == 0); - } - case 1: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - world.Update(); - - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); - REQUIRE(host->GetPoints() == 5); - REQUIRE(symbiont->GetPoints() == 5); - } - case 2: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - world.Update(); - - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); - REQUIRE(host->GetPoints() == 50); - REQUIRE(symbiont->GetPoints() == 100); - } - case 3: { - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - world.Update(); - - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); - // incorrect output - CHECK(host->GetPoints() == 50); - CHECK(symbiont->GetPoints() == 100); - } - } + THEN("Events are processed") { + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + + world.Update(); // Update 0 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + + world.Update(); // Update 1 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); + + world.Update(); // Update 2 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 50); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); } } } } + TEST_CASE("TaskValueEvent Mulitple Task Names Events", "[sgp][events]") { - GIVEN("multiple-tasks-events.json"){ + GIVEN("multiple-tasks-events.json") { emp::Random random(1); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json"); - // Fill in any generic configs here - int num_updates = 2; - config.UPDATES(num_updates); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task-value-events-cfg/multiple-tasks-events.json"); + config.UPDATES(100); + test_utils::SetWellMixed(config, 10, 0); + config.TASK_IO_BANK_SIZE(10); - WHEN("Multiple tasks are in a single event"){ + WHEN("Multiple tasks are in a single event") { world_t world(random, &config); world.Setup(); auto& builder = world.GetProgramBuilder(); + auto& event_manager = world.GetEventManager(); + auto& recurring_events = event_manager.GetRecurringEvents(); + auto& one_time_events = event_manager.GetOneTimeEvents(); - // get NAND Task id + // get task ids const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); const size_t not_task_id = world.GetTaskEnv().GetTaskSet().GetID("NOT"); const size_t or_task_id = world.GetTaskEnv().GetTaskSet().GetID("OR"); @@ -478,152 +408,47 @@ TEST_CASE("TaskValueEvent Mulitple Task Names Events", "[sgp][events]") { const size_t andnot_task_id = world.GetTaskEnv().GetTaskSet().GetID("AND_NOT"); const size_t ornot_task_id = world.GetTaskEnv().GetTaskSet().GetID("OR_NOT"); - THEN("Events are processed"){ - switch(world.GetUpdate()){ - case 1: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(not_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(and_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(or_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(xor_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nor_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(equ_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(andnot_task_id).task_value == 5); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(ornot_task_id).task_value == 5); - } - case 2: { - world.Update(); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 100); - - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(not_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(and_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(or_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(xor_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(nor_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(equ_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(andnot_task_id).task_value == 100); - REQUIRE(world.GetTaskEnv().GetSymTaskReq(ornot_task_id).task_value == 100); - } - } - } - } - } -} - - - -// ***** CREATE NAND TEST ***** -TEST_CASE("Checking if NAND op is done for more than two updates", "[sgp][events]") { - GIVEN("onetime-events.json"){ - emp::Random random(2); - sgpmode::SymConfigSGP config; - config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - config.EVENTS_CFG_PATH("source/test/sgp_mode_test/task_value_json_files/onetime-events.json"); - - // Fill in any generic configs here - int num_updates = 5; - config.UPDATES(num_updates); - config.SYM_ONLY_FIRST_TASK_CREDIT(1); - config.HOST_ONLY_FIRST_TASK_CREDIT(1); - config.POP_SIZE(1); - config.CYCLES_PER_UPDATE(52); - - config.HOST_REPRO_RES(10000); - config.SYM_HORIZ_TRANS_RES(10000); - config.SYM_VERT_TRANS_RES(10000); - - - WHEN("One time add, mul, and change task-value events are loaded"){ - world_t world(random, &config); - world.Setup(); - auto& builder = world.GetProgramBuilder(); - - // create host with NAND operation - emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // create symbiont with NAND operation - emp::Ptr symbiont = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); - // add host and sym - world.AddOrgAt(host, 0); - host->AddSymbiont(symbiont); - - // get NAND Task id - const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); - - THEN("Events are processed"){ - switch(world.GetUpdate()){ - case 0: { - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - - CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 0); - CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 0); - - CHECK(symbiont->GetPoints() == 0); - CHECK(host->GetPoints() == 0); - } - - case 1: { - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); - - CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 1); - CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 1); - - CHECK(symbiont->GetPoints() == 5); - CHECK(host->GetPoints() == 5); - } - - case 2: { - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 15); - - CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 2); - CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 2); - - CHECK(symbiont->GetPoints() == 20); - CHECK(host->GetPoints() == 20); - } - - case 3: { - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 30); - - CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 4); - CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 4); - - CHECK(symbiont->GetPoints() == 80); - CHECK(host->GetPoints() == 80); - } - - case 4: { - world.Update(); - CHECK(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); - - CHECK(host->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 5); - CHECK(symbiont->GetHardware().GetCPUState().GetTaskPerformanceCount(nand_task_id) == 5); - - CHECK(symbiont->GetPoints() == 180); - CHECK(host->GetPoints() == 180); - } - } + THEN("Events are processed") { + world.Update(); // Update 0 events + // Check sym task values + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(not_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(and_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(or_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(xor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nor_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(equ_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(andnot_task_id).task_value == 5); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(ornot_task_id).task_value == 5); + + world.Update(); // Update 1 events + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(and_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(or_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(xor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(nor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(equ_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(andnot_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetHostTaskReq(ornot_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nand_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(not_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(and_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(or_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(xor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(nor_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(equ_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(andnot_task_id).task_value == 100); + REQUIRE(world.GetTaskEnv().GetSymTaskReq(ornot_task_id).task_value == 100); } } } diff --git a/source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json b/source/test/sgp_mode_test/task-value-events-cfg/add-reoccur-events.json similarity index 100% rename from source/test/sgp_mode_test/task_value_json_files/add-reoccur-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/add-reoccur-events.json diff --git a/source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json b/source/test/sgp_mode_test/task-value-events-cfg/change-reoccur-events.json similarity index 100% rename from source/test/sgp_mode_test/task_value_json_files/change-reoccur-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/change-reoccur-events.json diff --git a/source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json b/source/test/sgp_mode_test/task-value-events-cfg/hostsym-only-events.json similarity index 100% rename from source/test/sgp_mode_test/task_value_json_files/hostsym-only-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/hostsym-only-events.json diff --git a/source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json b/source/test/sgp_mode_test/task-value-events-cfg/multiple-tasks-events.json similarity index 100% rename from source/test/sgp_mode_test/task_value_json_files/multiple-tasks-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/multiple-tasks-events.json diff --git a/source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json b/source/test/sgp_mode_test/task-value-events-cfg/multiply-reoccur-events.json similarity index 100% rename from source/test/sgp_mode_test/task_value_json_files/multiply-reoccur-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/multiply-reoccur-events.json diff --git a/source/test/sgp_mode_test/task_value_json_files/onetime-events.json b/source/test/sgp_mode_test/task-value-events-cfg/onetime-events.json similarity index 100% rename from source/test/sgp_mode_test/task_value_json_files/onetime-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/onetime-events.json diff --git a/source/test/sgp_mode_test/task_value_json_files/reoccur-events.json b/source/test/sgp_mode_test/task-value-events-cfg/reoccur-events.json similarity index 88% rename from source/test/sgp_mode_test/task_value_json_files/reoccur-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/reoccur-events.json index a6b38fb8..886c5b52 100644 --- a/source/test/sgp_mode_test/task_value_json_files/reoccur-events.json +++ b/source/test/sgp_mode_test/task-value-events-cfg/reoccur-events.json @@ -5,7 +5,7 @@ "task_name": "NAND", "action": "add", "value": 5, - "timing": "1:3:1", + "timing": "1:5:2", "group": "shared" }, { diff --git a/source/test/sgp_mode_test/task_value_json_files/test-events.json b/source/test/sgp_mode_test/task-value-events-cfg/test-events.json similarity index 100% rename from source/test/sgp_mode_test/task_value_json_files/test-events.json rename to source/test/sgp_mode_test/task-value-events-cfg/test-events.json From de892e55ed4b07b65d633268ff34d884a7f2a24f Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 15:14:49 -0400 Subject: [PATCH 39/42] Update spacing for consistency --- .../unit_tests/SGPHardware.test.cc | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc b/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc index 869abbad..4d1b4650 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc @@ -12,8 +12,8 @@ #include #include -TEST_CASE("Test Printing Simple Instructions", "[sgp]"){ - using world_t = sgpmode::SGPWorld; +TEST_CASE("Test Printing Simple Instructions", "[sgp]") { + using world_t = sgpmode::SGPWorld; using cpu_state_t = sgpmode::CPUState; using hw_spec_t = sgpmode::SGPHardwareSpec; using hardware_t = sgpmode::SGPHardware; @@ -27,7 +27,7 @@ TEST_CASE("Test Printing Simple Instructions", "[sgp]"){ config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); config.FILE_PATH("hardware_test_output"); - config.POP_SIZE(1); + config.INIT_POP_SIZE(1); config.START_MOI(0); config.TASK_IO_UNIQUE_OUTPUT(true); @@ -42,7 +42,7 @@ TEST_CASE("Test Printing Simple Instructions", "[sgp]"){ std::ostringstream output; - WHEN("Program contains Nop Instruction"){ + WHEN("Program contains Nop Instruction") { program_t program; prog_builder.AddStartAnchor(program); prog_builder.AddInst(program, "Nop-0", 0); @@ -51,11 +51,11 @@ TEST_CASE("Test Printing Simple Instructions", "[sgp]"){ hw.PrintCode(output); - THEN("Global Anchor and Nop Instruction should be printed"){ + THEN("Global Anchor and Nop Instruction should be printed") { REQUIRE(output.str() == "AA:\n nop-0 \n"); } } - WHEN("Program contains Increment Instruction"){ + WHEN("Program contains Increment Instruction") { program_t program; prog_builder.AddStartAnchor(program); prog_builder.AddInst(program, "Increment", 0); @@ -64,11 +64,11 @@ TEST_CASE("Test Printing Simple Instructions", "[sgp]"){ hw.PrintCode(output); - THEN("Global Anchor and Increment Instruction should be printed"){ + THEN("Global Anchor and Increment Instruction should be printed") { REQUIRE(output.str() == "AA:\n increment r0\n"); } } - WHEN("Program contains Decrement Instruction"){ + WHEN("Program contains Decrement Instruction") { program_t program; prog_builder.AddStartAnchor(program); prog_builder.AddInst(program, "Decrement", 0); @@ -77,12 +77,12 @@ TEST_CASE("Test Printing Simple Instructions", "[sgp]"){ hw.PrintCode(output); - THEN("Global Anchor and Increment Instruction should be printed"){ + THEN("Global Anchor and Increment Instruction should be printed") { REQUIRE(output.str() == "AA:\n decrement r0\n"); } } - WHEN("Program contains Nand Instruction"){ + WHEN("Program contains Nand Instruction") { program_t program; prog_builder.AddStartAnchor(program); prog_builder.AddInst(program, "Nand", 0, 1, 0); @@ -91,13 +91,13 @@ TEST_CASE("Test Printing Simple Instructions", "[sgp]"){ hw.PrintCode(output); - THEN("Global Anchor and Nand Instruction should be printed"){ + THEN("Global Anchor and Nand Instruction should be printed") { REQUIRE(output.str() == "AA:\n nand r0, r1, r0\n"); } } } -TEST_CASE("Test Printing Complex Instructions", "[sgp]"){ +TEST_CASE("Test Printing Complex Instructions", "[sgp]") { using world_t = sgpmode::SGPWorld; using cpu_state_t = sgpmode::CPUState; using hw_spec_t = sgpmode::SGPHardwareSpec; @@ -112,7 +112,7 @@ TEST_CASE("Test Printing Complex Instructions", "[sgp]"){ config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); config.FILE_PATH("hardware_test_output"); - config.POP_SIZE(1); + config.INIT_POP_SIZE(1); config.START_MOI(0); config.TASK_IO_UNIQUE_OUTPUT(true); @@ -127,7 +127,7 @@ TEST_CASE("Test Printing Complex Instructions", "[sgp]"){ std::ostringstream output; - WHEN("Program contains JumpIfNEq Instruction"){ + WHEN("Program contains JumpIfNEq Instruction") { program_t program; tag_t start_tag(prog_builder.GetStartTag()); tag_t tag1("0000000000000000000000000000000000000000000000000000000000000001"); @@ -145,12 +145,12 @@ TEST_CASE("Test Printing Complex Instructions", "[sgp]"){ hw.PrintCode(output); - THEN("Global Anchor and Nop Instruction should be printed"){ + THEN("Global Anchor and Nop Instruction should be printed") { REQUIRE(output.str() == "AA:\n nop-0 \n nop-0 \n nop-0 \nAB:\n nop-0 \n nop-0 \n jumpifneq r0, r1, AB\n"); } } - WHEN("Program contains JumpIfEq Instruction"){ + WHEN("Program contains JumpIfEq Instruction") { program_t program; tag_t start_tag(prog_builder.GetStartTag()); tag_t tag1("0000000000000000000000000000000000000000000000000000000000000001"); @@ -168,12 +168,12 @@ TEST_CASE("Test Printing Complex Instructions", "[sgp]"){ hw.PrintCode(output); - THEN("Global Anchor and Nop Instruction should be printed"){ + THEN("Global Anchor and Nop Instruction should be printed") { REQUIRE(output.str() == "AA:\n nop-0 \n nop-0 \n nop-0 \nAB:\n nop-0 \n nop-0 \n jumpifeq r0, r1, AB\n"); } } - WHEN("Program contains JumpIfLess Instruction"){ + WHEN("Program contains JumpIfLess Instruction") { program_t program; tag_t start_tag(prog_builder.GetStartTag()); tag_t tag1("0000000000000000000000000000000000000000000000000000000000000010"); @@ -191,9 +191,9 @@ TEST_CASE("Test Printing Complex Instructions", "[sgp]"){ hw.PrintCode(output); - THEN("Global Anchor and Nop Instruction should be printed"){ + THEN("Global Anchor and Nop Instruction should be printed") { REQUIRE(output.str() == "AA:\n nop-0 \n nop-0 \n nop-0 \nAB:\n nop-0 \n nop-0 \n jumpifless r0, r1, AB\n"); } } - + } \ No newline at end of file From 537df7457b0a860d06e3a963d83db5200d428a12 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 15:53:28 -0400 Subject: [PATCH 40/42] Add events file cfg to sgpmode tests, fix pointer that wasn't being deleted in sgp host tests --- .../functional_tests/HealthMode.test.cc | 2 +- .../functional_tests/NutrientMode.test.cc | 1 + .../PopulationStructure.test.cc | 4 ++ .../functional_tests/ProgramBuilder.test.cc | 1 + .../SGPHardware_Tasks.test.cc | 1 + .../SGPHost_Reproduce.test.cc | 4 ++ .../SGPHost_SGPSymbiont.test.cc | 1 + .../SGPHost_SGPSymbiont_Tasks.test.cc | 1 + .../functional_tests/SGPHost_Tasks.test.cc | 1 + .../SGPSymbiont_Reproduce.test.cc | 5 ++ .../functional_tests/SGPWorld.test.cc | 2 + .../SGPWorldData_SGPWorld.test.cc | 1 + .../functional_tests/StressMode.test.cc | 2 + .../functional_tests/TaskValueEvent.test.cc | 16 ------ .../need_updating/SenseTask_Tasks.test.cc | 32 ++++++------ .../unit_tests/Instructions.test.cc | 3 ++ .../unit_tests/SGPCureHosts.test.cc | 1 + .../unit_tests/SGPHardware.test.cc | 2 + .../sgp_mode_test/unit_tests/SGPHost.test.cc | 50 ++++++++++++------- .../unit_tests/SGPSymbiont.test.cc | 3 ++ .../sgp_mode_test/unit_tests/SGPWorld.test.cc | 14 ++++++ .../unit_tests/SGPWorldData.test.cc | 1 + .../unit_tests/SGPWorldSetup.test.cc | 5 ++ 23 files changed, 103 insertions(+), 50 deletions(-) diff --git a/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc b/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc index 94b69e08..692d48e9 100644 --- a/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc +++ b/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc @@ -45,9 +45,9 @@ void ConfigureHealthTestConfig(sgpmode::SymConfigSGP& config) { config.WORLD_WIDTH(2); config.WORLD_HEIGHT(2); config.SPATIAL_STRUCT_MODE("well-mixed"); - // general sgp settings config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); config.CYCLES_PER_UPDATE(4); diff --git a/source/test/sgp_mode_test/functional_tests/NutrientMode.test.cc b/source/test/sgp_mode_test/functional_tests/NutrientMode.test.cc index c129b0ef..4eec61b3 100644 --- a/source/test/sgp_mode_test/functional_tests/NutrientMode.test.cc +++ b/source/test/sgp_mode_test/functional_tests/NutrientMode.test.cc @@ -26,6 +26,7 @@ TEST_CASE("SGPSymbiont Normal Nutrient without multiplier", "[sgp][sgp-functiona config.NUTRIENT_DONATE_PROP(0.5); config.NUTRIENT_STEAL_PROP(0.5); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_IO_BANK_SIZE(10); config.NUTRIENT_INTERACTION_MULTIPLIER(1.0); // for testing, set multiplier to 1 so we can directly compare expected transfer to actual transfer test_utils::SetWellMixed(config, 1, 0); diff --git a/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc b/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc index 8ae8e59e..3df02c74 100644 --- a/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc +++ b/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc @@ -38,6 +38,7 @@ TEST_CASE( "Spatial structure grid mode (sgp mode)", "[sgp][spatial-structure]" config.WORLD_HEIGHT(height); config.TASK_IO_BANK_SIZE(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FREE_LIVING_SYMS(1); config.MOVE_FREE_SYMS(1); config.SYM_HORIZ_TRANS_RES(0); @@ -277,6 +278,7 @@ TEST_CASE("Spatial structure loaded from files (sgp mode)", "[sgp][spatial-struc config.SYM_HORIZ_TRANS_RES(0); config.TASK_IO_BANK_SIZE(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); size_t sym_limit = 10; config.SYM_LIMIT(sym_limit); config.INIT_POP_SIZE(0); @@ -344,6 +346,7 @@ TEST_CASE("World uses custom spatial structure (sgp mode)", "[sgp][spatial-struc config.SYM_LIMIT(sym_limit); config.TASK_IO_BANK_SIZE(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.SPATIAL_STRUCT_MODE("load"); config.SPATIAL_STRUCT_LOAD_MODE("edges"); config.SPATIAL_STRUCT_CFG_PATH("source/test/data/chain-edges.csv"); @@ -476,6 +479,7 @@ TEST_CASE("Organism in isolated position neither reproduces nor receives offspri config.START_MOI(0); config.TASK_IO_BANK_SIZE(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); diff --git a/source/test/sgp_mode_test/functional_tests/ProgramBuilder.test.cc b/source/test/sgp_mode_test/functional_tests/ProgramBuilder.test.cc index 86a3efbb..550c8d2e 100644 --- a/source/test/sgp_mode_test/functional_tests/ProgramBuilder.test.cc +++ b/source/test/sgp_mode_test/functional_tests/ProgramBuilder.test.cc @@ -45,6 +45,7 @@ TEST_CASE("ProgramBuilder generates a programs as advertised", "[sgp]") { config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("ProgramBuilder_test_output"); config.START_MOI(0); config.TASK_IO_UNIQUE_OUTPUT(true); diff --git a/source/test/sgp_mode_test/functional_tests/SGPHardware_Tasks.test.cc b/source/test/sgp_mode_test/functional_tests/SGPHardware_Tasks.test.cc index 6d89635e..19c29039 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPHardware_Tasks.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPHardware_Tasks.test.cc @@ -28,6 +28,7 @@ TEST_CASE("Ancestor hardware can attempt reproduction and do NOT", "[sgp]") { config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPHardware_test_output"); config.TASK_IO_BANK_SIZE(10); test_utils::SetWellMixed(config, 1); diff --git a/source/test/sgp_mode_test/functional_tests/SGPHost_Reproduce.test.cc b/source/test/sgp_mode_test/functional_tests/SGPHost_Reproduce.test.cc index 305d4752..b280e15b 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPHost_Reproduce.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPHost_Reproduce.test.cc @@ -34,6 +34,7 @@ TEST_CASE("Reproduction without points or mutations", "[sgp][sgp-functional]") { config.SEED(62); config.START_MOI(0); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); @@ -84,6 +85,7 @@ TEST_CASE("Mutations occur during reproduction", "[sgp]") { config.SGP_MUT_PER_BIT_RATE(1.0); config.HOST_REPRO_RES(0); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); @@ -119,6 +121,7 @@ TEST_CASE("SGPHost Reproduce function results in correct parental task tracking" config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPHost_test_output"); test_utils::SetWellMixed(config, 1, 1); config.START_MOI(0); @@ -196,6 +199,7 @@ TEST_CASE("SGPHost lineage tracking test", "[sgp]") { config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPHost_test_output"); test_utils::SetWellMixed(config, 1, 1); config.TASK_IO_BANK_SIZE(10); diff --git a/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont.test.cc b/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont.test.cc index 7c07ed80..5ce489bb 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont.test.cc @@ -30,6 +30,7 @@ TEST_CASE("Host Process allows symbionts to process", "[sgp][sgp-functional]") { sgpmode::SymConfigSGP config; config.CYCLES_PER_UPDATE(8); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_IO_BANK_SIZE(10); test_utils::SetWellMixed(config, 1, 1); world_t world(random, &config); diff --git a/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont_Tasks.test.cc b/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont_Tasks.test.cc index 91a503c9..4b7a3086 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont_Tasks.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPHost_SGPSymbiont_Tasks.test.cc @@ -37,6 +37,7 @@ TEST_CASE("Only first task credit for hosts vs. symbionts","[sgp]"){ config.TASK_PROFILE_MODE("self-all"); config.VT_TASK_MATCH(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.CYCLES_PER_UPDATE(52); config.TASK_IO_BANK_SIZE(10); test_utils::SetWellMixed(config, 1, 1); diff --git a/source/test/sgp_mode_test/functional_tests/SGPHost_Tasks.test.cc b/source/test/sgp_mode_test/functional_tests/SGPHost_Tasks.test.cc index 0e1250dc..6ac0a726 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPHost_Tasks.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPHost_Tasks.test.cc @@ -29,6 +29,7 @@ TEST_CASE("Host Task Credit", "[sgp]") { test_utils::SetWellMixed(config, 1, 1); config.SYM_LIMIT(2); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); diff --git a/source/test/sgp_mode_test/functional_tests/SGPSymbiont_Reproduce.test.cc b/source/test/sgp_mode_test/functional_tests/SGPSymbiont_Reproduce.test.cc index 5666092c..7147651b 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPSymbiont_Reproduce.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPSymbiont_Reproduce.test.cc @@ -29,6 +29,7 @@ TEST_CASE("SGPSymbiont Reproduce", "[sgp][sgp-functional]") { size_t not_id = 1; sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_MODE("parent-all"); config.TASK_IO_BANK_SIZE(10); test_utils::SetWellMixed(config, 1, 1); @@ -111,6 +112,7 @@ TEST_CASE("SGPSymbiont Vertical Transmission", "[sgp][sgp-functional]"){ config.DATA_INT(26); config.VERTICAL_TRANSMISSION(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_IO_BANK_SIZE(10); test_utils::SetWellMixed(config, 100, 0); GIVEN("A host infected with a symbiont in the world when self-all task mode and task match required for vt"){ @@ -250,6 +252,7 @@ TEST_CASE("SGPSymbiont Vertical Transmission off", "[sgp][sgp-functional]"){ emp::Random random(51); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.DATA_INT(26); config.HOST_REPRO_RES(0); config.SYM_VERT_TRANS_RES(0); @@ -296,6 +299,7 @@ TEST_CASE("SGPSymbiont Horizontal Transmission", "[sgp][sgp-functional]"){ config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE("task-profile-compatible"); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.OUSTING(true); // This way, if the offspring happens to try to infect parent's host, the tests still pass because successful horizontal transmission. GIVEN("Two hosts one of which is infected with a symbiont, horiz task match on and self-all task profile mode"){ @@ -532,6 +536,7 @@ TEST_CASE("SGPSymbiont Reproduce tracks lineage task information", "[sgp][sgp-fu config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPSymbiont_test_output"); config.START_MOI(1); // Initialize host with a symbiont config.TASK_IO_UNIQUE_OUTPUT(true); diff --git a/source/test/sgp_mode_test/functional_tests/SGPWorld.test.cc b/source/test/sgp_mode_test/functional_tests/SGPWorld.test.cc index f660d149..baedf909 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPWorld.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPWorld.test.cc @@ -28,6 +28,7 @@ TEST_CASE("A world containing a single infected host and its symbiont is updated test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); sgpmode::SGPWorld world(random, &config); world.Setup(); @@ -64,6 +65,7 @@ TEST_CASE("A world containing a single uninfected host is updated correctly", "[ test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); sgpmode::SGPWorld world(random, &config); world.Setup(); diff --git a/source/test/sgp_mode_test/functional_tests/SGPWorldData_SGPWorld.test.cc b/source/test/sgp_mode_test/functional_tests/SGPWorldData_SGPWorld.test.cc index 6dee95b1..99e91d53 100644 --- a/source/test/sgp_mode_test/functional_tests/SGPWorldData_SGPWorld.test.cc +++ b/source/test/sgp_mode_test/functional_tests/SGPWorldData_SGPWorld.test.cc @@ -52,6 +52,7 @@ TEST_CASE("Correct data files are created and written to", "[sgp][sgp-functional size_t prog_length = 20; config.CYCLES_PER_UPDATE(prog_length); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPData_test_output"); emp::Random random(config.SEED()); diff --git a/source/test/sgp_mode_test/functional_tests/StressMode.test.cc b/source/test/sgp_mode_test/functional_tests/StressMode.test.cc index f8a6d10a..9e087f72 100644 --- a/source/test/sgp_mode_test/functional_tests/StressMode.test.cc +++ b/source/test/sgp_mode_test/functional_tests/StressMode.test.cc @@ -24,6 +24,7 @@ TEST_CASE("Stress event", "[sgp]") { config.SYM_VERT_TRANS_RES(1000); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPStressMode_test_output"); test_utils::SetWellMixed(config, 100, 100); config.TASK_IO_BANK_SIZE(10); @@ -130,6 +131,7 @@ TEST_CASE("Stress hosts evolve", "[sgp][sgp-functional]") { config.HOST_REPRO_RES(20); config.BASE_DEATH_CHANCE(0); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); config.CYCLES_PER_UPDATE(4); diff --git a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc index a32e6c0c..ffd1ac13 100644 --- a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc +++ b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc @@ -113,7 +113,6 @@ TEST_CASE("TaskValueEvent Multiple Reoccuring Events", "[sgp][events]") { world_t world(random, &config); world.Setup(); - auto& builder = world.GetProgramBuilder(); auto& event_manager = world.GetEventManager(); REQUIRE(event_manager.GetOneTimeEvents().size() == 0); REQUIRE(event_manager.GetRecurringEvents().size() == 2); @@ -197,8 +196,6 @@ TEST_CASE("TaskValueEvent Reoccuring Multiply Event", "[sgp][events]") { auto& builder = world.GetProgramBuilder(); auto& event_manager = world.GetEventManager(); auto& recurring_events = event_manager.GetRecurringEvents(); - auto& one_time_events = event_manager.GetOneTimeEvents(); - // create host with NAND operation emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); // create symbiont with NAND operation @@ -247,10 +244,8 @@ TEST_CASE("TaskValueEvent Reoccuring Add Event", "[sgp][events]") { WHEN("Reoccuring add task-value events are loaded") { world_t world(random, &config); world.Setup(); - auto& builder = world.GetProgramBuilder(); auto& event_manager = world.GetEventManager(); auto& recurring_events = event_manager.GetRecurringEvents(); - auto& one_time_events = event_manager.GetOneTimeEvents(); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); @@ -293,10 +288,6 @@ TEST_CASE("TaskValueEvent Reoccuring Change Event", "[sgp][events]") { WHEN("Reoccuring change task-value event is loaded") { world_t world(random, &config); world.Setup(); - auto& builder = world.GetProgramBuilder(); - auto& event_manager = world.GetEventManager(); - auto& recurring_events = event_manager.GetRecurringEvents(); - auto& one_time_events = event_manager.GetOneTimeEvents(); // get NAND Task id const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); @@ -344,9 +335,6 @@ TEST_CASE("TaskValueEvent Host/Sym Only Events", "[sgp][events]") { world.Setup(); auto& builder = world.GetProgramBuilder(); auto& event_manager = world.GetEventManager(); - auto& recurring_events = event_manager.GetRecurringEvents(); - auto& one_time_events = event_manager.GetOneTimeEvents(); - // create host with NAND operation emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); // create symbiont with NAND operation @@ -392,11 +380,7 @@ TEST_CASE("TaskValueEvent Mulitple Task Names Events", "[sgp][events]") { WHEN("Multiple tasks are in a single event") { world_t world(random, &config); world.Setup(); - auto& builder = world.GetProgramBuilder(); auto& event_manager = world.GetEventManager(); - auto& recurring_events = event_manager.GetRecurringEvents(); - auto& one_time_events = event_manager.GetOneTimeEvents(); - // get task ids const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); const size_t not_task_id = world.GetTaskEnv().GetTaskSet().GetID("NOT"); diff --git a/source/test/sgp_mode_test/need_updating/SenseTask_Tasks.test.cc b/source/test/sgp_mode_test/need_updating/SenseTask_Tasks.test.cc index c536fd6a..2bfe59f3 100644 --- a/source/test/sgp_mode_test/need_updating/SenseTask_Tasks.test.cc +++ b/source/test/sgp_mode_test/need_updating/SenseTask_Tasks.test.cc @@ -26,6 +26,7 @@ TEST_CASE("Test host SenseTask instruction after a rewarded task", "[sgp]"){ config.CYCLES_PER_UPDATE(0); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("Instructions_test_output"); config.START_MOI(0); config.TASK_IO_UNIQUE_OUTPUT(true); @@ -57,10 +58,10 @@ TEST_CASE("Test host SenseTask instruction after a rewarded task", "[sgp]"){ host_hw.Reset(); host_hw.SetProgram(host_program); world.AssignNewEnvIO(host_hw.GetCPUState()); - - // NOT is currently not rewarded. + + // NOT is currently not rewarded. REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value < 0); - + // Initial register values host_hw.SetRegisters({3, 2, 5}); @@ -78,6 +79,7 @@ TEST_CASE("Test host SenseTask instruction after a punished task", "[sgp]"){ config.CYCLES_PER_UPDATE(0); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("Instructions_test_output"); config.START_MOI(0); config.TASK_IO_UNIQUE_OUTPUT(true); @@ -110,17 +112,17 @@ TEST_CASE("Test host SenseTask instruction after a punished task", "[sgp]"){ host_hw.SetProgram(host_program); world.AssignNewEnvIO(host_hw.GetCPUState()); - // NAND is not currently punished. + // NAND is not currently punished. REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value > 0); - - + + // Initial register values host_hw.SetRegisters({7, 12, 9}); // Run host program host_hw.RunCPUStep(5); - + THEN("SenseTask puts a 1 into register 1"){ REQUIRE(host_hw.GetRegister(1) == 1); } @@ -132,6 +134,7 @@ TEST_CASE("Test symbiont SenseTask instruction after a punished task", "[sgp]"){ config.CYCLES_PER_UPDATE(0); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("Instructions_test_output"); config.START_MOI(1); config.TASK_IO_UNIQUE_OUTPUT(true); @@ -161,15 +164,15 @@ TEST_CASE("Test symbiont SenseTask instruction after a punished task", "[sgp]"){ sym_hw.SetProgram(sym_program); world.AssignNewEnvIO(sym_hw.GetCPUState()); - // NOT is currently punished. + // NOT is currently punished. REQUIRE(world.GetTaskEnv().GetHostTaskReq(not_task_id).task_value < 0); - + // Initial register values sym_hw.SetRegisters({3, 2, 5}); // Run symbiont program - sym_hw.RunCPUStep(4); - + sym_hw.RunCPUStep(4); + THEN("SenseTask puts a 0 into register 1"){ REQUIRE(sym_hw.GetRegister(1) == 0); } @@ -181,6 +184,7 @@ TEST_CASE("Test symbiont SenseTask instruction after a rewarded task", "[sgp]"){ config.CYCLES_PER_UPDATE(0); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("Instructions_test_output"); config.START_MOI(1); config.TASK_IO_UNIQUE_OUTPUT(true); @@ -211,15 +215,15 @@ TEST_CASE("Test symbiont SenseTask instruction after a rewarded task", "[sgp]"){ sym_hw.SetProgram(sym_program); world.AssignNewEnvIO(sym_hw.GetCPUState()); - // NAND is currently rewarded. + // NAND is currently rewarded. REQUIRE(world.GetTaskEnv().GetHostTaskReq(nand_task_id).task_value > 0); - + // Initial register values sym_hw.SetRegisters({7, 12, 9}); // run symbiont program sym_hw.RunCPUStep(5); - + THEN("SenseTask puts a 1 into register 1"){ REQUIRE(sym_hw.GetRegister(1) == 1); } diff --git a/source/test/sgp_mode_test/unit_tests/Instructions.test.cc b/source/test/sgp_mode_test/unit_tests/Instructions.test.cc index be36a39c..196b78a5 100644 --- a/source/test/sgp_mode_test/unit_tests/Instructions.test.cc +++ b/source/test/sgp_mode_test/unit_tests/Instructions.test.cc @@ -66,6 +66,7 @@ TEST_CASE("Test non-interactive instructions", "[sgp]") { config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("Instructions_test_output"); config.START_MOI(0); config.TASK_IO_BANK_SIZE(10); @@ -492,6 +493,7 @@ TEST_CASE("Test host-symbiont interactive instructions", "[sgp]") { config.DONATION_STEAL_INST(true); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("Instructions_test_output"); config.START_MOI(0); config.TASK_IO_UNIQUE_OUTPUT(true); @@ -689,6 +691,7 @@ TEST_CASE("Test freeliving symbiont instructions", "[sgp]") { config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("Instructions_test_output"); config.START_MOI(0); config.TASK_IO_UNIQUE_OUTPUT(true); diff --git a/source/test/sgp_mode_test/unit_tests/SGPCureHosts.test.cc b/source/test/sgp_mode_test/unit_tests/SGPCureHosts.test.cc index e755f505..f0d03975 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPCureHosts.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPCureHosts.test.cc @@ -24,6 +24,7 @@ TEST_CASE("SGP Cure Hosts tests", "[sgp]") { // set up configs sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPCureHosts_test_output"); config.SEED(61); test_utils::SetWellMixed(config, 2, 2); diff --git a/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc b/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc index 4d1b4650..254c3e02 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPHardware.test.cc @@ -26,6 +26,7 @@ TEST_CASE("Test Printing Simple Instructions", "[sgp]") { config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("hardware_test_output"); config.INIT_POP_SIZE(1); config.START_MOI(0); @@ -111,6 +112,7 @@ TEST_CASE("Test Printing Complex Instructions", "[sgp]") { config.HOST_REPRO_RES(1); config.SEED(61); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("hardware_test_output"); config.INIT_POP_SIZE(1); config.START_MOI(0); diff --git a/source/test/sgp_mode_test/unit_tests/SGPHost.test.cc b/source/test/sgp_mode_test/unit_tests/SGPHost.test.cc index 79401dca..4a1f82ef 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPHost.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPHost.test.cc @@ -30,7 +30,7 @@ TEST_CASE("Mutate", "[sgp]") { config.TASK_IO_BANK_SIZE(10); config.SGP_MUT_PER_BIT_RATE(1.0); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); @@ -66,7 +66,7 @@ TEST_CASE("No Mutate", "[sgp]") { config.TASK_IO_BANK_SIZE(10); config.SGP_MUT_PER_BIT_RATE(0.0); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); @@ -100,6 +100,7 @@ TEST_CASE("SGPHost destructor cleans up shared pointers and in-progress reproduc test_utils::SetWellMixed(config, 1, 0); config.TASK_IO_BANK_SIZE(10); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); @@ -132,6 +133,7 @@ TEST_CASE("Host == operators", "[sgp][sgp-unit]") { emp::Random random(31); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_IO_BANK_SIZE(10); world_t world(random, &config); auto& prog_builder = world.GetProgramBuilder(); @@ -172,6 +174,7 @@ TEST_CASE("Host > & < operators", "[sgp][sgp-unit]") { emp::Random random(31); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_IO_BANK_SIZE(10); world_t world(random, &config); auto& prog_builder = world.GetProgramBuilder(); @@ -201,6 +204,7 @@ TEST_CASE("MakeNew returns identical host", "[sgp][sgp-unit]") { emp::Random random(31); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); emp::Ptr host = emp::NewPtr(&random, &world, &config); @@ -227,6 +231,7 @@ TEST_CASE("SetReproCount & GetReproCount","[sgp][sgp-unit]") { emp::Random random(31); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); world_t world(random, &config); @@ -257,6 +262,7 @@ TEST_CASE("ProcessOutputBuffer", "[sgp][sgp-unit]") { GIVEN("A host with valid values in its input and output buffers") { emp::Random random(31); sgpmode::SymConfigSGP config; + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/echo-task-env.json"); config.TASK_IO_BANK_SIZE(10); test_utils::SetWellMixed(config, 1, 0); @@ -284,29 +290,35 @@ TEST_CASE("ProcessOutputBuffer", "[sgp][sgp-unit]") { } } - -TEST_CASE("Check that hosts and syms can't have negative points", "[sgp][sgp-unit]"){ +TEST_CASE("Check that hosts and syms can't have negative points", "[sgp][sgp-unit]") { using world_t = sgpmode::SGPWorld; using cpu_state_t = sgpmode::CPUState; using hw_spec_t = sgpmode::SGPHardwareSpec; using sgp_host_t = sgpmode::SGPHost; using sgp_sym_t = sgpmode::SGPSymbiont; - GIVEN("A host and sym starting with zero points"){ - emp::Random random(31); - sgpmode::SymConfigSGP config; - config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); - world_t world(random, &config); - auto& prog_builder = world.GetProgramBuilder(); - emp::Ptr host = emp::NewPtr(&random, &world, &config, prog_builder.CreateReproProgram(100)); - emp::Ptr sym = emp::NewPtr(&random, &world, &config, prog_builder.CreateNotProgram(100)); - - WHEN("points are added to make total points negative"){ - sym->AddPoints(-100); - host->AddPoints(-100); - THEN("point value should be set to zero"){ - REQUIRE(host->GetPoints() == 0); - REQUIRE(sym->GetPoints() == 0); + + GIVEN("A host and sym starting with zero points") { + emp::Random random(31); + sgpmode::SymConfigSGP config; + config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); + test_utils::SetWellMixed(config, 1, 0); + world_t world(random, &config); + world.Setup(); + + auto& prog_builder = world.GetProgramBuilder(); + emp::Ptr host = emp::NewPtr(&random, &world, &config, prog_builder.CreateReproProgram(100)); + emp::Ptr sym = emp::NewPtr(&random, &world, &config, prog_builder.CreateNotProgram(100)); + + WHEN("points are added to make total points negative") { + sym->AddPoints(-100); + host->AddPoints(-100); + THEN("point value should be set to zero") { + REQUIRE(host->GetPoints() == 0); + REQUIRE(sym->GetPoints() == 0); } } + host.Delete(); + sym.Delete(); } } \ No newline at end of file diff --git a/source/test/sgp_mode_test/unit_tests/SGPSymbiont.test.cc b/source/test/sgp_mode_test/unit_tests/SGPSymbiont.test.cc index 34ec3d3e..575b23c8 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPSymbiont.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPSymbiont.test.cc @@ -26,6 +26,7 @@ using sgp_sym_t = sgpmode::SGPSymbiont; emp::Random random(31); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); world_t world(random, &config); @@ -62,6 +63,7 @@ TEST_CASE("Symbiont > & < operator","[sgp][sgp-unit]") { emp::Random random(31); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); world_t world(random, &config); @@ -87,6 +89,7 @@ TEST_CASE("Symbiont Process", "[sgp][sgp-unit]") { emp::Random random(34); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.CYCLES_PER_UPDATE(3); test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); diff --git a/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc b/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc index a5918809..adb3a878 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc @@ -31,6 +31,7 @@ TEST_CASE("Update only hosts test", "[sgp]") { emp::Random random(61); sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_IO_BANK_SIZE(10); test_utils::SetWellMixed(config, 4, 0); world_t world(random, &config); @@ -70,6 +71,7 @@ TEST_CASE("Ousting is permitted", "[sgp]") { config.OUSTING(1); config.SYM_LIMIT(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); world_t world(random, &config); world.Setup(); @@ -100,6 +102,7 @@ TEST_CASE("NoBetterOrEquallyMatchingSymbionts returns false for an incoming wors test_utils::SetWellMixed(config, 1, 0); config.SEED(2312); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); @@ -134,6 +137,7 @@ TEST_CASE("NoBetterOrEquallyMatchingSymbionts returns false for an incoming equa test_utils::SetWellMixed(config, 1, 0); config.SEED(2312); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); @@ -168,6 +172,7 @@ TEST_CASE("NoBetterOrEquallyMatchingSymbionts returns true for an incoming bette test_utils::SetWellMixed(config, 1, 0); config.SEED(2312); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); @@ -202,6 +207,7 @@ TEST_CASE("NoBetterMatchingSymbionts returns false for an incoming worse match", test_utils::SetWellMixed(config, 1, 0); config.SEED(2312); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); @@ -236,6 +242,7 @@ TEST_CASE("NoBetterMatchingSymbionts returns true for an incoming equal match", test_utils::SetWellMixed(config, 1, 0); config.SEED(2312); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); @@ -270,6 +277,7 @@ TEST_CASE("NoBetterMatchingSymbionts returns true for an incoming better match", test_utils::SetWellMixed(config, 1, 0); config.SEED(2312); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); @@ -306,6 +314,7 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor config.SEED(33); config.FIND_NEIGHBOR_HOST_ATTEMPTS(5); // increase attempts to avoid issues if randomly picks current host first config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE("always"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); @@ -351,6 +360,7 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon config.SEED(11); config.FIND_NEIGHBOR_HOST_ATTEMPTS(5); // increase attempts to avoid issues if randomly picks current host first config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE("task-profile-compatible"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); @@ -396,6 +406,7 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor config.SEED(11); config.FIND_NEIGHBOR_HOST_ATTEMPTS(5); // increase attempts to avoid issues if randomly picks current host first config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE("always"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); @@ -441,6 +452,7 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon config.SEED(11); config.FIND_NEIGHBOR_HOST_ATTEMPTS(5); // increase attempts to avoid issues if randomly picks current host first config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE("task-profile-compatible"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); @@ -485,6 +497,7 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor config.SEED(11); config.FIND_NEIGHBOR_HOST_ATTEMPTS(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE("always"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); @@ -522,6 +535,7 @@ TEST_CASE("SGP Horizontal SymDoBirth", "[sgp][sgp-unit]") { config.OUSTING(1); config.FIND_NEIGHBOR_HOST_ATTEMPTS(1); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE("task-profile-strictly-stronger-match"); world_t world(random, &config); diff --git a/source/test/sgp_mode_test/unit_tests/SGPWorldData.test.cc b/source/test/sgp_mode_test/unit_tests/SGPWorldData.test.cc index 20fd82a0..c93ce421 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPWorldData.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPWorldData.test.cc @@ -35,6 +35,7 @@ TEST_CASE("CreateDataFiles creates data files", "[sgp][sgp-functional]") { test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.FILE_PATH("SGPData_test_output"); config.FILE_PATH("_test"); emp::Random random(config.SEED()); diff --git a/source/test/sgp_mode_test/unit_tests/SGPWorldSetup.test.cc b/source/test/sgp_mode_test/unit_tests/SGPWorldSetup.test.cc index 89a9a0f8..5e785d03 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPWorldSetup.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPWorldSetup.test.cc @@ -31,6 +31,7 @@ TEST_CASE("Setup with an empty population", "[sgp]") { config.TASK_IO_BANK_SIZE(10); config.SEED(234); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); emp::Random random(config.SEED()); world_t world(random, &config); @@ -50,6 +51,7 @@ TEST_CASE("Setup with an empty population", "[sgp]") { TEST_CASE("SetupHosts adds correct number of hosts", "[sgp][sgp-unit]") { sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); config.SEED(44); @@ -71,6 +73,7 @@ TEST_CASE("SetupHosts adds correct number of hosts", "[sgp][sgp-unit]") { TEST_CASE("SetupHosts adds infected hosts correctly", "[sgp][sgp-unit]") { sgpmode::SymConfigSGP config; config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); test_utils::SetWellMixed(config, 4); config.TASK_IO_BANK_SIZE(10); config.SEED(44); @@ -100,6 +103,7 @@ TEST_CASE("Setup correctly sets host task profile functions", "[sgp][sgp-unit]") test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); WHEN("TASK_PROFILE_MODE tracks all parent tasks") { config.TASK_PROFILE_MODE("parent-all"); @@ -142,6 +146,7 @@ TEST_CASE("Setup correctly sets symbiont task profile functions", "[sgp][sgp-uni test_utils::SetWellMixed(config, 4, 0); config.TASK_IO_BANK_SIZE(10); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); WHEN("TASK_PROFILE_MODE tracks all parent tasks") { config.TASK_PROFILE_MODE("parent-all"); From 37ed4ee992812bef5cb1d6f3acb64927db2b08c6 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 15:57:20 -0400 Subject: [PATCH 41/42] Add no-events.json events file --- source/test/sgp_mode_test/no-events.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 source/test/sgp_mode_test/no-events.json diff --git a/source/test/sgp_mode_test/no-events.json b/source/test/sgp_mode_test/no-events.json new file mode 100644 index 00000000..6cc4cc51 --- /dev/null +++ b/source/test/sgp_mode_test/no-events.json @@ -0,0 +1,3 @@ +{ + "events": [] +} \ No newline at end of file From 9a8a494601d4daa51730b9a25451502379d2e925 Mon Sep 17 00:00:00 2001 From: Alexander Lalejini Date: Thu, 30 Jul 2026 16:42:37 -0400 Subject: [PATCH 42/42] Fix issues with config files not being found, add emp_assert to make backtracing easier in a debugger when events file is not found --- source/sgp_mode/events/EventManager.h | 1 + source/test/pgg_mode_test/PGGSymbiont.test.cc | 443 +++++++++--------- .../functional_tests/HealthMode.test.cc | 1 + .../functional_tests/TaskValueEvent.test.cc | 2 - 4 files changed, 224 insertions(+), 223 deletions(-) diff --git a/source/sgp_mode/events/EventManager.h b/source/sgp_mode/events/EventManager.h index 3cce1763..f99342b1 100644 --- a/source/sgp_mode/events/EventManager.h +++ b/source/sgp_mode/events/EventManager.h @@ -271,6 +271,7 @@ class EventManager { const bool event_file_exists = std::filesystem::exists(event_filepath); if (!event_file_exists) { std::cout << "Event file does not exist: " << event_filepath << std::endl; + emp_assert(false, "Event file does not exist."); std::exit(EXIT_FAILURE); } // read event.json file diff --git a/source/test/pgg_mode_test/PGGSymbiont.test.cc b/source/test/pgg_mode_test/PGGSymbiont.test.cc index 700c0068..3f488112 100644 --- a/source/test/pgg_mode_test/PGGSymbiont.test.cc +++ b/source/test/pgg_mode_test/PGGSymbiont.test.cc @@ -3,305 +3,306 @@ TEST_CASE("PGGSymbiont Constructor", "[pgg]") { - emp::Ptr random = emp::NewPtr(12); - SymConfigPGG config; - PGGWorld w(*random, &config); - PGGWorld * world = &w; + emp::Ptr random = emp::NewPtr(12); + SymConfigPGG config; + PGGWorld w(*random, &config); + PGGWorld * world = &w; - double donation = 1; + double donation = 1; - double int_val = 0.5; - emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val,donation); - CHECK(symbiont->GetDonation() == donation); - CHECK(symbiont->GetAge() == 0); - CHECK(symbiont->GetPoints() == 0); + double int_val = 0.5; + emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val,donation); + CHECK(symbiont->GetDonation() == donation); + CHECK(symbiont->GetAge() == 0); + CHECK(symbiont->GetPoints() == 0); - donation = 2; - emp::Ptr symbiont2 = emp::NewPtr(random, world, &config, int_val,donation); - CHECK(symbiont2->GetDonation() == 2); - CHECK(symbiont2->GetAge() == 0); - CHECK(symbiont2->GetPoints() == 0); + donation = 2; + emp::Ptr symbiont2 = emp::NewPtr(random, world, &config, int_val,donation); + CHECK(symbiont2->GetDonation() == 2); + CHECK(symbiont2->GetAge() == 0); + CHECK(symbiont2->GetPoints() == 0); - int_val = 2; - REQUIRE_THROWS(emp::NewPtr(random, world, &config, int_val) ); + int_val = 2; + REQUIRE_THROWS(emp::NewPtr(random, world, &config, int_val) ); - symbiont.Delete(); - symbiont2.Delete(); - random.Delete(); + symbiont.Delete(); + symbiont2.Delete(); + random.Delete(); } TEST_CASE("PGGmutate", "[pgg]") { - emp::Ptr random = emp::NewPtr(37); - SymConfigPGG config; - PGGWorld w(*random, &config); - PGGWorld * world = &w; + emp::Ptr random = emp::NewPtr(37); + SymConfigPGG config; + PGGWorld w(*random, &config); + PGGWorld * world = &w; - WHEN("Mutation rate is not zero") { - double int_val = 0; - double donation = 0.01; - config.MUTATION_SIZE(0.002); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val,donation); + WHEN("Mutation rate is not zero") { + double int_val = 0; + double donation = 0.01; + config.MUTATION_SIZE(0.002); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val,donation); - symbiont->Mutate(); + symbiont->Mutate(); - THEN("Mutation occurs and donation value changes, but stays within bounds") { - REQUIRE(symbiont->GetDonation() != donation); - REQUIRE(symbiont->GetDonation() <= 1); - REQUIRE(symbiont->GetDonation() >= 0); - } - symbiont.Delete(); + THEN("Mutation occurs and donation value changes, but stays within bounds") { + REQUIRE(symbiont->GetDonation() != donation); + REQUIRE(symbiont->GetDonation() <= 1); + REQUIRE(symbiont->GetDonation() >= 0); } - WHEN("Mutation rate is zero") { - double int_val = 1; - double donation = 0.1; - config.SYM_HORIZ_TRANS_RES(100.0); - config.HORIZ_TRANS(true); - config.MUTATION_RATE(0); - config.MUTATION_SIZE(0); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, donation); + symbiont.Delete(); + } + WHEN("Mutation rate is zero") { + double int_val = 1; + double donation = 0.1; + config.SYM_HORIZ_TRANS_RES(100.0); + config.HORIZ_TRANS(true); + config.MUTATION_RATE(0); + config.MUTATION_SIZE(0); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, donation); - symbiont->Mutate(); + symbiont->Mutate(); - THEN("Mutation does not occur and donation value does not change") { - REQUIRE(symbiont->GetDonation() == donation); - } - symbiont.Delete(); + THEN("Mutation does not occur and donation value does not change") { + REQUIRE(symbiont->GetDonation() == donation); } - random.Delete(); + symbiont.Delete(); + } + random.Delete(); } TEST_CASE("PGGSymbiont ProcessPool", "[pgg]"){ - emp::Ptr random = emp::NewPtr(15); - SymConfigPGG config; - PGGWorld world(*random, &config); - config.SYNERGY(5); - config.PGG_SYNERGY(1.1); - double host_int_val = 1; - double sym_int_val = 0; - double donation = 0.1; - - emp::Ptr symbiont = emp::NewPtr(random, &world, &config, sym_int_val,donation); - emp::Ptr host = emp::NewPtr(random, &world, &config, host_int_val); - host->AddSymbiont(symbiont); - - host->DistribResources(40); - - CHECK(symbiont->GetPoints() == 40.4); - CHECK(host->GetPoints() == 0); - - host.Delete(); - random.Delete(); + emp::Ptr random = emp::NewPtr(15); + SymConfigPGG config; + PGGWorld world(*random, &config); + config.SYNERGY(5); + config.PGG_SYNERGY(1.1); + double host_int_val = 1; + double sym_int_val = 0; + double donation = 0.1; + + emp::Ptr symbiont = emp::NewPtr(random, &world, &config, sym_int_val,donation); + emp::Ptr host = emp::NewPtr(random, &world, &config, host_int_val); + host->AddSymbiont(symbiont); + + host->DistribResources(40); + + CHECK(symbiont->GetPoints() == 40.4); + CHECK(host->GetPoints() == 0); + + host.Delete(); + random.Delete(); } TEST_CASE("PGGProcess", "[pgg]") { - emp::Ptr random = emp::NewPtr(9); - SymConfigPGG config; - PGGWorld w(*random, &config); - PGGWorld * world = &w; + emp::Ptr random = emp::NewPtr(9); + SymConfigPGG config; + config.SPATIAL_STRUCT_MODE("well-mixed"); + PGGWorld w(*random, &config); + PGGWorld * world = &w; - //add new test for free living sym not moving when it shouldnt - WHEN("Horizontal transmission is true and points is greater than sym_h_res") { - double int_val = 1; - double points = 0.0; - config.SYM_HORIZ_TRANS_RES(140.0); - config.HORIZ_TRANS(true); - config.MUTATION_SIZE(0); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); + //add new test for free living sym not moving when it shouldnt + WHEN("Horizontal transmission is true and points is greater than sym_h_res") { + double int_val = 1; + double points = 0.0; + config.SYM_HORIZ_TRANS_RES(140.0); + config.HORIZ_TRANS(true); + config.MUTATION_SIZE(0); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); - int add_points = 200; - symbiont->AddPoints(add_points); + int add_points = 200; + symbiont->AddPoints(add_points); - int location = 10; - symbiont->Process(location); + int location = 10; + symbiont->Process(location); - THEN("Points changes and is set to 0") { - int points_post_reproduction = 0; - REQUIRE(symbiont->GetPoints() == points_post_reproduction); - } - symbiont.Delete(); + THEN("Points changes and is set to 0") { + int points_post_reproduction = 0; + REQUIRE(symbiont->GetPoints() == points_post_reproduction); } + symbiont.Delete(); + } - WHEN("Horizontal transmission is true and points is less than sym_h_res") { - double int_val = 1; - double points = 0.0; - config.SYM_HORIZ_TRANS_RES(200.0); - config.HORIZ_TRANS(true); - config.MUTATION_SIZE(0.0); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); + WHEN("Horizontal transmission is true and points is less than sym_h_res") { + double int_val = 1; + double points = 0.0; + config.SYM_HORIZ_TRANS_RES(200.0); + config.HORIZ_TRANS(true); + config.MUTATION_SIZE(0.0); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); - int add_points = 50; - symbiont->AddPoints(add_points); + int add_points = 50; + symbiont->AddPoints(add_points); - int location = 10; - symbiont->Process(location); + int location = 10; + symbiont->Process(location); - THEN("Points does not change") { - int points_post_reproduction = 50; - REQUIRE(symbiont->GetPoints() == points_post_reproduction); - } - symbiont.Delete(); + THEN("Points does not change") { + int points_post_reproduction = 50; + REQUIRE(symbiont->GetPoints() == points_post_reproduction); } + symbiont.Delete(); + } - WHEN("Horizontal transmission is false and points and points is greater then sym_h_res") { - double int_val = 1; - double points = 100.0; - config.SYM_HORIZ_TRANS_RES(80.0); - config.HORIZ_TRANS(false); - config.MUTATION_SIZE(0.0); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); + WHEN("Horizontal transmission is false and points and points is greater then sym_h_res") { + double int_val = 1; + double points = 100.0; + config.SYM_HORIZ_TRANS_RES(80.0); + config.HORIZ_TRANS(false); + config.MUTATION_SIZE(0.0); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); - int location = 10; - symbiont->Process(location); + int location = 10; + symbiont->Process(location); - THEN("Points does not change") { - int points_post_reproduction = 100; - REQUIRE(symbiont->GetPoints() == points_post_reproduction); - } - symbiont.Delete(); + THEN("Points does not change") { + int points_post_reproduction = 100; + REQUIRE(symbiont->GetPoints() == points_post_reproduction); } + symbiont.Delete(); + } - WHEN("Horizontal transmission is false and points and points is less then sym_h_res") { - double int_val = 1; - double points = 40.0; - config.SYM_HORIZ_TRANS_RES(80.0); - config.HORIZ_TRANS(false); - config.MUTATION_SIZE(0.0); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); + WHEN("Horizontal transmission is false and points and points is less then sym_h_res") { + double int_val = 1; + double points = 40.0; + config.SYM_HORIZ_TRANS_RES(80.0); + config.HORIZ_TRANS(false); + config.MUTATION_SIZE(0.0); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, int_val, 0,points); - int location = 10; - symbiont->Process(location); + int location = 10; + symbiont->Process(location); - THEN("Points does not change") { - int points_post_reproduction = 40; - REQUIRE(symbiont->GetPoints() == points_post_reproduction); - } - symbiont.Delete(); + THEN("Points does not change") { + int points_post_reproduction = 40; + REQUIRE(symbiont->GetPoints() == points_post_reproduction); } - random.Delete(); + symbiont.Delete(); + } + random.Delete(); } TEST_CASE("PGGSymbiont ProcessResources", "[pgg]"){ - emp::Ptr random = emp::NewPtr(18); - SymConfigPGG config; - PGGWorld w(*random, &config); - PGGWorld * world = &w; - config.SYNERGY(5); - + emp::Ptr random = emp::NewPtr(18); + SymConfigPGG config; + PGGWorld w(*random, &config); + PGGWorld * world = &w; + config.SYNERGY(5); - WHEN("sym_int_val < 0"){ - double sym_int_val = -0.6; + WHEN("sym_int_val < 0"){ + double sym_int_val = -0.6; - WHEN("host_int_val > 0"){ - double host_int_val = 0.2; - emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); - host->AddSymbiont(symbiont); - double expected_sym_points = 68; // hostDonation + stolen - double expected_return = 0; // hostportion * synergy + WHEN("host_int_val > 0"){ + double host_int_val = 0.2; + emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); + host->AddSymbiont(symbiont); - host->SetResInProcess(80); + double expected_sym_points = 68; // hostDonation + stolen + double expected_return = 0; // hostportion * synergy - THEN("sym receives a donation and stolen resources, host receives betrayal"){ - REQUIRE(symbiont->ProcessResources(20) == expected_return); - REQUIRE(symbiont->GetPoints() == expected_sym_points); + host->SetResInProcess(80); - } - host.Delete(); - } + THEN("sym receives a donation and stolen resources, host receives betrayal"){ + REQUIRE(symbiont->ProcessResources(20) == expected_return); + REQUIRE(symbiont->GetPoints() == expected_sym_points); - WHEN("host_int_val < 0 and resources are placed into defense"){ + } + host.Delete(); + } - WHEN("host successfully defends from symsteal"){ - double host_int_val = -0.8; - emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); - host->AddSymbiont(symbiont); + WHEN("host_int_val < 0 and resources are placed into defense"){ - double expected_sym_points = 0; // hostDonation + stolen - double expected_return = 0; // hostportion * synergy + WHEN("host successfully defends from symsteal"){ + double host_int_val = -0.8; + emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); + host->AddSymbiont(symbiont); - host->SetResInProcess(20); - THEN("symbiont is unsuccessful at stealing"){ - REQUIRE(symbiont->ProcessResources(0) == expected_return); - REQUIRE(symbiont->GetPoints() == expected_sym_points); - } - host.Delete(); - } + double expected_sym_points = 0; // hostDonation + stolen + double expected_return = 0; // hostportion * synergy - WHEN("host fails at defense"){ - double host_int_val = -0.5; - emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); - host->AddSymbiont(symbiont); + host->SetResInProcess(20); + THEN("symbiont is unsuccessful at stealing"){ + REQUIRE(symbiont->ProcessResources(0) == expected_return); + REQUIRE(symbiont->GetPoints() == expected_sym_points); + } + host.Delete(); + } - double expected_sym_points = 5; // hostDonation + stolen - double expected_return = 0; // hostportion * synergy + WHEN("host fails at defense"){ + double host_int_val = -0.5; + emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); + host->AddSymbiont(symbiont); - host->SetResInProcess(50); + double expected_sym_points = 5; // hostDonation + stolen + double expected_return = 0; // hostportion * synergy - THEN("Sym steals successfully"){ - REQUIRE(symbiont->ProcessResources(0) == expected_return); - REQUIRE(symbiont->GetPoints() == Approx(expected_sym_points)); - } - host.Delete(); - } + host->SetResInProcess(50); + THEN("Sym steals successfully"){ + REQUIRE(symbiont->ProcessResources(0) == expected_return); + REQUIRE(symbiont->GetPoints() == Approx(expected_sym_points)); } + host.Delete(); + } } - WHEN("sym_int_val > 0") { - double sym_int_val = 0.2; - double host_int_val = 0.5; - emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); - emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); - host->AddSymbiont(symbiont); + } - double expected_sym_points = 40; // hostDonation - hostPortion - double expected_return = 50; // hostPortion * synergy + WHEN("sym_int_val > 0") { + double sym_int_val = 0.2; + double host_int_val = 0.5; + emp::Ptr host = emp::NewPtr(random, world, &config, host_int_val); + emp::Ptr symbiont = emp::NewPtr(random, world, &config, sym_int_val); + host->AddSymbiont(symbiont); - host->SetResInProcess(50); + double expected_sym_points = 40; // hostDonation - hostPortion + double expected_return = 50; // hostPortion * synergy + host->SetResInProcess(50); - THEN("Sym attempts to give benefit back"){ - REQUIRE(symbiont->ProcessResources(50) == expected_return); - REQUIRE(symbiont->GetPoints() == expected_sym_points); - } - host.Delete(); + + THEN("Sym attempts to give benefit back"){ + REQUIRE(symbiont->ProcessResources(50) == expected_return); + REQUIRE(symbiont->GetPoints() == expected_sym_points); } - random.Delete(); + host.Delete(); + } + random.Delete(); } TEST_CASE("PGGSymbiont MakeNew", "[pgg]"){ - emp::Ptr random = emp::NewPtr(3); - SymConfigPGG config; - PGGWorld world(*random, &config); - - double host_int_val = 0.2; - emp::Ptr symbiont1 = emp::NewPtr(random, &world, &config, host_int_val); - emp::Ptr symbiont2 = symbiont1->MakeNew(); - THEN("The new symbiont has properties of the original symbiont and has 0 points and 0 age"){ - REQUIRE(symbiont1->GetIntVal() == symbiont2->GetIntVal()); - REQUIRE(symbiont1->GetInfectionChance() == symbiont2->GetInfectionChance()); - REQUIRE(symbiont1->GetDonation() == symbiont2->GetDonation()); - REQUIRE(symbiont2->GetPoints() == 0); - REQUIRE(symbiont2->GetAge() == 0); - //check that the offspring is the correct class - REQUIRE(symbiont2->GetName() == "PGGSymbiont"); - } - symbiont1.Delete(); - symbiont2.Delete(); - random.Delete(); + emp::Ptr random = emp::NewPtr(3); + SymConfigPGG config; + PGGWorld world(*random, &config); + + double host_int_val = 0.2; + emp::Ptr symbiont1 = emp::NewPtr(random, &world, &config, host_int_val); + emp::Ptr symbiont2 = symbiont1->MakeNew(); + THEN("The new symbiont has properties of the original symbiont and has 0 points and 0 age"){ + REQUIRE(symbiont1->GetIntVal() == symbiont2->GetIntVal()); + REQUIRE(symbiont1->GetInfectionChance() == symbiont2->GetInfectionChance()); + REQUIRE(symbiont1->GetDonation() == symbiont2->GetDonation()); + REQUIRE(symbiont2->GetPoints() == 0); + REQUIRE(symbiont2->GetAge() == 0); + //check that the offspring is the correct class + REQUIRE(symbiont2->GetName() == "PGGSymbiont"); + } + symbiont1.Delete(); + symbiont2.Delete(); + random.Delete(); } diff --git a/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc b/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc index 692d48e9..c6c31dab 100644 --- a/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc +++ b/source/test/sgp_mode_test/functional_tests/HealthMode.test.cc @@ -647,6 +647,7 @@ TEST_CASE("Health hosts evolve", "[sgp][sgp-functional][health-mode-evolution]") config.WORLD_HEIGHT(100); config.HOST_REPRO_RES(20); config.TASK_ENV_CFG_PATH("source/test/sgp_mode_test/hardware-test-env.json"); + config.EVENTS_CFG_PATH("source/test/sgp_mode_test/no-events.json"); config.TASK_PROFILE_COMPATIBILITY_MODE("task-any-match"); config.TASK_PROFILE_MODE("self-all"); config.CYCLES_PER_UPDATE(4); diff --git a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc index ffd1ac13..db6af55d 100644 --- a/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc +++ b/source/test/sgp_mode_test/functional_tests/TaskValueEvent.test.cc @@ -334,7 +334,6 @@ TEST_CASE("TaskValueEvent Host/Sym Only Events", "[sgp][events]") { world_t world(random, &config); world.Setup(); auto& builder = world.GetProgramBuilder(); - auto& event_manager = world.GetEventManager(); // create host with NAND operation emp::Ptr host = emp::NewPtr(&random, &world, &config, builder.CreateNandProgram(50)); // create symbiont with NAND operation @@ -380,7 +379,6 @@ TEST_CASE("TaskValueEvent Mulitple Task Names Events", "[sgp][events]") { WHEN("Multiple tasks are in a single event") { world_t world(random, &config); world.Setup(); - auto& event_manager = world.GetEventManager(); // get task ids const size_t nand_task_id = world.GetTaskEnv().GetTaskSet().GetID("NAND"); const size_t not_task_id = world.GetTaskEnv().GetTaskSet().GetID("NOT");