Herding Micro AI: code cleanup
This commit is contained in:
parent
d063521934
commit
e65705a247
7 changed files with 64 additions and 78 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue