Herding Micro AI: code cleanup

This commit is contained in:
mattsc 2014-04-13 09:23:08 -07:00
parent 54babada05
commit 074df432d7
7 changed files with 64 additions and 78 deletions

View file

@ -19,7 +19,7 @@ end
local function get_enemies(cfg, radius)
local enemies = wesnoth.get_units {
{ "filter_side", { {"enemy_of", {side = wesnoth.current.side} } } },
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
{ "filter_location",
{ radius = radius,
{ "filter", { side = wesnoth.current.side, { "and", cfg.filter_second } } } }
@ -44,42 +44,40 @@ end
function ca_herding_attack_close_enemy:execution(ai, cfg)
local sheep = get_sheep(cfg)
local dogs = get_dogs(cfg)
local sheep = wesnoth.get_units { side = wesnoth.current.side, {"and", cfg.filter_second} }
-- We start with enemies within attack_distance (default: 4) hexes, which will be attacked
local radius = cfg.attack_distance or 4
local enemies = get_enemies(cfg, radius)
max_rating, best_dog, best_enemy, best_hex = -9e99, {}, {}, {}
for i,e in ipairs(enemies) do
for j,d in ipairs(dogs) do
local reach_map = AH.get_reachable_unocc(d)
for _,enemy in ipairs(enemies) do
for _,dog in ipairs(dogs) do
local reach_map = AH.get_reachable_unocc(dog)
reach_map:iter( function(x, y, v)
-- most important: distance to enemy
local rating = - H.distance_between(x, y, e.x, e.y) * 100.
-- Most important: distance from enemy
local rating = - H.distance_between(x, y, enemy.x, enemy.y) * 100.
-- 2nd: distance from any sheep
for k,s in ipairs(sheep) do
rating = rating - H.distance_between(x, y, s.x, s.y)
for _,single_sheep in ipairs(sheep) do
rating = rating - H.distance_between(x, y, single_sheep.x, single_sheep.y)
end
-- 3rd: most distant dog goes first
rating = rating + H.distance_between(e.x, e.y, d.x, d.y) / 100.
rating = rating + H.distance_between(enemy.x, enemy.y, dog.x, dog.y) / 100.
reach_map:insert(x, y, rating)
if (rating > max_rating) then
max_rating = rating
best_hex = { x, y }
best_dog, best_enemy = d, e
best_dog, best_enemy, best_hex = dog, enemy, { x, y }
end
end)
--AH.put_labels(reach_map)
--W.message { speaker = d.id, message = 'My turn' }
end
end
-- If we found a move, we do it, and attack if possible
if max_rating > -9e99 then
--print('Dog moving in to attack')
AH.movefull_stopunit(ai, best_dog, best_hex)
if (not best_dog) or (not best_dog.valid) then return end
if (not best_enemy) or (not best_enemy.valid) then return end
if H.distance_between(best_dog.x, best_dog.y, best_enemy.x, best_enemy.y) == 1 then
AH.checked_attack(ai, best_dog, best_enemy)
end
@ -87,50 +85,44 @@ function ca_herding_attack_close_enemy:execution(ai, cfg)
end
-- If we got here, no enemies to attack where found, so we go on to block other enemies
--print('Dogs: No enemies close enough to warrant attack')
-- Now we get all enemies within attention_distance hexes
local radius = cfg.attention_distance or 8
local enemies = get_enemies(cfg, radius)
-- Find closest sheep/enemy pair first
local min_dist, closest_sheep, closest_enemy = 9e99, {}, {}
for i,e in ipairs(enemies) do
for j,s in ipairs(sheep) do
local d = H.distance_between(e.x, e.y, s.x, s.y)
if d < min_dist then
min_dist = d
closest_sheep, closest_enemy = s, e
for _,enemy in ipairs(enemies) do
for _,single_sheep in ipairs(sheep) do
local dist = H.distance_between(enemy.x, enemy.y, single_sheep.x, single_sheep.y)
if dist < min_dist then
min_dist = dist
closest_sheep, closest_enemy = single_sheep, enemy
end
end
end
--print('Closest enemy, sheep:', closest_enemy.id, closest_sheep.id)
-- Move dogs in between enemies and sheep
max_rating, best_dog, best_hex = -9e99, {}, {}
for i,d in ipairs(dogs) do
local reach_map = AH.get_reachable_unocc(d)
for _,dog in ipairs(dogs) do
local reach_map = AH.get_reachable_unocc(dog)
reach_map:iter( function(x, y, v)
-- We want equal distance between enemy and closest sheep
local rating = - math.abs(H.distance_between(x, y, closest_sheep.x, closest_sheep.y) - H.distance_between(x, y, closest_enemy.x, closest_enemy.y)) * 100
local rating = - math.abs(
H.distance_between(x, y, closest_sheep.x, closest_sheep.y)
- H.distance_between(x, y, closest_enemy.x, closest_enemy.y)
) * 100
-- 2nd: closeness to sheep
rating = rating - H.distance_between(x, y, closest_sheep.x, closest_sheep.y)
reach_map:insert(x, y, rating)
-- 3rd: most distant dog goes first
rating = rating + H.distance_between(closest_enemy.x, closest_enemy.y, d.x, d.y) / 100.
rating = rating + H.distance_between(closest_enemy.x, closest_enemy.y, dog.x, dog.y) / 100.
reach_map:insert(x, y, rating)
if (rating > max_rating) then
max_rating = rating
best_hex = { x, y }
best_dog = d
max_rating, best_hex, best_dog = rating, { x, y }, dog
end
end)
--AH.put_labels(reach_map)
--W.message { speaker = d.id, message = 'My turn' }
end
-- Move dog to intercept
--print('Dog moving in to intercept')
AH.movefull_stopunit(ai, best_dog, best_hex)
end

View file

@ -30,7 +30,6 @@ function ca_herding_dog_move:execution(ai, cfg)
av_dist = av_dist + H.distance_between(x, y, cfg.herd_x, cfg.herd_y)
end)
av_dist = av_dist / herding_perimeter:size()
--print('Average distance:', av_dist)
local best_hex = AH.find_best_move(dog, function(x, y)
-- Prefer hexes on herding_perimeter, or close to it
@ -39,13 +38,14 @@ function ca_herding_dog_move:execution(ai, cfg)
if herding_perimeter:get(x, y) then
rating = rating + 1000 + math.random(99) / 100.
else
rating = rating - math.abs(H.distance_between(x, y, cfg.herd_x, cfg.herd_y) - av_dist) + math.random(99) / 100.
rating = rating
- math.abs(H.distance_between(x, y, cfg.herd_x, cfg.herd_y) - av_dist)
+ math.random(99) / 100.
end
return rating
end)
--print('Dog wandering')
AH.movefull_stopunit(ai, dog, best_hex)
end

View file

@ -5,14 +5,18 @@ return function(cfg)
-- Find the area that the sheep can occupy
-- First, find all contiguous hexes around center hex that are inside herding_perimeter
local herding_area = LS.of_pairs(wesnoth.get_locations {
x = cfg.herd_x, y = cfg.herd_y, radius = 999,
{"filter_radius", { { "not", cfg.filter_location } } }
} )
x = cfg.herd_x,
y = cfg.herd_y,
radius = 999,
{ "filter_radius", { { "not", cfg.filter_location } } }
})
-- Then, also exclude hexes next to herding_perimeter; some of the functions work better like that
herding_area:iter( function(x, y, v)
for xa, ya in H.adjacent_tiles(x, y) do
if (wesnoth.match_location(xa, ya, cfg.filter_location) ) then herding_area:remove(x, y) end
if (wesnoth.match_location(xa, ya, cfg.filter_location) ) then
herding_area:remove(x, y)
end
end
end)

View file

@ -15,12 +15,15 @@ local function get_sheep_to_herd(cfg)
local all_sheep = wesnoth.get_units {
side = wesnoth.current.side,
{ "and", cfg.filter_second },
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, {"and", cfg.filter} } } } }
{ "not", { { "filter_adjacent", { side = wesnoth.current.side, { "and", cfg.filter } } } } }
}
local sheep_to_herd = {}
local herding_area = herding_area(cfg)
for i,s in ipairs(all_sheep) do
if (not herding_area:get(s.x, s.y)) then table.insert(sheep_to_herd, s) end
for _,single_sheep in ipairs(all_sheep) do
if (not herding_area:get(single_sheep.x, single_sheep.y)) then
table.insert(sheep_to_herd, single_sheep)
end
end
return sheep_to_herd
end
@ -30,10 +33,9 @@ local ca_herding_herd_sheep = {}
function ca_herding_herd_sheep:evaluation(ai, cfg)
-- If dogs have moves left, and there is a sheep with moves left outside the
-- herding area, chase it back
if get_dogs(cfg)[1] then
if get_sheep_to_herd(cfg)[1] then return cfg.ca_score end
end
return 0
if (not get_dogs(cfg)[1]) then return 0 end
if (not get_sheep_to_herd(cfg)[1]) then return 0 end
return cfg.ca_score
end
function ca_herding_herd_sheep:execution(ai, cfg)
@ -42,48 +44,39 @@ function ca_herding_herd_sheep:execution(ai, cfg)
local max_rating, best_dog, best_hex = -9e99, {}, {}
local c_x, c_y = cfg.herd_x, cfg.herd_y
for i,s in ipairs(sheep_to_herd) do
-- This is the rating that depends only on the sheep's position
for _,single_sheep in ipairs(sheep_to_herd) do
-- Farthest sheep goes first
local sheep_rating = H.distance_between(c_x, c_y, s.x, s.y) / 10.
local sheep_rating = H.distance_between(c_x, c_y, single_sheep.x, single_sheep.y) / 10.
-- Sheep with no movement left gets big hit
if (s.moves == 0) then sheep_rating = sheep_rating - 100. end
if (single_sheep.moves == 0) then sheep_rating = sheep_rating - 100. end
for i,d in ipairs(dogs) do
local reach_map = AH.get_reachable_unocc(d)
for _,dog in ipairs(dogs) do
local reach_map = AH.get_reachable_unocc(dog)
reach_map:iter( function(x, y, v)
local dist = H.distance_between(x, y, s.x, s.y)
local dist = H.distance_between(x, y, single_sheep.x, single_sheep.y)
local rating = sheep_rating - dist
-- Needs to be on "far side" of sheep, wrt center for adjacent hexes
if (H.distance_between(x, y, c_x, c_y) <= H.distance_between(s.x, s.y, c_x, c_y))
if (H.distance_between(x, y, c_x, c_y) <= H.distance_between(single_sheep.x, single_sheep.y, c_x, c_y))
and (dist == 1)
then rating = rating - 1000 end
-- And the closer dog goes first (so that it might be able to chase another sheep afterward)
rating = rating - H.distance_between(x, y, d.x, d.y) / 100.
rating = rating - H.distance_between(x, y, dog.x, dog.y) / 100.
-- Finally, prefer to stay on path, if possible
if (wesnoth.match_location(x, y, cfg.filter_location) ) then rating = rating + 0.001 end
reach_map:insert(x, y, rating)
if (rating > max_rating) then
max_rating = rating
best_dog = d
best_hex = { x, y }
max_rating, best_dog, best_hex = rating, dog, { x, y }
end
end)
--AH.put_labels(reach_map)
--W.message{ speaker = d.id, message = 'My turn' }
end
end
-- Now we move the best dog
-- If it's already in the best position, we just take moves away from it
-- (to avoid black-listing of CA, in the worst case)
if (best_hex[1] == best_dog.x) and (best_hex[2] == best_dog.y) then
AH.checked_stopunit_moves(ai, best_dog)
else
--print('Dog moving to herd sheep')
AH.checked_move(ai, best_dog, best_hex[1], best_hex[2]) -- partial move only
AH.checked_move(ai, best_dog, best_hex[1], best_hex[2]) -- partial move only!
end
end

