Skip to content

Commit

Permalink
Merge pull request #5630 from mattsc/remove_fai_uses
Browse files Browse the repository at this point in the history
Remove Formula AI uses
  • Loading branch information
mattsc committed Mar 24, 2021
2 parents 124adb3 + 4086758 commit f1c37f6
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 276 deletions.
33 changes: 0 additions & 33 deletions data/ai/dev/formula_ai.cfg

This file was deleted.

37 changes: 0 additions & 37 deletions data/ai/dev/formula_ai_poisoning.cfg

This file was deleted.

136 changes: 109 additions & 27 deletions data/ai/micro_ais/cas/ca_patrol.lua
@@ -1,4 +1,5 @@
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local BC = wesnoth.dofile "ai/lua/battle_calcs.lua"
local MAIUV = wesnoth.require "ai/micro_ais/micro_ai_unit_variables.lua"

local function get_patrol(cfg)
Expand All @@ -10,6 +11,78 @@ local function get_patrol(cfg)
return patrol
end

local function get_best_attack(unit, loc, last_waypoint, cfg)
local attack_range = cfg.attack_range or 1

-- The attack calculation can be somewhat expensive, check first if there are enemies within the specified range
local enemies = AH.get_attackable_enemies(
{ id = cfg.attack, { "filter_location", { x = loc[1], y = loc[2], radius = attack_range } } },
wesnoth.current.side,
{ ignore_visibility = cfg.attack_invisible_enemies }
)

