Micro AIs: avoid using table.remove
It’s slow. The inverse logic using table.insert is much faster, especially for large tables. Only kept table.remove in a couple places where it doesn’t matter.
This commit is contained in:
parent
a131572e09
commit
161470c149
8 changed files with 70 additions and 77 deletions
|
@ -46,13 +46,18 @@ function ca_forest_animals_move:execution(ai, cfg)
|
|||
|
||||
-- Get the locations of all the rabbit holes
|
||||
W.store_items { variable = 'holes_wml' }
|
||||
local holes = H.get_variable_array('holes_wml')
|
||||
local all_items = H.get_variable_array('holes_wml')
|
||||
W.clear_variable { name = 'holes_wml' }
|
||||
|
||||
-- If cfg.rabbit_hole_img is set, only items with that image or halo count as holes
|
||||
if cfg.rabbit_hole_img then
|
||||
if (holes[i].image ~= cfg.rabbit_hole_img) and (holes[i].halo ~= cfg.rabbit_hole_img) then
|
||||
table.remove(holes, i)
|
||||
local holes = {}
|
||||
for _, item in ipairs(all_items) do
|
||||
if cfg.rabbit_hole_img then
|
||||
if (item.image == cfg.rabbit_hole_img) or (item.halo == cfg.rabbit_hole_img) then
|
||||
table.insert(holes, item)
|
||||
end
|
||||
else
|
||||
table.insert(holes, item)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,33 +19,30 @@ function ca_forest_animals_new_rabbit:execution(ai, cfg)
|
|||
|
||||
-- Get the locations of all items on that map (which could be rabbit holes)
|
||||
W.store_items { variable = 'holes_wml' }
|
||||
local holes = H.get_variable_array('holes_wml')
|
||||
local all_items = H.get_variable_array('holes_wml')
|
||||
W.clear_variable { name = 'holes_wml' }
|
||||
|
||||
-- Eliminate all holes that have an enemy within 'rabbit_enemy_distance' hexes
|
||||
-- We also add a random number to the ones we keep, for selection of the holes later
|
||||
--print('before:', #holes)
|
||||
for i = #holes,1,-1 do
|
||||
local holes = {}
|
||||
for _, item in ipairs(all_items) do
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_location", { x = holes[i].x, y = holes[i].y, radius = rabbit_enemy_distance } }
|
||||
{ "filter_location", { x = item.x, y = item.y, radius = rabbit_enemy_distance } }
|
||||
}
|
||||
if enemies[1] then
|
||||
table.remove(holes, i)
|
||||
else
|
||||
if (not enemies[1]) then
|
||||
-- If cfg.rabbit_hole_img is set, only items with that image or halo count as holes
|
||||
if cfg.rabbit_hole_img then
|
||||
if (holes[i].image ~= cfg.rabbit_hole_img) and (holes[i].halo ~= cfg.rabbit_hole_img) then
|
||||
table.remove(holes, i)
|
||||
else
|
||||
holes[i].random = math.random(100)
|
||||
if (item.image == cfg.rabbit_hole_img) or (item.halo == cfg.rabbit_hole_img) then
|
||||
item.random = math.random(100)
|
||||
table.insert(holes, item)
|
||||
end
|
||||
else
|
||||
holes[i].random = math.random(100)
|
||||
item.random = math.random(100)
|
||||
table.insert(holes, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
--print('after:', #holes)
|
||||
table.sort(holes, function(a, b) return a.random > b.random end)
|
||||
|
||||
local rabbits = wesnoth.get_units { side = wesnoth.current.side, type = cfg.rabbit_type }
|
||||
|
|
|
@ -29,16 +29,16 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
-- For convenience, we check for locations here, and just pass that to the exec function
|
||||
-- This is mostly to make the unique_goals option easier
|
||||
local width, height = wesnoth.get_map_size()
|
||||
local locs = wesnoth.get_locations {
|
||||
local all_locs = wesnoth.get_locations {
|
||||
x = '1-' .. width,
|
||||
y = '1-' .. height,
|
||||
{ "and", cfg.filter_location }
|
||||
}
|
||||
--print('#locs org', #locs)
|
||||
if (#locs == 0) then return 0 end
|
||||
if (#all_locs == 0) then return 0 end
|
||||
|
||||
-- If 'unique_goals' is set, check whether there are locations left to go to.
|
||||
-- This does not have to be a persistent variable
|
||||
local locs = {}
|
||||
if cfg.unique_goals then
|
||||
-- First, some cleanup of previous turn data
|
||||
local str = 'goals_taken_' .. (wesnoth.current.turn - 1)
|
||||
|
@ -46,27 +46,31 @@ function ca_goto:evaluation(ai, cfg, self)
|
|||
|
||||
-- Now on to the current turn
|
||||
local str = 'goals_taken_' .. wesnoth.current.turn
|
||||
for i = #locs,1,-1 do
|
||||
if self.data[str] and self.data[str]:get(locs[i][1], locs[i][2]) then
|
||||
table.remove(locs, i)
|
||||
for _, loc in ipairs(all_locs) do
|
||||
if (not self.data[str]) or (not self.data[str]:get(loc[1], loc[2])) then
|
||||
table.insert(locs, loc)
|
||||
end
|
||||
end
|
||||
else
|
||||
locs = all_locs
|
||||
end
|
||||
--print('#locs mod', #locs)
|
||||
if (not locs[1]) then return 0 end
|
||||
|
||||
-- Find the goto units
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side,
|
||||
local all_units = wesnoth.get_units { side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }, formula = '$this_unit.moves > 0'
|
||||
}
|
||||
|
||||
-- Exclude released units
|
||||
local units = {}
|
||||
if cfg.release_unit_at_goal then
|
||||
for i_unit=#units,1,-1 do
|
||||
if MAIUV.get_mai_unit_variables(units[i_unit], cfg.ai_id, "release") then
|
||||
table.remove(units, i_unit)
|
||||
for _, unit in ipairs(all_units) do
|
||||
if (not MAIUV.get_mai_unit_variables(unit, cfg.ai_id, "release")) then
|
||||
table.insert(units, unit)
|
||||
end
|
||||
end
|
||||
else
|
||||
units = all_units
|
||||
end
|
||||
if (not units[1]) then return 0 end
|
||||
|
||||
|
|
|
@ -16,26 +16,26 @@ local function messenger_find_enemies_in_way(unit, goal_x, goal_y)
|
|||
-- If unit cannot get there:
|
||||
if cost >= 42424242 then return end
|
||||
|
||||
-- Exclude the hex the unit is currently on
|
||||
table.remove(path, 1)
|
||||
if (not path[1]) then return end
|
||||
-- The second path hex is the first that is important for the following analysis
|
||||
if (not path[2]) then return end
|
||||
|
||||
-- Is there an enemy unit on the first path hex itself?
|
||||
-- Is there an enemy unit on the second path hex?
|
||||
-- This would be caught by the adjacent hex check later, but not in the right order
|
||||
local enemy = wesnoth.get_units { x = path[1][1], y = path[1][2],
|
||||
local enemy = wesnoth.get_units { x = path[2][1], y = path[2][2],
|
||||
{ "filter_side", { {"enemy_of", {side = wesnoth.current.side} } } }
|
||||
}[1]
|
||||
if enemy then
|
||||
--print(' enemy on first path hex:',enemy.id)
|
||||
--print(' enemy on second path hex:',enemy.id)
|
||||
return enemy
|
||||
end
|
||||
|
||||
-- After that, go through adjacent hexes of all the other path hexes
|
||||
for i, p in ipairs(path) do
|
||||
local sub_path, sub_cost = wesnoth.find_path( unit, p[1], p[2], { ignore_units = true })
|
||||
for i = 2, #path do
|
||||
local path_hex = path[i]
|
||||
local sub_path, sub_cost = wesnoth.find_path( unit, path_hex[1], path_hex[2], { ignore_units = true })
|
||||
if sub_cost <= unit.moves then
|
||||
-- Check for enemy units on one of the adjacent hexes (which includes 2 hexes on path)
|
||||
for x, y in H.adjacent_tiles(p[1], p[2]) do
|
||||
for x, y in H.adjacent_tiles(path_hex[1], path_hex[2]) do
|
||||
local enemy = wesnoth.get_units { x = x, y = y,
|
||||
{ "filter_side", { {"enemy_of", {side = wesnoth.current.side} } } }
|
||||
}[1]
|
||||
|
@ -69,26 +69,25 @@ 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 my_units = wesnoth.get_units {
|
||||
local all_units = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
formula = '$this_unit.attacks_left > 0',
|
||||
{ "not", filter },
|
||||
{ "and", cfg.filter_second }
|
||||
}
|
||||
|
||||
-- Eliminate units without attacks
|
||||
for i = #my_units,1,-1 do
|
||||
if (not H.get_child(my_units[i].__cfg, 'attack')) then
|
||||
table.remove(my_units, i)
|
||||
-- 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
|
||||
--print('#my_units', #my_units)
|
||||
|
||||
if (not my_units[1]) then return end
|
||||
if (not units[1]) then return end
|
||||
|
||||
local my_attacks = AH.get_attacks(my_units, { simulate_combat = true })
|
||||
local attacks = AH.get_attacks(units, { simulate_combat = true })
|
||||
|
||||
for i, att in ipairs(my_attacks) do
|
||||
for i, att in ipairs(attacks) do
|
||||
if (att.target.x == enemy_in_way.x) and (att.target.y == enemy_in_way.y) then
|
||||
|
||||
-- Rating: expected HP of attacker and defender
|
||||
|
@ -108,7 +107,7 @@ local function messenger_find_clearing_attack(unit, goal_x, goal_y, cfg)
|
|||
-- If we got here, that means there's an enemy in the way, but none of the units can reach it
|
||||
--> try to fight our way to that enemy
|
||||
--print('Find different attack to get to enemy in way')
|
||||
for i, att in ipairs(my_attacks) do
|
||||
for i, att in ipairs(attacks) do
|
||||
|
||||
-- Rating: expected HP of attacker and defender
|
||||
local rating = att.att_stats.average_hp - 2 * att.def_stats.average_hp
|
||||
|
|
|
@ -6,21 +6,14 @@ local LS = wesnoth.require "lua/location_set.lua"
|
|||
local ca_simple_attack = {}
|
||||
|
||||
function ca_simple_attack:evaluation(ai, cfg, self)
|
||||
|
||||
-- Find all units that can attack and match the SUF
|
||||
local units = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
formula = '$this_unit.attacks_left > 0',
|
||||
{ "and", cfg.filter }
|
||||
}
|
||||
|
||||
-- Eliminate units without attacks
|
||||
for i = #units,1,-1 do
|
||||
if (not H.get_child(units[i].__cfg, 'attack')) then
|
||||
table.remove(units, i)
|
||||
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
|
||||
--print('#units', #units)
|
||||
if (not units[1]) then return 0 end
|
||||
|
||||
-- Get all possible attacks
|
||||
|
|
|
@ -32,10 +32,8 @@ function swarm_move:execution(ai, cfg)
|
|||
}
|
||||
--print('#units, #units_no_moves, #enemies', #units, #units_no_moves, #enemies)
|
||||
|
||||
-- pick a random unit and remove it from 'units'
|
||||
local rand = math.random(#units)
|
||||
local unit = units[rand]
|
||||
table.remove(units, rand)
|
||||
-- pick one unit at random
|
||||
local unit = units[math.random(#units)]
|
||||
|
||||
-- Find best place for that unit to move to
|
||||
local best_hex = AH.find_best_move(unit, function(x, y)
|
||||
|
|
|
@ -43,20 +43,18 @@ function ca_wolves_multipacks_attack:execution(ai, cfg)
|
|||
|
||||
-- ... and check if any targets are in reach
|
||||
local attacks = {}
|
||||
if wolves[1] then attacks = AH.get_attacks(wolves, { simulate_combat = true }) end
|
||||
--print('pack, wolves, attacks:', pack_number, #wolves, #attacks)
|
||||
if wolves[1] then all_attacks = AH.get_attacks(wolves, { simulate_combat = true }) end
|
||||
--print('pack, wolves, attacks:', pack_number, #wolves, #all_attacks)
|
||||
|
||||
-- Eliminate targets that would split up the wolves by more than 3 hexes
|
||||
-- This also takes care of wolves joining as a pack rather than attacking individually
|
||||
for i=#attacks,1,-1 do
|
||||
--print(i, attacks[i].x, attacks[i].y)
|
||||
local attacks = {}
|
||||
for _, attack in ipairs(all_attacks) do
|
||||
for j,w in ipairs(wolves) do
|
||||
local nh = AH.next_hop(w, attacks[i].dst.x, attacks[i].dst.y)
|
||||
local d = H.distance_between(nh[1], nh[2], attacks[i].dst.x, attacks[i].dst.y)
|
||||
--print(' ', i, w.x, w.y, d)
|
||||
if d > 3 then
|
||||
table.remove(attacks, i)
|
||||
--print('Removing attack')
|
||||
local nh = AH.next_hop(w, attack.dst.x, attack.dst.y)
|
||||
local d = H.distance_between(nh[1], nh[2], attack.dst.x, attack.dst.y)
|
||||
if d <= 3 then
|
||||
table.insert(attacks, attack)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
|
|
@ -121,8 +121,7 @@ function wolves_multipacks_functions.assign_packs(cfg)
|
|||
end
|
||||
-- Now insert the best pack into that 'packs' array
|
||||
packs[new_pack] = {}
|
||||
-- Need to count down for table.remove to work correctly
|
||||
for i = pack_size,1,-1 do
|
||||
for i = 1,pack_size do
|
||||
table.insert(packs[new_pack], { x = best_wolves[i].x, y = best_wolves[i].y, id = best_wolves[i].id })
|
||||
MAIUV.set_mai_unit_variables(best_wolves[i], cfg.ai_id, { pack = new_pack })
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue