diff --git a/apps/SpatialCoop2017/source/SimplePDWorld.h b/apps/SpatialCoop2017/source/SimplePDWorld.h index 16a46c4321..5db8cd00be 100644 --- a/apps/SpatialCoop2017/source/SimplePDWorld.h +++ b/apps/SpatialCoop2017/source/SimplePDWorld.h @@ -2,230 +2,232 @@ #include "tools/Random.h" struct Org { - double x; - double y; - bool coop; - double fitness; + double x; + double y; + bool coop; + double fitness; - emp::vector neighbors; + emp::vector neighbors; }; // Create a class to maintain a simple Prisoner's Dilema world. class SimplePDWorld { -public: - // Parameters - double r; // Neighborhood radius - double u; // cost / benefit ratio - size_t N; // Population size - size_t E; // How many epochs should a popuilation run for? - size_t num_runs; // How many runs should we do? - bool use_ave; // Use the average payoff for fitness instead if the total. - - emp::Random random; // All-purpose random-number generator - size_t epoch; // What epoch are we currently on? - - // Calculations we'll need later. - double r_sqr; // r squared (for comparisons) - emp::vector pop; - - // Prisoner's Dilema payout table... - double payoff_CC; - double payoff_CD; - double payoff_DC; - double payoff_DD; - - // Helper functions - void CalcFitness(size_t id); - void Repro(); - public: - SimplePDWorld(double _r=0.02, double _u=0.175, size_t _N=6400, size_t _E=5000, bool _ave=false, int seed=0) - : num_runs(10), random(seed) - { - Setup(_r, _u, _N, _E, _ave); // Call Setup since we a starting a new population. - } - - const emp::vector & GetPop() const { return pop; } - double GetR() const { return r; } - double GetU() const { return u; } - size_t GetN() const { return N; } - size_t GetE() const { return E; } - size_t GetNumRuns() const { return num_runs; } - size_t GetEpoch() const { return epoch; } - - void SetR(double _r) { r = _r; } - void SetU(double _u) { u = _u; } - void SetN(size_t _N) { N = _N; } - void SetE(size_t _E) { E = _E; } - void SetNumRuns(size_t n) { num_runs = n; } - - void UseAve(bool _in=true) { use_ave = _in; } - - void Setup(double _r=0.02, double _u=0.0025, size_t _N=6400, size_t _E=5000, bool _ave=false) { - // Store the input values. - r = _r; u = _u; N = _N; E = _E, use_ave = _ave; - epoch = 0; + public: + // Parameters + double r; // Neighborhood radius + double u; // cost / benefit ratio + size_t N; // Population size + size_t E; // How many epochs should a popuilation run for? + size_t num_runs; // How many runs should we do? + bool use_ave; // Use the average payoff for fitness instead if the total. + + emp::Random random; // All-purpose random-number generator + size_t epoch; // What epoch are we currently on? // Calculations we'll need later. - r_sqr = r * r; // r squared (for comparisons) - pop.resize(N); - - // Setup the payout matric. - payoff_CC = 1.0; - payoff_CD = 0.0; - payoff_DC = 1.0 + u; - payoff_DD = u; - - // Initialize each organism - for (Org & org : pop) { - org.x = random.GetDouble(1.0); - org.y = random.GetDouble(1.0); - org.coop = random.P(0.5); - org.neighbors.resize(0); + double r_sqr; // r squared (for comparisons) + emp::vector pop; + + // Prisoner's Dilema payout table... + double payoff_CC; + double payoff_CD; + double payoff_DC; + double payoff_DD; + + // Helper functions + void CalcFitness(size_t id); + void Repro(); + + public: + SimplePDWorld(double _r = 0.02, double _u = 0.175, size_t _N = 6400, size_t _E = 5000, bool _ave = false, int seed = 0) + : num_runs(10), random(seed) { + Setup(_r, _u, _N, _E, _ave); // Call Setup since we a starting a new population. } - // Determine if pairs of organisms are neighbors; - // @CAO: Can speed up by using Surface2D. - for (size_t i = 1; i < N; i++) { - for (size_t j = 0; j < i; j++) { - Org & org1 = pop[i]; - Org & org2 = pop[j]; - double x_dist = emp::Abs(org1.x - org2.x); - if (x_dist > (1.0 - x_dist)) x_dist = 1.0 - x_dist; - double y_dist = emp::Abs(org1.y - org2.y); - if (x_dist > (1.0 - x_dist)) x_dist = 1.0 - x_dist; - double dist_sqr = x_dist * x_dist + y_dist * y_dist; - - // Test if this pair are within neighbor radius... - if (dist_sqr < r_sqr) { - org1.neighbors.push_back(j); - org2.neighbors.push_back(i); + const emp::vector& GetPop() const { return pop; } + double GetR() const { return r; } + double GetU() const { return u; } + size_t GetN() const { return N; } + size_t GetE() const { return E; } + size_t GetNumRuns() const { return num_runs; } + size_t GetEpoch() const { return epoch; } + + void SetR(double _r) { r = _r; } + void SetU(double _u) { u = _u; } + void SetN(size_t _N) { N = _N; } + void SetE(size_t _E) { E = _E; } + void SetNumRuns(size_t n) { num_runs = n; } + + void UseAve(bool _in = true) { use_ave = _in; } + + void Setup(double _r = 0.02, double _u = 0.0025, size_t _N = 6400, size_t _E = 5000, bool _ave = false) { + // Store the input values. + r = _r; + u = _u; + N = _N; + E = _E, use_ave = _ave; + epoch = 0; + + // Calculations we'll need later. + r_sqr = r * r; // r squared (for comparisons) + pop.resize(N); + + // Setup the payout matric. + payoff_CC = 1.0; + payoff_CD = 0.0; + payoff_DC = 1.0 + u; + payoff_DD = u; + + // Initialize each organism + for (Org& org : pop) { + org.x = random.GetDouble(1.0); + org.y = random.GetDouble(1.0); + org.coop = random.P(0.5); + org.neighbors.resize(0); + } + + // Determine if pairs of organisms are neighbors; + // @CAO: Can speed up by using Surface2D. + for (size_t i = 1; i < N; i++) { + for (size_t j = 0; j < i; j++) { + Org& org1 = pop[i]; + Org& org2 = pop[j]; + double x_dist = abs(org1.x - org2.x); + if (x_dist > (1.0 - x_dist)) x_dist = 1.0 - x_dist; + double y_dist = abs(org1.y - org2.y); + if (x_dist > (1.0 - x_dist)) x_dist = 1.0 - x_dist; + double dist_sqr = x_dist * x_dist + y_dist * y_dist; + + // Test if this pair are within neighbor radius... + if (dist_sqr < r_sqr) { + org1.neighbors.push_back(j); + org2.neighbors.push_back(i); + } + } } - } - } - // Calculate the initial fitness for each organism in the population. - for (size_t id = 0; id < N; id++) { - CalcFitness(id); + // Calculate the initial fitness for each organism in the population. + for (size_t id = 0; id < N; id++) { + CalcFitness(id); + } } - } - void Reset() { Setup(r,u,N,E); } + void Reset() { Setup(r, u, N, E); } - void Run(size_t steps=-1) { - if (steps > E) steps = E; - // Run the organisms! - size_t end_epoch = epoch + steps; - while (epoch < end_epoch) { - for (size_t o = 0; o < N; o++) Repro(); - epoch++; + void Run(size_t steps = -1) { + if (steps > E) steps = E; + // Run the organisms! + size_t end_epoch = epoch + steps; + while (epoch < end_epoch) { + for (size_t o = 0; o < N; o++) Repro(); + epoch++; + } } - } - size_t CountCoop(); - void PrintNeighborInfo(std::ostream & os); + size_t CountCoop(); + void PrintNeighborInfo(std::ostream& os); }; - // To calculate the fitness of an organism, have it play // against all its neighbors and take the average payout. void SimplePDWorld::CalcFitness(size_t id) { - Org & org = pop[id]; - - int C_count = 0; - int D_count = 0; - for (size_t n : org.neighbors) { - if (pop[n].coop) C_count++; - else D_count++; - } + Org& org = pop[id]; + + int C_count = 0; + int D_count = 0; + for (size_t n : org.neighbors) { + if (pop[n].coop) + C_count++; + else + D_count++; + } - double C_value = payoff_CC; - double D_value = payoff_CD; + double C_value = payoff_CC; + double D_value = payoff_CD; - if (!org.coop) { - C_value = payoff_DC; - D_value = payoff_DD; - } + if (!org.coop) { + C_value = payoff_DC; + D_value = payoff_DD; + } - double total_C = C_value * (double) C_count; - double total_D = D_value * (double) D_count; - org.fitness = total_C + total_D; + double total_C = C_value * (double)C_count; + double total_D = D_value * (double)D_count; + org.fitness = total_C + total_D; - if (use_ave) org.fitness /= (double) org.neighbors.size(); + if (use_ave) org.fitness /= (double)org.neighbors.size(); } - // Reproduce into a single, random cell. void SimplePDWorld::Repro() { - size_t id = random.GetUInt(N); - Org & org = pop[id]; - bool start_coop = org.coop; - - // Determine the total fitness of neighbors. - double total_fitness = 0; - for (size_t n : org.neighbors) { - total_fitness += pop[n].fitness; - } - - // If neighbor fitnesses are non-zero, choose one of them. - if (total_fitness > 0) { - // Include the focal organism in the pool - double choice = random.GetDouble(total_fitness + org.fitness); - - // If we aren't keeping the focal organism, we have to pick - if (choice < total_fitness){ - for (size_t n : org.neighbors) { - if (choice < pop[n].fitness) { - org.coop = pop[n].coop; // Copy strategy of winner! - break; + size_t id = random.GetUInt(N); + Org& org = pop[id]; + bool start_coop = org.coop; + + // Determine the total fitness of neighbors. + double total_fitness = 0; + for (size_t n : org.neighbors) { + total_fitness += pop[n].fitness; + } + + // If neighbor fitnesses are non-zero, choose one of them. + if (total_fitness > 0) { + // Include the focal organism in the pool + double choice = random.GetDouble(total_fitness + org.fitness); + + // If we aren't keeping the focal organism, we have to pick + if (choice < total_fitness) { + for (size_t n : org.neighbors) { + if (choice < pop[n].fitness) { + org.coop = pop[n].coop; // Copy strategy of winner! + break; + } + choice -= pop[n].fitness; + } } - choice -= pop[n].fitness; - } } - } - - // If we haven't changed our strategy, no need to continue. - if (org.coop == start_coop) return; - - // Now that we have updated the organism, calculate its fitness again - // (even if no change, since neighbors may have changed). - CalcFitness(id); - // Also update neighbors' fitnesses - for (size_t n : org.neighbors) { - CalcFitness(n); - } -} + // If we haven't changed our strategy, no need to continue. + if (org.coop == start_coop) return; + + // Now that we have updated the organism, calculate its fitness again + // (even if no change, since neighbors may have changed). + CalcFitness(id); + // Also update neighbors' fitnesses + for (size_t n : org.neighbors) { + CalcFitness(n); + } +} // Count how many cooperators we currently have in the population. size_t SimplePDWorld::CountCoop() { - size_t count = 0.0; - for (Org & org : pop) if (org.coop) count++; - return count; + size_t count = 0.0; + for (Org& org : pop) + if (org.coop) count++; + return count; } - // Print out a histogram of neighborhood sizes. -void SimplePDWorld::PrintNeighborInfo(std::ostream & os) { - size_t total = 0; - size_t max_size = 0; - size_t min_size = pop[0].neighbors.size(); - for (Org & org : pop) { - size_t cur_size = org.neighbors.size(); - total +=cur_size; - if (cur_size > max_size) max_size = cur_size; - if (cur_size < min_size) min_size = cur_size; - } - emp::vector hist(max_size+1, 0); - for (Org & org : pop) { - size_t cur_size = org.neighbors.size(); - hist[cur_size]++; - } - // double avg_size = ((double) total) / (double) N; - - os << "neighbors,count\n"; -// << "average," << avg_size << '\n'; - for (size_t i = 0; i < hist.size(); i++) { - os << i << ',' << hist[i] << '\n'; - } - os.flush(); +void SimplePDWorld::PrintNeighborInfo(std::ostream& os) { + size_t total = 0; + size_t max_size = 0; + size_t min_size = pop[0].neighbors.size(); + for (Org& org : pop) { + size_t cur_size = org.neighbors.size(); + total += cur_size; + if (cur_size > max_size) max_size = cur_size; + if (cur_size < min_size) min_size = cur_size; + } + emp::vector hist(max_size + 1, 0); + for (Org& org : pop) { + size_t cur_size = org.neighbors.size(); + hist[cur_size]++; + } + // double avg_size = ((double) total) / (double) N; + + os << "neighbors,count\n"; + // << "average," << avg_size << '\n'; + for (size_t i = 0; i < hist.size(); i++) { + os << i << ',' << hist[i] << '\n'; + } + os.flush(); } diff --git a/apps/SpatialCoop2017/source/web/SimplePDWorld-web.cc b/apps/SpatialCoop2017/source/web/SimplePDWorld-web.cc index 0faf8f3480..9a33fd0379 100644 --- a/apps/SpatialCoop2017/source/web/SimplePDWorld-web.cc +++ b/apps/SpatialCoop2017/source/web/SimplePDWorld-web.cc @@ -2,8 +2,8 @@ // Copyright (C) Michigan State University, 2017. // Released under the MIT Software license; see doc/LICENSE -#include "web/web.h" #include "../SimplePDWorld.h" +#include "web/web.h" namespace UI = emp::web; @@ -16,209 +16,221 @@ int cur_x = -1; int cur_y = -1; void DrawCanvas() { - UI::Canvas canvas = doc.Canvas("canvas"); - canvas.Clear("black"); + UI::Canvas canvas = doc.Canvas("canvas"); + canvas.Clear("black"); - const emp::vector & pop = world.GetPop(); + const emp::vector& pop = world.GetPop(); - if (cur_x >= 0) { - canvas.Circle(cur_x, cur_y, world_size*world.GetR(), "pink"); - } + if (cur_x >= 0) { + canvas.Circle(cur_x, cur_y, world_size * world.GetR(), "pink"); + } - for (const Org & org : pop) { - if (org.coop) { - canvas.Circle(org.x*world_size, org.y*world_size, 2, "blue", "#8888FF"); - } else { - canvas.Circle(org.x*world_size, org.y*world_size, 2, "#FF8888", "red"); + for (const Org& org : pop) { + if (org.coop) { + canvas.Circle(org.x * world_size, org.y * world_size, 2, "blue", "#8888FF"); + } else { + canvas.Circle(org.x * world_size, org.y * world_size, 2, "#FF8888", "red"); + } } - } - doc.Text("ud_text").Redraw(); + doc.Text("ud_text").Redraw(); } void CanvasClick(int x, int y) { - cur_x = x; - cur_y = y; - DrawCanvas(); + cur_x = x; + cur_y = y; + DrawCanvas(); } struct RunInfo { - size_t id; + size_t id; - double r; - double u; - size_t N; - size_t E; + double r; + double u; + size_t N; + size_t E; - size_t cur_epoch; - size_t num_coop; - size_t num_defect; + size_t cur_epoch; + size_t num_coop; + size_t num_defect; - RunInfo(size_t _id, double _r, double _u, size_t _N, size_t _E) - : id(_id), r(_r), u(_u), N(_N), E(_E) - , cur_epoch(0), num_coop(0), num_defect(0) - { ; } + RunInfo(size_t _id, double _r, double _u, size_t _N, size_t _E) + : id(_id), r(_r), u(_u), N(_N), E(_E), cur_epoch(0), num_coop(0), num_defect(0) { ; } }; struct RunList { - emp::vector runs; - size_t cur_run = 0; + emp::vector runs; + size_t cur_run = 0; - void AddRun(double r, double u, size_t N, size_t E) { - size_t id = runs.size(); - runs.emplace_back(id, r, u, N, E); - } + void AddRun(double r, double u, size_t N, size_t E) { + size_t id = runs.size(); + runs.emplace_back(id, r, u, N, E); + } - bool Active() const { return cur_run < runs.size(); } + bool Active() const { return cur_run < runs.size(); } }; RunList run_list; int anim_step = 1; -void TogglePlay() -{ - auto & anim = doc.Animate("anim_world"); - anim.ToggleActive(); - auto but = doc.Button("start_but"); - if (anim.GetActive()) but.SetLabel("Pause"); - else but.SetLabel("Start"); - - but = doc.Button("run_but"); - if (anim.GetActive()) but.SetLabel("Stop"); - else but.SetLabel("Fast Forward!"); +void TogglePlay() { + auto& anim = doc.Animate("anim_world"); + anim.ToggleActive(); + auto but = doc.Button("start_but"); + if (anim.GetActive()) + but.SetLabel("Pause"); + else + but.SetLabel("Start"); + + but = doc.Button("run_but"); + if (anim.GetActive()) + but.SetLabel("Stop"); + else + but.SetLabel("Fast Forward!"); } -int main() -{ - doc << "

