autofarm: make crop assignment more stable

develop
Kelly Kinkade 2018-08-27 22:29:14 -05:00
parent 3802412d68
commit 6afb76d41c
1 changed files with 46 additions and 15 deletions

@ -22,10 +22,13 @@
#include "modules/Maps.h" #include "modules/Maps.h"
#include "modules/World.h" #include "modules/World.h"
#include <queue>
using std::vector; using std::vector;
using std::string; using std::string;
using std::map; using std::map;
using std::set; using std::set;
using std::queue;
using std::endl; using std::endl;
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
@ -195,27 +198,53 @@ public:
} }
} }
void set_farms(color_ostream& out, vector<int> plants, vector<df::building_farmplotst*> farms) void set_farms(color_ostream& out, set<int> plants, vector<df::building_farmplotst*> farms)
{ {
if (farms.empty()) // this algorithm attempts to change as few farms as possible, while ensuring that
return; // the number of farms planting each eligible plant is "as equal as possible"
if (plants.empty()) if (farms.empty() || plants.empty())
plants = vector<int>{ -1 }; return; // do nothing if there are no farms or no plantable plants
int season = *df::global::cur_season; int season = *df::global::cur_season;
for (int idx = 0; idx < farms.size(); idx++) int min = farms.size() / plants.size(); // the number of farms that should plant each eligible plant, rounded down
int extra = farms.size() - min * plants.size(); // the remainder that cannot be evenly divided
map<int, int> counters;
counters.empty();
queue<df::building_farmplotst*> toChange;
toChange.empty();
for (auto farm : farms)
{ {
df::building_farmplotst* farm = farms[idx];
int o = farm->plant_id[season]; int o = farm->plant_id[season];
int n = plants[idx % plants.size()]; if (plants.count(o)==0 || counters[o] > min || (counters[o] == min && extra == 0))
if (n != o) toChange.push(farm); // this farm is an excess instance for the plant it is currently planting
else
{ {
farm->plant_id[season] = plants[idx % plants.size()]; if (counters[o] == min)
extra--; // allocate off one of the remainder farms
counters[o]++;
}
}
for (auto n : plants)
{
int c = counters[n];
while (toChange.size() > 0 && (c < min || (c == min && extra > 0)))
{
// pick one of the excess farms and change it to plant this plant
df::building_farmplotst* farm = toChange.front();
int o = farm->plant_id[season];
farm->plant_id[season] = n;
out << "autofarm: changing farm #" << farm->id << out << "autofarm: changing farm #" << farm->id <<
" from " << ((o == -1) ? "NONE" : world->raws.plants.all[o]->name) << " from " << ((o == -1) ? "NONE" : world->raws.plants.all[o]->name) <<
" to " << ((n == -1) ? "NONE" : world->raws.plants.all[n]->name) << endl; " to " << ((n == -1) ? "NONE" : world->raws.plants.all[n]->name) << endl;
toChange.pop();
if (c++ == min)
extra--;
} }
} }
} }
@ -248,7 +277,7 @@ public:
} }
} }
map<df::biome_type, vector<int>> plants; map<df::biome_type, set<int>> plants;
plants.clear(); plants.clear();
for (auto plantable : plantable_plants) for (auto plantable : plantable_plants)
@ -257,7 +286,7 @@ public:
if (lastCounts[plant->index] < getThreshold(plant->index)) if (lastCounts[plant->index] < getThreshold(plant->index))
for (auto biome : plantable.second) for (auto biome : plantable.second)
{ {
plants[biome].push_back(plant->index); plants[biome].insert(plant->index);
} }
} }
@ -362,7 +391,9 @@ static command_result autofarm(color_ostream &out, vector <string> & parameters)
{ {
CoreSuspender suspend; CoreSuspender suspend;
if (parameters.size() == 1 && parameters[0] == "enable") if (parameters.size() == 1 && parameters[0] == "runonce")
autofarmInstance->process(out);
else if (parameters.size() == 1 && parameters[0] == "enable")
plugin_enable(out, true); plugin_enable(out, true);
else if (parameters.size() == 1 && parameters[0] == "disable") else if (parameters.size() == 1 && parameters[0] == "disable")
plugin_enable(out, false); plugin_enable(out, false);