AI recruitment: fix units on recall list interfering with recruiting

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.
This commit is contained in:
mattsc 2018-04-11 16:54:00 -07:00
parent cd4bb21c81
commit 1597a2ce27
2 changed files with 33 additions and 22 deletions

View file

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

View file

@ -174,6 +174,7 @@ private:
// 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,