Skip to content

Commit

Permalink
Messenger Micro AI: add [avoid] tag functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsc committed Mar 14, 2021
1 parent 0c4d8eb commit a60736b
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 49 deletions.
34 changes: 20 additions & 14 deletions data/ai/micro_ais/cas/ca_messenger_attack.lua
Expand Up @@ -4,13 +4,13 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
local messenger_next_waypoint = wesnoth.require "ai/micro_ais/cas/ca_messenger_f_next_waypoint.lua"
local best_attack

local function messenger_find_enemies_in_way(messenger, goal_x, goal_y)
local function messenger_find_enemies_in_way(messenger, goal_x, goal_y, avoid_map)
-- Returns the first unit on or next to the path of the messenger
-- @messenger: proxy table for the messenger unit
-- @goal_x,@goal_y: coordinates of the goal toward which the messenger moves
-- Returns proxy table for the first unit found, or nil if none was found

local path, cost = AH.find_path_with_shroud(messenger, goal_x, goal_y, { ignore_units = true })
local path, cost = AH.find_path_with_avoid(messenger, goal_x, goal_y, avoid_map, { ignore_enemies = true })
if cost >= 42424242 then return end

-- The second path hex is the first that is important for the following analysis
Expand All @@ -23,7 +23,7 @@ local function messenger_find_enemies_in_way(messenger, goal_x, goal_y)

-- After that, go through adjacent hexes of all the other path hexes
for i = 2,#path do
local sub_path, sub_cost = AH.find_path_with_shroud(messenger, path[i][1], path[i][2], { ignore_units = true })
local sub_path, sub_cost = AH.find_path_with_avoid(messenger, path[i][1], path[i][2], avoid_map, { ignore_enemies = true })
if (sub_cost <= messenger.moves) then
for xa,ya in H.adjacent_tiles(path[i][1], path[i][2]) do
local enemy = wesnoth.units.get(xa, ya)
Expand All @@ -42,7 +42,9 @@ local function messenger_find_clearing_attack(messenger, goal_x, goal_y, cfg)
-- @goal_x,@goal_y: coordinates of the goal toward which the messenger moves
-- Returns proxy table containing the attack, or nil if none was found

local enemy_in_way = messenger_find_enemies_in_way(messenger, goal_x, goal_y)
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)

local enemy_in_way = messenger_find_enemies_in_way(messenger, goal_x, goal_y, avoid_map)
if (not enemy_in_way) then return end

local filter = wml.get_child(cfg, "filter") or { id = cfg.id }
Expand All @@ -57,8 +59,9 @@ local function messenger_find_clearing_attack(messenger, goal_x, goal_y, cfg)

local max_rating = - math.huge
for _,attack in ipairs(attacks) do
if (attack.target.x == enemy_in_way.x) and (attack.target.y == enemy_in_way.y) then

if (attack.target.x == enemy_in_way.x) and (attack.target.y == enemy_in_way.y)
and (not avoid_map:get(attack.dst.x, attack.dst.y))
then
-- Rating: expected HP of attacker and defender
local rating = attack.att_stats.average_hp - 2 * attack.def_stats.average_hp

Expand All @@ -74,16 +77,18 @@ local function messenger_find_clearing_attack(messenger, goal_x, goal_y, cfg)
--> try to fight our way to that enemy
for _,attack in ipairs(attacks) do
-- Rating: expected HP of attacker and defender
local rating = attack.att_stats.average_hp - 2 * attack.def_stats.average_hp
if (not avoid_map:get(attack.dst.x, attack.dst.y)) then
local rating = attack.att_stats.average_hp - 2 * attack.def_stats.average_hp

-- Give a huge bonus for closeness to enemy_in_way
local tmp_defender = wesnoth.units.get(attack.target.x, attack.target.y)
local dist = wesnoth.map.distance_between(enemy_in_way.x, enemy_in_way.y, tmp_defender.x, tmp_defender.y)
-- Give a huge bonus for closeness to enemy_in_way
local tmp_defender = wesnoth.units.get(attack.target.x, attack.target.y)
local dist = wesnoth.map.distance_between(enemy_in_way.x, enemy_in_way.y, tmp_defender.x, tmp_defender.y)

