local H = wesnoth.require "lua/helper.lua" local AH = wesnoth.require "ai/lua/ai_helper.lua" local function get_coward(cfg) local filter = cfg.filter or { id = cfg.id } local coward = AH.get_units_with_moves { side = wesnoth.current.side, { "and", filter } }[1] return coward end local ca_coward = {} function ca_coward:evaluation(ai, cfg) if get_coward(cfg) then return cfg.ca_score end return 0 end function ca_coward:execution(ai, cfg) local coward = get_coward(cfg) local reach = wesnoth.find_reach(coward) 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 } } } -- 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 for i,hex in ipairs(reach) do -- Only consider unoccupied hexes 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 _,enemy in ipairs(enemies) do local dist = H.distance_between(hex[1], hex[2], enemy.x, enemy.y) rating = rating - 1 / dist^2 end -- Store this weighting in the third field of each 'reach' element reach[i][3] = rating else reach[i][3] = -9e99 end 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,pos in ipairs(best_pos) do -- Weighting based on distance from 'seek' and 'avoid' 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 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 max_rating, best_hex = -9e99 for _,pos in ipairs(best_overall) do if (pos[3] > max_rating) then max_rating, best_hex = pos[3], pos end end 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) end return ca_coward