AI: improve efficiency of move-to-any-enemy CA
The new method avoids a lot of path finding that the previous method did, esp. on large maps with many units and/or enemies, leading to significant speed improvements in some cases. This fixes #6504
This commit is contained in:
parent
a6bf253c73
commit
b07320f4d0
1 changed files with 35 additions and 14 deletions
|
@ -2,6 +2,10 @@
|
|||
-- Move AI units toward any enemy on the map. This has a very low CA score and
|
||||
-- only kicks in when the AI would do nothing else. It prevents the AI from
|
||||
-- being inactive on maps without enemy leaders and villages.
|
||||
-- It has a very simple algorithm that does well enough in many cases, but should
|
||||
-- be considered a fall-back option. If more complex behavior is desired, use
|
||||
-- the move-to-targets CA and customize it with [goal] tags. That works even
|
||||
-- on maps without enemy leaders and villages.
|
||||
|
||||
local H = wesnoth.require "helper"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
@ -32,30 +36,47 @@ function ca_move_to_any_enemy:evaluation(cfg, data, filter_own)
|
|||
-- checks this, but we might as well eliminate unreachable enemies right away
|
||||
local enemies = AH.get_attackable_enemies({}, wesnoth.current.side, { avoid_map = avoid_map })
|
||||
|
||||
local unit, destination
|
||||
-- Find first unit that can reach a hex adjacent to an enemy, and find closest enemy of those reachable.
|
||||
-- This does not need to find the absolutely best combination, close to that is good enough.
|
||||
-- Presort unit/enemy pairs by distance in hexes to avoid unnecessary path finding.
|
||||
-- As AI units are dealt with one by one below, this only needs to be done individually
|
||||
-- for each AI unit, not for the whole set of pairs.
|
||||
local unit_distances = {}
|
||||
for i,u in ipairs(units) do
|
||||
local best_cost, best_path, best_enemy = AH.no_path
|
||||
local enemy_distances = {}
|
||||
for i,e in ipairs(enemies) do
|
||||
-- We only need to look at adjacent hexes. And we don't worry whether they
|
||||
-- are occupied by other enemies. If that is the case, no path will be found,
|
||||
-- but one of those enemies will later be found as potential target.
|
||||
for xa,ya in H.adjacent_tiles(e.x, e.y) do
|
||||
if (not avoid_map:get(xa, ya)) then
|
||||
local path, cost = AH.find_path_with_avoid(u, xa, ya, avoid_map)
|
||||
local dist = wesnoth.map.distance_between(u, e)
|
||||
table.insert(enemy_distances, { x = e.x, y = e.y, dist = dist })
|
||||
end
|
||||
-- Sort enemies by distance for this AI unit
|
||||
table.sort(enemy_distances, function(a, b) return (a.dist < b.dist) end)
|
||||
|
||||
table.insert(unit_distances, { x = u.x, y = u.y, enemy_distances = enemy_distances })
|
||||
end
|
||||
-- Finally, sort AI units by distance to their closest enemies
|
||||
table.sort(unit_distances, function(a, b) return (a.enemy_distances[1].dist < b.enemy_distances[1].dist) end)
|
||||
|
||||
local unit, destination
|
||||
-- Find the closest enemies to the sorted AI units. This does not need to find the absolutely best
|
||||
-- combination, such as which hex adjacent to the unit is best, as these enemies are out
|
||||
-- of reach of the AI (otherwise other CAs would have triggered previously).
|
||||
-- This moves one AI unit at a time in order to speed up evaluation.
|
||||
for i,ud in ipairs(unit_distances) do
|
||||
local u = wesnoth.units.get(ud.x, ud.y)
|
||||
local best_cost, best_path = AH.no_path
|
||||
for i,ed in ipairs(ud.enemy_distances) do
|
||||
-- Only do path finding if the distance to the enemy in less than the current best path cost,
|
||||
-- otherwise it is impossible to find a shorter path.
|
||||
if (not best_path) or (ed.dist < best_cost) then
|
||||
if (not avoid_map:get(ed.x, ed.y)) then
|
||||
local path, cost = AH.find_path_with_avoid(u, ed.x, ed.y, avoid_map, { ignore_enemies = true })
|
||||
if (cost < best_cost) then
|
||||
best_cost = cost
|
||||
best_path = path
|
||||
best_enemy = e
|
||||
-- We also don't care if this is the closest adjacent hex, just pick the first found
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if best_enemy then
|
||||
if best_path then
|
||||
MTAE_destination = AH.next_hop(u, nil, nil, { path = best_path, avoid_map = avoid_map })
|
||||
if (MTAE_destination[1] ~= u.x) or (MTAE_destination[2] ~= u.y) then
|
||||
MTAE_unit = u
|
||||
|
|
Loading…
Add table
Reference in a new issue