From a76de8b777d3f73ffc64387c6385a6196e7f704c Mon Sep 17 00:00:00 2001 From: RossM Date: Tue, 3 Apr 2012 23:26:15 -0700 Subject: [PATCH] Fix detection of military dwarves. Add overview comments explaining why autolabor works how it does. --- plugins/autolabor.cpp | 45 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 5fb5216e5..fedde8f33 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -31,6 +31,31 @@ using df::global::world; #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) +/* + * Autolabor module for dfhack + * + * The idea behind this module is to constantly adjust labors so that the right dwarves + * are assigned to new tasks. The key is that, for almost all labors, once a dwarf begins + * a job it will finish that job even if the associated labor is removed. Thus the + * strategy is to frequently decide, for each labor, which dwarves should possibly take + * a new job for that labor if it comes in and which shouldn't, and then set the labors + * appropriately. The updating should happen as often as can be reasonably done without + * causing lag. + * + * The obvious thing to do is to just set each labor on a single idle dwarf who is best + * suited to doing new jobs of that labor. This works in a way, but it leads to a lot + * of idle dwarves since only one dwarf will be dispatched for each labor in an update + * cycle, and dwarves that finish tasks will wait for the next update before being + * dispatched. An improvement is to also set some labors on dwarves that are currently + * doing a job, so that they will immediately take a new job when they finish. The + * details of which dwarves should have labors set is mostly a heuristic. + * + * A complication to the above simple scheme is labors that have associated equipment. + * Enabling/disabling these labors causes dwarves to change equipment, and disabling + * them in the middle of a job may cause the job to be abandoned. Those labors + * (mining, hunting, and woodcutting) need to be handled carefully to minimize churn. + */ + static int enable_autolabor = 0; static bool print_debug = 0; @@ -659,11 +684,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { dwarf_info[dwarf].state = CHILD; } + else if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession)) + dwarf_info[dwarf].state = MILITARY; else if (dwarfs[dwarf]->job.current_job == NULL) { - if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession)) - dwarf_info[dwarf].state = MILITARY; - else if (is_on_break) + if (is_on_break) dwarf_info[dwarf].state = OTHER; else if (dwarfs[dwarf]->meetings.size() > 0) dwarf_info[dwarf].state = OTHER; @@ -773,6 +798,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) auto mode = labor_infos[labor].mode; + // Find candidate dwarfs, and calculate a preference value for each dwarf for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { if (dwarf_info[dwarf].state == CHILD) @@ -827,9 +853,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } + // Sort candidates by preference value values_sorter ivs(values); std::sort(candidates.begin(), candidates.end(), ivs); + // Disable the labor on everyone for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { if (dwarf_info[dwarf].state == CHILD) @@ -852,6 +880,17 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (state_count[IDLE] < 2) want_idle_dwarf = false; + /* + * Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in + * order of preference, and then assign additional dwarfs that meet any of these conditions: + * - The dwarf is idle and there are no idle dwarves assigned to this labor + * - The dwarf has nonzero skill associated with the labor + * - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. + * We stop assigning dwarfs when we reach the maximum allowed. + * Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs + * (sleeping, eating, on break, etc.) will have labors assigned, but will not be counted. + * Military and children/nobles will not have labors assigned. + */ for (int i = 0; i < candidates.size() && labor_infos[labor].active_dwarfs < max_dwarfs; i++) { int dwarf = candidates[i];