Experimental AI: correctly deal with hidden and petrified units

This commit is contained in:
mattsc 2016-10-19 07:36:35 -07:00
parent 339b4f3ee1
commit f2406ac829
3 changed files with 25 additions and 29 deletions

View File

@ -14,6 +14,9 @@ return {
-- (default always returns false)
-- leader_takes_village: function that returns true if and only if the leader is going to move to capture a village this turn
-- (default always returns true)
-- Note: the recruiting code assumes full knowledge of units on the map and the recruit lists of other sides for the purpose of
-- finding the best unit types to recruit. It does not work otherwise. It assumes normal vision of the AI side (that is, it disregards
-- hidden enemy units) for determining from which keep hex the leader should recruit and on which castle hexes to recruit new units
init = function(ai_cas, params)
if not params then
params = {}
@ -354,7 +357,7 @@ return {
local no_space = true
for i,c in ipairs(data.castle.locs) do
local unit = wesnoth.get_unit(c[1], c[2])
if (not unit) then
if (not AH.is_visible_unit(wesnoth.current.side, unit)) then
no_space = false
break
end
@ -612,19 +615,19 @@ return {
-- and also the closest enemy
local max_rating = -1
local enemy_leaders = AH.get_live_units { canrecruit = 'yes',
{ "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } }
}
local enemy_leaders = AH.get_attackable_enemies { canrecruit = 'yes' }
local closest_enemy_distance, closest_enemy_location = AH.get_closest_enemy()
for i,c in ipairs(data.castle.locs) do
local rating = 0
local unit = wesnoth.get_unit(c[1], c[2])
if (not unit) then
if (not AH.is_visible_unit(wesnoth.current.side, unit)) then
for j,e in ipairs(enemy_leaders) do
rating = rating + 1 / H.distance_between(c[1], c[2], e.x, e.y) ^ 2.
end
rating = rating + 1 / H.distance_between(c[1], c[2], closest_enemy_location.x, closest_enemy_location.y) ^ 2.
if closest_enemy_location then
rating = rating + 1 / H.distance_between(c[1], c[2], closest_enemy_location.x, closest_enemy_location.y) ^ 2.
end
if (rating > max_rating) then
max_rating, best_hex = rating, { c[1], c[2] }
end
@ -650,9 +653,9 @@ return {
local target_hex = recruit_data.recruit.target_hex
local distance_to_enemy, enemy_location
if target_hex[1] then
distance_to_enemy, enemy_location = AH.get_closest_enemy(target_hex)
distance_to_enemy, enemy_location = AH.get_closest_enemy(target_hex, wesnoth.current.side, { viewing_side = 0 })
else
distance_to_enemy, enemy_location = AH.get_closest_enemy(best_hex)
distance_to_enemy, enemy_location = AH.get_closest_enemy(best_hex, wesnoth.current.side, { viewing_side = 0 })
end
local gold_limit = 9e99

View File

@ -67,6 +67,9 @@ return {
-------- Castle Switch CA --------------
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
-- the enemy leaders are likely to be while the AI does not know how to do that.
local potential_enemy_leaders = AH.get_live_units { canrecruit = 'yes',
{ "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } }
}
@ -228,7 +231,7 @@ return {
local should_wait = false
for i,loc in ipairs(castle) do
local unit = wesnoth.get_unit(loc[1], loc[2])
if not unit then
if (not AH.is_visible_unit(wesnoth.current.side, unit)) then
should_wait = false
break
elseif unit.moves > 0 then
@ -273,9 +276,7 @@ return {
return 0
end
local enemies = AH.get_live_units {
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
}
local enemies = AH.get_attackable_enemies()
local villages = wesnoth.get_villages()
-- Just in case:
@ -331,7 +332,7 @@ return {
for i,u in ipairs(units) do
-- Skip villages that have units other than 'u' itself on them
local village_occupied = false
if unit_in_way and ((unit_in_way.x ~= u.x) or (unit_in_way.y ~= u.y)) then
if AH.is_visible_unit(wesnoth.current.side, unit_in_way) and ((unit_in_way ~= u)) then
village_occupied = true
end
@ -486,21 +487,13 @@ return {
function generic_rush:spread_poison_exec()
local attacker = wesnoth.get_unit(self.data.attack.src.x, self.data.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 print_time(' Executing spread_poison CA') end
if AH.show_messages() then W.message { speaker = attacker.id, message = 'Poison attack' } end
local defender = wesnoth.get_unit(self.data.attack.target.x, self.data.attack.target.y)
AH.movefull_stopunit(ai, attacker, self.data.attack.dst.x, self.data.attack.dst.y)
if (not attacker) or (not attacker.valid) then return end
if (not defender) or (not defender.valid) then return end
-- Find the poison weapon
-- If several attacks have poison, this will always find the last one
local is_poisoner, poison_weapon = AH.has_weapon_special(attacker, "poison")
AH.checked_attack(ai, attacker, defender, poison_weapon)
AH.robust_move_and_attack(ai, attacker, self.data.attack.dst, self.data.attack.target, { weapon = poison_weapon })
self.data.attack = nil
end
@ -544,7 +537,7 @@ return {
end
function generic_rush:retreat_injured_units_exec()
AH.movefull_outofway_stopunit(ai, self.data.retreat_unit, self.data.retreat_loc)
AH.robust_move_and_attack(ai, self.data.retreat_unit, self.data.retreat_loc)
self.data.retreat_unit = nil
self.data.retreat_loc = nil
end

View File

@ -109,9 +109,7 @@ end
function retreat_functions.get_retreat_injured_units(healees, regenerates)
-- Only retreat to safe locations
local enemies = AH.get_live_units {
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
}
local enemies = AH.get_attackable_enemies()
local enemy_attack_map = BC.get_attack_map(enemies)
local healing_locs = retreat_functions.get_healing_locations()
@ -151,7 +149,9 @@ function retreat_functions.get_retreat_injured_units(healees, regenerates)
for j,loc in ipairs(possible_locations) do
local unit_in_way = wesnoth.get_unit(loc[1], loc[2])
if (not unit_in_way) or ((unit_in_way.moves > 0) and (unit_in_way.side == wesnoth.current.side)) then
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way))
or ((unit_in_way.moves > 0) and (unit_in_way.side == wesnoth.current.side))
then
local rating = base_rating
local heal_score = 0
if regenerates then