Spatial Prisoner's Dilema

"; - auto canvas = doc.AddCanvas(world_size, world_size, "canvas"); - // canvas.On("click", CanvasClick); - auto & anim = doc.AddAnimation("anim_world", [](){ - if (run_list.Active()) { - size_t id = run_list.cur_run; - auto & run = run_list.runs[id]; - if (run.cur_epoch == 0) { // Are we starting a new run? - world.Setup(run.r, run.u, run.N, run.E); +int main() { + doc << "

Spatial Prisoner's Dilema

"; + auto canvas = doc.AddCanvas(world_size, world_size, "canvas"); + // canvas.On("click", CanvasClick); + auto& anim = doc.AddAnimation("anim_world", []() { + if (run_list.Active()) { + size_t id = run_list.cur_run; + auto& run = run_list.runs[id]; + if (run.cur_epoch == 0) { // Are we starting a new run? + std::cout << run.r << ' ' << run.u << ' ' << run.N << ' ' << run.E << std::endl; + world.Setup(0.02, 0.175, 6400, 5000); + //world.Setup(run.r, run.u, run.N, run.E); + DrawCanvas(); + } + } + world.Run(anim_step); DrawCanvas(); - } - } - world.Run(anim_step); - DrawCanvas(); - if (run_list.Active()) { - size_t id = run_list.cur_run; - size_t cur_epoch = world.GetEpoch(); - if (run_list.runs[id].E <= cur_epoch) { // Are we done with this run? - run_list.cur_run++; - } - run_list.runs[id].cur_epoch = cur_epoch; - run_list.runs[id].num_coop = world.CountCoop(); - run_list.runs[id].num_defect = run_list.runs[id].N - run_list.runs[id].num_coop; - - auto result_tab = doc.Table("result_tab"); - result_tab.Freeze(); - result_tab.GetCell(id+1,5).ClearChildren() << cur_epoch; - result_tab.GetCell(id+1,6).ClearChildren() << run_list.runs[id].num_coop; - result_tab.GetCell(id+1,7).ClearChildren() << run_list.runs[id].num_defect; - result_tab.Activate(); - } - } ); - - doc << "
"; - doc.AddButton([&anim](){ - anim_step = 1; - TogglePlay(); - }, "Play", "start_but"); - doc.AddButton([](){ world.Run(1); DrawCanvas(); }, "Step", "step_but"); - doc.AddButton([&anim](){ - anim_step = 100; - TogglePlay(); - }, "Fast Forward!", "run_but"); - doc.AddButton([](){ world.Reset(); DrawCanvas(); }, "Randomize", "rand_but"); - auto ud_text = doc.AddText("ud_text"); - ud_text << " Epoch = " << UI::Live(world.epoch); - - doc << "
Radius (r) = "; - doc.AddTextArea([](const std::string & str){ - double r = emp::from_string(str); - world.SetR(r); - }, "r_set").SetText(emp::to_string(world.GetR())); - - doc << "
cost/benefit ratio (u) = "; - doc.AddTextArea([](const std::string & str){ - double u = emp::from_string(str); - world.SetU(u); - }, "u_set").SetText(emp::to_string(world.GetU())); - - - doc << "
Population Size (N) = "; - doc.AddTextArea([](const std::string & str){ - size_t N = emp::from_string(str); - world.SetN(N); - }, "N_set").SetText(emp::to_string(world.GetN())); - - - doc << "
Num Epochs on Run (E) = "; - doc.AddTextArea([](const std::string & str){ - size_t E = emp::from_string(str); - world.SetE(E); - }, "E_set").SetText(emp::to_string(world.GetE())); - - doc << "
" - << "NOTE: You must hit 'Randomize' after changing any parameters for them to take effect." - << "
" - << "

