Skip to content

Commit

Permalink
AI recruitment: fix units on recall list interfering with recruiting
Browse files Browse the repository at this point in the history
Units on the recall list are treated as recruits by the AI. The code
previously did not check whether a unit it worth being recalled until
after it makes the decision which unit type to recruit/recall. If it
then can only recall, but not recruit units of that type and all of
them are deemed not worth it, the entire recruitment CA is disabled
(via black listing), meaning recruiting is abandoned also.

This commit fixes this by checking the unit value vs. the recall cost
before putting recalls on the recruitment list.

(cherry picked from commit 1597a2c)
  • Loading branch information
mattsc committed Oct 28, 2018
1 parent ea8c865 commit b9f26a4
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 22 deletions.
54 changes: 32 additions & 22 deletions src/ai/default/recruitment.cpp
Expand Up @@ -262,6 +262,10 @@ void recruitment::execute() {
if (!ufilt(*recall, map_location::null_location())) {
continue;
}
const double recall_value = recruitment::recall_unit_value(recall);
if (recall_value < 0) {
continue; // Unit is not worth to get recalled.
}
data.recruits.insert(recall->type_id());
data.scores[recall->type_id()] = 0.0;
global_recruits.insert(recall->type_id());
Expand Down Expand Up @@ -474,6 +478,33 @@ action_result_ptr recruitment::execute_recruit(const std::string& type, data& le
* If this value is less then the recall cost, we dismiss the unit.
* The unit with the highest value will be returned.
*/

double recruitment::recall_unit_value(const unit_const_ptr & recall_unit) const {
double average_cost_of_advanced_unit = 0;
int counter = 0;
for (const std::string& advancement : recall_unit->advances_to()) {
const unit_type* advancement_type = unit_types.find(advancement);
if (!advancement_type) {
continue;
}
average_cost_of_advanced_unit += advancement_type->cost();
++counter;
}
if (counter > 0) {
average_cost_of_advanced_unit /= counter;
} else {
// Unit don't have advancements. Use cost of unit itself.
average_cost_of_advanced_unit = recall_unit->cost();
}
double xp_quantity = static_cast<double>(recall_unit->experience()) /
recall_unit->max_experience();
double recall_value = recall_unit->cost() + xp_quantity * average_cost_of_advanced_unit;
if (recall_value < current_team().recall_cost()) {
recall_value = -1; // Unit is not worth to get recalled.
}
return recall_value;
}

const std::string* recruitment::get_appropriate_recall(const std::string& type,
const data& leader_data) const {
const std::string* best_recall_id = nullptr;
Expand All @@ -488,28 +519,7 @@ const std::string* recruitment::get_appropriate_recall(const std::string& type,
LOG_AI_RECRUITMENT << "Refused recall because of filter: " << recall_unit->id() << "\n";
continue;
}
double average_cost_of_advanced_unit = 0;
int counter = 0;
for (const std::string& advancement : recall_unit->advances_to()) {
const unit_type* advancement_type = unit_types.find(advancement);
if (!advancement_type) {
continue;
}
average_cost_of_advanced_unit += advancement_type->cost();
++counter;
}
if (counter > 0) {
average_cost_of_advanced_unit /= counter;
} else {
// Unit don't have advancements. Use cost of unit itself.
average_cost_of_advanced_unit = recall_unit->cost();
}
double xp_quantity = static_cast<double>(recall_unit->experience()) /
recall_unit->max_experience();
double recall_value = recall_unit->cost() + xp_quantity * average_cost_of_advanced_unit;
if (recall_value < current_team().recall_cost()) {
continue; // Unit is not worth to get recalled.
}
const double recall_value = recruitment::recall_unit_value(recall_unit);
if (recall_value > best_recall_value) {
best_recall_id = &recall_unit->id();
best_recall_value = recall_value;
Expand Down
1 change: 1 addition & 0 deletions src/ai/default/recruitment.hpp
Expand Up @@ -174,6 +174,7 @@ class recruitment : public candidate_action {
// Helper functions for execute()
action_result_ptr execute_recall(const std::string& id, data& leader_data);
action_result_ptr execute_recruit(const std::string& type, data& leader_data);
double recall_unit_value(const unit_const_ptr & recall_unit) const;
const std::string* get_appropriate_recall(const std::string& type,
const data& leader_data) const;
data* get_best_leader_from_ratio_scores(std::vector<data>& leader_data,
Expand Down

0 comments on commit b9f26a4

Please sign in to comment.