rating = rating + 100. / dist
rating = rating + 100. / dist

if (rating > max_rating) then
max_rating, best_attack = rating, attack
if (rating > max_rating) then
max_rating, best_attack = rating, attack
end
end
end

Expand All @@ -108,7 +113,8 @@ function ca_messenger_attack:evaluation(cfg)
end

function ca_messenger_attack:execution(cfg)
AH.robust_move_and_attack(ai, best_attack.src, best_attack.dst, best_attack.target)
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)
AH.robust_move_and_attack(ai, best_attack.src, best_attack.dst, best_attack.target, { avoid_map = avoid_map })
best_attack = nil
end

Expand Down
58 changes: 31 additions & 27 deletions data/ai/micro_ais/cas/ca_messenger_escort_move.lua
Expand Up @@ -32,6 +32,8 @@ function ca_messenger_escort_move:execution(cfg)
local escorts = get_escorts(cfg)
local _, _, _, messengers = messenger_next_waypoint(cfg)

local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)

local enemies = AH.get_attackable_enemies()

local base_rating_map = LS.create()
Expand All @@ -44,40 +46,42 @@ function ca_messenger_escort_move:execution(cfg)
local unit_rating = unit.max_moves / 100. + unit.hitpoints / 1000.

reach_map:iter( function(x, y, v)
local base_rating = base_rating_map:get(x, y)

if (not base_rating) then
base_rating = 0
if (not avoid_map:get(x, y)) or ((x == unit.x) and (y == unit.y)) then
local base_rating = base_rating_map:get(x, y)

if (not base_rating) then
base_rating = 0

-- Distance from messenger is most important; only closest messenger counts for this
-- Give somewhat of a bonus for the messenger that has moved the farthest through the waypoints
local max_messenger_rating = - math.huge
for _,m in ipairs(messengers) do
local messenger_rating = 1. / (M.distance_between(x, y, m.x, m.y) + 2.)
local wp_rating = MAIUV.get_mai_unit_variables(m, cfg.ai_id, "wp_rating")
messenger_rating = messenger_rating * 10. * (1. + wp_rating * 2.)

if (messenger_rating > max_messenger_rating) then
max_messenger_rating = messenger_rating
end
end

-- Distance from messenger is most important; only closest messenger counts for this
-- Give somewhat of a bonus for the messenger that has moved the farthest through the waypoints
local max_messenger_rating = - math.huge
for _,m in ipairs(messengers) do
local messenger_rating = 1. / (M.distance_between(x, y, m.x, m.y) + 2.)
local wp_rating = MAIUV.get_mai_unit_variables(m, cfg.ai_id, "wp_rating")
messenger_rating = messenger_rating * 10. * (1. + wp_rating * 2.)
base_rating = base_rating + max_messenger_rating

if (messenger_rating > max_messenger_rating) then
max_messenger_rating = messenger_rating
-- Distance from (sum of) enemies is important too
-- This favors placing escort units between the messenger and close enemies
for _,e in ipairs(enemies) do
base_rating = base_rating + 1. / (M.distance_between(x, y, e.x, e.y) + 2.)
end
end

base_rating = base_rating + max_messenger_rating

-- Distance from (sum of) enemies is important too
-- This favors placing escort units between the messenger and close enemies
for _,e in ipairs(enemies) do
base_rating = base_rating + 1. / (M.distance_between(x, y, e.x, e.y) + 2.)
base_rating_map:insert(x, y, base_rating)
end

base_rating_map:insert(x, y, base_rating)
end

local rating = base_rating + unit_rating
local rating = base_rating + unit_rating

if (rating > max_rating) then
max_rating = rating
best_unit, best_hex = unit, { x, y }
if (rating > max_rating) then
max_rating = rating
best_unit, best_hex = unit, { x, y }
end
end
end)
end
Expand Down
21 changes: 14 additions & 7 deletions data/ai/micro_ais/cas/ca_messenger_move.lua
Expand Up @@ -16,23 +16,30 @@ end
function ca_messenger_move:execution(cfg)
local messenger, x, y = messenger_next_waypoint(cfg)

