Lua AIs: do not use engine's 'data' variable unless necessary
Now that all the AIs use external CAs, there is no need to use the persistent 'data' variable any more, unless information is to be exchanged between different CAs or is supposed to be persistent across save/load cycles.
(cherry-picked from commit 3bfd59f28b
)
This commit is contained in:
parent
740bd456ab
commit
ed406495d7
12 changed files with 148 additions and 117 deletions
|
@ -3,6 +3,9 @@
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local M = wesnoth.map
|
||||
|
||||
local CS_leader_score
|
||||
-- Note that leader_target is also needed by the recruiting CA, so it must be stored in 'data'
|
||||
|
||||
local function get_reachable_enemy_leaders(unit)
|
||||
-- We're cheating a little here and also find hidden enemy leaders. That's
|
||||
-- because a human player could make a pretty good educated guess as to where
|
||||
|
@ -51,7 +54,7 @@ function ca_castle_switch:evaluation(cfg, data)
|
|||
local next_hop = AH.next_hop(leader, data.leader_target[1], data.leader_target[2])
|
||||
if next_hop and next_hop[1] == data.leader_target[1]
|
||||
and next_hop[2] == data.leader_target[2] then
|
||||
return data.leader_score
|
||||
return CS_leader_score
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -158,7 +161,7 @@ function ca_castle_switch:evaluation(cfg, data)
|
|||
data.leader_target = next_hop
|
||||
|
||||
-- if we're on a keep, wait until there are no movable units on the castle before moving off
|
||||
data.leader_score = 290000
|
||||
CS_leader_score = 290000
|
||||
if wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep then
|
||||
local castle = wesnoth.get_locations {
|
||||
x = "1-"..width, y = "1-"..height,
|
||||
|
@ -178,12 +181,12 @@ function ca_castle_switch:evaluation(cfg, data)
|
|||
end
|
||||
end
|
||||
if should_wait then
|
||||
data.leader_score = 15000
|
||||
CS_leader_score = 15000
|
||||
end
|
||||
end
|
||||
|
||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||
return data.leader_score
|
||||
return CS_leader_score
|
||||
end
|
||||
|
||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||
|
|
|
@ -4,6 +4,8 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
||||
local M = wesnoth.map
|
||||
|
||||
local GV_unit, GV_village
|
||||
|
||||
local ca_grab_villages = {}
|
||||
|
||||
function ca_grab_villages:evaluation(cfg, data)
|
||||
|
@ -116,7 +118,7 @@ function ca_grab_villages:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
if best_village then
|
||||
data.unit, data.village = best_unit, best_village
|
||||
GV_unit, GV_village = best_unit, best_village
|
||||
if (max_rating >= 1000) then
|
||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||
return return_value
|
||||
|
@ -131,10 +133,10 @@ end
|
|||
|
||||
function ca_grab_villages:execution(cfg, data)
|
||||
if AH.print_exec() then AH.print_ts(' Executing grab_villages CA') end
|
||||
if AH.show_messages() then wesnoth.wml_actions.message { speaker = data.unit.id, message = 'Grab villages' } end
|
||||
if AH.show_messages() then wesnoth.wml_actions.message { speaker = GV_unit.id, message = 'Grab villages' } end
|
||||
|
||||
AH.movefull_stopunit(ai, data.unit, data.village)
|
||||
data.unit, data.village = nil, nil
|
||||
AH.movefull_stopunit(ai, GV_unit, GV_village)
|
||||
GV_unit, GV_village = nil, nil
|
||||
end
|
||||
|
||||
return ca_grab_villages
|
||||
|
|
|
@ -26,6 +26,8 @@ local M = wesnoth.map
|
|||
-- so that we can follow up with stronger units. In addition, use of poison or
|
||||
-- slow attacks is strongly discouraged. See code for exact equations.
|
||||
|
||||
local XP_attack
|
||||
|
||||
local ca_attack_highxp = {}
|
||||
|
||||
function ca_attack_highxp:evaluation(cfg, data)
|
||||
|
@ -284,15 +286,15 @@ function ca_attack_highxp:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
if best_attack then
|
||||
data.XP_attack = best_attack
|
||||
XP_attack = best_attack
|
||||
end
|
||||
|
||||
return max_ca_score
|
||||
end
|
||||
|
||||
function ca_attack_highxp:execution(cfg, data)
|
||||
AH.robust_move_and_attack(ai, data.XP_attack.src, data.XP_attack.dst, data.XP_attack.target, { weapon = data.XP_attack.attack_num })
|
||||
data.XP_attack = nil
|
||||
AH.robust_move_and_attack(ai, XP_attack.src, XP_attack.dst, XP_attack.target, { weapon = XP_attack.attack_num })
|
||||
XP_attack = nil
|
||||
end
|
||||
|
||||
return ca_attack_highxp
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local MTAE_unit, MTAE_destination
|
||||
|
||||
local ca_move_to_any_enemy = {}
|
||||
|
||||
function ca_move_to_any_enemy:evaluation(cfg, data)
|
||||
|
@ -45,8 +47,8 @@ function ca_move_to_any_enemy:evaluation(cfg, data)
|
|||
return 0
|
||||
end
|
||||
|
||||
data.destination = destination
|
||||
data.unit = unit
|
||||
MTAE_destination = destination
|
||||
MTAE_unit = unit
|
||||
|
||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||
return 1
|
||||
|
@ -54,7 +56,8 @@ end
|
|||
|
||||
function ca_move_to_any_enemy:execution(cfg, data)
|
||||
if AH.print_exec() then AH.print_ts(' Executing move_to_any_enemy CA') end
|
||||
AH.checked_move(ai, data.unit, data.destination[1], data.destination[2])
|
||||
AH.checked_move(ai, MTAE_unit, MTAE_destination[1], MTAE_destination[2])
|
||||
MTAE_unit, MTAE_destination = nil,nil
|
||||
end
|
||||
|
||||
return ca_move_to_any_enemy
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local R = wesnoth.require "ai/lua/retreat.lua"
|
||||
|
||||
local retreat_unit, retreat_loc
|
||||
|
||||
local ca_retreat_injured = {}
|
||||
|
||||
function ca_retreat_injured:evaluation(cfg, data)
|
||||
|
@ -15,8 +17,8 @@ function ca_retreat_injured:evaluation(cfg, data)
|
|||
}
|
||||
local unit, loc = R.retreat_injured_units(units)
|
||||
if unit then
|
||||
data.retreat_unit = unit
|
||||
data.retreat_loc = loc
|
||||
retreat_unit = unit
|
||||
retreat_loc = loc
|
||||
|
||||
-- First check if attacks are possible for any unit
|
||||
-- If one with > 50% chance of kill is possible, set return_value to lower than combat CA
|
||||
|
@ -36,9 +38,9 @@ end
|
|||
|
||||
function ca_retreat_injured:execution(cfg, data)
|
||||
if AH.print_exec() then AH.print_ts(' Executing retreat_injured CA') end
|
||||
AH.robust_move_and_attack(ai, data.retreat_unit, data.retreat_loc)
|
||||
data.retreat_unit = nil
|
||||
data.retreat_loc = nil
|
||||
AH.robust_move_and_attack(ai, retreat_unit, retreat_loc)
|
||||
retreat_unit = nil
|
||||
retreat_loc = nil
|
||||
end
|
||||
|
||||
return ca_retreat_injured
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local SP_attack
|
||||
|
||||
local ca_spread_poison = {}
|
||||
|
||||
function ca_spread_poison:evaluation(cfg, data)
|
||||
|
@ -78,7 +80,7 @@ function ca_spread_poison:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
if best_attack then
|
||||
data.attack = best_attack
|
||||
SP_attack = best_attack
|
||||
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
||||
return 190000
|
||||
end
|
||||
|
@ -87,16 +89,16 @@ function ca_spread_poison:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
function ca_spread_poison:execution(cfg, data)
|
||||
local attacker = wesnoth.get_unit(data.attack.src.x, data.attack.src.y)
|
||||
local attacker = wesnoth.get_unit(SP_attack.src.x, SP_attack.src.y)
|
||||
-- If several attacks have poison, this will always find the last one
|
||||
local is_poisoner, poison_weapon = AH.has_weapon_special(attacker, "poison")
|
||||
|
||||
if AH.print_exec() then AH.print_ts(' Executing spread_poison CA') end
|
||||
if AH.show_messages() then wesnoth.wml_actions.message { speaker = attacker.id, message = 'Poison attack' } end
|
||||
|
||||
AH.robust_move_and_attack(ai, attacker, data.attack.dst, data.attack.target, { weapon = poison_weapon })
|
||||
AH.robust_move_and_attack(ai, attacker, SP_attack.dst, SP_attack.target, { weapon = poison_weapon })
|
||||
|
||||
data.attack = nil
|
||||
SP_attack = nil
|
||||
end
|
||||
|
||||
return ca_spread_poison
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local BD_attacker, BD_target, BD_weapon, BD_bottleneck_attacks_done
|
||||
|
||||
local ca_bottleneck_attack = {}
|
||||
|
||||
function ca_bottleneck_attack:evaluation(cfg, data)
|
||||
|
@ -48,29 +50,29 @@ function ca_bottleneck_attack:evaluation(cfg, data)
|
|||
|
||||
if (not best_attacker) then
|
||||
-- In this case we take attacks away from all units
|
||||
data.BD_bottleneck_attacks_done = true
|
||||
BD_bottleneck_attacks_done = true
|
||||
else
|
||||
data.BD_bottleneck_attacks_done = false
|
||||
data.BD_attacker = best_attacker
|
||||
data.BD_target = best_target
|
||||
data.BD_weapon = best_weapon
|
||||
BD_bottleneck_attacks_done = false
|
||||
BD_attacker = best_attacker
|
||||
BD_target = best_target
|
||||
BD_weapon = best_weapon
|
||||
end
|
||||
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_bottleneck_attack:execution(cfg, data)
|
||||
if data.BD_bottleneck_attacks_done then
|
||||
if BD_bottleneck_attacks_done then
|
||||
local units = AH.get_units_with_attacks { side = wesnoth.current.side }
|
||||
for _,unit in ipairs(units) do
|
||||
AH.checked_stopunit_attacks(ai, unit)
|
||||
end
|
||||
else
|
||||
AH.checked_attack(ai, data.BD_attacker, data.BD_target, data.BD_weapon)
|
||||
AH.checked_attack(ai, BD_attacker, BD_target, BD_weapon)
|
||||
end
|
||||
|
||||
data.BD_attacker, data.BD_target, data.BD_weapon = nil, nil, nil
|
||||
data.BD_bottleneck_attacks_done = nil
|
||||
BD_attacker, BD_target, BD_weapon = nil, nil, nil
|
||||
BD_bottleneck_attacks_done = nil
|
||||
end
|
||||
|
||||
return ca_bottleneck_attack
|
||||
|
|
|
@ -5,6 +5,10 @@ local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
|||
local MAISD = wesnoth.require "ai/micro_ais/micro_ai_self_data.lua"
|
||||
local M = wesnoth.map
|
||||
|
||||
local BD_unit, BD_hex
|
||||
local BD_level_up_defender, BD_level_up_weapon, BD_bottleneck_moves_done
|
||||
local BD_is_my_territory, BD_def_map, BD_healer_map, BD_leadership_map, BD_healing_map
|
||||
|
||||
local function bottleneck_is_my_territory(map, enemy_map)
|
||||
-- Create map that contains 'true' for all hexes that are
|
||||
-- on the AI's side of the map
|
||||
|
@ -85,16 +89,16 @@ end
|
|||
local function bottleneck_create_positioning_map(max_value, data)
|
||||
-- Create the positioning maps for the healers and leaders, if not given by WML keys
|
||||
-- @max_value: the rating value for the first hex in the set
|
||||
-- data.BD_def_map must have been created when this function is called.
|
||||
-- BD_def_map must have been created when this function is called.
|
||||
|
||||
-- Find all locations adjacent to def_map.
|
||||
-- This might include hexes on the line itself.
|
||||
-- Only store those that are not in enemy territory.
|
||||
local map = LS.create()
|
||||
data.BD_def_map:iter(function(x, y, v)
|
||||
BD_def_map:iter(function(x, y, v)
|
||||
for xa,ya in H.adjacent_tiles(x, y) do
|
||||
if data.BD_is_my_territory:get(xa, ya) then
|
||||
local rating = data.BD_def_map:get(x, y) or 0
|
||||
if BD_is_my_territory:get(xa, ya) then
|
||||
local rating = BD_def_map:get(x, y) or 0
|
||||
rating = rating + (map:get(xa, ya) or 0)
|
||||
map:insert(xa, ya, rating)
|
||||
end
|
||||
|
@ -109,7 +113,7 @@ local function bottleneck_create_positioning_map(max_value, data)
|
|||
|
||||
-- We merge the defense map into this, as healers/leaders (by default)
|
||||
-- can take position on the front line
|
||||
map:union_merge(data.BD_def_map,
|
||||
map:union_merge(BD_def_map,
|
||||
function(x, y, v1, v2) return v1 or v2 end
|
||||
)
|
||||
|
||||
|
@ -126,18 +130,18 @@ local function bottleneck_get_rating(unit, x, y, has_leadership, is_healer, data
|
|||
-- Defense positioning rating
|
||||
-- We exclude healers/leaders here, as we don't necessarily want them on the front line
|
||||
if (not is_healer) and (not has_leadership) then
|
||||
rating = data.BD_def_map:get(x, y) or 0
|
||||
rating = BD_def_map:get(x, y) or 0
|
||||
end
|
||||
|
||||
-- Healer positioning rating
|
||||
if is_healer then
|
||||
local healer_rating = data.BD_healer_map:get(x, y) or 0
|
||||
local healer_rating = BD_healer_map:get(x, y) or 0
|
||||
if (healer_rating > rating) then rating = healer_rating end
|
||||
end
|
||||
|
||||
-- Leadership unit positioning rating
|
||||
if has_leadership then
|
||||
local leadership_rating = data.BD_leadership_map:get(x, y) or 0
|
||||
local leadership_rating = BD_leadership_map:get(x, y) or 0
|
||||
|
||||
-- If leadership unit is injured -> prefer hexes next to healers
|
||||
if (unit.hitpoints < unit.max_hitpoints) then
|
||||
|
@ -155,18 +159,18 @@ local function bottleneck_get_rating(unit, x, y, has_leadership, is_healer, data
|
|||
|
||||
-- Injured unit positioning
|
||||
if (unit.hitpoints < unit.max_hitpoints) then
|
||||
local healing_rating = data.BD_healing_map:get(x, y) or 0
|
||||
local healing_rating = BD_healing_map:get(x, y) or 0
|
||||
if (healing_rating > rating) then rating = healing_rating end
|
||||
end
|
||||
|
||||
-- If this did not produce a positive rating, we add a
|
||||
-- distance-based rating, to get units to the bottleneck in the first place
|
||||
if (rating <= 0) and data.BD_is_my_territory:get(x, y) then
|
||||
if (rating <= 0) and BD_is_my_territory:get(x, y) then
|
||||
local combined_dist = 0
|
||||
data.BD_def_map:iter(function(x_def, y_def, v)
|
||||
BD_def_map:iter(function(x_def, y_def, v)
|
||||
combined_dist = combined_dist + M.distance_between(x, y, x_def, y_def)
|
||||
end)
|
||||
combined_dist = combined_dist / data.BD_def_map:size()
|
||||
combined_dist = combined_dist / BD_def_map:size()
|
||||
rating = 1000 - combined_dist * 10.
|
||||
end
|
||||
|
||||
|
@ -194,7 +198,7 @@ local function bottleneck_move_out_of_way(unit_in_way, data)
|
|||
|
||||
local best_reach, best_hex = - math.huge
|
||||
for _,loc in ipairs(reach) do
|
||||
if data.BD_is_my_territory:get(loc[1], loc[2]) and (not occ_hexes:get(loc[1], loc[2])) then
|
||||
if BD_is_my_territory:get(loc[1], loc[2]) and (not occ_hexes:get(loc[1], loc[2])) then
|
||||
-- Criterion: MP left after the move has been done
|
||||
if (loc[3] > best_reach) then
|
||||
best_reach, best_hex = loc[3], { loc[1], loc[2] }
|
||||
|
@ -232,54 +236,54 @@ function ca_bottleneck_move:evaluation(cfg, data)
|
|||
if (not units[1]) then return 0 end
|
||||
|
||||
-- Set up the array that tells the AI where to defend the bottleneck
|
||||
data.BD_def_map = bottleneck_triple_from_keys(cfg.x, cfg.y, 10000)
|
||||
BD_def_map = bottleneck_triple_from_keys(cfg.x, cfg.y, 10000)
|
||||
|
||||
-- Territory map, describing which hex is on AI's side of the bottleneck
|
||||
-- This one is a bit expensive, esp. on large maps -> don't delete every move and reuse
|
||||
-- However, after a reload, data.BD_is_my_territory is an empty string
|
||||
-- -> need to recalculate in that case also (the reason is that is_my_territory is not a WML table)
|
||||
if (not data.BD_is_my_territory) or (type(data.BD_is_my_territory) == 'string') then
|
||||
-- However, after a reload, BD_is_my_territory is empty
|
||||
-- -> need to recalculate in that case also
|
||||
if (not BD_is_my_territory) or (type(BD_is_my_territory) == 'string') then
|
||||
local enemy_map = bottleneck_triple_from_keys(cfg.enemy_x, cfg.enemy_y, 10000)
|
||||
data.BD_is_my_territory = bottleneck_is_my_territory(data.BD_def_map, enemy_map)
|
||||
BD_is_my_territory = bottleneck_is_my_territory(BD_def_map, enemy_map)
|
||||
end
|
||||
|
||||
-- Healer positioning map
|
||||
if cfg.healer_x and cfg.healer_y then
|
||||
data.BD_healer_map = bottleneck_triple_from_keys(cfg.healer_x, cfg.healer_y, 5000)
|
||||
BD_healer_map = bottleneck_triple_from_keys(cfg.healer_x, cfg.healer_y, 5000)
|
||||
else
|
||||
data.BD_healer_map = bottleneck_create_positioning_map(5000, data)
|
||||
BD_healer_map = bottleneck_create_positioning_map(5000, data)
|
||||
end
|
||||
-- Use def_map values for any healer hexes that are defined in def_map as well
|
||||
data.BD_healer_map:inter_merge(data.BD_def_map,
|
||||
BD_healer_map:inter_merge(BD_def_map,
|
||||
function(x, y, v1, v2) return v2 or v1 end
|
||||
)
|
||||
|
||||
-- Leadership position map
|
||||
if cfg.leadership_x and cfg.leadership_y then
|
||||
data.BD_leadership_map = bottleneck_triple_from_keys(cfg.leadership_x, cfg.leadership_y, 4000)
|
||||
BD_leadership_map = bottleneck_triple_from_keys(cfg.leadership_x, cfg.leadership_y, 4000)
|
||||
else
|
||||
data.BD_leadership_map = bottleneck_create_positioning_map(4000, data)
|
||||
BD_leadership_map = bottleneck_create_positioning_map(4000, data)
|
||||
end
|
||||
-- Use def_map values for any leadership hexes that are defined in def_map as well
|
||||
data.BD_leadership_map:inter_merge(data.BD_def_map,
|
||||
BD_leadership_map:inter_merge(BD_def_map,
|
||||
function(x, y, v1, v2) return v2 or v1 end
|
||||
)
|
||||
|
||||
-- Healing map: positions next to healers
|
||||
-- Healers get moved with higher priority, so don't need to check their MP
|
||||
local healers = wesnoth.get_units { side = wesnoth.current.side, ability = "healing" }
|
||||
data.BD_healing_map = LS.create()
|
||||
BD_healing_map = LS.create()
|
||||
for _,healer in ipairs(healers) do
|
||||
for xa,ya in H.adjacent_tiles(healer.x, healer.y) do
|
||||
-- Cannot be on the line, and needs to be in own territory
|
||||
if data.BD_is_my_territory:get(xa, ya) then
|
||||
if BD_is_my_territory:get(xa, ya) then
|
||||
local min_dist = math.huge
|
||||
data.BD_def_map:iter( function(xd, yd, vd)
|
||||
BD_def_map:iter( function(xd, yd, vd)
|
||||
local dist_line = M.distance_between(xa, ya, xd, yd)
|
||||
if (dist_line < min_dist) then min_dist = dist_line end
|
||||
end)
|
||||
if (min_dist > 0) then
|
||||
data.BD_healing_map:insert(xa, ya, 3000 + min_dist) -- Farther away from enemy is good
|
||||
BD_healing_map:insert(xa, ya, 3000 + min_dist) -- Farther away from enemy is good
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -314,7 +318,7 @@ function ca_bottleneck_move:evaluation(cfg, data)
|
|||
local attacks = {}
|
||||
for _,enemy in ipairs(enemies) do
|
||||
for xa,ya in H.adjacent_tiles(enemy.x, enemy.y) do
|
||||
if data.BD_is_my_territory:get(xa, ya) then
|
||||
if BD_is_my_territory:get(xa, ya) then
|
||||
local unit_in_way = wesnoth.get_unit(xa, ya)
|
||||
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way)) then
|
||||
unit_in_way = nil
|
||||
|
@ -417,8 +421,8 @@ function ca_bottleneck_move:evaluation(cfg, data)
|
|||
|
||||
if (level_up_rating > max_rating) then
|
||||
max_rating, best_unit, best_hex = level_up_rating, unit, { loc[1], loc[2] }
|
||||
data.BD_level_up_defender = attack.defender
|
||||
data.BD_level_up_weapon = n_weapon
|
||||
BD_level_up_defender = attack.defender
|
||||
BD_level_up_weapon = n_weapon
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -428,7 +432,7 @@ function ca_bottleneck_move:evaluation(cfg, data)
|
|||
|
||||
-- Set the variables for the exec() function
|
||||
if (not best_hex) then
|
||||
data.BD_bottleneck_moves_done = true
|
||||
BD_bottleneck_moves_done = true
|
||||
else
|
||||
-- If there's another unit in the best location, moving it out of the way becomes the best move
|
||||
local unit_in_way = wesnoth.get_units { x = best_hex[1], y = best_hex[2],
|
||||
|
@ -441,19 +445,19 @@ function ca_bottleneck_move:evaluation(cfg, data)
|
|||
if unit_in_way then
|
||||
best_hex = bottleneck_move_out_of_way(unit_in_way, data)
|
||||
best_unit = unit_in_way
|
||||
data.BD_level_up_defender = nil
|
||||
data.BD_level_up_weapon = nil
|
||||
BD_level_up_defender = nil
|
||||
BD_level_up_weapon = nil
|
||||
end
|
||||
|
||||
data.BD_bottleneck_moves_done = false
|
||||
data.BD_unit, data.BD_hex = best_unit, best_hex
|
||||
BD_bottleneck_moves_done = false
|
||||
BD_unit, BD_hex = best_unit, best_hex
|
||||
end
|
||||
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_bottleneck_move:execution(cfg, data)
|
||||
if data.BD_bottleneck_moves_done then
|
||||
if BD_bottleneck_moves_done then
|
||||
local units = {}
|
||||
if MAISD.get_mai_self_data(data, cfg.ai_id, "side_leader_activated") then
|
||||
units = AH.get_units_with_moves { side = wesnoth.current.side }
|
||||
|
@ -466,16 +470,16 @@ function ca_bottleneck_move:execution(cfg, data)
|
|||
end
|
||||
else
|
||||
-- Don't want full move, as this might be stepping out of the way
|
||||
local cfg = { partial_move = true, weapon = data.BD_level_up_weapon }
|
||||
AH.robust_move_and_attack(ai, data.BD_unit, data.BD_hex, data.BD_level_up_defender, cfg)
|
||||
local cfg = { partial_move = true, weapon = BD_level_up_weapon }
|
||||
AH.robust_move_and_attack(ai, BD_unit, BD_hex, BD_level_up_defender, cfg)
|
||||
end
|
||||
|
||||
-- Now delete almost everything
|
||||
-- Keep only data.BD_is_my_territory because it is very expensive
|
||||
data.BD_unit, data.BD_hex = nil, nil
|
||||
data.BD_level_up_defender, data.BD_level_up_weapon = nil, nil
|
||||
data.BD_bottleneck_moves_done = nil
|
||||
data.BD_def_map, data.BD_healer_map, data.BD_leadership_map, data.BD_healing_map = nil, nil, nil, nil
|
||||
-- Keep only BD_is_my_territory because it is very expensive
|
||||
BD_unit, BD_hex = nil, nil
|
||||
BD_level_up_defender, BD_level_up_weapon = nil, nil
|
||||
BD_bottleneck_moves_done = nil
|
||||
BD_def_map, BD_healer_map, BD_leadership_map, BD_healing_map = nil, nil, nil, nil
|
||||
end
|
||||
|
||||
return ca_bottleneck_move
|
||||
|
|
|
@ -2,11 +2,14 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local FAU = wesnoth.require "ai/micro_ais/cas/ca_fast_attack_utils.lua"
|
||||
local LS = wesnoth.require "location_set"
|
||||
|
||||
local fast_combat_units, fast_combat_unit_i,fast_target, fast_dst
|
||||
local gamedata, move_cache
|
||||
|
||||
local ca_fast_combat = {}
|
||||
|
||||
function ca_fast_combat:evaluation(cfg, data)
|
||||
data.move_cache = { turn = wesnoth.current.turn }
|
||||
data.gamedata = FAU.gamedata_setup()
|
||||
move_cache = { turn = wesnoth.current.turn }
|
||||
gamedata = FAU.gamedata_setup()
|
||||
|
||||
local filter_own = wml.get_child(cfg, "filter")
|
||||
local filter_enemy = wml.get_child(cfg, "filter_second")
|
||||
|
@ -15,24 +18,24 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
local units_sorted = true
|
||||
if (not filter_own) and (not filter_enemy) then
|
||||
local attacks_aspect = ai.aspects.attacks
|
||||
if (not data.fast_combat_units) or (not data.fast_combat_units[1]) then
|
||||
if (not fast_combat_units) or (not fast_combat_units[1]) then
|
||||
-- Leader is dealt with in a separate CA
|
||||
data.fast_combat_units = {}
|
||||
fast_combat_units = {}
|
||||
for _,unit in ipairs(attacks_aspect.own) do
|
||||
if (not unit.canrecruit) then
|
||||
table.insert(data.fast_combat_units, unit)
|
||||
table.insert(fast_combat_units, unit)
|
||||
end
|
||||
end
|
||||
if (not data.fast_combat_units[1]) then return 0 end
|
||||
if (not fast_combat_units[1]) then return 0 end
|
||||
units_sorted = false
|
||||
end
|
||||
enemies = attacks_aspect.enemy
|
||||
else
|
||||
if (not data.fast_combat_units) or (not data.fast_combat_units[1]) then
|
||||
data.fast_combat_units = AH.get_live_units(
|
||||
if (not fast_combat_units) or (not fast_combat_units[1]) then
|
||||
fast_combat_units = AH.get_live_units(
|
||||
FAU.build_attack_filter("own", filter_own)
|
||||
)
|
||||
if (not data.fast_combat_units[1]) then return 0 end
|
||||
if (not fast_combat_units[1]) then return 0 end
|
||||
units_sorted = false
|
||||
end
|
||||
enemies = AH.get_live_units(
|
||||
|
@ -43,9 +46,9 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
if not units_sorted then
|
||||
-- For speed reasons, we'll go through the arrays from the end, so they are sorted backwards
|
||||
if cfg.weak_units_first then
|
||||
table.sort(data.fast_combat_units, function(a,b) return a.hitpoints > b.hitpoints end)
|
||||
table.sort(fast_combat_units, function(a,b) return a.hitpoints > b.hitpoints end)
|
||||
else
|
||||
table.sort(data.fast_combat_units, function(a,b) return a.hitpoints < b.hitpoints end)
|
||||
table.sort(fast_combat_units, function(a,b) return a.hitpoints < b.hitpoints end)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -75,12 +78,12 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
-- Get the locations to be avoided
|
||||
local avoid_map = FAU.get_avoid_map(cfg)
|
||||
|
||||
for i = #data.fast_combat_units,1,-1 do
|
||||
local unit = data.fast_combat_units[i]
|
||||
for i = #fast_combat_units,1,-1 do
|
||||
local unit = fast_combat_units[i]
|
||||
|
||||
if unit and unit.valid and (unit.attacks_left > 0) and (#unit.attacks > 0) then
|
||||
local unit_info = FAU.get_unit_info(unit, data.gamedata)
|
||||
local unit_copy = FAU.get_unit_copy(unit.id, data.gamedata)
|
||||
local unit_info = FAU.get_unit_info(unit, gamedata)
|
||||
local unit_copy = FAU.get_unit_copy(unit.id, gamedata)
|
||||
local attacks = AH.get_attacks({ unit }, { include_occupied = cfg.include_occupied_attack_hexes, viewing_side = viewing_side })
|
||||
|
||||
if (#attacks > 0) then
|
||||
|
@ -90,16 +93,16 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
and (not avoid_map:get(attack.dst.x, attack.dst.y))
|
||||
then
|
||||
local target = wesnoth.get_unit(attack.target.x, attack.target.y)
|
||||
local target_info = FAU.get_unit_info(target, data.gamedata)
|
||||
local target_info = FAU.get_unit_info(target, gamedata)
|
||||
|
||||
local att_stat, def_stat = FAU.battle_outcome(
|
||||
unit_copy, target, { attack.dst.x, attack.dst.y },
|
||||
unit_info, target_info, data.gamedata, data.move_cache
|
||||
unit_info, target_info, gamedata, move_cache
|
||||
)
|
||||
|
||||
local rating, attacker_rating, defender_rating, extra_rating = FAU.attack_rating(
|
||||
{ unit_info }, target_info, { { attack.dst.x, attack.dst.y } },
|
||||
{ att_stat }, def_stat, data.gamedata,
|
||||
{ att_stat }, def_stat, gamedata,
|
||||
{
|
||||
aggression = aggression,
|
||||
leader_weight = cfg.leader_weight
|
||||
|
@ -115,22 +118,23 @@ function ca_fast_combat:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
if best_target then
|
||||
data.fast_combat_unit_i = i
|
||||
data.fast_target, data.fast_dst = best_target, best_dst
|
||||
fast_combat_unit_i = i
|
||||
fast_target, fast_dst = best_target, best_dst
|
||||
return cfg.ca_score
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
data.fast_combat_units[i] = nil
|
||||
fast_combat_units[i] = nil
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ca_fast_combat:execution(cfg, data)
|
||||
AH.robust_move_and_attack(ai, data.fast_combat_units[data.fast_combat_unit_i], data.fast_dst, data.fast_target)
|
||||
data.fast_combat_units[data.fast_combat_unit_i] = nil
|
||||
AH.robust_move_and_attack(ai, fast_combat_units[fast_combat_unit_i], fast_dst, fast_target)
|
||||
fast_combat_units[fast_combat_unit_i] = nil
|
||||
fast_combat_unit_i,fast_target, fast_dst = nil, nil, nil
|
||||
end
|
||||
|
||||
return ca_fast_combat
|
||||
|
|
|
@ -3,6 +3,9 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local FAU = wesnoth.require "ai/micro_ais/cas/ca_fast_attack_utils.lua"
|
||||
local LS = wesnoth.require "location_set"
|
||||
|
||||
local leader, fast_target, fast_dst
|
||||
local gamedata, move_cache
|
||||
|
||||
local ca_fast_combat_leader = {}
|
||||
|
||||
function ca_fast_combat_leader:evaluation(cfg, data)
|
||||
|
@ -15,8 +18,8 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
leader_attack_max_units = (cfg and cfg.leader_attack_max_units) or 3
|
||||
leader_additional_threat = (cfg and cfg.leader_additional_threat) or 1
|
||||
|
||||
data.move_cache = { turn = wesnoth.current.turn }
|
||||
data.gamedata = FAU.gamedata_setup()
|
||||
move_cache = { turn = wesnoth.current.turn }
|
||||
gamedata = FAU.gamedata_setup()
|
||||
|
||||
local filter_own = wml.get_child(cfg, "filter")
|
||||
local filter_enemy = wml.get_child(cfg, "filter_second")
|
||||
|
@ -101,8 +104,8 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
end
|
||||
end
|
||||
|
||||
local leader_info = FAU.get_unit_info(leader, data.gamedata)
|
||||
local leader_copy = FAU.get_unit_copy(leader.id, data.gamedata)
|
||||
local leader_info = FAU.get_unit_info(leader, gamedata)
|
||||
local leader_copy = FAU.get_unit_copy(leader.id, gamedata)
|
||||
|
||||
-- If threatened_leader_fights=yes, check out the current threat (power only,
|
||||
-- not units) on the AI leader
|
||||
|
@ -149,16 +152,16 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
|
||||
if acceptable_attack then
|
||||
local target = wesnoth.get_unit(attack.target.x, attack.target.y)
|
||||
local target_info = FAU.get_unit_info(target, data.gamedata)
|
||||
local target_info = FAU.get_unit_info(target, gamedata)
|
||||
|
||||
local att_stat, def_stat = FAU.battle_outcome(
|
||||
leader_copy, target, { attack.dst.x, attack.dst.y },
|
||||
leader_info, target_info, data.gamedata, data.move_cache
|
||||
leader_info, target_info, gamedata, move_cache
|
||||
)
|
||||
|
||||
local rating, attacker_rating, defender_rating, extra_rating = FAU.attack_rating(
|
||||
{ leader_info }, target_info, { { attack.dst.x, attack.dst.y } },
|
||||
{ att_stat }, def_stat, data.gamedata,
|
||||
{ att_stat }, def_stat, gamedata,
|
||||
{
|
||||
aggression = aggression,
|
||||
leader_weight = leader_weight
|
||||
|
@ -175,8 +178,8 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
if best_target then
|
||||
data.leader = leader
|
||||
data.fast_target, data.fast_dst = best_target, best_dst
|
||||
leader = leader
|
||||
fast_target, fast_dst = best_target, best_dst
|
||||
return cfg.ca_score
|
||||
end
|
||||
end
|
||||
|
@ -185,8 +188,8 @@ function ca_fast_combat_leader:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
function ca_fast_combat_leader:execution(cfg, data)
|
||||
AH.robust_move_and_attack(ai, data.leader, data.fast_dst, data.fast_target)
|
||||
data.leader, data.fast_target, data.fast_dst = nil, nil, nil
|
||||
AH.robust_move_and_attack(ai, leader, fast_dst, fast_target)
|
||||
leader, fast_target, fast_dst = nil, nil, nil
|
||||
end
|
||||
|
||||
return ca_fast_combat_leader
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local AANS_attack
|
||||
|
||||
local ca_aggressive_attack_no_suicide = {}
|
||||
|
||||
function ca_aggressive_attack_no_suicide:evaluation(cfg, data)
|
||||
|
@ -45,7 +47,7 @@ function ca_aggressive_attack_no_suicide:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
if best_attack then
|
||||
data.attack = best_attack
|
||||
AANS_attack = best_attack
|
||||
return 100000
|
||||
end
|
||||
|
||||
|
@ -53,8 +55,8 @@ function ca_aggressive_attack_no_suicide:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
function ca_aggressive_attack_no_suicide:execution(cfg, data)
|
||||
AH.robust_move_and_attack(ai, data.attack.src, data.attack.dst, data.attack.target)
|
||||
data.attack = nil
|
||||
AH.robust_move_and_attack(ai, AANS_attack.src, AANS_attack.dst, AANS_attack.target)
|
||||
AANS_attack = nil
|
||||
end
|
||||
|
||||
return ca_aggressive_attack_no_suicide
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
local R = wesnoth.require "ai/lua/retreat.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local retreat_unit, retreat_dst
|
||||
|
||||
local retreat = {}
|
||||
|
||||
function retreat:evaluation(cfg, data)
|
||||
|
@ -14,8 +16,8 @@ function retreat:evaluation(cfg, data)
|
|||
local unit, dst, enemy_threat = R.retreat_injured_units(units)
|
||||
|
||||
if unit then
|
||||
data.retreat_unit = unit
|
||||
data.retreat_dst = dst
|
||||
retreat_unit = unit
|
||||
retreat_dst = dst
|
||||
return 101000
|
||||
end
|
||||
|
||||
|
@ -23,8 +25,8 @@ function retreat:evaluation(cfg, data)
|
|||
end
|
||||
|
||||
function retreat:execution(cfg, data)
|
||||
AH.movefull_outofway_stopunit(ai, data.retreat_unit, data.retreat_dst[1], data.retreat_dst[2])
|
||||
data.retreat_unit, data.retreat_dst = nil, nil
|
||||
AH.movefull_outofway_stopunit(ai, retreat_unit, retreat_dst[1], retreat_dst[2])
|
||||
retreat_unit, retreat_dst = nil, nil
|
||||
end
|
||||
|
||||
return retreat
|
||||
|
|
Loading…
Add table
Reference in a new issue