Merge branch 'master' of https://github.com/wesnoth/wesnoth
This commit is contained in:
commit
51d31196bc
28 changed files with 268 additions and 317 deletions
|
@ -19,38 +19,34 @@ function ca_big_animals:evaluation(ai, cfg)
|
|||
end
|
||||
|
||||
function ca_big_animals:execution(ai, cfg)
|
||||
-- Big animals just move toward goal that gets set occasionally
|
||||
-- Avoid the other big animals (bears, yetis, spiders) and the dogs, otherwise attack whatever is in their range
|
||||
-- The only difference in behavior is the area in which the units move
|
||||
-- Big animals just move toward a goal that gets (re)set occasionally
|
||||
-- and attack whatever is in their range (except for some units that they avoid)
|
||||
|
||||
local big_animals = get_big_animals(cfg)
|
||||
local avoid = LS.of_pairs(wesnoth.get_locations { radius = 1,
|
||||
local avoid_map = LS.of_pairs(wesnoth.get_locations { radius = 1,
|
||||
{ "filter", { { "and", cfg.avoid_unit },
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } }
|
||||
} }
|
||||
})
|
||||
--AH.put_labels(avoid)
|
||||
|
||||
for i,unit in ipairs(big_animals) do
|
||||
for _,unit in ipairs(big_animals) do
|
||||
local goal = MAIUV.get_mai_unit_variables(unit, cfg.ai_id)
|
||||
|
||||
-- Unit gets a new goal if none exist or on any move with 10% random chance
|
||||
-- Unit gets a new goal if none is set or on any move with a 10% random chance
|
||||
local r = math.random(10)
|
||||
if (not goal.goal_x) or (r == 1) then
|
||||
local locs = AH.get_passable_locations(cfg.filter_location or {})
|
||||
local rand = math.random(#locs)
|
||||
--print(type, ': #locs', #locs, rand)
|
||||
|
||||
goal.goal_x, goal.goal_y = locs[rand][1], locs[rand][2]
|
||||
MAIUV.set_mai_unit_variables(unit, cfg.ai_id, goal)
|
||||
end
|
||||
--print('Big animal goto: ', unit.id, goal.goal_x, goal.goal_y, r)
|
||||
|
||||
-- hexes the unit can reach
|
||||
local reach_map = AH.get_reachable_unocc(unit)
|
||||
local wander_terrain = cfg.filter_location_wander or {}
|
||||
reach_map:iter( function(x, y, v)
|
||||
-- Remove tiles that do not comform to the wander terrain filter
|
||||
if (not wesnoth.match_location(x, y, wander_terrain) ) then
|
||||
if (not wesnoth.match_location(x, y, wander_terrain)) then
|
||||
reach_map:remove(x, y)
|
||||
end
|
||||
end)
|
||||
|
@ -58,56 +54,52 @@ function ca_big_animals:execution(ai, cfg)
|
|||
-- Now find the one of these hexes that is closest to the goal
|
||||
local max_rating, best_hex = -9e99, {}
|
||||
reach_map:iter( function(x, y, v)
|
||||
-- Distance from goal is first rating
|
||||
local rating = - H.distance_between(x, y, goal.goal_x, goal.goal_y)
|
||||
|
||||
-- Proximity to an enemy unit is a plus
|
||||
local enemy_hp = 500
|
||||
for xa, ya in H.adjacent_tiles(x, y) do
|
||||
for xa,ya in H.adjacent_tiles(x, y) do
|
||||
local enemy = wesnoth.get_unit(xa, ya)
|
||||
if enemy and (enemy.side ~= wesnoth.current.side) then
|
||||
if enemy and wesnoth.is_enemy(enemy.side, wesnoth.current.side) then
|
||||
if (enemy.hitpoints < enemy_hp) then enemy_hp = enemy.hitpoints end
|
||||
end
|
||||
end
|
||||
rating = rating + 500 - enemy_hp -- prefer attack on weakest enemy
|
||||
rating = rating + 500 - enemy_hp -- Prefer attack on weakest enemy
|
||||
|
||||
-- However, hexes that enemy bears, yetis and spiders can reach get a massive negative hit
|
||||
-- meaning that they will only ever be chosen if there's no way around them
|
||||
if avoid:get(x, y) then rating = rating - 1000 end
|
||||
-- Hexes reachable by units to be be avoided get a massive negative hit
|
||||
if avoid_map:get(x, y) then rating = rating - 1000 end
|
||||
|
||||
reach_map:insert(x, y, rating)
|
||||
if (rating > max_rating) then
|
||||
max_rating, best_hex = rating, { x, y }
|
||||
end
|
||||
end)
|
||||
--print(' best_hex: ', best_hex[1], best_hex[2])
|
||||
--AH.put_labels(reach_map)
|
||||
|
||||
if (best_hex[1] ~= unit.x) or (best_hex[2] ~= unit.y) then
|
||||
AH.checked_move(ai, unit, best_hex[1], best_hex[2]) -- partial move only
|
||||
AH.checked_move(ai, unit, best_hex[1], best_hex[2]) -- Partial move only
|
||||
if (not unit) or (not unit.valid) then return end
|
||||
else -- If animal did not move, we need to stop it (also delete the goal)
|
||||
else -- If unit did not move, we need to stop it (also delete the goal)
|
||||
AH.checked_stopunit_moves(ai, unit)
|
||||
if (not unit) or (not unit.valid) then return end
|
||||
MAIUV.delete_mai_unit_variables(unit, cfg.ai_id)
|
||||
end
|
||||
|
||||
-- Or if this gets the unit to the goal, we also delete the goal
|
||||
-- If this gets the unit to the goal, we also delete the goal
|
||||
if (unit.x == goal.goal_x) and (unit.y == goal.goal_y) then
|
||||
MAIUV.delete_mai_unit_variables(unit, cfg.ai_id)
|
||||
end
|
||||
|
||||
-- Finally, if the unit ended up next to enemies, attack the weakest of those
|
||||
local min_hp, target = 9e99, {}
|
||||
for x, y in H.adjacent_tiles(unit.x, unit.y) do
|
||||
local enemy = wesnoth.get_unit(x, y)
|
||||
if enemy and (enemy.side ~= wesnoth.current.side) then
|
||||
local min_hp, target = 9e99
|
||||
for xa,ya in H.adjacent_tiles(unit.x, unit.y) do
|
||||
local enemy = wesnoth.get_unit(xa, ya)
|
||||
if enemy and wesnoth.is_enemy(enemy.side, wesnoth.current.side) then
|
||||
if (enemy.hitpoints < min_hp) then
|
||||
min_hp, target = enemy.hitpoints, enemy
|
||||
end
|
||||
end
|
||||
end
|
||||
if target.id then
|
||||
|
||||
if target then
|
||||
AH.checked_attack(ai, unit, target)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,13 +36,13 @@ function ca_coward:execution(ai, cfg)
|
|||
return
|
||||
end
|
||||
|
||||
for i,r in ipairs(reach) do
|
||||
for i,hex 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]
|
||||
local occ_hex = wesnoth.get_units { x = hex[1], y = hex[2], { "not", { id = coward.id } } }[1]
|
||||
if not occ_hex then
|
||||
local rating = 0
|
||||
for _,e in ipairs(enemies) do
|
||||
local dist = H.distance_between(r[1], r[2], e.x, e.y)
|
||||
for _,enemy in ipairs(enemies) do
|
||||
local dist = H.distance_between(hex[1], hex[2], enemy.x, enemy.y)
|
||||
rating = rating - 1 / dist^2
|
||||
end
|
||||
|
||||
|
@ -58,10 +58,10 @@ function ca_coward:execution(ai, cfg)
|
|||
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
|
||||
for i,pos 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 dist_seek = AH.generalized_distance(pos[1], pos[2], cfg.seek_x, cfg.seek_y)
|
||||
local dist_avoid = AH.generalized_distance(pos[1], pos[2], cfg.avoid_x, cfg.avoid_y)
|
||||
local rating = 1 / (dist_seek + 1) - 1 / (dist_avoid + 1)^2 * 0.75
|
||||
|
||||
best_pos[i][4] = rating
|
||||
|
@ -74,9 +74,9 @@ function ca_coward:execution(ai, cfg)
|
|||
-- 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 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
|
||||
for _,pos in ipairs(best_overall) do
|
||||
if (pos[3] > max_rating) then
|
||||
max_rating, best_hex = pos[3], pos
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
|
||||
local function get_forest_animals(cfg)
|
||||
-- We want the deer/rabbits to move first, tuskers later
|
||||
-- We want the deer/rabbits to move first, tuskers afterward
|
||||
local deer_type = cfg.deer_type or "no_unit_of_this_type"
|
||||
local rabbit_type = cfg.rabbit_type or "no_unit_of_this_type"
|
||||
local forest_animals = AH.get_units_with_moves {
|
||||
|
@ -14,16 +14,16 @@ local function get_forest_animals(cfg)
|
|||
|
||||
local tusker_type = cfg.tusker_type or "no_unit_of_this_type"
|
||||
local all_tuskers = wesnoth.get_units { side = wesnoth.current.side, type = tusker_type }
|
||||
for i,t in ipairs(all_tuskers) do
|
||||
if (t.moves > 0) then table.insert(forest_animals, t) end
|
||||
for _,tusker in ipairs(all_tuskers) do
|
||||
if (tusker.moves > 0) then table.insert(forest_animals, tusker) end
|
||||
end
|
||||
|
||||
-- Tusklets get moved by this CA if there are no tuskers left
|
||||
if not all_tuskers[1] then
|
||||
local tusklet_type = cfg.tusklet_type or "no_unit_of_this_type"
|
||||
local tusklets = wesnoth.get_units { side = wesnoth.current.side, type = tusklet_type }
|
||||
for i,t in ipairs(tusklets) do
|
||||
if (t.moves > 0) then table.insert(forest_animals, t) end
|
||||
for _,tusklet in ipairs(tusklets) do
|
||||
if (tusklet.moves > 0) then table.insert(forest_animals, tusklet) end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,10 +38,9 @@ function ca_forest_animals_move:evaluation(ai, cfg)
|
|||
end
|
||||
|
||||
function ca_forest_animals_move:execution(ai, cfg)
|
||||
local forest_animals = get_forest_animals(cfg)
|
||||
|
||||
-- These animals run from any enemy
|
||||
local enemies = wesnoth.get_units { { "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} } }
|
||||
local forest_animals = get_forest_animals(cfg)
|
||||
local enemies = wesnoth.get_units { { "filter_side", { { "enemy_of", {side = wesnoth.current.side } } } } }
|
||||
|
||||
-- Get the locations of all the rabbit holes
|
||||
W.store_items { variable = 'holes_wml' }
|
||||
|
@ -49,73 +48,64 @@ function ca_forest_animals_move:execution(ai, cfg)
|
|||
W.clear_variable { name = 'holes_wml' }
|
||||
|
||||
-- If cfg.rabbit_hole_img is set, only items with that image or halo count as holes
|
||||
local holes = {}
|
||||
for _, item in ipairs(all_items) do
|
||||
if cfg.rabbit_hole_img then
|
||||
local holes
|
||||
if cfg.rabbit_hole_img then
|
||||
for _,item in ipairs(all_items) do
|
||||
if (item.image == cfg.rabbit_hole_img) or (item.halo == cfg.rabbit_hole_img) then
|
||||
table.insert(holes, item)
|
||||
end
|
||||
else
|
||||
table.insert(holes, item)
|
||||
end
|
||||
else
|
||||
holes = all_items
|
||||
end
|
||||
|
||||
local hole_map = LS.create()
|
||||
for i,h in ipairs(holes) do hole_map:insert(h.x, h.y, 1) end
|
||||
--AH.put_labels(hole_map)
|
||||
for _,hole in ipairs(holes) do hole_map:insert(hole.x, hole.y, 1) end
|
||||
|
||||
-- Each unit moves independently
|
||||
for i,unit in ipairs(forest_animals) do
|
||||
--print('Unit', i, unit.x, unit.y)
|
||||
for _,unit in ipairs(forest_animals) do
|
||||
-- Behavior is different depending on whether a predator is close or not
|
||||
local close_enemies = {}
|
||||
for j,e in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, e.x, e.y) <= unit.max_moves+1) then
|
||||
table.insert(close_enemies, e)
|
||||
for _,enemy in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, enemy.x, enemy.y) <= unit.max_moves+1) then
|
||||
table.insert(close_enemies, enemy)
|
||||
end
|
||||
end
|
||||
--print(' #close_enemies', #close_enemies)
|
||||
|
||||
-- If no close enemies, do a random move
|
||||
local wander_terrain = cfg.filter_location or {}
|
||||
if (not close_enemies[1]) then
|
||||
-- All hexes the unit can reach that are unoccupied
|
||||
local reach = AH.get_reachable_unocc(unit)
|
||||
local locs = wesnoth.get_locations(wander_terrain)
|
||||
local locs_map = LS.of_pairs(locs)
|
||||
--print(' #all reachable', reach:size())
|
||||
local wander_locs = wesnoth.get_locations(wander_terrain)
|
||||
local locs_map = LS.of_pairs(wander_locs)
|
||||
|
||||
-- Select only those that satisfy wander_terrain
|
||||
local reachable_terrain = {}
|
||||
local reachable_wander_terrain = {}
|
||||
reach:iter( function(x, y, v)
|
||||
local terrain = wesnoth.get_terrain(x,y)
|
||||
--print(x, y, terrain)
|
||||
if locs_map:get(x,y) then -- doesn't work with '^', so start search at char 2
|
||||
table.insert(reachable_terrain, {x, y})
|
||||
if locs_map:get(x,y) then
|
||||
table.insert(reachable_wander_terrain, {x, y})
|
||||
end
|
||||
end)
|
||||
--print(' #reachable_terrain', #reachable_terrain)
|
||||
|
||||
-- Choose one of the possible locations at random
|
||||
if reachable_terrain[1] then
|
||||
local rand = math.random(#reachable_terrain)
|
||||
if reachable_wander_terrain[1] then
|
||||
local rand = math.random(#reachable_wander_terrain)
|
||||
-- This is not a full move, as running away might happen next
|
||||
if (unit.x ~= reachable_terrain[rand][1]) or (unit.y ~= reachable_terrain[rand][2]) then
|
||||
AH.checked_move(ai, unit, reachable_terrain[rand][1], reachable_terrain[rand][2])
|
||||
if (unit.x ~= reachable_wander_terrain[rand][1]) or (unit.y ~= reachable_wander_terrain[rand][2]) then
|
||||
AH.checked_move(ai, unit, reachable_wander_terrain[rand][1], reachable_wander_terrain[rand][2])
|
||||
end
|
||||
else -- or if no close reachable terrain was found, move toward the closest
|
||||
local locs = wesnoth.get_locations(wander_terrain)
|
||||
else -- Or if no close reachable terrain was found, move toward the closest
|
||||
local best_hex, min_dist = {}, 9e99
|
||||
for j,l in ipairs(locs) do
|
||||
local d = H.distance_between(l[1], l[2], unit.x, unit.y)
|
||||
if d < min_dist then
|
||||
best_hex, min_dist = l,d
|
||||
for _,loc in ipairs(wander_locs) do
|
||||
local dist = H.distance_between(loc[1], loc[2], unit.x, unit.y)
|
||||
if dist < min_dist then
|
||||
best_hex, min_dist = loc, dist
|
||||
end
|
||||
end
|
||||
|
||||
if (best_hex[1]) then
|
||||
local x,y = wesnoth.find_vacant_tile(best_hex[1], best_hex[2], unit)
|
||||
local next_hop = AH.next_hop(unit, x, y)
|
||||
--print(next_hop[1], next_hop[2])
|
||||
|
||||
if (unit.x ~= next_hop[1]) or (unit.y ~= next_hop[2]) then
|
||||
AH.checked_move(ai, unit, next_hop[1], next_hop[2])
|
||||
end
|
||||
|
@ -125,17 +115,13 @@ function ca_forest_animals_move:execution(ai, cfg)
|
|||
|
||||
-- Now we check for close enemies again, as we might just have moved within reach of some
|
||||
local close_enemies = {}
|
||||
|
||||
-- We use a trick here to exclude the case when the unit might have been
|
||||
-- removed in an event above
|
||||
if unit and unit.valid then
|
||||
for j,e in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, e.x, e.y) <= unit.max_moves+1) then
|
||||
table.insert(close_enemies, e)
|
||||
for _,enemy in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, enemy.x, enemy.y) <= unit.max_moves+1) then
|
||||
table.insert(close_enemies, enemy)
|
||||
end
|
||||
end
|
||||
end
|
||||
--print(' #close_enemies after move', #close_enemies, #enemies, unit.id)
|
||||
|
||||
-- If there are close enemies, run away (and rabbits disappear into holes)
|
||||
local rabbit_type = cfg.rabbit_type or "no_unit_of_this_type"
|
||||
|
@ -144,33 +130,34 @@ function ca_forest_animals_move:execution(ai, cfg)
|
|||
-- Returns nil if the only hex that can be reached is the one the unit is on
|
||||
local farthest_hex = AH.find_best_move(unit, function(x, y)
|
||||
local rating = 0
|
||||
for i,e in ipairs(close_enemies) do
|
||||
local d = H.distance_between(e.x, e.y, x, y)
|
||||
rating = rating - 1 / d^2
|
||||
for _,enemy in ipairs(close_enemies) do
|
||||
local dist = H.distance_between(enemy.x, enemy.y, x, y)
|
||||
rating = rating - 1 / dist^2
|
||||
end
|
||||
|
||||
-- If this is a rabbit, try to go for holes
|
||||
if (unit.type == rabbit_type) and hole_map:get(x, y) then
|
||||
rating = rating + 1000
|
||||
-- but if possible, go to another hole
|
||||
-- But if possible, go to another hole if unit is on one
|
||||
if (x == unit.x) and (y == unit.y) then rating = rating - 10 end
|
||||
end
|
||||
|
||||
return rating
|
||||
end)
|
||||
--print(' farthest_hex: ', farthest_hex[1], farthest_hex[2])
|
||||
|
||||
-- This will always find at least the hex the unit is on
|
||||
-- so no check is necessary
|
||||
AH.movefull_stopunit(ai, unit, farthest_hex)
|
||||
|
||||
-- If this is a rabbit ending on a hole -> disappears
|
||||
if (unit.type == rabbit_type) and hole_map:get(farthest_hex[1], farthest_hex[2]) then
|
||||
if unit and unit.valid
|
||||
and (unit.type == rabbit_type) and hole_map:get(farthest_hex[1], farthest_hex[2])
|
||||
then
|
||||
local command = "wesnoth.put_unit(x1, y1)"
|
||||
ai.synced_command(command, farthest_hex[1], farthest_hex[2])
|
||||
end
|
||||
end
|
||||
|
||||
-- Finally, take moves away, as only partial move might have been done
|
||||
-- Also attacks, as these units never attack
|
||||
-- Also take attacks away, as these units never attack
|
||||
if unit and unit.valid then AH.checked_stopunit_all(ai, unit) end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,9 +5,8 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
local ca_forest_animals_new_rabbit = {}
|
||||
|
||||
function ca_forest_animals_new_rabbit:evaluation(ai, cfg)
|
||||
-- Put new rabbits out the if there are fewer than cfg.rabbit_number
|
||||
-- but only if cfg.rabbit_type is set, otherwise do nothing
|
||||
-- If this gets executed, we'll let the CA black-list itself
|
||||
-- Put new rabbits on map if there are fewer than cfg.rabbit_number
|
||||
-- To end this, we'll let the CA black-list itself
|
||||
|
||||
if (not cfg.rabbit_type) then return 0 end
|
||||
return cfg.ca_score
|
||||
|
@ -25,11 +24,12 @@ function ca_forest_animals_new_rabbit:execution(ai, cfg)
|
|||
-- Eliminate all holes that have an enemy within 'rabbit_enemy_distance' hexes
|
||||
-- We also add a random number to the ones we keep, for selection of the holes later
|
||||
local holes = {}
|
||||
for _, item in ipairs(all_items) do
|
||||
for _,item in ipairs(all_items) do
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "filter_location", { x = item.x, y = item.y, radius = rabbit_enemy_distance } }
|
||||
}
|
||||
|
||||
if (not enemies[1]) then
|
||||
-- If cfg.rabbit_hole_img is set, only items with that image or halo count as holes
|
||||
if cfg.rabbit_hole_img then
|
||||
|
@ -46,13 +46,10 @@ function ca_forest_animals_new_rabbit:execution(ai, cfg)
|
|||
table.sort(holes, function(a, b) return a.random > b.random end)
|
||||
|
||||
local rabbits = wesnoth.get_units { side = wesnoth.current.side, type = cfg.rabbit_type }
|
||||
--print('total number:', number)
|
||||
number = number - #rabbits
|
||||
--print('to add number:', number)
|
||||
number = math.min(number, #holes)
|
||||
--print('to add number possible:', number)
|
||||
|
||||
-- Now we just can take the first 'number' (randomized) holes
|
||||
-- Now we simply take the first 'number' (randomized) holes
|
||||
local tmp_unit = wesnoth.get_units { side = wesnoth.current.side }[1]
|
||||
for i = 1,number do
|
||||
local x, y = -1, -1
|
||||
|
@ -66,7 +63,7 @@ function ca_forest_animals_new_rabbit:execution(ai, cfg)
|
|||
.. wesnoth.current.side
|
||||
.. ", type = '"
|
||||
.. cfg.rabbit_type
|
||||
.. "' } )"
|
||||
.. "' })"
|
||||
ai.synced_command(command, x, y)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,9 +22,7 @@ local ca_forest_animals_tusker_attack = {}
|
|||
function ca_forest_animals_tusker_attack:evaluation(ai, cfg)
|
||||
-- Check whether there is an enemy next to a tusklet and attack it ("protective parents" AI)
|
||||
|
||||
-- Both cfg.tusker_type and cfg.tusklet_type need to be set for this to kick in
|
||||
if (not cfg.tusker_type) or (not cfg.tusklet_type) then return 0 end
|
||||
|
||||
if (not get_tuskers(cfg)[1]) then return 0 end
|
||||
if (not get_adjacent_enemies(cfg)[1]) then return 0 end
|
||||
return cfg.ca_score
|
||||
|
@ -35,37 +33,37 @@ function ca_forest_animals_tusker_attack:execution(ai, cfg)
|
|||
local adjacent_enemies = get_adjacent_enemies(cfg)
|
||||
|
||||
-- Find the closest enemy to any tusker
|
||||
local min_dist, attacker, target = 9e99, {}, {}
|
||||
for i,t in ipairs(tuskers) do
|
||||
for j,e in ipairs(adjacent_enemies) do
|
||||
local dist = H.distance_between(t.x, t.y, e.x, e.y)
|
||||
local min_dist, attacker, target = 9e99
|
||||
for _,tusker in ipairs(tuskers) do
|
||||
for _,enemy in ipairs(adjacent_enemies) do
|
||||
local dist = H.distance_between(tusker.x, tusker.y, enemy.x, enemy.y)
|
||||
if (dist < min_dist) then
|
||||
min_dist, attacker, target = dist, t, e
|
||||
min_dist, attacker, target = dist, tusker, enemy
|
||||
end
|
||||
end
|
||||
end
|
||||
--print(attacker.id, target.id)
|
||||
|
||||
-- The tusker moves as close to enemy as possible
|
||||
-- Closeness to tusklets is secondary criterion
|
||||
local adj_tusklets = wesnoth.get_units { side = wesnoth.current.side, type = cfg.tusklet_type,
|
||||
local adj_tusklets = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
type = cfg.tusklet_type,
|
||||
{ "filter_adjacent", { id = target.id } }
|
||||
}
|
||||
|
||||
local best_hex = AH.find_best_move(attacker, function(x, y)
|
||||
local rating = - H.distance_between(x, y, target.x, target.y)
|
||||
for i,t in ipairs(adj_tusklets) do
|
||||
if (H.distance_between(x, y, t.x, t.y) == 1) then rating = rating + 0.1 end
|
||||
for _,tusklet in ipairs(adj_tusklets) do
|
||||
if (H.distance_between(x, y, tusklet.x, tusklet.y) == 1) then rating = rating + 0.1 end
|
||||
end
|
||||
|
||||
return rating
|
||||
end)
|
||||
--print('attacker', attacker.x, attacker.y, ' -> ', best_hex[1], best_hex[2])
|
||||
|
||||
AH.movefull_stopunit(ai, attacker, best_hex)
|
||||
if (not attacker) or (not attacker.valid) then return end
|
||||
if (not target) or (not target.valid) then return end
|
||||
|
||||
-- If adjacent, attack
|
||||
local dist = H.distance_between(attacker.x, attacker.y, target.x, target.y)
|
||||
if (dist == 1) then
|
||||
AH.checked_attack(ai, attacker, target)
|
||||
|
|
|
@ -21,11 +21,9 @@ local ca_forest_animals_tusklet_move = {}
|
|||
|
||||
function ca_forest_animals_tusklet_move:evaluation(ai, cfg)
|
||||
-- Tusklets will simply move toward the closest tusker, without regard for anything else
|
||||
-- Except if no tuskers are left, in which case the previous CA takes over and does a random move
|
||||
-- Except if no tuskers are left, in which case ca_forest_animals_move takes over and does a random move
|
||||
|
||||
-- Both cfg.tusker_type and cfg.tusklet_type need to be set for this to kick in
|
||||
if (not cfg.tusker_type) or (not cfg.tusklet_type) then return 0 end
|
||||
|
||||
if (not get_tusklets(cfg)[1]) then return 0 end
|
||||
if (not get_tuskers(cfg)[1]) then return 0 end
|
||||
return cfg.ca_score
|
||||
|
@ -35,22 +33,19 @@ function ca_forest_animals_tusklet_move:execution(ai, cfg)
|
|||
local tusklets = get_tusklets(cfg)
|
||||
local tuskers = get_tuskers(cfg)
|
||||
|
||||
for i,tusklet in ipairs(tusklets) do
|
||||
-- find closest tusker
|
||||
local goto_tusker, min_dist = {}, 9999
|
||||
for i,t in ipairs(tuskers) do
|
||||
local dist = H.distance_between(t.x, t.y, tusklet.x, tusklet.y)
|
||||
for _,tusklet in ipairs(tusklets) do
|
||||
local goto_tusker, min_dist = {}, 9e99
|
||||
for _,tusker in ipairs(tuskers) do
|
||||
local dist = H.distance_between(tusker.x, tusker.y, tusklet.x, tusklet.y)
|
||||
if (dist < min_dist) then
|
||||
min_dist, goto_tusker = dist, t
|
||||
min_dist, goto_tusker = dist, tusker
|
||||
end
|
||||
end
|
||||
--print('closets tusker:', goto_tusker.x, goto_tusker.y, goto_tusker.id)
|
||||
|
||||
-- Move tusklet toward that tusker
|
||||
local best_hex = AH.find_best_move(tusklet, function(x, y)
|
||||
return -H.distance_between(x, y, goto_tusker.x, goto_tusker.y)
|
||||
return - H.distance_between(x, y, goto_tusker.x, goto_tusker.y)
|
||||
end)
|
||||
--print('tusklet', tusklet.x, tusklet.y, ' -> ', best_hex[1], best_hex[2])
|
||||
|
||||
AH.movefull_stopunit(ai, tusklet, best_hex)
|
||||
|
||||
-- Also make sure tusklets never attack
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ function ca_patrol:execution(ai, cfg)
|
|||
MAIUV.set_mai_unit_variables(patrol, cfg.ai_id, patrol_vars)
|
||||
|
||||
local tmp_wp = {}
|
||||
for j,wp in ipairs(waypoints) do tmp_wp[n_wp-j+1] = wp end
|
||||
for j,wp2 in ipairs(waypoints) do tmp_wp[n_wp-j+1] = wp2 end
|
||||
waypoints = tmp_wp
|
||||
else
|
||||
patrol_vars.patrol_x = waypoints[1][1]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
local AH = wesnoth.require("ai/lua/ai_helper.lua")
|
||||
|
||||
local internal_recruit_cas = {}
|
||||
local internal_params = {}
|
||||
|
||||
-- The following external engine creates the CA functions recruit_rushers_eval and recruit_rushers_exec
|
||||
-- It also exposes find_best_recruit and find_best_recruit_hex for use by other recruit engines
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ function ca_simple_attack:evaluation(ai, cfg, self)
|
|||
if (not enemies[1]) then return 0 end
|
||||
|
||||
enemy_map = LS.create()
|
||||
for _,e in ipairs(enemies) do enemy_map:insert(e.x, e.y) end
|
||||
for _,enemy in ipairs(enemies) do enemy_map:insert(enemy.x, enemy.y) end
|
||||
end
|
||||
|
||||
-- Now find the best of the possible attacks
|
||||
|
|
|
@ -38,13 +38,13 @@ function ca_stationed_guardian:execution(ai, cfg)
|
|||
-- 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)
|
||||
for _,enemy in ipairs(enemies) do
|
||||
local dist_s = H.distance_between(cfg.station_x, cfg.station_y, enemy.x, enemy.y)
|
||||
local dist_g = H.distance_between(cfg.guard_x, cfg.guard_y, enemy.x, enemy.y)
|
||||
|
||||
-- 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
|
||||
target, min_dist = enemy, dist_g
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -53,15 +53,15 @@ function ca_stationed_guardian:execution(ai, cfg)
|
|||
-- 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
|
||||
for xa,ya 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]
|
||||
local occ_hex = wesnoth.get_units { x = xa, y = ya, { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
local defense = 100 - wesnoth.unit_defense(guardian, wesnoth.get_terrain(x, y))
|
||||
local nh = AH.next_hop(guardian, x, y)
|
||||
local defense = 100 - wesnoth.unit_defense(guardian, wesnoth.get_terrain(xa, ya))
|
||||
local nh = AH.next_hop(guardian, xa, ya)
|
||||
if nh then
|
||||
if (nh[1] == x) and (nh[2] == y) and (defense > best_defense) then
|
||||
best_defense, attack_loc = defense, {x, y}
|
||||
if (nh[1] == xa) and (nh[2] == ya) and (defense > best_defense) then
|
||||
best_defense, attack_loc = defense, { xa, ya }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -80,13 +80,13 @@ function ca_stationed_guardian:execution(ai, cfg)
|
|||
-- Go through all hexes the guardian can reach, find closest to target
|
||||
-- Cannot use next_hop here since target hex is occupied by enemy
|
||||
local nh, min_dist = {}, 9e99
|
||||
for _,r in ipairs(reach) do
|
||||
for _,hex 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]
|
||||
local occ_hex = wesnoth.get_units { x = hex[1], y = hex[2], { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
local dist = H.distance_between(r[1], r[2], target.x, target.y)
|
||||
local dist = H.distance_between(hex[1], hex[2], target.x, target.y)
|
||||
if (dist < min_dist) then
|
||||
min_dist, nh = d, { r[1], r[2] }
|
||||
min_dist, nh = dist, { hex[1], hex[2] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,8 +5,8 @@ local ca_swarm_move = {}
|
|||
|
||||
function ca_swarm_move:evaluation(ai, cfg)
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for _,u in ipairs(units) do
|
||||
if (u.moves > 0) then return cfg.ca_score end
|
||||
for _,unit in ipairs(units) do
|
||||
if (unit.moves > 0) then return cfg.ca_score end
|
||||
end
|
||||
|
||||
return 0
|
||||
|
@ -19,11 +19,11 @@ function ca_swarm_move:execution(ai, cfg)
|
|||
-- If no close enemies, swarm will move semi-randomly, staying close together, but away from enemies
|
||||
local all_units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
local units, units_no_moves = {}, {}
|
||||
for _,u in ipairs(all_units) do
|
||||
if (u.moves > 0) then
|
||||
table.insert(units, u)
|
||||
for _,unit in ipairs(all_units) do
|
||||
if (unit.moves > 0) then
|
||||
table.insert(units, unit)
|
||||
else
|
||||
table.insert(units_no_moves, u)
|
||||
table.insert(units_no_moves, unit)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -40,9 +40,9 @@ function ca_swarm_move:execution(ai, cfg)
|
|||
|
||||
-- Only units within 'vision_distance' count for rejoining
|
||||
local close_units_no_moves = {}
|
||||
for _,u in ipairs(units_no_moves) do
|
||||
if (H.distance_between(unit.x, unit.y, u.x, u.y) <= vision_distance) then
|
||||
table.insert(close_units_no_moves, u)
|
||||
for _,unit_noMP in ipairs(units_no_moves) do
|
||||
if (H.distance_between(unit.x, unit.y, unit_noMP.x, unit_noMP.y) <= vision_distance) then
|
||||
table.insert(close_units_no_moves, unit_noMP)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -50,14 +50,14 @@ function ca_swarm_move:execution(ai, cfg)
|
|||
if (not close_units_no_moves[1]) then
|
||||
rating = rating + H.distance_between(x, y, unit.x, unit.y)
|
||||
else -- Otherwise, minimize distance from units that have already moved
|
||||
for _,u in ipairs(close_units_no_moves) do
|
||||
rating = rating - H.distance_between(x, y, u.x, u.y)
|
||||
for _,close_unit in ipairs(close_units_no_moves) do
|
||||
rating = rating - H.distance_between(x, y, close_unit.x, close_unit.y)
|
||||
end
|
||||
end
|
||||
|
||||
-- We also try to stay out of attack range of any enemy
|
||||
for _,e in ipairs(enemies) do
|
||||
local dist = H.distance_between(x, y, e.x, e.y)
|
||||
for _,enemy in ipairs(enemies) do
|
||||
local dist = H.distance_between(x, y, enemy.x, enemy.y)
|
||||
if (dist < enemy_distance) then
|
||||
rating = rating - (enemy_distance - dist) * 10.
|
||||
end
|
||||
|
|
|
@ -33,17 +33,17 @@ function ca_swarm_scatter:execution(ai, cfg)
|
|||
-- but only for units that are within 'vision_distance' of one of those enemies
|
||||
for _,unit in ipairs(units) do
|
||||
local unit_enemies = {}
|
||||
for _,e in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, e.x, e.y) <= vision_distance) then
|
||||
table.insert(unit_enemies, e)
|
||||
for _,enemy in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, enemy.x, enemy.y) <= vision_distance) then
|
||||
table.insert(unit_enemies, enemy)
|
||||
end
|
||||
end
|
||||
|
||||
if unit_enemies[1] then
|
||||
local best_hex = AH.find_best_move(unit, function(x, y)
|
||||
local rating = 0
|
||||
for _,e in ipairs(unit_enemies) do
|
||||
rating = rating + H.distance_between(x, y, e.x, e.y)
|
||||
for _,enemy in ipairs(unit_enemies) do
|
||||
rating = rating + H.distance_between(x, y, enemy.x, enemy.y)
|
||||
end
|
||||
return rating
|
||||
end)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
|
||||
local function get_wolves(cfg)
|
||||
local wolves = AH.get_units_with_moves {
|
||||
|
@ -31,50 +30,44 @@ function ca_wolves_move:execution(ai, cfg)
|
|||
local wolves = get_wolves(cfg)
|
||||
local prey = get_prey(cfg)
|
||||
|
||||
-- When wandering (later) they avoid dogs, but not here
|
||||
local avoid_units = wesnoth.get_units { type = cfg.avoid_type,
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } }
|
||||
}
|
||||
--print('#avoid_units', #avoid_units)
|
||||
-- negative hit for hexes these types of units can attack
|
||||
local avoid = BC.get_attack_map(avoid_units).units -- max_moves=true is always set for enemy units
|
||||
local avoid_map = BC.get_attack_map(avoid_units).units
|
||||
|
||||
-- Find prey that is closest to all 3 wolves
|
||||
local target, min_dist = {}, 9999
|
||||
for i,p in ipairs(prey) do
|
||||
-- Find prey that is closest to the wolves
|
||||
local target, min_dist = {}, 9e99
|
||||
for _,prey_unit in ipairs(prey) do
|
||||
local dist = 0
|
||||
for j,w in ipairs(wolves) do
|
||||
dist = dist + H.distance_between(w.x, w.y, p.x, p.y)
|
||||
for _,wolf in ipairs(wolves) do
|
||||
dist = dist + H.distance_between(wolf.x, wolf.y, prey_unit.x, prey_unit.y)
|
||||
end
|
||||
if (dist < min_dist) then
|
||||
min_dist, target = dist, p
|
||||
min_dist, target = dist, prey_unit
|
||||
end
|
||||
end
|
||||
--print('target:', target.x, target.y, target.id)
|
||||
|
||||
-- Now sort wolf from furthest to closest
|
||||
-- Now sort wolf from farthest to closest
|
||||
table.sort(wolves, function(a, b)
|
||||
return H.distance_between(a.x, a.y, target.x, target.y) > H.distance_between(b.x, b.y, target.x, target.y)
|
||||
end)
|
||||
|
||||
-- First wolf moves toward target, but tries to stay away from map edges
|
||||
local w,h,b = wesnoth.get_map_size()
|
||||
local width, height = wesnoth.get_map_size()
|
||||
local wolf1 = AH.find_best_move(wolves[1], function(x, y)
|
||||
local d_1t = H.distance_between(x, y, target.x, target.y)
|
||||
local rating = -d_1t
|
||||
if x <= 5 then rating = rating - (6 - x) / 1.4 end
|
||||
if y <= 5 then rating = rating - (6 - y) / 1.4 end
|
||||
if (w - x) <= 5 then rating = rating - (6 - (w - x)) / 1.4 end
|
||||
if (h - y) <= 5 then rating = rating - (6 - (h - y)) / 1.4 end
|
||||
local dist_1t = H.distance_between(x, y, target.x, target.y)
|
||||
local rating = - dist_1t
|
||||
if (x <= 5) then rating = rating - (6 - x) / 1.4 end
|
||||
if (y <= 5) then rating = rating - (6 - y) / 1.4 end
|
||||
if (width - x <= 5) then rating = rating - (6 - (width - x)) / 1.4 end
|
||||
if (height - y <= 5) then rating = rating - (6 - (height - y)) / 1.4 end
|
||||
|
||||
-- Hexes that avoid_type units can reach get a massive negative hit
|
||||
-- meaning that they will only ever be chosen if there's no way around them
|
||||
if avoid:get(x, y) then rating = rating - 1000 end
|
||||
-- Hexes that avoid_type units can reach get a massive penalty
|
||||
if avoid_map:get(x, y) then rating = rating - 1000 end
|
||||
|
||||
return rating
|
||||
end)
|
||||
--print('wolf 1 ->', wolves[1].x, wolves[1].y, wolf1[1], wolf1[2])
|
||||
--W.message { speaker = wolves[1].id, message = "Me first"}
|
||||
|
||||
AH.movefull_stopunit(ai, wolves[1], wolf1)
|
||||
|
||||
for i = 2,#wolves do
|
||||
|
@ -89,13 +82,12 @@ function ca_wolves_move:execution(ai, cfg)
|
|||
end
|
||||
|
||||
-- Same distance from Wolf 1 and target for all the wolves
|
||||
local dst_t = H.distance_between(x, y, target.x, target.y)
|
||||
local dst_1t = H.distance_between(wolf1[1], wolf1[2], target.x, target.y)
|
||||
rating = rating - (dst_t - dst_1t)^2
|
||||
local dist_t = H.distance_between(x, y, target.x, target.y)
|
||||
local dist_1t = H.distance_between(wolf1[1], wolf1[2], target.x, target.y)
|
||||
rating = rating - (dist_t - dist_1t)^2
|
||||
|
||||
-- Hexes that avoid_type units can reach get a massive negative hit
|
||||
-- meaning that they will only ever be chosen if there's no way around them
|
||||
if avoid:get(x, y) then rating = rating - 1000 end
|
||||
-- Hexes that avoid_type units can reach get a massive penalty
|
||||
if avoid_map:get(x, y) then rating = rating - 1000 end
|
||||
|
||||
return rating
|
||||
end)
|
||||
|
|
|
@ -24,23 +24,20 @@ function ca_wolves_wander:execution(ai, cfg)
|
|||
|
||||
-- Number of wolves that can reach each hex
|
||||
local reach_map = LS.create()
|
||||
for i,w in ipairs(wolves) do
|
||||
local r = AH.get_reachable_unocc(w)
|
||||
for _,wolf in ipairs(wolves) do
|
||||
local r = AH.get_reachable_unocc(wolf)
|
||||
reach_map:union_merge(r, function(x, y, v1, v2) return (v1 or 0) + (v2 or 0) end)
|
||||
end
|
||||
|
||||
-- Add a random rating; avoid avoid_type units
|
||||
local avoid_units = wesnoth.get_units { type = cfg.avoid_type,
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } }
|
||||
}
|
||||
--print('#avoid_units', #avoid_units)
|
||||
-- negative hit for hexes these units can attack
|
||||
local avoid = BC.get_attack_map(avoid_units).units
|
||||
local avoid_map = BC.get_attack_map(avoid_units).units
|
||||
|
||||
local max_rating, goal_hex = -9e99, {}
|
||||
reach_map:iter( function (x, y, v)
|
||||
local rating = v + math.random(99)/100.
|
||||
if avoid:get(x, y) then rating = rating - 1000 end
|
||||
if avoid_map:get(x, y) then rating = rating - 1000 end
|
||||
|
||||
if (rating > max_rating) then
|
||||
max_rating, goal_hex = rating, { x, y }
|
||||
|
@ -48,17 +45,16 @@ function ca_wolves_wander:execution(ai, cfg)
|
|||
|
||||
reach_map:insert(x, y, rating)
|
||||
end)
|
||||
--AH.put_labels(reach_map)
|
||||
--W.message { speaker = 'narrator', message = "Wolves random wander"}
|
||||
|
||||
for i,w in ipairs(wolves) do
|
||||
for _,wolf in ipairs(wolves) do
|
||||
-- For each wolf, we need to check that goal hex is reachable, and out of harm's way
|
||||
local best_hex = AH.find_best_move(w, function(x, y)
|
||||
local best_hex = AH.find_best_move(wolf, function(x, y)
|
||||
local rating = - H.distance_between(x, y, goal_hex[1], goal_hex[2])
|
||||
if avoid:get(x, y) then rating = rating - 1000 end
|
||||
if avoid_map:get(x, y) then rating = rating - 1000 end
|
||||
return rating
|
||||
end)
|
||||
AH.movefull_stopunit(ai, w, best_hex)
|
||||
|
||||
AH.movefull_stopunit(ai, wolf, best_hex)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ function ca_zone_guardian:execution(ai, cfg)
|
|||
}
|
||||
if enemies[1] then
|
||||
local target, min_dist = {}, 9e99
|
||||
for _,e in ipairs(enemies) do
|
||||
local dist = H.distance_between(guardian.x, guardian.y, e.x, e.y)
|
||||
for _,enemy in ipairs(enemies) do
|
||||
local dist = H.distance_between(guardian.x, guardian.y, enemy.x, enemy.y)
|
||||
if (dist < min_dist) then
|
||||
target, min_dist = e, dist
|
||||
target, min_dist = enemy, dist
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -41,15 +41,15 @@ function ca_zone_guardian:execution(ai, cfg)
|
|||
-- 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
|
||||
for xa,ya 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]
|
||||
local occ_hex = wesnoth.get_units { x = xa, y = ya, { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
local defense = 100 - wesnoth.unit_defense(guardian, wesnoth.get_terrain(x, y))
|
||||
local nh = AH.next_hop(guardian, x, y)
|
||||
local defense = 100 - wesnoth.unit_defense(guardian, wesnoth.get_terrain(xa, ya))
|
||||
local nh = AH.next_hop(guardian, xa, ya)
|
||||
if nh then
|
||||
if (nh[1] == x) and (nh[2] == y) and (defense > best_defense) then
|
||||
best_defense, attack_loc = defense, { x, y }
|
||||
if (nh[1] == xa) and (nh[2] == ya) and (defense > best_defense) then
|
||||
best_defense, attack_loc = defense, { xa, ya }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -68,13 +68,13 @@ function ca_zone_guardian:execution(ai, cfg)
|
|||
-- Go through all hexes the guardian can reach, find closest to target
|
||||
-- Cannot use next_hop here since target hex is occupied by enemy
|
||||
local nh, min_dist = {}, 9e99
|
||||
for _,r in ipairs(reach) do
|
||||
for _,hex 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]
|
||||
local occ_hex = wesnoth.get_units { x = hex[1], y = hex[2], { "not", { id = guardian.id } } }[1]
|
||||
if not occ_hex then
|
||||
local dist = H.distance_between(r[1], r[2], target.x, target.y)
|
||||
local dist = H.distance_between(hex[1], hex[2], target.x, target.y)
|
||||
if (dist < min_dist) then
|
||||
min_dist, nh = dist, { r[1], r[2] }
|
||||
min_dist, nh = dist, { hex[1], hex[2] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,8 +44,11 @@ flg_manager::flg_manager(const std::vector<const config*>& era_factions,
|
|||
side_(side),
|
||||
lock_settings_(lock_settings),
|
||||
saved_game_(saved_game),
|
||||
has_no_recruits_((side_.has_attribute("default_recruit") ?
|
||||
side_["default_recruit"].empty() : side_["recruit"].empty()) &&
|
||||
has_no_recruits_(
|
||||
((side_.has_attribute("default_recruit") ?
|
||||
side_["default_recruit"].empty() :
|
||||
side_["recruit"].empty()) ||
|
||||
side_["no_recruit"].to_bool()) &&
|
||||
side_["previous_recruits"].empty() && side_["extra_recruit"].empty()),
|
||||
color_(color),
|
||||
available_factions_(),
|
||||
|
|
|
@ -203,7 +203,7 @@ public:
|
|||
{
|
||||
model_.clear_stuff_list();
|
||||
|
||||
const config& vars = resources::state_of_game
|
||||
const config& vars = resources::gamedata
|
||||
? resources::gamedata->get_variables()
|
||||
: config();
|
||||
|
||||
|
@ -229,7 +229,7 @@ public:
|
|||
}
|
||||
|
||||
int i = 0; ///@todo replace with precached data
|
||||
const config& vars = resources::state_of_game
|
||||
const config& vars = resources::gamedata
|
||||
? resources::gamedata->get_variables()
|
||||
: config();
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ const std::string controller_names[] = {
|
|||
|
||||
const std::string attributes_to_trim[] = {
|
||||
"side",
|
||||
"type",
|
||||
"gender",
|
||||
"recruit",
|
||||
"extra_recruit",
|
||||
"previous_recruits",
|
||||
|
@ -919,7 +921,11 @@ config side_engine::new_config() const
|
|||
|
||||
// Save default "recruit" so that correct faction lists would be
|
||||
// initialized by flg_manager when the new side config is sent over network.
|
||||
// In case recruit list was empty, set a flag to indicate that.
|
||||
res["default_recruit"] = cfg_["recruit"];
|
||||
if (res["default_recruit"].empty()) {
|
||||
res["no_recruit"] = true;
|
||||
}
|
||||
|
||||
// If the user is allowed to change type, faction, leader etc,
|
||||
// then import their new values in the config.
|
||||
|
|
|
@ -310,12 +310,12 @@ void wait::join_game(bool observe)
|
|||
era_factions.push_back(&side);
|
||||
}
|
||||
|
||||
const bool map_settings =
|
||||
level_.child("multiplayer")["mp_use_map_settings"].to_bool();
|
||||
const bool lock_settings =
|
||||
level_["force_lock_settings"].to_bool();
|
||||
const bool saved_game =
|
||||
level_.child("multiplayer")["savegame"].to_bool();
|
||||
|
||||
flg_manager flg(era_factions, *side_choice, map_settings,
|
||||
flg_manager flg(era_factions, *side_choice, lock_settings,
|
||||
saved_game, color);
|
||||
|
||||
std::vector<std::string> choices;
|
||||
|
|
|
@ -53,7 +53,7 @@ controller::controller(display& disp, const vconfig& data, const std::string& sc
|
|||
, segment_index_(segment_index)
|
||||
, parts_()
|
||||
{
|
||||
ASSERT_LOG(resources::state_of_game != NULL, "Ouch: gamestate is NULL when initializing storyscreen controller");
|
||||
ASSERT_LOG(resources::gamedata != NULL, "Ouch: gamedata is NULL when initializing storyscreen controller");
|
||||
resolve_wml(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace
|
|||
* @todo FIXME: the variable repository should be
|
||||
* a class of variable.hpp, and not the game_state.
|
||||
*/
|
||||
#define repos (resources::state_of_game)
|
||||
#define repos (resources::gamedata)
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue