Guardian Micro AIs: code cleanup
This commit is contained in:
parent
20eb5b27dc
commit
41687b5569
4 changed files with 96 additions and 142 deletions
|
@ -7,6 +7,7 @@ local function get_coward(cfg)
|
|||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
}[1]
|
||||
|
||||
return coward
|
||||
end
|
||||
|
||||
|
@ -17,88 +18,69 @@ function ca_coward:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
-- cfg parameters: id, distance, seek_x, seek_y, avoid_x, avoid_y
|
||||
function ca_coward:execution(ai, cfg)
|
||||
local coward = get_coward(cfg)
|
||||
local reach = wesnoth.find_reach(coward)
|
||||
|
||||
-- enemy units within reach
|
||||
local filter_second = cfg.filter_second or { { "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} } }
|
||||
local filter_second =
|
||||
cfg.filter_second
|
||||
or { { "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } } }
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "and", filter_second },
|
||||
{ "filter_location", {x = coward.x, y = coward.y, radius = cfg.distance} }
|
||||
{ "filter_location", { x = coward.x, y = coward.y, radius = cfg.distance } }
|
||||
}
|
||||
|
||||
-- if no enemies are within reach: keep unit from doing anything and exit
|
||||
-- If no enemies are close: keep unit from doing anything and exit
|
||||
if not enemies[1] then
|
||||
AH.checked_stopunit_all(ai, coward)
|
||||
return
|
||||
end
|
||||
|
||||
-- Go through all hexes the unit can reach
|
||||
for i,r in ipairs(reach) do
|
||||
|
||||
-- only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x=r[1], y=r[2], { "not", { id = coward.id } } }[1]
|
||||
-- Only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x = r[1], y = r[2], { "not", { id = coward.id } } }[1]
|
||||
if not occ_hex then
|
||||
-- Find combined distance weighting of all enemy units within distance
|
||||
local value = 0
|
||||
for j,e in ipairs(enemies) do
|
||||
local d = H.distance_between(r[1], r[2], e.x, e.y)
|
||||
value = value + 1/ d^2
|
||||
local rating = 0
|
||||
for _,e in ipairs(enemies) do
|
||||
local dist = H.distance_between(r[1], r[2], e.x, e.y)
|
||||
rating = rating - 1 / dist^2
|
||||
end
|
||||
--wesnoth.fire("label", {x=r[1], y=r[2], text = math.floor(value*1000) } )
|
||||
|
||||
-- Store this weighting in the third field of each 'reach' element
|
||||
reach[i][3] = value
|
||||
reach[i][3] = rating
|
||||
else
|
||||
reach[i][3] = 9999
|
||||
reach[i][3] = -9e99
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort 'reach' by values, smallest first
|
||||
table.sort(reach, function(a, b) return a[3] < b[3] end )
|
||||
-- Select those within factor 2 of the minimum
|
||||
local best_pos = AH.filter(reach, function(tmp) return tmp[3] < reach[1][3]*2 end)
|
||||
-- Select those within factor 2 of the maximum (note: ratings are negative)
|
||||
table.sort(reach, function(a, b) return a[3] > b[3] end )
|
||||
local best_pos = AH.filter(reach, function(tmp) return tmp[3] > reach[1][3] * 2 end)
|
||||
|
||||
-- Now take 'seek' and 'avoid' into account
|
||||
for i,b in ipairs(best_pos) do
|
||||
-- Weighting based on distance from 'seek' and 'avoid'
|
||||
local dist_seek = AH.generalized_distance(b[1], b[2], cfg.seek_x, cfg.seek_y)
|
||||
local dist_avoid = AH.generalized_distance(b[1], b[2], cfg.avoid_x, cfg.avoid_y)
|
||||
local rating = 1 / (dist_seek + 1) - 1 / (dist_avoid + 1)^2 * 0.75
|
||||
|
||||
-- weighting based on distance from 'seek' and 'avoid'
|
||||
local ds = AH.generalized_distance(b[1], b[2], cfg.seek_x, cfg.seek_y)
|
||||
local da = AH.generalized_distance(b[1], b[2], cfg.avoid_x, cfg.avoid_y)
|
||||
--items.place_image(b[1], b[2], "items/ring-red.png")
|
||||
local value = 1 / (ds+1) - 1 / (da+1)^2 * 0.75
|
||||
|
||||
--wesnoth.fire("label", {x=b[1], y=b[2], text = math.floor(value*1000) } )
|
||||
best_pos[i][3] = value
|
||||
best_pos[i][4] = rating
|
||||
end
|
||||
|
||||
-- Sort 'best_pos" by value, largest first
|
||||
table.sort(best_pos, function(a, b) return a[3] > b[3] end)
|
||||
-- and select all those that have the maximum score
|
||||
local best_overall = AH.filter(best_pos, function(tmp) return tmp[3] == best_pos[1][3] end)
|
||||
-- Select all those that have the maximum score
|
||||
table.sort(best_pos, function(a, b) return a[4] > b[4] end)
|
||||
local best_overall = AH.filter(best_pos, function(tmp) return tmp[4] == best_pos[1][4] end)
|
||||
|
||||
-- As final step, if there are more than one remaining locations,
|
||||
-- we take the one with the minimum score in the distance-from_enemy criterion
|
||||
local min, mx, my = 9999, 0, 0
|
||||
for i,b in ipairs(best_overall) do
|
||||
|
||||
--items.place_image(b[1], b[2], "items/ring-white.png")
|
||||
local value = 0
|
||||
for j,e in ipairs(enemies) do
|
||||
local d = H.distance_between(b[1], b[2], e.x, e.y)
|
||||
value = value + 1/d^2
|
||||
end
|
||||
|
||||
if value < min then
|
||||
min = value
|
||||
mx,my = b[1], b[2]
|
||||
-- we take the one with the minimum score in the distance-from-enemy criterion
|
||||
local max_rating, best_hex = -9e99
|
||||
for _,b in ipairs(best_overall) do
|
||||
if (b[3] > max_rating) then
|
||||
max_rating, best_hex = b[3], b
|
||||
end
|
||||
end
|
||||
--items.place_image(mx, my, "items/ring-gold.png")
|
||||
|
||||
AH.movefull_stopunit(ai, coward, mx, my)
|
||||
AH.movefull_stopunit(ai, coward, best_hex[1], best_hex[2])
|
||||
if (not coward) or (not coward.valid) then return end
|
||||
|
||||
AH.checked_stopunit_all(ai, coward)
|
||||
|
|
|
@ -6,6 +6,7 @@ local function get_guardian(cfg)
|
|||
side = wesnoth.current.side,
|
||||
{ "and", filter }
|
||||
}[1]
|
||||
|
||||
return guardian
|
||||
end
|
||||
|
||||
|
@ -14,12 +15,13 @@ local ca_return_guardian = {}
|
|||
function ca_return_guardian:evaluation(ai, cfg)
|
||||
local guardian = get_guardian(cfg)
|
||||
if guardian then
|
||||
if ((guardian.x ~= cfg.return_x) or (guardian.y ~= cfg.return_y)) then
|
||||
return cfg.ca_score
|
||||
else
|
||||
if (guardian.x == cfg.return_x) and (guardian.y == cfg.return_y) then
|
||||
return cfg.ca_score - 20
|
||||
else
|
||||
return cfg.ca_score
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
|
|
@ -18,97 +18,84 @@ function ca_stationed_guardian:evaluation(ai, cfg)
|
|||
end
|
||||
|
||||
function ca_stationed_guardian:execution(ai, cfg)
|
||||
-- (s_x,s_y): coordinates where guardian is stationed; tries to move here if there is nobody to attack
|
||||
-- (g_x,g_y): location that the guardian guards
|
||||
-- (s_x, s_y): coordinates where guardian is stationed; tries to move here if there is nobody to attack
|
||||
-- (g_x, g_y): location that the guardian guards
|
||||
|
||||
local guardian = get_guardian(cfg)
|
||||
|
||||
-- find if there are enemies within 'distance'
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_location", {x = guardian.x, y = guardian.y, radius = cfg.distance} }
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "filter_location", { x = guardian.x, y = guardian.y, radius = cfg.distance } }
|
||||
}
|
||||
|
||||
-- if no enemies are within 'distance': keep guardian from doing anything and exit
|
||||
-- If no enemies are within cfg.distance: keep guardian from doing anything and exit
|
||||
if not enemies[1] then
|
||||
--print("No enemies close -> sleeping:",guardian.id)
|
||||
AH.checked_stopunit_moves(ai, guardian)
|
||||
return
|
||||
end
|
||||
|
||||
-- Otherwise, guardian will either attack or move toward station
|
||||
--print("Guardian unit waking up",guardian.id)
|
||||
-- enemies must be within 'distance' of guard, (s_x,s_y) *and* (g_x,g_y)
|
||||
-- simultaneous for guard to attack
|
||||
local target = {}
|
||||
local min_dist = 9999
|
||||
for i,e in ipairs(enemies) do
|
||||
local ds = H.distance_between(cfg.station_x, cfg.station_y, e.x, e.y)
|
||||
local dg = H.distance_between(cfg.guard_x, cfg.guard_y, e.x, e.y)
|
||||
-- Enemies must be within cfg.distance of guardian, (s_x, s_y) *and* (g_x, g_y)
|
||||
-- simultaneously for guardian to attack
|
||||
local target, min_dist = {}, 9e99
|
||||
for _,e in ipairs(enemies) do
|
||||
local dist_s = H.distance_between(cfg.station_x, cfg.station_y, e.x, e.y)
|
||||
local dist_g = H.distance_between(cfg.guard_x, cfg.guard_y, e.x, e.y)
|
||||
|
||||
-- If valid target found, save the one with the shortest distance from (g_x,g_y)
|
||||
if (ds <= cfg.distance) and (dg <= cfg.distance) and (dg < min_dist) then
|
||||
--print("target:", e.id, ds, dg)
|
||||
target = e
|
||||
min_dist = dg
|
||||
-- If valid target found, save the one with the shortest distance from (g_x, g_y)
|
||||
if (dist_s <= cfg.distance) and (dist_g <= cfg.distance) and (dist_g < min_dist) then
|
||||
target, min_dist = e, dist_g
|
||||
end
|
||||
end
|
||||
|
||||
-- If a valid target was found, guardian attacks this target, or moves toward it
|
||||
if (min_dist ~= 9999) then
|
||||
--print ("Go for enemy unit:", target.id)
|
||||
|
||||
-- Find tiles adjacent to the target, and save the one that our guardian
|
||||
-- can reach with the highest defense rating
|
||||
if (min_dist < 9e99) then
|
||||
-- Find tiles adjacent to the target
|
||||
-- Save the one with the highest defense rating that guardian can reach
|
||||
local best_defense, attack_loc = -9e99, {}
|
||||
for x,y in H.adjacent_tiles(target.x, target.y) do
|
||||
-- only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x=x, y=y, { "not", { id = guardian.id } } }[1]
|
||||
-- Only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x = x, y = y, { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
-- defense rating of the hex
|
||||
local defense = 100 - wesnoth.unit_defense(guardian, wesnoth.get_terrain(x, y))
|
||||
--print(x,y,defense)
|
||||
local nh = AH.next_hop(guardian, x, y)
|
||||
-- if this is best defense rating and guardian can reach it, save this location
|
||||
if (nh[1] == x) and (nh[2] == y) and (defense > best_defense) then
|
||||
best_defense, attack_loc = defense, {x, y}
|
||||
if nh then
|
||||
if (nh[1] == x) and (nh[2] == y) and (defense > best_defense) then
|
||||
best_defense, attack_loc = defense, {x, y}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If a valid hex was found: move there and attack
|
||||
if (best_defense ~= -9e99) then
|
||||
--print("Attack at:",attack_loc[1],attack_loc[2],best_defense)
|
||||
AH.movefull_stopunit(ai, guardian, attack_loc)
|
||||
if (not guardian) or (not guardian.valid) then return end
|
||||
if (not target) or (not target.valid) then return end
|
||||
|
||||
AH.checked_attack(ai, guardian, target)
|
||||
else -- otherwise move toward that enemy
|
||||
--print("Cannot reach target, moving toward it")
|
||||
else -- Otherwise move toward that enemy
|
||||
local reach = wesnoth.find_reach(guardian)
|
||||
|
||||
-- Go through all hexes the guardian can reach, find closest to target
|
||||
local nh = {} -- cannot use next_hop here since target hex is occupied by enemy
|
||||
local min_dist = 9999
|
||||
for i,r in ipairs(reach) do
|
||||
-- only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x=r[1], y=r[2], { "not", { id = guardian.id } } }[1]
|
||||
-- Cannot use next_hop here since target hex is occupied by enemy
|
||||
local nh, min_dist = {}, 9e99
|
||||
for _,r in ipairs(reach) do
|
||||
-- Only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x = r[1], y = r[2], { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
local d = H.distance_between(r[1], r[2], target.x, target.y)
|
||||
if d < min_dist then
|
||||
min_dist = d
|
||||
nh = {r[1], r[2]}
|
||||
local dist = H.distance_between(r[1], r[2], target.x, target.y)
|
||||
if (dist < min_dist) then
|
||||
min_dist, nh = d, { r[1], r[2] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Finally, execute the move toward the target
|
||||
AH.movefull_stopunit(ai, guardian, nh)
|
||||
end
|
||||
|
||||
-- If no enemy within the target zone, move toward station position
|
||||
-- If no enemy is within the target zone, move toward station position
|
||||
else
|
||||
--print "Move toward station"
|
||||
local nh = AH.next_hop(guardian, cfg.station_x, cfg.station_y)
|
||||
AH.movefull_stopunit(ai, guardian, nh)
|
||||
end
|
||||
|
|
|
@ -21,85 +21,70 @@ end
|
|||
function ca_zone_guardian:execution(ai, cfg)
|
||||
local guardian = get_guardian(cfg)
|
||||
local reach = wesnoth.find_reach(guardian)
|
||||
local zone_enemy = cfg.filter_location_enemy or cfg.filter_location
|
||||
-- enemy units within reach
|
||||
|
||||
local zone_enemy = cfg.filter_location_enemy or cfg.filter_location
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "filter_location", zone_enemy }
|
||||
}
|
||||
if enemies[1] then
|
||||
|
||||
local target = {}
|
||||
local min_dist = 9999
|
||||
for i,e in ipairs(enemies) do
|
||||
local dg = H.distance_between(guardian.x, guardian.y, e.x, e.y)
|
||||
|
||||
-- If valid target found, save the one with the shortest distance from guardian
|
||||
if (dg < min_dist) then
|
||||
--print("target:", e.id, ds, dg)
|
||||
target = e
|
||||
min_dist = dg
|
||||
local target, min_dist = {}, 9e99
|
||||
for _,e in ipairs(enemies) do
|
||||
local dist = H.distance_between(guardian.x, guardian.y, e.x, e.y)
|
||||
if (dist < min_dist) then
|
||||
target, min_dist = e, dist
|
||||
end
|
||||
end
|
||||
|
||||
-- If a valid target was found, guardian attacks this target, or moves toward it
|
||||
if (min_dist ~= 9999) then
|
||||
--print ("Go for enemy unit:", target.id)
|
||||
|
||||
-- Find tiles adjacent to the target, and save the one that our guardian
|
||||
-- can reach with the highest defense rating
|
||||
if (min_dist < 9e99) then
|
||||
-- Find tiles adjacent to the target
|
||||
-- Save the one with the highest defense rating that guardian can reach
|
||||
local best_defense, attack_loc = -9e99, {}
|
||||
for x,y in H.adjacent_tiles(target.x, target.y) do
|
||||
-- only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x=x, y=y, { "not", { id = guardian.id } } }[1]
|
||||
-- Only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x = x, y = y, { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
-- defense rating of the hex
|
||||
local defense = 100 - wesnoth.unit_defense(guardian, wesnoth.get_terrain(x, y))
|
||||
--print(x,y,defense)
|
||||
local nh = AH.next_hop(guardian, x, y)
|
||||
-- if this is best defense rating and guardian can reach it, save this location
|
||||
if nh then
|
||||
if (nh[1] == x) and (nh[2] == y) and (defense > best_defense) then
|
||||
best_defense, attack_loc = defense, {x, y}
|
||||
best_defense, attack_loc = defense, { x, y }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If a valid hex was found: move there and attack
|
||||
if (best_defense ~= -9e99) then
|
||||
--print("Attack at:",attack_loc[1],attack_loc[2],best_defense)
|
||||
if (best_defense > -9e99) then
|
||||
AH.movefull_stopunit(ai, guardian, attack_loc)
|
||||
if (not guardian) or (not guardian.valid) then return end
|
||||
if (not target) or (not target.valid) then return end
|
||||
|
||||
AH.checked_attack(ai, guardian, target)
|
||||
else -- otherwise move toward that enemy
|
||||
--print("Cannot reach target, moving toward it")
|
||||
else -- Otherwise move toward that enemy
|
||||
local reach = wesnoth.find_reach(guardian)
|
||||
|
||||
-- Go through all hexes the guardian can reach, find closest to target
|
||||
local nh = {} -- cannot use next_hop here since target hex is occupied by enemy
|
||||
local min_dist = 9999
|
||||
for i,r in ipairs(reach) do
|
||||
-- only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x=r[1], y=r[2], { "not", { id = guardian.id } } }[1]
|
||||
-- Cannot use next_hop here since target hex is occupied by enemy
|
||||
local nh, min_dist = {}, 9e99
|
||||
for _,r in ipairs(reach) do
|
||||
-- Only consider unoccupied hexes
|
||||
local occ_hex = wesnoth.get_units { x = r[1], y = r[2], { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
local d = H.distance_between(r[1], r[2], target.x, target.y)
|
||||
if d < min_dist then
|
||||
min_dist = d
|
||||
nh = {r[1], r[2]}
|
||||
local dist = H.distance_between(r[1], r[2], target.x, target.y)
|
||||
if (dist < min_dist) then
|
||||
min_dist, nh = dist, { r[1], r[2] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Finally, execute the move toward the target
|
||||
AH.movefull_stopunit(ai, guardian, nh)
|
||||
end
|
||||
end
|
||||
|
||||
-- If no enemy around or within the zone, move toward "random" position which are mainy the borders
|
||||
-- If no enemy around or within the zone, move toward station or zone
|
||||
else
|
||||
--print "Move toward newpos"
|
||||
local newpos
|
||||
-- If cfg.station_x/y are given, move toward that location
|
||||
if cfg.station_x and cfg.station_y then
|
||||
|
@ -125,8 +110,6 @@ function ca_zone_guardian:execution(ai, cfg)
|
|||
|
||||
local locs = locs_map:to_pairs()
|
||||
|
||||
-- If possible locations were found, move guardian toward a random one,
|
||||
-- otherwise the guardian stays where it is
|
||||
if (#locs > 0) then
|
||||
local newind = math.random(#locs)
|
||||
newpos = { locs[newind][1], locs[newind][2] }
|
||||
|
|
Loading…
Add table
Reference in a new issue