Lua AIs: distinguish between healing locations and villages

There are rating contributions for hexes which heal (not all of which are villages) and others for hexes that provide income (villages). Previously only villages were considered for both types of hexes.
This commit is contained in:
mattsc 2018-11-07 12:24:31 -08:00
parent 8bca955ad5
commit 1856377d35
4 changed files with 34 additions and 27 deletions

View file

@ -809,10 +809,10 @@ function battle_calcs.attack_rating(attacker, defender, dst, cfg, cache)
damage = damage + 6 * (att_stats.slowed - att_stats.hp_chance[0])
end
-- If attack is from a village (or other healing location), count that as slightly more than the healing amount
-- If attack is from a healing location, count that as slightly more than the healing amount
damage = damage - 1.25 * wesnoth.get_terrain_info(wesnoth.get_terrain(dst[1], dst[2])).healing
-- Equivalently, if attack is adjacent to an unoccupied village, that's bad
-- Equivalently, if attack is adjacent to an unoccupied healing location, that's bad
for xa,ya in H.adjacent_tiles(dst[1], dst[2]) do
local healing = wesnoth.get_terrain_info(wesnoth.get_terrain(xa, ya)).healing
if (healing > 0) and (not wesnoth.get_unit(xa, ya)) then
@ -863,7 +863,7 @@ function battle_calcs.attack_rating(attacker, defender, dst, cfg, cache)
damage = damage + 6 * (def_stats.slowed - def_stats.hp_chance[0])
end
-- If defender is on a village (or other healing location), count that as slightly more than the healing amount
-- If defender is on a healing location, count that as slightly more than the healing amount
damage = damage - 1.25 * wesnoth.get_terrain_info(wesnoth.get_terrain(defender.x, defender.y)).healing
if (damage < 0) then damage = 0. end
@ -905,9 +905,8 @@ function battle_calcs.attack_rating(attacker, defender, dst, cfg, cache)
-- If defender is on a village, add a bonus rating (we want to get rid of those preferentially)
-- So yes, this is positive, even though it's a plus for the defender
-- Defenders on villages also got a negative damage rating above (these don't exactly cancel each other though)
local is_village = wesnoth.get_terrain_info(wesnoth.get_terrain(defender.x, defender.y)).village
if is_village then
-- Note: defenders on healing locations also got a negative damage rating above (these don't exactly cancel each other though)
if wesnoth.get_terrain_info(wesnoth.get_terrain(defender.x, defender.y)).village then
defender_value = defender_value * (1. + 10. / attacker.max_hitpoints)
end

View file

@ -42,14 +42,14 @@ function ca_spread_poison:evaluation(cfg, data)
-- Don't try to poison a unit that cannot be poisoned
local cant_poison = defender.status.poisoned or defender.status.unpoisonable
-- For now, we also simply don't poison units on villages (unless standard combat CA does it)
-- For now, we also simply don't poison units on healing locations (unless standard combat CA does it)
local defender_terrain = wesnoth.get_terrain(defender.x, defender.y)
local on_village = wesnoth.get_terrain_info(defender_terrain).village
local healing = wesnoth.get_terrain_info(defender_terrain).healing
-- Also, poisoning units that would level up through the attack or could level on their turn as a result is very bad
local about_to_level = defender.max_experience - defender.experience <= (attacker.level * 2 * wesnoth.game_config.combat_experience)
if (not cant_poison) and (not on_village) and (not about_to_level) then
if (not cant_poison) and (healing == 0) and (not about_to_level) then
-- Strongest enemy gets poisoned first
local rating = defender.hitpoints

View file

@ -49,13 +49,17 @@ function ca_fast_attack_utils.gamedata_setup()
local village_map = {}
for _,village in ipairs(wesnoth.get_villages()) do
if (not village_map[village[1]]) then village_map[village[1]] = {} end
village_map[village[1]][village[2]] = {
owner = wesnoth.get_village_owner(village[1], village[2]),
healing = wesnoth.get_terrain_info(wesnoth.get_terrain(village[1], village[2])).healing
}
village_map[village[1]][village[2]] = true
end
gamedata.village_map = village_map
local healing_map = {}
for _,loc in ipairs(AH.get_healing_locations {}) do
if (not healing_map[loc[1]]) then healing_map[loc[1]] = {} end
healing_map[loc[1]][loc[2]] = wesnoth.get_terrain_info(wesnoth.get_terrain(loc[1], loc[2])).healing
end
gamedata.healing_map = healing_map
-- Only uses one leader per side right now, but only used for finding direction
-- of move -> sufficient for this.
gamedata.leaders = {}
@ -181,7 +185,7 @@ function ca_fast_attack_utils.is_acceptable_attack(damage_taken, damage_done, ag
return (damage_done / damage_taken) >= (1 - aggression)
end
function ca_fast_attack_utils.damage_rating_unit(attacker_info, defender_info, att_stat, def_stat, healing, cfg)
function ca_fast_attack_utils.damage_rating_unit(attacker_info, defender_info, att_stat, def_stat, is_village, healing, cfg)
-- Calculate the rating for the damage received by a single attacker on a defender.
-- The attack att_stat both for the attacker and the defender need to be precalculated for this.
-- Unit information is passed in unit_infos format, rather than as unit proxy tables for speed reasons.
@ -191,8 +195,10 @@ function ca_fast_attack_utils.damage_rating_unit(attacker_info, defender_info, a
-- Input parameters:
-- @attacker_info, @defender_info: unit_info tables produced by ca_fast_attack_utils.get_unit_info()
-- @att_stat, @def_stat: attack statistics for the two units
-- @healing: the healing given by a village
-- @is_village: whether the hex from which the attacker attacks is a village
-- Set to nil or false if not, to anything if it is a village (does not have to be a boolean)
-- @healing: the amount of healing given by the hex from which the attacker attacks
-- Set to nil or false if there is no healing
--
-- Optional parameters:
-- @cfg: the optional weights listed right below (currently only leader_weight)
@ -210,7 +216,7 @@ function ca_fast_attack_utils.damage_rating_unit(attacker_info, defender_info, a
damage = damage + 4 * (att_stat.slowed - att_stat.hp_chance[0])
end
-- If attack is from a village, count its healing_value
-- If attack is from a healing location, count its healing_value
if healing then
damage = damage - healing
-- Otherwise only: if attacker can regenerate, add the regeneration bonus
@ -290,22 +296,24 @@ function ca_fast_attack_utils.attack_rating(attacker_infos, defender_info, dsts,
local attacker_rating = 0
for i,attacker_info in ipairs(attacker_infos) do
local attacker_healing = gamedata.village_map[dsts[i][1]]
local attacker_on_village = gamedata.village_map[dsts[i][1]]
and gamedata.village_map[dsts[i][1]][dsts[i][2]]
and gamedata.village_map[dsts[i][1]][dsts[i][2]].healing
local attacker_healing = gamedata.healing_map[dsts[i][1]]
and gamedata.healing_map[dsts[i][1]][dsts[i][2]]
attacker_rating = attacker_rating + ca_fast_attack_utils.damage_rating_unit(
attacker_info, defender_info, att_stats[i], def_stat, attacker_healing, cfg
attacker_info, defender_info, att_stats[i], def_stat, attacker_on_village, attacker_healing, cfg
)
end
-- attacker_info is passed only to figure out whether the attacker might level up
-- TODO: This is only works for the first attacker in a combo at the moment
local defender_x, defender_y = defender_info.x, defender_info.y
local defender_healing = gamedata.village_map[defender_x]
local defender_on_village = gamedata.village_map[defender_x]
and gamedata.village_map[defender_x][defender_y]
and gamedata.village_map[defender_x][defender_y].healing
local defender_healing = gamedata.healing_map[defender_x]
and gamedata.healing_map[defender_x][defender_y]
local defender_rating = ca_fast_attack_utils.damage_rating_unit(
defender_info, attacker_infos[1], def_stat, att_stats[1], defender_healing, cfg
defender_info, attacker_infos[1], def_stat, att_stats[1], defender_on_village, defender_healing, cfg
)
-- Now we add some extra ratings. They are positive for attacks that should be preferred
@ -319,8 +327,8 @@ function ca_fast_attack_utils.attack_rating(attacker_infos, defender_info, dsts,
extra_rating = extra_rating + defender_starting_damage_fraction * 0.33
-- If defender is on a village, add a bonus rating (we want to get rid of those preferentially)
-- This is in addition to the damage bonus already included above (but as an extra rating)
if defender_healing then
-- This is in addition to the healing bonus already included above (but as an extra rating)
if defender_on_village then
extra_rating = extra_rating + 10.
end

View file

@ -35,11 +35,11 @@ function ca_healer_move:evaluation(cfg, data)
local healees, healees_MP = {}, {}
for _,healee in ipairs(all_healees) do
-- Potential healees are units without MP that don't already have a healer (also without MP) next to them
-- Also, they cannot be on a village or regenerate
-- Also, they cannot be on a healing location or regenerate
if (healee.moves == 0) then
if (not healee:matches { ability = "regenerates" }) then
local is_village = wesnoth.get_terrain_info(wesnoth.get_terrain(healee.x, healee.y)).village
if (not is_village) then
local healing = wesnoth.get_terrain_info(wesnoth.get_terrain(healee.x, healee.y)).healing
if (healing == 0) then
local is_healee = true
for _,healer in ipairs(healers_noMP) do
if (M.distance_between(healee.x, healee.y, healer.x, healer.y) == 1) then