Skip to content

Commit f2406ac

Browse files
committed
Experimental AI: correctly deal with hidden and petrified units
1 parent 339b4f3 commit f2406ac

File tree

3 files changed

+25
-29
lines changed

3 files changed

+25
-29
lines changed

data/ai/lua/generic_recruit_engine.lua

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ return {
1414
-- (default always returns false)
1515
-- leader_takes_village: function that returns true if and only if the leader is going to move to capture a village this turn
1616
-- (default always returns true)
17+
-- Note: the recruiting code assumes full knowledge of units on the map and the recruit lists of other sides for the purpose of
18+
-- finding the best unit types to recruit. It does not work otherwise. It assumes normal vision of the AI side (that is, it disregards
19+
-- hidden enemy units) for determining from which keep hex the leader should recruit and on which castle hexes to recruit new units
1720
init = function(ai_cas, params)
1821
if not params then
1922
params = {}
@@ -354,7 +357,7 @@ return {
354357
local no_space = true
355358
for i,c in ipairs(data.castle.locs) do
356359
local unit = wesnoth.get_unit(c[1], c[2])
357-
if (not unit) then
360+
if (not AH.is_visible_unit(wesnoth.current.side, unit)) then
358361
no_space = false
359362
break
360363
end
@@ -612,19 +615,19 @@ return {
612615
-- and also the closest enemy
613616
local max_rating = -1
614617

615-
local enemy_leaders = AH.get_live_units { canrecruit = 'yes',
616-
{ "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } }
617-
}
618+
local enemy_leaders = AH.get_attackable_enemies { canrecruit = 'yes' }
618619
local closest_enemy_distance, closest_enemy_location = AH.get_closest_enemy()
619620

620621
for i,c in ipairs(data.castle.locs) do
621622
local rating = 0
622623
local unit = wesnoth.get_unit(c[1], c[2])
623-
if (not unit) then
624+
if (not AH.is_visible_unit(wesnoth.current.side, unit)) then
624625
for j,e in ipairs(enemy_leaders) do
625626
rating = rating + 1 / H.distance_between(c[1], c[2], e.x, e.y) ^ 2.
626627
end
627-
rating = rating + 1 / H.distance_between(c[1], c[2], closest_enemy_location.x, closest_enemy_location.y) ^ 2.
628+
if closest_enemy_location then
629+
rating = rating + 1 / H.distance_between(c[1], c[2], closest_enemy_location.x, closest_enemy_location.y) ^ 2.
630+
end
628631
if (rating > max_rating) then
629632
max_rating, best_hex = rating, { c[1], c[2] }
630633
end
@@ -650,9 +653,9 @@ return {
650653
local target_hex = recruit_data.recruit.target_hex
651654
local distance_to_enemy, enemy_location
652655
if target_hex[1] then
653-
distance_to_enemy, enemy_location = AH.get_closest_enemy(target_hex)
656+
distance_to_enemy, enemy_location = AH.get_closest_enemy(target_hex, wesnoth.current.side, { viewing_side = 0 })
654657
else
655-
distance_to_enemy, enemy_location = AH.get_closest_enemy(best_hex)
658+
distance_to_enemy, enemy_location = AH.get_closest_enemy(best_hex, wesnoth.current.side, { viewing_side = 0 })
656659
end
657660

658661
local gold_limit = 9e99

data/ai/lua/generic_rush_engine.lua

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ return {
6767

6868
-------- Castle Switch CA --------------
6969
local function get_reachable_enemy_leaders(unit)
70+
-- We're cheating a little here and also find hidden enemy leaders. That's
71+
-- because a human player could make a pretty good educated guess as to where
72+
-- the enemy leaders are likely to be while the AI does not know how to do that.
7073
local potential_enemy_leaders = AH.get_live_units { canrecruit = 'yes',
7174
{ "filter_side", { { "enemy_of", {side = wesnoth.current.side} } } }
7275
}
@@ -228,7 +231,7 @@ return {
228231
local should_wait = false
229232
for i,loc in ipairs(castle) do
230233
local unit = wesnoth.get_unit(loc[1], loc[2])
231-
if not unit then
234+
if (not AH.is_visible_unit(wesnoth.current.side, unit)) then
232235
should_wait = false
233236
break
234237
elseif unit.moves > 0 then
@@ -273,9 +276,7 @@ return {
273276
return 0
274277
end
275278

276-
local enemies = AH.get_live_units {
277-
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
278-
}
279+
local enemies = AH.get_attackable_enemies()
279280

280281
local villages = wesnoth.get_villages()
281282
-- Just in case:
@@ -331,7 +332,7 @@ return {
331332
for i,u in ipairs(units) do
332333
-- Skip villages that have units other than 'u' itself on them
333334
local village_occupied = false
334-
if unit_in_way and ((unit_in_way.x ~= u.x) or (unit_in_way.y ~= u.y)) then
335+
if AH.is_visible_unit(wesnoth.current.side, unit_in_way) and ((unit_in_way ~= u)) then
335336
village_occupied = true
336337
end
337338

@@ -486,21 +487,13 @@ return {
486487

487488
function generic_rush:spread_poison_exec()
488489
local attacker = wesnoth.get_unit(self.data.attack.src.x, self.data.attack.src.y)
490+
-- If several attacks have poison, this will always find the last one
491+
local is_poisoner, poison_weapon = AH.has_weapon_special(attacker, "poison")
489492

490493
if AH.print_exec() then print_time(' Executing spread_poison CA') end
491494
if AH.show_messages() then W.message { speaker = attacker.id, message = 'Poison attack' } end
492495

493-
local defender = wesnoth.get_unit(self.data.attack.target.x, self.data.attack.target.y)
494-
495-
AH.movefull_stopunit(ai, attacker, self.data.attack.dst.x, self.data.attack.dst.y)
496-
if (not attacker) or (not attacker.valid) then return end
497-
if (not defender) or (not defender.valid) then return end
498-
499-
-- Find the poison weapon
500-
-- If several attacks have poison, this will always find the last one
501-
local is_poisoner, poison_weapon = AH.has_weapon_special(attacker, "poison")
502-
503-
AH.checked_attack(ai, attacker, defender, poison_weapon)
496+
AH.robust_move_and_attack(ai, attacker, self.data.attack.dst, self.data.attack.target, { weapon = poison_weapon })
504497

505498
self.data.attack = nil
506499
end
@@ -544,7 +537,7 @@ return {
544537
end
545538

546539
function generic_rush:retreat_injured_units_exec()
547-
AH.movefull_outofway_stopunit(ai, self.data.retreat_unit, self.data.retreat_loc)
540+
AH.robust_move_and_attack(ai, self.data.retreat_unit, self.data.retreat_loc)
548541
self.data.retreat_unit = nil
549542
self.data.retreat_loc = nil
550543
end

data/ai/lua/retreat.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,7 @@ end
109109

110110
function retreat_functions.get_retreat_injured_units(healees, regenerates)
111111
-- Only retreat to safe locations
112-
local enemies = AH.get_live_units {
113-
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
114-
}
112+
local enemies = AH.get_attackable_enemies()
115113
local enemy_attack_map = BC.get_attack_map(enemies)
116114

117115
local healing_locs = retreat_functions.get_healing_locations()
@@ -151,7 +149,9 @@ function retreat_functions.get_retreat_injured_units(healees, regenerates)
151149

152150
for j,loc in ipairs(possible_locations) do
153151
local unit_in_way = wesnoth.get_unit(loc[1], loc[2])
154-
if (not unit_in_way) or ((unit_in_way.moves > 0) and (unit_in_way.side == wesnoth.current.side)) then
152+
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way))
153+
or ((unit_in_way.moves > 0) and (unit_in_way.side == wesnoth.current.side))
154+
then
155155
local rating = base_rating
156156
local heal_score = 0
157157
if regenerates then

0 commit comments

Comments
 (0)