Multipack Wolves MAI: add [avoid] tag functionality

This includes both adding an [avoid] tag to the MAI itself, and taking the default AI [avoid] tag into account. If both are given, the former takes precedence over the latter.
This commit is contained in:
mattsc 2018-11-01 18:10:38 -07:00
parent d7418ab8e2
commit 873bf2f390
3 changed files with 56 additions and 24 deletions

View file

@ -23,6 +23,8 @@ end
function ca_wolves_multipacks_attack:execution(cfg)
local packs = WMPF.assign_packs(cfg)
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
-- Attacks are dealt with on a pack by pack basis
-- and we want all wolves in a pack to move first, before going on to the next pack
for pack_number,pack in pairs(packs) do
@ -41,20 +43,23 @@ function ca_wolves_multipacks_attack:execution(cfg)
local all_attacks = {}
if wolves[1] then all_attacks = AH.get_attacks(wolves, { simulate_combat = true }) end
-- Eliminate targets that would split up the wolves by more than 3 hexes
-- Eliminate targets that would split up the wolves by more than 3 hexes.
-- Also eliminate attacks in areas specified by [avoid] tags.
local attacks = {}
for _,attack in ipairs(all_attacks) do
local attack_splits_pack = false
for _,wolf in ipairs(wolves) do
local nh = AH.next_hop(wolf, attack.dst.x, attack.dst.y)
local dist = M.distance_between(nh[1], nh[2], attack.dst.x, attack.dst.y)
if (dist > 3) then
attack_splits_pack = true
break
if (not avoid_map:get(attack.dst.x, attack.dst.y)) then
local attack_splits_pack = false
for _,wolf in ipairs(wolves) do
local nh = AH.next_hop(wolf, attack.dst.x, attack.dst.y)
local dist = M.distance_between(nh[1], nh[2], attack.dst.x, attack.dst.y)
if (dist > 3) then
attack_splits_pack = true
break
end
end
if (not attack_splits_pack) then
table.insert(attacks, attack)
end
end
if (not attack_splits_pack) then
table.insert(attacks, attack)
end
end
@ -169,13 +174,13 @@ function ca_wolves_multipacks_attack:execution(cfg)
rating = rating - M.distance_between(x, y, wolf_no_moves.x, wolf_no_moves.y)
end
return rating
end)
end, { avoid_map = avoid_map })
if cfg.show_pack_number then
WMPF.clear_label(wolf_moves.x, wolf_moves.y)
end
AH.movefull_stopunit(ai, wolf_moves, best_hex)
AH.movefull_stopunit(ai, wolf_moves, best_hex or { wolf_moves.x, wolf_moves.y })
if cfg.show_pack_number and wolf_moves and wolf_moves.valid then
WMPF.put_label(wolf_moves.x, wolf_moves.y, pack_number)

View file