local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)

if (messenger.x ~= x) or (messenger.y ~= y) then
local wp = AH.get_closest_location(
{ x, y },
{ { "not", { { "filter", { { "not", { side = wesnoth.current.side } } } } } } },
messenger
messenger, avoid_map
)
x, y = wp[1], wp[2]
end

local next_hop = AH.next_hop(messenger, x, y, { ignore_own_units = true } )
local avoid_map = AH.get_avoid_map(ai, wml.get_child(cfg, "avoid"), true)

local path = AH.find_path_with_avoid(messenger, x, y, avoid_map)
if (not path) then path = { { messenger.x, messenger.y } } end
local next_hop = AH.next_hop(messenger, x, y, { path = path, avoid_map = avoid_map } )
if (not next_hop) then next_hop = { messenger.x, messenger.y } end

-- Compare this to the "ideal path"
local path = AH.find_path_with_shroud(messenger, x, y, { ignore_units = 'yes' })
local path = AH.find_path_with_avoid(messenger, x, y, avoid_map, { ignore_enemies = true })
if (not path) then path = { { messenger.x, messenger.y } } end
local optimum_hop = { messenger.x, messenger.y }
for _,step in ipairs(path) do
local sub_path, sub_cost = AH.find_path_with_shroud(messenger, step[1], step[2])
local sub_path, sub_cost = AH.find_path_with_avoid(messenger, step[1], step[2], avoid_map)
if sub_cost > messenger.moves then
break
else
Expand Down Expand Up @@ -60,14 +67,14 @@ function ca_messenger_move:execution(cfg)
if unit_in_way then unit_in_way:extract() end

messenger.loc = { next_hop[1], next_hop[2] }
local _, cost1 = AH.find_path_with_shroud(messenger, x, y, { ignore_units = 'yes' })
local _, cost1 = AH.find_path_with_avoid(messenger, x, y, avoid_map, { ignore_enemies = true })

local unit_in_way2 = wesnoth.units.get(optimum_hop[1], optimum_hop[2])
if (unit_in_way2 == messenger) then unit_in_way2 = nil end
if unit_in_way2 then unit_in_way2:extract() end

messenger.loc = { optimum_hop[1], optimum_hop[2] }
local _, cost2 = AH.find_path_with_shroud(messenger, x, y, { ignore_units = 'yes' })
local _, cost2 = AH.find_path_with_avoid(messenger, x, y, avoid_map, { ignore_enemies = true })

messenger.loc = { x_current, y_current }
if unit_in_way then unit_in_way:to_map() end
Expand All @@ -78,7 +85,7 @@ function ca_messenger_move:execution(cfg)
if (cost2 + messenger.max_moves/2 < cost1) then next_hop = optimum_hop end

if next_hop and ((next_hop[1] ~= messenger.x) or (next_hop[2] ~= messenger.y)) then
AH.robust_move_and_attack(ai, messenger, next_hop)
AH.robust_move_and_attack(ai, messenger, next_hop, nil, { avoid_map = avoid_map })
else
AH.checked_stopunit_moves(ai, messenger)
end
Expand Down
2 changes: 1 addition & 1 deletion data/ai/micro_ais/mai-defs/escort.lua
Expand Up @@ -8,7 +8,7 @@ function wesnoth.micro_ais.messenger_escort(cfg)
AH.get_multi_named_locs_xy('waypoint', cfg, 'Messenger [micro_ai] tag')
end
local required_keys = {}
local optional_keys = { "id", "enemy_death_chance", "[filter]", "[filter_second]", "invert_order", "messenger_death_chance", "waypoint_loc", "waypoint_x", "waypoint_y" }
local optional_keys = { "[avoid]", "id", "enemy_death_chance", "[filter]", "[filter_second]", "invert_order", "messenger_death_chance", "waypoint_loc", "waypoint_x", "waypoint_y" }
local score = cfg.ca_score or 300000
local CA_parms = {
ai_id = 'mai_messenger',
Expand Down

0 comments on commit a60736b

Please sign in to comment.