Full Runs

" - << "You can perform many runs at once with the same configuration. " - << "Setup the configuration above, choose the number of runs, and queue them up (as many as you like, even with different parameters). " - << "The next time you start (or fast forward) above, it will start working its way through the queued runs. " - << "
" - << "How many runs? "; - - auto run_input = doc.AddTextArea([](const std::string & str){ - size_t num_runs = emp::from_string(str); - world.SetNumRuns(num_runs); - }, "run_count"); - run_input.SetText(emp::to_string(world.GetNumRuns())); - - doc.AddButton([run_input](){ - //size_t num_runs = emp::from_string(run_input.GetText()); - size_t num_runs = world.GetNumRuns(); - auto result_tab = doc.Table("result_tab"); - for (int run_id = 0; run_id < num_runs; run_id++) { - run_list.AddRun(world.GetR(), world.GetU(), world.GetN(), world.GetE()); - - // Update the table. - int line_id = result_tab.GetNumRows(); - result_tab.Rows(line_id+1); - result_tab.GetCell(line_id, 0) << run_id; - result_tab.GetCell(line_id, 1) << world.GetR(); - result_tab.GetCell(line_id, 2) << world.GetU(); - result_tab.GetCell(line_id, 3) << world.GetN(); - result_tab.GetCell(line_id, 4) << world.GetE(); - result_tab.GetCell(line_id, 5) << "Waiting..."; // world.GetE(); - result_tab.GetCell(line_id, 6) << "Waiting..."; // world.CountCoop(); - result_tab.GetCell(line_id, 7) << "Waiting..."; // (world.GetN() - world.CountCoop()); - - // Draw the new table. - result_tab.CellsCSS("border", "1px solid black"); - result_tab.Redraw(); - } - }, "Queue", "queue_but"); - - doc << "
"; + if (run_list.Active()) { + size_t id = run_list.cur_run; + size_t cur_epoch = world.GetEpoch(); + if (run_list.runs[id].E <= cur_epoch) { // Are we done with this run? + run_list.cur_run++; + } + run_list.runs[id].cur_epoch = cur_epoch; + run_list.runs[id].num_coop = world.CountCoop(); + run_list.runs[id].num_defect = run_list.runs[id].N - run_list.runs[id].num_coop; + + auto result_tab = doc.Table("result_tab"); + result_tab.Freeze(); + result_tab.GetCell(id + 1, 5).ClearChildren() << cur_epoch; + result_tab.GetCell(id + 1, 6).ClearChildren() << run_list.runs[id].num_coop; + result_tab.GetCell(id + 1, 7).ClearChildren() << run_list.runs[id].num_defect; + result_tab.Activate(); + } + }); + + doc << "
"; + doc.AddButton([&anim]() { + anim_step = 1; + TogglePlay(); + }, + "Play", "start_but"); + doc.AddButton([]() { world.Run(1); DrawCanvas(); }, "Step", "step_but"); + doc.AddButton([&anim]() { + anim_step = 100; + TogglePlay(); + }, + "Fast Forward!", "run_but"); + doc.AddButton([]() { world.Reset(); DrawCanvas(); }, "Randomize", "rand_but"); + auto ud_text = doc.AddText("ud_text"); + ud_text << " Epoch = " << UI::Live(world.epoch); + + doc << "
Radius (r) = "; + doc.AddTextArea([](const std::string& str) { + double r = emp::from_string(str); + world.SetR(r); + }, + "r_set") + .SetText(emp::to_string(world.GetR())); + + doc << "
cost/benefit ratio (u) = "; + doc.AddTextArea([](const std::string& str) { + double u = emp::from_string(str); + world.SetU(u); + }, + "u_set") + .SetText(emp::to_string(world.GetU())); + + doc << "
Population Size (N) = "; + doc.AddTextArea([](const std::string& str) { + size_t N = emp::from_string(str); + world.SetN(N); + }, + "N_set") + .SetText(emp::to_string(world.GetN())); + + doc << "
Num Epochs on Run (E) = "; + doc.AddTextArea([](const std::string& str) { + size_t E = emp::from_string(str); + world.SetE(E); + }, + "E_set") + .SetText(emp::to_string(world.GetE())); + + doc << "
" + << "NOTE: You must hit 'Randomize' after changing any parameters for them to take effect." + << "
" + << "