View file

@ -1,6 +1,5 @@
local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local LS = wesnoth.require "lua/location_set.lua"
local herding_area = wesnoth.require "ai/micro_ais/cas/ca_herding_f_herding_area.lua"
@ -34,13 +33,11 @@ function ca_herding_sheep_move:execution(ai, cfg)
end
end
end)
--AH.put_labels(reach_map)
-- Choose one of the possible locations at random (or the current location, if no move possible)
local x, y = sheep.x, sheep.y
if (reach_map:size() > 0) then
x, y = AH.LS_random_hex(reach_map)
--print('Sheep -> :', x, y)
end
-- If this move remains within herding area or dogs have no moves left, or sheep doesn't move

View file

@ -22,8 +22,8 @@ function ca_herding_sheep_runs_dog:execution(ai, cfg)
-- Simply get the first sheep, order does not matter
local sheep = get_next_sheep(cfg)
-- Get the first dog it is adjacent to
local dog = wesnoth.get_units { side = wesnoth.current.side, {"and", cfg.filter},
-- Get the first dog that the sheep is adjacent to
local dog = wesnoth.get_units { side = wesnoth.current.side, { "and", cfg.filter },
{ "filter_adjacent", { x = sheep.x, y = sheep.y } }
}[1]

View file

@ -25,20 +25,20 @@ function ca_herding_sheep_runs_enemy:evaluation(ai, cfg)
end
function ca_herding_sheep_runs_enemy:execution(ai, cfg)
-- Simply start with the first of the sheep, order does not matter
-- Simply start with the first sheep, order does not matter
local sheep = get_next_sheep(cfg)
-- And find the close enemies
local enemies = wesnoth.get_units {
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
{ "filter_location", { x = sheep.x, y = sheep.y , radius = (cfg.attention_distance or 8) } }
}
--print('#enemies', #enemies)
-- Maximize distance between sheep and enemies
local best_hex = AH.find_best_move(sheep, function(x, y)
local rating = 0
for i,e in ipairs(enemies) do rating = rating + H.distance_between(x, y, e.x, e.y) end
for _,enemy in ipairs(enemies) do
rating = rating + H.distance_between(x, y, enemy.x, enemy.y)
end
return rating
end)