mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-28 08:43:53 +00:00

Insert these calls in loops that do expensive calculations for the CAs of the default and experimental AIs.
93 lines
3.4 KiB
Lua
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
|