AI: allow list of ids for two passive leader aspects

In addition to 'yes' and 'no', comma separated lists of leader ids are now also accepted as values for these aspects. This allows setting the behavior only for specific leaders.
This commit is contained in:
mattsc 2019-11-23 17:07:36 -08:00
parent 96faf5b54d
commit 926662216a
10 changed files with 114 additions and 51 deletions

View file

@ -1062,6 +1062,20 @@ end
--------- Unit related helper functions ----------
function ai_helper.is_passive_leader(aspect_value, id)
if (aspect_value == 'yes') then return true end
if (aspect_value == 'no') then return false end
local aspect_ids = ai_helper.split(aspect_value)
for _,aspect_id in ipairs(aspect_ids) do
if (aspect_id == id) then
return true
end
end
return false
end
function ai_helper.get_live_units(filter)
-- Note: the order of the filters and the [and] tags are important for speed reasons
return wesnoth.units.find_on_map { { "not", { status = "petrified" } }, { "and", filter } }

View file

@ -84,7 +84,15 @@ function ca_castle_switch:evaluation(cfg, data, filter_own, recruiting_leader)
}, true)
end
if (not leaders[1]) then
local leader
for _,l in pairs(leaders) do
if (not AH.is_passive_leader(ai.aspects.passive_leader, l.id)) then
leader = l
break
end
end
if not leader then
-- CA is irrelevant if no leader or the leader may have moved from another CA
data.CS_leader, data.CS_leader_target = nil, nil
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
@ -237,7 +245,7 @@ end
function ca_castle_switch:execution(cfg, data, filter_own)
if AH.print_exec() then AH.print_ts(' Executing castle_switch CA') end
if AH.show_messages() then wesnoth.wml_actions.message { speaker = leader.id, message = 'Switching castles' } end
if AH.show_messages() then wesnoth.wml_actions.message { speaker = data.leader.id, message = 'Switching castles' } end
AH.robust_move_and_attack(ai, data.CS_leader, data.CS_leader_target, nil, { partial_move = true })
data.CS_leader, data.CS_leader_target = nil

View file

@ -18,14 +18,15 @@ local params = { score_function = (function() return 196000 end) }
if ca_castle_switch then
params.min_turn_1_recruit = (function() return ca_castle_switch:evaluation({}, dummy_engine.data) > 0 end)
params.leader_takes_village = (function(leader)
if ca_castle_switch:evaluation({}, dummy_engine.data, nil, leader) > 0 then
local castle_switch_score = ca_castle_switch:evaluation({}, dummy_engine.data, nil, leader)
if castle_switch_score > 0 then
local take_village = #(wesnoth.get_villages {
x = dummy_engine.data.CS_leader_target[1],
y = dummy_engine.data.CS_leader_target[2]
}) > 0
return take_village
return castle_switch_score, take_village
end
return not ai.aspects.passive_leader
return 0
end
)
end
@ -45,4 +46,4 @@ function ca_recruit_rushers:execution(cfg, data)
return dummy_engine:recruit_rushers_exec()
end
return ca_recruit_rushers
return ca_recruit_rushers

View file