-- An enemy on the last waypoint gets attacked preferentially and independent of
-- whether its id is given in cfg.attack; but it still needs to be within attack range
local enemy_last_waypoint
if last_waypoint then
enemy_last_waypoint = AH.get_attackable_enemies(
{ x = last_waypoint[1], y = last_waypoint[2] },
wesnoth.current.side,
{ ignore_visibility = cfg.attack_invisible_enemies }
)[1]
if enemy_last_waypoint and (wesnoth.map.distance_between(enemy_last_waypoint, loc) <= attack_range) then
local already_included = false
for _,enemy in ipairs(enemies) do
if (enemy.id == enemy_last_waypoint.id) then
already_included = true
break
end
end
if (not already_included) then
table.insert(enemies, enemy_last_waypoint)
end
end
end
if (#enemies == 0) then return end

local old_moves, old_loc
if ((loc[1] ~= unit.x) or (loc[2] ~= unit.y)) then
old_moves, old_loc = unit.moves, unit.loc
local _,sub_cost = AH.find_path_with_shroud(unit, loc)
unit.moves = unit.moves - sub_cost
unit.loc = loc
end
local attacks = AH.get_attacks({ unit }, { ignore_visibility = cfg.attack_invisible_enemies })
if old_moves then
unit.moves, unit.loc = old_moves, old_loc
end

local max_rating, best_enemy, best_dst = -math.huge
for _,attack in ipairs(attacks) do
for _,enemy in ipairs(enemies) do
if (attack.target.x == enemy.x) and (attack.target.y == enemy.y) then
local dst = { attack.dst.x, attack.dst.y }
local rating = BC.attack_rating(unit, enemy, dst)

-- Prioritize any enemy on the last waypoint
if enemy_last_waypoint and (enemy_last_waypoint.id == enemy.id) then
rating = rating + 1000
end

if (rating > max_rating) then
max_rating = rating
best_enemy = enemy
best_dst = dst
end

break
end
end
end

return best_enemy, best_dst
end

local ca_patrol = {}

function ca_patrol:evaluation(cfg)
Expand Down Expand Up @@ -42,15 +115,16 @@ function ca_patrol:execution(cfg)
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
end

while patrol.moves > 0 do
-- Check whether one of the enemies to be attacked is next to the patroller
-- If so, don't move, but attack that enemy
local adjacent_enemy = AH.get_attackable_enemies {
id = cfg.attack,
{ "filter_adjacent", { id = patrol.id } }
}[1]
if adjacent_enemy then break end
-- Check for a possible attack from the patrol's current position first, that
-- way we can skip the other evaluation if one is found
local last_waypoint
if cfg.one_time_only then last_waypoint = waypoints[n_wp] end
local enemy, dst
if (patrol.attacks_left > 0) and (#patrol.attacks > 0) then
enemy, dst = get_best_attack(patrol, patrol.loc, last_waypoint, cfg)
end

while (not enemy) and (patrol.moves > 0) do
-- Also check whether we're next to any unit (enemy or ally) which is on the next waypoint
local unit_on_wp = AH.get_visible_units(wesnoth.current.side, {
x = patrol_vars.patrol_x,
Expand Down Expand Up @@ -97,7 +171,7 @@ function ca_patrol:execution(cfg)
end
end

-- If we're on the last waypoint on one_time_only is set, stop here
-- If we're on the last waypoint and one_time_only is set, stop here
if cfg.one_time_only and
(patrol.x == waypoints[n_wp][1]) and (patrol.y == waypoints[n_wp][2])
then
Expand All @@ -106,6 +180,16 @@ function ca_patrol:execution(cfg)
local x, y = wesnoth.find_vacant_tile(patrol_vars.patrol_x, patrol_vars.patrol_y, patrol)
local nh = AH.next_hop(patrol, x, y)
if nh and ((nh[1] ~= patrol.x) or (nh[2] ~= patrol.y)) then
-- Check whether an attackable enemy comes into attack range at any hex along the way
local path = AH.find_path_with_shroud(patrol, nh[1], nh[2])
for i = 2,#path do -- The patrol's current position is already checked above
local loc = path[i]
enemy, dst = get_best_attack(patrol, loc, last_waypoint, cfg)
if enemy then
nh = loc
break
end
end
AH.checked_move(ai, patrol, nh[1], nh[2])
else
AH.checked_stopunit_moves(ai, patrol)
Expand All @@ -114,28 +198,26 @@ function ca_patrol:execution(cfg)
if (not patrol) or (not patrol.valid) then return end
end

-- Attack unit on the last waypoint under all circumstances if cfg.one_time_only is set
local adjacent_enemy
if cfg.one_time_only then
adjacent_enemy = AH.get_attackable_enemies {
x = waypoints[n_wp][1],
y = waypoints[n_wp][2],
{ "filter_adjacent", { id = patrol.id } }
}[1]
-- It is possible that the patrol unexpectedly ends up next to an enemy, e.g. because of an ambush
if not (enemy) then
enemy, dst = get_best_attack(patrol, patrol.loc, last_waypoint, cfg)
end

-- Otherwise attack adjacent enemy (if specified)
if (not adjacent_enemy) then
adjacent_enemy = AH.get_attackable_enemies {
id = cfg.attack,
{ "filter_adjacent", { id = patrol.id } }
}[1]
end
-- It is also possible that the patrol cannot make it to 'dst' because of an ambush,
-- in which case we can check whether the ambusher can/should be attacked.
-- So we need to execute the move and the attack separately.
if enemy then
AH.robust_move_and_attack(ai, patrol, dst)
if (not patrol) or (not patrol.valid) then return end

if adjacent_enemy then AH.checked_attack(ai, patrol, adjacent_enemy) end
if (not patrol) or (not patrol.valid) then return end
if (patrol.x ~= dst[1]) or (patrol.y ~= dst[2]) then
enemy, dst = get_best_attack(patrol, patrol.loc, last_waypoint, cfg)
end
end

AH.checked_stopunit_all(ai, patrol)
if enemy then
AH.robust_move_and_attack(ai, patrol, dst, enemy)
end
end

return ca_patrol
2 changes: 1 addition & 1 deletion data/ai/micro_ais/mai-defs/patrol.lua
Expand Up @@ -8,7 +8,7 @@ function wesnoth.micro_ais.patrol(cfg)
AH.get_multi_named_locs_xy('waypoint', cfg, 'Patrol [micro_ai] tag')
end
local required_keys = {}
local optional_keys = { "id", "[filter]", "attack", "one_time_only", "out_and_back", "waypoint_loc", "waypoint_x", "waypoint_y" }
local optional_keys = { "id", "[filter]", "attack", "attack_range", "attack_invisible_enemies", "one_time_only", "out_and_back", "waypoint_loc", "waypoint_x", "waypoint_y" }
local CA_parms = {
ai_id = 'mai_patrol',
{ ca_id = "move", location = 'ca_patrol.lua', score = cfg.ca_score or 300000 }
Expand Down
74 changes: 0 additions & 74 deletions data/ai/micro_ais/scenarios/guardians.cfg
@@ -1,17 +1,5 @@
#textdomain wesnoth-ai

#define HOME_GUARDIAN X Y
# Meant to be used as a suffix to a unit-generating macro call.
# The previously-generated unit will treat (X,Y) as its home.
[+unit]
[ai]
[vars]
home_loc="loc({X},{Y})"
[/vars]
[/ai]
[/unit]
#enddef

[test]
id=guardians
name= _ "Guardians"
Expand Down Expand Up @@ -54,26 +42,6 @@
recruit=Orcish Archer,Orcish Grunt
persistent=no
gold=30

[ai]
[modify_ai]
side=1
action=add
# wmllint: unbalanced-on
path=stage[main_loop].candidate_action[]
# wmllint: unbalanced-off
[candidate_action]
engine=fai
name=go_home
id=go_home
type=movement
# wmlindent: start ignoring
evaluation="if( (null != me.vars.home_loc), {AI_CA_MOVE_TO_TARGETS_SCORE}+10, 0)"
action="if( (me.loc != me.vars.home_loc), move(me.loc, next_hop(me.loc, me.vars.home_loc)), move(me.loc, me.loc)#do not move this turn#)"
# wmlindent: stop ignoring
[/candidate_action]
[/modify_ai]
[/ai]
[/side]

# Put all the units and markers out there
Expand Down Expand Up @@ -264,31 +232,6 @@
return_x,return_y=21,9
[/micro_ai]

# The home guards
[unit]
type=Troll Whelp
side=2
id=home1
name= _ "Home Guard 1"
x,y=19,2
[variables]
label="home 19,2"
[/variables]
[/unit]
{HOME_GUARDIAN 19 2}

[unit]
type=Troll
side=2
id=home 2
name= _ "Home Guard 2"
x,y=21,10
[variables]
label="home 21,10"
[/variables]
[/unit]
{HOME_GUARDIAN 21 10}

# The stationed guardians
[unit]
type=Skeleton Archer
Expand Down Expand Up @@ -480,23 +423,6 @@ separate attack Zone"
[/message]
[/command]
[/set_menu_item]
[set_menu_item]
id=m03_home
description= _ "Home Guard"
image=units/trolls/grunt.png~CROP(31,7,24,24)
[show_if]
{VARIABLE_CONDITIONAL scenario_name equals guardians}
[/show_if]
[command]
[message]
speaker=narrator
image=portraits/trolls/troll.png
caption= _ "Home Guard"
message= _ "A 'home guard' is a variant on the 'guardian' AI special. With this variant, the unit has an assigned 'home' location, and will return there if not involved in combat and if not going to a village, whether for healing or to capture it this turn. (By contrast, the standard guardian AI will cause the unit to stay where it last attacked.) This differs from 'return guardian' in that a home guard will press the attack, possibly getting drawn quite far from 'home', rather than returning after each attack. (It can also be lured away by a string of closely-placed villages, but that is something a map builder can control.)
This also demonstrates how to combine candidate actions from Formula AI and Lua AI in one side. The home guard is written in Formula AI, while the return and stationed guardians and the cowards are written in Lua AI. In addition the non-guardian units of the side follow the default AI behavior."
[/message]
[/command]
[/set_menu_item]
[set_menu_item]
id=m04_stationed
description= _ "Stationed Guardian"
Expand Down

0 comments on commit f1c37f6

Please sign in to comment.