Full Runs

" + << "You can perform many runs at once with the same configuration. " + << "Setup the configuration above, choose the number of runs, and queue them up (as many as you like, even with different parameters). " + << "The next time you start (or fast forward) above, it will start working its way through the queued runs. " + << "
" + << "How many runs? "; + + auto run_input = doc.AddTextArea([](const std::string& str) { + size_t num_runs = emp::from_string(str); + world.SetNumRuns(num_runs); + }, + "run_count"); + run_input.SetText(emp::to_string(world.GetNumRuns())); + + doc.AddButton([run_input]() { + //size_t num_runs = emp::from_string(run_input.GetText()); + size_t num_runs = world.GetNumRuns(); + auto result_tab = doc.Table("result_tab"); + for (int run_id = 0; run_id < num_runs; run_id++) { + run_list.AddRun(world.GetR(), world.GetU(), world.GetN(), world.GetE()); + + // Update the table. + int line_id = result_tab.GetNumRows(); + result_tab.Rows(line_id + 1); + result_tab.GetCell(line_id, 0) << run_id; + result_tab.GetCell(line_id, 1) << world.GetR(); + result_tab.GetCell(line_id, 2) << world.GetU(); + result_tab.GetCell(line_id, 3) << world.GetN(); + result_tab.GetCell(line_id, 4) << world.GetE(); + result_tab.GetCell(line_id, 5) << "Waiting..."; // world.GetE(); + result_tab.GetCell(line_id, 6) << "Waiting..."; // world.CountCoop(); + result_tab.GetCell(line_id, 7) << "Waiting..."; // (world.GetN() - world.CountCoop()); + + // Draw the new table. + result_tab.CellsCSS("border", "1px solid black"); + result_tab.Redraw(); + } + }, + "Queue", "queue_but"); + + doc << "
"; + + auto result_tab = doc.AddTable(1, 8, "result_tab"); + result_tab.SetCSS("border-collapse", "collapse"); + result_tab.SetCSS("border", "3px solid black"); + result_tab.CellsCSS("border", "1px solid black"); + + result_tab.GetCell(0, 0).SetHeader() << "ID"; + result_tab.GetCell(0, 1).SetHeader() << "r"; + result_tab.GetCell(0, 2).SetHeader() << "u"; + result_tab.GetCell(0, 3).SetHeader() << "N"; + result_tab.GetCell(0, 4).SetHeader() << "E"; + result_tab.GetCell(0, 5).SetHeader() << "Epoch"; + result_tab.GetCell(0, 6).SetHeader() << "Num Coop"; + result_tab.GetCell(0, 7).SetHeader() << "Num Defect"; - auto result_tab = doc.AddTable(1,8, "result_tab"); - result_tab.SetCSS("border-collapse", "collapse"); - result_tab.SetCSS("border", "3px solid black"); - result_tab.CellsCSS("border", "1px solid black"); - - result_tab.GetCell(0,0).SetHeader() << "ID"; - result_tab.GetCell(0,1).SetHeader() << "r"; - result_tab.GetCell(0,2).SetHeader() << "u"; - result_tab.GetCell(0,3).SetHeader() << "N"; - result_tab.GetCell(0,4).SetHeader() << "E"; - result_tab.GetCell(0,5).SetHeader() << "Epoch"; - result_tab.GetCell(0,6).SetHeader() << "Num Coop"; - result_tab.GetCell(0,7).SetHeader() << "Num Defect"; - - DrawCanvas(); + DrawCanvas(); } diff --git a/apps/queue-manager b/apps/queue-manager new file mode 160000 index 0000000000..bb4e71f4ff --- /dev/null +++ b/apps/queue-manager @@ -0,0 +1 @@ +Subproject commit bb4e71f4ff71e8d8ee964f70a5271b9485fc4489 diff --git a/source/config/SettingConfig.h b/source/config/SettingConfig.h index e605e7250c..caf5de60f8 100644 --- a/source/config/SettingConfig.h +++ b/source/config/SettingConfig.h @@ -11,150 +11,166 @@ #ifndef EMP_SETTING_CONFIG_H #define EMP_SETTING_CONFIG_H -#include #include #include #include +#include #include "../base/Ptr.h" #include "../base/vector.h" #include "../config/command_line.h" -#include "../tools/math.h" #include "../tools/map_utils.h" +#include "../tools/math.h" #include "../tools/string_utils.h" #include "../tools/vector_utils.h" namespace emp { - /// Class to take a set of value for each "setting" and then step through all combinations of - /// those values for a factorial analysis. +/// Class to take a set of value for each "setting" and then step through all combinations of +/// those values for a factorial analysis. - class SettingConfig { - private: +class SettingConfig { + private: /// Base class to describe information about a single setting. struct SettingBase { - std::string name; ///< Name for this setting - std::string desc; ///< Description of setting - char flag; ///< Command-line flag ('\0' for none) - std::string option; ///< Command-line longer option. - std::string args_label; ///< Label for option arguments (used in --help) - - SettingBase(const std::string & _name, const std::string & _desc, - const char _flag, const std::string & _args_label) - : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--",_name)) - , args_label(_args_label) { } - virtual ~SettingBase() { } - - virtual size_t GetSize() const = 0; ///< How many values are available? - virtual std::string AsString() const = 0; ///< All values, as a single string. - virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. - virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. - virtual bool SetValueID(size_t) {return false; } ///< Setup cur value in linked variable - virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? - virtual size_t GetID() const { return (size_t) -1; } ///< Combination ID for this setting. - - bool IsOptionMatch(const std::string & test_option) const { return test_option == option; } - bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } + std::string name; ///< Name for this setting + std::string desc; ///< Description of setting + char flag; ///< Command-line flag ('\0' for none) + std::string option; ///< Command-line longer option. + std::string args_label; ///< Label for option arguments (used in --help) + + SettingBase(const std::string &_name, const std::string &_desc, + const char _flag, const std::string &_args_label) + : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--", _name)), args_label(_args_label) {} + virtual ~SettingBase() {} + + virtual emp::Ptr Clone() const = 0; + + virtual size_t GetSize() const = 0; ///< How many values are available? + virtual std::string AsString() const = 0; ///< All values, as a single string. + virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. + virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. + virtual bool SetValueID(size_t) { return false; } ///< Setup cur value in linked variable + virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? + virtual size_t GetID() const { return (size_t)-1; } ///< Combination ID for this setting. + + bool IsOptionMatch(const std::string &test_option) const { return test_option == option; } + bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } }; /// Full details about a single setting, including type information and values. template struct SettingInfo : public SettingBase { - T value; - emp::Ptr var_ptr = nullptr; - - SettingInfo(const std::string & _name, ///< Unique name for this setting. - const std::string & _desc, ///< Description of this setting (for help) - const char _flag, ///< Single char flag for easy access (e.g., "-h") - const std::string & _arg, ///< Label for option argument (for help) - emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { } - - size_t GetSize() const override { return 1; } - std::string AsString() const override { return emp::to_string(value); } - std::string AsString(size_t id) const override { - emp_assert(id == 0); - return emp::to_string(value); - } - - bool FromString(const std::string_view & input) override { - value = emp::from_string(input); - // @CAO: Could do more tests to make sure whole string was used. - if (!var_ptr.IsNull()) *var_ptr = value; - return true; - } + T value; + emp::Ptr var_ptr = nullptr; + + SettingInfo(const std::string &_name, ///< Unique name for this setting. + const std::string &_desc, ///< Description of this setting (for help) + const char _flag, ///< Single char flag for easy access (e.g., "-h") + const std::string &_arg, ///< Label for option argument (for help) + emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { ; } + + emp::Ptr Clone() const override { + auto si_ptr = emp::NewPtr>(name, desc, flag, args_label); + si_ptr->value = this->value; + return si_ptr; + } + + size_t GetSize() const override { return 1; } + std::string AsString() const override { return emp::to_string(value); } + std::string AsString(size_t id) const override { + emp_assert(id == 0); + return emp::to_string(value); + } + + bool FromString(const std::string_view &input) override { + value = emp::from_string(input); + // @CAO: Could do more tests to make sure whole string was used. + if (!var_ptr.IsNull()) *var_ptr = value; + return true; + } }; /// Allow a single setting to have multiple values specified that should be stepped through. template struct ComboSettingInfo : public SettingBase { - emp::vector values; ///< Set of values to use for this setting. - emp::Ptr var_ptr = nullptr; ///< Pointer to variable to set as combos change. - size_t id; ///< Unique ID/position for this setting. - - ComboSettingInfo(const std::string & _name, ///< Unique name for this setting. - const std::string & _desc, ///< Description of this setting (for help) - const char _flag, ///< Char flag for easy access (e.g., "-h") - const std::string & _args, ///< Label for option arguments (for help) - emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { } - - size_t GetSize() const override { return values.size(); } - std::string AsString() const override { - std::stringstream ss; - for (size_t i=0; i < values.size(); i++) { - if (i) ss << ','; - ss << values[i]; + emp::vector values; ///< Set of values to use for this setting. + emp::Ptr var_ptr = nullptr; ///< Pointer to variable to set as combos change. + size_t id; ///< Unique ID/position for this setting. + + ComboSettingInfo(const std::string &_name, ///< Unique name for this setting. + const std::string &_desc, ///< Description of this setting (for help) + const char _flag, ///< Char flag for easy access (e.g., "-h") + const std::string &_args, ///< Label for option arguments (for help) + emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { ; } + + emp::Ptr Clone() const override { + auto csi_ptr = emp::NewPtr>(name, desc, flag, args_label); + csi_ptr->values = this->values; + return csi_ptr; } - return ss.str(); - } - std::string AsString(size_t id) const override { - return emp::to_string(values[id]); - } - - bool FromString(const std::string_view & input) override { - // Divide up inputs into comma-separated units. - auto slices = emp::slice(input, ','); - - // Clear out the values to set, one at a time (in most cases, aleady clear) - values.resize(0); - - // Process each slice into one or more values. - for (auto & cur_str : slices) { - // If we are working with an arithmetic type, check if this is a range. - if constexpr (std::is_arithmetic::value) { - auto r_slices = emp::slice(cur_str, ':'); - if (r_slices.size() > 3) return false; // Error! Too many slices!!! - T start = emp::from_string( r_slices[0] ); - T end = emp::from_string( r_slices.back() ); // Same as start if one value. - T step = (r_slices.size() == 3) ? emp::from_string( r_slices[1] ) : 1; - while (start <= end) { - values.push_back(start); - start += step; + + size_t GetSize() const override { return values.size(); } + std::string AsString() const override { + std::stringstream ss; + for (size_t i = 0; i < values.size(); i++) { + if (i) ss << ','; + ss << values[i]; } - } - - // Otherwise do a direct conversion. - else { - values.push_back( emp::from_string(cur_str) ); - } + return ss.str(); + } + std::string AsString(size_t id) const override { + return emp::to_string(values[id]); } - if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; - return values.size(); // Must result in at least one value. - } + bool FromString(const std::string_view &input) override { + // Divide up inputs into comma-separated units. + auto slices = emp::slice(input, ','); + + // Clear out the values to set, one at a time (in most cases, aleady clear) + values.resize(0); + + // Process each slice into one or more values. + for (auto &cur_str : slices) { + // If we are working with an arithmetic type, check if this is a range. + if constexpr (std::is_arithmetic::value) { + auto r_slices = emp::slice(cur_str, ':'); + if (r_slices.size() > 3) return false; // Error! Too many slices!!! + T start = emp::from_string(r_slices[0]); + T end = emp::from_string(r_slices.back()); // Same as start if one value. + T step = (r_slices.size() == 3) ? emp::from_string(r_slices[1]) : 1; + while (start <= end) { + values.push_back(start); + start += step; + } + } + + // Otherwise do a direct conversion. + else { + values.push_back(emp::from_string(cur_str)); + } + } + + if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; + return values.size(); // Must result in at least one value. + } - bool SetValueID(size_t id) override { if (var_ptr) *var_ptr = values[id]; return true; } - bool IsComboSetting() override { return true; } - size_t GetID() const override { return id; } + bool SetValueID(size_t id) override { + if (var_ptr) *var_ptr = values[id]; + return true; + } + bool IsComboSetting() override { return true; } + size_t GetID() const override { return id; } }; /// A setting that is just a flag with an action function to run if it's called. struct ActionFlag { - std::string name; ///< Name for this flag - std::string desc; ///< Description of flag - char flag; ///< Command-line flag ('\0' for none) - std::function fun; ///< Function to be called if flag is set. + std::string name; ///< Name for this flag + std::string desc; ///< Description of flag + char flag; ///< Command-line flag ('\0' for none) + std::function fun; ///< Function to be called if flag is set. }; std::string exe_name = ""; @@ -162,340 +178,381 @@ namespace emp { std::map> setting_map; ///< All settings by name std::map action_map; ///< Action flags - emp::vector> combo_settings; ///< Multi-value settings (in order) - emp::vector cur_combo; ///< Which combo settings are we currently using? - size_t combo_id = 0; ///< Unique value indicating which combination we are on. + emp::vector> combo_settings; ///< Multi-value settings (in order) + emp::vector cur_combo; ///< Which combo settings are we currently using? + size_t combo_id = 0; ///< Unique value indicating which combination we are on. emp::vector unused_args; // Arguments that we were unable to process. std::string errors; - public: + public: SettingConfig() = default; + SettingConfig(const SettingConfig &other) { + combo_settings.resize(other.combo_settings.size()); + for (size_t i = 0; i < combo_settings.size(); i++) { + combo_settings[i] = other.combo_settings[i]->Clone(); + } + for (const auto &entry : other.setting_map) { + setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); + } + + action_map = other.action_map; + cur_combo = other.cur_combo; + combo_id = other.combo_id; + unused_args = other.unused_args; + errors = other.errors; + exe_name = other.exe_name; + } + ~SettingConfig() { - for (auto [name,ptr] : setting_map) ptr.Delete(); + for (auto [name, ptr] : setting_map) ptr.Delete(); } - const std::string & GetExeName() const { return exe_name; } + const std::string &GetExeName() const { return exe_name; } size_t GetComboID() const { return combo_id; } - const emp::vector & GetUnusedArgs() const { return unused_args; } - const std::string & GetErrors() const { return errors; } + const emp::vector &GetUnusedArgs() const { return unused_args; } + const std::string &GetErrors() const { return errors; } bool HasUnusedArgs() const { return unused_args.size(); } bool HasErrors() const { return errors.size(); } + /// Retrieves all of the setting names in config and places into std::vector + std::vector GetSettingMapNames() const { + std::vector result; + for (const auto &p : setting_map) { + result.push_back(p.first); + } + return result; + } + + /// Retrieves all of the setting names in config and places into std::vector + std::vector> GetSettingMapBase() const { + std::vector> result; + for (const auto &p : setting_map) { + result.push_back(p.second); + } + return result; + } + /// Get the current value of a specified setting. template - const T & GetValue(const std::string & name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; - - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - size_t id = cur_combo[ptr->id]; - return ptr->values[id]; - } - - // Otherwise we have a regular setting. - return base_ptr.Cast>()->value; + const T &GetValue(const std::string &name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + size_t id = cur_combo[ptr->id]; + return ptr->values[id]; + } + + // Otherwise we have a regular setting. + return base_ptr.Cast>()->value; } /// Scan through all values and return the maximum. template - T MaxValue(const std::string & name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; - - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - return emp::FindMax(ptr->values); - } - - // Otherwise we have a regular setting with just one value. - return base_ptr.Cast>()->value; + T MaxValue(const std::string &name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + return emp::FindMax(ptr->values); + } + + // Otherwise we have a regular setting with just one value. + return base_ptr.Cast>()->value; } - /// Add a new setting of a specified type. Returns the (initially empty) vector of values + /// Add a new setting of a specified type. Returns the (initially empty) vector of values /// to allow easy setting. /// Example: /// config.AddSetting("num_runs") = 200; template - T & AddSetting(const std::string & name, - const std::string & desc, - const char option_flag, - T & var, - const std::string & args_label="Value") - { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); - setting_map[name] = new_ptr; - return new_ptr->value; + T &AddSetting(const std::string &name, + const std::string &desc, + const char option_flag, + T &var, + const std::string &args_label = "Value") { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); + setting_map[name] = new_ptr; + return new_ptr->value; + } + + /// Add a new setting not linked to a variable + + template + T &AddSetting(const std::string &name, + const std::string &desc = "", + const char option_flag = '\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Value"); + setting_map[name] = new_ptr; + return new_ptr->value; } - /// Add a new setting of a specified type. Returns the (initially empty) vector of values + /// Add a new setting of a specified type. Returns the (initially empty) vector of values /// to allow easy setting. /// Example: /// config.AddComboSetting("pop_size") = { 100,200,400,800 }; template - emp::vector & AddComboSetting(const std::string & name, - const std::string & desc="", - const char option_flag='\0') { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; + emp::vector &AddComboSetting(const std::string &name, + const std::string &desc = "", + const char option_flag = '\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; } /// A setting can also be linked to a value that is kept up-to-date. template - emp::vector & AddComboSetting(const std::string & name, - const std::string & desc, - const char option_flag, - T & var, - const std::string & args_label="Values...") - { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; + emp::vector &AddComboSetting(const std::string &name, + const std::string &desc, + const char option_flag, + T &var, + const std::string &args_label = "Values...") { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; } - void AddAction(const std::string & name, - const std::string & desc, + void AddAction(const std::string &name, + const std::string &desc, const char flag, - std::function fun) - { - std::string name_option = emp::to_string("--", name); - std::string flag_option = emp::to_string("-", flag); - emp_assert(!emp::Has(action_map, name_option)); - emp_assert(!emp::Has(action_map, flag_option)); - action_map[name_option] = ActionFlag{ name, desc, flag, fun }; - action_map[flag_option] = ActionFlag{ name, desc, flag, fun }; + std::function fun) { + std::string name_option = emp::to_string("--", name); + std::string flag_option = emp::to_string("-", flag); + emp_assert(!emp::Has(action_map, name_option)); + emp_assert(!emp::Has(action_map, flag_option)); + action_map[name_option] = ActionFlag{name, desc, flag, fun}; + action_map[flag_option] = ActionFlag{name, desc, flag, fun}; } /// Access ALL values for a specified setting, to be modified freely. template - emp::vector & ComboValues(const std::string & name) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - return setting_map[name].DynamicCast>()->values; + emp::vector &ComboValues(const std::string &name) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + return setting_map[name].DynamicCast>()->values; } /// Start over stepping through all combinations of parameter values. void ResetCombos() { - // Setup as base combo. - for (size_t & x : cur_combo) x = 0; - combo_id = 0; + // Setup as base combo. + for (size_t &x : cur_combo) x = 0; + combo_id = 0; - // Setup all linked values. - for (auto x : combo_settings) x->SetValueID(0); + // Setup all linked values. + for (auto x : combo_settings) x->SetValueID(0); } /// Add a single new value to the specified setting. template - void AddComboValue(const std::string & name, T && val) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - auto ptr = setting_map[name].DynamicCast>(); - ptr->values.emplace_back(std::forward(val)); + void AddComboValue(const std::string &name, T &&val) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + auto ptr = setting_map[name].DynamicCast>(); + ptr->values.emplace_back(std::forward(val)); } /// Set all values for the specified setting. template - void SetComboValues(const std::string & name, T1 && val1, Ts &&... vals) { - emp_assert(emp::Has(setting_map, name)); - auto ptr = setting_map[name].DynamicCast>(); - emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); + void SetComboValues(const std::string &name, T1 &&val1, Ts &&... vals) { + emp_assert(emp::Has(setting_map, name)); + auto ptr = setting_map[name].DynamicCast>(); + emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); } /// Determine how many unique combinations there currently are. size_t CountCombos() { - size_t result = 1; - for (auto ptr : combo_settings) result *= ptr->GetSize(); - return result; + size_t result = 1; + for (auto ptr : combo_settings) result *= ptr->GetSize(); + return result; } /// Set the next combination of settings to be active. Return true if successful /// or false if we ran through all combinations and reset. bool NextCombo() { - combo_id++; - for (size_t i = 0; i < cur_combo.size(); i++) { - cur_combo[i]++; - - // Check if this new combo is valid. - if (cur_combo[i] < combo_settings[i]->GetSize()) { - combo_settings[i]->SetValueID( cur_combo[i] ); // Set value in linked variable. - return true; - } + combo_id++; + for (size_t i = 0; i < cur_combo.size(); i++) { + cur_combo[i]++; + + // Check if this new combo is valid. + if (cur_combo[i] < combo_settings[i]->GetSize()) { + combo_settings[i]->SetValueID(cur_combo[i]); // Set value in linked variable. + return true; + } - // Since it's not, prepare to move on to the next one. - cur_combo[i] = 0; - combo_settings[i]->SetValueID(0); - } + // Since it's not, prepare to move on to the next one. + cur_combo[i] = 0; + combo_settings[i]->SetValueID(0); + } - // No valid combo found. - combo_id = 0; - return false; + // No valid combo found. + combo_id = 0; + return false; } /// Get the set of headers used for the CSV file. - std::string GetSettingHeaders(const std::string & separator=",") { - std::string out_str; - for (auto [name,ptr] : setting_map) { - if (out_str.size()) out_str += separator; - out_str += ptr->name; - } - return out_str; + std::string GetSettingHeaders(const std::string &separator = ",") { + std::string out_str; + for (auto [name, ptr] : setting_map) { + if (out_str.size()) out_str += separator; + out_str += ptr->name; + } + return out_str; } /// Convert all of the current values into a comma-separated string. - std::string CurSettings(const std::string & separator=",") const { - std::string out_str; - for (auto [name,ptr] : setting_map) { - if (ptr) { - if (out_str.size()) out_str += separator; - out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); + std::string CurSettings(const std::string &separator = ",") const { + std::string out_str; + for (auto [name, ptr] : setting_map) { + if (ptr) { + if (out_str.size()) out_str += separator; + out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); + } } - } - return out_str; + return out_str; } /// Get the set of headers used for the CSV file. - std::string GetComboHeaders(const std::string & separator=",") { - std::string out_string; - for (size_t i = 0; i < combo_settings.size(); i++) { - if (i) out_string += separator; - out_string += combo_settings[i]->name; - } - return out_string; + std::string GetComboHeaders(const std::string &separator = ",") { + std::string out_string; + for (size_t i = 0; i < combo_settings.size(); i++) { + if (i) out_string += separator; + out_string += combo_settings[i]->name; + } + return out_string; } /// Convert all of the current values into a comma-separated string. - std::string CurComboString(const std::string & separator=",", - bool use_labels=false, ///< Print name with each value? - bool multi_only=false ///< Only print values that can change? - ) const { - std::string out_str; - for (size_t i = 0; i < cur_combo.size(); i++) { - if (multi_only && combo_settings[i]->GetSize() == 1) continue; - if (out_str.size()) out_str += separator; - if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); - out_str += combo_settings[i]->AsString(cur_combo[i]); - } - return out_str; + std::string CurComboString(const std::string &separator = ",", + bool use_labels = false, ///< Print name with each value? + bool multi_only = false ///< Only print values that can change? + ) const { + std::string out_str; + for (size_t i = 0; i < cur_combo.size(); i++) { + if (multi_only && combo_settings[i]->GetSize() == 1) continue; + if (out_str.size()) out_str += separator; + if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); + out_str += combo_settings[i]->AsString(cur_combo[i]); + } + return out_str; } /// Scan through all settings for a match option and return ID. - emp::Ptr FindOptionMatch(const std::string & option_name) { - for (const auto & [name, ptr] : setting_map) { - if (ptr->IsOptionMatch(option_name)) return ptr; - } - return nullptr; + emp::Ptr FindOptionMatch(const std::string &option_name) { + for (const auto &[name, ptr] : setting_map) { + if (ptr->IsOptionMatch(option_name)) return ptr; + } + return nullptr; } /// Scan through all settings for a match option and return ID. - emp::Ptr FindFlagMatch(const char symbol) { - for (const auto & [name, ptr] : setting_map) { - if (ptr->IsFlagMatch(symbol)) return ptr; - } - return nullptr; + emp::Ptr FindFlagMatch(const char symbol) { + for (const auto &[name, ptr] : setting_map) { + if (ptr->IsFlagMatch(symbol)) return ptr; + } + return nullptr; } /// Take an input set of config options, process them, and track set of unprocessed ones. - bool ProcessOptions(const emp::vector & args) { - exe_name = args[0]; - - for (size_t i = 1; i < args.size(); i++) { - const std::string & cur_arg = args[i]; - if (cur_arg.size() < 2 || cur_arg[0] != '-') { - unused_args.push_back(cur_arg); - continue; // If isn't an option, continue. - } + bool ProcessOptions(const emp::vector &args) { + exe_name = args[0]; + + for (size_t i = 1; i < args.size(); i++) { + const std::string &cur_arg = args[i]; + if (cur_arg.size() < 2 || cur_arg[0] != '-') { + unused_args.push_back(cur_arg); + continue; // If isn't an option, continue. + } - // See if this is a fully spelled-out option. - auto setting_ptr = FindOptionMatch(cur_arg); - if (!setting_ptr.IsNull()) { - if (++i >= args.size()) { - errors += "ERROR: Must provide args for option '--"; - errors += setting_ptr->name; - errors += "' to use!\n"; - std::cerr << errors; - return false; - } - setting_ptr->FromString(args[i]); - // @CAO: Should make sure string translated correctly. - } + // See if this is a fully spelled-out option. + auto setting_ptr = FindOptionMatch(cur_arg); + if (!setting_ptr.IsNull()) { + if (++i >= args.size()) { + errors += "ERROR: Must provide args for option '--"; + errors += setting_ptr->name; + errors += "' to use!\n"; + std::cerr << errors; + return false; + } + setting_ptr->FromString(args[i]); + // @CAO: Should make sure string translated correctly. + } - // See if we have a flag option. - setting_ptr = FindFlagMatch(cur_arg[1]); - if (!setting_ptr.IsNull()) { - // Check if the flag is followed by the values without whitespace. - if (cur_arg.size() > 2) { - setting_ptr->FromString( emp::view_string(cur_arg,2) ); - } - else if (++i >= args.size()) { - std::cout << "ERROR: Must provide args to use!\n"; - return false; - } - else { - setting_ptr->FromString(args[i]); - } - } + // See if we have a flag option. + setting_ptr = FindFlagMatch(cur_arg[1]); + if (!setting_ptr.IsNull()) { + // Check if the flag is followed by the values without whitespace. + if (cur_arg.size() > 2) { + setting_ptr->FromString(emp::view_string(cur_arg, 2)); + } else if (++i >= args.size()) { + std::cout << "ERROR: Must provide args to use!\n"; + return false; + } else { + setting_ptr->FromString(args[i]); + } + } - // Or see of this is a flag trigger. - else if (Has(action_map, cur_arg)) { - action_map[cur_arg].fun(); - } + // Or see of this is a flag trigger. + else if (Has(action_map, cur_arg)) { + action_map[cur_arg].fun(); + } - // Otherwise this argument will go unused; send it back. - else unused_args.push_back(cur_arg); - } + // Otherwise this argument will go unused; send it back. + else + unused_args.push_back(cur_arg); + } - return true; + return true; } - bool ProcessOptions(int argc, char* argv[]) { - return ProcessOptions(emp::cl::args_to_strings(argc, argv)); + bool ProcessOptions(int argc, char *argv[]) { + return ProcessOptions(emp::cl::args_to_strings(argc, argv)); } - template void PrintHelp(const Ts &... examples) const { + std::cout << "Format: " << exe_name << " [OPTIONS...]\n" + << "\nSetting Options:\n"; + for (auto [name, ptr] : setting_map) { + std::string spacing(emp::Max(1, 12 - (int)ptr->args_label.size()), ' '); + std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " + << ptr->desc << " (--" << name << ") [" + << ptr->AsString() << "]\n"; + } + + std::cout << "\nAction Options:\n"; + for (auto [name, action] : action_map) { + if (name.size() == 2) continue; // Skip flag entries. + std::cout << " -" << action.flag << " : " + << action.desc << " (" << name << ")\n"; + } + + if constexpr (sizeof...(examples) > 0) { + std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; + } - std::cout << "Format: " << exe_name << " [OPTIONS...]\n" - << "\nSetting Options:\n"; - for (auto [name, ptr] : setting_map) { - std::string spacing(emp::Max(1, 12 - (int) ptr->args_label.size()), ' '); - std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " - << ptr->desc << " (--" << name << ") [" - << ptr->AsString() << "]\n"; - } - - std::cout << "\nAction Options:\n"; - for (auto [name, action] : action_map) { - if (name.size() == 2) continue; // Skip flag entries. - std::cout << " -" << action.flag << " : " - << action.desc << " (" << name << ")\n"; - } - - if constexpr (sizeof...(examples) > 0) { - std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; - } - - std::cout.flush(); + std::cout.flush(); } - }; +}; // namespace emp -} +} // namespace emp #endif