@ -12,8 +12,9 @@ return {
-- (default = 0.1)
-- min_turn_1_recruit: function that returns true if only enough units to grab nearby villages should be recruited turn 1, false otherwise
-- (default always returns false)
-- leader_takes_village: function that returns true if and only if the leader is going to move to capture a village this turn
-- (default returns 'not ai.aspects.passive_leader')
-- leader_takes_village: function that returns the score of the castle_switch CA as its first parameter.
-- If this score is greater than zero, the second parameter is a boolean indicating whether the
-- castle switch move will make the leader end up on a village.
-- enemy_types: array of default enemy unit types to consider if there are no enemies on the map
-- and no enemy sides exist or have recruit lists
-- Note: the recruiting code assumes full knowledge of units on the map and the recruit lists of other sides for the purpose of
@ -928,7 +929,18 @@ return {
data.castle.assigned_villages_x = {}
data.castle.assigned_villages_y = {}
if not ai.aspects.passive_leader and (not params.leader_takes_village or params.leader_takes_village(leader)) then
-- If castle_switch CA makes the unit end up on a village, skip one village for the leader.
-- Also do so if the leader is not passive. Note that the castle_switch CA will also return zero
-- when the leader is passive, but not only in that case.
local ltv_score, skip_one_village = 0
if params.leader_takes_village then
ltv_score, skip_one_village = params.leader_takes_village(leader)
end
if (ltv_score == 0) then
skip_one_village = not AH.is_passive_leader(ai.aspects.passive_leader, leader.id)
end
if skip_one_village then
-- skip one village for the leader
for i,v in ipairs(villages) do
local path, cost = wesnoth.find_path(leader, v[1], v[2], {max_cost = leader.max_moves+1})

View file

@ -730,21 +730,21 @@ double readonly_context_impl::get_leader_value() const
}
bool readonly_context_impl::get_passive_leader() const
std::string readonly_context_impl::get_passive_leader() const
{
if (passive_leader_) {
return passive_leader_->get();
}
return false;
return std::string();
}
bool readonly_context_impl::get_passive_leader_shares_keep() const
std::string readonly_context_impl::get_passive_leader_shares_keep() const
{
if (passive_leader_shares_keep_) {
return passive_leader_shares_keep_->get();
}
return false;
return std::string();
}
@ -1149,7 +1149,7 @@ void readonly_context_impl::recalculate_move_maps() const
possible_moves_ = moves_map();
srcdst_ = move_map();
calculate_possible_moves(possible_moves_,srcdst_,dstsrc_,false,false,&get_avoid());
if (get_passive_leader() && !get_passive_leader_shares_keep()) {
if (is_passive_leader("") && !is_passive_keep_sharing_leader("")) {
unit_map::iterator i = resources::gameboard->units().find_leader(get_side());
if (i.valid()) {
map_location loc = i->get_location();
@ -1299,4 +1299,14 @@ bool readonly_context_impl::is_keep_ignoring_leader(const std::string &id) const
return applies_to_leader(leader_ignores_keep_->get(), id);
}
bool readonly_context_impl::is_passive_leader(const std::string &id) const
{
return applies_to_leader(passive_leader_->get(), id);
}
bool readonly_context_impl::is_passive_keep_sharing_leader(const std::string &id) const
{
return applies_to_leader(passive_leader_shares_keep_->get(), id);
}
} //of namespace ai

View file

@ -282,10 +282,10 @@ public:
virtual double get_leader_value() const = 0;
virtual bool get_passive_leader() const = 0;
virtual std::string get_passive_leader() const = 0;
virtual bool get_passive_leader_shares_keep() const = 0;
virtual std::string get_passive_leader_shares_keep() const = 0;
virtual const moves_map& get_possible_moves() const = 0;
@ -332,6 +332,10 @@ public:
virtual bool is_keep_ignoring_leader(const std::string &id) const = 0;
virtual bool is_passive_leader(const std::string &id) const = 0;
virtual bool is_passive_keep_sharing_leader(const std::string &id) const = 0;
virtual bool is_dst_src_valid_lua() const = 0;
virtual bool is_dst_src_enemy_valid_lua() const = 0;
@ -766,13 +770,13 @@ public:
}
virtual bool get_passive_leader() const override
virtual std::string get_passive_leader() const override
{
return target_->get_passive_leader();
}
virtual bool get_passive_leader_shares_keep() const override
virtual std::string get_passive_leader_shares_keep() const override
{
return target_->get_passive_leader_shares_keep();
}
@ -873,6 +877,16 @@ public:
return target_->is_keep_ignoring_leader(id);
}
virtual bool is_passive_leader(const std::string &id) const override
{
return target_->is_passive_leader(id);
}
virtual bool is_passive_keep_sharing_leader(const std::string &id) const override
{
return target_->is_passive_keep_sharing_leader(id);
}
virtual bool is_dst_src_valid_lua() const override
{
return target_->is_dst_src_valid_lua();
@ -1364,10 +1378,10 @@ public:
virtual double get_leader_value() const override;
virtual bool get_passive_leader() const override;
virtual std::string get_passive_leader() const override;
virtual bool get_passive_leader_shares_keep() const override;
virtual std::string get_passive_leader_shares_keep() const override;
virtual const moves_map& get_possible_moves() const override;
@ -1413,6 +1427,10 @@ public:
virtual bool is_keep_ignoring_leader(const std::string &id) const override;
virtual bool is_passive_leader(const std::string &id) const override;
virtual bool is_passive_keep_sharing_leader(const std::string &id) const override;
virtual bool is_dst_src_valid_lua() const override;
virtual bool is_dst_src_enemy_valid_lua() const override;
@ -1511,8 +1529,8 @@ private:
mutable bool dst_src_enemy_valid_lua_;
mutable bool src_dst_valid_lua_;
mutable bool src_dst_enemy_valid_lua_;
typesafe_aspect_ptr<bool> passive_leader_;
typesafe_aspect_ptr<bool> passive_leader_shares_keep_;
typesafe_aspect_ptr<std::string> passive_leader_;
typesafe_aspect_ptr<std::string> passive_leader_shares_keep_;
mutable moves_map possible_moves_;
typesafe_aspect_ptr<double> recruitment_diversity_;
typesafe_aspect_ptr<config> recruitment_instructions_;

View file

@ -82,7 +82,7 @@ std::shared_ptr<attacks_vector> aspect_attacks_base::analyze_targets() const
std::vector<map_location> unit_locs;
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
if (i->side() == get_side() && i->attacks_left() && !(i->can_recruit() && get_passive_leader())) {
if (i->side() == get_side() && i->attacks_left() && !(i->can_recruit() && is_passive_leader(i->id()))) {
if (!is_allowed_attacker(*i)) {
continue;
}

View file

@ -80,7 +80,7 @@ double goto_phase::evaluate()
for(std::vector<map_location>::const_iterator g = gotos.begin(); g != gotos.end(); ++g) {
unit_map::const_iterator ui = units_.find(*g);
// passive_leader: never moves or attacks
if(ui->can_recruit() && get_passive_leader()){
if(ui->can_recruit() && is_passive_leader(ui->id())){
continue;
}
// end of passive_leader
@ -410,9 +410,6 @@ double move_leader_to_keep_phase::evaluate()
if (is_keep_ignoring_leader("")) {
return BAD_SCORE;
}
if (get_passive_leader() && !get_passive_leader_shares_keep()) {
return BAD_SCORE;
}
// 1. Collect all leaders in a list
// 2. Get the suitable_keep for each leader
@ -434,7 +431,7 @@ double move_leader_to_keep_phase::evaluate()
int shortest_distance = 99999;
for (const unit_map::const_iterator& leader : leaders) {
if (leader->incapacitated() || leader->movement_left() == 0 || !is_allowed_unit(*leader) || is_keep_ignoring_leader(leader->id())) {
if (leader->incapacitated() || leader->movement_left() == 0 || !is_allowed_unit(*leader) || is_keep_ignoring_leader(leader->id()) || (is_passive_leader(leader->id()) && !is_passive_keep_sharing_leader(leader->id()))) {
continue;
}
@ -638,7 +635,7 @@ void get_villages_phase::get_villages(
treachmap reachmap;
for(unit_map::const_iterator u_itor = units_.begin();
u_itor != units_.end(); ++u_itor) {
if(u_itor->can_recruit() && get_passive_leader()){
if(u_itor->can_recruit() && is_passive_leader(u_itor->id())){
continue;
}
if(u_itor->side() == get_side() && u_itor->movement_left() && is_allowed_unit(*u_itor)) {
@ -685,8 +682,6 @@ void get_villages_phase::find_villages(
{
std::map<map_location, double> vulnerability;
const bool passive_leader = get_passive_leader();
std::size_t min_distance = 100000;
const gamemap &map_ = resources::gameboard->map();
std::vector<team> &teams_ = resources::gameboard->teams();
@ -701,10 +696,6 @@ void get_villages_phase::find_villages(
const map_location &current_loc = j->first;
if(j->second == leader_loc_) {
if(passive_leader) {
continue;
}
const std::size_t distance = distance_between(keep_loc_, current_loc);
if(distance < min_distance) {
min_distance = distance;
@ -753,7 +744,7 @@ void get_villages_phase::find_villages(
}
const unit_map::const_iterator u = resources::gameboard->units().find(j->second);
if (u == resources::gameboard->units().end() || u->get_state("guardian") || !is_allowed_unit(*u)) {
if (u == resources::gameboard->units().end() || u->get_state("guardian") || !is_allowed_unit(*u) || (u->can_recruit() && is_passive_leader(u->id()))) {
continue;
}
@ -1351,7 +1342,7 @@ double get_healing_phase::evaluate()
for(; u_it != units_.end(); ++u_it) {
unit &u = *u_it;
if(u.can_recruit() && get_passive_leader()){
if(u.can_recruit() && is_passive_leader(u.id())){
continue;
}
@ -1613,9 +1604,18 @@ leader_shares_keep_phase::~leader_shares_keep_phase()
double leader_shares_keep_phase::evaluate()
{
if(get_passive_leader() && !get_passive_leader_shares_keep()){
bool have_active_leader = false;
std::vector<unit_map::unit_iterator> ai_leaders = resources::gameboard->units().find_leaders(get_side());
for (unit_map::unit_iterator &ai_leader : ai_leaders) {
if (!is_passive_leader(ai_leader->id()) || is_passive_keep_sharing_leader(ai_leader->id())) {
have_active_leader = true;
break;
}
}
if(!have_active_leader) {
return BAD_SCORE;
}
bool allied_leaders_available = false;
for(team &tmp_team : resources::gameboard->teams()) {
if(!current_team().is_enemy(tmp_team.side())){
@ -1645,7 +1645,7 @@ void leader_shares_keep_phase::execute()
//check for each ai leader if he should move away from his keep
for (unit_map::unit_iterator &ai_leader : ai_leaders) {
if(!ai_leader.valid() || !is_allowed_unit(*ai_leader)) {
if(!ai_leader.valid() || !is_allowed_unit(*ai_leader) || (is_passive_leader(ai_leader->id()) && !is_passive_keep_sharing_leader(ai_leader->id()))) {
//This can happen if wml killed or moved a leader during a movement events of another leader
continue;
}

View file

@ -454,16 +454,16 @@ static int cfun_ai_get_leader_value(lua_State *L)
static int cfun_ai_get_passive_leader(lua_State *L)
{
DEPRECATED_ASPECT_MESSAGE("passive_leader");
bool passive_leader = get_readonly_context(L).get_passive_leader();
lua_pushboolean(L, passive_leader);
std::string passive_leader = get_readonly_context(L).get_passive_leader();
lua_pushstring(L, passive_leader.c_str());
return 1;
}
static int cfun_ai_get_passive_leader_shares_keep(lua_State *L)
{
DEPRECATED_ASPECT_MESSAGE("passive_leader_shares_keep");
bool passive_leader_shares_keep = get_readonly_context(L).get_passive_leader_shares_keep();
lua_pushboolean(L, passive_leader_shares_keep);
std::string passive_leader_shares_keep = get_readonly_context(L).get_passive_leader_shares_keep();
lua_pushstring(L, passive_leader_shares_keep.c_str());
return 1;
}

View file

@ -212,10 +212,10 @@ static register_aspect_factory< composite_aspect<std::string>>
static register_aspect_factory< composite_aspect<double>>
leader_value__composite_aspect_factory("leader_value*composite_aspect");
static register_aspect_factory< composite_aspect<bool>>
static register_aspect_factory< composite_aspect<std::string>>
passive_leader__composite_aspect_factory("passive_leader*composite_aspect");
static register_aspect_factory< composite_aspect<bool>>
static register_aspect_factory< composite_aspect<std::string>>
passive_leader_shares_keep__composite_aspect_factory("passive_leader_shares_keep*composite_aspect");
static register_aspect_factory< composite_aspect<double>>
@ -283,10 +283,10 @@ static register_aspect_factory< standard_aspect<std::string>>
static register_aspect_factory< standard_aspect<double>>
leader_value__standard_aspect_factory("leader_value*standard_aspect");
static register_aspect_factory< standard_aspect<bool>>
static register_aspect_factory< standard_aspect<std::string>>
passive_leader__standard_aspect_factory("passive_leader*standard_aspect");
static register_aspect_factory< standard_aspect<bool>>
static register_aspect_factory< standard_aspect<std::string>>
passive_leader_shares_keep__standard_aspect_factory("passive_leader_shares_keep*standard_aspect");
static register_aspect_factory< standard_aspect<double>>
@ -358,10 +358,10 @@ static register_aspect_factory< standard_aspect<std::string>>
static register_aspect_factory< standard_aspect<double>>
leader_value__standard_aspect_factory2("leader_value*");
static register_aspect_factory< standard_aspect<bool>>
static register_aspect_factory< standard_aspect<std::string>>
passive_leader__standard_aspect_factory2("passive_leader*");
static register_aspect_factory< standard_aspect<bool>>
static register_aspect_factory< standard_aspect<std::string>>
passive_leader_shares_keep__standard_aspect_factory2("passive_leader_shares_keep*");
static register_aspect_factory< standard_aspect<double>>
@ -429,10 +429,10 @@ static register_lua_aspect_factory< lua_aspect<std::string>>
static register_lua_aspect_factory< lua_aspect<double>>
leader_value__lua_aspect_factory("leader_value*lua_aspect");
static register_lua_aspect_factory< lua_aspect<bool>>
static register_lua_aspect_factory< lua_aspect<std::string>>
passive_leader__lua_aspect_factory("passive_leader*lua_aspect");
static register_lua_aspect_factory< lua_aspect<bool>>
static register_lua_aspect_factory< lua_aspect<std::string>>
passive_leader_shares_keep__lua_aspect_factory("passive_leader_shares_keep*lua_aspect");
static register_lua_aspect_factory< lua_aspect<double>>