mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-26 17:51:51 +00:00

Now it's renamed to wesnoth.units.find_on_map. wesnoth.units.find implements the case of finding units on either the map or a recall list.
94 lines
3.2 KiB
Lua
94 lines
3.2 KiB
Lua
local LS = wesnoth.require "location_set"
|
|
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
|
local F = wesnoth.require "functional"
|
|
|
|
local function get_protected_units(cfg)
|
|
local units = {}
|
|
for u in wml.child_range(cfg, "unit") do
|
|
table.insert(units, AH.get_units_with_moves { id = u.id }[1])
|
|
end
|
|
return units
|
|
end
|
|
|
|
local ca_protect_unit_move = {}
|
|
|
|
function ca_protect_unit_move:evaluation(cfg)
|
|
if get_protected_units(cfg)[1] then return 94999 end
|
|
return 0
|
|
end
|
|
|
|
function ca_protect_unit_move:execution(cfg, data)
|
|
-- Find and execute best (safest) move toward goal
|
|
local protected_units = get_protected_units(cfg)
|
|
|
|
-- Need to take the protected units off the map, as they don't count into the map scores
|
|
-- as long as they can still move
|
|
for _,unit in ipairs(protected_units) do unit:extract() end
|
|
|
|
local units = wesnoth.units.find_on_map { side = wesnoth.current.side }
|
|
local enemy_units = AH.get_attackable_enemies()
|
|
|
|
local attack_map = BC.get_attack_map(units).units -- enemy attack map
|
|
local enemy_attack_map = BC.get_attack_map(enemy_units).units -- enemy attack map
|
|
|
|
-- Now put the protected units back out there
|
|
for _,unit in ipairs(protected_units) do unit:to_map() end
|
|
|
|
-- We move the weakest (fewest HP unit) first
|
|
local unit = F.choose(protected_units, function(u) return - u.hitpoints end)
|
|
local goal = {}
|
|
for u in wml.child_range(cfg, "unit") do
|
|
if (unit.id == u.id) then goal = AH.get_named_loc_xy('goal', u) end
|
|
end
|
|
|
|
local reach_map = AH.get_reachable_unocc(unit)
|
|
local enemy_inverse_distance_map = AH.inverse_distance_map(enemy_units, reach_map)
|
|
|
|
local terrain_defense_map = LS.create()
|
|
reach_map:iter(function(x, y, data)
|
|
terrain_defense_map:insert(x, y, 100 - unit:defense(wesnoth.get_terrain(x, y)))
|
|
end)
|
|
|
|
local goal_distance_map = LS.create()
|
|
reach_map:iter(function(x, y, data)
|
|
goal_distance_map:insert(x, y, wesnoth.map.distance_between(x, y, goal[1], goal[2]))
|
|
end)
|
|
|
|
-- Configuration parameters (no option to change these enabled at the moment)
|
|
local enemy_weight = 100.
|
|
local my_unit_weight = 1.
|
|
local distance_weight = 3.
|
|
local terrain_weight = 0.1
|
|
|
|
-- If there are no enemies left, only distance to goal matters
|
|
-- This is to avoid rare situations where moving toward goal rating is canceled by rating for moving away from own units
|
|
if (not enemy_units[1]) then
|
|
enemy_weight = 0
|
|
my_unit_weight = 0
|
|
distance_weight = 3
|
|
terrain_weight = 0
|
|
end
|
|
|
|
local max_rating, best_hex = - math.huge
|
|
for ind,_ in pairs(reach_map.values) do
|
|
local rating =
|
|
(attack_map.values[ind] or 0) * my_unit_weight
|
|
- (enemy_attack_map.values[ind] or 0) * enemy_weight
|
|
|
|
rating = rating - goal_distance_map.values[ind] * distance_weight
|
|
|
|
rating = rating + terrain_defense_map.values[ind] * terrain_weight
|
|
|
|
rating = rating + (enemy_attack_map.values[ind] or 0) / 10.
|
|
|
|
if (rating > max_rating) then
|
|
max_rating, best_hex = rating, ind
|
|
end
|
|
end
|
|
|
|
AH.movefull_stopunit(ai, unit, AH.get_LS_xy(best_hex))
|
|
end
|
|
|
|
return ca_protect_unit_move
|