@ -21,6 +21,8 @@ end
function ca_wolves_multipacks_wander:execution(cfg)
local packs = WMPF.assign_packs(cfg)
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
for pack_number,pack in pairs(packs) do
-- If any of the wolves has a goal set, this is used for the entire pack
local wolves, goal = {}, {}
@ -43,21 +45,38 @@ function ca_wolves_multipacks_wander:execution(cfg)
-- Pack gets a new goal if none exist or on any move with 10% random chance
local rand = math.random(10)
if (not goal[1]) or (rand == 1) then
local locs = AH.get_locations_no_borders {}
local all_locs = AH.get_locations_no_borders {}
local locs = {}
for _,loc in ipairs(all_locs) do
if (not avoid_map:get(loc[1], loc[2])) then
table.insert(locs, loc)
end
end
-- Need to find reachable terrain for this to be a viable goal
-- We only check whether the first wolf can get there
local unreachable = true
while unreachable do
while locs[1] and unreachable do
local rand = math.random(#locs)
local next_hop = AH.next_hop(wolves[1], locs[rand][1], locs[rand][2])
if next_hop then
goal = { locs[rand][1], locs[rand][2] }
unreachable = nil
else
table.remove(locs, rand)
end
end
end
-- It is possible that no goal is found here due to the combination of
-- impassable terrain and [avoid] instructions
if (not goal[1]) then
for _,wolf in ipairs(wolves) do
AH.checked_stopunit_moves(ai, wolf)
end
wolves = {}
end
-- This goal is saved with every wolf of the pack
for _,wolf in ipairs(wolves) do
MAIUV.insert_mai_unit_variables(wolf, cfg.ai_id, { goal_x = goal[1], goal_y = goal[2] })
@ -72,10 +91,10 @@ function ca_wolves_multipacks_wander:execution(cfg)
-- Number of wolves that can reach each hex,
local reach_map = LS.create()
for _,wolf in ipairs(wolves) do
local reach = wesnoth.find_reach(wolf)
for _,loc in ipairs(reach) do
reach_map:insert(loc[1], loc[2], (reach_map:get(loc[1], loc[2]) or 0) + 100)
end
local single_reach_map = AH.get_reachmap(wolf, { avoid_map = avoid_map })
single_reach_map:iter( function(x, y, v)
reach_map:insert(x, y, (reach_map:get(x, y) or 0) + 100)
end)
end
-- Keep only those hexes that can be reached by all wolves in the pack
@ -100,7 +119,7 @@ function ca_wolves_multipacks_wander:execution(cfg)
-- If there's no hex that all units can reach, use the 'center of gravity' between them
-- Then we move the first wolf (fewest MP) toward that hex, and the position of that wolf
-- becomes the goto coordinates for the others
if (not goto_hex) then
if wolves[1] and (not goto_hex) then
local cg = { 0, 0 } -- Center of gravity hex
for _,wolf in ipairs(wolves) do
cg = { cg[1] + wolf.x, cg[2] + wolf.y }
@ -111,11 +130,19 @@ function ca_wolves_multipacks_wander:execution(cfg)
-- Find closest move for Wolf #1 to that position, which then becomes the goto hex
goto_hex = AH.find_best_move(wolves[1], function(x, y)
return -M.distance_between(x, y, cg[1], cg[2])
end)
end, { avoid_map = avoid_map })
-- We could move this wolf right here, but for convenience all the actual moves
-- are done together below. This should be a small extra calculation cost
end
-- It is possible here also that no goto_hex is found
if (not goto_hex) then
for _,wolf in ipairs(wolves) do
AH.checked_stopunit_moves(ai, wolf)
end
wolves = {}
end
-- Now all wolves in the pack are moved toward goto_hex, starting with the one with fewest MP
-- Distance to goal hex is taken into account as secondary criterion
for _,wolf in ipairs(wolves) do
@ -123,13 +150,13 @@ function ca_wolves_multipacks_wander:execution(cfg)
local rating = -M.distance_between(x, y, goto_hex[1], goto_hex[2])
rating = rating -M.distance_between(x, y, goal[1], goal[2]) / 100.
return rating
end)
end, { avoid_map = avoid_map })
if cfg.show_pack_number then
WMPF.clear_label(wolf.x, wolf.y)
end
local move_result = AH.movefull_stopunit(ai, wolf, best_hex)
local move_result = AH.movefull_stopunit(ai, wolf, best_hex or { wolf.x, wolf.y })
if cfg.show_pack_number and wolf and wolf.valid then
WMPF.put_label(wolf.x, wolf.y, pack_number)

View file

@ -154,7 +154,7 @@ function wesnoth.micro_ais.swarm(cfg)
end
function wesnoth.micro_ais.wolves_multipacks(cfg)
local optional_keys = { "type", "pack_size", "show_pack_number" }
local optional_keys = { "[avoid]", "type", "pack_size", "show_pack_number" }
local score = cfg.ca_score or 300000
local CA_parms = {
ai_id = 'mai_wolves_multipacks',