|  |  | @ -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); | 
			
		
	
	
		
		
			
				
					|  |  | 
 |