Micro AIs: avoid using formula= in SUFs

It’s slow for finding units with moves or attacks left.  The
alternative method of getting all units and then looping over the table
with a condition is much faster.
This commit is contained in:
mattsc 2014-04-10 10:06:25 -07:00
parent 161470c149
commit c4cb6168e4
34 changed files with 130 additions and 122 deletions

View file

@ -741,6 +741,34 @@ function ai_helper.get_live_units(filter)
return wesnoth.get_units(live_filter)
end
function ai_helper.get_units_with_moves(filter)
-- Using formula = '$this_unit.moves > 0' is slow, this method is much faster
local all_units = wesnoth.get_units(filter)
local units = {}
for _, unit in ipairs(all_units) do
if (unit.moves > 0) then table.insert(units, unit) end
end
return units
end
function ai_helper.get_units_with_attacks(filter)
-- Using formula = '$this_unit.attacks_left > 0' is slow, this method is much faster
-- Also need to check that units actually have attacks (as attacks_left > 0 with no attacks is possible)
-- The latter has to go through unit.__cfg which is slow, but there is no way around that, as far as I know
local all_units = wesnoth.get_units(filter)
local units = {}
for _, unit in ipairs(all_units) do
if (unit.attacks_left > 0) and (H.get_schild(unit.__cfg, 'attack')) then
table.insert(units, unit)
end
end
return units
end
function ai_helper.get_closest_enemy(loc)
-- Get the closest enemy to loc, or the leader if loc not specified
local x, y

View file

@ -4,10 +4,9 @@ local LS = wesnoth.require "lua/location_set.lua"
local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
local function get_big_animals(cfg)
local big_animals = wesnoth.get_units {
local big_animals = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and" , cfg.filter },
formula = '$this_unit.moves > 0'
{ "and" , cfg.filter }
}
return big_animals
end

View file

