wesnoth/data/ai/lua/ca_level_up_attack.lua
mattsc b1b521f582 Lua AI CAs: add handle_user_interact calls
Insert these calls in loops that do expensive calculations for the CAs of the default and experimental AIs.
2022-07-12 22:57:16 -04:00

93 lines
3.4 KiB
Lua

-- An example CA that tries to level up units by attacking weakened enemies.
-- Ported from level_up_attack_eval.fai and level_up_attack_move.fai
local LS = wesnoth.require "location_set"
local F = wesnoth.require "functional"
local level_up_attack = {}
local function kill_xp(unit)
local ratio = unit.level
if ratio == 0 then ratio = 0.5 end
return wesnoth.game_config.kill_experience * ratio
end
local function get_best_defense_loc(moves, attacker, victim)
local attack_spots = F.filter(moves, function(v)
return wesnoth.map.distance_between(v, victim) == 1
end)
return F.choose(attack_spots, function(loc)
return attacker:defense_on(loc)
end)
end
local function iter_possible_targets(moves, attacker)
moves = LS.of_pairs(moves)
-- The criteria are: a) unit is reachable b) unit's health is low
local targets = wesnoth.units.find({
})
return coroutine.wrap(function()
local checked = LS.create()
moves:iter(function(to_x, to_y)
for adj_x, adj_y in wesnoth.current.map:iter_adjacent(to_x, to_y) do
if not checked:get(adj_x, adj_y) then
checked:insert(adj_x, adj_y)
local u = wesnoth.units.get(adj_x, adj_y)
if u and u.hitpoints / u.max_hitpoints < 0.2 then
coroutine.yield(u)
end
end
end
end)
end)
end
local possible_attacks
function level_up_attack:evaluation(cfg, data, filter_own)
possible_attacks = LS.create()
local moves = LS.of_raw(ai.get_src_dst())
local units = wesnoth.units.find(filter_own)
for _,me in ipairs(units) do
wesnoth.interface.handle_user_interact()
local save_x, save_y = me.x, me.y
if not moves[me] or #moves[me] == 0 then
goto continue
end
if kill_xp(me) <= (me.max_experience - me.experience) then
goto continue
end
for target in iter_possible_targets(moves[me], me) do
local defense_loc = get_best_defense_loc(moves[me], me, target)
me:to_map(defense_loc.x, defense_loc.y)
local attacker_outcome, defender_outcome = wesnoth.simulate_combat(me, target)
-- Only consider attacks where
-- a) there's a chance the defender dies and
-- b) there's no chance the attacker dies
if defender_outcome.hp_chance[0] == 0 or attacker_outcome.hp_chance[0] > 0 then
goto continue
end
-- If killing the defender is the most likely result, save this as a possible attack
local best = F.choose_map(defender_outcome.hp_chance, function(k, v) return v end)
if best.key == 0 then
possible_attacks:insert(defense_loc, {
chance = defender_outcome.hp_chance[0],
attacker = me, target = target
})
end
end
::continue::
me:to_map(save_x, save_y)
end
local _, best_score = F.choose_map(possible_attacks:to_map(), 'chance')
return math.max(0, best_score) * 100000
end
function level_up_attack:execution(cfg, data)
local best_attack = F.choose_map(possible_attacks:to_map(), 'chance')
ai.move(best_attack.value.attacker, best_attack.key)
ai.attack(best_attack.value.attacker, best_attack.value.target)
end
return level_up_attack