@ -6,8 +6,8 @@ local ca_bottleneck_attack = {}
function ca_bottleneck_attack:evaluation(ai, cfg, self)
-- All units with attacks_left and enemies next to them
-- This will be much easier once the 'attacks' variable is implemented
local attackers = wesnoth.get_units {
side = wesnoth.current.side, formula = "$this_unit.attacks_left > 0",
local attackers = AH.get_units_with_attacks {
side = wesnoth.current.side,
{ "filter_adjacent", {
{ "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } }
} }
@ -70,7 +70,7 @@ end
function ca_bottleneck_attack:execution(ai, cfg, self)
if self.data.bottleneck_attacks_done then
local units = wesnoth.get_units { side = wesnoth.current.side, formula = '$this_unit.attacks_left > 0' }
local units = AH.get_units_with_attacks { side = wesnoth.current.side }
for i,u in ipairs(units) do
AH.checked_stopunit_attacks(ai, u)
end

View file

@ -235,13 +235,9 @@ function ca_bottleneck_move:evaluation(ai, cfg, self)
-- Now find all units, including the leader or not, depending on situation and settings
local units = {}
if MAISD.get_mai_self_data(self.data, cfg.ai_id, "side_leader_activated") then
units = wesnoth.get_units { side = wesnoth.current.side,
formula = '$this_unit.moves > 0'
}
units = AH.get_units_with_moves { side = wesnoth.current.side }
else
units = wesnoth.get_units { side = wesnoth.current.side, canrecruit = 'no',
formula = '$this_unit.moves > 0'
}
units = AH.get_units_with_moves { side = wesnoth.current.side, canrecruit = 'no' }
end
-- If there's no units with moves left, nothing to be done here
@ -487,13 +483,9 @@ function ca_bottleneck_move:execution(ai, cfg, self)
if self.data.bottleneck_moves_done then
local units = {}
if MAISD.get_mai_self_data(self.data, cfg.ai_id, "side_leader_activated") then
units = wesnoth.get_units { side = wesnoth.current.side,
formula = '$this_unit.moves > 0'
}
units = AH.get_units_with_moves { side = wesnoth.current.side }
else
units = wesnoth.get_units { side = wesnoth.current.side, canrecruit = 'no',
formula = '$this_unit.moves > 0'
}
units = AH.get_units_with_moves { side = wesnoth.current.side, canrecruit = 'no' }
end
for i,u in ipairs(units) do
AH.checked_stopunit_moves(ai, u)

View file

@ -3,11 +3,10 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_coward(cfg)
local filter = cfg.filter or { id = cfg.id }
local coward = wesnoth.get_units({
local coward = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", filter },
formula = '$this_unit.moves > 0' }
)[1]
{ "and", filter }
}[1]
return coward
end

View file

@ -7,10 +7,9 @@ local function get_forest_animals(cfg)
-- We want the deer/rabbits to move first, tuskers later
local deer_type = cfg.deer_type or "no_unit_of_this_type"
local rabbit_type = cfg.rabbit_type or "no_unit_of_this_type"
local forest_animals = wesnoth.get_units {
local forest_animals = AH.get_units_with_moves {
side = wesnoth.current.side,
type = deer_type .. ',' .. rabbit_type,
formula = '$this_unit.moves > 0'
type = deer_type .. ',' .. rabbit_type
}
local tusker_type = cfg.tusker_type or "no_unit_of_this_type"

View file

@ -2,10 +2,9 @@ local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_tuskers(cfg)
local tuskers = wesnoth.get_units {
local tuskers = AH.get_units_with_moves {
side = wesnoth.current.side,
type = cfg.tusker_type,
formula = '$this_unit.moves > 0'
type = cfg.tusker_type
}
return tuskers
end

View file

@ -2,10 +2,9 @@ local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_tusklets(cfg)
local tusklets = wesnoth.get_units {
local tusklets = AH.get_units_with_moves {
side = wesnoth.current.side,
type = cfg.tusklet_type,
formula = '$this_unit.moves > 0'
type = cfg.tusklet_type
}
return tusklets
end

View file

@ -57,8 +57,9 @@ function ca_goto:evaluation(ai, cfg, self)
if (not locs[1]) then return 0 end
-- Find the goto units
local all_units = wesnoth.get_units { side = wesnoth.current.side,
{ "and", cfg.filter }, formula = '$this_unit.moves > 0'
local all_units = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter }
}
-- Exclude released units

View file

@ -5,10 +5,9 @@ local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
local MAISD = wesnoth.require "ai/micro_ais/micro_ai_self_data.lua"
local function get_hang_out_units(cfg)
local units = wesnoth.get_units {
local units = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter },
formula = '$this_unit.moves > 0'
{ "and", cfg.filter }
}
return units
end

View file

@ -17,17 +17,26 @@ function ca_healer_move:evaluation(ai, cfg, self)
cfg = cfg or {}
local healers = wesnoth.get_units { side = wesnoth.current.side, ability = "healing",
formula = '$this_unit.moves > 0', { "and", cfg.filter }
local all_healers = wesnoth.get_units {
side = wesnoth.current.side,
ability = "healing",
{ "and", cfg.filter }
}
local healers, healers_noMP = {}, {}
for _, healer in ipairs(all_healers) do
if (healer.moves > 0) then
table.insert(healers, healer)
else
table.insert(healers_noMP, healer)
end
end
if (not healers[1]) then return 0 end
local healers_noMP = wesnoth.get_units { side = wesnoth.current.side, ability = "healing",
formula = '$this_unit.moves = 0', { "and", cfg.filter }
}
local all_units = wesnoth.get_units{ side = wesnoth.current.side,
{"and", cfg.filter_second}
local all_units = wesnoth.get_units {
side = wesnoth.current.side,
{ "and", cfg.filter_second }
}
local healees, units_MP = {}, {}

View file

@ -10,10 +10,9 @@ local function get_sheep(cfg)
end
local function get_dogs(cfg)
local dogs = wesnoth.get_units {
local dogs = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter },
formula = '$this_unit.moves > 0'
{ "and", cfg.filter }
}
return dogs
end

View file

@ -3,11 +3,10 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
local LS = wesnoth.require "lua/location_set.lua"
local function get_dog(cfg)
local dogs = wesnoth.get_units {
local dogs = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter },
formula = '$this_unit.moves > 0',
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, {"and", cfg.filter_second} } } } }
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, { "and", cfg.filter_second } } } } }
}
return dogs[1]
end

View file

@ -4,10 +4,9 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
local herding_area = wesnoth.require "ai/micro_ais/cas/ca_herding_f_herding_area.lua"
local function get_dogs(cfg)
local dogs = wesnoth.get_units {
local dogs = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter },
formula = '$this_unit.moves > 0'
{ "and", cfg.filter }
}
return dogs
end

View file

@ -5,10 +5,9 @@ local LS = wesnoth.require "lua/location_set.lua"
local herding_area = wesnoth.require "ai/micro_ais/cas/ca_herding_f_herding_area.lua"
local function get_next_sheep(cfg)
local sheep = wesnoth.get_units {
local sheep = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter_second },
formula = '$this_unit.moves > 0'
{ "and", cfg.filter_second }
}
return sheep[1]
end
@ -47,7 +46,10 @@ function ca_herding_sheep_move:execution(ai, cfg)
-- If this move remains within herding area or dogs have no moves left, or sheep doesn't move
-- make it a full move, otherwise partial move
local herding_area = herding_area(cfg)
local dogs = wesnoth.get_units { side = wesnoth.current.side, {"and", cfg.filter}, formula = '$this_unit.moves > 0' }
local dogs = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter }
}
if herding_area:get(x, y) or (not dogs[1]) or ((x == sheep.x) and (y == sheep.y)) then
AH.movefull_stopunit(ai, sheep, x, y)
else

View file

@ -2,9 +2,10 @@ local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_next_sheep(cfg)
local sheep = wesnoth.get_units { side = wesnoth.current.side, {"and", cfg.filter_second},
formula = '$this_unit.moves > 0',
{ "filter_adjacent", { side = wesnoth.current.side, {"and", cfg.filter} } }
local sheep = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter_second },
{ "filter_adjacent", { side = wesnoth.current.side, { "and", cfg.filter } } }
}
return sheep[1]
end

View file

@ -2,11 +2,12 @@ local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_next_sheep(cfg)
local sheep = wesnoth.get_units { side = wesnoth.current.side, {"and", cfg.filter_second},
formula = '$this_unit.moves > 0',
local sheep = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter_second },
{ "filter_location",
{
{ "filter", { { "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} } }
{ "filter", { { "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } } }
},
radius = (cfg.attention_distance or 8)
}

View file

@ -39,11 +39,10 @@ end
local function get_hunter(cfg)
local filter = cfg.filter or { id = cfg.id }
local hunter = wesnoth.get_units({
local hunter = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", filter },
formula = '$this_unit.moves > 0' }
)[1]
{ "and", filter }
}[1]
return hunter
end

View file

@ -2,8 +2,9 @@ local LS = wesnoth.require "lua/location_set.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_lurker(cfg)
local lurker = wesnoth.get_units { side = wesnoth.current.side,
{ "and", cfg.filter }, formula = '$this_unit.moves > 0'
local lurker = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter }
}[1]
return lurker
end

View file

@ -69,20 +69,11 @@ local function messenger_find_clearing_attack(unit, goal_x, goal_y, cfg)
-- Find all units that can attack this enemy
local filter = cfg.filter or { id = cfg.id }
local all_units = wesnoth.get_units {
local units = AH.get_units_with_attacks {
side = wesnoth.current.side,
{ "not", filter },
{ "and", cfg.filter_second }
}
-- Only keep units that have attacks and have attacks left
local units = {}
for _, unit in ipairs(all_units) do
if (unit.attacks_left > 0) and (H.get_child(unit.__cfg, 'attack')) then
table.insert(units, unit)
end
end
if (not units[1]) then return end
local attacks = AH.get_attacks(units, { simulate_combat = true })

View file

@ -6,9 +6,8 @@ local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
local messenger_next_waypoint = wesnoth.require "ai/micro_ais/cas/ca_messenger_f_next_waypoint.lua"
local function get_escorts(cfg)
local escorts = wesnoth.get_units {
local escorts = AH.get_units_with_moves {
side = wesnoth.current.side,
formula = '$this_unit.moves > 0',
{ "and", cfg.filter_second }
}
return escorts
@ -33,7 +32,7 @@ function ca_messenger_escort_move:execution(ai, cfg)
local _, _, _, messengers = messenger_next_waypoint(cfg)
local enemies = wesnoth.get_units {
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } }
}
local base_rating_map = LS.create()

View file

@ -4,11 +4,10 @@ local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"
local function get_patrol(cfg)
local filter = cfg.filter or { id = cfg.id }
local patrol = wesnoth.get_units({
local patrol = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", filter },
formula = '$this_unit.moves > 0' }
)[1]
{ "and", filter }
}[1]
return patrol
end

View file

@ -11,7 +11,7 @@ function ca_protect_unit_attack:evaluation(ai, cfg, self)
local units = {}
for i,id in ipairs(cfg.id) do
table.insert(units, wesnoth.get_units{ id = id, formula = '$this_unit.attacks_left > 0' }[1])
table.insert(units, AH.get_units_with_attacks { id = id }[1])
end
if (not units[1]) then return 0 end

View file

@ -5,7 +5,7 @@ local ca_protect_unit_finish = {}
function ca_protect_unit_finish:evaluation(ai, cfg, self)
-- If a unit can make it to the goal, this is the first thing that happens
for i,id in ipairs(cfg.id) do
local unit = wesnoth.get_units{ id = id, formula = '$this_unit.moves > 0' }[1]
local unit = AH.get_units_with_moves { id = id }[1]
if unit then
local path, cost = wesnoth.find_path(unit, cfg.goal_x[i], cfg.goal_y[i])
if (cost <= unit.moves) and ((unit.x ~= cfg.goal_x[i]) or (unit.y ~= cfg.goal_y[i])) then

View file

@ -6,7 +6,7 @@ local BC = wesnoth.require "ai/lua/battle_calcs.lua"
local function get_protected_units(cfg)
local units = {}
for i,id in ipairs(cfg.id) do
table.insert(units, wesnoth.get_units { id = id, formula = '$this_unit.moves > 0' }[1])
table.insert(units, AH.get_units_with_moves { id = id }[1])
end
return units
end

View file

@ -2,11 +2,10 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_guardian(cfg)
local filter = cfg.filter or { id = cfg.id }
local guardian = wesnoth.get_units({
local guardian = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", filter },
formula = '$this_unit.moves > 0' }
)[1]
{ "and", filter }
}[1]
return guardian
end

View file

@ -6,14 +6,10 @@ local LS = wesnoth.require "lua/location_set.lua"
local ca_simple_attack = {}
function ca_simple_attack:evaluation(ai, cfg, self)
local all_units = wesnoth.get_units { side = wesnoth.current.side, { "and", cfg.filter } }
-- Only keep units that have attacks and have attacks left
local units = {}
for _, unit in ipairs(all_units) do
if (unit.attacks_left > 0) and (H.get_child(unit.__cfg, 'attack')) then
table.insert(units, unit)
end
end
local units = AH.get_units_with_attacks {
side = wesnoth.current.side,
{ "and", cfg.filter }
}
if (not units[1]) then return 0 end
-- Get all possible attacks

View file

@ -3,11 +3,10 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
local function get_guardian(cfg)
local filter = cfg.filter or { id = cfg.id }
local guardian = wesnoth.get_units({
local guardian = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", filter },
formula = '$this_unit.moves > 0' }
)[1]
{ "and", filter }
}[1]
return guardian
end

View file

@ -13,12 +13,7 @@ local function get_enemies(cfg)
end
local function get_swarm_units(cfg)
local units = {}
local all_units = wesnoth.get_units { side = wesnoth.current.side }
for i,u in ipairs(all_units) do
if (u.moves > 0) then table.insert(units, u) end
end
return units
return AH.get_units_with_moves { side = wesnoth.current.side }
end
local swarm_scatter = {}

View file

@ -4,10 +4,9 @@ local BC = wesnoth.require "ai/lua/battle_calcs.lua"
local LS = wesnoth.require "lua/location_set.lua"
local function get_wolves(cfg)
local wolves = wesnoth.get_units {
local wolves = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter },
formula = '$this_unit.moves > 0'
{ "and", cfg.filter }
}
return wolves
end

View file

@ -12,7 +12,10 @@ function ca_wolves_multipacks_attack:evaluation(ai, cfg)
-- If wolves have attacks left, call this CA
-- It will generally be disabled by being black-listed, so as to avoid
-- having to do the full attack evaluation for every single move
local wolves = wesnoth.get_units { side = wesnoth.current.side, type = unit_type, formula = '$this_unit.attacks_left > 0' }
local wolves = AH.get_units_with_attacks {
side = wesnoth.current.side,
type = unit_type
}
if wolves[1] then return cfg.ca_score end
return 0

View file

@ -11,7 +11,10 @@ function ca_wolves_multipacks_wander:evaluation(ai, cfg)
local unit_type = cfg.type or "Wolf"
-- When there's nothing to attack, the wolves wander and regroup into their packs
local wolves = wesnoth.get_units { side = wesnoth.current.side, type = unit_type, formula = '$this_unit.moves > 0' }
local wolves = AH.get_units_with_moves {
side = wesnoth.current.side,
type = unit_type
}
if wolves[1] then return cfg.ca_score end
return 0

View file

@ -4,8 +4,9 @@ local BC = wesnoth.require "ai/lua/battle_calcs.lua"
local LS = wesnoth.require "lua/location_set.lua"
local function get_wolves(cfg)
local wolves = wesnoth.get_units { side = wesnoth.current.side,
formula = '$this_unit.moves > 0', { "and", cfg.filter }
local wolves = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", cfg.filter }
}
return wolves
end

View file

@ -4,11 +4,10 @@ local LS = wesnoth.require "lua/location_set.lua"
local function get_guardian(cfg)
local filter = cfg.filter or { id = cfg.id }
local guardian = wesnoth.get_units({
local guardian = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", filter },
formula = '$this_unit.moves > 0' }
)[1]
{ "and", filter }
}[1]
return guardian
end